From 690437872483a93cc1ffde4f2ec4f10a1ec3adc1 Mon Sep 17 00:00:00 2001 From: Rhys Weatherley Date: Fri, 30 Apr 2004 23:29:28 +0000 Subject: [PATCH] Initial revision --- .cvsignore | 13 + AUTHORS | 1 + COPYING | 340 ++ ChangeLog | 106 + INSTALL | 182 + Makefile.am | 2 + NEWS | 0 README | 65 + auto_gen.sh | 37 + config.guess | 1409 ++++++++ config.sub | 1473 ++++++++ configure.in | 378 ++ doc/.cvsignore | 6 + doc/Makefile.am | 100 + doc/extract-docs.sh | 38 + doc/libjit.3 | 49 + doc/libjit.texi | 1247 +++++++ doc/mkhtml.sh | 29 + doc/mkpdf.sh | 5 + doc/texinfo.tex | 5484 +++++++++++++++++++++++++++++ dpas/.cvsignore | 7 + dpas/Makefile.am | 25 + dpas/README | 8 + dpas/dpas-function.c | 88 + dpas/dpas-internal.h | 121 + dpas/dpas-main.c | 265 ++ dpas/dpas-parser.y | 2247 ++++++++++++ dpas/dpas-scanner.l | 345 ++ dpas/dpas-scope.c | 352 ++ dpas/dpas-scope.h | 146 + dpas/dpas-semantics.h | 158 + dpas/dpas-types.c | 1139 ++++++ dpas/dpas-types.h | 185 + include/.cvsignore | 3 + include/Makefile.am | 2 + include/jit/.cvsignore | 4 + include/jit/Makefile.am | 24 + include/jit/jit-apply.h | 68 + include/jit/jit-block.h | 46 + include/jit/jit-common.h | 90 + include/jit/jit-context.h | 55 + include/jit/jit-defs.h.in | 70 + include/jit/jit-dump.h | 41 + include/jit/jit-elf.h | 84 + include/jit/jit-except.h | 78 + include/jit/jit-function.h | 78 + include/jit/jit-init.h | 35 + include/jit/jit-insn.h | 270 ++ include/jit/jit-intrinsic.h | 408 +++ include/jit/jit-meta.h | 40 + include/jit/jit-opcode.h | 553 +++ include/jit/jit-plus.h | 314 ++ include/jit/jit-type.h | 136 + include/jit/jit-util.h | 90 + include/jit/jit-value.h | 101 + include/jit/jit-walk.h | 98 + include/jit/jit.h | 50 + install-sh | 251 ++ jit/.cvsignore | 4 + jit/Makefile.am | 52 + jit/jit-alloc.c | 255 ++ jit/jit-apply-arm.c | 95 + jit/jit-apply-arm.h | 37 + jit/jit-apply-func.h | 83 + jit/jit-apply-x86.c | 244 ++ jit/jit-apply-x86.h | 320 ++ jit/jit-apply.c | 980 ++++++ jit/jit-block.c | 373 ++ jit/jit-cache.c | 1351 +++++++ jit/jit-cache.h | 271 ++ jit/jit-context.c | 260 ++ jit/jit-dump.c | 759 ++++ jit/jit-dynlib.c | 424 +++ jit/jit-elf-defs.h | 2134 +++++++++++ jit/jit-elf-read.c | 1558 ++++++++ jit/jit-elf-write.c | 541 +++ jit/jit-except.cpp | 491 +++ jit/jit-function.c | 1380 ++++++++ jit/jit-gen-arm.c | 159 + jit/jit-gen-arm.h | 761 ++++ jit/jit-gen-x86.h | 1592 +++++++++ jit/jit-init.c | 61 + jit/jit-insn.c | 6237 +++++++++++++++++++++++++++++++++ jit/jit-internal.h | 616 ++++ jit/jit-interp.cpp | 4503 ++++++++++++++++++++++++ jit/jit-interp.h | 225 ++ jit/jit-intrinsic.c | 3482 ++++++++++++++++++ jit/jit-live.c | 205 ++ jit/jit-memory.c | 178 + jit/jit-memory.h | 76 + jit/jit-meta.c | 216 ++ jit/jit-opcode.c | 584 +++ jit/jit-pool.c | 87 + jit/jit-reg-alloc.c | 1191 +++++++ jit/jit-reg-alloc.h | 56 + jit/jit-rules-arm.c | 669 ++++ jit/jit-rules-arm.h | 89 + jit/jit-rules-interp.c | 1351 +++++++ jit/jit-rules-interp.h | 110 + jit/jit-rules-x86.c | 1042 ++++++ jit/jit-rules-x86.h | 81 + jit/jit-rules.c | 124 + jit/jit-rules.h | 222 ++ jit/jit-string.c | 486 +++ jit/jit-thread.c | 128 + jit/jit-thread.h | 115 + jit/jit-type.c | 1323 +++++++ jit/jit-value.c | 2380 +++++++++++++ jit/jit-walk.c | 229 ++ jitplus/.cvsignore | 3 + jitplus/Makefile.am | 9 + jitplus/jit-plus-context.cpp | 79 + jitplus/jit-plus-function.cpp | 1159 ++++++ jitplus/jit-plus-value.cpp | 245 ++ missing | 336 ++ mkinstalldirs | 40 + tools/.cvsignore | 5 + tools/Makefile.am | 14 + tools/gen-apply.c | 2211 ++++++++++++ tutorial/.cvsignore | 8 + tutorial/Makefile.am | 24 + tutorial/README | 11 + tutorial/t1.c | 74 + tutorial/t2.c | 111 + tutorial/t3.c | 115 + tutorial/t4.cpp | 100 + 126 files changed, 63773 insertions(+) create mode 100644 .cvsignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 auto_gen.sh create mode 100755 config.guess create mode 100755 config.sub create mode 100644 configure.in create mode 100644 doc/.cvsignore create mode 100644 doc/Makefile.am create mode 100755 doc/extract-docs.sh create mode 100644 doc/libjit.3 create mode 100644 doc/libjit.texi create mode 100755 doc/mkhtml.sh create mode 100755 doc/mkpdf.sh create mode 100644 doc/texinfo.tex create mode 100644 dpas/.cvsignore create mode 100644 dpas/Makefile.am create mode 100644 dpas/README create mode 100644 dpas/dpas-function.c create mode 100644 dpas/dpas-internal.h create mode 100644 dpas/dpas-main.c create mode 100644 dpas/dpas-parser.y create mode 100644 dpas/dpas-scanner.l create mode 100644 dpas/dpas-scope.c create mode 100644 dpas/dpas-scope.h create mode 100644 dpas/dpas-semantics.h create mode 100644 dpas/dpas-types.c create mode 100644 dpas/dpas-types.h create mode 100644 include/.cvsignore create mode 100644 include/Makefile.am create mode 100644 include/jit/.cvsignore create mode 100644 include/jit/Makefile.am create mode 100644 include/jit/jit-apply.h create mode 100644 include/jit/jit-block.h create mode 100644 include/jit/jit-common.h create mode 100644 include/jit/jit-context.h create mode 100644 include/jit/jit-defs.h.in create mode 100644 include/jit/jit-dump.h create mode 100644 include/jit/jit-elf.h create mode 100644 include/jit/jit-except.h create mode 100644 include/jit/jit-function.h create mode 100644 include/jit/jit-init.h create mode 100644 include/jit/jit-insn.h create mode 100644 include/jit/jit-intrinsic.h create mode 100644 include/jit/jit-meta.h create mode 100644 include/jit/jit-opcode.h create mode 100644 include/jit/jit-plus.h create mode 100644 include/jit/jit-type.h create mode 100644 include/jit/jit-util.h create mode 100644 include/jit/jit-value.h create mode 100644 include/jit/jit-walk.h create mode 100644 include/jit/jit.h create mode 100755 install-sh create mode 100644 jit/.cvsignore create mode 100644 jit/Makefile.am create mode 100644 jit/jit-alloc.c create mode 100644 jit/jit-apply-arm.c create mode 100644 jit/jit-apply-arm.h create mode 100644 jit/jit-apply-func.h create mode 100644 jit/jit-apply-x86.c create mode 100644 jit/jit-apply-x86.h create mode 100644 jit/jit-apply.c create mode 100644 jit/jit-block.c create mode 100644 jit/jit-cache.c create mode 100644 jit/jit-cache.h create mode 100644 jit/jit-context.c create mode 100644 jit/jit-dump.c create mode 100644 jit/jit-dynlib.c create mode 100644 jit/jit-elf-defs.h create mode 100644 jit/jit-elf-read.c create mode 100644 jit/jit-elf-write.c create mode 100644 jit/jit-except.cpp create mode 100644 jit/jit-function.c create mode 100644 jit/jit-gen-arm.c create mode 100644 jit/jit-gen-arm.h create mode 100644 jit/jit-gen-x86.h create mode 100644 jit/jit-init.c create mode 100644 jit/jit-insn.c create mode 100644 jit/jit-internal.h create mode 100644 jit/jit-interp.cpp create mode 100644 jit/jit-interp.h create mode 100644 jit/jit-intrinsic.c create mode 100644 jit/jit-live.c create mode 100644 jit/jit-memory.c create mode 100644 jit/jit-memory.h create mode 100644 jit/jit-meta.c create mode 100644 jit/jit-opcode.c create mode 100644 jit/jit-pool.c create mode 100644 jit/jit-reg-alloc.c create mode 100644 jit/jit-reg-alloc.h create mode 100644 jit/jit-rules-arm.c create mode 100644 jit/jit-rules-arm.h create mode 100644 jit/jit-rules-interp.c create mode 100644 jit/jit-rules-interp.h create mode 100644 jit/jit-rules-x86.c create mode 100644 jit/jit-rules-x86.h create mode 100644 jit/jit-rules.c create mode 100644 jit/jit-rules.h create mode 100644 jit/jit-string.c create mode 100644 jit/jit-thread.c create mode 100644 jit/jit-thread.h create mode 100644 jit/jit-type.c create mode 100644 jit/jit-value.c create mode 100644 jit/jit-walk.c create mode 100644 jitplus/.cvsignore create mode 100644 jitplus/Makefile.am create mode 100644 jitplus/jit-plus-context.cpp create mode 100644 jitplus/jit-plus-function.cpp create mode 100644 jitplus/jit-plus-value.cpp create mode 100755 missing create mode 100755 mkinstalldirs create mode 100644 tools/.cvsignore create mode 100644 tools/Makefile.am create mode 100644 tools/gen-apply.c create mode 100644 tutorial/.cvsignore create mode 100644 tutorial/Makefile.am create mode 100644 tutorial/README create mode 100644 tutorial/t1.c create mode 100644 tutorial/t2.c create mode 100644 tutorial/t3.c create mode 100644 tutorial/t4.cpp diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..3ae6b91 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,13 @@ +Makefile +Makefile.in +.deps +configure +config.log +config.cache +config.status +aclocal.m4 +confdefs.h +config.h +config.h.in +stamp-h +stamp-h.in diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e7ba620 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Rhys Weatherley diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + 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. + + GNU GENERAL PUBLIC LICENSE + 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. + + + 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. + + , 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. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b326cf6 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,106 @@ + +2004-05-01 Rhys Weatherley + + * jit/jit-apply-x86.c, jit/jit-rules-x86.c, tools/gen-apply.c: + fix function prolog and epilog handling for structure returns + under x86/cdecl. + +2004-04-30 Rhys Weatherley + + * include/jit/jit-function.h, include/jit/jit-insn.h, + include/jit/jit-opcode.h, include/jit/jit-plus.h, jit/jit-except.cpp, + jit/jit-function.c, jit/jit-insn.c, jit/jit-internal.h, + jit/jit-interp.cpp, jit/jit-interp.h, jit/jit-opcode.c, + jit/jit-rules-arm.c, jit/jit-rules-interp.c, jit/jit-rules-x86.c, + jit/jit-rules.h, jitplus/jit-plus-function.cpp: put some + infrastructure in place to support "try"-related instructions. + + * dpas/dpas-parser.y, dpas/dpas-scope.h, dpas/dpas-semantics.h: + introduce effective address based l-values into dpas. + +2004-04-27 Rhys Weatherley + + * jit/jit-dump.c, jit/jit-reg-alloc.c: fix a register stack bug + in "_jit_regs_set_value"; add some more debug code. + + * include/jit/jit-opcode.h, jit/jit-insn.c, jit/jit-internal.h, + jit/jit-interp.cpp, jit/jit-opcode.c: wrap function calls + with code to set up and remove exception frame information. + + * jit/jit-function.c, jit/jit-insn.c, jit/jit-internal.h: + intuit "nothrow" and "noreturn" flags for compiled functions. + + * include/jit/jit-insn.h, include/jit/jit-plus.h, jit/jit-insn.c, + jitplus/jit-plus-function.cpp: add "jit_insn_throw", + "jit_insn_rethrow", and "jit_insn_get_call_stack" to assist + with throwing exceptions. + + * include/jit/jit-insn.h, jit/jit-block.c, jit/jit-insn.c, + jit/jit-internal.h: add some instructions for managing "try" blocks. + + * include/jit/jit-opcode.h, jit/jit-block.c, jit/jit-insn.c, + jit/jit-internal.h, jit/jit-opcode.c: more work on "try" blocks. + +2004-04-26 Rhys Weatherley + + * jit/jit-reg-alloc.c, jit/jit-reg-alloc.h: add functions to + assist with register allocation on stack-based architectures. + + * jit/jit-rules-interp.c: implement "_jit_gen_load_value" for + the interpreter backend. + + * jit/jit-interp.cpp, jit/jit-interp.h: fix some opcode names that + should be prefixed with "JIT_OP". + + * jit/jit-function.c, jit/jit-internal.h, jit/jit-reg-alloc.c, + jit/jit-reg-alloc.h, jit/jit-rules-arm.c, jit/jit-rules-interp.c, + jit/jit-rules-interp.h, jit/jit-rules-x86.c, jit/jit-rules.h: + interpreter back end code generation for binary, unary, and + branch opcodes. + + * doc/libjit.texi, jit/jit-rules-interp.c: fix the Texinfo section + markers in the "Porting" chapter. + + * jit/jit-rules-interp.c: code generation for interpreter return's. + + * include/jit/jit-except.h, include/jit/jit-function.h, + include/jit/jit-plus.h, jit/jit-except.cpp, jit/jit-function.c, + jit/jit-interp.cpp, jit/jit-interp.h, jit/jit-rules-interp.c, + jitplus/jit-plus-function.cpp: implement "jit_function_apply" + and "jit_function_apply_vararg" for the interpreter back end. + + * jitplus/jit-plus-function.cpp: initialization errors in + the C++ binding. + + * jit/jit-function.c, jit/jit-insn.c, jit/jit-interp.cpp, + jit/jit-rules-interp.c: work on code generation for function calls. + + * doc/libjit.texi, include/jit/jit-insn.h, include/jit/jit-plus.h, + jit/jit-insn.c, jitplus/jit-plus-function.cpp, tutorial/t2.c: + add a "flags" parmeter to "jit_insn_call" and friends, so that + "nothrow", "noreturn", and "tail" options can be supplied. + + * jit/jit-dump.c, jit/jit-interp.cpp, jit/jit-interp.h, + jit/jit-opcode.c, jit/jit-rules-interp.c: dump the translated + native code from "jit_dump_function" are compilation. + + * jit/jit-interp.cpp, jit/jit-rules-interp.c: argument variable + offsets should in item units, not byte units. + + * jit/jit-insn.c, jit/jit-reg-alloc.c, jit/jit-reg-alloc.h, + jit/jit-rules-interp.c: improve register allocation in stacks. + +2004-04-25 Rhys Weatherley + + * jit/jit-apply-func.h, jit/jit-apply-x86.c, jit/jit-apply-x86.h, + jit/jit-cache.c: use CPU-specifc padding for aligning code + areas with NOP's, for greater runtime efficiency. + + * include/jit/jit-elf.h, jit/Makefile.am, jit/jit-elf-write.c: + add the skeleton of the ELF writing routines. + + * jit/jit-interp.cpp: modify VM_BR_TARGET so that it is PIC-friendly. + +2004-04-24 Rhys Weatherley + + * */*: Initial public release. Most of the skeleton code is in place. diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..b42a17a --- /dev/null +++ b/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..5e82876 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = include tools jit jitplus dpas tutorial doc diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..a524a8b --- /dev/null +++ b/README @@ -0,0 +1,65 @@ + +libjit +------ + +This library in this distribution implements Just-In-Time compilation +functionality. Unlike other JIT's, this one is designed to be independent +of any particular virtual machine bytecode format or language. The hope +is that Free Software projects can get a leg-up on proprietry VM vendors +by using this library rather than spending large amounts of time writing +their own JIT from scratch. + +This JIT is also designed to be portable to multiple archictures. +If you run libjit on a machine for which a native code generator is +not yet available, then libjit will fall back to interpreting the code. +This way, you don't need to write your own interpreter for your +bytecode format if you don't want to. + +The library is distributed under the terms of the GNU General Public +License. See the COPYING file for details. + +The documentation for libjit is in Texinfo format. A general overview +is in "doc/libjit.texi" with the remainder of the function documentation +in the source files themselves. + +Building libjit +--------------- + +You will need GNU make, Bison, and Flex to build libjit, and it is +also highly recommended that you use gcc. To configure, build, +and install, you would do the following: + + ./configure + make + make install + +Compiler notes +-------------- + +It is highly recommended that you build libjit with gcc and not +some other C compiler, even if you plan to use some other C +compiler to access the library. We make use of a number of gcc +builtins to assist with stack walking, dynamic function calls, +and closures. + +It is also recommended that you don't use the "-fomit-frame-pointer" +option when compiling programs that use libjit. Otherwise stack walking +may not work correctly, leading to problems when throwing exceptions. +The configure script for libjit will detect the presence of this +option in CFLAGS and remove it when building libjit itself. + +Contacting the authors +---------------------- + +The primary author is Rhys Weatherley at Southern Storm Software, Pty Ltd. +He can be reached via e-mail at "rweather@southern-storm.com.au". + +The latest version of libjit will always be available from the Southern +Storm Web site: + + http://www.southern-storm.com.au/libjit/ + +Discussions about libjit can be carried out on the DotGNU Portable.NET +mailing list, "pnet-developers@dotgnu.org". Visit "www.dotgnu.org" +for details on how to subscribe to this list. A separate mailing list +will be created if there is demand for it. diff --git a/auto_gen.sh b/auto_gen.sh new file mode 100755 index 0000000..766ba35 --- /dev/null +++ b/auto_gen.sh @@ -0,0 +1,37 @@ +#!/bin/sh +# +# auto_gen.sh - Make the Makefile.in and configure files. +# +# Copyright (C) 2001, 2002 Southern Storm Software, Pty Ltd. +# +# 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 + +# Run aclocal to update the macros. +aclocal + +# Run autoheader to generate config.h.in. +autoheader + +# Get extra options to use depending upon the automake version. +AM_VERSION=`automake --version` +case "$AM_VERSION" in + automake*1.4*) AM_FLAGS="" ;; + *) AM_FLAGS="--ignore-deps" ;; +esac + +# Run automake and autoconf. +automake --add-missing --copy $AM_FLAGS +autoconf +exit 0 diff --git a/config.guess b/config.guess new file mode 100755 index 0000000..78f6b92 --- /dev/null +++ b/config.guess @@ -0,0 +1,1409 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-01-10' + +# This file 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. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit 0 ;; + amiga:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + arc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + hp300:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mac68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + macppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme68k:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvme88k:OpenBSD:*:*) + echo m88k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + mvmeppc:OpenBSD:*:*) + echo powerpc-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + pmax:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sgi:OpenBSD:*:*) + echo mipseb-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + sun3:OpenBSD:*:*) + echo m68k-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + wgrisc:OpenBSD:*:*) + echo mipsel-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:OpenBSD:*:*) + echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} + exit 0 ;; + *:MicroBSD:*:*) + echo ${UNAME_MACHINE}-unknown-microbsd${UNAME_RELEASE} + exit 0 ;; + alpha:OSF1:*:*) + if test $UNAME_RELEASE = "V4.0"; then + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + fi + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + eval $set_cc_for_build + cat <$dummy.s + .data +\$Lformat: + .byte 37,100,45,37,120,10,0 # "%d-%x\n" + + .text + .globl main + .align 4 + .ent main +main: + .frame \$30,16,\$26,0 + ldgp \$29,0(\$27) + .prologue 1 + .long 0x47e03d80 # implver \$0 + lda \$2,-1 + .long 0x47e20c21 # amask \$2,\$1 + lda \$16,\$Lformat + mov \$0,\$17 + not \$1,\$18 + jsr \$26,printf + ldgp \$29,0(\$26) + mov 0,\$16 + jsr \$26,exit + .end main +EOF + $CC_FOR_BUILD -o $dummy $dummy.s 2>/dev/null + if test "$?" = 0 ; then + case `$dummy` in + 0-0) + UNAME_MACHINE="alpha" + ;; + 1-0) + UNAME_MACHINE="alphaev5" + ;; + 1-1) + UNAME_MACHINE="alphaev56" + ;; + 1-101) + UNAME_MACHINE="alphapca56" + ;; + 2-303) + UNAME_MACHINE="alphaev6" + ;; + 2-307) + UNAME_MACHINE="alphaev67" + ;; + 2-1307) + UNAME_MACHINE="alphaev68" + ;; + 3-1307) + UNAME_MACHINE="alphaev7" + ;; + esac + fi + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit 0 ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit 0 ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit 0 ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit 0;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit 0 ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit 0 ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit 0 ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit 0;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit 0;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit 0 ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit 0 ;; + DRS?6000:UNIX_SV:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7 && exit 0 ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit 0 ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit 0 ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit 0 ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit 0 ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit 0 ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit 0 ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit 0 ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit 0 ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit 0 ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit 0 ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit 0 ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit 0 ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c \ + && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ + && exit 0 + echo mips-mips-riscos${UNAME_RELEASE} + exit 0 ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit 0 ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit 0 ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit 0 ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit 0 ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit 0 ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit 0 ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit 0 ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit 0 ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit 0 ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit 0 ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit 0 ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit 0 ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit 0 ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo rs6000-ibm-aix3.2.5 + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit 0 ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit 0 ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit 0 ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit 0 ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit 0 ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit 0 ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit 0 ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit 0 ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit 0 ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + # avoid double evaluation of $set_cc_for_build + test -n "$CC_FOR_BUILD" || eval $set_cc_for_build + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit 0 ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit 0 ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 + echo unknown-hitachi-hiuxwe2 + exit 0 ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit 0 ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit 0 ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit 0 ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit 0 ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit 0 ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit 0 ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit 0 ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit 0 ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit 0 ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit 0 ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit 0 ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + *:UNICOS/mp:*:*) + echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit 0 ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit 0 ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit 0 ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit 0 ;; + *:FreeBSD:*:*) + # Determine whether the default compiler uses glibc. + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #if __GLIBC__ >= 2 + LIBC=gnu + #else + LIBC= + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} + exit 0 ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit 0 ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit 0 ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit 0 ;; + x86:Interix*:3*) + echo i586-pc-interix3 + exit 0 ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit 0 ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit 0 ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit 0 ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit 0 ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit 0 ;; + *:GNU:*:*) + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit 0 ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit 0 ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` + test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 + ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit 0 ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit 0 ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit 0 ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit 0 ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit 0 ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit 0 ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit 0 ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit 0 ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit 0 ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit 0 ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit 0 ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #ifdef __INTEL_COMPILER + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` + test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 + test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit 0 ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit 0 ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit 0 ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit 0 ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit 0 ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit 0 ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit 0 ;; + i*86:*:5:[78]*) + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit 0 ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit 0 ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit 0 ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit 0 ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit 0 ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit 0 ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit 0 ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit 0 ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit 0 ;; + M68*:*:R3V[567]*:*) + test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; + 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4.3${OS_REL} && exit 0 + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && echo i486-ncr-sysv4 && exit 0 ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit 0 ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit 0 ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit 0 ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit 0 ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit 0 ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit 0 ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit 0 ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit 0 ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit 0 ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit 0 ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit 0 ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit 0 ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit 0 ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit 0 ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit 0 ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit 0 ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit 0 ;; + *:Darwin:*:*) + case `uname -p` in + *86) UNAME_PROCESSOR=i686 ;; + powerpc) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit 0 ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit 0 ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit 0 ;; + NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit 0 ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit 0 ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit 0 ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit 0 ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit 0 ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit 0 ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit 0 ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit 0 ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit 0 ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit 0 ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit 0 ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit 0 ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit 0 ;; + c34*) + echo c34-convex-bsd + exit 0 ;; + c38*) + echo c38-convex-bsd + exit 0 ;; + c4*) + echo c4-convex-bsd + exit 0 ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/config.sub b/config.sub new file mode 100755 index 0000000..04baf3d --- /dev/null +++ b/config.sub @@ -0,0 +1,1473 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + +timestamp='2003-01-03' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit 0 ;; + --version | -v ) + echo "$version" ; exit 0 ;; + --help | --h* | -h ) + echo "$usage"; exit 0 ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit 0;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | freebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ + | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k \ + | m32r | m68000 | m68k | m88k | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | msp430 \ + | ns16k | ns32k \ + | openrisc | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh3e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \ + | strongarm \ + | tahoe | thumb | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* \ + | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* \ + | clipper-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* \ + | m32r-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | msp430-* \ + | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh3e-* | sh[34]eb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ + | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* | tic30-* | tic4x-* | tic54x-* | tic80-* | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ + | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + crds | unos) + basic_machine=m68k-crds + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + mmix*) + basic_machine=mmix-knuth + os=-mmixware + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nv1) + basic_machine=nv1-cray + os=-unicosmp + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + or32 | or32-*) + basic_machine=or32-unknown + os=-coff + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2) + basic_machine=i686-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic4x | c4x*) + basic_machine=tic4x-unknown + os=-coff + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh3 | sh4 | sh3eb | sh4eb | sh[1234]le | sh3ele) + basic_machine=sh-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparc | sparcv9 | sparcv9b) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -netbsd* | -openbsd* | -freebsd* | -riscix* \ + | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -microbsd*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-ibm) + os=-aix + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..35d3069 --- /dev/null +++ b/configure.in @@ -0,0 +1,378 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(include/jit/jit.h) + +dnl Determine the build, host, and target system types. +AC_CANONICAL_SYSTEM + +dnl Initialize automake. +AM_INIT_AUTOMAKE(libjit, 0.0.0) +AM_CONFIG_HEADER(config.h) + +dnl Turn off the cygwin library if building for Win32. +dnl Note: We have to include if we will be using "__int64" +dnl because otherwise the mingw32 compiler won't define it correctly. +AC_MSG_CHECKING([if building for some Win32 platform]) +AC_SUBST(JIT_INT64_INCLUDE) +case "$host" in + *-*-cygwin*) + platform_win32=yes + if test "x$CC" = "x" ; then + CC="gcc -mno-cygwin" + fi + if test "x$CXX" = "x" ; then + if test "x$CC" = "xcl" ; then + CXX="cl" + else + CXX="g++ -mno-cygwin" + fi + fi + suppress_libm=yes + JIT_INT64_INCLUDE='#include ' + ;; + *-*-mingw*) + platform_win32=yes + if test "x$CC" = "xcl" ; then + if test "x$CXX" = "x" ; then + CXX="cl" + fi + fi + suppress_libm=yes + JIT_INT64_INCLUDE='#include ' + ;; + *) + platform_win32=no + suppress_libm=no + JIT_INT64_INCLUDE= + ;; +esac +AC_MSG_RESULT($platform_win32) + +dnl Checks for programs. +AC_PROG_AWK +AC_PROG_CC +AC_PROG_CXX +AC_PROG_INSTALL +AC_PROG_LN_S +AC_PROG_MAKE_SET +AC_PROG_RANLIB +AC_PROG_YACC +AM_PROG_LEX + +dnl Set the correct flags for compiling with MSVC. "/QIfist" is needed +dnl on systems with both VC 6.0 and VC 7.0 installed: sometimes VC 7.0 +dnl picks up the wrong intrinsic libraries (particularly for __ftol2). +dnl The "/EHs" option is required to enable exception handling in C++. +if test "x$CC" = "xcl" ; then + CFLAGS="/nologo /QIfist" + CXXFLAGS="/nologo /QIfist /EHs" +fi + +dnl Check for file extensions. +AC_EXEEXT +AC_OBJEXT + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(string.h strings.h memory.h stdlib.h stdarg.h varargs.h) +AC_CHECK_HEADERS(tgmath.h math.h ieeefp.h pthread.h unistd.h sys/types.h) +AC_CHECK_HEADERS(sys/mman.h fcntl.h dlfcn.h sys/cygwin.h sys/stat.h) + +dnl A macro that helps detect the size of types in a cross-compile environment. +AC_DEFUN([AC_COMPILE_CHECK_SIZEOF], +[changequote(<<, >>)dnl +dnl The name to #define. +define(<>, translit(sizeof_$1, [a-z *], [A-Z_P]))dnl +dnl The cache variable name. +define(<>, translit(ac_cv_sizeof_$1, [ *], [_p]))dnl +changequote([, ])dnl +AC_MSG_CHECKING(size of $1) +AC_CACHE_VAL(AC_CV_NAME, +[for ac_size in 4 8 1 2 12 16 $2 ; do # List sizes in rough order of prevalence. + AC_TRY_COMPILE([#include "confdefs.h" +#include +], [switch (0) case 0: case (sizeof ($1) == $ac_size):;], AC_CV_NAME=$ac_size) + if test x$AC_CV_NAME != x ; then break; fi +done +]) +if test x$AC_CV_NAME = x ; then + AC_CV_NAME=0 +fi +AC_MSG_RESULT($AC_CV_NAME) +AC_DEFINE_UNQUOTED(AC_TYPE_NAME, $AC_CV_NAME, [The number of bytes in type $1]) +undefine([AC_TYPE_NAME])dnl +undefine([AC_CV_NAME])dnl +]) + +dnl A macro that detects if "char" is unsigned in a cross-compile environment. +AC_DEFUN([AC_COMPILE_CHAR_UNSIGNED], +[AC_MSG_CHECKING(if char is unsigned) +AC_CACHE_VAL(ac_cv_c_char_unsigned, +AC_TRY_COMPILE([#include "confdefs.h" +], [switch (-1) case -1: case (char)255:;], ac_cv_c_char_unsigned=yes)) +if test x$ac_cv_c_char_unsigned = x ; then + ac_cv_c_char_unsigned=no +fi +AC_MSG_RESULT($ac_cv_c_char_unsigned) +if test $ac_cv_c_char_unsigned = yes && test "$GCC" != yes; then + AC_DEFINE(__CHAR_UNSIGNED__, 1, [Define to 1 if "char" is unsigned]) +fi +]) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_COMPILE_CHAR_UNSIGNED +AC_COMPILE_CHECK_SIZEOF(char, 1) +AC_COMPILE_CHECK_SIZEOF(short, 2) +AC_COMPILE_CHECK_SIZEOF(int, 4) +AC_COMPILE_CHECK_SIZEOF(long, 4) +AC_COMPILE_CHECK_SIZEOF(long long, 8) +AC_COMPILE_CHECK_SIZEOF(__int64, 8) +AC_COMPILE_CHECK_SIZEOF(float, 4) +AC_COMPILE_CHECK_SIZEOF(double, 8) +AC_COMPILE_CHECK_SIZEOF(long double, 12) +AC_COMPILE_CHECK_SIZEOF(void *, 4) + +dnl Determine the types to use for specific sizes of integers and floats. +AC_SUBST(JITINT8) +AC_SUBST(JITUINT8) +AC_SUBST(JITINT16) +AC_SUBST(JITINT32) +AC_SUBST(JITINT64) +AC_SUBST(JITINT64CXX) +AC_SUBST(JITNATIVEINT) +AC_SUBST(JITFLOAT32) +AC_SUBST(JITFLOAT64) +AC_SUBST(JITNATIVEFLOAT) +AC_SUBST(JITNATIVEINTDEFINE) +AC_SUBST(JITNFLOATISDOUBLE) +AC_MSG_CHECKING(for the 8-bit integer types) +if test "$ac_cv_sizeof_char" = 1 ; then + if test "x$ac_cv_c_char_unsigned" = "xyes" ; then + JITINT8="signed char" + else + JITINT8=char + fi + JITUINT8="unsigned char" +elif test "$ac_cv_sizeof_short" = 1 ; then + JITINT8=short + JITUINT8="unsigned short" +elif test "$ac_cv_sizeof_int" = 1 ; then + JITINT8=int + JITUINT8="unsigned int" +elif test "$ac_cv_sizeof_long" = 1 ; then + JITINT8=long + JITUINT8="unsigned long" +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT([$JITINT8, $JITUINT8]) +AC_MSG_CHECKING(for the 16-bit integer types) +if test "$ac_cv_sizeof_short" = 2 ; then + JITINT16=short +elif test "$ac_cv_sizeof_int" = 2 ; then + JITINT16=int +elif test "$ac_cv_sizeof_long" = 2 ; then + JITINT16=long +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT([$JITINT16, unsigned $JITINT16]) +AC_MSG_CHECKING(for the 32-bit integer types) +if test "$ac_cv_sizeof_int" = 4 ; then + JITINT32=int +elif test "$ac_cv_sizeof_long" = 4 ; then + JITINT32=long +elif test "$ac_cv_sizeof_short" = 4 ; then + JITINT32=short +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT([$JITINT32, unsigned $JITINT32]) +AC_MSG_CHECKING(for the 64-bit integer types) +JITINT64CXX= +if test "$ac_cv_sizeof___int64" = 8 ; then + if test "x$JIT_INT64_INCLUDE" = "x" ; then + JITINT64='long long' + else + dnl __int64 doesn't work with g++, although it does with gcc. + JITINT64='__int64' + JITINT64CXX='long long' + fi +elif test "$ac_cv_sizeof_int" = 8 ; then + JITINT64=int +elif test "$ac_cv_sizeof_long" = 8 ; then + JITINT64=long +elif test "$ac_cv_sizeof_long_long" = 8 ; then + JITINT64='long long' +else + AC_MSG_ERROR(unknown) +fi +if test "x$JITINT64CXX" = "x" ; then + JITINT64CXX="$JITINT64" +fi +AC_MSG_RESULT([$JITINT64, unsigned $JITINT64]) +AC_MSG_CHECKING(for the native integer types) +if test "$ac_cv_sizeof_void_p" = 4 ; then + JITNATIVEINT="$JITINT32" + JITNATIVEINTDEFINE="JIT_NATIVE_INT32" +elif test "$ac_cv_sizeof_void_p" = 8 ; then + JITNATIVEINT="$JITINT64" + JITNATIVEINTDEFINE="JIT_NATIVE_INT64" +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT([$JITNATIVEINT, unsigned $JITNATIVEINT]) +AC_MSG_CHECKING(for the 32-bit floating-point type) +if test "$ac_cv_sizeof_float" = 4 ; then + JITFLOAT32=float +elif test "$ac_cv_sizeof_double" = 4 ; then + JITFLOAT32=double +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT($JITFLOAT32) +AC_MSG_CHECKING(for the 64-bit floating-point type) +if test "$ac_cv_sizeof_float" = 8 ; then + JITFLOAT64=float +elif test "$ac_cv_sizeof_double" = 8 ; then + JITFLOAT64=double +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT($JITFLOAT64) +AC_MSG_CHECKING(for the native floating-point type) +JITNFLOATISDOUBLE='' +if test "x$platform_win32" = "xyes" ; then + dnl MSVC's "long double" is the same as "double", so we make sure + dnl to preserve compatibility between MSVC and gcc. + JITNATIVEFLOAT='double' + JITNFLOATISDOUBLE='#define JIT_NFLOAT_IS_DOUBLE 1' +elif test "$ac_cv_sizeof_long_double" != 0 ; then + if test "$ac_cv_sizeof_long_double" = "$ac_cv_sizeof_double" ; then + JITNATIVEFLOAT='double' + JITNFLOATISDOUBLE='#define JIT_NFLOAT_IS_DOUBLE 1' + else + JITNATIVEFLOAT='long double' + fi +elif test "$ac_cv_sizeof_double" != 0 ; then + JITNATIVEFLOAT=double + JITNFLOATISDOUBLE='#define JIT_NFLOAT_IS_DOUBLE 1' +elif test "$ac_cv_sizeof_float" != 0 ; then + JITNATIVEFLOAT=float +else + AC_MSG_ERROR(unknown) +fi +AC_MSG_RESULT($JITNATIVEFLOAT) + +dnl Check to see if we are using gcc or not. +if test x$GCC = xyes ; then + CFLAGS="$CFLAGS -Wall" +fi +if test x$GXX = xyes ; then + CXXFLAGS="$CXXFLAGS -Wall" +fi + +dnl If CFLAGS contains "-fomit-frame-pointer", then remove it. +dnl We need to have frame pointers to perform stack walking. +case "x$CFLAGS" in + *-fomit-frame-pointer*) + CFLAGS=`echo "$CFLAGS" | sed 's/-fomit-frame-pointer//'` ;; + *) ;; +esac +case "x$CXXFLAGS" in + *-fomit-frame-pointer*) + CXXFLAGS=`echo "$CXXFLAGS" | sed 's/-fomit-frame-pointer//'` ;; + *) ;; +esac + +dnl Find the option to use to turn on C++ exception handling. +AC_CACHE_CHECK(for C++ exception handling option, ac_cv_prog_cxx_exceptions, +[echo 'int main(int argc, char **argv){try { throw 1; } catch(int i) { return i; } return 0;}' > conftest.c +if test -z "`${CXX-c++} -o conftest conftest.c 2>&1`"; then + ac_cv_prog_cxx_exceptions='none needed' +else + if test -z "`${CXX-c++} -fexceptions -o conftest conftest.c 2>&1`"; then + ac_cv_prog_cxx_exceptions=-fexceptions + else + if test -z "`${CXX-c++} -fhandle-exceptions -o conftest conftest.c 2>&1`"; then + ac_cv_prog_cxx_exceptions=-fhandle-exceptions + else + ac_cv_prog_cxx_exceptions='none needed' + fi + fi +fi +rm -f conftest* +]) +if test "x$ac_cv_prog_cxx_exceptions" != "xnone needed" ; then + CXXFLAGS="$ac_cv_prog_cxx_exceptions $CXXFLAGS" +fi + +dnl Determine if the C++ compiler understands the "throw()" idiom. +AC_CACHE_CHECK(for C++ throw() idiom, ac_cv_prog_cxx_throw_idiom, +[echo 'extern "C" void func(void) throw(); int main(int argc, char **argv){return 0;}' > conftest.c +if test -z "`${CXX-c++} -o conftest conftest.c 2>&1`"; then + ac_cv_prog_cxx_throw_idiom=yes +else + ac_cv_prog_cxx_throw_idiom=no +fi +rm -f conftest* +]) +AC_SUBST(JITTHROWIDIOM) +if test "x$ac_cv_prog_cxx_throw_idiom" = "xyes" ; then + JITTHROWIDIOM='throw()' +else + JITTHROWIDIOM='' +fi + +dnl Determine if the C++ compiler understands the "-fno-gcse" option. +dnl We will get better code in the interpreter if we use this option. +AC_CACHE_CHECK(for -fno-gcse option, ac_cv_prog_no_gcse, +[echo 'int main(int argc, char **argv){ return 0;}' > conftest.c +if test -z "`${CXX-c++} -fno-gcse -o conftest conftest.c 2>&1`"; then + ac_cv_prog_no_gcse=yes +else + ac_cv_prog_no_gcse=no +fi +rm -f conftest* +]) +if test "x$ac_cv_prog_no_gcse" = "xyes" ; then + CXXFLAGS="-fno-gcse $CXXFLAGS" +fi + +dnl Checks for library functions. +if test "x$suppress_libm" = "xno" ; then + AC_CHECK_LIB(m, sin) +fi +if test "x$platform_win32" = "xno" ; then + AC_CHECK_LIB(dl, dlopen) + AC_CHECK_LIB(pthread, pthread_create) +fi +AC_FUNC_MEMCMP +AC_CHECK_FUNCS(memset memcmp memchr memcpy memmove bcopy bzero bcmp) +AC_CHECK_FUNCS(strlen strcpy strcat strncpy strcmp strncmp) +AC_CHECK_FUNCS(strcoll _strcoll stricoll _stricoll strcasecmp) +AC_CHECK_FUNCS(strncoll _strncoll strnicoll _strnicoll strncasecmp) +AC_CHECK_FUNCS(strchr strrchr vsprintf vsnprintf _vsnprintf getpagesize) +AC_CHECK_FUNCS(isnan isinf finite fmod remainder drem ceil floor) +AC_CHECK_FUNCS(acos asin atan atan2 cos cosh exp log log10 pow) +AC_CHECK_FUNCS(sin sinh sqrt tan tanh) +AC_CHECK_FUNCS(isnanf isinff finitef fmodf remainderf dremf ceilf floorf) +AC_CHECK_FUNCS(acosf asinf atanf atan2f cosf coshf expf logf log10f powf) +AC_CHECK_FUNCS(sinf sinhf sqrtf tanf tanhf) +AC_CHECK_FUNCS(isnanl isinfl finitel fmodl remainderl dreml ceill floorl) +AC_CHECK_FUNCS(acosl asinl atanl atan2l cosl coshl expl logl log10l powl) +AC_CHECK_FUNCS(sinl sinhl sqrtl tanl tanhl) +AC_CHECK_FUNCS(dlopen cygwin_conv_to_win32_path mmap munmap mprotect) +AC_FUNC_ALLOCA + +AC_OUTPUT([ +Makefile +include/Makefile +include/jit/Makefile +include/jit/jit-defs.h +tools/Makefile +jit/Makefile +jitplus/Makefile +dpas/Makefile +tutorial/Makefile +doc/Makefile]) diff --git a/doc/.cvsignore b/doc/.cvsignore new file mode 100644 index 0000000..ef955d2 --- /dev/null +++ b/doc/.cvsignore @@ -0,0 +1,6 @@ +Makefile +Makefile.in +.deps +libjit.info* +libjitext-* +*.pdf diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 0000000..d24d152 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,100 @@ + +man_MANS = libjit.3 +EXTRA_DIST = $(man_MANS) + +info_TEXINFOS = libjit.texi +libjit_TEXINFOS = \ + $(srcdir)/libjitext-alloc.texi \ + $(srcdir)/libjitext-apply.texi \ + $(srcdir)/libjitext-block.texi \ + $(srcdir)/libjitext-context.texi \ + $(srcdir)/libjitext-dynlib.texi \ + $(srcdir)/libjitext-elf-read.texi \ + $(srcdir)/libjitext-except.texi \ + $(srcdir)/libjitext-function.texi \ + $(srcdir)/libjitext-init.texi \ + $(srcdir)/libjitext-insn.texi \ + $(srcdir)/libjitext-intrinsic.texi \ + $(srcdir)/libjitext-memory.texi \ + $(srcdir)/libjitext-meta.texi \ + $(srcdir)/libjitext-reg-alloc.texi \ + $(srcdir)/libjitext-rules-interp.texi \ + $(srcdir)/libjitext-string.texi \ + $(srcdir)/libjitext-type.texi \ + $(srcdir)/libjitext-value.texi \ + $(srcdir)/libjitext-walk.texi \ + $(srcdir)/libjitext-plus-context.texi \ + $(srcdir)/libjitext-plus-function.texi \ + $(srcdir)/libjitext-plus-value.texi + +$(srcdir)/libjitext-alloc.texi: $(top_srcdir)/jit/jit-alloc.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-apply.texi: $(top_srcdir)/jit/jit-apply.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-block.texi: $(top_srcdir)/jit/jit-block.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-context.texi: $(top_srcdir)/jit/jit-context.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-dynlib.texi: $(top_srcdir)/jit/jit-dynlib.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-elf-read.texi: $(top_srcdir)/jit/jit-elf-read.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-except.texi: $(top_srcdir)/jit/jit-except.cpp + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-function.texi: $(top_srcdir)/jit/jit-function.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-init.texi: $(top_srcdir)/jit/jit-init.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-insn.texi: $(top_srcdir)/jit/jit-insn.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-intrinsic.texi: $(top_srcdir)/jit/jit-intrinsic.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-memory.texi: $(top_srcdir)/jit/jit-memory.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-meta.texi: $(top_srcdir)/jit/jit-meta.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-reg-alloc.texi: $(top_srcdir)/jit/jit-reg-alloc.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-rules-interp.texi: $(top_srcdir)/jit/jit-rules-interp.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-string.texi: $(top_srcdir)/jit/jit-string.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-type.texi: $(top_srcdir)/jit/jit-type.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-value.texi: $(top_srcdir)/jit/jit-value.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-walk.texi: $(top_srcdir)/jit/jit-walk.c + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-plus-context.texi: \ + $(top_srcdir)/jitplus/jit-plus-context.cpp + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-plus-function.texi: \ + $(top_srcdir)/jitplus/jit-plus-function.cpp + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +$(srcdir)/libjitext-plus-value.texi: \ + $(top_srcdir)/jitplus/jit-plus-value.cpp + $(SHELL) $(srcdir)/extract-docs.sh $< >$@ + +CLEANFILES = $(srcdir)/libjit.info $(srcdir)/libjit.info-* \ + $(srcdir)/libjitext-*.texi diff --git a/doc/extract-docs.sh b/doc/extract-docs.sh new file mode 100755 index 0000000..ccc8311 --- /dev/null +++ b/doc/extract-docs.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# +# extract-docs.sh - Extract texinfo documentation from a source file. +# +# Usage: extract-docs.sh source.c >output.texi +# +# Copyright (C) 2004 Southern Storm Software, Pty Ltd. +# +# 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 + +in_doc=false +echo '' +echo "@c Extracted automatically from $1 - DO NOT EDIT" +echo '' +while read LINE ; do + case "$LINE" in + /\*@*) in_doc=true ;; + @\*/*) echo '' + in_doc=false ;; + *) if test "$in_doc" = true ; then + echo "$LINE" + fi ;; + esac +done < "$1" | sed -e '1,$s/^ *\* *//' + +exit 0 diff --git a/doc/libjit.3 b/doc/libjit.3 new file mode 100644 index 0000000..fe31480 --- /dev/null +++ b/doc/libjit.3 @@ -0,0 +1,49 @@ +.\" Copyright (c) 2004 Southern Storm Software, Pty Ltd. +.\" +.\" 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 +.TH libjit 3 "18 April 2004" "Southern Storm Software" "Just-In-Time Compiler Library" +.SH NAME +libjit \- Just-In-Time Compiler Library +.SH SYNOPSIS +.ll +8 +.B #include + +Link with +.B -ljit +.SH DESCRIPTION +The \fBlibjit\fR library has an extensive set of routines that takes care +of the bulk of the Just-In-Time compilation process, without tying the +programmer down with language or bytecode specifics. + +Unlike other systems such as the JVM, .NET, Parrot, and LLVM, \fBlibjit\fR +is not a virtual machine in its own right. It is the foundation upon which a +number of different virtual machines, dynamic scripting languages, etc, +can be built. +.SH "AUTHOR" +Written by Southern Storm Software, Pty Ltd. + +http://www.southern-storm.com.au/ +.SH "SEE ALSO" +The full documentation for +.B libjit +is maintained as a Texinfo manual. If the +.B info +and +.B libjit +programs are properly installed at your site, the command +.IP +.B info libjit +.PP +should give you access to the complete manual. diff --git a/doc/libjit.texi b/doc/libjit.texi new file mode 100644 index 0000000..8fd4c35 --- /dev/null +++ b/doc/libjit.texi @@ -0,0 +1,1247 @@ +\input texinfo @c -*-texinfo-*- +@c %** start of header +@setfilename libjit.info +@settitle Just-In-Time Compiler Library +@setchapternewpage off +@c %** end of header + +@dircategory Libraries +@direntry +* Libjit: (libjit). Just-In-Time Compiler Library +@end direntry + +@ifinfo +The libjit library assists with the process of building +Just-In-Time compilers for languages, virtual machines, +and emulators. + +Copyright @copyright{} 2004 Southern Storm Software, Pty Ltd +@end ifinfo + +@titlepage +@sp 10 +@center @titlefont{Just-In-Time Compiler Library} + +@vskip 0pt plus 1fill +@center{Copyright @copyright{} 2004 Southern Storm Software, Pty Ltd} +@end titlepage + +@syncodeindex fn cp +@syncodeindex vr cp +@syncodeindex tp cp + +@c ----------------------------------------------------------------------- + +@node Top, Introduction, , (dir) +@menu +* Introduction:: Introduction and rationale for libjit +* Features:: Features of libjit +* Tutorials:: Tutorials in using libjit +* Initialization:: Initializing the JIT +* Functions:: Building and compiling functions with the JIT +* Types:: Manipulating system types +* Values:: Working with temporary values in the JIT +* Instructions:: Working with instructions in the JIT +* Basic Blocks:: Working with basic blocks in the JIT +* Intrinsics:: Intrinsic functions available to libjit users +* Exceptions:: Handling exceptions +* ELF Binaries:: Manipulating ELF binaries +* Utility Routines:: Miscellaneous utility routines +* C++ Interface:: Using libjit from C++ +* Porting:: Porting libjit to new architectures +* Why GPL?:: Why we use GPL and not LGPL for libjit +* Index:: Index of concepts and facilities +@end menu + +@c ----------------------------------------------------------------------- + +@node Introduction, Features, Top, Top +@chapter Introduction and rationale for libjit +@cindex Introduction + +Just-In-Time compilers are becoming increasingly popular for executing +dynamic languages like Perl and Python and for semi-dynamic languages +like Java and C#. Studies have shown that JIT techniques can get close to, +and sometimes exceed, the performance of statically-compiled native code. + +However, there is a problem with current JIT approaches. In almost every +case, the JIT is specific to the object model, runtime support library, +garbage collector, or bytecode peculiarities of a particular system. +This inevitably leads to duplication of effort, where all of the good +JIT work that has gone into one virtual machine cannot be reused in another. + +JIT's are not only useful for implementing languages. They can also be used +in other programming fields. Graphical applications can achieve greater +performance if they can compile a special-purpose rendering routine +on the fly, customized to the rendering task at hand, rather than using +static routines. Needless to say, such applications have no need for +object models, garbage collectors, or huge runtime class libraries. + +Most of the work on a JIT is concerned with arithmetic, numeric type +conversion, memory loads/stores, looping, performing data flow analysis, +assigning registers, and generating the executable machine code. +Only a very small proportion of the work is concerned with language specifics. + +The goal of the @code{libjit} project is to provide an extensive set of +routines that takes care of the bulk of the JIT process, without tying the +programmer down with language specifics. Where we provide support for +common object models, we do so strictly in add-on libraries, +not as part of the core code. + +Unlike other systems such as the JVM, .NET, Parrot, and LLVM, @code{libjit} +is not a virtual machine in its own right. It is the foundation upon which a +number of different virtual machines, dynamic scripting languages, +or customized rendering routines can be built. + +This should free developers to think about the design of their front +ends, and not get bogged down in the details of code execution. +Meanwhile, experts in the design and implementation of JIT's can concentrate +on solving code execution problems, instead of front end support issues. + +This document describes how to use the library in application programs. +We start with a list of features and some simple tutorials. Finally, +we provide a complete reference guide for all of the API functions in +@code{libjit}, broken down by function category. + +@section Obtaining libjit + +The latest version of @code{libjit} can be obtained from Southern +Storm Software, Pty Ltd's Web site: + +@quotation +@uref{http://www.southern-storm.com.au/libjit.html} +@end quotation + +@section Further reading + +While it isn't strictly necessary to know about compiler internals +to use @code{libjit}, you can make more effective use of the library +if you do. We recommend the "Dragon Book" as an excellent resource +on compiler internals, particularly the sections on code generation +and optimization: + +@quotation +Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman, "Compilers: +Principles, Techniques, and Tools", Addison-Wesley, 1986. +@end quotation + +IBM, Intel, and others have done a lot of research into JIT implementation +techniques over the years. If you are interested in working on the +internals of @code{libjit}, then you may want to make yourself familiar +with the relevant literature (this is by no means a complete list): + +@quotation +IBM's Jikes RVM (Research Virtual Machine), @* +@uref{http://www-124.ibm.com/developerworks/oss/jikesrvm/}. + +Intel's ORP (Open Runtime Platform), @* +@uref{http://orp.sourceforge.net/}. +@end quotation + +@c ----------------------------------------------------------------------- + +@node Features, Tutorials, Introduction, Top +@chapter Features of libjit +@cindex Features + +@itemize +@item +The primary interface is in C, for maximal reusability. Class +interfaces are available for programmers who prefer C++. + +@item +Designed for portability to all major 32-bit and 64-bit platforms. + +@item +Simple three-address API for library users, but opaque enough that other +representations can be used inside the library in future without +affecting existing users. + +@item +Up-front or on-demand compilation of any function. + +@item +In-built support to re-compile functions with greater optimization, +automatically redirecting previous callers to the new version. + +@item +Fallback interpreter for running code on platforms that don't +have a native code generator yet. This reduces the need for +programmers to write their own interpreters for such platforms. + +@item +Arithmetic, bitwise, conversion, and comparison operators for 8-bit, +16-bit, 32-bit, or 64-bit integer types; and 32-bit, 64-bit, or longer +floating point types. Includes overflow detecting arithmetic for +integer types. + +@item +Large set of mathematical and trigonometric operations +(sqrt, sin, cos, min, abs, etc) for inlining floating-point library functions. + +@item +Simplified type layout and exception handling mechanisms, upon which a +variety of different object models can be built. + +@item +Support for nested functions, able to access their parent's local variables +(for implementing Pascal-style languages). +@end itemize + +@c ----------------------------------------------------------------------- + +@node Tutorials, Tutorial 1, Features, Top +@chapter Tutorials in using libjit +@cindex Tutorials + +In this chapter, we describe how to use @code{libjit} with a number of +short tutorial exercises. Full source for these tutorials can be found +in the @code{tutorial} directory of the @code{libjit} source tree. + +For simplicity, we will ignore errors such as out of memory conditions, +but a real program would be expected to handle such errors. + +@menu +* Tutorial 1:: Tutorial 1 - mul_add +* Tutorial 2:: Tutorial 2 - gcd +* Tutorial 3:: Tutorial 3 - compiling on-demand +* Tutorial 4:: Tutorial 4 - mul_add, C++ version +* Dynamic Pascal:: Dynamic Pascal - A full JIT example +@end menu + +@c ----------------------------------------------------------------------- + +@node Tutorial 1, Tutorial 2, Tutorials, Tutorials +@section Tutorial 1 - mul_add +@cindex mul_add tutorial + +In the first tutorial, we will build and compile the following function +(the source code can be found in @code{tutorial/t1.c}): + +@example +int mul_add(int x, int y, int z) +@{ + return x * y + z; +@} +@end example + +@noindent +To use the JIT, we first include the @code{} file: + +@example +#include +@end example + +All of the header files are placed into the @code{jit} sub-directory, +to separate them out from regular system headers. When @code{libjit} +is installed, you will typically find these headers in +@code{/usr/local/include/jit} or @code{/usr/include/jit}, depending upon +how your system is configured. + +You should also link with the @code{-ljit} option and use a C++ linker, +not a C linker (because @code{libjit} contains a small amount of C++ code). +If you are using Autotools, then you can force the use of a C++ linker +by adding @code{AC_PROG_CXX} to @code{configure.in} and adding the +following line to your @code{Makefile.am}: + +@example +CCLD = $(CXX) +@end example + +@noindent +Every program that uses @code{libjit} needs to call @code{jit_context_create}: + +@example +jit_context_t context; +... +context = jit_context_create(); +@end example + +Almost everything that is done with @code{libjit} is done relative +to a context. In particular, a context holds all of the functions +that you have built and compiled. + +You can have multiple contexts at any one time, but normally you will +only need one. Multiple contexts may be useful if you wish to +run multiple virtual machines side by side in the same process, +without them interfering with each other. + +Whenever we are constructing a function, we need to lock down the +context to prevent multiple threads from using the builder at a time: + +@example +jit_context_build_start(context); +@end example + +The next step is to construct the function object that will represent +our @code{mul_add} function: + +@example +jit_function_t function; +... +function = jit_function_create(context, signature); +@end example + +The @code{signature} is a @code{jit_type_t} object that describes the +function's parameters and return value. This tells @code{libjit} how +to generate the proper calling conventions for the function: + +@example +jit_type_t params[3]; +jit_type_t signature; +... +params[0] = jit_type_int; +params[1] = jit_type_int; +params[2] = jit_type_int; +signature = jit_type_create_signature + (jit_abi_cdecl, jit_type_int, params, 3, 1); +@end example + +This declares a function that takes three parameters of type +@code{int} and returns a result of type @code{int}. We've requested +that the function use the @code{cdecl} application binary interface (ABI), +which indicates normal C calling conventions. @xref{Types}, for +more information on signature types. + +Now that we have a function object, we need to construct the instructions +in its body. First, we obtain references to each of the function's +parameter values: + +@example +jit_value_t x, y, z; +... +x = jit_value_get_param(function, 0); +y = jit_value_get_param(function, 1); +z = jit_value_get_param(function, 2); +@end example + +Values are one of the two cornerstones of the @code{libjit} process. +Values represent parameters, local variables, and intermediate +temporary results. Once we have the parameters, we compute +the result of @code{x * y + z} as follows: + +@example +jit_value_t temp1, temp2; +... +temp1 = jit_insn_mul(function, x, y); +temp2 = jit_insn_add(function, temp1, z); +@end example + +This demonstrates the other cornerstone of the @code{libjit} process: +instructions. Each of these instructions takes two values as arguments +and returns a new temporary value with the result. + +Students of compiler design will notice that the above statements look +very suspiciously like the "three address statements" that are described +in compiler textbooks. And that is indeed what they are internally within +@code{libjit}. + +If you don't know what three address statements are, then don't worry. +The library hides most of the details from you. All you need to do is +break your code up into simple operation steps (addition, multiplication, +negation, copy, etc). Then perform the steps one at a time, using +the temporary values in subsequent steps. @xref{Instructions}, for +a complete list of all instructions that are supported by @code{libjit}. + +Now that we have computed the desired result, we return it to the caller +using @code{jit_insn_return}: + +@example +jit_insn_return(function, temp2); +@end example + +We have completed the process of building the function body. Now we +compile it into its executable form: + +@example +jit_function_compile(function); +jit_context_build_end(context); +@end example + +As a side-effect, this will discard all of the memory associated with +the values and instructions that we constructed while building the +function. They are no longer required, because we now have the +executable form that we require. + +We also unlock the context, because it is now safe for other threads +to access the function building process. + +Up until this point, we haven't executed the @code{mul_add} function. +All we have done is build and compile it, ready for execution. To execute it, +we call @code{jit_function_apply}: + +@example +jit_int arg1, arg2, arg3; +void *args[3]; +jit_int result; +... +arg1 = 3; +arg2 = 5; +arg3 = 2; +args[0] = &arg1; +args[1] = &arg2; +args[2] = &arg3; +jit_function_apply(function, args, &result); +printf("mul_add(3, 5, 2) = %d\n", (int)result); +@end example + +We pass an array of pointers to @code{jit_function_apply}, each one +pointing to the corresponding argument value. This gives us a very +general purpose mechanism for calling any function that may be +built and compiled using @code{libjit}. If all went well, the +program should print the following: + +@example +mul_add(3, 5, 2) = 17 +@end example + +You will notice that we used @code{jit_int} as the type of the arguments, +not @code{int}. The @code{jit_int} type is guaranteed to be 32 bits +in size on all platforms, whereas @code{int} varies in size from platform +to platform. Since we wanted our function to work the same everywhere, +we used a type with a predictable size. + +If you really wanted the system @code{int} type, you would use +@code{jit_type_sys_int} instead of @code{jit_type_int} when you +created the function's signature. The @code{jit_type_sys_int} type +is guaranteed to match the local system's @code{int} precision. + +@noindent +Finally, we clean up the context and all of the memory that was used: + +@example +jit_context_destroy(context); +@end example + +@c ----------------------------------------------------------------------- + +@node Tutorial 2, Tutorial 3, Tutorial 1, Tutorials +@section Tutorial 2 - gcd +@cindex gcd tutorial + +In this second tutorial, we implement the subtracting Euclidean +Greatest Common Divisor (GCD) algorithm over positive integers. +This tutorial demonstrates how to handle conditional branching +and function calls. In C, the code for the @code{gcd} function +is as follows: + +@example +unsigned int gcd(unsigned int x, unsigned int y) +@{ + if(x == y) + @{ + return x; + @} + else if(x < y) + @{ + return gcd(x, y - x); + @} + else + @{ + return gcd(x - y, y); + @} +@} +@end example + +The source code for this tutorial can be found in @code{tutorial/t2.c}. +Many of the details are similar to the previous tutorial. We omit +those details here and concentrate on how to build the function body. +@xref{Tutorial 1}, for more information. + +@noindent +We start by checking the condition @code{x == y}: + +@example +jit_value_t x, y, temp1; +... +x = jit_value_get_param(function, 0); +y = jit_value_get_param(function, 1); +temp1 = jit_insn_eq(function, x, y); +@end example + +This is very similar to our previous tutorial, except that we are using +the @code{eq} operator this time. If the condition is not true, we +want to skip the @code{return} statement. We achieve this with the +@code{jit_insn_branch_if_not} instruction: + +@example +jit_label_t label1 = jit_label_undefined; +... +jit_insn_branch_if_not(function, temp1, &label1); +@end example + +The label must be initialized to @code{jit_label_undefined}. It will be +updated by @code{jit_insn_branch_if_not} to refer to a future position in +the code that we haven't seen yet. + +If the condition is true, then execution falls through to the next +instruction where we return @code{x} to the caller: + +@example +jit_insn_return(function, x); +@end example + +If the condition was not true, then we branched to @code{label1} above. +We fix the location of the label using @code{jit_insn_label}: + +@example +jit_insn_label(function, &label1); +@end example + +@noindent +We use similar code to check the condition @code{x < y}, and branch +to @code{label2} if it is not true: + +@example +jit_value_t temp2; +jit_label_t label2 = jit_label_undefined; +... +temp2 = jit_insn_lt(function, x, y); +jit_insn_branch_if_not(function, temp2, &label2); +@end example + +At this point, we need to call the @code{gcd} function with the +arguments @code{x} and @code{y - x}. The code for this is +fairly straight-forward. The @code{jit_insn_call} instruction calls +the function listed in its third argument. In this case, we are calling +ourselves recursively: + +@example +jit_value_t temp_args[2]; +jit_value_t temp3; +... +temp_args[0] = x; +temp_args[1] = jit_insn_sub(function, y, x); +temp3 = jit_insn_call + (function, "gcd", function, 0, temp_args, 2, 0); +jit_insn_return(function, temp3); +@end example + +The string @code{"gcd"} in the second argument is for diagnostic purposes +only. It can be helpful when debugging, but the @code{libjit} library +otherwise makes no use of it. You can set it to NULL if you wish. + +In general, @code{libjit} does not maintain mappings from names to +@code{jit_function_t} objects. It is assumed that the front end will +take care of that, using whatever naming scheme is appropriate to +its needs. + +@noindent +The final part of the @code{gcd} function is similar to the previous one: + +@example +jit_value_t temp4; +... +jit_insn_label(function, &label2); +temp_args[0] = jit_insn_sub(function, x, y); +temp_args[1] = y; +temp4 = jit_insn_call + (function, "gcd", function, 0, temp_args, 2, 0); +jit_insn_return(function, temp4); +@end example + +@noindent +We can now compile the function and execute it in the usual manner. + +@c ----------------------------------------------------------------------- + +@node Tutorial 3, Tutorial 4, Tutorial 2, Tutorials +@section Tutorial 3 - compiling on-demand +@cindex On-demand compilation tutorial + +In the previous tutorials, we compiled everything that we needed +at startup time, and then entered the execution phase. The real power +of a JIT becomes apparent when you use it to compile functions +only as they are called. You can thus avoid compiling functions +that are never called in a given program run, saving memory and +startup time. + +We demonstrate how to do on-demand compilation by rewriting Tutorial 1. +The source code for the modified version is in @code{tutorial/t3.c}. + +When the @code{mul_add} function is created, we don't create its function +body or call @code{jit_function_compile}. We instead provide a +C function called @code{compile_mul_add} that performs on-demand +compilation: + +@example +jit_function_t function; +... +function = jit_function_create(context, signature); +jit_function_set_on_demand_compiler(function, compile_mul_add); +@end example + +We can now call this function with @code{jit_function_apply}, and the +system will automatically call @code{compile_mul_add} for us if the +function hasn't been built yet. The contents of @code{compile_mul_add} +are fairly obvious: + +@example +int compile_mul_add(jit_function_t function) +@{ + jit_value_t x, y, z; + jit_value_t temp1, temp2; + + x = jit_value_get_param(function, 0); + y = jit_value_get_param(function, 1); + z = jit_value_get_param(function, 2); + + temp1 = jit_insn_mul(function, x, y); + temp2 = jit_insn_add(function, temp1, z); + + jit_insn_return(function, temp2); + return 1; +@} +@end example + +When the on-demand compiler returns, @code{libjit} will call +@code{jit_function_compile} and then jump to the newly compiled code. +Upon the second and subsequent calls to the function, @code{libjit} +will bypass the on-demand compiler and call the compiled code directly. + +Sometimes you may wish to force a commonly used function to +be recompiled, so that you can apply additional optimization. +To do this, you must set the "recompilable" flag just after the +function is first created: + +@example +jit_function_t function; +... +function = jit_function_create(context, signature); +jit_function_set_recompilable(function); +jit_function_set_on_demand_compiler(function, compile_mul_add); +@end example + +Then, you force the function to be recompiled with a call to +@code{jit_function_recompile}: + +@example +jit_function_recompile(function); +@end example + +After this, any existing references to the function will be redirected +to the new version. However, if some thread is currently executing the +previous version, then it will keep doing so until the previous version +exits. Only after that will subsequent calls go to the new version. + +In this tutorial, we use the same on-demand compiler when we +recompile @code{mul_add}. In a real program, you would probably call +@code{jit_function_set_on_demand_compiler} to set a new on-demand +compiler that performs greater levels of optimization. + +If you no longer intend to recompile the function, you should call +@code{jit_function_clear_recompilable} so that @code{libjit} can +manage the function more efficiently from then on. + +The exact conditions under which a function should be recompiled +are not specified by @code{libjit}. It may be because the function +has been called several times and has reached some threshold. +Or it may be because some other function that it calls has become a +candidate for inlining. It is up to the front end to decide when +recompilation is warranted, usually based on language-specific +heuristics. + +@c ----------------------------------------------------------------------- + +@node Tutorial 4, Dynamic Pascal, Tutorial 3, Tutorials +@section Tutorial 4 - mul_add, C++ version +@cindex mul_add C++ tutorial + +While @code{libjit} can be easily accessed from C++ programs using +the C API's, you may instead wish to use an API that better reflects +the C++ programming paradigm. We demonstrate how to do this by rewriting +Tutorial 3 using the @code{libjitplus} library. + +@noindent +To use the @code{libjitplus} library, we first include +the @code{} file: + +@example +#include +@end example + +This file incorporates all of the definitions from @code{}, +so you have full access to the underlying C API if you need it. + +This time, instead of building the @code{mul_add} function with +@code{jit_function_create} and friends, we define a class to represent it: + +@example +class mul_add_function : public jit_function +@{ +public: + mul_add_function(jit_context& context) : jit_function(context) + @{ + create(); + set_recompilable(); + @} + +protected: + virtual jit_type_t create_signature(); + virtual void build(); +@}; +@end example + +Where we used @code{jit_function_t} and @code{jit_context_t} before, +we now use the C++ @code{jit_function} and @code{jit_context} classes. + +In our constructor, we attach ourselves to the context and then call +the @code{create()} method. This is in turn will call our overridden +virtual method @code{create_signature()} to obtain the signature: + +@example +jit_type_t mul_add_function::create_signature() +@{ + // Return type, followed by three parameters, + // terminated with "end_params". + return signature_helper + (jit_type_int, jit_type_int, jit_type_int, + jit_type_int, end_params); +@} +@end example + +The @code{signature_helper()} method is provided for your convenience, +to help with building function signatures. You can create your own +signature manually using @code{jit_type_create_signature} if you wish. + +The final thing we do in the constructor is call @code{set_recompilable()} +to mark the @code{mul_add} function as recompilable, just as we did in +Tutorial 3. + +The C++ library will create the function as compilable on-demand for +us, so we don't have to do that explicitly. But we do have to override +the virtual @code{build()} method to build the function's body on-demand: + +@example +void mul_add_function::build() +@{ + jit_value x = get_param(0); + jit_value y = get_param(1); + jit_value z = get_param(2); + + insn_return(x * y + z); +@} +@end example + +This is similar to the first version that we wrote in Tutorial 1. +Instructions are created with @code{insn_*} methods that correspond +to their @code{jit_insn_*} counterparts in the C library. + +One of the nice things about the C++ API compared to the C API is that we +can use overloaded operators to manipulate @code{jit_value} objects. +This can simplify the function build process considerably when we +have lots of expressions to compile. We could have used @code{insn_mul} +and @code{insn_add} instead in this example and the result would have +been the same. + +Now that we have our @code{mul_add_function} class, we can create +an instance of the function and apply it as follows: + +@example +jit_context context; +mul_add_function mul_add(context); + +jit_int arg1 = 3; +jit_int arg2 = 5; +jit_int arg3 = 2; +jit_int args[3]; +args[0] = &arg1; +args[1] = &arg2; +args[2] = &arg3; + +mul_add.apply(args, &result); +@end example + +@noindent +@xref{C++ Interface}, for more information on the @code{libjitplus} +library. + +@c ----------------------------------------------------------------------- + +@node Dynamic Pascal, Initialization, Tutorial 4, Tutorials +@section Dynamic Pascal - A full JIT example +@cindex Dynamic Pascal + +This @code{libjit/dpas} directory contains an implementation of +"Dynamic Pascal", or "dpas" as we like to call it. It is provided +as an example of using @code{libjit} in a real working environment. +We also use it to write test programs that exercise the JIT's capabilities. + +Other Pascal implementations compile the source to executable form, +which is then run separately. Dynamic Pascal loads the source code +at runtime, dynamically JIT'ing the program as it goes. It thus has +a lot in common with scripting languages like Perl and Python. + +If you are writing a bytecode-based virtual machine, you would use +a similar approach to Dynamic Pascal. The key difference is that +you would build the JIT data structures after loading the bytecode +rather than after parsing the source code. + +To run a Dynamic Pascal program, use @code{dpas name.pas}. You may also +need to pass the @code{-I} option to specify the location of the system +library if you have used an @code{import} clause in your program. e.g. +@code{dpas -I$HOME/libjit/dpas/library name.pas}. + +@noindent +This Pascal grammar is based on the EBNF description at the following URL: + +@uref{http://www.cs.qub.ac.uk/~S.Fitzpatrick/Teaching/Pascal/EBNF.html} + +@noindent +There are a few differences to "Standard Pascal": + +@enumerate +@item +Identifiers are case-insensitive, but case-preserving. + +@item +Program headings are normally @code{program Name (Input, Output);}. This can +be abbreviated to @code{program Name;} as the program modifiers are ignored. + +@item +Some GNU Pascal operators like @code{xor}, @code{shl}, @code{@@}, etc +have been added. + +@item +The integer type names (@code{Integer}, @code{Cardinal}, @code{LongInt}, etc) +follow those used in GNU Pascal also. The @code{Integer} type is always +32-bits in size, while @code{LongInt} is always 64-bits in size. + +@item +The types @code{SysInt}, @code{SysCard}, @code{SysLong}, @code{SysLongCard}, +@code{SysLongestInt}, and @code{SysLongestCard} are guaranteed to be the +same size as the underlying C system's @code{int}, @code{unsigned int}, +@code{long}, @code{unsigned long}, @code{long long}, and +@code{unsigned long long} types. + +@item +The type @code{Address} is logically equivalent to C's @code{void *}. +Any pointer or array can be implicitly cast to @code{Address}. An explicit +cast is required to cast back to a typed pointer (you cannot cast back +to an array). + +@item +The @code{String} type is declared as @code{^Char}. Single-dimensional +arrays of @code{Char} can be implicitly cast to any @code{String} +destination. Strings are not bounds-checked, so be careful. Arrays +are bounds-checked. + +@item +Pointers can be used as arrays. e.g. @code{p[n]} will access the n'th +item of an unbounded array located at @code{p}. Use with care. + +@item +We don't support @code{file of} types. Data can be written to stdout +using @code{Write} and @code{WriteLn}, but that is the extent of +the I/O facilities. + +@item +The declaration @code{import Name1, Name2, ...;} can be used at the head of a +program to declare additional files to include. e.g. @code{import stdio} will +import the contents of @code{stdio.pas}. We don't support units. + +@item +The idiom @code{; ..} can be used at the end of a formal parameter list to +declare that the procedure or function takes a variable number of arguments. +The builtin function @code{va_arg(Type)} is used to extract the arguments. + +@item +The directive @code{import("Library")} can be used to declare that a function +or procedure was imported from an external C library. For example, the +following imports the C @code{puts} and @code{printf} functions: + +@example +function puts (str : String) : SysInt; import ("libc") +function printf (format : String; ..) : SysInt; import ("libc") +@end example + +Functions that are imported in this manner have case-sensitive names. +i.e. using @code{Printf} above will fail. + +@item +The @code{throw} keyword can be used to throw an exception. The argument +must be a pointer. The @code{try}, @code{catch}, and @code{finally} +keywords are used to manage such exceptions further up the stack. e.g. + +@example +try + ... +catch Name : Type + ... +finally + ... +end +@end example + +The @code{catch} block will be invoked with the exception pointer that was +supplied to @code{throw}, after casting it to @code{Type} (which must +be a pointer type). Specifying @code{throw} on its own without an argument +will rethrow the current exception pointer, and can only be used inside a +@code{catch} block. + +Dynamic Pascal does not actually check the type of the thrown pointer. +If you have multiple kinds of exceptions, then you must store some kind +of type indicator in the block that is thrown and then inspect @code{^Name} +to see what the indicator says. + +@item +The @code{exit} keyword can be used to break out of a loop. + +@item +Function calls can be used as procedure calls. The return value is ignored. + +@item +Hexadecimal constants can be expressed as @code{XXH}. The first digit +must be between 0 and 9, but the remaining digits can be any hex digit. + +@item +Ternary conditionals can be expressed as @code{(if e1 then e2 else e3)}. +The brackets are required. This is equivalent to C's @code{e1 ? e2 : e3}. + +@item +Assigning to a function result will immediately return. i.e. it is +similar to @code{return value;} in C. It isn't necessary to arrange for +execution to flow through to the end of the function as in regular Pascal. + +@item +The term @code{sizeof(Type)} can be used to get the size of a type. + +@item +Procedure and function headings can appear in a record type to declare a +field with a @code{pointer to procedure/function} type. +@end enumerate + +@c ----------------------------------------------------------------------- + +@node Initialization, Functions, Dynamic Pascal, Top +@chapter Initializing the JIT +@cindex Initialization +@cindex Contexts + +@include libjitext-init.texi +@include libjitext-context.texi + +@c ----------------------------------------------------------------------- + +@node Functions, Types, Initialization, Top +@chapter Building and compiling functions with the JIT +@cindex Building functions +@cindex Compiling functions + +@include libjitext-function.texi + +@c ----------------------------------------------------------------------- + +@node Types, Values, Functions, Top +@chapter Manipulating system types +@cindex Manipulating system types + +@include libjitext-type.texi + +@c ----------------------------------------------------------------------- + +@node Values, Instructions, Types, Top +@chapter Working with temporary values in the JIT +@cindex Working with values + +@include libjitext-value.texi + +@c ----------------------------------------------------------------------- + +@node Instructions, Basic Blocks, Values, Top +@chapter Working with instructions in the JIT +@cindex Working with instructions + +@include libjitext-insn.texi + +@c ----------------------------------------------------------------------- + +@node Basic Blocks, Intrinsics, Instructions, Top +@chapter Working with basic blocks in the JIT +@cindex Working with basic blocks + +@include libjitext-block.texi + +@c ----------------------------------------------------------------------- + +@node Intrinsics, Exceptions, Basic Blocks, Top +@chapter Intrinsic functions available to libjit users +@cindex Intrinsics + +@include libjitext-intrinsic.texi + +@c ----------------------------------------------------------------------- + +@node Exceptions, ELF Binaries, Intrinsics, Top +@chapter Handling exceptions +@cindex Handling exceptions + +@include libjitext-except.texi + +@c ----------------------------------------------------------------------- + +@node ELF Binaries, Utility Routines, Exceptions, Top +@chapter Manipulating ELF binaries +@cindex ELF binaries + +@include libjitext-elf-read.texi + +@c ----------------------------------------------------------------------- + +@node Utility Routines, C++ Interface, ELF Binaries, Top +@chapter Miscellaneous utility routines +@cindex Utility routines +@cindex jit-util.h + +The @code{libjit} library provides a number of utility routines +that it itself uses internally, but which may also be useful to front ends. + +@include libjitext-alloc.texi +@include libjitext-memory.texi +@include libjitext-string.texi +@include libjitext-meta.texi +@include libjitext-apply.texi +@include libjitext-walk.texi +@include libjitext-dynlib.texi + +@c ----------------------------------------------------------------------- + +@node C++ Interface, C++ Contexts, Utility Routines, Top +@chapter Using libjit from C++ +@cindex Using libjit from C++ + +This chapter describes the classes and methods that are available +in the @code{libjitplus} library. To use this library, you must +include the header @code{} and link with the +@code{-ljitplus} and @code{-ljit} options. + +@menu +* C++ Contexts:: Contexts in C++ +* C++ Values:: Values in C++ +* C++ Functions:: Functions in C++ +@end menu + +@c ----------------------------------------------------------------------- + +@node C++ Contexts, C++ Values, C++ Interface, C++ Interface +@chapter Contexts in C++ +@cindex C++ contexts + +@include libjitext-plus-context.texi + +@c ----------------------------------------------------------------------- + +@node C++ Values, C++ Functions, C++ Contexts, C++ Interface +@chapter Values in C++ +@cindex C++ values + +@include libjitext-plus-value.texi + +@c ----------------------------------------------------------------------- + +@node C++ Functions, Porting, C++ Values, C++ Interface +@chapter Functions in C++ +@cindex C++ functions + +@include libjitext-plus-function.texi + +@c ----------------------------------------------------------------------- + +@node Porting, Porting Apply, C++ Functions, Top +@chapter Porting libjit to new architectures +@cindex Porting libjit + +This chapter describes what needs to be done to port @code{libjit} +to a new CPU architecture. It is assumed that the reader is familiar +with compiler implementation techniques and the particulars of their +target CPU's instruction set. + +We will use @code{ARCH} to represent the name of the architecture +in the sections that follow. It is usually the name of the CPU in +lower case (e.g. @code{x86}, @code{arm}, @code{ppc}, etc). By +convention, all back end functions should be prefixed with @code{_jit}, +because they are not part of the public API. + +@menu +* Porting Apply:: Porting the function apply facility +* Instruction Generation:: Creating the instruction generation macros +* Architecture Rules:: Writing the architecture definition rules +* Register Allocation:: Allocating registers in the back end +@end menu + +@c ----------------------------------------------------------------------- + +@node Porting Apply, Instruction Generation, Porting, Porting +@section Porting the function apply facility +@cindex Porting apply + +The first step in porting @code{libjit} to a new architecture is to port +the @code{jit_apply} facility. This provides support for calling +arbitrary C functions from your application or from JIT'ed code. +If you are familiar with @code{libffi} or @code{ffcall}, then +@code{jit_apply} provides a similar facility. + +Even if you don't intend to write a native code generator, you will +probably still need to port @code{jit_apply} to each new architecture. + +The @code{libjit} library makes use of gcc's @code{__builtin_apply} +facility to do most of the hard work of function application. +This gcc facility takes three arguments: a pointer to the function +to invoke, a structure containing register arguments, and a size +value that indicates the number of bytes to push onto the stack +for the call. + +Unfortunately, the register argument structure is very system dependent. +There is no standard format for it, but it usually looks something +like this: + +@table @code +@item stack_args +Pointer to an array of argument values to push onto the stack. + +@item struct_ptr +Pointer to the buffer to receive a @code{struct} return value. +The @code{struct_ptr} field is only present if the architecture +passes @code{struct} pointers in a special register. + +@item word_reg[0..N] +Values for the word registers. Platforms that pass values in +registers will populate these fields. Not present if the architecture +does not use word registers for function calls. + +@item float_reg[0..N] +Values for the floating-point registers. Not present if the architecture +does not use floating-point registers for function calls. +@end table + +It is possible to automatically detect the particulars of this structure +by making test function calls and inspecting where the arguments end up +in the structure. The @code{gen-apply} program in @code{libjit/tools} +takes care of this. It outputs the @code{jit-apply-rules.h} file, +which tells @code{jit_apply} how to operate. + +The @code{gen-apply} program will normally "just work", but it is possible +that some architectures will be stranger than usual. You will need to modify +@code{gen-apply} to detect this additional strangeness, and perhaps +also modify @code{libjit/jit/jit-apply.c}. + +If you aren't using gcc to compile @code{libjit}, then things may +not be quite this easy. You may have to write some inline assembly +code to emulate @code{__builtin_apply}. See the file +@code{jit-apply-x86.h} for an example of how to do this. +Be sure to add an @code{#include} line to @code{jit-apply-func.h} +once you do this. + +The other half of @code{jit_apply} is closure and redirector support. +Closures are used to wrap up interpreted functions so that they can be +called as regular C functions. Redirectors are used to help compile a +JIT'ed function on-demand, and then redirect control to it. + +Unfortunately, you will have to write some assembly code to support +closures and redirectors. The builtin gcc facilities are not complete +enough to handle the task. See @code{jit-apply-x86.c} and +@code{jit-apply-arm.c} for some examples from existing architectures. +You may be able to get some ideas from the @code{libffi} and +@code{ffcall} libraries as to what you need to do on your architecture. + +@c ----------------------------------------------------------------------- + +@node Instruction Generation, Architecture Rules, Porting Apply, Porting +@section Creating the instruction generation macros +@cindex Instruction generation macros + +You will need a large number of macros and support functions to +generate the raw instructions for your chosen CPU. These macros are +fairly generic and are not necessarily specific to @code{libjit}. +There may already be a suitable set of macros for your CPU in +some other Free Software project. + +Typically, the macros are placed into a file called @code{jit-gen-ARCH.h} +in the @code{libjit/jit} directory. If some of the macros are complicated, +you can place helper functions into the file @code{jit-gen-ARCH.c}. +Remember to add both @code{jit-gen-ARCH.h} and @code{jit-gen-ARCH.c} +to @code{Makefile.am} in @code{libjit/jit}. + +Existing examples that you can look at for ideas are @code{jit-gen-x86.h} +and @code{jit-gen-arm.h}. The macros in these existing files assume that +instructions can be output to a buffer in a linear fashion, and that each +instruction is relatively independent of the next. + +This independence principle may not be true of all CPU's. For example, +the @code{ia64} packs up to three instructions into a single "bundle" +for parallel execution. We recommend that the macros should appear to +use linear output, but call helper functions to pack bundles after the fact. +This will make it easier to write the architecture definition rules. +A similar approach could be used for performing instruction scheduling +on platforms that require it. + +@c ----------------------------------------------------------------------- + +@node Architecture Rules, Register Allocation, Instruction Generation, Porting +@section Writing the architecture definition rules +@cindex Architecture definition rules + +@include libjitext-rules-interp.texi + +@c ----------------------------------------------------------------------- + +@node Register Allocation, Why GPL?, Architecture Rules, Porting +@section Allocating registers in the back end +@cindex Register allocation + +@include libjitext-reg-alloc.texi + +@c ----------------------------------------------------------------------- + +@node Why GPL?, Index, Register Allocation, Top +@chapter Why we use GPL and not LGPL for libjit +@cindex Why GPL? + +A common question about @code{libjit} is likely to be why we use the +GNU General Public License instead of the GNU Lesser General Public +License. + +At its core, the issue is "Who are we helping gain extra value?". We wish +to help the authors of other Free Software projects, as well as those who +are working on Open Source projects with licenses that are compatible with +the GPL. But we do not wish to help proprietary software companies +without adequate compensation to the community. + +Consider the following scenario: a proprietary software company devises +a new programming language and a new virtual machine to go with it. +However, their implementation isn't very compelling to customers +because its bytecode interpreter is too slow. + +If @code{libjit} was distributed under the terms of the LGPL, the +company could link against the library and get a compelling implementation. +This would increase the sale value of their software considerably. +However, they wouldn't be compelled to give anything back to the +Free Software community in exchange for this extra value. + +Under the GPL, the company must either GPL their own code, write their +own JIT from scratch, or negotiate a separate license with the authors +of @code{libjit}. In the first instance, they give back to the community +the value they have used. In the second instance, they neither give value +to nor take value from the community. In the third instance, the authors +are compensated for their work, which allows those authors to continue +contributing Free Software to the community. + +For more information on why we have taken this stance, please read the +document "Why you shouldn't use the Library GPL for your next library" on +the Free Software Foundation's Web site: + +@quotation +@uref{http://www.gnu.org/licenses/why-not-lgpl.html}. +@end quotation + +@c ----------------------------------------------------------------------- + +@page + +@node Index, , Why GPL?, Top +@unnumbered Index of concepts and facilities + +@printindex cp + +@contents +@bye diff --git a/doc/mkhtml.sh b/doc/mkhtml.sh new file mode 100755 index 0000000..0af42f8 --- /dev/null +++ b/doc/mkhtml.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# mkhtml.sh - Make html documentation from Texinfo input. +# +# Usage: mkhtml outdir + +# Check the command-line. +if [ -z "$1" ]; then + echo "Usage: $0 outdir" + exit 1 +fi + +# Check that we are executed in the correct directory. +if [ ! -f libjit.texi ]; then + echo "Cannot find libjit.texi" + exit 1 +fi + +# Create the output directory. +if [ ! -d "$1" ]; then + mkdir "$1" +fi + +# Get the full pathname of the input file. +PATHNAME=`pwd`/libjit.texi + +# Change to the output directory and execute "texi2html". +cd "$1" +exec texi2html -split_chapter "$PATHNAME" diff --git a/doc/mkpdf.sh b/doc/mkpdf.sh new file mode 100755 index 0000000..84eae06 --- /dev/null +++ b/doc/mkpdf.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# +# mkpdf - Make the PDF version of texinfo documentation + +exec texi2dvi --pdf libjit.texi diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100644 index 0000000..aa52853 --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,5484 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{1999-01-05}% +% +% Copyright (C) 1985, 86, 88, 90, 91, 92, 93, 94, 95, 96, 97, 98 +% Free Software Foundation, Inc. +% +% This texinfo.tex file 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, or (at +% your option) any later version. +% +% This texinfo.tex file is distributed in the hope that it will be +% 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 texinfo.tex file; see the file COPYING. If not, write +% to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +% Boston, MA 02111-1307, USA. +% +% In other words, you are welcome to use, share and improve this program. +% You are forbidden to forbid anyone else to use, share and improve +% what you give them. Help stamp out software-hoarding! +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% ftp://ftp.gnu.org/pub/gnu/texinfo.tex +% /home/gd/gnu/doc/texinfo.tex on the GNU machines. +% (and all GNU mirrors, see http://www.gnu.org/order/ftp.html) +% ftp://tug.org/tex/texinfo.tex +% ftp://ctan.org/macros/texinfo/texinfo.tex +% (and all CTAN mirrors, finger ctan@ctan.org for a list). +% The texinfo.tex in the texinfo distribution itself could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. +% Please include a precise test case in each bug report, +% including a complete document with which we can reproduce the problem. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For simple +% manuals, however, you can get away with: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever, to process the dvi file. +% The extra runs of TeX get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + +% Save some parts of plain tex whose names we will redefine. + +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexi=\i +\let\ptexlbrace=\{ +\let\ptexrbrace=\} +\let\ptexstar=\* +\let\ptext=\t + +% We never want plain's outer \+ definition in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + + +\message{Basics,} +\chardef\other=12 + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortContents\undefined \gdef\putwordShortContents{Short Contents}\fi +\ifx\putwordTableofContents\undefined\gdef\putwordTableofContents{Table of Contents}\fi + +% Ignore a token. +% +\def\gobble#1{} + +\hyphenation{ap-pen-dix} +\hyphenation{mini-buf-fer mini-buf-fers} +\hyphenation{eshell} +\hyphenation{white-space} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen \bindingoffset +\newdimen \normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\ifx\eTeXversion\undefined +\def\loggingall{\tracingcommands2 \tracingstats2 + \tracingpages1 \tracingoutput1 \tracinglostchars1 + \tracingmacros2 \tracingparagraphs1 \tracingrestores1 + \showboxbreadth\maxdimen\showboxdepth\maxdimen +}% +\else +\def\loggingall{\tracingcommands3 \tracingstats2 + \tracingpages1 \tracingoutput1 \tracinglostchars1 + \tracingmacros2 \tracingparagraphs1 \tracingrestores1 + \tracingscantokens1 \tracingassigns1 \tracingifs1 + \tracinggroups1 \tracingnesting2 + \showboxbreadth\maxdimen\showboxdepth\maxdimen +}% +\fi + +% For @cropmarks command. +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \escapechar = `\\ % use backslash in output files. + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + \shipout\vbox{% + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingxxx.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 2\baselineskip + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \turnoffactive + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1 \unvbox#1 +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg#1{% + \let\next = #1% + \begingroup + \obeylines + \futurelet\temp\parseargx +} + +% If the next token is an obeyed space (from an @example environment or +% the like), remove it and recurse. Otherwise, we're done. +\def\parseargx{% + % \obeyedspace is defined far below, after the definition of \sepspaces. + \ifx\obeyedspace\temp + \expandafter\parseargdiscardspace + \else + \expandafter\parseargline + \fi +} + +% Remove a single space (as the delimiter token to the macro call). +{\obeyspaces % + \gdef\parseargdiscardspace {\futurelet\temp\parseargx}} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + % + % First remove any @c comment, then any @comment. + % Result of each macro is put in \toks0. + \argremovec #1\c\relax % + \expandafter\argremovecomment \the\toks0 \comment\relax % + % + % Call the caller's macro, saved as \next in \parsearg. + \expandafter\next\expandafter{\the\toks0}% + }% +} + +% Since all \c{,omment} does is throw away the argument, we can let TeX +% do that for us. The \relax here is matched by the \relax in the call +% in \parseargline; it could be more or less anything, its purpose is +% just to delimit the argument to the \c. +\def\argremovec#1\c#2\relax{\toks0 = {#1}} +\def\argremovecomment#1\comment#2\relax{\toks0 = {#1}} + +% \argremovec{,omment} might leave us with trailing spaces, though; e.g., +% @end itemize @c foo +% will have two active spaces as part of the argument with the +% `itemize'. Here we remove all active spaces from #1, and assign the +% result to \toks0. +% +% This loses if there are any *other* active characters besides spaces +% in the argument -- _ ^ +, for example -- since they get expanded. +% Fortunately, Texinfo does not define any such commands. (If it ever +% does, the catcode of the characters in questionwill have to be changed +% here.) But this means we cannot call \removeactivespaces as part of +% \argremovec{,omment}, since @c uses \parsearg, and thus the argument +% that \parsearg gets might well have any character at all in it. +% +\def\removeactivespaces#1{% + \begingroup + \ignoreactivespaces + \edef\temp{#1}% + \global\toks0 = \expandafter{\temp}% + \endgroup +} + +% Change the active space to expand to nothing. +% +\begingroup + \obeyspaces + \gdef\ignoreactivespaces{\obeyspaces\let =\empty} +\endgroup + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +%% These are used to keep @begin/@end levels from running away +%% Call \inENV within environments (after a \begingroup) +\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi} +\def\ENVcheck{% +\ifENV\errmessage{Still within an environment; press RETURN to continue} +\endgroup\fi} % This is not perfect, but it should reduce lossage + +% @begin foo is the same as @foo, for now. +\newhelp\EMsimple{Press RETURN to continue.} + +\outer\def\begin{\parsearg\beginxxx} + +\def\beginxxx #1{% +\expandafter\ifx\csname #1\endcsname\relax +{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else +\csname #1\endcsname\fi} + +% @end foo executes the definition of \Efoo. +% +\def\end{\parsearg\endxxx} +\def\endxxx #1{% + \removeactivespaces{#1}% + \edef\endthing{\the\toks0}% + % + \expandafter\ifx\csname E\endthing\endcsname\relax + \expandafter\ifx\csname \endthing\endcsname\relax + % There's no \foo, i.e., no ``environment'' foo. + \errhelp = \EMsimple + \errmessage{Undefined command `@end \endthing'}% + \else + \unmatchedenderror\endthing + \fi + \else + % Everything's ok; the right environment has been started. + \csname E\endthing\endcsname + \fi +} + +% There is an environment #1, but it hasn't been started. Give an error. +% +\def\unmatchedenderror#1{% + \errhelp = \EMsimple + \errmessage{This `@end #1' doesn't have a matching `@#1'}% +} + +% Define the control sequence \E#1 to give an unmatched @end error. +% +\def\defineunmatchedend#1{% + \expandafter\def\csname E#1\endcsname{\unmatchedenderror{#1}}% +} + + +% Single-spacing is done by various environments (specifically, in +% \nonfillstart and \quotations). +\newskip\singlespaceskip \singlespaceskip = 12.5pt +\def\singlespace{% + % Why was this kern here? It messes up equalizing space above and below + % environments. --karl, 6may93 + %{\advance \baselineskip by -\singlespaceskip + %\kern \baselineskip}% + \setleading \singlespaceskip +} + +%% Simple single-character @ commands + +% @@ prints an @ +% Kludge this until the fonts are right (grr). +\def\@{{\tt\char64}} + +% This is turned off because it was never documented +% and you can use @w{...} around a quote to suppress ligatures. +%% Define @` and @' to be the same as ` and ' +%% but suppressing ligatures. +%\def\`{{`}} +%\def\'{{'}} + +% Used to generate quoted braces. +\def\mylbrace {{\tt\char123}} +\def\myrbrace {{\tt\char125}} +\let\{=\mylbrace +\let\}=\myrbrace +\begingroup + % Definitions to produce actual \{ & \} command in an index. + \catcode`\{ = 12 \catcode`\} = 12 + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\@ = 0 \catcode`\\ = 12 + @gdef@lbracecmd[\{]% + @gdef@rbracecmd[\}]% +@endgroup + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @v @H. +\let\, = \c +\let\dotaccent = \. +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \t +\let\ubaraccent = \b +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown +% Plain TeX defines: @AA @AE @O @OE @L (and lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ptexi + \else\ifx\temp\jmacro \j + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=3000 } + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=3000 } + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=3000 } + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +\def\group{\begingroup + \ifnum\catcode13=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + % + % The \vtop we start below produces a box with normal height and large + % depth; thus, TeX puts \baselineskip glue before it, and (when the + % next line of text is done) \lineskip glue after it. (See p.82 of + % the TeXbook.) Thus, space below is not quite equal to space + % above. But it's pretty close. + \def\Egroup{% + \egroup % End the \vtop. + \endgroup % End the \group. + }% + % + \vtop\bgroup + % We have to put a strut on the last line in case the @group is in + % the midst of an example, rather than completely enclosing it. + % Otherwise, the interline space between the last line of the group + % and the first line afterwards is too small. But we can't put the + % strut in \Egroup, since there it would be on a line by itself. + % Hence this just inserts a strut at the beginning of each line. + \everypar = {\strut}% + % + % Since we have a strut on every line, we don't need any of TeX's + % normal interline spacing. + \offinterlineskip + % + % OK, but now we have to do something about blank + % lines in the input in @example-like environments, which normally + % just turn into \lisppar, which will insert no space now that we've + % turned off the interline space. Simplest is to make them be an + % empty paragraph. + \ifx\par\lisppar + \edef\par{\leavevmode \par}% + % + % Reset ^^M's definition to new definition of \par. + \obeylines + \fi + % + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\def\need{\parsearg\needx} + +% Old definition--didn't work. +%\def\needx #1{\par % +%% This method tries to make TeX break the page naturally +%% if the depth of the box does not fit. +%{\baselineskip=0pt% +%\vtop to #1\mil{\vfil}\kern -#1\mil\nobreak +%\prevdepth=-1000pt +%}} + +\def\needx#1{% + % Go into vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % Don't add any leading before our big empty box, but allow a page + % break, since the best break might be right here. + \allowbreak + \nointerlineskip + \vtop to #1\mil{\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak +} + +% @br forces paragraph break + +\let\br = \par + +% @dots{} output an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in a typewriter +% font as three actual period characters. +% +\def\dots{% + \leavevmode + \hbox to 1.5em{% + \hskip 0pt plus 0.25fil minus 0.25fil + .\hss.\hss.% + \hskip 0pt plus 0.5fil minus 0.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \leavevmode + \hbox to 2em{% + \hskip 0pt plus 0.25fil minus 0.25fil + .\hss.\hss.\hss.% + \hskip 0pt plus 0.5fil minus 0.5fil + }% + \spacefactor=3000 +} + + +% @page forces the start of a new page +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\def\exdent{\parsearg\exdentyyy} +\def\exdentyyy #1{{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break}} + +% This defn is used inside nofill environments such as @example. +\def\nofillexdent{\parsearg\nofillexdentyyy} +\def\nofillexdentyyy #1{{\advance \leftskip by -\exdentamount +\leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{TEXT} puts TEXT in the margin next to the current paragraph. + +\def\inmargin#1{% +\strut\vadjust{\nobreak\kern-\strutdepth + \vtop to \strutdepth{\baselineskip\strutdepth\vss + \llap{\rightskip=\inmarginspacing \vbox{\noindent #1}}\null}}} +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} + +%\hbox{{\rm#1}}\hfil\break}} + +% @include file insert text of that file as input. +% Allow normal characters that we make active in the argument (a file name). +\def\include{\begingroup + \catcode`\\=12 + \catcode`~=12 + \catcode`^=12 + \catcode`_=12 + \catcode`|=12 + \catcode`<=12 + \catcode`>=12 + \catcode`+=12 + \parsearg\includezzz} +% Restore active chars for included file. +\def\includezzz#1{\endgroup\begingroup + % Read the included file in a group so nested @include's work. + \def\thisfile{#1}% + \input\thisfile +\endgroup} + +\def\thisfile{} + +% @center line outputs that line, centered + +\def\center{\parsearg\centerzzz} +\def\centerzzz #1{{\advance\hsize by -\leftskip +\advance\hsize by -\rightskip +\centerline{#1}}} + +% @sp n outputs n lines of vertical space + +\def\sp{\parsearg\spxxx} +\def\spxxx #1{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment + +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} + +\let\c=\comment + +% @paragraphindent is defined for the Info formatting commands only. +\let\paragraphindent=\comment + +% Prevent errors for section commands. +% Used in @ignore and in failing conditionals. +\def\ignoresections{% +\let\chapter=\relax +\let\unnumbered=\relax +\let\top=\relax +\let\unnumberedsec=\relax +\let\unnumberedsection=\relax +\let\unnumberedsubsec=\relax +\let\unnumberedsubsection=\relax +\let\unnumberedsubsubsec=\relax +\let\unnumberedsubsubsection=\relax +\let\section=\relax +\let\subsec=\relax +\let\subsubsec=\relax +\let\subsection=\relax +\let\subsubsection=\relax +\let\appendix=\relax +\let\appendixsec=\relax +\let\appendixsection=\relax +\let\appendixsubsec=\relax +\let\appendixsubsection=\relax +\let\appendixsubsubsec=\relax +\let\appendixsubsubsection=\relax +\let\contents=\relax +\let\smallbook=\relax +\let\titlepage=\relax +} + +% Used in nested conditionals, where we have to parse the Texinfo source +% and so want to turn off most commands, in case they are used +% incorrectly. +% +\def\ignoremorecommands{% + \let\defcodeindex = \relax + \let\defcv = \relax + \let\deffn = \relax + \let\deffnx = \relax + \let\defindex = \relax + \let\defivar = \relax + \let\defmac = \relax + \let\defmethod = \relax + \let\defop = \relax + \let\defopt = \relax + \let\defspec = \relax + \let\deftp = \relax + \let\deftypefn = \relax + \let\deftypefun = \relax + \let\deftypevar = \relax + \let\deftypevr = \relax + \let\defun = \relax + \let\defvar = \relax + \let\defvr = \relax + \let\ref = \relax + \let\xref = \relax + \let\printindex = \relax + \let\pxref = \relax + \let\settitle = \relax + \let\setchapternewpage = \relax + \let\setchapterstyle = \relax + \let\everyheading = \relax + \let\evenheading = \relax + \let\oddheading = \relax + \let\everyfooting = \relax + \let\evenfooting = \relax + \let\oddfooting = \relax + \let\headings = \relax + \let\include = \relax + \let\lowersections = \relax + \let\down = \relax + \let\raisesections = \relax + \let\up = \relax + \let\set = \relax + \let\clear = \relax + \let\item = \relax +} + +% Ignore @ignore ... @end ignore. +% +\def\ignore{\doignore{ignore}} + +% Ignore @ifinfo, @ifhtml, @ifnottex, @html, @menu, and @direntry text. +% +\def\ifinfo{\doignore{ifinfo}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifnottex{\doignore{ifnottex}} +\def\html{\doignore{html}} +\def\menu{\doignore{menu}} +\def\direntry{\doignore{direntry}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory = \comment + +% Ignore text until a line `@end #1'. +% +\def\doignore#1{\begingroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define a command to swallow text until we reach `@end #1'. + % This @ is a catcode 12 token (that is the normal catcode of @ in + % this texinfo.tex file). We change the catcode of @ below to match. + \long\def\doignoretext##1@end #1{\enddoignore}% + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \catcode32 = 10 + % + % Ignore braces, too, so mismatched braces don't cause trouble. + \catcode`\{ = 9 + \catcode`\} = 9 + % + % We must not have @c interpreted as a control sequence. + \catcode`\@ = 12 + % + % Make the letter c a comment character so that the rest of the line + % will be ignored. This way, the document can have (for example) + % @c @end ifinfo + % and the @end ifinfo will be properly ignored. + % (We've just changed @ to catcode 12.) + \catcode`\c = 14 + % + % And now expand that command. + \doignoretext +} + +% What we do to finish off ignored text. +% +\def\enddoignore{\endgroup\ignorespaces}% + +\newif\ifwarnedobs\warnedobsfalse +\def\obstexwarn{% + \ifwarnedobs\relax\else + % We need to warn folks that they may have trouble with TeX 3.0. + % This uses \immediate\write16 rather than \message to get newlines. + \immediate\write16{} + \immediate\write16{***WARNING*** for users of Unix TeX 3.0!} + \immediate\write16{This manual trips a bug in TeX version 3.0 (tex hangs).} + \immediate\write16{If you are running another version of TeX, relax.} + \immediate\write16{If you are running Unix TeX 3.0, kill this TeX process.} + \immediate\write16{ Then upgrade your TeX installation if you can.} + \immediate\write16{ (See ftp://ftp.gnu.org/pub/gnu/TeX.README.)} + \immediate\write16{If you are stuck with version 3.0, run the} + \immediate\write16{ script ``tex3patch'' from the Texinfo distribution} + \immediate\write16{ to use a workaround.} + \immediate\write16{} + \global\warnedobstrue + \fi +} + +% **In TeX 3.0, setting text in \nullfont hangs tex. For a +% workaround (which requires the file ``dummy.tfm'' to be installed), +% uncomment the following line: +%%%%%\font\nullfont=dummy\let\obstexwarn=\relax + +% Ignore text, except that we keep track of conditional commands for +% purposes of nesting, up to an `@end #1' command. +% +\def\nestedignore#1{% + \obstexwarn + % We must actually expand the ignored text to look for the @end + % command, so that nested ignore constructs work. Thus, we put the + % text into a \vbox and then do nothing with the result. To minimize + % the change of memory overflow, we follow the approach outlined on + % page 401 of the TeXbook: make the current font be a dummy font. + % + \setbox0 = \vbox\bgroup + % Don't complain about control sequences we have declared \outer. + \ignoresections + % + % Define `@end #1' to end the box, which will in turn undefine the + % @end command again. + \expandafter\def\csname E#1\endcsname{\egroup\ignorespaces}% + % + % We are going to be parsing Texinfo commands. Most cause no + % trouble when they are used incorrectly, but some commands do + % complicated argument parsing or otherwise get confused, so we + % undefine them. + % + % We can't do anything about stray @-signs, unfortunately; + % they'll produce `undefined control sequence' errors. + \ignoremorecommands + % + % Set the current font to be \nullfont, a TeX primitive, and define + % all the font commands to also use \nullfont. We don't use + % dummy.tfm, as suggested in the TeXbook, because not all sites + % might have that installed. Therefore, math mode will still + % produce output, but that should be an extremely small amount of + % stuff compared to the main input. + % + \nullfont + \let\tenrm = \nullfont \let\tenit = \nullfont \let\tensl = \nullfont + \let\tenbf = \nullfont \let\tentt = \nullfont \let\smallcaps = \nullfont + \let\tensf = \nullfont + % Similarly for index fonts (mostly for their use in + % smallexample) + \let\indrm = \nullfont \let\indit = \nullfont \let\indsl = \nullfont + \let\indbf = \nullfont \let\indtt = \nullfont \let\indsc = \nullfont + \let\indsf = \nullfont + % + % Don't complain when characters are missing from the fonts. + \tracinglostchars = 0 + % + % Don't bother to do space factor calculations. + \frenchspacing + % + % Don't report underfull hboxes. + \hbadness = 10000 + % + % Do minimal line-breaking. + \pretolerance = 10000 + % + % Do not execute instructions in @tex + \def\tex{\doignore{tex}}% + % Do not execute macro definitions. + % `c' is a comment character, so the word `macro' will get cut off. + \def\macro{\doignore{ma}}% +} + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. Make sure the catcode of space is correct to avoid +% losing inside @example, for instance. +% +\def\set{\begingroup\catcode` =10 + \catcode`\-=12 \catcode`\_=12 % Allow - and _ in VAR. + \parsearg\setxxx} +\def\setxxx#1{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + \def\temp{#2}% + \ifx\temp\empty \global\expandafter\let\csname SET#1\endcsname = \empty + \else \setzzz{#1}#2\endsetzzz % Remove the trailing space \setxxx inserted. + \fi + \endgroup +} +% Can't use \xdef to pre-expand #2 and save some time, since \temp or +% \next or other control sequences that we've defined might get us into +% an infinite loop. Consider `@set foo @cite{bar}'. +\def\setzzz#1#2 \endsetzzz{\expandafter\gdef\csname SET#1\endcsname{#2}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\def\clear{\parsearg\clearxxx} +\def\clearxxx#1{\global\expandafter\let\csname SET#1\endcsname=\relax} + +% @value{foo} gets the text saved in variable foo. +% +{ + \catcode`\_ = \active + % + % We might end up with active _ or - characters in the argument if + % we're called from @code, as @code{@value{foo-bar_}}. So \let any + % such active characters to their normal equivalents. + \gdef\value{\begingroup + \catcode`\-=12 \catcode`\_=12 + \indexbreaks \let_\normalunderscore + \valuexxx} +} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we \let\value to this in \indexdummies). Ones +% whose names contain - or _ still won't work, but we can't do anything +% about that. The command has to be fully expandable, since the result +% winds up in the index file. This means that if the variable's value +% contains other Texinfo commands, it's almost certain it will fail +% (although perhaps we could fix that with sufficient work to do a +% one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +\def\ifset{\parsearg\ifsetxxx} +\def\ifsetxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifsetfail + \else + \expandafter\ifsetsucceed + \fi +} +\def\ifsetsucceed{\conditionalsucceed{ifset}} +\def\ifsetfail{\nestedignore{ifset}} +\defineunmatchedend{ifset} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +\def\ifclear{\parsearg\ifclearxxx} +\def\ifclearxxx #1{% + \expandafter\ifx\csname SET#1\endcsname\relax + \expandafter\ifclearsucceed + \else + \expandafter\ifclearfail + \fi +} +\def\ifclearsucceed{\conditionalsucceed{ifclear}} +\def\ifclearfail{\nestedignore{ifclear}} +\defineunmatchedend{ifclear} + +% @iftex, @ifnothtml, @ifnotinfo always succeed; we read the text +% following, through the first @end iftex (etc.). Make `@end iftex' +% (etc.) valid only after an @iftex. +% +\def\iftex{\conditionalsucceed{iftex}} +\def\ifnothtml{\conditionalsucceed{ifnothtml}} +\def\ifnotinfo{\conditionalsucceed{ifnotinfo}} +\defineunmatchedend{iftex} +\defineunmatchedend{ifnothtml} +\defineunmatchedend{ifnotinfo} + +% We can't just want to start a group at @iftex (for example) and end it +% at @end iftex, since then @set commands inside the conditional have no +% effect (they'd get reverted at the end of the group). So we must +% define \Eiftex to redefine itself to be its previous value. (We can't +% just define it to fail again with an ``unmatched end'' error, since +% the @ifset might be nested.) +% +\def\conditionalsucceed#1{% + \edef\temp{% + % Remember the current value of \E#1. + \let\nece{prevE#1} = \nece{E#1}% + % + % At the `@end #1', redefine \E#1 to be its previous value. + \def\nece{E#1}{\let\nece{E#1} = \nece{prevE#1}}% + }% + \temp +} + +% We need to expand lots of \csname's, but we don't want to expand the +% control sequences after we've constructed them. +% +\def\nece#1{\expandafter\noexpand\csname#1\endcsname} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math means output in math mode. +% We don't use $'s directly in the definition of \math because control +% sequences like \math are expanded when the toc file is written. Then, +% we read the toc file back, the $'s will be normal characters (as they +% should be, according to the definition of Texinfo). So we must use a +% control sequence to switch into and out of math mode. +% +% This isn't quite enough for @math to work properly in indices, but it +% seems unlikely it will ever be needed there. +% +\let\implicitmath = $ +\def\math#1{\implicitmath #1\implicitmath} + +% @bullet and @minus need the same treatment as @math, just above. +\def\bullet{\implicitmath\ptexbullet\implicitmath} +\def\minus{\implicitmath-\implicitmath} + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \iflinks + \readauxfile + \fi % \openindices needs to do some work in any case. + \openindices + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \global\let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + % Just to be on the safe side, close the input stream before the \input. + \openin 1 texinfo.cnf + \ifeof1 \let\temp=\relax \else \def\temp{\input texinfo.cnf }\fi + \closein1 + \temp + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{fonts,} +% Font-change commands. + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf analogous to plain's \rm, etc. +\newfam\sffam +\def\sf{\fam=\sffam \tensf} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this one. +\def\ttsl{\tenttsl} + +% Use Computer Modern fonts at \magstephalf (11pt). +\newcount\mainmagstep +\mainmagstep=\magstephalf + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor +\def\setfont#1#2#3#4{\font#1=\fontprefix#2#3 scaled #4} + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\undefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +\ifx\bigger\relax +\let\mainmagstep=\magstep1 +\setfont\textrm\rmshape{12}{1000} +\setfont\texttt\ttshape{12}{1000} +\else +\setfont\textrm\rmshape{10}{\mainmagstep} +\setfont\texttt\ttshape{10}{\mainmagstep} +\fi +% Instead of cmb10, you many want to use cmbx10. +% cmbx10 is a prettier font on its own, but cmb10 +% looks better when embedded in a line with cmr10. +\setfont\textbf\bfshape{10}{\mainmagstep} +\setfont\textit\itshape{10}{\mainmagstep} +\setfont\textsl\slshape{10}{\mainmagstep} +\setfont\textsf\sfshape{10}{\mainmagstep} +\setfont\textsc\scshape{10}{\mainmagstep} +\setfont\textttsl\ttslshape{10}{\mainmagstep} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep + +% A few fonts for @defun, etc. +\setfont\defbf\bxshape{10}{\magstep1} %was 1314 +\setfont\deftt\ttshape{10}{\magstep1} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \bf} + +% Fonts for indices and small examples (9pt). +% We actually use the slanted font rather than the italic, +% because texinfo normally uses the slanted fonts for that. +% Do not make many font distinctions in general in the index, since they +% aren't very useful. +\setfont\ninett\ttshape{9}{1000} +\setfont\ninettsl\ttslshape{10}{900} +\setfont\indrm\rmshape{9}{1000} +\setfont\indit\itshape{9}{1000} +\setfont\indsl\slshape{9}{1000} +\let\indtt=\ninett +\let\indttsl=\ninettsl +\let\indsf=\indrm +\let\indbf=\indrm +\setfont\indsc\scshape{10}{900} +\font\indi=cmmi9 +\font\indsy=cmsy9 + +% Fonts for title page: +\setfont\titlerm\rmbshape{12}{\magstep3} +\setfont\titleit\itbshape{10}{\magstep4} +\setfont\titlesl\slbshape{10}{\magstep4} +\setfont\titlett\ttbshape{12}{\magstep3} +\setfont\titlettsl\ttslshape{10}{\magstep4} +\setfont\titlesf\sfbshape{17}{\magstep1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\authorrm{\secrm} + +% Chapter (and unnumbered) fonts (17.28pt). +\setfont\chaprm\rmbshape{12}{\magstep2} +\setfont\chapit\itbshape{10}{\magstep3} +\setfont\chapsl\slbshape{10}{\magstep3} +\setfont\chaptt\ttbshape{12}{\magstep2} +\setfont\chapttsl\ttslshape{10}{\magstep3} +\setfont\chapsf\sfbshape{17}{1000} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 + +% Section fonts (14.4pt). +\setfont\secrm\rmbshape{12}{\magstep1} +\setfont\secit\itbshape{10}{\magstep2} +\setfont\secsl\slbshape{10}{\magstep2} +\setfont\sectt\ttbshape{12}{\magstep1} +\setfont\secttsl\ttslshape{10}{\magstep2} +\setfont\secsf\sfbshape{12}{\magstep1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 + +% \setfont\ssecrm\bxshape{10}{\magstep1} % This size an font looked bad. +% \setfont\ssecit\itshape{10}{\magstep1} % The letters were too crowded. +% \setfont\ssecsl\slshape{10}{\magstep1} +% \setfont\ssectt\ttshape{10}{\magstep1} +% \setfont\ssecsf\sfshape{10}{\magstep1} + +%\setfont\ssecrm\bfshape{10}{1315} % Note the use of cmb rather than cmbx. +%\setfont\ssecit\itshape{10}{1315} % Also, the size is a little larger than +%\setfont\ssecsl\slshape{10}{1315} % being scaled magstep1. +%\setfont\ssectt\ttshape{10}{1315} +%\setfont\ssecsf\sfshape{10}{1315} + +%\let\ssecbf=\ssecrm + +% Subsection fonts (13.15pt). +\setfont\ssecrm\rmbshape{12}{\magstephalf} +\setfont\ssecit\itbshape{10}{1315} +\setfont\ssecsl\slbshape{10}{1315} +\setfont\ssectt\ttbshape{12}{\magstephalf} +\setfont\ssecttsl\ttslshape{10}{1315} +\setfont\ssecsf\sfbshape{12}{\magstephalf} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{\magstep1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +% The smallcaps and symbol fonts should actually be scaled \magstep1.5, +% but that is not a standard magnification. + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts, we +% don't bother to reset \scriptfont and \scriptscriptfont (which would +% also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0 = \tenrm \textfont1 = \teni \textfont2 = \tensy + \textfont\itfam = \tenit \textfont\slfam = \tensl \textfont\bffam = \tenbf + \textfont\ttfam = \tentt \textfont\sffam = \tensf +} + + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this so that font changes will continue to work +% in math mode, where it is the current \fam that is relevant in most +% cases, not the current font. Plain TeX does \def\bf{\fam=\bffam +% \tenbf}, for example. By redefining \tenbf, we obviate the need to +% redefine \bf itself. +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy \let\tenttsl=\textttsl + \resetmathfonts} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \resetmathfonts \setleading{25pt}} +\def\titlefont#1{{\titlefonts\rm #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy \let\tenttsl=\chapttsl + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy \let\tenttsl=\secttsl + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy \let\tenttsl=\ssecttsl + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts % Maybe make sssec fonts scaled magstephalf? +\def\indexfonts{% + \let\tenrm=\indrm \let\tenit=\indit \let\tensl=\indsl + \let\tenbf=\indbf \let\tentt=\indtt \let\smallcaps=\indsc + \let\tensf=\indsf \let\teni=\indi \let\tensy=\indsy \let\tenttsl=\indttsl + \resetmathfonts \setleading{12pt}} + +% Set up the default fonts, so we can use them for creating boxes. +% +\textfonts + +% Define these so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000} +\setfont\shortcontbf\bxshape{12}{1000} +\setfont\shortcontsl\slshape{12}{1000} + +%% Add scribe-like font environments, plus @l for inline lisp (usually sans +%% serif) and @ii for TeX italic + +% \smartitalic{ARG} outputs arg in italics, followed by an italic correction +% unless the following character is such as not to need one. +\def\smartitalicx{\ifx\next,\else\ifx\next-\else\ifx\next.\else\/\fi\fi\fi} +\def\smartslanted#1{{\sl #1}\futurelet\next\smartitalicx} +\def\smartitalic#1{{\it #1}\futurelet\next\smartitalicx} + +\let\i=\smartitalic +\let\var=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic +\let\cite=\smartslanted + +\def\b#1{{\bf #1}} +\let\strong=\b + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +\def\t#1{% + {\tt \rawbackslash \frenchspacing #1}% + \null +} +\let\ttfont=\t +\def\samp#1{`\tclose{#1}'\null} +\setfont\smallrm\rmshape{8}{1000} +\font\smallsy=cmsy9 +\def\key#1{{\smallrm\textfont2=\smallsy \leavevmode\hbox{% + \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% + \vbox{\hrule\kern-0.4pt + \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% + \kern-0.4pt\hrule}% + \kern-.06em\raise0.4pt\hbox{\angleright}}}} +% The old definition, with no lozenge: +%\def\key #1{{\ttsl \nohyphenation \uppercase{#1}}\null} +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \frenchspacing + #1% + }% + \null +} + +% We *must* turn on hyphenation at `-' and `_' in \code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active + \catcode`\_=\active + % + \global\def\code{\begingroup + \catcode`\-=\active \let-\codedash + \catcode`\_=\active \let_\codeunder + \codex + } + % + % If we end up with any active - characters when handling the index, + % just treat them as a normal -. + \global\def\indexbreaks{\catcode`\-=\active \let-\realdash} +} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{\ifusingtt{\normalunderscore\discretionary{}{}{}}{\_}} +\def\codex #1{\tclose{#1}\endgroup} + +%\let\exp=\tclose %Was temporary + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\def\kbdinputstyle{\parsearg\kbdinputstylexxx} +\def\kbdinputstylexxx#1{% + \def\arg{#1}% + \ifx\arg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\arg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\arg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is kbdinputdistinct. (Too much of a hassle to call the macro, +% the catcodes are wrong for parsearg to work.) +\gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl} + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\kbdfont\look}}\fi +\else{\tclose{\kbdfont\look}}\fi} + +% For @url, @env, @command quotes seem unnecessary, so use \code. +\let\url=\code +\let\env=\code +\let\command=\code + +% @uref (abbreviation for `urlref') takes an optional second argument +% specifying the text to display. First (mandatory) arg is the url. +% Perhaps eventually put in a hypertex \special here. +% +\def\uref#1{\urefxxx #1,,\finish} +\def\urefxxx#1,#2,#3\finish{% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \unhbox0\ (\code{#1})% + \else + \code{#1}% + \fi +} + +% rms does not like the angle brackets --karl, 17may97. +% So now @email is just like @uref. +%\def\email#1{\angleleft{\tt #1}\angleright} +\let\email=\uref + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +\def\kbd#1{\def\look{#1}\expandafter\kbdfoo\look??\par} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @acronym downcases the argument and prints in smallcaps. +\def\acronym#1{{\smallcaps \lowercase{#1}}} + +% @pounds{} is a sterling sign. +\def\pounds{{\it\$}} + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\def\shorttitlepage{\parsearg\shorttitlepagezzz} +\def\shorttitlepagezzz #1{\begingroup\hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\def\titlepage{\begingroup \parindent=0pt \textfonts + \let\subtitlerm=\tenrm + \def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines}% + % + \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}% + % + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % + % Now you can print the title using @title. + \def\title{\parsearg\titlezzz}% + \def\titlezzz##1{\leftline{\titlefonts\rm ##1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt}% + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Now you can put text using @subtitle. + \def\subtitle{\parsearg\subtitlezzz}% + \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}% + % + % @author should come last, but may come many times. + \def\author{\parsearg\authorzzz}% + \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi + {\authorfont \leftline{##1}}}% + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \oldpage + \let\page = \oldpage + \hbox{}}% +% \def\page{\oldpage \hbox{}} +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi + % + \HEADINGSon +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +%%% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make Tex use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + +\def\evenheading{\parsearg\evenheadingxxx} +\def\oddheading{\parsearg\oddheadingxxx} +\def\everyheading{\parsearg\everyheadingxxx} + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\oddfooting{\parsearg\oddfootingxxx} +\def\everyfooting{\parsearg\everyfootingxxx} + +{\catcode`\@=0 % + +\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish} +\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish} +\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\everyheadingxxx#1{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish} +\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish} +\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -\baselineskip + \global\advance\vsize by -\baselineskip +} + +\gdef\everyfootingxxx#1{\oddfootingxxx{#1}\evenfootingxxx{#1}} +% +}% unbind the catcode of @. + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\HEADINGSoff{ +\global\evenheadline={\hfil} \global\evenfootline={\hfil} +\global\oddheadline={\hfil} \global\oddfootline={\hfil}} +\HEADINGSoff +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{ +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{ +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% Produces Day Month Year style of output. +\def\today{\number\day\space +\ifcase\month\or +January\or February\or March\or April\or May\or June\or +July\or August\or September\or October\or November\or December\fi +\space\number\year} + +% Use this if you want the Month Day, Year style of output. +%\def\today{\ifcase\month\or +%January\or February\or March\or April\or May\or June\or +%July\or August\or September\or October\or November\or December\fi +%\space\number\day, \number\year} + +% @settitle line... specifies the title of the document, for headings +% It generates no output of its own + +\def\thistitle{No Title} +\def\settitle{\parsearg\settitlezzz} +\def\settitlezzz #1{\gdef\thistitle{#1}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x), @kitem(x), @xitem(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @vtable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz} +\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \itemxpar \parsearg\xitemzzz} + +\def\internalBkitem{\smallbreak \parsearg\kitemzzz} +\def\internalBkitemx{\itemxpar \parsearg\kitemzzz} + +\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}% + \itemzzz {#1}} + +\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}% + \itemzzz {#1}} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemfont{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. Unfortunately + % we can't prevent a possible page break at the following + % \baselineskip glue. + \nobreak + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a table}} +\def\itemx{\errmessage{@itemx while not in a table}} +\def\kitem{\errmessage{@kitem while not in a table}} +\def\kitemx{\errmessage{@kitemx while not in a table}} +\def\xitem{\errmessage{@xitem while not in a table}} +\def\xitemx{\errmessage{@xitemx while not in a table}} + +% Contains a kludge to get @end[description] to work. +\def\description{\tablez{\dontindex}{1}{}{}{}{}} + +% @table, @ftable, @vtable. +\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex} +{\obeylines\obeyspaces% +\gdef\tablex #1^^M{% +\tabley\dontindex#1 \endtabley}} + +\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex} +{\obeylines\obeyspaces% +\gdef\ftablex #1^^M{% +\tabley\fnitemindex#1 \endtabley +\def\Eftable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\vtable{\begingroup\inENV\obeylines\obeyspaces\vtablex} +{\obeylines\obeyspaces% +\gdef\vtablex #1^^M{% +\tabley\vritemindex#1 \endtabley +\def\Evtable{\endgraf\afterenvbreak\endgroup}% +\let\Etable=\relax}} + +\def\dontindex #1{} +\def\fnitemindex #1{\doind {fn}{\code{#1}}}% +\def\vritemindex #1{\doind {vr}{\code{#1}}}% + +{\obeyspaces % +\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup% +\tablez{#1}{#2}{#3}{#4}{#5}{#6}}} + +\def\tablez #1#2#3#4#5#6{% +\aboveenvbreak % +\begingroup % +\def\Edescription{\Etable}% Necessary kludge. +\let\itemindex=#1% +\ifnum 0#3>0 \advance \leftskip by #3\mil \fi % +\ifnum 0#4>0 \tableindent=#4\mil \fi % +\ifnum 0#5>0 \advance \rightskip by #5\mil \fi % +\def\itemfont{#2}% +\itemmax=\tableindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \tableindent % +\exdentamount=\tableindent +\parindent = 0pt +\parskip = \smallskipamount +\ifdim \parskip=0pt \parskip=2pt \fi% +\def\Etable{\endgraf\afterenvbreak\endgroup}% +\let\item = \internalBitem % +\let\itemx = \internalBitemx % +\let\kitem = \internalBkitem % +\let\kitemx = \internalBkitemx % +\let\xitem = \internalBxitem % +\let\xitemx = \internalBxitemx % +} + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\def\itemize{\parsearg\itemizezzz} + +\def\itemizezzz #1{% + \begingroup % ended by the @end itemize + \itemizey {#1}{\Eitemize} +} + +\def\itemizey #1#2{% +\aboveenvbreak % +\itemmax=\itemindent % +\advance \itemmax by -\itemmargin % +\advance \leftskip by \itemindent % +\exdentamount=\itemindent +\parindent = 0pt % +\parskip = \smallskipamount % +\ifdim \parskip=0pt \parskip=2pt \fi% +\def#2{\endgraf\afterenvbreak\endgroup}% +\def\itemcontents{#1}% +\let\item=\itemizeitem} + +% Set sfcode to normal for the chars that usually have another value. +% These are `.?!:;,' +\def\frenchspacing{\sfcode46=1000 \sfcode63=1000 \sfcode33=1000 + \sfcode58=1000 \sfcode59=1000 \sfcode44=1000 } + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\def\enumerate{\parsearg\enumeratezzz} +\def\enumeratezzz #1{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + \begingroup % ended by the @end enumerate + % + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call itemizey, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \itemizey{#1.}\Eenumerate\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + +% Definition of @item while inside @itemize. + +\def\itemizeitem{% +\advance\itemno by 1 +{\let\par=\endgraf \smallbreak}% +\ifhmode \errmessage{In hmode at itemizeitem}\fi +{\parskip=0in \hskip 0pt +\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}% +\vadjust{\penalty 1200}}% +\flushcr} + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. +% +% For those who want to use more than one line's worth of words in +% the preamble, break the line within one argument and it +% will parse correctly, i.e., +% +% @multitable {Column 1 template} {Column 2 template} {Column 3 +% template} +% Not: +% @multitable {Column 1 template} {Column 2 template} +% {Column 3 template} + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab, @multitable or @end multitable do not need to be on their +% own lines, but it will not hurt if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the part of the @columnfraction before the decimal point, which +% is presumably either 0 or the empty string (but we don't check, we +% just throw it away). #2 is the decimal part, which we use as the +% percent of \hsize for this column. +\def\pickupwholefraction#1.#2 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{.#2\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip }% Add a normal word space as a separator; + % typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable syntax +\def\tab{&\hskip1sp\relax} % 2/2/96 + % tiny skip here makes sure this column space is + % maintained, even if it is never used. + +% @multitable ... @end multitable definitions: +% +\def\multitable{\parsearg\dotable} +\def\dotable#1{\bgroup + \vskip\parskip + \let\item\crcr + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + \def\Emultitable{\global\setpercentfalse\cr\egroup\egroup}% + % + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % \everycr will reset column counter, \colcount, at the end of + % each line. Every column entry will cause \colcount to advance by one. + % The table preamble + % looks at the current \colcount to find the correct column width. + \everycr{\noalign{% + % + % \filbreak%% keeps underfull box messages off when table breaks over pages. + % Maybe so, but it also creates really weird page breaks when the table + % breaks over pages. Wouldn't \vfil be better? Wait until the problem + % manifests itself, so it can be fixed for real --karl. + \global\colcount=0\relax}}% + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup&\global\advance\colcount by 1\relax + \multistrut\vtop{\hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively marking + % characters. + \noindent\ignorespaces##\unskip\multistrut}\cr +} + +\def\setmultitablespacing{% test to see if user has set \multitablelinespace. +% If so, do nothing. If not, give it an appropriate dimension based on +% current baselineskip. +\ifdim\multitablelinespace=0pt +%% strut to put in table in case some entry doesn't have descenders, +%% to keep lines equally spaced +\let\multistrut = \strut +%% Test to see if parskip is larger than space between lines of +%% table. If not, do nothing. +%% If so, set to same dimension as multitablelinespace. +\else +\gdef\multistrut{\vrule height\multitablelinespace depth\dp0 +width0pt\relax} \fi +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt %% to keep parskip somewhat smaller + %% than skip between lines in the table. +\fi} + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within \newindex. +{\catcode`\@=11 +\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} + +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. + +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}} +} + +\def\defcodeindex{\parsearg\newcodeindex} + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% The \closeout helps reduce unnecessary open files; the limit on the +% Acorn RISC OS is a mere 16 files. +\def\synindex#1 #2 {% + \expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname + \expandafter\closeout\csname#1indfile\endcsname + \expandafter\let\csname#1indfile\endcsname=\synindexfoo + \expandafter\xdef\csname#1index\endcsname{% define \xxxindex + \noexpand\doindex{#2}}% +} + +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +\def\syncodeindex#1 #2 {% + \expandafter\let\expandafter\synindexfoo\expandafter=\csname#2indfile\endcsname + \expandafter\closeout\csname#1indfile\endcsname + \expandafter\let\csname#1indfile\endcsname=\synindexfoo + \expandafter\xdef\csname#1index\endcsname{% define \xxxindex + \noexpand\docodeindex{#2}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +\def\indexdummies{% +\def\ { }% +% Take care of the plain tex accent commands. +\def\"{\realbackslash "}% +\def\`{\realbackslash `}% +\def\'{\realbackslash '}% +\def\^{\realbackslash ^}% +\def\~{\realbackslash ~}% +\def\={\realbackslash =}% +\def\b{\realbackslash b}% +\def\c{\realbackslash c}% +\def\d{\realbackslash d}% +\def\u{\realbackslash u}% +\def\v{\realbackslash v}% +\def\H{\realbackslash H}% +% Take care of the plain tex special European modified letters. +\def\oe{\realbackslash oe}% +\def\ae{\realbackslash ae}% +\def\aa{\realbackslash aa}% +\def\OE{\realbackslash OE}% +\def\AE{\realbackslash AE}% +\def\AA{\realbackslash AA}% +\def\o{\realbackslash o}% +\def\O{\realbackslash O}% +\def\l{\realbackslash l}% +\def\L{\realbackslash L}% +\def\ss{\realbackslash ss}% +% Take care of texinfo commands likely to appear in an index entry. +% (Must be a way to avoid doing expansion at all, and thus not have to +% laboriously list every single command here.) +\def\@{@}% will be @@ when we switch to @ as escape char. +% Need these in case \tex is in effect and \{ is a \delimiter again. +% But can't use \lbracecmd and \rbracecmd because texindex assumes +% braces and backslashes are used only as delimiters. +\let\{ = \mylbrace +\let\} = \myrbrace +\def\_{{\realbackslash _}}% +\def\w{\realbackslash w }% +\def\bf{\realbackslash bf }% +%\def\rm{\realbackslash rm }% +\def\sl{\realbackslash sl }% +\def\sf{\realbackslash sf}% +\def\tt{\realbackslash tt}% +\def\gtr{\realbackslash gtr}% +\def\less{\realbackslash less}% +\def\hat{\realbackslash hat}% +\def\TeX{\realbackslash TeX}% +\def\dots{\realbackslash dots }% +\def\result{\realbackslash result}% +\def\equiv{\realbackslash equiv}% +\def\expansion{\realbackslash expansion}% +\def\print{\realbackslash print}% +\def\error{\realbackslash error}% +\def\point{\realbackslash point}% +\def\copyright{\realbackslash copyright}% +\def\tclose##1{\realbackslash tclose {##1}}% +\def\code##1{\realbackslash code {##1}}% +\def\uref##1{\realbackslash uref {##1}}% +\def\url##1{\realbackslash url {##1}}% +\def\env##1{\realbackslash env {##1}}% +\def\command##1{\realbackslash command {##1}}% +\def\option##1{\realbackslash option {##1}}% +\def\dotless##1{\realbackslash dotless {##1}}% +\def\samp##1{\realbackslash samp {##1}}% +\def\,##1{\realbackslash ,{##1}}% +\def\t##1{\realbackslash t {##1}}% +\def\r##1{\realbackslash r {##1}}% +\def\i##1{\realbackslash i {##1}}% +\def\b##1{\realbackslash b {##1}}% +\def\sc##1{\realbackslash sc {##1}}% +\def\cite##1{\realbackslash cite {##1}}% +\def\key##1{\realbackslash key {##1}}% +\def\file##1{\realbackslash file {##1}}% +\def\var##1{\realbackslash var {##1}}% +\def\kbd##1{\realbackslash kbd {##1}}% +\def\dfn##1{\realbackslash dfn {##1}}% +\def\emph##1{\realbackslash emph {##1}}% +\def\acronym##1{\realbackslash acronym {##1}}% +% +% Handle some cases of @value -- where the variable name does not +% contain - or _, and the value does not contain any +% (non-fully-expandable) commands. +\let\value = \expandablevalue +% +\unsepspaces +} + +% If an index command is used in an @example environment, any spaces +% therein should become regular spaces in the raw index file, not the +% expansion of \tie (\\leavevmode \penalty \@M \ ). +{\obeyspaces + \gdef\unsepspaces{\obeyspaces\let =\space}} + +% \indexnofonts no-ops all font-change commands. +% This is used when outputting the strings to sort the index by. +\def\indexdummyfont#1{#1} +\def\indexdummytex{TeX} +\def\indexdummydots{...} + +\def\indexnofonts{% +% Just ignore accents. +\let\,=\indexdummyfont +\let\"=\indexdummyfont +\let\`=\indexdummyfont +\let\'=\indexdummyfont +\let\^=\indexdummyfont +\let\~=\indexdummyfont +\let\==\indexdummyfont +\let\b=\indexdummyfont +\let\c=\indexdummyfont +\let\d=\indexdummyfont +\let\u=\indexdummyfont +\let\v=\indexdummyfont +\let\H=\indexdummyfont +\let\dotless=\indexdummyfont +% Take care of the plain tex special European modified letters. +\def\oe{oe}% +\def\ae{ae}% +\def\aa{aa}% +\def\OE{OE}% +\def\AE{AE}% +\def\AA{AA}% +\def\o{o}% +\def\O{O}% +\def\l{l}% +\def\L{L}% +\def\ss{ss}% +\let\w=\indexdummyfont +\let\t=\indexdummyfont +\let\r=\indexdummyfont +\let\i=\indexdummyfont +\let\b=\indexdummyfont +\let\emph=\indexdummyfont +\let\strong=\indexdummyfont +\let\cite=\indexdummyfont +\let\sc=\indexdummyfont +%Don't no-op \tt, since it isn't a user-level command +% and is used in the definitions of the active chars like <, >, |... +%\let\tt=\indexdummyfont +\let\tclose=\indexdummyfont +\let\code=\indexdummyfont +\let\url=\indexdummyfont +\let\uref=\indexdummyfont +\let\env=\indexdummyfont +\let\command=\indexdummyfont +\let\option=\indexdummyfont +\let\file=\indexdummyfont +\let\samp=\indexdummyfont +\let\kbd=\indexdummyfont +\let\key=\indexdummyfont +\let\var=\indexdummyfont +\let\TeX=\indexdummytex +\let\dots=\indexdummydots +\def\@{@}% +} + +% To define \realbackslash, we must make \ not be an escape. +% We must first make another character (@) an escape +% so we do not become unable to do a definition. + +{\catcode`\@=0 \catcode`\\=\other + @gdef@realbackslash{\}} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% For \ifx comparisons. +\def\emptymacro{\empty} + +% Most index entries go through here, but \dosubind is the general case. +% +\def\doind#1#2{\dosubind{#1}{#2}\empty} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% \empty if called from \doind, as we usually are. The main exception +% is with defuns, which call us directly. +% +\def\dosubind#1#2#3{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt #2}}% + \fi + {% + \count255=\lastpenalty + {% + \indexdummies % Must do this here, since \bf, etc expand at this stage + \escapechar=`\\ + {% + \let\folio = 0% We will expand all macros now EXCEPT \folio. + \def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + \def\thirdarg{#3}% + % + % If third arg is present, precede it with space in sort key. + \ifx\thirdarg\emptymacro + \let\subentry = \empty + \else + \def\subentry{ #3}% + \fi + % + % First process the index-string with all font commands turned off + % to get the string to sort by. + {\indexnofonts \xdef\indexsorttmp{#2\subentry}}% + % + % Now produce the complete index entry, with both the sort key and the + % original text, including any font commands. + \toks0 = {#2}% + \edef\temp{% + \write\csname#1indfile\endcsname{% + \realbackslash entry{\indexsorttmp}{\folio}{\the\toks0}}% + }% + % + % If third (subentry) arg is present, add it to the index string. + \ifx\thirdarg\emptymacro \else + \toks0 = {#3}% + \edef\temp{\temp{\the\toks0}}% + \fi + % + % If a skip is the last thing on the list now, preserve it + % by backing up by \lastskip, doing the \write, then inserting + % the skip again. Otherwise, the whatsit generated by the + % \write will make \lastskip zero. The result is that sequences + % like this: + % @end defun + % @tindex whatever + % @defun ... + % will have extra space inserted, because the \medbreak in the + % start of the @defun won't see the skip inserted by the @end of + % the previous defun. + % + % But don't do any of this if we're not in vertical mode. We + % don't want to do a \vskip and prematurely end a paragraph. + % + % Avoid page breaks due to these extra skips, too. + % + \iflinks + \ifvmode + \skip0 = \lastskip + \ifdim\lastskip = 0pt \else \nobreak\vskip-\lastskip \fi + \fi + % + \temp % do the write + % + % + \ifvmode \ifdim\skip0 = 0pt \else \nobreak\vskip\skip0 \fi \fi + \fi + }% + }% + \penalty\count255 + }% +} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\def\printindex{\parsearg\doprintindex} +\def\doprintindex#1{\begingroup + \dobreak \chapheadingskip{10000}% + % + \indexfonts \rm + \tolerance = 9500 + \indexbreaks + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + (Index is nonexistent) + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + (Index is empty) + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\rawbackslashxx}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \penalty -300 + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + \vskip .33\baselineskip plus .1\baselineskip + % + % Do our best not to break after the initial. + \nobreak +}} + +% This typesets a paragraph consisting of #1, dot leaders, and then #2 +% flush to the right margin. It is used for index and table of contents +% entries. The paragraph is indented by \leftskip. +% +\def\entry#1#2{\begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing columns. + \vskip 0pt plus1pt + % + % Start a ``paragraph'' for the index entry so the line breaking + % parameters we've set above will have an effect. + \noindent + % + % Insert the text of the index entry. TeX will do line-breaking on it. + #1% + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \def\tempa{{\rm }}% + \def\tempb{#2}% + \edef\tempc{\tempa}% + \edef\tempd{\tempb}% + \ifx\tempc\tempd\ \else% + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ #2% The page number ends the paragraph. + \fi% + \par +\endgroup} + +% Like \dotfill except takes at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu ${\it .}$ \mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm + +\def\secondary #1#2{ +{\parfillskip=0in \parskip=0in +\hangindent =1in \hangafter=1 +\noindent\hskip\secondaryindent\hbox{#1}\indexdotfill #2\par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {\global\setbox\partialpage = \vbox{% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case, we must prevent the second \partialpage from + % simply overwriting the first, causing us to lose the page. + % This will preserve it until a real output routine can ship it + % out. Generally, \partialpage will be empty when this runs and + % this will be a no-op. + \unvbox\partialpage + % + % Unvbox the main output page. + \unvbox255 + \kern-\topskip \kern\baselineskip + }}% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \advance\vsize by -\ht\partialpage + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +\def\pagesofar{% + % Re-output the contents of the output page -- any previous material, + % followed by the two boxes we just split, in box0 and box2. + \advance\vsize by \ht\partialpage + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +\def\enddoublecolumns{% + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +\def\balancecolumns{% + % Called at the end of the double column material. + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Define chapters, sections, etc. + +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +\def\appendixletter{\char\the\appendixno} + +% Each @chapter defines this as the name of the chapter. +% page headings and footings can use it. @section does likewise. +\def\thischapter{} +\def\thissection{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raise/lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% Choose a numbered-heading macro +% #1 is heading level if unmodified by @raisesections or @lowersections +% #2 is text for heading +\def\numhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \chapterzzz{#2} +\or + \seczzz{#2} +\or + \numberedsubseczzz{#2} +\or + \numberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \chapterzzz{#2} + \else + \numberedsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses appendix heading levels +\def\apphead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \appendixzzz{#2} +\or + \appendixsectionzzz{#2} +\or + \appendixsubseczzz{#2} +\or + \appendixsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \appendixzzz{#2} + \else + \appendixsubsubseczzz{#2} + \fi +\fi +} + +% like \numhead, but chooses numberless heading levels +\def\unnmhead#1#2{\absseclevel=\secbase\advance\absseclevel by #1 +\ifcase\absseclevel + \unnumberedzzz{#2} +\or + \unnumberedseczzz{#2} +\or + \unnumberedsubseczzz{#2} +\or + \unnumberedsubsubseczzz{#2} +\else + \ifnum \absseclevel<0 + \unnumberedzzz{#2} + \else + \unnumberedsubsubseczzz{#2} + \fi +\fi +} + +% @chapter, @appendix, @unnumbered. +\def\thischaptername{No Chapter Title} +\outer\def\chapter{\parsearg\chapteryyy} +\def\chapteryyy #1{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz #1{% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \chapno by 1 \message{\putwordChapter\space \the\chapno}% +\chapmacro {#1}{\the\chapno}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +% We don't substitute the actual chapter name into \thischapter +% because we don't want its macros evaluated now. +\xdef\thischapter{\putwordChapter{} \the\chapno: \noexpand\thischaptername}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% + {\the\chapno}}}% +\temp +\donoderef +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec +} + +\outer\def\appendix{\parsearg\appendixyyy} +\def\appendixyyy #1{\apphead0{#1}} % normally apphead0 calls appendixzzz +\def\appendixzzz #1{% +\secno=0 \subsecno=0 \subsubsecno=0 +\global\advance \appendixno by 1 +\message{\putwordAppendix\space \appendixletter}% +\chapmacro {#1}{\putwordAppendix{} \appendixletter}% +\gdef\thissection{#1}% +\gdef\thischaptername{#1}% +\xdef\thischapter{\putwordAppendix{} \appendixletter: \noexpand\thischaptername}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash chapentry{\the\toks0}% + {\putwordAppendix{} \appendixletter}}}% +\temp +\appendixnoderef +\global\let\section = \appendixsec +\global\let\subsection = \appendixsubsec +\global\let\subsubsection = \appendixsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\def\centerchap{\parsearg\centerchapyyy} +\def\centerchapyyy #1{{\let\unnumbchapmacro=\centerchapmacro \unnumberedyyy{#1}}} + +% @top is like @unnumbered. +\outer\def\top{\parsearg\unnumberedyyy} + +\outer\def\unnumbered{\parsearg\unnumberedyyy} +\def\unnumberedyyy #1{\unnmhead0{#1}} % normally unnmhead0 calls unnumberedzzz +\def\unnumberedzzz #1{% +\secno=0 \subsecno=0 \subsubsecno=0 +% +% This used to be simply \message{#1}, but TeX fully expands the +% argument to \message. Therefore, if #1 contained @-commands, TeX +% expanded them. For example, in `@unnumbered The @cite{Book}', TeX +% expanded @cite (which turns out to cause errors because \cite is meant +% to be executed, not expanded). +% +% Anyway, we don't want the fully-expanded definition of @cite to appear +% as a result of the \message, we just want `@cite' itself. We use +% \the to achieve this: TeX expands \the only once, +% simply yielding the contents of . (We also do this for +% the toc entries.) +\toks0 = {#1}\message{(\the\toks0)}% +% +\unnumbchapmacro {#1}% +\gdef\thischapter{#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbchapentry{\the\toks0}}}% +\temp +\unnumbnoderef +\global\let\section = \unnumberedsec +\global\let\subsection = \unnumberedsubsec +\global\let\subsubsection = \unnumberedsubsubsec +} + +% Sections. +\outer\def\numberedsec{\parsearg\secyyy} +\def\secyyy #1{\numhead1{#1}} % normally calls seczzz +\def\seczzz #1{% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% + {\the\chapno}{\the\secno}}}% +\temp +\donoderef +\nobreak +} + +\outer\def\appendixsection{\parsearg\appendixsecyyy} +\outer\def\appendixsec{\parsearg\appendixsecyyy} +\def\appendixsecyyy #1{\apphead1{#1}} % normally calls appendixsectionzzz +\def\appendixsectionzzz #1{% +\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 % +\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash secentry{\the\toks0}% + {\appendixletter}{\the\secno}}}% +\temp +\appendixnoderef +\nobreak +} + +\outer\def\unnumberedsec{\parsearg\unnumberedsecyyy} +\def\unnumberedsecyyy #1{\unnmhead1{#1}} % normally calls unnumberedseczzz +\def\unnumberedseczzz #1{% +\plainsecheading {#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsecentry{\the\toks0}}}% +\temp +\unnumbnoderef +\nobreak +} + +% Subsections. +\outer\def\numberedsubsec{\parsearg\numberedsubsecyyy} +\def\numberedsubsecyyy #1{\numhead2{#1}} % normally calls numberedsubseczzz +\def\numberedsubseczzz #1{% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% + {\the\chapno}{\the\secno}{\the\subsecno}}}% +\temp +\donoderef +\nobreak +} + +\outer\def\appendixsubsec{\parsearg\appendixsubsecyyy} +\def\appendixsubsecyyy #1{\apphead2{#1}} % normally calls appendixsubseczzz +\def\appendixsubseczzz #1{% +\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 % +\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsecentry{\the\toks0}% + {\appendixletter}{\the\secno}{\the\subsecno}}}% +\temp +\appendixnoderef +\nobreak +} + +\outer\def\unnumberedsubsec{\parsearg\unnumberedsubsecyyy} +\def\unnumberedsubsecyyy #1{\unnmhead2{#1}} %normally calls unnumberedsubseczzz +\def\unnumberedsubseczzz #1{% +\plainsubsecheading {#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsecentry% + {\the\toks0}}}% +\temp +\unnumbnoderef +\nobreak +} + +% Subsubsections. +\outer\def\numberedsubsubsec{\parsearg\numberedsubsubsecyyy} +\def\numberedsubsubsecyyy #1{\numhead3{#1}} % normally numberedsubsubseczzz +\def\numberedsubsubseczzz #1{% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% + {\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% +\temp +\donoderef +\nobreak +} + +\outer\def\appendixsubsubsec{\parsearg\appendixsubsubsecyyy} +\def\appendixsubsubsecyyy #1{\apphead3{#1}} % normally appendixsubsubseczzz +\def\appendixsubsubseczzz #1{% +\gdef\thissection{#1}\global\advance \subsubsecno by 1 % +\subsubsecheading {#1} + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash subsubsecentry{\the\toks0}% + {\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}}}% +\temp +\appendixnoderef +\nobreak +} + +\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubsecyyy} +\def\unnumberedsubsubsecyyy #1{\unnmhead3{#1}} %normally unnumberedsubsubseczzz +\def\unnumberedsubsubseczzz #1{% +\plainsubsubsecheading {#1}\gdef\thissection{#1}% +\toks0 = {#1}% +\edef\temp{\noexpand\writetocentry{\realbackslash unnumbsubsubsecentry% + {\the\toks0}}}% +\temp +\unnumbnoderef +\nobreak +} + +% These are variants which are not "outer", so they can appear in @ifinfo. +% Actually, they should now be obsolete; ordinary section commands should work. +\def\infotop{\parsearg\unnumberedzzz} +\def\infounnumbered{\parsearg\unnumberedzzz} +\def\infounnumberedsec{\parsearg\unnumberedseczzz} +\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz} +\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz} + +\def\infoappendix{\parsearg\appendixzzz} +\def\infoappendixsec{\parsearg\appendixseczzz} +\def\infoappendixsubsec{\parsearg\appendixsubseczzz} +\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz} + +\def\infochapter{\parsearg\chapterzzz} +\def\infosection{\parsearg\sectionzzz} +\def\infosubsection{\parsearg\subsectionzzz} +\def\infosubsubsection{\parsearg\subsubsectionzzz} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\global\let\section = \numberedsec +\global\let\subsection = \numberedsubsec +\global\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + + +\def\majorheading{\parsearg\majorheadingzzz} +\def\majorheadingzzz #1{% +{\advance\chapheadingskip by 10pt \chapbreak }% +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +\def\chapheading{\parsearg\chapheadingzzz} +\def\chapheadingzzz #1{\chapbreak % +{\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\penalty 200} + +% @heading, @subheading, @subsubheading. +\def\heading{\parsearg\plainsecheading} +\def\subheading{\parsearg\plainsubsecheading} +\def\subsubheading{\parsearg\plainsubsubsecheading} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +%%% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} + +%%% Define plain chapter starts, and page on/off switching for it +% Parameter controlling skip before chapter headings (if needed) + +\newskip\chapheadingskip + +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{ +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +\def\CHAPFplain{ +\global\let\chapmacro=\chfplain +\global\let\unnumbchapmacro=\unnchfplain +\global\let\centerchapmacro=\centerchfplain} + +% Plain chapter opening. +% #1 is the text, #2 the chapter number or empty if unnumbered. +\def\chfplain#1#2{% + \pchapsepmacro + {% + \chapfonts \rm + \def\chapnum{#2}% + \setbox0 = \hbox{#2\ifx\chapnum\empty\else\enspace\fi}% + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent = \wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% Plain opening for unnumbered. +\def\unnchfplain#1{\chfplain{#1}{}} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerchfplain#1{{% + \def\centerparametersmaybe{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt + }% + \chfplain{#1}{}% +}} + +\CHAPFplain % The default + +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\raggedright + \rm #1\hfill}}\bigskip \par\nobreak +} + +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} + +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rm #1}\hfill}}\bigskip \par\nobreak +} + +\def\CHAPFopen{ +\global\let\chapmacro=\chfopen +\global\let\unnumbchapmacro=\unnchfopen +\global\let\centerchapmacro=\centerchfopen} + + +% Section titles. +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip {-1000}} +\def\secheading#1#2#3{\sectionheading{sec}{#2.#3}{#1}} +\def\plainsecheading#1{\sectionheading{sec}{}{#1}} + +% Subsection titles. +\newskip \subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}} +\def\subsecheading#1#2#3#4{\sectionheading{subsec}{#2.#3.#4}{#1}} +\def\plainsubsecheading#1{\sectionheading{subsec}{}{#1}} + +% Subsubsection titles. +\let\subsubsecheadingskip = \subsecheadingskip +\let\subsubsecheadingbreak = \subsecheadingbreak +\def\subsubsecheading#1#2#3#4#5{\sectionheading{subsubsec}{#2.#3.#4.#5}{#1}} +\def\plainsubsubsecheading#1{\sectionheading{subsubsec}{}{#1}} + + +% Print any size section title. +% +% #1 is the section type (sec/subsec/subsubsec), #2 is the section +% number (maybe empty), #3 the text. +\def\sectionheading#1#2#3{% + {% + \expandafter\advance\csname #1headingskip\endcsname by \parskip + \csname #1headingbreak\endcsname + }% + {% + % Switch to the right set of fonts. + \csname #1fonts\endcsname \rm + % + % Only insert the separating space if we have a section number. + \def\secnum{#2}% + \setbox0 = \hbox{#2\ifx\secnum\empty\else\enspace\fi}% + % + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \raggedright + \hangindent = \wd0 % zero if no section number + \unhbox0 #3}% + }% + \ifdim\parskip<10pt \nobreak\kern10pt\nobreak\kern-\parskip\fi \nobreak +} + + +\message{toc,} +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. We supply {\folio} at the end of the +% argument, which will end up as the last argument to the \...entry macro. +% +% We open the .toc file here instead of at @setfilename or any other +% given time so that @contents can be put in the document anywhere. +% +\newif\iftocfileopened +\def\writetocentry#1{% + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + \iflinks \write\tocfile{#1{\folio}}\fi +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Finish up the main text and prepare to read what we've written +% to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \unnumbchapmacro{#1}\def\thischapter{}% + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11 + % We can't do this, because then an actual ^ in a section + % title fails, e.g., @chapter ^ -- exponentiation. --karl, 9jul97. + %\catcode`\^=7 % to see ^^e4 as \"a etc. juha@piuha.ydi.vtt.fi + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \pageno = \lastnegativepageno \fi +} + + +% Normal (long) toc. +\def\contents{% + \startcontents{\putwordTableofContents}% + \openin 1 \jobname.toc + \ifeof 1 \else + \closein 1 + \input \jobname.toc + \fi + \vfill \eject + \endgroup + \lastnegativepageno = \pageno + \pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortContents}% + % + \let\chapentry = \shortchapentry + \let\unnumbchapentry = \shortunnumberedentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf \let\sl=\shortcontsl + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\secentry ##1##2##3##4{} + \def\unnumbsecentry ##1##2{} + \def\subsecentry ##1##2##3##4##5{} + \def\unnumbsubsecentry ##1##2{} + \def\subsubsecentry ##1##2##3##4##5##6{} + \def\unnumbsubsubsecentry ##1##2{} + \openin 1 \jobname.toc + \ifeof 1 \else + \closein 1 + \input \jobname.toc + \fi + \vfill \eject + \endgroup + \lastnegativepageno = \pageno + \pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Chapter-level things, for both the long and short contents. +\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}} + +% See comments in \dochapentry re vbox and related settings +\def\shortchapentry#1#2#3{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno{#3}}% +} + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g. `Appendix A' for an appendix, or `3' for a chapter. +% We could simplify the code here by writing out an \appendixentry +% command in the toc file for appendices, instead of using \chapentry +% for both, but it doesn't seem worth it. +\setbox0 = \hbox{\shortcontrm \putwordAppendix } +\newdimen\shortappendixwidth \shortappendixwidth = \wd0 + +\def\shortchaplabel#1{% + % We typeset #1 in a box of constant width, regardless of the text of + % #1, so the chapter titles will come out aligned. + \setbox0 = \hbox{#1}% + \dimen0 = \ifdim\wd0 > \shortappendixwidth \shortappendixwidth \else 0pt \fi + % + % This space should be plenty, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + \advance\dimen0 by 1.1em + \hbox to \dimen0{#1\hfil}% +} + +\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}} +\def\shortunnumberedentry#1#2{\tocentry{#1}{\doshortpageno{#2}}} + +% Sections. +\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}} +\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}} + +% Subsections. +\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}} +\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}} + +% And subsubsections. +\def\subsubsecentry#1#2#3#4#5#6{% + \dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}} +\def\unnumbsubsubsecentry#1#2{\dosubsubsecentry{#1}{#2}} + +% This parameter controls the indentation of the various levels. +\newdimen\tocindent \tocindent = 3pc + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno{#2}}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno{#2}}% +\endgroup} + +% Final typesetting of a toc entry; we use the same \entry macro as for +% the index entries, but we want to suppress hyphenation here. (We +% can't do that in the \entry macro, since index entries might consist +% of hyphenated-identifiers-that-do-not-fit-on-a-line-and-nothing-else.) +\def\tocentry#1#2{\begingroup + \vskip 0pt plus1pt % allow a little stretch for the sake of nice page breaks + % Do not use \turnoffactive in these arguments. Since the toc is + % typeset in cmr, so characters such as _ would come out wrong; we + % have to do the usual translation tricks. + \entry{#1}{#2}% +\endgroup} + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\let\subsecentryfonts = \textfonts +\let\subsubsecentryfonts = \textfonts + + +\message{environments,} + +% Since these characters are used in examples, it should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% Furthermore, these definitions must come after we define our fonts. +\newbox\dblarrowbox \newbox\longdblarrowbox +\newbox\pushcharbox \newbox\bullbox +\newbox\equivbox \newbox\errorbox + +%{\tentt +%\global\setbox\dblarrowbox = \hbox to 1em{\hfil$\Rightarrow$\hfil} +%\global\setbox\longdblarrowbox = \hbox to 1em{\hfil$\mapsto$\hfil} +%\global\setbox\pushcharbox = \hbox to 1em{\hfil$\dashv$\hfil} +%\global\setbox\equivbox = \hbox to 1em{\hfil$\ptexequiv$\hfil} +% Adapted from the manmac format (p.420 of TeXbook) +%\global\setbox\bullbox = \hbox to 1em{\kern.15em\vrule height .75ex width .85ex +% depth .1ex\hfil} +%} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +\def\point{$\star$} +\def\result{\leavevmode\raise.15ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\raise.1ex\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\lower.1ex\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% Adapted from the TeXbook's \boxit. +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \tensf error\kern-1.5pt} + +\global\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{ + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} + +% The @error{} command. +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @tex ... @end tex escapes into raw Tex temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain tex @ character. + +\def\tex{\begingroup + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie + \catcode `\%=14 + \catcode 43=12 % plus + \catcode`\"=12 + \catcode`\==12 + \catcode`\|=12 + \catcode`\<=12 + \catcode`\>=12 + \escapechar=`\\ + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\*=\ptexstar + \let\t=\ptext + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +\let\Etex=\endgroup} + +% Define @lisp ... @endlisp. +% @lisp does a \begingroup so it can rebind things, +% including the definition of @endlisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% Make each space character in the input produce a normal interword +% space in the output. Don't allow a line break at this space, as this +% is used only in environments like @example, where each line of input +% should produce a line of output anyway. +% +{\obeyspaces % +\gdef\sepspaces{\obeyspaces\let =\tie}} + +% Define \obeyedspace to be our active space, whatever it is. This is +% for use in \parsearg. +{\sepspaces% +\global\let\obeyedspace= } + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip +% +\def\aboveenvbreak{{\advance\envskipamount by \parskip +\endgraf \ifdim\lastskip<\envskipamount +\removelastskip \penalty-50 \vskip\envskipamount \fi}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\long\def\cartouche{% +\begingroup + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt %we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either +% side, and for 6pt waste from +% each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing=\comment + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \hsize=\cartinner + \kern3pt + \begingroup + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip +\def\Ecartouche{% + \endgroup + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup +\endgroup +}} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\def\nonfillstart{% + \aboveenvbreak + \inENV % This group ends at the end of the body + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \singlespace + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + \parindent = 0pt + \emergencystretch = 0pt % don't try to avoid overfull boxes + % @cartouche defines \nonarrowing to inhibit narrowing + % at next level down. + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \let\exdent=\nofillexdent + \let\nonarrowing=\relax + \fi +} + +% Define the \E... control sequence only if we are inside the particular +% environment, so the error checking in \end will work. +% +% To end an @example-like environment, we first end the paragraph (via +% \afterenvbreak's vertical glue), and then the group. That way we keep +% the zero \parskip that the environments set -- \parskip glue will be +% inserted at the beginning of the next paragraph in the document, after +% the environment. +% +\def\nonfillfinish{\afterenvbreak\endgroup} + +% @lisp: indented, narrowed, typewriter font. +\def\lisp{\begingroup + \nonfillstart + \let\Elisp = \nonfillfinish + \tt + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} + +% @example: Same as @lisp. +\def\example{\begingroup \def\Eexample{\nonfillfinish\endgroup}\lisp} + +% @small... is usually equivalent to the non-small (@smallbook +% redefines). We must call \example (or whatever) last in the +% definition, since it reads the return following the @example (or +% whatever) command. +% +% This actually allows (for example) @end display inside an +% @smalldisplay. Too bad, but makeinfo will catch the error anyway. +% +\def\smalldisplay{\begingroup\def\Esmalldisplay{\nonfillfinish\endgroup}\display} +\def\smallexample{\begingroup\def\Esmallexample{\nonfillfinish\endgroup}\lisp} +\def\smallformat{\begingroup\def\Esmallformat{\nonfillfinish\endgroup}\format} +\def\smalllisp{\begingroup\def\Esmalllisp{\nonfillfinish\endgroup}\lisp} + +% Real @smallexample and @smalllisp (when @smallbook): use smaller fonts. +% Originally contributed by Pavel@xerox. +\def\smalllispx{\begingroup + \def\Esmalllisp{\nonfillfinish\endgroup}% + \def\Esmallexample{\nonfillfinish\endgroup}% + \indexfonts + \lisp +} + +% @display: same as @lisp except keep current font. +% +\def\display{\begingroup + \nonfillstart + \let\Edisplay = \nonfillfinish + \gobble +} + +% @smalldisplay (when @smallbook): @display plus smaller fonts. +% +\def\smalldisplayx{\begingroup + \def\Esmalldisplay{\nonfillfinish\endgroup}% + \indexfonts \rm + \display +} + +% @format: same as @display except don't narrow margins. +% +\def\format{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eformat = \nonfillfinish + \gobble +} + +% @smallformat (when @smallbook): @format plus smaller fonts. +% +\def\smallformatx{\begingroup + \def\Esmallformat{\nonfillfinish\endgroup}% + \indexfonts \rm + \format +} + +% @flushleft (same as @format). +% +\def\flushleft{\begingroup \def\Eflushleft{\nonfillfinish\endgroup}\format} + +% @flushright. +% +\def\flushright{\begingroup + \let\nonarrowing = t + \nonfillstart + \let\Eflushright = \nonfillfinish + \advance\leftskip by 0pt plus 1fill + \gobble +} + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. +% +\def\quotation{% + \begingroup\inENV %This group ends at the end of the @quotation body + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \singlespace + \parindent=0pt + % We have retained a nonzero parskip for the environment, since we're + % doing normal filling. So to avoid extra space below the environment... + \def\Equotation{\parskip = 0pt \nonfillfinish}% + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \let\nonarrowing = \relax + \fi +} + + +\message{defuns,} +% Define formatter for defuns +% First, allow user to change definition object font (\df) internally +\def\setdeffont #1 {\csname DEF#1\endcsname} + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deftypemargin \deftypemargin=12pt +\newskip\deflastargmargin \deflastargmargin=18pt + +\newcount\parencount +% define \functionparens, which makes ( and ) and & do special things. +% \functionparens affects the group it is contained in. +\def\activeparens{% +\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active +\catcode`\[=\active \catcode`\]=\active} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +{\activeparens % Now, smart parens don't turn on until &foo (see \amprm) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +\global\let(=\lparen \global\let)=\rparen +\global\let[=\lbrack \global\let]=\rbrack + +\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 } +\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} +% This is used to turn on special parens +% but make & act ordinary (given that it's active). +\gdef\boldbraxnoamp{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb\let&=\ampnr} + +% Definitions of (, ) and & used in args for functions. +% This is the definition of ( outside of all parentheses. +\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested + \global\advance\parencount by 1 +} +% +% This is the definition of ( when already inside a level of parens. +\gdef\opnested{\char`\(\global\advance\parencount by 1 } +% +\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0. + % also in that case restore the outer-level definition of (. + \ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi + \global\advance \parencount by -1 } +% If we encounter &foo, then turn on ()-hacking afterwards +\gdef\amprm#1 {{\rm\}\let(=\oprm \let)=\clrm\ } +% +\gdef\normalparens{\boldbrax\let&=\ampnr} +} % End of definition inside \activeparens +%% These parens (in \boldbrax) actually are a little bolder than the +%% contained text. This is especially needed for [ and ] +\def\opnr{{\sf\char`\(}\global\advance\parencount by 1 } +\def\clnr{{\sf\char`\)}\global\advance\parencount by -1 } +\def\ampnr{\&} +\def\lbrb{{\bf\char`\[}} +\def\rbrb{{\bf\char`\]}} + +% First, defname, which formats the header line itself. +% #1 should be the function name. +% #2 should be the type of definition, such as "Function". + +\def\defname #1#2{% +% Get the values of \leftskip and \rightskip as they were +% outside the @def... +\dimen2=\leftskip +\advance\dimen2 by -\defbodyindent +\noindent +\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}% +\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line +\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations +\parshape 2 0in \dimen0 \defargsindent \dimen1 +% Now output arg 2 ("Function" or some such) +% ending at \deftypemargin from the right margin, +% but stuck inside a box of width 0 so it does not interfere with linebreaking +{% Adjust \hsize to exclude the ambient margins, +% so that \rightline will obey them. +\advance \hsize by -\dimen2 +\rlap{\rightline{{\rm #2}\hskip -1.25pc }}}% +% Make all lines underfull and no complaints: +\tolerance=10000 \hbadness=10000 +\advance\leftskip by -\defbodyindent +\exdentamount=\defbodyindent +{\df #1}\enskip % Generate function name +} + +% Actually process the body of a definition +% #1 should be the terminating control sequence, such as \Edefun. +% #2 should be the "another name" control sequence, such as \defunx. +% #3 should be the control sequence that actually processes the header, +% such as \defunheader. + +\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\activeparens\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % 61 is `=' +\obeylines\activeparens\spacesplit#3} + +% #1 is the \E... control sequence to end the definition (which we define). +% #2 is the \...x control sequence for consecutive fns (which we define). +% #3 is the control sequence to call to resume processing. +% #4, delimited by the space, is the class name. +% +\def\defmethparsebody#1#2#3#4 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#4}}} + +% @deftypemethod has an extra argument that nothing else does. Sigh. +% #1 is the \E... control sequence to end the definition (which we define). +% #2 is the \...x control sequence for consecutive fns (which we define). +% #3 is the control sequence to call to resume processing. +% #4, delimited by the space, is the class name. +% #5 is the method's return type. +% +\def\deftypemethparsebody#1#2#3#4 #5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#4}{#5}}} + +\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\activeparens\spacesplit{#3{#5}}} + +% These parsing functions are similar to the preceding ones +% except that they do not make parens into active characters. +% These are used for "variables" since they have no arguments. + +\def\defvarparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2{\begingroup\obeylines\spacesplit#3}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup % +\catcode 61=\active % +\obeylines\spacesplit#3} + +% This is used for \def{tp,vr}parsebody. It could probably be used for +% some of the others, too, with some judicious conditionals. +% +\def\parsebodycommon#1#2#3{% + \begingroup\inENV % + \medbreak % + % Define the end token that this defining construct specifies + % so that it will exit this group. + \def#1{\endgraf\endgroup\medbreak}% + \def#2##1 {\begingroup\obeylines\spacesplit{#3{##1}}}% + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent + \begingroup\obeylines +} + +\def\defvrparsebody#1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{#3{#4}}% +} + +% This loses on `@deftp {Data Type} {struct termios}' -- it thinks the +% type is just `struct', because we lose the braces in `{struct +% termios}' when \spacesplit reads its undelimited argument. Sigh. +% \let\deftpparsebody=\defvrparsebody +% +% So, to get around this, we put \empty in with the type name. That +% way, TeX won't find exactly `{...}' as an undelimited argument, and +% won't strip off the braces. +% +\def\deftpparsebody #1#2#3#4 {% + \parsebodycommon{#1}{#2}{#3}% + \spacesplit{\parsetpheaderline{#3{#4}}}\empty +} + +% Fine, but then we have to eventually remove the \empty *and* the +% braces (if any). That's what this does. +% +\def\removeemptybraces\empty#1\relax{#1} + +% After \spacesplit has done its work, this is called -- #1 is the final +% thing to call, #2 the type name (which starts with \empty), and #3 +% (which might be empty) the arguments. +% +\def\parsetpheaderline#1#2#3{% + #1{\removeemptybraces#2\relax}{#3}% +}% + +\def\defopvarparsebody #1#2#3#4#5 {\begingroup\inENV % +\medbreak % +% Define the end token that this defining construct specifies +% so that it will exit this group. +\def#1{\endgraf\endgroup\medbreak}% +\def#2##1 ##2 {\def#4{##1}% +\begingroup\obeylines\spacesplit{#3{##2}}}% +\parindent=0in +\advance\leftskip by \defbodyindent +\exdentamount=\defbodyindent +\begingroup\obeylines\spacesplit{#3{#5}}} + +% Split up #2 at the first space token. +% call #1 with two arguments: +% the first is all of #2 before the space token, +% the second is all of #2 after that space token. +% If #2 contains no space token, all of it is passed as the first arg +% and the second is passed as empty. + +{\obeylines +\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}% +\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{% +\ifx\relax #3% +#1{#2}{}\else #1{#2}{#3#4}\fi}} + +% So much for the things common to all kinds of definitions. + +% Define @defun. + +% First, define the processing that is wanted for arguments of \defun +% Use this to expand the args and terminate the paragraph they make up + +\def\defunargs #1{\functionparens \sl +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +\hyphenchar\tensl=0 +#1% +\hyphenchar\tensl=45 +\ifnum\parencount=0 \else \errmessage{Unbalanced parentheses in @def}\fi% +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\nobreak\vskip -\parskip\nobreak +} + +\def\deftypefunargs #1{% +% Expand, preventing hyphenation at `-' chars. +% Note that groups don't affect changes in \hyphenchar. +% Use \boldbraxnoamp, not \functionparens, so that & is not special. +\boldbraxnoamp +\tclose{#1}% avoid \code because of side effects on active chars +\interlinepenalty=10000 +\advance\rightskip by 0pt plus 1fil +\endgraf\nobreak\vskip -\parskip\nobreak +} + +% Do complete processing of one @defun or @defunx line already parsed. + +% @deffn Command forward-char nchars + +\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader} + +\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}% +\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defun == @deffn Function + +\def\defun{\defparsebody\Edefun\defunx\defunheader} + +\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Function}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefun int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefun{\defparsebody\Edeftypefun\deftypefunx\deftypefunheader} + +% #1 is the data type. #2 is the name and args. +\def\deftypefunheader #1#2{\deftypefunheaderx{#1}#2 \relax} +% #1 is the data type, #2 the name, #3 the args. +\def\deftypefunheaderx #1#2 #3\relax{% +\doind {fn}{\code{#2}}% Make entry in function index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Function}% +\deftypefunargs {#3}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @deftypefn {Library Function} int foobar (int @var{foo}, float @var{bar}) + +\def\deftypefn{\defmethparsebody\Edeftypefn\deftypefnx\deftypefnheader} + +% \defheaderxcond#1\relax$$$ +% puts #1 in @code, followed by a space, but does nothing if #1 is null. +\def\defheaderxcond#1#2$$${\ifx#1\relax\else\code{#1#2} \fi} + +% #1 is the classification. #2 is the data type. #3 is the name and args. +\def\deftypefnheader #1#2#3{\deftypefnheaderx{#1}{#2}#3 \relax} +% #1 is the classification, #2 the data type, #3 the name, #4 the args. +\def\deftypefnheaderx #1#2#3 #4\relax{% +\doind {fn}{\code{#3}}% Make entry in function index +\begingroup +\normalparens % notably, turn off `&' magic, which prevents +% at least some C++ text from working +\defname {\defheaderxcond#2\relax$$$#3}{#1}% +\deftypefunargs {#4}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defmac == @deffn Macro + +\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader} + +\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Macro}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% @defspec == @deffn Special Form + +\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader} + +\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index +\begingroup\defname {#1}{Special Form}% +\defunargs {#2}\endgroup % +\catcode 61=\other % Turn off change made in \defparsebody +} + +% This definition is run if you use @defunx +% anywhere other than immediately after a @defun or @defunx. + +\def\deffnx #1 {\errmessage{@deffnx in invalid context}} +\def\defunx #1 {\errmessage{@defunx in invalid context}} +\def\defmacx #1 {\errmessage{@defmacx in invalid context}} +\def\defspecx #1 {\errmessage{@defspecx in invalid context}} +\def\deftypefnx #1 {\errmessage{@deftypefnx in invalid context}} +\def\deftypemethodx #1 {\errmessage{@deftypemethodx in invalid context}} +\def\deftypefunx #1 {\errmessage{@deftypefunx in invalid context}} + +% @defmethod, and so on + +% @defop CATEGORY CLASS OPERATION ARG... + +\def\defop #1 {\def\defoptype{#1}% +\defopparsebody\Edefop\defopx\defopheader\defoptype} + +\def\defopheader #1#2#3{% +\dosubind {fn}{\code{#2}}{\putwordon\ #1}% Make entry in function index +\begingroup\defname {#2}{\defoptype{} on #1}% +\defunargs {#3}\endgroup % +} + +% @deftypemethod CLASS RETURN-TYPE METHOD ARG... +% +\def\deftypemethod{% + \deftypemethparsebody\Edeftypemethod\deftypemethodx\deftypemethodheader} +% +% #1 is the class name, #2 the data type, #3 the method name, #4 the args. +\def\deftypemethodheader#1#2#3#4{% + \dosubind{fn}{\code{#3}}{\putwordon\ \code{#1}}% entry in function index + \begingroup + \defname{\defheaderxcond#2\relax$$$#3}{\putwordMethodon\ \code{#1}}% + \deftypefunargs{#4}% + \endgroup +} + +% @defmethod == @defop Method +% +\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader} +% +% #1 is the class name, #2 the method name, #3 the args. +\def\defmethodheader#1#2#3{% + \dosubind{fn}{\code{#2}}{\putwordon\ \code{#1}}% entry in function index + \begingroup + \defname{#2}{\putwordMethodon\ \code{#1}}% + \defunargs{#3}% + \endgroup +} + +% @defcv {Class Option} foo-class foo-flag + +\def\defcv #1 {\def\defcvtype{#1}% +\defopvarparsebody\Edefcv\defcvx\defcvarheader\defcvtype} + +\def\defcvarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{\defcvtype{} of #1}% +\defvarargs {#3}\endgroup % +} + +% @defivar == @defcv {Instance Variable} + +\def\defivar{\defvrparsebody\Edefivar\defivarx\defivarheader} + +\def\defivarheader #1#2#3{% +\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index +\begingroup\defname {#2}{Instance Variable of #1}% +\defvarargs {#3}\endgroup % +} + +% These definitions are run if you use @defmethodx, etc., +% anywhere other than immediately after a @defmethod, etc. + +\def\defopx #1 {\errmessage{@defopx in invalid context}} +\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}} +\def\defcvx #1 {\errmessage{@defcvx in invalid context}} +\def\defivarx #1 {\errmessage{@defivarx in invalid context}} + +% Now @defvar + +% First, define the processing that is wanted for arguments of @defvar. +% This is actually simple: just print them in roman. +% This must expand the args and terminate the paragraph they make up +\def\defvarargs #1{\normalparens #1% +\interlinepenalty=10000 +\endgraf\nobreak\vskip -\parskip\nobreak} + +% @defvr Counter foo-count + +\def\defvr{\defvrparsebody\Edefvr\defvrx\defvrheader} + +\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}% +\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup} + +% @defvar == @defvr Variable + +\def\defvar{\defvarparsebody\Edefvar\defvarx\defvarheader} + +\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{Variable}% +\defvarargs {#2}\endgroup % +} + +% @defopt == @defvr {User Option} + +\def\defopt{\defvarparsebody\Edefopt\defoptx\defoptheader} + +\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index +\begingroup\defname {#1}{User Option}% +\defvarargs {#2}\endgroup % +} + +% @deftypevar int foobar + +\def\deftypevar{\defvarparsebody\Edeftypevar\deftypevarx\deftypevarheader} + +% #1 is the data type. #2 is the name, perhaps followed by text that +% is actually part of the data type, which should not be put into the index. +\def\deftypevarheader #1#2{% +\dovarind#2 \relax% Make entry in variables index +\begingroup\defname {\defheaderxcond#1\relax$$$#2}{Variable}% +\interlinepenalty=10000 +\endgraf\nobreak\vskip -\parskip\nobreak +\endgroup} +\def\dovarind#1 #2\relax{\doind{vr}{\code{#1}}} + +% @deftypevr {Global Flag} int enable + +\def\deftypevr{\defvrparsebody\Edeftypevr\deftypevrx\deftypevrheader} + +\def\deftypevrheader #1#2#3{\dovarind#3 \relax% +\begingroup\defname {\defheaderxcond#2\relax$$$#3}{#1} +\interlinepenalty=10000 +\endgraf\nobreak\vskip -\parskip\nobreak +\endgroup} + +% This definition is run if you use @defvarx +% anywhere other than immediately after a @defvar or @defvarx. + +\def\defvrx #1 {\errmessage{@defvrx in invalid context}} +\def\defvarx #1 {\errmessage{@defvarx in invalid context}} +\def\defoptx #1 {\errmessage{@defoptx in invalid context}} +\def\deftypevarx #1 {\errmessage{@deftypevarx in invalid context}} +\def\deftypevrx #1 {\errmessage{@deftypevrx in invalid context}} + +% Now define @deftp +% Args are printed in bold, a slight difference from @defvar. + +\def\deftpargs #1{\bf \defvarargs{#1}} + +% @deftp Class window height width ... + +\def\deftp{\deftpparsebody\Edeftp\deftpx\deftpheader} + +\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}% +\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup} + +% This definition is run if you use @deftpx, etc +% anywhere other than immediately after a @deftp, etc. + +\def\deftpx #1 {\errmessage{@deftpx in invalid context}} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\undefined + \newwrite\macscribble + \def\scanmacro#1{% + \begingroup \newlinechar`\^^M + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{#1}% + \immediate\closeout\macscribble + \let\xeatspaces\eatspaces + \input \jobname.tmp + \endgroup +} +\else +\def\scanmacro#1{% +\begingroup \newlinechar`\^^M +\let\xeatspaces\eatspaces\scantokens{#1}\endgroup} +\fi + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% Utility routines. +% Thisdoes \let #1 = #2, except with \csnames. +\def\cslet#1#2{% +\expandafter\expandafter +\expandafter\let +\expandafter\expandafter +\csname#1\endcsname +\csname#2\endcsname} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=12\catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \. + +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. + +\def\macrobodyctxt{% + \catcode`\~=12 + \catcode`\^=12 + \catcode`\_=12 + \catcode`\|=12 + \catcode`\<=12 + \catcode`\>=12 + \catcode`\+=12 + \catcode`\{=12 + \catcode`\}=12 + \catcode`\@=12 + \catcode`\^^M=12 + \usembodybackslash} + +\def\macroargctxt{% + \catcode`\~=12 + \catcode`\^=12 + \catcode`\_=12 + \catcode`\|=12 + \catcode`\<=12 + \catcode`\>=12 + \catcode`\+=12 + \catcode`\@=12 + \catcode`\\=12} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. + +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0% + \else + \expandafter\parsemargdef \argl;% + \fi + \expandafter\ifx \csname macsave.\the\macname\endcsname \relax + \cslet{macsave.\the\macname}{\the\macname}% + \else + \message{Warning: redefining \the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\def\unmacro{\parsearg\unmacroxxx} +\def\unmacroxxx#1{% + \expandafter\ifx \csname macsave.\the\macname\endcsname \relax + \errmessage{Macro \the\macname\ not defined.}% + \else + \cslet{#1}{macsave.#1}% + \expandafter\let \csname macsave.\the\macname\endcsname \undefined + \fi +} + +% This makes use of the obscure feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname #1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.blah for each blah +% in the params list, to be ##N where N is the position in that list. +% That gets used by \mbodybackslash (above). + +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. + +\def\parsemargdef#1;{\paramno=0\def\paramlist{}% + \let\hash\relax\let\xeatspaces\relax\parsemargdefxxx#1,;,} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1% + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) + +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% + +% This defines the macro itself. There are six cases: recursive and +% nonrecursive macros of zero, one, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname} + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % many + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname} + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \fi + \fi} + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg) +\def\braceorline#1{\let\next=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \next} + + +\message{cross references,} +\newwrite\auxfile + +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{\putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's job is to define \lastnode. +\def\node{\ENVcheck\parsearg\nodezzz} +\def\nodezzz#1{\nodexxx [#1,]} +\def\nodexxx[#1,#2]{\gdef\lastnode{#1}} +\let\nwnode=\node +\let\lastnode=\relax + +% The sectioning commands (@chapter, etc.) call these. +\def\donoderef{% + \ifx\lastnode\relax\else + \expandafter\expandafter\expandafter\setref{\lastnode}% + {Ysectionnumberandtype}% + \global\let\lastnode=\relax + \fi +} +\def\unnumbnoderef{% + \ifx\lastnode\relax\else + \expandafter\expandafter\expandafter\setref{\lastnode}{Ynothing}% + \global\let\lastnode=\relax + \fi +} +\def\appendixnoderef{% + \ifx\lastnode\relax\else + \expandafter\expandafter\expandafter\setref{\lastnode}% + {Yappendixletterandtype}% + \global\let\lastnode=\relax + \fi +} + + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\def\anchor#1{\setref{#1}{Ynothing}} + + +% \setref{NAME}{SNT} defines a cross-reference point NAME, namely +% NAME-title, NAME-pg, and NAME-SNT. Called from \foonoderef. We have +% to set \indexdummies so commands such as @code in a section title +% aren't expanded. It would be nicer not to expand the titles in the +% first place, but there's so many layers that that is hard to do. +% +\def\setref#1#2{{% + \indexdummies + \dosetq{#1-title}{Ytitle}% + \dosetq{#1-pg}{Ypagenumber}% + \dosetq{#1-snt}{#2} +}} + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \def\printedmanual{\ignorespaces #5}% + \def\printednodename{\ignorespaces #3}% + \setbox1=\hbox{\printedmanual}% + \setbox0=\hbox{\printednodename}% + \ifdim \wd0 = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname\relax + % Use the node name inside the square brackets. + \def\printednodename{\ignorespaces #1}% + \else + % Use the actual chapter/section title appear inside + % the square brackets. Use the real section title if we have it. + \ifdim \wd1 > 0pt + % It is in another manual, so we don't have it. + \def\printednodename{\ignorespaces #1}% + \else + \ifhavexrefs + % We know the real title if we have the xref values. + \def\printednodename{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printednodename{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % If we use \unhbox0 and \unhbox1 to print the node names, TeX does not + % insert empty discretionaries after hyphens, which means that it will + % not find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, this + % is a loss. Therefore, we give the text of the node name again, so it + % is as if TeX is seeing it for the first time. + \ifdim \wd1 > 0pt + \putwordsection{} ``\printednodename'' in \cite{\printedmanual}% + \else + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\normalturnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % [mynode], + [\printednodename],\space + % page 3 + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi +\endgroup} + +% \dosetq is the interface for calls from other macros + +% Use \normalturnoffactive so that punctuation chars such as underscore +% and backslash work in node names. (\turnoffactive doesn't do \.) +\def\dosetq#1#2{% + {\let\folio=0 + \normalturnoffactive + \edef\next{\write\auxfile{\internalsetq{#1}{#2}}}% + \iflinks + \next + \fi + }% +} + +% \internalsetq {foo}{page} expands into +% CHARACTERS 'xrdef {foo}{...expansion of \Ypage...} +% When the aux file is read, ' is the escape character + +\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}} + +% Things to be expanded by \internalsetq + +\def\Ypagenumber{\folio} + +\def\Ytitle{\thissection} + +\def\Ynothing{} + +\def\Ysectionnumberandtype{% +\ifnum\secno=0 \putwordChapter\xreftie\the\chapno % +\else \ifnum \subsecno=0 \putwordSection\xreftie\the\chapno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\def\Yappendixletterandtype{% +\ifnum\secno=0 \putwordAppendix\xreftie'char\the\appendixno{}% +\else \ifnum \subsecno=0 \putwordSection\xreftie'char\the\appendixno.\the\secno % +\else \ifnum \subsubsecno=0 % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno % +\else % +\putwordSection\xreftie'char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno % +\fi \fi \fi } + +\gdef\xreftie{'tie} + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Non-3.0. +\else + \def\linenumber{\the\inputlineno:\space} +\fi + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. + +\def\refx#1#2{% + \expandafter\ifx\csname X#1\endcsname\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + \message{\linenumber Undefined cross reference `#1'.}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \csname X#1\endcsname + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. +% +\def\xrdef#1{\begingroup + % Reenable \ as an escape while reading the second argument. + \catcode`\\ = 0 + \afterassignment\endgroup + \expandafter\gdef\csname X#1\endcsname +} + +% Read the last existing aux file, if any. No error if none exists. +\def\readauxfile{\begingroup + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + \catcode`\@=\other + \catcode`\^=\other + % It was suggested to define this as 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % Make the characters 128-255 be printing characters + {% + \count 1=128 + \def\loop{% + \catcode\count 1=\other + \advance\count 1 by 1 + \ifnum \count 1<256 \loop \fi + }% + }% + % The aux file uses ' as the escape (for now). + % Turn off \ as an escape so we do not lose on + % entries which were dumped with control sequences in their names. + % For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^ + % Reference to such entries still does not work the way one would wish, + % but at least they do not bomb out when the aux file is read in. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\%=\other + \catcode`\'=0 + \catcode`\\=\other + % + \openin 1 \jobname.aux + \ifeof 1 \else + \closein 1 + \input \jobname.aux + \global\havexrefstrue + \global\warnedobstrue + \fi + % Open the new aux file. TeX will close it automatically at exit. + \openout\auxfile=\jobname.aux +\endgroup} + + +% Footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for info output only. +\let\footnotestyle=\comment + +\let\ptexfootnote=\footnote + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \footnotezzz +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset and anything else that uses +% \parseargline fail inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\long\gdef\footnotezzz{\insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + % Hang the footnote text off the number. + \hang + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + \futurelet\next\fo@t +} +\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t + \else\let\next\f@t\fi \next} +\def\f@@t{\bgroup\aftergroup\@foot\let\next} +\def\f@t#1{#1\@foot} +\def\@foot{\strut\egroup} + +}%end \catcode `\@=11 + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +\def\setleading#1{% + \normalbaselineskip = #1\relax + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt} + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + \closein 1 + % Do not bother showing banner with post-v2.7 epsf.tex (available in + % doc/epsf.tex until it shows up on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +% +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://ftp.tug.org/tex/epsf.tex.} +% +% Only complain once about lack of epsf.tex. +\def\image#1{% + \ifx\epsfbox\undefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is just the usual extra ignored arg for parsing this stuff. +\def\imagexxx#1,#2,#3,#4\finish{% + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + % If the image is by itself, center it. + \ifvmode + \nobreak\medskip + \nobreak + \centerline{\epsfbox{#1.eps}}% + \bigbreak + \else + \epsfbox{#1.eps}% + \fi +} + + +\message{paper sizes,} +% And other related parameters. + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be so finicky about underfull hboxes, either. +\hbadness = 2000 + +% Following George Bush, just get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. This makes it come to about 9pt for the 8.5x11 format. We +% call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = \hsize + \divide\emergencystretch by 45 + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; 3) voffset; +% 4) hoffset; 5) binding offset; 6) topskip. Then whoever calls us can +% set \parskip and call \setleading for \baselineskip. +% +\def\internalpagesizes#1#2#3#4#5#6{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \setleading{13.2pt}% + % + % If page is nothing but text, make it come out even. + \internalpagesizes{46\baselineskip}{6in}{\voffset}{.25in}{\bindingoffset}{36pt}% +}} + +% Use @smallbook to reset parameters for 7x9.5 (or so) format. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \setleading{12pt}% + % + \internalpagesizes{7.5in}{5.in}{\voffset}{.25in}{\bindingoffset}{16pt}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \deftypemargin = 0pt + \defbodyindent = .5cm + % + \let\smalldisplay = \smalldisplayx + \let\smallexample = \smalllispx + \let\smallformat = \smallformatx + \let\smalllisp = \smalllispx +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \setleading{12pt}% + \parskip = 3pt plus 2pt minus 1pt + % + \internalpagesizes{53\baselineskip}{160mm}{\voffset}{4mm}{\bindingoffset}{44pt}% + % + \tolerance = 700 + \hfuzz = 1pt +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. Top margin +% 29mm, hence bottom margin 28mm, nominal side margin 3cm. +\def\afourlatex{{\globaldefs = 1 + \setleading{13.6pt}% + % + \afourpaper + \internalpagesizes{237mm}{150mm}{3.6mm}{3.6mm}{3mm}{7mm}% + % + \globaldefs = 0 +}} + +% Use @afourwide to print on European A4 paper in wide format. +\def\afourwide{% + \afourpaper + \internalpagesizes{9.5in}{6.5in}{\hoffset}{\normaloffset}{\bindingoffset}{7mm}% + % + \globaldefs = 0 +} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\def\pagesizes{\parsearg\pagesizesxxx} +\def\pagesizesxxx#1{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{13.2pt}% + % + \internalpagesizes{#1}{\hsize}{\voffset}{\normaloffset}{\bindingoffset}{44pt}% +}} + +% Set default to letter. +% +\letterpaper + +\message{and turning on texinfo input format.} + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other +\catcode`\~=\other +\catcode`\^=\other +\catcode`\_=\other +\catcode`\|=\other +\catcode`\<=\other +\catcode`\>=\other +\catcode`\+=\other +\def\normaldoublequote{"} +\def\normaltilde{~} +\def\normalcaret{^} +\def\normalunderscore{_} +\def\normalverticalbar{|} +\def\normalless{<} +\def\normalgreater{>} +\def\normalplus{+} + +% This macro is used to make a character print one way in ttfont +% where it can probably just be output, and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\the\font=0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em height.1ex}} + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +%\catcode 27=\active +%\def^^[{$\diamondsuit$} + +% Set up an active definition for =, but don't enable it most of the time. +{\catcode`\==\active +\global\def={{\tt \char 61}}} + +\catcode`+=\active +\catcode`\_=\active + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +\catcode`\@=0 + +% \rawbackslashxx output one backslash character in current font +\global\chardef\rawbackslashxx=`\\ +%{\catcode`\\=\other +%@gdef@rawbackslashxx{\}} + +% \rawbackslash redefines \ as input to do \rawbackslashxx. +{\catcode`\\=\active +@gdef@rawbackslash{@let\=@rawbackslashxx }} + +% \normalbackslash outputs one backslash in fixed width font. +\def\normalbackslash{{\tt\rawbackslashxx}} + +% Say @foo, not \foo, in error messages. +\escapechar=`\@ + +% \catcode 17=0 % Define control-q +\catcode`\\=\active + +% Used sometimes to turn off (effectively) the active characters +% even after parsing them. +@def@turnoffactive{@let"=@normaldoublequote +@let\=@realbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus} + +@def@normalturnoffactive{@let"=@normaldoublequote +@let\=@normalbackslash +@let~=@normaltilde +@let^=@normalcaret +@let_=@normalunderscore +@let|=@normalverticalbar +@let<=@normalless +@let>=@normalgreater +@let+=@normalplus} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\{ in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also back turn on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active @catcode`@_=@active} + +% These look ok in all fonts, so just make them not special. The @rm below +% makes sure that the current font starts out as the newly loaded cmr10 +@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other + +@textfonts +@rm + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d" +@c time-stamp-end: "}" +@c End: diff --git a/dpas/.cvsignore b/dpas/.cvsignore new file mode 100644 index 0000000..8722e93 --- /dev/null +++ b/dpas/.cvsignore @@ -0,0 +1,7 @@ +Makefile +Makefile.in +.deps +dpas-parser.h +dpas-parser.c +dpas-scanner.c +dpas diff --git a/dpas/Makefile.am b/dpas/Makefile.am new file mode 100644 index 0000000..c405af3 --- /dev/null +++ b/dpas/Makefile.am @@ -0,0 +1,25 @@ + +noinst_PROGRAMS = dpas + +dpas_SOURCES = \ + dpas-internal.h \ + dpas-main.c \ + dpas-function.c \ + dpas-parser.y \ + dpas-scanner.l \ + dpas-scope.c \ + dpas-scope.h \ + dpas-types.c \ + dpas-types.h + +AM_YFLAGS = -d + +CCLD = $(CXX) + +dpas_LDADD = $(top_builddir)/jit/libjit.a +dpas_DEPENDENCIES = $(top_builddir)/jit/libjit.a + +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir) \ + -DDPAS_INCLUDE_DIR=\"$(datadir)/dpas\" + +CLEANFILES = dpas-parser.c dpas-scanner.c dpas-parser.h diff --git a/dpas/README b/dpas/README new file mode 100644 index 0000000..be6f196 --- /dev/null +++ b/dpas/README @@ -0,0 +1,8 @@ + +This directory contains an implementation of "Dynamic Pascal", or "dpas" +as we like to call it. It is provided as an example of using "libjit" +in a real environment. We also use it to write test programs that +exercise the JIT's capabilities. + +More information on Dynamic Pascal can be found in libjit's Texinfo +documentation, "libjit/doc/libjit.texi". diff --git a/dpas/dpas-function.c b/dpas/dpas-function.c new file mode 100644 index 0000000..98511d3 --- /dev/null +++ b/dpas/dpas-function.c @@ -0,0 +1,88 @@ +/* + * dpas-function.c - Special handling for Dynamic Pascal functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "dpas-internal.h" + +static jit_context_t current_context; +static jit_function_t *function_stack = 0; +static int function_stack_size = 0; + +jit_context_t dpas_current_context(void) +{ + if(!current_context) + { + current_context = jit_context_create(); + if(!current_context) + { + dpas_out_of_memory(); + } + } + return current_context; +} + +jit_function_t dpas_current_function(void) +{ + if(function_stack_size > 0) + { + return function_stack[function_stack_size - 1]; + } + else + { + /* We are probably compiling the "main" method for this module */ + jit_type_t signature = jit_type_create_signature + (jit_abi_cdecl, jit_type_void, 0, 0, 1); + if(!signature) + { + dpas_out_of_memory(); + } + return dpas_new_function(signature); + } +} + +jit_function_t dpas_new_function(jit_type_t signature) +{ + jit_function_t func; + func = jit_function_create(dpas_current_context(), signature); + if(!func) + { + dpas_out_of_memory(); + } + function_stack = (jit_function_t *)jit_realloc + (function_stack, sizeof(jit_function_t) * (function_stack_size + 1)); + if(!function_stack) + { + dpas_out_of_memory(); + } + function_stack[function_stack_size++] = func; + return func; +} + +void dpas_pop_function(void) +{ + if(function_stack_size > 0) + { + --function_stack_size; + } +} + +int dpas_function_is_nested(void) +{ + return (function_stack_size > 1); +} diff --git a/dpas/dpas-internal.h b/dpas/dpas-internal.h new file mode 100644 index 0000000..db7bc1b --- /dev/null +++ b/dpas/dpas-internal.h @@ -0,0 +1,121 @@ +/* + * dpas-internal.h - Internal definitions for the Dynamic Pascal compiler. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _DPAS_INTERNAL_H +#define _DPAS_INTERNAL_H + +#include +#include + +#include "dpas-scope.h" +#include "dpas-types.h" +#include "dpas-semantics.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Current filename and line number. + */ +extern char *dpas_filename; +extern long dpas_linenum; + +/* + * Information about a parameter list (also used for record fields). + */ +typedef struct +{ + char **names; + jit_type_t *types; + int len; + int has_vararg; + +} dpas_params; + +/* + * Flag that is set when an error is encountered. + */ +extern int dpas_error_reported; + +/* + * Function that is called when the system runs out of memory. + */ +void dpas_out_of_memory(void); + +/* + * Process an "import" clause within a program. + */ +void dpas_import(const char *name); + +/* + * Load the contents of a source file. + */ +void dpas_load_file(char *filename, FILE *file); + +/* + * Report an error on the current line. + */ +void dpas_error(const char *format, ...); + +/* + * Report a warning on the current line. + */ +void dpas_warning(const char *format, ...); + +/* + * Report an error on a specific line. + */ +void dpas_error_on_line(const char *filename, long linenum, + const char *format, ...); + +/* + * Get the JIT context that we are using to compile functions. + */ +jit_context_t dpas_current_context(void); + +/* + * Get the current function that is being compiled. Returns NULL + * if we are currently at the global level. + */ +jit_function_t dpas_current_function(void); + +/* + * Create a new function and push it onto the context stack. + * The function is initialized to read parameters that are + * compatible with the supplied signature. + */ +jit_function_t dpas_new_function(jit_type_t signature); + +/* + * Pop out of the current function. + */ +void dpas_pop_function(void); + +/* + * Determine if the current function is nested. + */ +int dpas_function_is_nested(void); + +#ifdef __cplusplus +}; +#endif + +#endif /* _DPAS_INTERNAL_H */ diff --git a/dpas/dpas-main.c b/dpas/dpas-main.c new file mode 100644 index 0000000..b0d8c2f --- /dev/null +++ b/dpas/dpas-main.c @@ -0,0 +1,265 @@ +/* + * dpas-main.c - Main entry point for the Dyanmic Pascal Compiler. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "dpas-internal.h" +#include +#include + +/* + * Command-line options. + */ +static char *progname = 0; +static char *filename = 0; +static char **include_dirs = 0; +static int num_include_dirs = 0; +static char **using_seen = 0; +static int num_using_seen = 0; + +/* + * Forward declarations. + */ +static void version(void); +static void usage(void); +static void add_include_dir(char *dir); +static void initialize(void); + +int main(int argc, char **argv) +{ + FILE *file; + + /* Parse the command-line options */ + progname = argv[0]; + while(argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') + { + if(!jit_strncmp(argv[1], "-I", 2)) + { + if(argv[1][2] == '\0') + { + ++argv; + --argc; + if(argc > 1) + { + add_include_dir(argv[1]); + } + else + { + usage(); + } + } + else + { + add_include_dir(argv[1] + 2); + } + } + else if(!jit_strcmp(argv[1], "-v") || + !jit_strcmp(argv[1], "--version")) + { + version(); + } + else + { + usage(); + } + ++argv; + --argc; + } + if(argc <= 1) + { + usage(); + } + filename = argv[1]; + ++argv; + --argc; + + /* Add the system-wide include directories to the list */ +#ifdef DPAS_INCLUDE_DIR + add_include_dir(DPAS_INCLUDE_DIR); +#endif + add_include_dir("/usr/local/share/dpas"); + add_include_dir("/usr/share/dpas"); + + /* Initialize the pre-defined types, constants, procedures, etc */ + initialize(); + + /* Load the specified program file */ + if(!jit_strcmp(filename, "-")) + { + dpas_load_file("(stdin)", stdin); + } + else if((file = fopen(filename, "r")) != NULL) + { + dpas_load_file(filename, file); + fclose(file); + } + else + { + perror(filename); + return 1; + } + + /* Bail out if we had errors during the compilation phase */ + if(dpas_error_reported) + { + return 1; + } + + /* TODO: JIT and execute the program */ + + /* Done */ + return 0; +} + +static void version(void) +{ + printf("Dynamic Pascal Version " VERSION "\n"); + printf("Copyright (c) 2004 Southern Storm Software, Pty Ltd.\n"); + exit(0); +} + +static void usage(void) +{ + printf("Dynamic Pascal Version " VERSION "\n"); + printf("Copyright (c) 2004 Southern Storm Software, Pty Ltd.\n"); + printf("\n"); + printf("Usage: %s [-Idir] file.pas [args]\n", progname); + exit(1); +} + +static void add_include_dir(char *dir) +{ + include_dirs = (char **)jit_realloc + (include_dirs, (num_include_dirs + 1) * sizeof(char *)); + if(!include_dirs) + { + dpas_out_of_memory(); + } + include_dirs[num_include_dirs++] = dir; +} + +void dpas_out_of_memory(void) +{ + fputs(progname, stderr); + fputs(": virtual memory exhausted\n", stderr); + exit(1); +} + +void dpas_import(const char *name) +{ + int posn; + char *pathname; + FILE *file; + + /* Bail out if we've already included this name before */ + for(posn = 0; posn < num_using_seen; ++posn) + { + if(!jit_strcmp(using_seen[posn], name)) + { + return; + } + } + + /* Add the name to the "seen" list */ + using_seen = (char **)jit_realloc + (using_seen, (num_using_seen + 1) * sizeof(char *)); + if(!using_seen) + { + dpas_out_of_memory(); + } + using_seen[num_using_seen] = jit_strdup(name); + if(!(using_seen[num_using_seen])) + { + dpas_out_of_memory(); + } + ++num_using_seen; + + /* Look in the same directory as the including source file first */ + posn = jit_strlen(dpas_filename); + while(posn > 0 && dpas_filename[posn - 1] != '/' && + dpas_filename[posn - 1] != '\\') + { + --posn; + } + if(posn > 0) + { + pathname = (char *)jit_malloc(posn + jit_strlen(name) + 5); + if(!pathname) + { + dpas_out_of_memory(); + } + jit_strncpy(pathname, dpas_filename, posn); + jit_strcpy(pathname + posn, name); + jit_strcat(pathname, ".pas"); + } + else + { + pathname = (char *)jit_malloc(jit_strlen(name) + 5); + if(!pathname) + { + dpas_out_of_memory(); + } + jit_strcpy(pathname, name); + jit_strcat(pathname, ".pas"); + } + if((file = fopen(pathname, "r")) != NULL) + { + dpas_load_file(pathname, file); + fclose(file); + jit_free(pathname); + return; + } + jit_free(pathname); + + /* Scan the include directories looking for the name */ + for(posn = 0; posn < num_include_dirs; ++posn) + { + pathname = (char *)jit_malloc(jit_strlen(include_dirs[posn]) + + jit_strlen(name) + 6); + if(!pathname) + { + dpas_out_of_memory(); + } + jit_strcpy(pathname, include_dirs[posn]); + jit_strcat(pathname, "/"); + jit_strcat(pathname, name); + jit_strcat(pathname, ".pas"); + if((file = fopen(pathname, "r")) != NULL) + { + dpas_load_file(pathname, file); + fclose(file); + jit_free(pathname); + return; + } + jit_free(pathname); + } + + /* If we get here, then we could not find the specified module */ + fprintf(stderr, "%s:%ld: could not locate the module `%s'\n", + dpas_filename, dpas_linenum, name); + dpas_error_reported = 1; +} + +/* + * Initialize the system. + */ +static void initialize(void) +{ + jit_init(); + dpas_init_types(); +} diff --git a/dpas/dpas-parser.y b/dpas/dpas-parser.y new file mode 100644 index 0000000..2b3c513 --- /dev/null +++ b/dpas/dpas-parser.y @@ -0,0 +1,2247 @@ +%{ +/* + * dpas-parser.y - Bison grammar for the Dynamic Pascal language. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "dpas-internal.h" +#include +#include +#ifdef HAVE_STDLIB_H + #include +#endif +#ifdef HAVE_STDARG_H + #include +#elif HAVE_VARARGS_H + #include +#endif + +/* + * Imports from the lexical analyser. + */ +extern int yylex(void); +#ifdef YYTEXT_POINTER +extern char *yytext; +#else +extern char yytext[]; +#endif + +/* + * Error reporting flag. + */ +int dpas_error_reported = 0; + +/* + * Report error messages from the parser. + */ +static void yyerror(char *msg) +{ + char *text = yytext; + char *newmsg; + int posn, outposn; + dpas_error_reported = 1; + if(!jit_strcmp(msg, "parse error") || !jit_strcmp(msg, "syntax error")) + { + /* This error is pretty much useless at telling the user + what is happening. Try to print a better message + based on the current input token */ + simpleError: + if(text && *text != '\0') + { + fprintf(stderr, "%s:%ld: parse error at or near `%s'\n", + dpas_filename, dpas_linenum, text); + } + else + { + fprintf(stderr, "%s:%ld: parse error\n", + dpas_filename, dpas_linenum); + } + } + else if(!jit_strncmp(msg, "parse error, expecting `", 24)) + { + /* We have to quote the token names in the "%token" declarations + within yacc grammars so that byacc doesn't mess up the output. + But the quoting causes Bison to output quote characters in + error messages which look awful. This code attempts to fix + things up */ + newmsg = jit_strdup(msg); + expectingError: + if(newmsg) + { + posn = 0; + outposn = 0; + while(newmsg[posn] != '\0') + { + if(newmsg[posn] == '`') + { + if(newmsg[posn + 1] == '"' && newmsg[posn + 2] == '`') + { + /* Convert <`"`> into <`> */ + posn += 2; + newmsg[outposn++] = '`'; + } + else if(newmsg[posn + 1] == '"') + { + /* Convert <`"> into <> */ + ++posn; + } + else if(newmsg[posn + 1] == '`' || + newmsg[posn + 1] == '\'') + { + /* Convert <``> or <`'> into <`> */ + ++posn; + newmsg[outposn++] = '`'; + } + else + { + /* Ordinary <`> on its own */ + newmsg[outposn++] = '`'; + } + } + else if(newmsg[posn] == '\\') + { + /* Ignore backslashes in the input */ + } + else if(newmsg[posn] == '"' && newmsg[posn + 1] == '\'') + { + /* Convert <"'> into <> */ + ++posn; + } + else if(newmsg[posn] == '\'' && newmsg[posn + 1] == '"' && + newmsg[posn + 2] == '\'') + { + /* Convert <'"'> into <'> */ + posn += 2; + newmsg[outposn++] = '\''; + } + else if(newmsg[posn] == '\'' && newmsg[posn + 1] == '\'') + { + /* Convert <''> into <'> */ + ++posn; + newmsg[outposn++] = '\''; + } + else if(newmsg[posn] == ' ' && newmsg[posn + 1] == '\'') + { + /* bison 1.75 - <'> following a space becomes <`> */ + ++posn; + newmsg[outposn++] = ' '; + newmsg[outposn++] = '`'; + } + else if(newmsg[posn] == '"') + { + /* Ignore quotes - bison 1.75 */ + } + else + { + /* Ordinary character */ + newmsg[outposn++] = newmsg[posn]; + } + ++posn; + } + newmsg[outposn] = '\0'; + if(text && *text != '\0') + { + fprintf(stderr, "%s:%ld: %s, at or near `%s'\n", + dpas_filename, dpas_linenum, newmsg, text); + } + else + { + fprintf(stderr, "%s:%ld: %s\n", + dpas_filename, dpas_linenum, newmsg); + } + jit_free(newmsg); + } + else + { + if(text && *text != '\0') + { + fprintf(stderr, "%s:%ld: %s at or near `%s'\n", + dpas_filename, dpas_linenum, msg, text); + } + else + { + fprintf(stderr, "%s:%ld: %s\n", + dpas_filename, dpas_linenum, msg); + } + } + } + else if(!jit_strncmp(msg, "parse error, unexpected ", 24)) + { + /* The error probably has the form "parse error, unexpected ..., + expecting ..." - strip out the "unexpected" part */ + posn = 24; + while(msg[posn] != '\0' && + jit_strncmp(msg + posn, ", expecting ", 12) != 0) + { + ++posn; + } + if(msg[posn] == '\0') + { + goto simpleError; + } + newmsg = (char *)jit_malloc(jit_strlen(msg) + 1); + if(!newmsg) + { + goto defaultError; + } + jit_strcpy(newmsg, "parse error, expecting "); + jit_strcat(newmsg, msg + posn + 12); + goto expectingError; + } + else + { + /* The parser has probably included information as to what + is expected in this context, so report that */ + defaultError: + if(text && *text != '\0') + { + fprintf(stderr, "%s:%ld: %s at or near `%s'\n", + dpas_filename, dpas_linenum, msg, text); + } + else + { + fprintf(stderr, "%s:%ld: %s\n", + dpas_filename, dpas_linenum, msg); + } + } +} + +void dpas_error(const char *format, ...) +{ + va_list va; +#ifdef HAVE_STDARG_H + va_start(va, format); +#else + va_start(va); +#endif + fprintf(stderr, "%s:%ld: ", dpas_filename, dpas_linenum); + vfprintf(stderr, format, va); + putc('\n', stderr); + va_end(va); + dpas_error_reported = 1; +} + +void dpas_warning(const char *format, ...) +{ + va_list va; +#ifdef HAVE_STDARG_H + va_start(va, format); +#else + va_start(va); +#endif + fprintf(stderr, "%s:%ld: warning: ", dpas_filename, dpas_linenum); + vfprintf(stderr, format, va); + putc('\n', stderr); + va_end(va); +} + +void dpas_error_on_line(const char *filename, long linenum, + const char *format, ...) +{ + va_list va; +#ifdef HAVE_STDARG_H + va_start(va, format); +#else + va_start(va); +#endif + fprintf(stderr, "%s:%ld: ", filename, linenum); + vfprintf(stderr, format, va); + putc('\n', stderr); + va_end(va); + dpas_error_reported = 1; +} + +static void dpas_undeclared(const char *name) +{ + dpas_error("`%s' is not declared in the current scope", name); +} + +static void dpas_redeclared(const char *name, dpas_scope_item_t item) +{ + dpas_error("`%s' is already declared in the current scope", name); + dpas_error_on_line(dpas_scope_item_filename(item), + dpas_scope_item_linenum(item), + "previous declaration of `%s' here", name); +} + +/* + * Add an item to a list of identifiers. + */ +static void identifier_list_add(char ***list, int *len, char *name) +{ + char **new_list = (char **)jit_realloc(*list, sizeof(char *) * (*len + 1)); + if(!new_list) + { + dpas_out_of_memory(); + } + new_list[*len] = name; + ++(*len); + *list = new_list; +} + +/* + * Free the contents of an identifier list. + */ +static void identifier_list_free(char **list, int len) +{ + int posn; + for(posn = 0; posn < len; ++posn) + { + jit_free(list[posn]); + } + jit_free(list); +} + +/* + * Determine if an identifier list contains a specific item. + */ +static int identifier_list_contains(char **list, int len, const char *name) +{ + int posn; + for(posn = 0; posn < len; ++posn) + { + if(list[posn] && !jit_stricmp(list[posn], name)) + { + return 1; + } + } + return 0; +} + +/* + * Add an item to a list of types. + */ +static void type_list_add(jit_type_t **list, int *len, jit_type_t type) +{ + jit_type_t *new_list = (jit_type_t *) + jit_realloc(*list, sizeof(jit_type_t) * (*len + 1)); + if(!new_list) + { + dpas_out_of_memory(); + } + new_list[*len] = type; + ++(*len); + *list = new_list; +} + +/* + * Initialize a parameter list. + */ +static void parameter_list_init(dpas_params *list) +{ + list->names = 0; + list->types = 0; + list->len = 0; + list->has_vararg = 0; +} + +/* + * Add an item to a list of parameters. + */ +static void parameter_list_add(dpas_params *list, char *name, jit_type_t type) +{ + char **new_names = (char **) + jit_realloc(list->names, sizeof(char *) * (list->len + 1)); + jit_type_t *new_types = (jit_type_t *) + jit_realloc(list->types, sizeof(jit_type_t) * (list->len + 1)); + if(!new_names || !new_types) + { + dpas_out_of_memory(); + } + new_names[list->len] = name; + new_types[list->len] = type; + ++(list->len); + list->names = new_names; + list->types = new_types; +} + +/* + * Free the contents of a parameter list. + */ +static void parameter_list_free(dpas_params *list) +{ + int posn; + for(posn = 0; posn < list->len; ++posn) + { + jit_free(list->names[posn]); + jit_type_free(list->types[posn]); + } + jit_free(list->names); + jit_free(list->types); +} + +/* + * Determine if a parameter list contains a specific item. + */ +static int parameter_list_contains(dpas_params *list, const char *name) +{ + int posn; + for(posn = 0; posn < list->len; ++posn) + { + if(list->names[posn] && !jit_stricmp(list->names[posn], name)) + { + return 1; + } + } + return 0; +} + +/* + * Create a new parameter list. + */ +static void parameter_list_create(dpas_params *list, char **names, + int num_names, jit_type_t type) +{ + char **temp = names; + parameter_list_init(list); + while(num_names > 0) + { + parameter_list_add(list, temp[0], jit_type_copy(type)); + --num_names; + ++temp; + } + if(names) + { + jit_free(names); + } +} + +/* + * Merge two parameter lists into one. + */ +static void parameter_list_merge(dpas_params *list1, dpas_params *list2) +{ + int posn; + char *name; + for(posn = 0; posn < list2->len; ++posn) + { + name = list2->names[posn]; + if(name && parameter_list_contains(list1, name)) + { + dpas_error("`%s' used twice in a parameter or field list", name); + jit_free(name); + name = 0; + } + parameter_list_add(list1, name, list2->types[posn]); + } + jit_free(list2->names); + jit_free(list2->types); +} + +/* + * Handle a numeric binary operator. + */ +#define handle_binary(name,func,arg1,arg2) \ + do { \ + if(!dpas_sem_is_rvalue(arg1) || \ + !dpas_type_is_numeric(dpas_sem_get_type(arg2)) || \ + !dpas_sem_is_rvalue(arg1) || \ + !dpas_type_is_numeric(dpas_sem_get_type(arg2))) \ + { \ + if(!dpas_sem_is_error(arg1) && !dpas_sem_is_error(arg2)) \ + { \ + dpas_error("invalid operands to binary `" name "'"); \ + } \ + dpas_sem_set_error(yyval.semvalue); \ + } \ + else \ + { \ + jit_value_t value; \ + value = func \ + (dpas_current_function(), \ + dpas_sem_get_value(arg1), \ + dpas_sem_get_value(arg2)); \ + dpas_sem_set_rvalue \ + (yyval.semvalue, jit_value_get_type(value), value); \ + } \ + } while (0) + +/* + * Handle an integer binary operator. + */ +#define handle_integer_binary(name,func,arg1,arg2) \ + do { \ + if(!dpas_sem_is_rvalue(arg1) || \ + !dpas_type_is_integer(dpas_sem_get_type(arg2)) || \ + !dpas_sem_is_rvalue(arg1) || \ + !dpas_type_is_integer(dpas_sem_get_type(arg2))) \ + { \ + if(!dpas_sem_is_error(arg1) && !dpas_sem_is_error(arg2)) \ + { \ + dpas_error("invalid operands to binary `" name "'"); \ + } \ + dpas_sem_set_error(yyval.semvalue); \ + } \ + else \ + { \ + jit_value_t value; \ + value = func \ + (dpas_current_function(), \ + dpas_sem_get_value(arg1), \ + dpas_sem_get_value(arg2)); \ + dpas_sem_set_rvalue \ + (yyval.semvalue, jit_value_get_type(value), value); \ + } \ + } while (0) + +/* + * Handle a comparison binary operator. + */ +#define handle_compare_binary(name,func,arg1,arg2) \ + do { \ + if(dpas_sem_is_rvalue(arg1) && \ + jit_type_is_pointer(dpas_sem_get_type(arg1)) && \ + dpas_sem_is_rvalue(arg2) && \ + jit_type_is_pointer(dpas_sem_get_type(arg2))) \ + { \ + jit_value_t value; \ + value = func \ + (dpas_current_function(), \ + dpas_sem_get_value(arg1), \ + dpas_sem_get_value(arg2)); \ + dpas_sem_set_rvalue \ + (yyval.semvalue, jit_value_get_type(value), value); \ + } \ + else \ + { \ + handle_binary(name, func, arg1, arg2); \ + } \ + } while (0) + +%} + +/* + * Define the structure of yylval. + */ +%union { + char *name; + struct + { + jit_ulong value; + jit_type_t type; + } int_const; + jit_nfloat real_const; + jit_constant_t const_value; + jit_type_t type; + struct + { + char **list; + int len; + } id_list; + struct + { + jit_type_t *list; + int len; + } type_list; + dpas_params parameters; + struct + { + char *name; + jit_type_t type; + } procedure; + struct + { + jit_type_t type; + dpas_params bounds; + } param_type; + dpas_semvalue semvalue; +} + +/* + * Primitive lexical tokens. + */ +%token IDENTIFIER "an identifier" +%token INTEGER_CONSTANT "an integer value" +%token STRING_CONSTANT "a string literal" +%token REAL_CONSTANT "a floating point value" + +/* + * Keywords. + */ +%token K_AND "`and'" +%token K_ARRAY "`array'" +%token K_BEGIN "`begin'" +%token K_CASE "`case'" +%token K_CATCH "`catch'" +%token K_CONST "`const'" +%token K_DIV "`div'" +%token K_DO "`do'" +%token K_DOWNTO "`downto'" +%token K_ELSE "`else'" +%token K_END "`end'" +%token K_EXIT "`exit'" +%token K_FINALLY "`finally'" +%token K_FOR "`for'" +%token K_FORWARD "`forward'" +%token K_FUNCTION "`function'" +%token K_GOTO "`goto'" +%token K_IF "`if'" +%token K_IN "`in'" +%token K_LABEL "`label'" +%token K_IMPORT "`import'" +%token K_MOD "`mod'" +%token K_MODULE "`module'" +%token K_NIL "`nil'" +%token K_NOT "`not'" +%token K_OF "`of'" +%token K_OR "`or'" +%token K_PACKED "`packed'" +%token K_POW "`pow'" +%token K_PROCEDURE "`procedure'" +%token K_PROGRAM "`program'" +%token K_RECORD "`record'" +%token K_REPEAT "`repeat'" +%token K_SET "`set'" +%token K_SHL "`shl'" +%token K_SHR "`shr'" +%token K_SIZEOF "`sizeof'" +%token K_THEN "`then'" +%token K_THROW "`throw'" +%token K_TO "`to'" +%token K_TRY "`try'" +%token K_TYPE "`type'" +%token K_UNTIL "`until'" +%token K_VAR "`var'" +%token K_VA_ARG "`va_arg'" +%token K_WITH "`with'" +%token K_WHILE "`while'" +%token K_XOR "`xor'" + +/* + * Operators. + */ +%token K_NE "`<>'" +%token K_LE "`<='" +%token K_GE "`>='" +%token K_ASSIGN "`:='" +%token K_DOT_DOT "`..'" + +/* + * Define the yylval types of the various non-terminals. + */ +%type IDENTIFIER STRING_CONSTANT Identifier Directive +%type INTEGER_CONSTANT +%type REAL_CONSTANT +%type Constant ConstantValue BasicConstant +%type IdentifierList +%type ArrayBoundsList VariantCaseList + +%type Type TypeIdentifier SimpleType StructuredType +%type BoundType Variant VariantList + +%type ParameterType ConformantArray + +%type FormalParameterList FormalParameterSection +%type FormalParameterSections FieldList FixedPart +%type RecordSection VariantPart BoundSpecificationList +%type BoundSpecification + +%type ProcedureHeading FunctionHeading +%type ProcedureOrFunctionHeading + +%type Variable Expression SimpleExpression +%type AdditionExpression Term Factor Power + +%expect 3 + +%start Program +%% + +/* + * Programs. + */ + +Program + : ProgramHeading ImportDeclarationPart ProgramBlock '.' + ; + +ProgramHeading + : K_PROGRAM Identifier '(' IdentifierList ')' ';' { + jit_free($2); + identifier_list_free($4.list, $4.len); + } + | K_PROGRAM Identifier ';' { + jit_free($2); + } + | K_MODULE Identifier '(' IdentifierList ')' ';' { + /* The "module" keyword is provided as an alternative + to "program" because it looks odd to put "program" + on code included via an "import" clause */ + jit_free($2); + identifier_list_free($4.list, $4.len); + } + | K_MODULE Identifier ';' { + jit_free($2); + } + ; + +ImportDeclarationPart + : /* empty */ + | K_IMPORT ImportDeclarations ';' + ; + +ImportDeclarations + : Identifier { + dpas_import($1); + jit_free($1); + } + | ImportDeclarations ',' Identifier { + dpas_import($3); + jit_free($3); + } + ; + +ProgramBlock + : ProgramBlock2 { + /* Get the "main" function for this module */ + jit_function_t func = dpas_current_function(); + + /* Make sure that the function is properly terminated */ + if(!jit_insn_default_return(func)) + { + dpas_out_of_memory(); + } + + /* Compile the function */ + /* TODO */ + jit_dump_function(stdout, func, "main"); + + /* Pop the "main" function */ + dpas_pop_function(); + } + ; + +ProgramBlock2 + : Block + | K_END + ; + +/* + * Identifiers and lists of identifiers. + */ + +Identifier + : IDENTIFIER { $$ = $1; } + ; + +IdentifierList + : Identifier { + $$.list = 0; + $$.len = 0; + identifier_list_add(&($$.list), &($$.len), $1); + } + | IdentifierList ',' Identifier { + $$ = $1; + if(identifier_list_contains($$.list, $$.len, $3)) + { + dpas_error("`%s' declared twice in an identifier list", $3); + identifier_list_add(&($$.list), &($$.len), 0); + jit_free($3); + } + else + { + identifier_list_add(&($$.list), &($$.len), $3); + } + } + ; + +/* + * Blocks. + */ + +Block + : DeclarationPart StatementPart + ; + +DeclarationPart + : LabelDeclarationPart ConstantDefinitionPart TypeDefinitionPart + VariableDeclarationPart ProcedureAndFunctionDeclarationPart + ; + +LabelDeclarationPart + : /* empty */ + | K_LABEL LabelList ';' + ; + +LabelList + : Label + | LabelList ',' Label + ; + +Label + : INTEGER_CONSTANT { /* label declarations are ignored */ } + ; + +ConstantDefinitionPart + : /* empty */ + | K_CONST ConstantDefinitionList + ; + +ConstantDefinitionList + : ConstantDefinition + | ConstantDefinitionList ConstantDefinition + ; + +ConstantDefinition + : Identifier '=' Constant ';' { + dpas_scope_item_t item; + item = dpas_scope_lookup(dpas_scope_current(), $1, 0); + if(item) + { + dpas_redeclared($1, item); + } + else + { + dpas_scope_add_const(dpas_scope_current(), $1, &($3), + dpas_filename, dpas_linenum); + } + jit_free($1); + jit_type_free($3.type); + } + ; + +TypeDefinitionPart + : /* empty */ + | K_TYPE TypeDefinitionList { + /* Check for undefined record types that were referred + to using a pointer reference of the form "^name" */ + dpas_scope_check_undefined(dpas_scope_current()); + } + ; + +TypeDefinitionList + : TypeDefinition + | TypeDefinitionList TypeDefinition + ; + +TypeDefinition + : Identifier '=' Type ';' { + dpas_scope_item_t item; + jit_type_t type; + item = dpas_scope_lookup(dpas_scope_current(), $1, 0); + type = (item ? dpas_scope_item_type(item) : 0); + if(!item) + { + dpas_type_set_name($3, $1); + dpas_scope_add(dpas_scope_current(), $1, $3, + DPAS_ITEM_TYPE, 0, 0, + dpas_filename, dpas_linenum); + } + else if(dpas_scope_item_kind(item) == DPAS_ITEM_TYPE && + jit_type_get_tagged_kind(type) == DPAS_TAG_NAME && + jit_type_get_tagged_type(type) == 0 && + jit_type_get_tagged_kind($3) == DPAS_TAG_NAME) + { + /* This is a defintion of a record type that was + previously encountered in a forward pointer + reference of the form "^name". We need to + back-patch the previous type with the type info */ + jit_type_set_tagged_type + (type, jit_type_get_tagged_type($3), 1); + } + else + { + dpas_redeclared($1, item); + } + jit_free($1); + jit_type_free($3); + } + ; + +VariableDeclarationPart + : /* empty */ + | K_VAR VariableDeclarationList + ; + +VariableDeclarationList + : VariableDeclaration + | VariableDeclarationList VariableDeclaration + ; + +VariableDeclaration + : IdentifierList ':' Type ';' { + /* Add each of the variables to the current scope */ + int posn; + dpas_scope_item_t item; + for(posn = 0; posn < $1.len; ++posn) + { + item = dpas_scope_lookup(dpas_scope_current(), + $1.list[posn], 0); + if(item) + { + dpas_redeclared($1.list[posn], item); + } + else + { + if(dpas_current_function()) + { + jit_value_t value; + value = jit_value_create + (dpas_current_function(), $3); + if(!value) + { + dpas_out_of_memory(); + } + dpas_scope_add(dpas_scope_current(), + $1.list[posn], $3, + DPAS_ITEM_VARIABLE, value, 0, + dpas_filename, dpas_linenum); + } + else + { + /* Allocate some memory to hold the global data */ + void *space = jit_calloc(1, jit_type_get_size($3)); + if(!space) + { + dpas_out_of_memory(); + } + dpas_scope_add(dpas_scope_current(), + $1.list[posn], $3, + DPAS_ITEM_GLOBAL_VARIABLE, space, 0, + dpas_filename, dpas_linenum); + } + } + } + + /* Free the id list and type, which we don't need any more */ + identifier_list_free($1.list, $1.len); + jit_type_free($3); + } + ; + +ProcedureAndFunctionDeclarationPart + : /* empty */ + | ProcedureOrFunctionList + ; + +ProcedureOrFunctionList + : ProcedureOrFunctionDeclaration ';' + | ProcedureOrFunctionList ProcedureOrFunctionDeclaration ';' + ; + +StatementPart + : K_BEGIN StatementSequence OptSemi K_END + | K_BEGIN OptSemi K_END + | K_BEGIN error K_END + ; + +OptSemi + : /* empty */ + | ';' + ; + +/* + * Procedure and function declarations. + */ + +ProcedureOrFunctionDeclaration + : ProcedureOrFunctionHeading ';' { + unsigned int num_params; + unsigned int param; + jit_type_t type; + const char *name; + dpas_scope_item_t item; + jit_function_t func; + + /* Declare the procedure/function into the current scope */ + item = dpas_scope_lookup(dpas_scope_current(), $1.name, 0); + if(item) + { + dpas_redeclared($1.name, item); + } + else + { + dpas_scope_add(dpas_scope_current(), $1.name, $1.type, + DPAS_ITEM_PROCEDURE, 0, 0, + dpas_filename, dpas_linenum); + } + + /* Push into a new scope for the procedure/function body */ + dpas_scope_push(); + func = dpas_new_function($1.type); + + /* Declare the parameters into the scope. If a name + is NULL, then it indicates that a duplicate parameter + name was detected, and replaced with no name */ + num_params = jit_type_num_params($1.type); + for(param = 0; param < num_params; ++param) + { + name = jit_type_get_name($1.type, param); + if(name) + { + jit_value_t value; + value = jit_value_get_param(func, param); + if(!value) + { + dpas_out_of_memory(); + } + dpas_scope_add(dpas_scope_current(), name, + jit_type_get_param($1.type, param), + DPAS_ITEM_VARIABLE, value, 0, + dpas_filename, dpas_linenum); + } + } + + /* Declare the function return variable into the scope */ + type = jit_type_get_return($1.type); + if(type != jit_type_void) + { + if(dpas_scope_lookup(dpas_scope_current(), $1.name, 0)) + { + dpas_error("`%s' is declared as both a parameter " + "and a function name", $1.name); + } + else + { + dpas_scope_add(dpas_scope_current(), $1.name, type, + DPAS_ITEM_FUNC_RETURN, 0, 0, + dpas_filename, dpas_linenum); + } + } + } Body { + jit_function_t func = dpas_current_function(); + int result; + + /* Make sure that the function is properly terminated */ + result = jit_insn_default_return(func); + if(!result) + { + dpas_out_of_memory(); + } + else if(result == 1 && + jit_type_get_return($1.type) != jit_type_void) + { + dpas_error("control reached the end of a function"); + } + + /* Pop from the procedure/function scope */ + dpas_pop_function(); + dpas_scope_pop(); + + /* TODO: compile the procedure/function */ + + jit_dump_function(stdout, func, $1.name); + + /* Free values that we no longer require */ + jit_free($1.name); + jit_type_free($1.type); + } + ; + +ProcedureOrFunctionHeading + : ProcedureHeading { $$ = $1; } + | FunctionHeading { $$ = $1; } + ; + +Body + : Block {} + | Directive {} + ; + +Directive + : K_FORWARD { $$ = 0; } + | K_IMPORT '(' STRING_CONSTANT ')' { $$ = $3; } + ; + +ProcedureHeading + : K_PROCEDURE Identifier FormalParameterList { + $$.name = $2; + $$.type = jit_type_create_signature + (($3.has_vararg ? jit_abi_vararg : jit_abi_cdecl), + jit_type_void, $3.types, (unsigned int)($3.len), 1); + if(!($$.type)) + { + dpas_out_of_memory(); + } + if(!jit_type_set_names($$.type, $3.names, + (unsigned int)($3.len))) + { + dpas_out_of_memory(); + } + parameter_list_free(&($3)); + } + ; + +FunctionHeading + : K_FUNCTION Identifier FormalParameterList ':' TypeIdentifier { + $$.name = $2; + $$.type = jit_type_create_signature + (($3.has_vararg ? jit_abi_vararg : jit_abi_cdecl), + $5, $3.types, (unsigned int)($3.len), 1); + if(!($$.type)) + { + dpas_out_of_memory(); + } + if(!jit_type_set_names($$.type, $3.names, + (unsigned int)($3.len))) + { + dpas_out_of_memory(); + } + parameter_list_free(&($3)); + jit_type_free($5); + } + ; + +FormalParameterList + : /* empty */ { parameter_list_init(&($$)); } + | '(' FormalParameterSections ')' { $$ = $2; } + | '(' FormalParameterSections ';' K_DOT_DOT ')' { + $$ = $2; + $$.has_vararg = 1; + } + ; + +FormalParameterSections + : FormalParameterSection { $$ = $1; } + | FormalParameterSections ';' FormalParameterSection { + $$ = $1; + parameter_list_merge(&($$), &($3)); + } + ; + +FormalParameterSection + : IdentifierList ':' ParameterType { + parameter_list_create(&($$), $1.list, $1.len, $3.type); + if($3.bounds.len > 0) + { + /* We should be using "var" with conformant array types */ + dpas_warning("`%s' should be declared as `var'", + $1.list[0]); + if($1.len > 1) + { + dpas_error("too many parameter names for " + "conformant array specification"); + } + parameter_list_merge(&($$), &($3.bounds)); + } + jit_type_free($3.type); + } + | K_VAR IdentifierList ':' ParameterType { + jit_type_t type = jit_type_create_pointer($4.type, 0); + if(!type) + { + dpas_out_of_memory(); + } + if($4.bounds.len == 0) + { + type = jit_type_create_tagged(type, DPAS_TAG_VAR, 0, 0, 0); + if(!type) + { + dpas_out_of_memory(); + } + } + parameter_list_create(&($$), $2.list, $2.len, type); + if($4.bounds.len > 0) + { + if($2.len > 1) + { + dpas_error("too many parameter names for " + "conformant array specification"); + } + parameter_list_merge(&($$), &($4.bounds)); + } + jit_type_free(type); + } + | ProcedureHeading { + parameter_list_init(&($$)); + parameter_list_add(&($$), $1.name, $1.type); + } + | FunctionHeading { + parameter_list_init(&($$)); + parameter_list_add(&($$), $1.name, $1.type); + } + ; + +ParameterType + : TypeIdentifier { + $$.type = $1; + parameter_list_init(&($$.bounds)); + } + | ConformantArray { $$ = $1; } + ; + +ConformantArray + : K_PACKED K_ARRAY '[' BoundSpecification ']' K_OF TypeIdentifier { + $$.type = dpas_create_conformant_array($7, $4.len, 1); + $$.bounds = $4; + } + | K_ARRAY '[' BoundSpecificationList ']' K_OF ParameterType { + $$.type = dpas_create_conformant_array($6.type, $3.len, 0); + $$.bounds = $3; + parameter_list_merge(&($$.bounds), &($6.bounds)); + } + ; + +BoundSpecificationList + : BoundSpecification { $$ = $1; } + | BoundSpecificationList ';' BoundSpecification { + $$ = $1; + parameter_list_merge(&($$), &($3)); + } + ; + +BoundSpecification + : Identifier K_DOT_DOT Identifier ':' TypeIdentifier { + if($5 != jit_type_int) + { + char *name = dpas_type_name($5); + dpas_error("`%s' cannot be used for array bounds; " + "must be `Integer'", name); + jit_free(name); + } + jit_type_free($5); + parameter_list_init(&($$)); + parameter_list_add(&($$), $1, jit_type_int); + if(jit_strcmp($1, $3) != 0) + { + parameter_list_add(&($$), $3, jit_type_int); + } + else + { + dpas_error("`%s' used twice in a parameter or " + "field list", $1); + parameter_list_add(&($$), 0, jit_type_int); + jit_free($3); + } + } + ; + +/* + * Statements. + */ + +StatementSequence + : Statement + | StatementSequence ';' Statement + ; + +Statement + : Label ':' InnerStatement + | InnerStatement + ; + +InnerStatement + : Variable K_ASSIGN Expression { + /* TODO: type checking and non-local variables */ + if(dpas_sem_is_lvalue($1) && dpas_sem_is_rvalue($3)) + { + if(!jit_insn_store(dpas_current_function(), + dpas_sem_get_value($1), + dpas_sem_get_value($3))) + { + dpas_out_of_memory(); + } + } + else + { + dpas_error("invalid assignment"); + } + } + | Identifier {} + | Identifier '(' ExpressionList ')' {} + | K_GOTO Label + | CompoundStatement + | K_WHILE Expression K_DO Statement + | K_REPEAT StatementSequence OptSemi K_UNTIL Expression + | K_FOR Identifier K_ASSIGN Expression Direction Expression K_DO Statement + | K_IF Expression K_THEN Statement + | K_IF Expression K_THEN Statement K_ELSE Statement + | CaseStatement + | K_WITH VariableList K_DO Statement + | K_THROW Expression + | K_THROW + | TryStatement + | K_EXIT + ; + +CompoundStatement + : K_BEGIN StatementSequence OptSemi K_END + | K_BEGIN error K_END + ; + +Direction + : K_TO + | K_DOWNTO + ; + +CaseStatement + : K_CASE Expression K_OF CaseLimbList + ; + +CaseLimbList + : CaseLimb + | CaseLimbList ';' CaseLimb + ; + +CaseLimb + : CaseLabelList ':' Statement + ; + +CaseLabelList + : Constant {} + | CaseLabelList ',' Constant {} + ; + +VariableList + : Variable {} + | VariableList ',' Variable {} + ; + +TryStatement + : K_TRY StatementSequence OptSemi CatchClause FinallyClause K_END + ; + +CatchClause + : /* empty */ + | K_CATCH Identifier ':' Type StatementSequence OptSemi + ; + +FinallyClause + : /* empty */ + | K_FINALLY StatementSequence OptSemi + ; + +/* + * Expressions. + */ + +Expression + : SimpleExpression { $$ = $1; } + | SimpleExpression '=' SimpleExpression { + handle_compare_binary("=", jit_insn_eq, $1, $3); + } + | SimpleExpression K_NE SimpleExpression { + handle_compare_binary("<>", jit_insn_ne, $1, $3); + } + | SimpleExpression '<' SimpleExpression { + handle_compare_binary("<", jit_insn_lt, $1, $3); + } + | SimpleExpression '>' SimpleExpression { + handle_compare_binary(">", jit_insn_gt, $1, $3); + } + | SimpleExpression K_LE SimpleExpression { + handle_compare_binary("<=", jit_insn_le, $1, $3); + } + | SimpleExpression K_GE SimpleExpression { + handle_compare_binary(">=", jit_insn_ge, $1, $3); + } + | SimpleExpression K_IN SimpleExpression { + /* TODO */ + } + ; + +ExpressionList + : Expression { /* TODO */ } + | ExpressionList ',' Expression { /* TODO */ } + ; + +SimpleExpression + : AdditionExpression { $$ = $1; } + | '+' AdditionExpression { + if(!dpas_sem_is_rvalue($2) || + !dpas_type_is_numeric(dpas_sem_get_type($2))) + { + if(!dpas_sem_is_error($2)) + { + dpas_error("invalid operand to unary `+'"); + } + dpas_sem_set_error($$); + } + else + { + $$ = $2; + } + } + | '-' AdditionExpression { + if(!dpas_sem_is_rvalue($2) || + !dpas_type_is_numeric(dpas_sem_get_type($2))) + { + if(!dpas_sem_is_error($2)) + { + dpas_error("invalid operand to unary `-'"); + } + dpas_sem_set_error($$); + } + else + { + jit_value_t value; + value = jit_insn_neg + (dpas_current_function(), dpas_sem_get_value($2)); + dpas_sem_set_rvalue($$, jit_value_get_type(value), value); + } + } + ; + +AdditionExpression + : Term { $$ = $1; } + | AdditionExpression '+' Term { + handle_binary("+", jit_insn_add, $1, $3); + } + | AdditionExpression '-' Term { + handle_binary("-", jit_insn_sub, $1, $3); + } + | AdditionExpression K_OR Term { + if(dpas_sem_is_rvalue($1) && + dpas_type_is_boolean(dpas_sem_get_type($1)) && + dpas_sem_is_rvalue($3) && + dpas_type_is_boolean(dpas_sem_get_type($3))) + { + /* Output code to compute a short-circuited "or" */ + jit_label_t label1 = jit_label_undefined; + jit_label_t label2 = jit_label_undefined; + jit_value_t value, const_value; + if(!jit_insn_branch_if + (dpas_current_function(), + dpas_sem_get_value($1), &label1)) + { + dpas_out_of_memory(); + } + if(!jit_insn_branch_if + (dpas_current_function(), + dpas_sem_get_value($3), &label1)) + { + dpas_out_of_memory(); + } + value = jit_value_create + (dpas_current_function(), jit_type_int); + const_value = jit_value_create_nint_constant + (dpas_current_function(), jit_type_int, 0); + if(!value || !const_value) + { + dpas_out_of_memory(); + } + if(!jit_insn_store(dpas_current_function(), + value, const_value)) + { + dpas_out_of_memory(); + } + if(!jit_insn_branch(dpas_current_function(), &label2)) + { + dpas_out_of_memory(); + } + if(!jit_insn_label(dpas_current_function(), &label1)) + { + dpas_out_of_memory(); + } + const_value = jit_value_create_nint_constant + (dpas_current_function(), jit_type_int, 1); + if(!const_value) + { + dpas_out_of_memory(); + } + if(!jit_insn_store(dpas_current_function(), + value, const_value)) + { + dpas_out_of_memory(); + } + if(!jit_insn_label(dpas_current_function(), &label2)) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue($$, dpas_type_boolean, value); + } + else + { + handle_integer_binary("or", jit_insn_or, $1, $3); + } + } + ; + +Term + : Power { $$ = $1; } + | Term '*' Power { + handle_binary("*", jit_insn_div, $1, $3); + } + | Term '/' Power { + /* Standard Pascal always returns a floating-point + result for '/', but we don't do that here yet */ + handle_binary("/", jit_insn_div, $1, $3); + } + | Term K_DIV Power { + handle_binary("div", jit_insn_div, $1, $3); + } + | Term K_MOD Power { + handle_binary("mod", jit_insn_rem, $1, $3); + } + | Term K_AND Power { + if(dpas_sem_is_rvalue($1) && + dpas_type_is_boolean(dpas_sem_get_type($1)) && + dpas_sem_is_rvalue($3) && + dpas_type_is_boolean(dpas_sem_get_type($3))) + { + /* Output code to compute a short-circuited "and" */ + jit_label_t label1 = jit_label_undefined; + jit_label_t label2 = jit_label_undefined; + jit_value_t value, const_value; + if(!jit_insn_branch_if_not + (dpas_current_function(), + dpas_sem_get_value($1), &label1)) + { + dpas_out_of_memory(); + } + if(!jit_insn_branch_if_not + (dpas_current_function(), + dpas_sem_get_value($3), &label1)) + { + dpas_out_of_memory(); + } + value = jit_value_create + (dpas_current_function(), jit_type_int); + const_value = jit_value_create_nint_constant + (dpas_current_function(), jit_type_int, 1); + if(!value || !const_value) + { + dpas_out_of_memory(); + } + if(!jit_insn_store(dpas_current_function(), + value, const_value)) + { + dpas_out_of_memory(); + } + if(!jit_insn_branch(dpas_current_function(), &label2)) + { + dpas_out_of_memory(); + } + if(!jit_insn_label(dpas_current_function(), &label1)) + { + dpas_out_of_memory(); + } + const_value = jit_value_create_nint_constant + (dpas_current_function(), jit_type_int, 0); + if(!const_value) + { + dpas_out_of_memory(); + } + if(!jit_insn_store(dpas_current_function(), + value, const_value)) + { + dpas_out_of_memory(); + } + if(!jit_insn_label(dpas_current_function(), &label2)) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue($$, dpas_type_boolean, value); + } + else + { + handle_integer_binary("and", jit_insn_and, $1, $3); + } + } + | Term K_XOR Power { + handle_integer_binary("xor", jit_insn_xor, $1, $3); + } + | Term K_SHL Power { + handle_integer_binary("shl", jit_insn_shl, $1, $3); + } + | Term K_SHR Power { + handle_integer_binary("shr", jit_insn_shr, $1, $3); + } + ; + +Power + : Factor { $$ = $1; } + | Power K_POW Factor { + handle_binary("pow", jit_insn_pow, $1, $3); + } + ; + +Factor + : Variable { $$ = $1; } + | BasicConstant { + jit_value_t value = jit_value_create_constant + (dpas_current_function(), &($1)); + dpas_sem_set_rvalue($$, $1.type, value); + } + | '[' ExpressionList ']' { /* TODO */ } + | '[' ']' { /* TODO */ } + | K_NOT Factor { + jit_value_t value; + if(dpas_sem_is_rvalue($2) && + dpas_type_is_boolean(dpas_sem_get_type($2))) + { + value = jit_insn_to_not_bool + (dpas_current_function(), dpas_sem_get_value($2)); + dpas_sem_set_rvalue($$, dpas_type_boolean, value); + } + else if(dpas_sem_is_rvalue($2) && + dpas_type_is_integer(dpas_sem_get_type($2))) + { + value = jit_insn_not + (dpas_current_function(), dpas_sem_get_value($2)); + dpas_sem_set_rvalue($$, jit_value_get_type(value), value); + } + else + { + if(!dpas_sem_is_error($2)) + { + dpas_error("invalid operand to unary `not'"); + } + dpas_sem_set_error($$); + } + } + | '&' Factor { + jit_type_t type; + if(dpas_sem_is_lvalue($2)) + { + jit_value_t value; + value = jit_insn_address_of + (dpas_current_function(), dpas_sem_get_value($2)); + type = jit_type_create_pointer(dpas_sem_get_type($2), 1); + if(!type) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue($$, type, value); + } + else if(dpas_sem_is_lvalue_ea($2)) + { + /* Turn the effective address into an r-value */ + type = jit_type_create_pointer(dpas_sem_get_type($2), 1); + if(!type) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue($$, type, dpas_sem_get_value($2)); + } + else + { + if(!dpas_sem_is_error($2)) + { + dpas_error("l-value required for address-of operator"); + } + dpas_sem_set_error($$); + } + } + | '@' Factor { + jit_type_t type; + if(dpas_sem_is_lvalue($2)) + { + jit_value_t value; + value = jit_insn_address_of + (dpas_current_function(), dpas_sem_get_value($2)); + type = jit_type_create_pointer(dpas_sem_get_type($2), 1); + if(!type) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue($$, jit_value_get_type(value), value); + } + else if(dpas_sem_is_lvalue_ea($2)) + { + /* Turn the effective address into an r-value */ + type = jit_type_create_pointer(dpas_sem_get_type($2), 1); + if(!type) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue($$, type, dpas_sem_get_value($2)); + } + else + { + if(!dpas_sem_is_error($2)) + { + dpas_error("l-value required for address-of operator"); + } + dpas_sem_set_error($$); + } + } + | '(' Expression ')' { $$ = $2; } + | Variable '(' ExpressionList ')' { /* TODO */ } + | K_VA_ARG '(' TypeIdentifier ')' { /* TODO */ } + | K_SIZEOF '(' Variable ')' { /* TODO */ } + | '(' K_IF Expression K_THEN Expression K_ELSE Expression ')' { + /* TODO */ + } + ; + +Variable + : Identifier { + dpas_scope_item_t item; + item = dpas_scope_lookup(dpas_scope_current(), $1, 1); + if(!item) + { + dpas_error("`%s' is not declared in the current scope", $1); + dpas_sem_set_error($$); + } + else + { + switch(dpas_scope_item_kind(item)) + { + case DPAS_ITEM_TYPE: + { + dpas_sem_set_type($$, dpas_scope_item_type(item)); + } + break; + + case DPAS_ITEM_VARIABLE: + { + dpas_sem_set_lvalue + ($$, dpas_scope_item_type(item), + (jit_value_t)dpas_scope_item_info(item)); + } + break; + + case DPAS_ITEM_GLOBAL_VARIABLE: + { + jit_value_t value; + void *address = dpas_scope_item_info(item); + value = jit_value_create_nint_constant + (dpas_current_function(), + jit_type_void_ptr, (jit_nint)address); + if(!value) + { + dpas_out_of_memory(); + } + dpas_sem_set_lvalue_ea + ($$, dpas_scope_item_type(item), value); + } + break; + + case DPAS_ITEM_CONSTANT: + { + jit_value_t const_value; + const_value = jit_value_create_constant + (dpas_current_function(), + (jit_constant_t *)dpas_scope_item_info(item)); + if(!const_value) + { + dpas_out_of_memory(); + } + dpas_sem_set_rvalue + ($$, dpas_scope_item_type(item), const_value); + } + break; + + case DPAS_ITEM_PROCEDURE: + { + dpas_sem_set_procedure + ($$, dpas_scope_item_type(item), item); + } + break; + + case DPAS_ITEM_WITH: + { + /* TODO */ + dpas_sem_set_error($$); + } + break; + + case DPAS_ITEM_FUNC_RETURN: + { + dpas_sem_set_return + ($$, dpas_scope_item_type(item)); + } + break; + + default: + { + dpas_sem_set_error($$); + } + break; + } + } + jit_free($1); + } + | Variable '[' ExpressionList ']' { + /* TODO */ + } + | Variable '.' Identifier { + /* Fetch the effective address of a structure field */ + /* TODO */ + } + | Variable '^' { + /* Dereference a pointer value */ + jit_value_t value; + if(!jit_type_is_pointer(dpas_sem_get_type($1))) + { + if(!dpas_sem_is_error($1)) + { + dpas_error("invalid operand to unary `^'"); + } + dpas_sem_set_error($$); + } + else if(dpas_sem_is_lvalue($1) || dpas_sem_is_rvalue($1)) + { + /* Turn the pointer value into an effective address */ + value = jit_insn_add_relative + (dpas_current_function(), dpas_sem_get_value($1), 0); + if(!value) + { + dpas_out_of_memory(); + } + dpas_sem_set_lvalue_ea + ($$, jit_type_get_ref(dpas_sem_get_type($1)), value); + } + else if(dpas_sem_is_lvalue_ea($1)) + { + /* Fetch the pointer value and construct a new adddress */ + value = jit_insn_load_relative + (dpas_current_function(), dpas_sem_get_value($1), + 0, jit_type_void_ptr); + if(!value) + { + dpas_out_of_memory(); + } + dpas_sem_set_lvalue_ea + ($$, jit_type_get_ref(dpas_sem_get_type($1)), value); + } + else + { + if(!dpas_sem_is_error($1)) + { + dpas_error("invalid operand to unary `^'"); + } + dpas_sem_set_error($$); + } + } + ; + +/* + * Types. + */ + +TypeIdentifier + : Identifier { + dpas_scope_item_t item; + item = dpas_scope_lookup(dpas_scope_current(), $1, 1); + if(!item) + { + dpas_undeclared($1); + $$ = jit_type_void; + } + else if(dpas_scope_item_kind(item) != DPAS_ITEM_TYPE) + { + dpas_error("`%s' does not refer to a type in the " + "current scope", $1); + $$ = jit_type_void; + } + else + { + $$ = jit_type_copy(dpas_scope_item_type(item)); + } + jit_free($1); + } + ; + +Type + : SimpleType { $$ = $1; } + | StructuredType { $$ = $1; } + | K_PACKED StructuredType { $$ = $2; } + ; + +SimpleType + : TypeIdentifier { $$ = $1; } + | '(' IdentifierList ')' { + jit_type_t type; + int posn; + dpas_scope_item_t item; + jit_constant_t value; + + /* Create the enumerated type */ + type = dpas_create_enum(jit_type_int, $2.len); + + /* Declare all of the identifiers into the current scope + as constants whose values correspond to the positions */ + for(posn = 0; posn < $2.len; ++posn) + { + if(!($2.list[posn])) + { + /* Placeholder for a duplicate identifier. The error + was already reported inside "IdentifierList" */ + continue; + } + item = dpas_scope_lookup(dpas_scope_current(), + $2.list[posn], 0); + if(item) + { + dpas_redeclared($2.list[posn], item); + continue; + } + value.type = type; + value.un.int_value = (jit_int)posn; + dpas_scope_add_const(dpas_scope_current(), + $2.list[posn], &value, + dpas_filename, dpas_linenum); + } + + /* Free the identifier list, which we no longer need */ + identifier_list_free($2.list, $2.len); + + /* Return the type as our semantic value */ + $$ = type; + } + | Constant K_DOT_DOT Constant { + /* Infer a common type for the subrange */ + jit_type_t type = dpas_common_type($1.type, $3.type, 1); + if(type) + { + /* TODO: check that value1 <= value2 */ + dpas_subrange range; + dpas_convert_constant(&(range.first), &($1), type); + dpas_convert_constant(&(range.last), &($3), type); + $$ = dpas_create_subrange(type, &range); + jit_type_free($1.type); + jit_type_free($3.type); + } + else + { + char *name1 = dpas_type_name($1.type); + char *name2 = dpas_type_name($3.type); + if(!jit_strcmp(name1, name2)) + { + dpas_error("cannot declare a subrange within `%s'", + name1); + } + else + { + dpas_error("cannot declare a subrange within " + "`%s' or `%s'", name1, name2); + } + jit_free(name1); + jit_free(name2); + $$ = $1.type; + jit_type_free($3.type); + } + } + ; + +StructuredType + : K_ARRAY '[' ArrayBoundsList ']' K_OF Type { + $$ = dpas_create_array($3.list, $3.len, $6); + } + | K_RECORD FieldList K_END { + jit_type_t type; + type = jit_type_create_struct + ($2.types, (unsigned int)($2.len), 1); + if(!type) + { + dpas_out_of_memory(); + } + if(!jit_type_set_names + (type, $2.names, (unsigned int)($2.len))) + { + dpas_out_of_memory(); + } + type = jit_type_create_tagged(type, DPAS_TAG_NAME, 0, 0, 0); + parameter_list_free(&($2)); + $$ = type; + } + | K_SET K_OF Type { + if(!dpas_is_set_compatible($3)) + { + char *name = dpas_type_name($3); + dpas_error("`%s' cannot be used as the member type " + "for a set", name); + jit_free(name); + } + $$ = jit_type_create_tagged + (jit_type_uint, DPAS_TAG_SET, + $3, (jit_meta_free_func)jit_type_free, 0); + } + | '^' Identifier { + dpas_scope_item_t item; + item = dpas_scope_lookup(dpas_scope_current(), $2, 1); + if(!item) + { + /* The name is not declared yet, so it is probably a + forward reference to some later record type. We need + to add a placeholder for the later definition */ + char *name; + name = jit_strdup($2); + if(!name) + { + dpas_out_of_memory(); + } + $$ = jit_type_create_tagged(0, DPAS_TAG_NAME, + name, jit_free, 0); + if(!($$)) + { + dpas_out_of_memory(); + } + dpas_scope_add(dpas_scope_current(), $2, $$, + DPAS_ITEM_TYPE, 0, 0, + dpas_filename, dpas_linenum); + } + else if(dpas_scope_item_kind(item) != DPAS_ITEM_TYPE) + { + dpas_error("`%s' does not refer to a type in the " + "current scope", $2); + $$ = jit_type_void; + } + else + { + $$ = jit_type_copy(dpas_scope_item_type(item)); + } + $$ = jit_type_create_pointer($$, 0); + if(!($$)) + { + dpas_out_of_memory(); + } + jit_free($2); + } + ; + +ArrayBoundsList + : BoundType { + $$.list = 0; + $$.len = 0; + type_list_add(&($$.list), &($$.len), $1); + } + | ArrayBoundsList ',' BoundType { + $$ = $1; + type_list_add(&($$.list), &($$.len), $3); + } + ; + +BoundType + : SimpleType { + /* The type must be an enumeration or integer subrange */ + if(jit_type_get_tagged_kind($1) == DPAS_TAG_ENUM) + { + $$ = $1; + } + else if(jit_type_get_tagged_kind($1) == DPAS_TAG_SUBRANGE && + jit_type_get_tagged_type($1) == jit_type_int) + { + $$ = $1; + } + else + { + char *name = dpas_type_name($1); + dpas_error("`%s' cannot be used as an array bound", name); + jit_free(name); + $$ = 0; + } + } + ; + +FieldList + : /* empty */ { parameter_list_init(&($$)); } + | FixedPart { $$ = $1; } + | FixedPart ';' { $$ = $1; } + | FixedPart ';' VariantPart { + $$ = $1; + parameter_list_merge(&($$), &($3)); + } + | FixedPart ';' VariantPart ';' { + $$ = $1; + parameter_list_merge(&($$), &($3)); + } + | VariantPart { $$ = $1; } + | VariantPart ';' { $$ = $1; } + ; + +FixedPart + : RecordSection { $$ = $1; } + | FixedPart ';' RecordSection { + $$ = $1; + parameter_list_merge(&($$), &($3)); + } + ; + +RecordSection + : IdentifierList ':' Type { + parameter_list_create(&($$), $1.list, $1.len, $3); + jit_type_free($3); + } + | ProcedureOrFunctionHeading { + parameter_list_init(&($$)); + parameter_list_add(&($$), $1.name, $1.type); + } + ; + +VariantPart + : K_CASE Identifier ':' TypeIdentifier K_OF VariantList { + parameter_list_init(&($$)); + parameter_list_add(&($$), $2, $4); + parameter_list_add(&($$), 0, $6); + } + | K_CASE ':' TypeIdentifier K_OF VariantList { + parameter_list_init(&($$)); + parameter_list_add(&($$), 0, $5); + jit_type_free($3); + } + ; + +VariantList + : VariantCaseList { + /* Create a union with all of the case limbs */ + $$ = jit_type_create_union + ($1.list, (unsigned int)($1.len), 0); + jit_free($1.list); + } + ; + +VariantCaseList + : Variant { + $$.list = 0; + $$.len = 0; + type_list_add(&($$.list), &($$.len), $1); + } + | VariantCaseList ';' Variant { + $$ = $1; + type_list_add(&($$.list), &($$.len), $3); + } + ; + +Variant + : VariantCaseLabelList ':' '(' FieldList ')' { + jit_type_t type; + type = jit_type_create_struct + ($4.types, (unsigned int)($4.len), 1); + if(!type) + { + dpas_out_of_memory(); + } + if(!jit_type_set_names + (type, $4.names, (unsigned int)($4.len))) + { + dpas_out_of_memory(); + } + parameter_list_free(&($4)); + $$ = type; + } + ; + +/* We ignore the variant case labels, as we don't perform any + checks on the variant structure - we just use it as a union */ +VariantCaseLabelList + : Constant {} + | VariantCaseLabelList ',' Constant {} + ; + +/* + * Constants. + */ + +Constant + : ConstantValue { $$ = $1; } + | '+' ConstantValue { + if($2.type != jit_type_int && + $2.type != jit_type_uint && + $2.type != jit_type_long && + $2.type != jit_type_ulong && + $2.type != jit_type_nfloat) + { + char *name = dpas_type_name($2.type); + dpas_error("unary `+' cannot be applied to a constant " + "of type `%s'", name); + jit_free(name); + } + $$ = $2; + } + | '-' ConstantValue { + if($2.type == jit_type_int) + { + $$.type = $2.type; + $$.un.int_value = -($2.un.int_value); + } + else if($2.type == jit_type_uint) + { + $$.type = jit_type_long; + $$.un.long_value = -((jit_long)($2.un.uint_value)); + } + else if($2.type == jit_type_long) + { + $$.type = jit_type_long; + $$.un.long_value = -($2.un.long_value); + } + else if($2.type == jit_type_ulong) + { + $$.type = jit_type_long; + $$.un.long_value = -((jit_long)($2.un.ulong_value)); + } + else if($2.type == jit_type_nfloat) + { + $$.type = jit_type_nfloat; + $$.un.nfloat_value = -($2.un.nfloat_value); + } + else + { + char *name = dpas_type_name($2.type); + dpas_error("unary `-' cannot be applied to a constant " + "of type `%s'", name); + jit_free(name); + $$ = $2; + } + } + ; + +ConstantValue + : BasicConstant { $$ = $1; } + | Identifier { + dpas_scope_item_t item; + item = dpas_scope_lookup(dpas_scope_current(), $1, 1); + if(!item) + { + dpas_error("`%s' is not declared in the current scope", $1); + $$.type = jit_type_int; + $$.un.int_value = 0; + } + else if(dpas_scope_item_kind(item) != DPAS_ITEM_CONSTANT) + { + dpas_error("`%s' is not declared as a constant " + "in the current scope", $1); + $$.type = jit_type_int; + $$.un.int_value = 0; + } + else + { + $$ = *((jit_constant_t *)(dpas_scope_item_info(item))); + } + jit_free($1); + } + ; + +BasicConstant + : INTEGER_CONSTANT { + $$.type = $1.type; + if($1.type == jit_type_int) + { + $$.un.int_value = (jit_int)($1.value); + } + else if($1.type == jit_type_uint) + { + $$.un.uint_value = (jit_uint)($1.value); + } + else if($1.type == jit_type_long) + { + $$.un.long_value = (jit_long)($1.value); + } + else + { + $$.un.ulong_value = $1.value; + } + } + | REAL_CONSTANT { + $$.type = jit_type_nfloat; + $$.un.nfloat_value = $1; + } + | STRING_CONSTANT { + /* Note: the string pointer leaks from the parser, + but since we probably need it to hang around for + runtime, this shouldn't be serious */ + $$.type = dpas_type_string; + $$.un.ptr_value = $1; + } + | K_NIL { + $$.type = dpas_type_nil; + $$.un.ptr_value = 0; + } + ; diff --git a/dpas/dpas-scanner.l b/dpas/dpas-scanner.l new file mode 100644 index 0000000..399fcb8 --- /dev/null +++ b/dpas/dpas-scanner.l @@ -0,0 +1,345 @@ +%{ +/* + * dpas-scanner.l - Input file for lex for the Dynamic Pascal token syntax. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "dpas-internal.h" +#include "dpas-parser.h" + +extern YYSTYPE yylval; + +/* + * Current file and line number. + */ +char *dpas_filename = ""; +long dpas_linenum = 1; + +/* + * Return a token code from the lexical analyser. + */ +#define RETURNTOK(x) return (x) + +/* + * Parse a string value. + */ +static char *dpas_parse_string(const char *text) +{ + int quote = *text++; + char *str = (char *)jit_malloc(jit_strlen(text)); + char *temp; + if(!str) + { + dpas_out_of_memory(); + } + temp = str; + while(*text != '\0') + { + if(text[0] == quote && text[1] == quote) + { + *temp++ = (char)quote; + text += 2; + } + else + { + *temp++ = *text++; + } + } + *temp = '\0'; + return str; +} + +/* + * Parse a floating-point value. + */ +static jit_nfloat dpas_parse_float(const char *text) +{ + double value = 0.0; + sscanf(text, "%lf", &value); + return (jit_nfloat)value; +} + +/* + * Infer the best type for an integer constant. + */ +static void dpas_infer_type(YYSTYPE *lval) +{ + jit_ulong value = lval->int_const.value; + if(value <= (jit_ulong)(jit_long)jit_max_int) + { + lval->int_const.type = jit_type_int; + } + else if(value <= (jit_ulong)jit_max_uint) + { + lval->int_const.type = jit_type_uint; + } + else if(value <= (jit_ulong)jit_max_long) + { + lval->int_const.type = jit_type_long; + } + else + { + lval->int_const.type = jit_type_ulong; + } +} + +/* + * Parse a decimal integer value. + */ +static void dpas_parse_decimal(const char *text, YYSTYPE *lval) +{ + jit_ulong value = 0; + while(*text != '\0') + { + value = value * 10 + (jit_ulong)(*text - '0'); + ++text; + } + lval->int_const.value = value; + dpas_infer_type(lval); +} + +/* + * Parse a hexadecimal integer value. + */ +static void dpas_parse_hex(const char *text, YYSTYPE *lval) +{ + jit_ulong value = 0; + while(*text != '\0') + { + if(*text >= '0' && *text <= '9') + { + value = value * 16 + (jit_ulong)(*text - '0'); + } + else if(*text >= 'A' && *text <= 'F') + { + value = value * 16 + (jit_ulong)(*text - 'A' + 10); + } + else + { + value = value * 16 + (jit_ulong)(*text - 'a' + 10); + } + ++text; + } + lval->int_const.value = value; + dpas_infer_type(lval); +} + +/* + * Forward declaration. + */ +static void dpas_skip_comment(int star_style); + +%} + +%option outfile="lex.yy.c" +%option noyywrap +%option nounput +%option case-insensitive + +DIGIT [0-9] +HEX [0-9A-Fa-f] +IDALPHA [a-zA-Z_] +EXPONENT [Ee][+-]?{DIGIT}+ +WHITE [ \t\v\r\f] + +%% + +"<>" { RETURNTOK(K_NE); } +"<=" { RETURNTOK(K_LE); } +">=" { RETURNTOK(K_GE); } +":=" { RETURNTOK(K_ASSIGN); } +".." { RETURNTOK(K_DOT_DOT); } +"**" { RETURNTOK(K_POW); } + +"and" { RETURNTOK(K_AND); } +"array" { RETURNTOK(K_ARRAY); } +"begin" { RETURNTOK(K_BEGIN); } +"case" { RETURNTOK(K_CASE); } +"catch" { RETURNTOK(K_CATCH); } +"const" { RETURNTOK(K_CONST); } +"div" { RETURNTOK(K_DIV); } +"do" { RETURNTOK(K_DO); } +"downto" { RETURNTOK(K_DOWNTO); } +"else" { RETURNTOK(K_ELSE); } +"end" { RETURNTOK(K_END); } +"exit" { RETURNTOK(K_EXIT); } +"finally" { RETURNTOK(K_FINALLY); } +"for" { RETURNTOK(K_FOR); } +"forward" { RETURNTOK(K_FORWARD); } +"function" { RETURNTOK(K_FUNCTION); } +"goto" { RETURNTOK(K_GOTO); } +"if" { RETURNTOK(K_IF); } +"in" { RETURNTOK(K_IN); } +"label" { RETURNTOK(K_LABEL); } +"import" { RETURNTOK(K_IMPORT); } +"mod" { RETURNTOK(K_MOD); } +"module" { RETURNTOK(K_MODULE); } +"nil" { RETURNTOK(K_NIL); } +"not" { RETURNTOK(K_NOT); } +"of" { RETURNTOK(K_OF); } +"or" { RETURNTOK(K_OR); } +"packed" { RETURNTOK(K_PACKED); } +"pow" { RETURNTOK(K_POW); } +"procedure" { RETURNTOK(K_PROCEDURE); } +"program" { RETURNTOK(K_PROGRAM); } +"record" { RETURNTOK(K_RECORD); } +"repeat" { RETURNTOK(K_REPEAT); } +"set" { RETURNTOK(K_SET); } +"shl" { RETURNTOK(K_SHL); } +"shr" { RETURNTOK(K_SHR); } +"sizeof" { RETURNTOK(K_SIZEOF); } +"then" { RETURNTOK(K_THEN); } +"throw" { RETURNTOK(K_THROW); } +"to" { RETURNTOK(K_TO); } +"try" { RETURNTOK(K_TRY); } +"type" { RETURNTOK(K_TYPE); } +"until" { RETURNTOK(K_UNTIL); } +"var" { RETURNTOK(K_VAR); } +"va_arg" { RETURNTOK(K_VA_ARG); } +"with" { RETURNTOK(K_WITH); } +"while" { RETURNTOK(K_WHILE); } +"xor" { RETURNTOK(K_XOR); } + +'(''|[^'])*' { yylval.name = dpas_parse_string(yytext); + RETURNTOK(STRING_CONSTANT); } + +\"(\"\"|[^"])*\" { yylval.name = dpas_parse_string(yytext); + RETURNTOK(STRING_CONSTANT); } + +{IDALPHA}({DIGIT}|{IDALPHA})* { + yylval.name = jit_strdup(yytext); + if(!(yylval.name)) + { + dpas_out_of_memory(); + } + RETURNTOK(IDENTIFIER); + } + +{DIGIT}+{EXPONENT} { yylval.real_const = dpas_parse_float(yytext); + RETURNTOK(REAL_CONSTANT); } +{DIGIT}+"."{DIGIT}*{EXPONENT} { yylval.real_const = dpas_parse_float(yytext); + RETURNTOK(REAL_CONSTANT); } +{DIGIT}+"."{DIGIT}+ { yylval.real_const = dpas_parse_float(yytext); + RETURNTOK(REAL_CONSTANT); } +{DIGIT}+"."[^.] { yylval.real_const = dpas_parse_float(yytext); + RETURNTOK(REAL_CONSTANT); } + +{DIGIT}{HEX}*[hH] { dpas_parse_hex(yytext, &yylval); + RETURNTOK(INTEGER_CONSTANT); } + +{DIGIT}+ { dpas_parse_decimal(yytext, &yylval); + RETURNTOK(INTEGER_CONSTANT); } + +{WHITE}+ ; + +\n { ++dpas_linenum; } + +"{" { dpas_skip_comment(0); } +"(*" { dpas_skip_comment(1); } + +. { RETURNTOK(((int)(yytext[0])) & 0xFF); } + +%% + +void dpas_load_file(char *filename, FILE *file) +{ + char *saved_filename; + long saved_linenum; + YY_BUFFER_STATE saved_buffer; + YY_BUFFER_STATE new_buffer; + extern int yyparse(void); + + /* Save the current state */ + saved_filename = dpas_filename; + saved_linenum = dpas_linenum; + saved_buffer = YY_CURRENT_BUFFER; + + /* Create a buffer for the new file */ + new_buffer = yy_create_buffer(file, BUFSIZ); + if(!new_buffer) + { + dpas_out_of_memory(); + } + + /* Switch to the new state */ + dpas_filename = filename; + dpas_linenum = 1; + yy_switch_to_buffer(new_buffer); + + /* Call the parser */ + if(yyparse()) + { + dpas_error_reported = 1; + } + + /* Bail out if this was the top-most file in the parse process, + because flex cannot switch to a NULL buffer */ + if(!saved_buffer) + { + return; + } + + /* Switch back to the original file */ + dpas_filename = saved_filename; + dpas_linenum = saved_linenum; + yy_switch_to_buffer(saved_buffer); + + /* Delete the buffer that we used on the file we just parsed */ + yy_delete_buffer(new_buffer); +} + +/* + * Skip a comment in the input stream. + */ +static void dpas_skip_comment(int star_style) +{ + int ch; + for(;;) + { + ch = input(); + if(ch == EOF) + { + break; + } + else if(ch == '}' && !star_style) + { + break; + } + else if(ch == '*' && star_style) + { + ch = input(); + while(ch == '*') + { + ch = input(); + } + if(ch == EOF || ch == ')') + { + break; + } + else if(ch == '\n') + { + ++dpas_linenum; + } + } + else if(ch == '\n') + { + ++dpas_linenum; + } + } +} + diff --git a/dpas/dpas-scope.c b/dpas/dpas-scope.c new file mode 100644 index 0000000..50b32d9 --- /dev/null +++ b/dpas/dpas-scope.c @@ -0,0 +1,352 @@ +/* + * dpas-scope.c - Scope handling for Dynamic Pascal. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "dpas-internal.h" + +/* + * Internal structure of a scope item. + */ +struct dpas_scope_item +{ + int kind; + char *name; + jit_type_t type; + void *info; + jit_meta_free_func free_info; + char *filename; + long linenum; + dpas_scope_item_t next; +}; + +/* + * Internal structure of a scope. + */ +struct dpas_scope +{ + dpas_scope_t parent; + dpas_scope_item_t first; + dpas_scope_item_t last; + dpas_scope_item_t first_with; + dpas_scope_item_t last_with; + int level; +}; + +dpas_scope_t dpas_scope_create(dpas_scope_t parent) +{ + dpas_scope_t scope; + scope = jit_new(struct dpas_scope); + if(!scope) + { + dpas_out_of_memory(); + } + scope->parent = parent; + scope->first = 0; + scope->last = 0; + scope->first_with = 0; + scope->last_with = 0; + if(parent) + { + scope->level = parent->level + 1; + } + else + { + scope->level = 0; + } + return scope; +} + +void dpas_scope_destroy(dpas_scope_t scope) +{ + if(scope) + { + dpas_scope_item_t item, next; + item = scope->first; + while(item != 0) + { + next = item->next; + if(item->name) + { + jit_free(item->name); + } + if(item->free_info) + { + (*(item->free_info))(item->info); + } + jit_type_free(item->type); + jit_free(item); + item = next; + } + item = scope->first_with; + while(item != 0) + { + next = item->next; + if(item->name) + { + jit_free(item->name); + } + if(item->free_info) + { + (*(item->free_info))(item->info); + } + jit_type_free(item->type); + jit_free(item); + item = next; + } + jit_free(scope); + } +} + +dpas_scope_item_t dpas_scope_lookup(dpas_scope_t scope, + const char *name, int up) +{ + dpas_scope_item_t item; + int check_with = 1; + while(scope != 0) + { + /* Search through the "with" items for a field match */ + if(check_with) + { + item = scope->first_with; + if(!item) + { + /* We are exiting from the top-most "with" scope + in the current procedure. Ignore the rest of + the "with" scopes on the stack because we only + care about local/global variables from now on */ + check_with = 0; + } + while(item != 0) + { + if(dpas_type_find_name(item->type, name) != JIT_INVALID_NAME) + { + return item; + } + item = item->next; + } + } + + /* Search the regular items for a match */ + item = scope->first; + while(item != 0) + { + if(!jit_stricmp(item->name, name)) + { + return item; + } + item = item->next; + } + + /* Move up to the parent scope if necessary */ + if(!up) + { + break; + } + scope = scope->parent; + } + return 0; +} + +/* + * Add an item to a scope list. + */ +static void scope_add(dpas_scope_item_t *first, dpas_scope_item_t *last, + const char *name, jit_type_t type, int kind, + void *info, jit_meta_free_func free_info, + char *filename, long linenum) +{ + dpas_scope_item_t item; + item = jit_new(struct dpas_scope_item); + if(!item) + { + dpas_out_of_memory(); + } + if(name) + { + item->name = jit_strdup(name); + if(!(item->name)) + { + dpas_out_of_memory(); + } + } + else + { + item->name = 0; + } + item->type = jit_type_copy(type); + item->kind = kind; + item->info = info; + item->free_info = free_info; + if(filename) + { + item->filename = jit_strdup(filename); + if(!(item->filename)) + { + dpas_out_of_memory(); + } + } + else + { + item->filename = 0; + } + item->linenum = linenum; + item->next = 0; + if(*last) + { + (*last)->next = item; + } + else + { + *first = item; + } + *last = item; +} + +void dpas_scope_add(dpas_scope_t scope, const char *name, jit_type_t type, + int kind, void *info, jit_meta_free_func free_info, + char *filename, long linenum) +{ + scope_add(&(scope->first), &(scope->last), + name, type, kind, info, free_info, filename, linenum); +} + +void dpas_scope_add_with(dpas_scope_t scope, jit_type_t type, + void *with_info, jit_meta_free_func free_info) +{ + scope_add(&(scope->first_with), &(scope->last_with), + 0, type, DPAS_ITEM_WITH, with_info, free_info, 0, 0); +} + +void dpas_scope_add_const(dpas_scope_t scope, const char *name, + jit_constant_t *value, char *filename, long linenum) +{ + jit_constant_t *new_value; + new_value = jit_new(jit_constant_t); + if(!new_value) + { + dpas_out_of_memory(); + } + *new_value = *value; + scope_add(&(scope->first), &(scope->last), name, value->type, + DPAS_ITEM_CONSTANT, new_value, jit_free, filename, linenum); +} + +void dpas_scope_check_undefined(dpas_scope_t scope) +{ + dpas_scope_item_t item = scope->first; + jit_type_t type, new_type; + while(item != 0) + { + if(item->kind == DPAS_ITEM_TYPE) + { + type = item->type; + if(jit_type_get_tagged_kind(type) == DPAS_TAG_NAME && + jit_type_get_tagged_type(type) == 0) + { + dpas_error_on_line + (item->filename, item->linenum, + "forward-referenced record type `%s' was not " + "declared", item->name); + new_type = jit_type_create_struct(0, 0, 0); + if(!new_type) + { + dpas_out_of_memory(); + } + jit_type_set_tagged_type(type, new_type, 0); + } + } + item = item->next; + } +} + +int dpas_scope_item_kind(dpas_scope_item_t item) +{ + return item->kind; +} + +jit_type_t dpas_scope_item_type(dpas_scope_item_t item) +{ + return item->type; +} + +void *dpas_scope_item_info(dpas_scope_item_t item) +{ + return item->info; +} + +const char *dpas_scope_item_filename(dpas_scope_item_t item) +{ + return item->filename; +} + +long dpas_scope_item_linenum(dpas_scope_item_t item) +{ + return item->linenum; +} + +int dpas_scope_level(dpas_scope_t scope) +{ + return scope->level; +} + +/* + * The global and current scopes. + */ +static dpas_scope_t global_scope = 0; +static dpas_scope_t current_scope = 0; + +dpas_scope_t dpas_scope_current(void) +{ + if(!current_scope) + { + /* Create the global scope for the first time */ + global_scope = dpas_scope_create(0); + + /* Create a wrapper around the global scope to contain + the program-private definitions. This allows the + program to override the global scope if it wants to */ + current_scope = dpas_scope_create(global_scope); + } + return current_scope; +} + +dpas_scope_t dpas_scope_global(void) +{ + if(!current_scope) + { + dpas_scope_current(); + } + return global_scope; +} + +dpas_scope_t dpas_scope_push(void) +{ + current_scope = dpas_scope_create(dpas_scope_current()); + return current_scope; +} + +void dpas_scope_pop(void) +{ + dpas_scope_t scope = dpas_scope_current(); + if(scope->parent && scope->parent != global_scope) + { + current_scope = scope->parent; + dpas_scope_destroy(scope); + } +} diff --git a/dpas/dpas-scope.h b/dpas/dpas-scope.h new file mode 100644 index 0000000..8d6633d --- /dev/null +++ b/dpas/dpas-scope.h @@ -0,0 +1,146 @@ +/* + * dpas-scope.h - Scope handling for Dynamic Pascal. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _DPAS_SCOPE_H +#define _DPAS_SCOPE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Kinds of items that may be stored in a scope. + */ +#define DPAS_ITEM_TYPE 1 +#define DPAS_ITEM_VARIABLE 2 +#define DPAS_ITEM_GLOBAL_VARIABLE 3 +#define DPAS_ITEM_CONSTANT 4 +#define DPAS_ITEM_PROCEDURE 5 +#define DPAS_ITEM_WITH 6 +#define DPAS_ITEM_FUNC_RETURN 7 + +/* + * Opaque type that represents a scope. + */ +typedef struct dpas_scope *dpas_scope_t; + +/* + * Opaque type that represents a scope item. + */ +typedef struct dpas_scope_item *dpas_scope_item_t; + +/* + * Create a new scope, with a specified parent scope. If the parent + * scope is NULL, then this creates the global scope. + */ +dpas_scope_t dpas_scope_create(dpas_scope_t parent); + +/* + * Destroy a scope that is no longer required. + */ +void dpas_scope_destroy(dpas_scope_t scope); + +/* + * Look up a name within a scope. If "up" is non-zero, then also + * check the parent scopes. + */ +dpas_scope_item_t dpas_scope_lookup(dpas_scope_t scope, + const char *name, int up); + +/* + * Add an entry to a scope. + */ +void dpas_scope_add(dpas_scope_t scope, const char *name, jit_type_t type, + int kind, void *info, jit_meta_free_func free_info, + char *filename, long linenum); + +/* + * Add a "with" field declaration to a scope. + */ +void dpas_scope_add_with(dpas_scope_t scope, jit_type_t type, + void *with_info, jit_meta_free_func free_info); + +/* + * Add a constant value to a scope. + */ +void dpas_scope_add_const(dpas_scope_t scope, const char *name, + jit_constant_t *value, char *filename, long linenum); + +/* + * Search a scope to look for undefined record types that were + * referenced by a construct of the form "^name". + */ +void dpas_scope_check_undefined(dpas_scope_t scope); + +/* + * Get the kind associated with a scope item. + */ +int dpas_scope_item_kind(dpas_scope_item_t item); + +/* + * Get the type associated with a scope item. + */ +jit_type_t dpas_scope_item_type(dpas_scope_item_t item); + +/* + * Get the information block associated with a scope item. + */ +void *dpas_scope_item_info(dpas_scope_item_t item); + +/* + * Get the filename associated with a scope item. + */ +const char *dpas_scope_item_filename(dpas_scope_item_t item); + +/* + * Get the line number associated with a scope item. + */ +long dpas_scope_item_linenum(dpas_scope_item_t item); + +/* + * Get the level associated with the current scope (global is zero). + */ +int dpas_scope_level(dpas_scope_t scope); + +/* + * Get the current scope. + */ +dpas_scope_t dpas_scope_current(void); + +/* + * Get the global scope. + */ +dpas_scope_t dpas_scope_global(void); + +/* + * Create a new scope and push into it. + */ +dpas_scope_t dpas_scope_push(void); + +/* + * Pop the current scope level and destroy it. + */ +void dpas_scope_pop(void); + +#ifdef __cplusplus +}; +#endif + +#endif /* _DPAS_SCOPE_H */ diff --git a/dpas/dpas-semantics.h b/dpas/dpas-semantics.h new file mode 100644 index 0000000..8d4dd95 --- /dev/null +++ b/dpas/dpas-semantics.h @@ -0,0 +1,158 @@ +/* + * dpas-semantics.h - Semantic value handling for Dynamic Pascal. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _DPAS_SEMANTICS_H +#define _DPAS_SEMANTICS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Structure of a semantic value. + */ +typedef struct +{ + int kind__; + jit_type_t type__; + jit_value_t value__; + +} dpas_semvalue; + +/* + * Semantic value kinds. + */ +#define DPAS_SEM_LVALUE (1 << 0) +#define DPAS_SEM_RVALUE (1 << 1) +#define DPAS_SEM_TYPE (1 << 2) +#define DPAS_SEM_PROCEDURE (1 << 3) +#define DPAS_SEM_ERROR (1 << 4) +#define DPAS_SEM_RETURN (1 << 5) +#define DPAS_SEM_LVALUE_EA (1 << 6) + +/* + * Set a semantic value to an l-value. + */ +#define dpas_sem_set_lvalue(sem,type,value) \ + do { \ + (sem).kind__ = DPAS_SEM_LVALUE | DPAS_SEM_RVALUE; \ + (sem).type__ = (type); \ + if(!(value)) \ + { \ + dpas_out_of_memory(); \ + } \ + (sem).value__ = (value); \ + } while (0) + +/* + * Set a semantic value to an l-value that is actually an effective + * address that must be dereferenced to get the actual l-value. + */ +#define dpas_sem_set_lvalue_ea(sem,type,value) \ + do { \ + (sem).kind__ = DPAS_SEM_LVALUE_EA | DPAS_SEM_RVALUE; \ + (sem).type__ = (type); \ + if(!(value)) \ + { \ + dpas_out_of_memory(); \ + } \ + (sem).value__ = (value); \ + } while (0) + +/* + * Set a semantic value to an r-value. + */ +#define dpas_sem_set_rvalue(sem,type,value) \ + do { \ + (sem).kind__ = DPAS_SEM_RVALUE; \ + (sem).type__ = (type); \ + if(!(value)) \ + { \ + dpas_out_of_memory(); \ + } \ + (sem).value__ = (value); \ + } while (0) + +/* + * Set a semantic value to a type. + */ +#define dpas_sem_set_type(sem,type) \ + do { \ + (sem).kind__ = DPAS_SEM_TYPE; \ + (sem).type__ = (type); \ + (sem).value__ = 0; \ + } while (0) + +/* + * Set a semantic value to a procedure/function reference. + */ +#define dpas_sem_set_procedure(sem,type,item) \ + do { \ + (sem).kind__ = DPAS_SEM_PROCEDURE; \ + (sem).type__ = (type); \ + (sem).value__ = (jit_value_t)(item); \ + } while (0) + +/* + * Set a semantic value to an error. + */ +#define dpas_sem_set_error(sem) \ + do { \ + (sem).kind__ = DPAS_SEM_ERROR; \ + (sem).type__ = 0; \ + (sem).value__ = 0; \ + } while (0) + +/* + * Set a semantic value to indicate the return slot. + */ +#define dpas_sem_set_return(sem,type) \ + do { \ + (sem).kind__ = DPAS_SEM_RETURN; \ + (sem).type__ = (type); \ + (sem).value__ = 0; \ + } while (0) + +/* + * Determine if a semantic value has a specific kind. + */ +#define dpas_sem_is_lvalue(sem) (((sem).kind__ & DPAS_SEM_LVALUE) != 0) +#define dpas_sem_is_lvalue_ea(sem) (((sem).kind__ & DPAS_SEM_LVALUE_EA) != 0) +#define dpas_sem_is_rvalue(sem) (((sem).kind__ & DPAS_SEM_RVALUE) != 0) +#define dpas_sem_is_type(sem) (((sem).kind__ & DPAS_SEM_TYPE) != 0) +#define dpas_sem_is_procedure(sem) (((sem).kind__ & DPAS_SEM_PROCEDURE) != 0) +#define dpas_sem_is_error(sem) (((sem).kind__ & DPAS_SEM_ERROR) != 0) +#define dpas_sem_is_return(sem) (((sem).kind__ & DPAS_SEM_RETURN) != 0) + +/* + * Extract the type information from a semantic value. + */ +#define dpas_sem_get_type(sem) ((sem).type__) + +/* + * Extract the value information from a semantic value. + */ +#define dpas_sem_get_value(sem) ((sem).value__) + +#ifdef __cplusplus +}; +#endif + +#endif /* _DPAS_SEMANTICS_H */ diff --git a/dpas/dpas-types.c b/dpas/dpas-types.c new file mode 100644 index 0000000..18f04a5 --- /dev/null +++ b/dpas/dpas-types.c @@ -0,0 +1,1139 @@ +/* + * dpas-types.c - Special handling for Dynamic Pascal types. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "dpas-internal.h" +#include + +/* + * Define some special integer types that are distinguished from normal ones. + */ +jit_type_t dpas_type_boolean; +jit_type_t dpas_type_cboolean; +jit_type_t dpas_type_char; +jit_type_t dpas_type_string; +jit_type_t dpas_type_address; +jit_type_t dpas_type_nil; + +/* + * Register a predefined type within the global scope. + */ +static void register_type(const char *name, jit_type_t type) +{ + dpas_scope_add(dpas_scope_global(), name, type, DPAS_ITEM_TYPE, 0, 0, + "(builtin)", 1); +} + +/* + * Get an integer type of a specific size. + */ +static jit_type_t get_int_type(unsigned int size) +{ + if(size == sizeof(jit_int)) + { + return jit_type_int; + } + else if(size == sizeof(jit_long)) + { + return jit_type_long; + } + else if(size == sizeof(jit_nint)) + { + return jit_type_nint; + } + else if(size == sizeof(jit_short)) + { + return jit_type_short; + } + else if(size == sizeof(jit_sbyte)) + { + return jit_type_sbyte; + } + else + { + return jit_type_int; + } +} + +/* + * Get an unsigned integer type of a specific size. + */ +static jit_type_t get_uint_type(unsigned int size) +{ + if(size == sizeof(jit_uint)) + { + return jit_type_uint; + } + else if(size == sizeof(jit_ulong)) + { + return jit_type_ulong; + } + else if(size == sizeof(jit_nuint)) + { + return jit_type_nuint; + } + else if(size == sizeof(jit_ushort)) + { + return jit_type_ushort; + } + else if(size == sizeof(jit_ubyte)) + { + return jit_type_ubyte; + } + else + { + return jit_type_uint; + } +} + +void dpas_init_types(void) +{ + jit_constant_t value; + + /* + * Create the special types. + */ + dpas_type_boolean = jit_type_create_tagged + (jit_type_sys_int, DPAS_TAG_BOOLEAN, 0, 0, 1); + dpas_type_cboolean = jit_type_create_tagged + (jit_type_sys_char, DPAS_TAG_CBOOLEAN, 0, 0, 1); + if(((char)0xFF) < 0) + { + dpas_type_char = jit_type_create_tagged + (jit_type_sbyte, DPAS_TAG_CHAR, 0, 0, 1); + } + else + { + dpas_type_char = jit_type_create_tagged + (jit_type_ubyte, DPAS_TAG_CHAR, 0, 0, 1); + } + dpas_type_string = jit_type_create_pointer(dpas_type_char, 1); + dpas_type_address = jit_type_void_ptr; + dpas_type_nil = jit_type_create_tagged + (jit_type_void_ptr, DPAS_TAG_NIL, 0, 0, 1); + + /* + * Register all of the builtin types. + */ + register_type("Boolean", dpas_type_boolean); + register_type("CBoolean", dpas_type_cboolean); + register_type("Char", dpas_type_char); + register_type("String", dpas_type_string); + register_type("Address", dpas_type_address); + + register_type("Integer", jit_type_int); + register_type("Cardinal", jit_type_uint); + register_type("Word", jit_type_uint); + + register_type("Byte", jit_type_ubyte); + register_type("ByteInt", jit_type_sbyte); + register_type("ByteWord", jit_type_ubyte); + register_type("ByteCard", jit_type_ubyte); + + register_type("ShortInt", jit_type_short); + register_type("ShortWord", jit_type_ushort); + register_type("ShortCard", jit_type_ushort); + + register_type("MedInt", jit_type_nint); + register_type("MedWord", jit_type_nuint); + register_type("MedCard", jit_type_nuint); + + register_type("LongInt", jit_type_long); + register_type("LongWord", jit_type_ulong); + register_type("LongCard", jit_type_ulong); + + register_type("LongestInt", jit_type_long); + register_type("LongestWord", jit_type_ulong); + register_type("LongestCard", jit_type_ulong); + + register_type("PtrInt", jit_type_nint); + register_type("PtrWord", jit_type_nuint); + register_type("PtrCard", jit_type_nuint); + + register_type("SmallInt", jit_type_short); + register_type("Comp", jit_type_long); + + register_type("ShortReal", jit_type_float32); + register_type("Single", jit_type_float32); + + register_type("Real", jit_type_float64); + register_type("Double", jit_type_float64); + + register_type("LongReal", jit_type_nfloat); + register_type("Extended", jit_type_nfloat); + + register_type("PtrDiffType", get_int_type(sizeof(ptrdiff_t))); + register_type("SizeType", get_uint_type(sizeof(size_t))); + + register_type("SysInt", jit_type_sys_int); + register_type("SysCard", jit_type_sys_uint); + register_type("SysWord", jit_type_sys_uint); + + register_type("SysLongInt", jit_type_sys_long); + register_type("SysLongCard", jit_type_sys_ulong); + register_type("SysLongWord", jit_type_sys_ulong); + + register_type("SysLongestInt", jit_type_sys_longlong); + register_type("SysLongestCard", jit_type_sys_ulonglong); + register_type("SysLongestWord", jit_type_sys_ulonglong); + + /* + * Register the "True" and "False" constants. + */ + value.type = dpas_type_boolean; + value.un.int_value = 1; + dpas_scope_add_const(dpas_scope_global(), "True", &value, "(builtin)", 1); + value.type = dpas_type_boolean; + value.un.int_value = 0; + dpas_scope_add_const(dpas_scope_global(), "False", &value, "(builtin)", 1); +} + +unsigned int dpas_type_find_name(jit_type_t type, const char *name) +{ + unsigned int field = jit_type_num_fields(type); + const char *fname; + while(field > 0) + { + --field; + fname = jit_type_get_name(type, field); + if(fname && !jit_stricmp(fname, name)) + { + return field; + } + } + return JIT_INVALID_NAME; +} + +/* + * Concatenate two strings. + */ +static char *concat_strings(char *str1, char *str2) +{ + char *str; + if(!str1 || !str2) + { + dpas_out_of_memory(); + } + str = (char *)jit_malloc(jit_strlen(str1) + jit_strlen(str2) + 1); + if(!str) + { + dpas_out_of_memory(); + } + jit_strcpy(str, str1); + jit_strcat(str, str2); + jit_free(str1); + jit_free(str2); + return str; +} + +static char *type_name(const char *embed_name, jit_type_t type) +{ + char *temp; + dpas_subrange *range; + char *name; + if(jit_type_is_primitive(type)) + { + if(type == jit_type_void) + { + temp = jit_strdup("void"); + } + else if(type == jit_type_sbyte) + { + temp = jit_strdup("ByteInt"); + } + else if(type == jit_type_ubyte) + { + temp = jit_strdup("Byte"); + } + else if(type == jit_type_short) + { + temp = jit_strdup("ShortInt"); + } + else if(type == jit_type_ushort) + { + temp = jit_strdup("ShortCard"); + } + else if(type == jit_type_int) + { + temp = jit_strdup("Integer"); + } + else if(type == jit_type_uint) + { + temp = jit_strdup("Cardinal"); + } + else if(type == jit_type_long) + { + temp = jit_strdup("LongInt"); + } + else if(type == jit_type_ulong) + { + temp = jit_strdup("LongCard"); + } + else if(type == jit_type_float32) + { + temp = jit_strdup("ShortReal"); + } + else if(type == jit_type_float64) + { + temp = jit_strdup("Real"); + } + else if(type == jit_type_nfloat) + { + temp = jit_strdup("LongReal"); + } + else + { + temp = jit_strdup("unknown-primitive-type"); + } + } + else if(jit_type_is_struct(type) || jit_type_is_union(type)) + { + /* Shouldn't happen: record types should be tagged with a name */ + temp = jit_strdup("unknown-struct-or-union"); + } + else if(jit_type_is_signature(type)) + { + char *temp; + jit_type_t return_type, param_type; + unsigned int param, num_params; + const char *param_name; + return_type = jit_type_get_return(type); + if(return_type == jit_type_void) + { + temp = jit_strdup("procedure"); + } + else + { + temp = jit_strdup("function"); + } + if(embed_name) + { + temp = concat_strings(temp, jit_strdup(" ")); + temp = concat_strings(temp, jit_strdup(embed_name)); + } + num_params = jit_type_num_params(type); + if(num_params > 0) + { + temp = concat_strings(temp, jit_strdup("(")); + for(param = 0; param < num_params; ++param) + { + if(param > 0) + { + temp = concat_strings(temp, jit_strdup(", ")); + } + param_type = jit_type_get_param(type, param); + param_name = jit_type_get_name(type, param); + temp = concat_strings + (temp, type_name(param_name, param_type)); + } + temp = concat_strings(temp, jit_strdup(")")); + } + if(return_type != jit_type_void) + { + temp = concat_strings(temp, jit_strdup(" : ")); + temp = concat_strings(temp, type_name(0, return_type)); + } + return temp; + } + else if(jit_type_is_pointer(type)) + { + if(jit_type_get_ref(type) == dpas_type_char) + { + temp = jit_strdup("String"); + } + else if(jit_type_get_ref(type) == jit_type_void) + { + temp = jit_strdup("Address"); + } + else + { + temp = concat_strings + (jit_strdup("^"), type_name(0, jit_type_get_ref(type))); + } + } + else if(jit_type_is_tagged(type)) + { + switch(jit_type_get_tagged_kind(type)) + { + case DPAS_TAG_BOOLEAN: temp = jit_strdup("Boolean"); break; + case DPAS_TAG_CBOOLEAN: temp = jit_strdup("CBoolean"); break; + case DPAS_TAG_CHAR: temp = jit_strdup("Char"); break; + case DPAS_TAG_NIL: temp = jit_strdup("nil"); break; + + case DPAS_TAG_NAME: + { + name = (char *)jit_type_get_tagged_data(type); + if(name) + { + temp = jit_strdup(name); + } + else + { + temp = jit_strdup("anonymous_record"); + } + } + break; + + case DPAS_TAG_VAR: + { + return concat_strings + (jit_strdup("var "), + type_name(embed_name, jit_type_get_ref + (jit_type_get_tagged_type(type)))); + } + /* Not reached */ + + case DPAS_TAG_SUBRANGE: + { + range = (dpas_subrange *)jit_type_get_tagged_data(type); + temp = concat_strings + (dpas_constant_name(&(range->first)), + concat_strings + (jit_strdup(".."), + dpas_constant_name(&(range->last)))); + } + break; + + case DPAS_TAG_ENUM: + { + name = ((dpas_enum *)jit_type_get_tagged_data(type))->name; + if(name) + { + temp = jit_strdup(name); + } + else + { + temp = jit_strdup("anonymous_enum"); + } + } + break; + + case DPAS_TAG_SET: + { + temp = concat_strings + (jit_strdup("set of "), + type_name(0, (jit_type_t)jit_type_get_tagged_data(type))); + } + break; + + case DPAS_TAG_ARRAY: + { + int dim; + jit_type_t array_type; + dpas_array *array_info = (dpas_array *) + jit_type_get_tagged_data(type); + temp = jit_strdup("array ["); + for(dim = 0; dim < array_info->num_bounds; ++dim) + { + if(dim != 0) + { + temp = concat_strings(temp, jit_strdup(", ")); + } + if(array_info->bounds[dim]) + { + temp = concat_strings + (temp, type_name(0, array_info->bounds[dim])); + } + else + { + /* User-supplied type which can't be used as a bound */ + temp = concat_strings(temp, jit_strdup("0 .. 0")); + } + } + temp = concat_strings(temp, jit_strdup("] of ")); + array_type = jit_type_get_tagged_type(type); + temp = concat_strings + (temp, type_name(0, jit_type_get_field(array_type, 0))); + } + break; + + case DPAS_TAG_CONFORMANT_ARRAY: + { + int dim; + jit_type_t array_type; + dpas_conformant_array *array_info; + array_info = (dpas_conformant_array *) + jit_type_get_tagged_data(type); + if(array_info->is_packed) + { + temp = jit_strdup("packed array ["); + } + else + { + temp = jit_strdup("array ["); + } + for(dim = 1; dim < array_info->num_bounds; ++dim) + { + temp = concat_strings(temp, jit_strdup(",")); + } + temp = concat_strings(temp, jit_strdup("] of ")); + array_type = jit_type_get_ref(jit_type_get_tagged_type(type)); + temp = concat_strings(temp, type_name(0, array_type)); + if(embed_name) + { + temp = concat_strings + (concat_strings(jit_strdup("var "), + jit_strdup(embed_name)), + concat_strings(jit_strdup(" : "), temp)); + } + else + { + temp = concat_strings(jit_strdup("var "), temp); + } + return temp; + } + /* Not reached */ + + default: temp = jit_strdup("unknown-tagged-type"); break; + } + } + else + { + /* We shouldn't get here */ + temp = jit_strdup("unknown-jit-type"); + } + if(!temp) + { + dpas_out_of_memory(); + } + if(embed_name) + { + temp = concat_strings(concat_strings + (jit_strdup(embed_name), jit_strdup(" : ")), temp); + } + return temp; +} + +char *dpas_type_name(jit_type_t type) +{ + char *temp = type_name(0, type); + if(!temp) + { + dpas_out_of_memory(); + } + return temp; +} + +char *dpas_type_name_with_var(const char *name, jit_type_t type) +{ + char *temp = type_name(name, type); + if(!temp) + { + dpas_out_of_memory(); + } + return temp; +} + +jit_type_t dpas_promote_type(jit_type_t type) +{ + if(jit_type_get_tagged_kind(type) == DPAS_TAG_SUBRANGE) + { + /* Convert subrange types into their real integer counterparts */ + type = jit_type_get_tagged_type(type); + } + if(type == jit_type_sbyte || type == jit_type_ubyte || + type == jit_type_short || type == jit_type_ushort) + { + return jit_type_int; + } + else if(type == jit_type_nint) + { + if(sizeof(jit_nint) == sizeof(jit_int)) + { + return jit_type_int; + } + else + { + return jit_type_long; + } + } + else if(type == jit_type_nuint) + { + if(sizeof(jit_nuint) == sizeof(jit_uint)) + { + return jit_type_uint; + } + else + { + return jit_type_ulong; + } + } + else if(type == jit_type_float32 || type == jit_type_float64) + { + return jit_type_nfloat; + } + else + { + return type; + } +} + +jit_type_t dpas_common_type(jit_type_t type1, jit_type_t type2, int int_only) +{ + type1 = dpas_promote_type(type1); + type2 = dpas_promote_type(type2); + if(type1 == type2) + { + if(int_only && type1 == jit_type_nfloat) + { + return 0; + } + return type1; + } + if(type1 == jit_type_int) + { + if(type2 == jit_type_uint) + { + return jit_type_int; + } + else if(type2 == jit_type_long || type2 == jit_type_ulong) + { + return jit_type_long; + } + else if(type2 == jit_type_nfloat) + { + if(!int_only) + { + return jit_type_nfloat; + } + } + } + else if(type1 == jit_type_uint) + { + if(type2 == jit_type_int) + { + return jit_type_int; + } + else if(type2 == jit_type_long) + { + return jit_type_long; + } + else if(type2 == jit_type_ulong) + { + return jit_type_ulong; + } + else if(type2 == jit_type_nfloat) + { + if(!int_only) + { + return jit_type_nfloat; + } + } + } + else if(type1 == jit_type_long) + { + if(type2 == jit_type_int || type2 == jit_type_uint || + type2 == jit_type_ulong) + { + return jit_type_long; + } + else if(type2 == jit_type_nfloat) + { + if(!int_only) + { + return jit_type_nfloat; + } + } + } + else if(type1 == jit_type_ulong) + { + if(type2 == jit_type_int || type2 == jit_type_long) + { + return jit_type_long; + } + else if(type2 == jit_type_uint) + { + return jit_type_ulong; + } + else if(type2 == jit_type_nfloat) + { + if(!int_only) + { + return jit_type_nfloat; + } + } + } + else if(type1 == jit_type_nfloat) + { + if(type2 == jit_type_int || type2 == jit_type_uint || + type2 == jit_type_long || type2 == jit_type_ulong) + { + if(!int_only) + { + return jit_type_nfloat; + } + } + } + return 0; +} + +jit_type_t dpas_create_subrange(jit_type_t underlying, dpas_subrange *values) +{ + dpas_subrange *copy; + jit_type_t type; + copy = jit_new(dpas_subrange); + if(!copy) + { + dpas_out_of_memory(); + } + *copy = *values; + type = jit_type_create_tagged(underlying, DPAS_TAG_SUBRANGE, + copy, jit_free, 1); + if(!type) + { + dpas_out_of_memory(); + } + return type; +} + +/* + * Free the information block for an enumerated type. + */ +static void free_enum(void *info) +{ + jit_free(((dpas_enum *)info)->name); + jit_free(info); +} + +jit_type_t dpas_create_enum(jit_type_t underlying, int num_elems) +{ + dpas_enum *enum_info; + jit_type_t type; + enum_info = jit_new(dpas_enum); + if(!enum_info) + { + dpas_out_of_memory(); + } + enum_info->name = 0; + enum_info->num_elems = num_elems; + type = jit_type_create_tagged(underlying, DPAS_TAG_ENUM, + enum_info, free_enum, 1); + if(!type) + { + dpas_out_of_memory(); + } + return type; +} + +jit_nuint dpas_type_range_size(jit_type_t type) +{ + if(jit_type_get_tagged_kind(type) == DPAS_TAG_ENUM) + { + return (jit_nuint)(jit_nint) + (((dpas_enum *)jit_type_get_tagged_data(type))->num_elems); + } + else if(jit_type_get_tagged_kind(type) == DPAS_TAG_SUBRANGE && + jit_type_get_tagged_type(type) == jit_type_int) + { + return (jit_nuint)(jit_nint) + (((dpas_subrange *)jit_type_get_tagged_data(type)) + ->last.un.int_value - + ((dpas_subrange *)jit_type_get_tagged_data(type)) + ->first.un.int_value + 1); + } + else + { + return 0; + } +} + +jit_type_t dpas_create_array(jit_type_t *bounds, int num_bounds, + jit_type_t elem_type) +{ + jit_type_t type; + jit_type_t tagged; + dpas_array *info; + jit_nuint size; + int posn; + + /* Create a struct type, with the element type in the first field */ + type = jit_type_create_struct(&elem_type, 1, 0); + if(!type) + { + dpas_out_of_memory(); + } + + /* Tag the structure with the array bound information */ + info = (dpas_array *)jit_malloc(sizeof(dpas_array)); + if(!info) + { + dpas_out_of_memory(); + } + info->bounds = bounds; + info->num_bounds = num_bounds; + tagged = jit_type_create_tagged(type, DPAS_TAG_ARRAY, info, jit_free, 0); + + /* Infer the total size of the array */ + size = jit_type_get_size(elem_type); + for(posn = 0; posn < num_bounds; ++posn) + { + size *= dpas_type_range_size(bounds[posn]); + } + + /* If the array is empty, then allocate space for one element + so that the type's total size is non-zero */ + if(!size) + { + size = jit_type_get_size(elem_type); + } + + /* Set the array's final size and alignment */ + jit_type_set_size_and_alignment + (type, size, jit_type_get_alignment(elem_type)); + + /* Return the tagged version of the array to the caller */ + return tagged; +} + +jit_type_t dpas_create_conformant_array(jit_type_t elem_type, int num_bounds, + int is_packed) +{ + jit_type_t type; + dpas_conformant_array *info; + + /* A conformant array is actually a pointer to the first element */ + type = jit_type_create_pointer(elem_type, 0); + if(!type) + { + dpas_out_of_memory(); + } + + /* Tag the pointer so that the rest of the system knows what it is */ + info = jit_new(dpas_conformant_array); + if(!info) + { + dpas_out_of_memory(); + } + info->num_bounds = num_bounds; + info->is_packed = is_packed; + type = jit_type_create_tagged(type, DPAS_TAG_CONFORMANT_ARRAY, + info, jit_free, 0); + if(!type) + { + dpas_out_of_memory(); + } + return type; +} + +void dpas_type_set_name(jit_type_t type, const char *name) +{ + char *copy; + dpas_enum *enum_info; + if(jit_type_get_tagged_kind(type) == DPAS_TAG_NAME) + { + if(!jit_type_get_tagged_data(type)) + { + copy = jit_strdup(name); + if(!copy) + { + dpas_out_of_memory(); + } + jit_type_set_tagged_data(type, copy, jit_free); + } + } + else if(jit_type_get_tagged_kind(type) == DPAS_TAG_ENUM) + { + enum_info = (dpas_enum *)jit_type_get_tagged_data(type); + if(!(enum_info->name)) + { + copy = jit_strdup(name); + if(!copy) + { + dpas_out_of_memory(); + } + enum_info->name = copy; + } + } +} + +void dpas_convert_constant(jit_constant_t *result, jit_constant_t *from, + jit_type_t to_type) +{ + jit_type_t from_type = dpas_promote_type(from->type); + to_type = dpas_promote_type(to_type); + result->type = to_type; + result->un = from->un; + if(to_type == jit_type_int) + { + if(from_type == jit_type_int) + { + result->un.int_value = jit_int_to_int(from->un.int_value); + } + else if(from_type == jit_type_uint) + { + result->un.int_value = jit_uint_to_int(from->un.uint_value); + } + else if(from_type == jit_type_long) + { + result->un.int_value = jit_long_to_int(from->un.long_value); + } + else if(from_type == jit_type_ulong) + { + result->un.int_value = jit_ulong_to_int(from->un.ulong_value); + } + else if(from_type == jit_type_nfloat) + { + result->un.int_value = jit_nfloat_to_int(from->un.nfloat_value); + } + } + else if(to_type == jit_type_uint) + { + if(from_type == jit_type_int) + { + result->un.uint_value = jit_int_to_uint(from->un.int_value); + } + else if(from_type == jit_type_uint) + { + result->un.uint_value = jit_uint_to_uint(from->un.uint_value); + } + else if(from_type == jit_type_long) + { + result->un.uint_value = jit_long_to_uint(from->un.long_value); + } + else if(from_type == jit_type_ulong) + { + result->un.uint_value = jit_ulong_to_uint(from->un.ulong_value); + } + else if(from_type == jit_type_nfloat) + { + result->un.uint_value = jit_nfloat_to_uint(from->un.nfloat_value); + } + } + else if(to_type == jit_type_long) + { + if(from_type == jit_type_int) + { + result->un.long_value = jit_int_to_long(from->un.int_value); + } + else if(from_type == jit_type_uint) + { + result->un.long_value = jit_uint_to_long(from->un.uint_value); + } + else if(from_type == jit_type_long) + { + result->un.long_value = jit_long_to_long(from->un.long_value); + } + else if(from_type == jit_type_ulong) + { + result->un.long_value = jit_ulong_to_long(from->un.ulong_value); + } + else if(from_type == jit_type_nfloat) + { + result->un.long_value = jit_nfloat_to_long(from->un.nfloat_value); + } + } + else if(to_type == jit_type_ulong) + { + if(from_type == jit_type_int) + { + result->un.ulong_value = jit_int_to_ulong(from->un.int_value); + } + else if(from_type == jit_type_uint) + { + result->un.ulong_value = jit_uint_to_ulong(from->un.uint_value); + } + else if(from_type == jit_type_long) + { + result->un.ulong_value = jit_long_to_ulong(from->un.long_value); + } + else if(from_type == jit_type_ulong) + { + result->un.ulong_value = jit_ulong_to_ulong(from->un.ulong_value); + } + else if(from_type == jit_type_nfloat) + { + result->un.ulong_value = jit_nfloat_to_ulong(from->un.nfloat_value); + } + } + else if(to_type == jit_type_nfloat) + { + if(from_type == jit_type_int) + { + result->un.nfloat_value = jit_int_to_nfloat(from->un.int_value); + } + else if(from_type == jit_type_uint) + { + result->un.nfloat_value = jit_uint_to_nfloat(from->un.uint_value); + } + else if(from_type == jit_type_long) + { + result->un.nfloat_value = jit_long_to_nfloat(from->un.long_value); + } + else if(from_type == jit_type_ulong) + { + result->un.nfloat_value = + jit_ulong_to_nfloat(from->un.ulong_value); + } + else if(from_type == jit_type_nfloat) + { + result->un.nfloat_value = from->un.nfloat_value; + } + } +} + +static char *format_integer(char *buf, int is_neg, jit_ulong value) +{ + buf += 64; + *(--buf) = '\0'; + if(value == 0) + { + *(--buf) = '0'; + } + else + { + while(value != 0) + { + *(--buf) = '0' + (int)(value % 10); + value /= 10; + } + } + if(is_neg) + { + *(--buf) = '-'; + } + return buf; +} + +char *dpas_constant_name(jit_constant_t *value) +{ + jit_type_t type; + char buf[64]; + char *result; + if(value->type == dpas_type_nil) + { + return jit_strdup("nil"); + } + else if(jit_type_is_pointer(value->type) && + jit_type_get_ref(value->type) == dpas_type_char) + { + return concat_strings + (concat_strings(jit_strdup("\""), + jit_strdup((char *)(value->un.ptr_value))), + jit_strdup("\"")); + } + type = dpas_promote_type(value->type); + if(type == jit_type_int) + { + if(value->un.int_value < 0) + { + result = format_integer + (buf, 1, (jit_ulong)(-((jit_long)(value->un.int_value)))); + } + else + { + result = format_integer + (buf, 0, (jit_ulong)(jit_long)(value->un.int_value)); + } + } + else if(type == jit_type_uint) + { + result = format_integer + (buf, 0, (jit_ulong)(value->un.uint_value)); + } + else if(type == jit_type_long) + { + if(value->un.int_value < 0) + { + result = format_integer + (buf, 1, (jit_ulong)(-(value->un.long_value))); + } + else + { + result = format_integer + (buf, 0, (jit_ulong)(value->un.long_value)); + } + } + else if(type == jit_type_ulong) + { + result = format_integer(buf, 0, value->un.ulong_value); + } + else if(type == jit_type_nfloat) + { + sprintf(buf, "%g", (double)(value->un.nfloat_value)); + result = buf; + } + else + { + result = "unknown constant"; + } + return jit_strdup(result); +} + +int dpas_is_set_compatible(jit_type_t type) +{ + if(jit_type_get_tagged_kind(type) == DPAS_TAG_ENUM) + { + return (((dpas_enum *)jit_type_get_tagged_data(type))->num_elems <= 32); + } + else if(jit_type_get_tagged_kind(type) == DPAS_TAG_SUBRANGE) + { + dpas_subrange *range; + if(jit_type_get_tagged_type(type) != jit_type_int) + { + return 0; + } + range = (dpas_subrange *)jit_type_get_tagged_data(type); + if(range->first.un.int_value < 0 || range->first.un.int_value > 31 || + range->last.un.int_value < 0 || range->last.un.int_value > 31) + { + return 0; + } + return 1; + } + return 0; +} + +int dpas_type_is_numeric(jit_type_t type) +{ + if(type == jit_type_sbyte || type == jit_type_ubyte || + type == jit_type_short || type == jit_type_ushort || + type == jit_type_int || type == jit_type_uint || + type == jit_type_long || type == jit_type_ulong || + type == jit_type_float32 || type == jit_type_float64 || + type == jit_type_nfloat) + { + return 1; + } + if(jit_type_get_tagged_kind(type) == DPAS_TAG_SUBRANGE) + { + return 1; + } + return 0; +} + +int dpas_type_is_integer(jit_type_t type) +{ + if(type == jit_type_sbyte || type == jit_type_ubyte || + type == jit_type_short || type == jit_type_ushort || + type == jit_type_int || type == jit_type_uint || + type == jit_type_long || type == jit_type_ulong) + { + return 1; + } + if(jit_type_get_tagged_kind(type) == DPAS_TAG_SUBRANGE) + { + return 1; + } + return 0; +} + +int dpas_type_is_boolean(jit_type_t type) +{ + return (type == dpas_type_boolean || type == dpas_type_cboolean); +} diff --git a/dpas/dpas-types.h b/dpas/dpas-types.h new file mode 100644 index 0000000..ac5c4fe --- /dev/null +++ b/dpas/dpas-types.h @@ -0,0 +1,185 @@ +/* + * dpas-types.h - Type handling for Dynamic Pascal. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _DPAS_TYPES_H +#define _DPAS_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Special type tags that are used in this language. + */ +#define DPAS_TAG_BOOLEAN 1 +#define DPAS_TAG_CBOOLEAN 2 +#define DPAS_TAG_CHAR 3 +#define DPAS_TAG_NIL 4 +#define DPAS_TAG_NAME 5 +#define DPAS_TAG_VAR 6 +#define DPAS_TAG_SUBRANGE 7 +#define DPAS_TAG_ENUM 8 +#define DPAS_TAG_SET 9 +#define DPAS_TAG_ARRAY 10 +#define DPAS_TAG_CONFORMANT_ARRAY 11 + +/* + * Define some special integer types that are distinguished from normal ones. + */ +extern jit_type_t dpas_type_boolean; +extern jit_type_t dpas_type_cboolean; +extern jit_type_t dpas_type_char; +extern jit_type_t dpas_type_string; +extern jit_type_t dpas_type_address; +extern jit_type_t dpas_type_nil; + +/* + * Information block for a subrange. + */ +typedef struct +{ + jit_constant_t first; + jit_constant_t last; + +} dpas_subrange; + +/* + * Information block for an enumeration. + */ +typedef struct +{ + char *name; + int num_elems; + +} dpas_enum; + +/* + * Information block for an array. + */ +typedef struct +{ + jit_type_t *bounds; + int num_bounds; + +} dpas_array; + +/* + * Information block for a conformant array. + */ +typedef struct +{ + int num_bounds; + int is_packed; + +} dpas_conformant_array; + +/* + * Initialize the standard types. + */ +void dpas_init_types(void); + +/* + * Find a field within a record type. This is similar to + * "jit_type_find_name", except that it ignores case. + */ +unsigned int dpas_type_find_name(jit_type_t type, const char *name); + +/* + * Get the name of a type, for printing in error messages. + */ +char *dpas_type_name(jit_type_t type); + +/* + * Get the name of a type, combined with a variable name. + */ +char *dpas_type_name_with_var(const char *name, jit_type_t type); + +/* + * Promote a numeric type to its best form for arithmetic. e.g. byte -> int. + */ +jit_type_t dpas_promote_type(jit_type_t type); + +/* + * Infer a common type for a binary operation. Returns NULL if not possible. + */ +jit_type_t dpas_common_type(jit_type_t type1, jit_type_t type2, int int_only); + +/* + * Create a subrange type. + */ +jit_type_t dpas_create_subrange(jit_type_t underlying, dpas_subrange *values); + +/* + * Create an enumerated type. + */ +jit_type_t dpas_create_enum(jit_type_t underlying, int num_elems); + +/* + * Get the size of a range type that is being used for an array bound. + */ +jit_nuint dpas_type_range_size(jit_type_t type); + +/* + * Create an array type. + */ +jit_type_t dpas_create_array(jit_type_t *bounds, int num_bounds, + jit_type_t elem_type); + +/* + * Create a conformant array type. + */ +jit_type_t dpas_create_conformant_array(jit_type_t elem_type, int num_bounds, + int is_packed); + +/* + * Set the name of an enumerated or record type. Ignored for other types, + * or types that already have a name. + */ +void dpas_type_set_name(jit_type_t type, const char *name); + +/* + * Convert a constant value into a new type. + */ +void dpas_convert_constant(jit_constant_t *result, jit_constant_t *from, + jit_type_t to_type); + +/* + * Get the name form of a constant value. + */ +char *dpas_constant_name(jit_constant_t *value); + +/* + * Determine if a type is set-compatible. It must be an enumerated + * or subrange type with 32 or less members. + */ +int dpas_is_set_compatible(jit_type_t type); + +/* + * Determine if a type is numeric, integer, or boolean. + */ +int dpas_type_is_numeric(jit_type_t type); +int dpas_type_is_integer(jit_type_t type); +int dpas_type_is_boolean(jit_type_t type); + +#ifdef __cplusplus +}; +#endif + +#endif /* _DPAS_TYPES_H */ diff --git a/include/.cvsignore b/include/.cvsignore new file mode 100644 index 0000000..051d1bd --- /dev/null +++ b/include/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +.deps diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..2d98b59 --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,2 @@ + +SUBDIRS = jit diff --git a/include/jit/.cvsignore b/include/jit/.cvsignore new file mode 100644 index 0000000..261801d --- /dev/null +++ b/include/jit/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +.deps +jit-defs.h diff --git a/include/jit/Makefile.am b/include/jit/Makefile.am new file mode 100644 index 0000000..b6e1a80 --- /dev/null +++ b/include/jit/Makefile.am @@ -0,0 +1,24 @@ + +libjitincludedir = $(includedir)/jit +libjitinclude_HEADERS = jit.h \ + jit-apply.h \ + jit-block.h \ + jit-common.h \ + jit-context.h \ + jit-defs.h \ + jit-dump.h \ + jit-elf.h \ + jit-except.h \ + jit-function.h \ + jit-init.h \ + jit-insn.h \ + jit-intrinsic.h \ + jit-meta.h \ + jit-opcode.h \ + jit-plus.h \ + jit-type.h \ + jit-util.h \ + jit-value.h \ + jit-walk.h + +DISTCLEANFILES = jit-defs.h diff --git a/include/jit/jit-apply.h b/include/jit/jit-apply.h new file mode 100644 index 0000000..342762e --- /dev/null +++ b/include/jit/jit-apply.h @@ -0,0 +1,68 @@ +/* + * jit-apply.h - Dynamic invocation and closure support functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_APPLY_H +#define _JIT_APPLY_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Prototype for closure functions. + */ +typedef void (*jit_closure_func)(jit_type_t signature, void *result, + void **args, void *user_data); + +/* + * Opaque type for accessing vararg parameters on closures. + */ +typedef struct jit_closure_va_list *jit_closure_va_list_t; + +/* + * External function declarations. + */ +void jit_apply(jit_type_t signature, void *func, + void **args, unsigned int num_fixed_args, + void *return_value); +void jit_apply_raw(jit_type_t signature, void *func, + void *args, void *return_value); +int jit_raw_supported(jit_type_t signature); +void *jit_closure_create(jit_context_t context, jit_type_t signature, + jit_closure_func func, void *user_data); +int jit_closures_supported(void); +jit_nint jit_closure_va_get_nint(jit_closure_va_list_t va); +jit_nuint jit_closure_va_get_nuint(jit_closure_va_list_t va); +jit_long jit_closure_va_get_long(jit_closure_va_list_t va); +jit_ulong jit_closure_va_get_ulong(jit_closure_va_list_t va); +jit_float32 jit_closure_va_get_float32(jit_closure_va_list_t va); +jit_float64 jit_closure_va_get_float64(jit_closure_va_list_t va); +jit_nfloat jit_closure_va_get_nfloat(jit_closure_va_list_t va); +void *jit_closure_va_get_ptr(jit_closure_va_list_t va); +void jit_closure_va_get_struct + (jit_closure_va_list_t va, void *buf, jit_type_t type); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_APPLY_H */ diff --git a/include/jit/jit-block.h b/include/jit/jit-block.h new file mode 100644 index 0000000..886ad52 --- /dev/null +++ b/include/jit/jit-block.h @@ -0,0 +1,46 @@ +/* + * jit-block.h - Functions for manipulating blocks. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_BLOCK_H +#define _JIT_BLOCK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +jit_function_t jit_block_get_function(jit_block_t block); +jit_context_t jit_block_get_context(jit_block_t block); +jit_label_t jit_block_get_label(jit_block_t block); +jit_block_t jit_block_next(jit_function_t func, jit_block_t previous); +jit_block_t jit_block_previous(jit_function_t func, jit_block_t previous); +jit_block_t jit_block_from_label(jit_function_t func, jit_label_t label); +int jit_block_set_meta(jit_block_t block, int type, void *data, + jit_meta_free_func free_data); +void *jit_block_get_meta(jit_block_t block, int type); +void jit_block_free_meta(jit_block_t block, int type); +int jit_block_is_reachable(jit_block_t block); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_BLOCK_H */ diff --git a/include/jit/jit-common.h b/include/jit/jit-common.h new file mode 100644 index 0000000..2b182d6 --- /dev/null +++ b/include/jit/jit-common.h @@ -0,0 +1,90 @@ +/* + * jit-common.h - Common type definitions that are used by the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_COMMON_H +#define _JIT_COMMON_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Opaque structure that represents a context. + */ +typedef struct _jit_context *jit_context_t; + +/* + * Opaque structure that represents a function. + */ +typedef struct _jit_function *jit_function_t; + +/* + * Opaque type that represents the compiled form of a function. + */ +typedef void *jit_function_compiled_t; + +/* + * Opaque structure that represents a block. + */ +typedef struct _jit_block *jit_block_t; + +/* + * Opaque structure that represents an instruction. + */ +typedef struct _jit_insn *jit_insn_t; + +/* + * Opaque structure that represents a value. + */ +typedef struct _jit_value *jit_value_t; + +/* + * Opaque structure that represents a type descriptor. + */ +typedef struct _jit_type *jit_type_t; + +/* + * Block label identifier. + */ +typedef jit_nuint jit_label_t; + +/* + * Value that represents an undefined label. + */ +#define jit_label_undefined ((jit_label_t)~((jit_uint)0)) + +/* + * Function that is used to free user-supplied metadata. + */ +typedef void (*jit_meta_free_func)(void *data); + +/* + * Function that is used to compile a function on demand. + * Returns zero if the compilation process failed for some reason. + */ +typedef int (*jit_on_demand_func)(jit_function_t func); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_COMMON_H */ diff --git a/include/jit/jit-context.h b/include/jit/jit-context.h new file mode 100644 index 0000000..6f52800 --- /dev/null +++ b/include/jit/jit-context.h @@ -0,0 +1,55 @@ +/* + * jit-context.h - Functions for manipulating JIT contexts. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_CONTEXT_H +#define _JIT_CONTEXT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +jit_context_t jit_context_create(void); +void jit_context_destroy(jit_context_t context); +int jit_context_supports_threads(jit_context_t context); +void jit_context_build_start(jit_context_t context); +void jit_context_build_end(jit_context_t context); +int jit_context_set_meta + (jit_context_t context, int type, void *data, + jit_meta_free_func free_data); +int jit_context_set_meta_numeric + (jit_context_t context, int type, jit_nuint data); +void *jit_context_get_meta(jit_context_t context, int type); +jit_nuint jit_context_get_meta_numeric(jit_context_t context, int type); +void jit_context_free_meta(jit_context_t context, int type); + +/* + * Standard meta values for builtin configurable options. + */ +#define JIT_OPTION_CACHE_LIMIT 10000 +#define JIT_OPTION_CACHE_PAGE_SIZE 10001 +#define JIT_OPTION_PRE_COMPILE 10002 + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_CONTEXT_H */ diff --git a/include/jit/jit-defs.h.in b/include/jit/jit-defs.h.in new file mode 100644 index 0000000..57e3d4d --- /dev/null +++ b/include/jit/jit-defs.h.in @@ -0,0 +1,70 @@ +/* + * jit-defs.h - Define the primitive numeric types for use by the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_DEFS_H +#define _JIT_DEFS_H + +#ifdef __cplusplus +extern "C" { +#endif + +@JIT_INT64_INCLUDE@ + +typedef @JITINT8@ jit_sbyte; +typedef @JITUINT8@ jit_ubyte; +typedef @JITINT16@ jit_short; +typedef unsigned @JITINT16@ jit_ushort; +typedef @JITINT32@ jit_int; +typedef unsigned @JITINT32@ jit_uint; +typedef @JITNATIVEINT@ jit_nint; +typedef unsigned @JITNATIVEINT@ jit_nuint; +#if defined(__cplusplus) && defined(__GNUC__) +typedef @JITINT64CXX@ jit_long; +typedef unsigned @JITINT64CXX@ jit_ulong; +#else +typedef @JITINT64@ jit_long; +typedef unsigned @JITINT64@ jit_ulong; +#endif +typedef @JITFLOAT32@ jit_float32; +typedef @JITFLOAT64@ jit_float64; +typedef @JITNATIVEFLOAT@ jit_nfloat; +typedef void *jit_ptr; + +#define @JITNATIVEINTDEFINE@ 1 +@JITNFLOATISDOUBLE@ + +#if defined(__cplusplus) && defined(__GNUC__) +#define JIT_NOTHROW @JITTHROWIDIOM@ +#else +#define JIT_NOTHROW +#endif + +#define jit_min_int (((jit_int)1) << (sizeof(jit_int) - 1)) +#define jit_max_int ((jit_int)(~jit_min_int)) +#define jit_max_uint ((jit_uint)(~((jit_uint)0))) +#define jit_min_long (((jit_long)1) << (sizeof(jit_long) - 1)) +#define jit_max_long ((jit_long)(~jit_min_long)) +#define jit_max_ulong ((jit_ulong)(~((jit_ulong)0))) + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_DEFS_H */ diff --git a/include/jit/jit-dump.h b/include/jit/jit-dump.h new file mode 100644 index 0000000..d036f32 --- /dev/null +++ b/include/jit/jit-dump.h @@ -0,0 +1,41 @@ +/* + * jit-dump.h - Functions for dumping JIT structures, for debugging. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_DUMP_H +#define _JIT_DUMP_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void jit_dump_type(FILE *stream, jit_type_t type); +void jit_dump_value(FILE *stream, jit_function_t func, + jit_value_t value, const char *prefix); +void jit_dump_insn(FILE *stream, jit_function_t func, jit_insn_t insn); +void jit_dump_function(FILE *stream, jit_function_t func, const char *name); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_DUMP_H */ diff --git a/include/jit/jit-elf.h b/include/jit/jit-elf.h new file mode 100644 index 0000000..eff30ed --- /dev/null +++ b/include/jit/jit-elf.h @@ -0,0 +1,84 @@ +/* + * - Routines to read and write ELF-format binaries. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_ELF_H +#define _JIT_ELF_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Opaque types that represent a loaded ELF binary in read or write mode. + */ +typedef struct jit_readelf *jit_readelf_t; +typedef struct jit_writeelf *jit_writeelf_t; + +/* + * Flags for "jit_readelf_open". + */ +#define JIT_READELF_FLAG_FORCE (1 << 0) /* Force file to load */ +#define JIT_READELF_FLAG_DEBUG (1 << 1) /* Print debugging information */ + +/* + * Result codes from "jit_readelf_open". + */ +#define JIT_READELF_OK 0 /* File was opened successfully */ +#define JIT_READELF_CANNOT_OPEN 1 /* Could not open the file */ +#define JIT_READELF_NOT_ELF 2 /* Not an ELF-format binary */ +#define JIT_READELF_WRONG_ARCH 3 /* Wrong architecture for local system */ +#define JIT_READELF_BAD_FORMAT 4 /* ELF file, but badly formatted */ +#define JIT_READELF_MEMORY 5 /* Insufficient memory to load the file */ + +/* + * External function declarations. + */ +int jit_readelf_open(jit_readelf_t *readelf, const char *filename, int flags); +void jit_readelf_close(jit_readelf_t readelf); +const char *jit_readelf_get_name(jit_readelf_t readelf); +void *jit_readelf_get_symbol(jit_readelf_t readelf, const char *name); +void *jit_readelf_get_section + (jit_readelf_t readelf, const char *name, jit_nuint *size); +void *jit_readelf_get_section_by_type + (jit_readelf_t readelf, jit_int type, jit_nuint *size); +void *jit_readelf_map_vaddr(jit_readelf_t readelf, jit_nuint vaddr); +unsigned int jit_readelf_num_needed(jit_readelf_t readelf); +const char *jit_readelf_get_needed(jit_readelf_t readelf, unsigned int index); +void jit_readelf_add_to_context(jit_readelf_t readelf, jit_context_t context); +int jit_readelf_resolve_all(jit_context_t context, int print_failures); + +jit_writeelf_t jit_writeelf_create(const char *library_name); +void jit_writeelf_destroy(jit_writeelf_t writeelf); +int jit_writeelf_write(jit_writeelf_t writeelf, const char *filename); +int jit_writeelf_add_function + (jit_writeelf_t writeelf, jit_function_t func, const char *name); +int jit_writeelf_add_needed + (jit_writeelf_t writeelf, const char *library_name); +int jit_writeelf_write_section + (jit_writeelf_t writeelf, const char *name, jit_int type, + const void *buf, unsigned int len, int discardable); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_ELF_H */ diff --git a/include/jit/jit-except.h b/include/jit/jit-except.h new file mode 100644 index 0000000..3afabc1 --- /dev/null +++ b/include/jit/jit-except.h @@ -0,0 +1,78 @@ +/* + * jit-except.h - Exception handling functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_EXCEPT_H +#define _JIT_EXCEPT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Builtin exception type codes, and result values for intrinsic functions. + */ +#define JIT_RESULT_OK 1 +#define JIT_RESULT_OVERFLOW 0 +#define JIT_RESULT_ARITHMETIC -1 +#define JIT_RESULT_DIVISION_BY_ZERO -2 +#define JIT_RESULT_COMPILE_ERROR -3 +#define JIT_RESULT_OUT_OF_MEMORY -4 +#define JIT_RESULT_NULL_REFERENCE -5 +#define JIT_RESULT_NULL_FUNCTION -6 +#define JIT_RESULT_CALLED_NESTED -7 + +/* + * Exception handling function for builtin exceptions. + */ +typedef void *(*jit_exception_func)(int exception_type); + +/* + * Opaque type that represents an exception stack trace. + */ +typedef struct jit_stack_trace *jit_stack_trace_t; + +/* + * External function declarations. + */ +void *jit_exception_get_last(void); +void jit_exception_set_last(void *object); +void jit_exception_clear_last(void); +void jit_exception_throw(void *object); +void jit_exception_builtin(int exception_type); +jit_exception_func jit_exception_set_handler(jit_exception_func handler); +jit_exception_func jit_exception_get_handler(void); +jit_stack_trace_t jit_exception_get_stack_trace(void); +unsigned int jit_stack_trace_get_size(jit_stack_trace_t trace); +jit_function_t jit_stack_trace_get_function + (jit_context_t context, jit_stack_trace_t trace, unsigned int posn); +void *jit_stack_trace_get_pc + (jit_stack_trace_t trace, unsigned int posn); +#define JIT_NO_OFFSET (~((unsigned int)0)) +unsigned int jit_stack_trace_get_offset + (jit_context_t context, jit_stack_trace_t trace, unsigned int posn); +void jit_stack_trace_free(jit_stack_trace_t trace); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_EXCEPT_H */ diff --git a/include/jit/jit-function.h b/include/jit/jit-function.h new file mode 100644 index 0000000..cd5e5d6 --- /dev/null +++ b/include/jit/jit-function.h @@ -0,0 +1,78 @@ +/* + * jit-function.h - Functions for manipulating functions blocks. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_FUNCTION_H +#define _JIT_FUNCTION_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +jit_function_t jit_function_create + (jit_context_t context, jit_type_t signature) JIT_NOTHROW; +jit_function_t jit_function_create_nested + (jit_context_t context, jit_type_t signature, + jit_function_t parent) JIT_NOTHROW; +void jit_function_abandon(jit_function_t func) JIT_NOTHROW; +jit_context_t jit_function_get_context(jit_function_t func) JIT_NOTHROW; +jit_type_t jit_function_get_signature(jit_function_t func) JIT_NOTHROW; +int jit_function_set_meta + (jit_function_t func, int type, void *data, + jit_meta_free_func free_data, int build_only) JIT_NOTHROW; +void *jit_function_get_meta(jit_function_t func, int type) JIT_NOTHROW; +void jit_function_free_meta(jit_function_t func, int type) JIT_NOTHROW; +jit_function_t jit_function_next + (jit_context_t context, jit_function_t prev) JIT_NOTHROW; +jit_function_t jit_function_previous + (jit_context_t context, jit_function_t prev) JIT_NOTHROW; +jit_block_t jit_function_get_entry(jit_function_t func) JIT_NOTHROW; +jit_block_t jit_function_get_current(jit_function_t func) JIT_NOTHROW; +jit_function_t jit_function_get_nested_parent(jit_function_t func) JIT_NOTHROW; +int jit_function_compile(jit_function_t func) JIT_NOTHROW; +int jit_function_recompile(jit_function_t func) JIT_NOTHROW; +int jit_function_is_compiled(jit_function_t func) JIT_NOTHROW; +void jit_function_set_recompilable(jit_function_t func) JIT_NOTHROW; +void jit_function_clear_recompilable(jit_function_t func) JIT_NOTHROW; +int jit_function_is_recompilable(jit_function_t func) JIT_NOTHROW; +void *jit_function_to_closure(jit_function_t func) JIT_NOTHROW; +jit_function_t jit_function_from_closure + (jit_context_t context, void *closure) JIT_NOTHROW; +jit_function_t jit_function_from_pc + (jit_context_t context, void *pc, void **handler) JIT_NOTHROW; +void *jit_function_to_vtable_pointer(jit_function_t func) JIT_NOTHROW; +void jit_function_set_on_demand_compiler + (jit_function_t func, jit_on_demand_func on_demand) JIT_NOTHROW; +int jit_function_apply + (jit_function_t func, void **args, void *return_area); +int jit_function_apply_vararg + (jit_function_t func, jit_type_t signature, void **args, void *return_area); +void jit_function_set_optimization_level + (jit_function_t func, unsigned int level) JIT_NOTHROW; +unsigned int jit_function_get_optimization_level + (jit_function_t func) JIT_NOTHROW; +unsigned int jit_function_get_max_optimization_level(void) JIT_NOTHROW; + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_FUNCTION_H */ diff --git a/include/jit/jit-init.h b/include/jit/jit-init.h new file mode 100644 index 0000000..e05215d --- /dev/null +++ b/include/jit/jit-init.h @@ -0,0 +1,35 @@ +/* + * jit-init.h - Initialization routines for the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_INIT_H +#define _JIT_INIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +void jit_init(void); +int jit_uses_interpreter(void); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_INIT_H */ diff --git a/include/jit/jit-insn.h b/include/jit/jit-insn.h new file mode 100644 index 0000000..b0f9d35 --- /dev/null +++ b/include/jit/jit-insn.h @@ -0,0 +1,270 @@ +/* + * jit-insn.h - Functions for manipulating instructions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_INSN_H +#define _JIT_INSN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Descriptor for an intrinsic function. + */ +typedef struct +{ + jit_type_t return_type; + jit_type_t ptr_result_type; + jit_type_t arg1_type; + jit_type_t arg2_type; + +} jit_intrinsic_descr_t; + +/* + * Structure for iterating over the instructions in a block. + * This should be treated as opaque. + */ +typedef struct +{ + jit_block_t block; + int posn; + +} jit_insn_iter_t; + +/* + * Flags for "jit_insn_call" and friends. + */ +#define JIT_CALL_NOTHROW (1 << 0) +#define JIT_CALL_NORETURN (1 << 1) +#define JIT_CALL_TAIL (1 << 2) + +int jit_insn_get_opcode(jit_insn_t insn) JIT_NOTHROW; +jit_value_t jit_insn_get_dest(jit_insn_t insn) JIT_NOTHROW; +jit_value_t jit_insn_get_value1(jit_insn_t insn) JIT_NOTHROW; +jit_value_t jit_insn_get_value2(jit_insn_t insn) JIT_NOTHROW; +jit_label_t jit_insn_get_label(jit_insn_t insn) JIT_NOTHROW; +jit_function_t jit_insn_get_function(jit_insn_t insn) JIT_NOTHROW; +void *jit_insn_get_native(jit_insn_t insn) JIT_NOTHROW; +const char *jit_insn_get_name(jit_insn_t insn) JIT_NOTHROW; +jit_type_t jit_insn_get_signature(jit_insn_t insn) JIT_NOTHROW; +int jit_insn_dest_is_value(jit_insn_t insn) JIT_NOTHROW; + +int jit_insn_label(jit_function_t func, jit_label_t *label) JIT_NOTHROW; +jit_value_t jit_insn_load(jit_function_t func, jit_value_t value) JIT_NOTHROW; +jit_value_t jit_insn_dup(jit_function_t func, jit_value_t value) JIT_NOTHROW; +jit_value_t jit_insn_load_small + (jit_function_t func, jit_value_t value) JIT_NOTHROW; +int jit_insn_store + (jit_function_t func, jit_value_t dest, jit_value_t value) JIT_NOTHROW; +jit_value_t jit_insn_load_relative + (jit_function_t func, jit_value_t value, + jit_nint offset, jit_type_t type) JIT_NOTHROW; +int jit_insn_store_relative + (jit_function_t func, jit_value_t dest, + jit_nint offset, jit_value_t value) JIT_NOTHROW; +jit_value_t jit_insn_add_relative + (jit_function_t func, jit_value_t value, jit_nint offset) JIT_NOTHROW; +int jit_insn_check_null(jit_function_t func, jit_value_t value) JIT_NOTHROW; + +jit_value_t jit_insn_add + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_add_ovf + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_sub + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_sub_ovf + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_mul + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_mul_ovf + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_div + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_rem + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_rem_ieee + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_neg + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_and + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_or + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_xor + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_not + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_shl + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_shr + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_ushr + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_sshr + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_eq + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_ne + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_lt + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_le + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_gt + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_ge + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_cmpl + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_cmpg + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_to_bool + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_to_not_bool + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_acos + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_asin + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_atan + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_atan2 + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_ceil + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_cos + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_cosh + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_exp + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_floor + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_log + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_log10 + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_pow + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_rint + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_round + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_sin + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_sinh + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_sqrt + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_tan + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_tanh + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_is_nan + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_is_finite + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_is_inf + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_abs + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_min + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_max + (jit_function_t func, jit_value_t value1, jit_value_t value2) JIT_NOTHROW; +jit_value_t jit_insn_sign + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +int jit_insn_branch + (jit_function_t func, jit_label_t *label) JIT_NOTHROW; +int jit_insn_branch_if + (jit_function_t func, jit_value_t value, jit_label_t *label) JIT_NOTHROW; +int jit_insn_branch_if_not + (jit_function_t func, jit_value_t value, jit_label_t *label) JIT_NOTHROW; +jit_value_t jit_insn_address_of + (jit_function_t func, jit_value_t value1) JIT_NOTHROW; +jit_value_t jit_insn_convert + (jit_function_t func, jit_value_t value, + jit_type_t type, int overflow_check) JIT_NOTHROW; + +jit_value_t jit_insn_call + (jit_function_t func, const char *name, + jit_function_t jit_func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) JIT_NOTHROW; +jit_value_t jit_insn_call_indirect + (jit_function_t func, jit_value_t value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) JIT_NOTHROW; +jit_value_t jit_insn_call_indirect_vtable + (jit_function_t func, jit_value_t value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) JIT_NOTHROW; +jit_value_t jit_insn_call_native + (jit_function_t func, const char *name, + void *native_func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) JIT_NOTHROW; +jit_value_t jit_insn_call_intrinsic + (jit_function_t func, const char *name, void *intrinsic_func, + const jit_intrinsic_descr_t *descriptor, + jit_value_t arg1, jit_value_t arg2) JIT_NOTHROW; +int jit_insn_incoming_reg + (jit_function_t func, jit_value_t value, int reg) JIT_NOTHROW; +int jit_insn_incoming_frame_posn + (jit_function_t func, jit_value_t value, jit_nint frame_offset) JIT_NOTHROW; +int jit_insn_outgoing_reg + (jit_function_t func, jit_value_t value, int reg) JIT_NOTHROW; +int jit_insn_return_reg + (jit_function_t func, jit_value_t value, int reg) JIT_NOTHROW; +int jit_insn_setup_for_nested + (jit_function_t func, int nested_level, int reg) JIT_NOTHROW; +int jit_insn_flush_struct(jit_function_t func, jit_value_t value) JIT_NOTHROW; +jit_value_t jit_insn_import + (jit_function_t func, jit_value_t value) JIT_NOTHROW; +int jit_insn_push(jit_function_t func, jit_value_t value) JIT_NOTHROW; +int jit_insn_pop_stack(jit_function_t func, jit_nint num_items) JIT_NOTHROW; +int jit_insn_return(jit_function_t func, jit_value_t value) JIT_NOTHROW; +int jit_insn_default_return(jit_function_t func) JIT_NOTHROW; +int jit_insn_throw(jit_function_t func, jit_value_t value) JIT_NOTHROW; +jit_value_t jit_insn_get_call_stack(jit_function_t func) JIT_NOTHROW; +int jit_insn_start_try + (jit_function_t func, jit_label_t *catch_label, + jit_label_t *finally_label, int finally_on_fault); +jit_value_t jit_insn_start_catch + (jit_function_t func, jit_label_t *catch_label); +int jit_insn_end_try(jit_function_t func); +int jit_insn_start_finally(jit_function_t func, jit_label_t *finally_label); +int jit_insn_return_from_finally(jit_function_t func); +jit_value_t jit_insn_start_filter + (jit_function_t func, jit_label_t *label, jit_type_t type); +int jit_insn_return_from_filter(jit_function_t func, jit_value_t value); +jit_value_t jit_insn_call_filter + (jit_function_t func, jit_label_t *label, + jit_value_t value, jit_type_t type); + +void jit_insn_iter_init(jit_insn_iter_t *iter, jit_block_t block) JIT_NOTHROW; +void jit_insn_iter_init_last + (jit_insn_iter_t *iter, jit_block_t block) JIT_NOTHROW; +jit_insn_t jit_insn_iter_next(jit_insn_iter_t *iter) JIT_NOTHROW; +jit_insn_t jit_insn_iter_previous(jit_insn_iter_t *iter) JIT_NOTHROW; + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_INSN_H */ diff --git a/include/jit/jit-intrinsic.h b/include/jit/jit-intrinsic.h new file mode 100644 index 0000000..1e56425 --- /dev/null +++ b/include/jit/jit-intrinsic.h @@ -0,0 +1,408 @@ +/* + * jit-intrinsic.h - Support routines for JIT intrinsics. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_INTRINSIC_H +#define _JIT_INTRINSIC_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Perform operations on signed 32-bit integers. + */ +jit_int jit_int_add(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_sub(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_mul(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_div + (jit_int *result, jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_rem + (jit_int *result, jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_add_ovf + (jit_int *result, jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_sub_ovf + (jit_int *result, jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_mul_ovf + (jit_int *result, jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_neg(jit_int value1) JIT_NOTHROW; +jit_int jit_int_and(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_or(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_xor(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_not(jit_int value1) JIT_NOTHROW; +jit_int jit_int_shl(jit_int value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_int_shr(jit_int value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_int_eq(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_ne(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_lt(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_le(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_gt(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_ge(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_cmp(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_abs(jit_int value1) JIT_NOTHROW; +jit_int jit_int_min(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_max(jit_int value1, jit_int value2) JIT_NOTHROW; +jit_int jit_int_sign(jit_int value1) JIT_NOTHROW; + +/* + * Perform operations on unsigned 32-bit integers. + */ +jit_uint jit_uint_add(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_sub(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_mul(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_div + (jit_uint *result, jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_rem + (jit_uint *result, jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_add_ovf + (jit_uint *result, jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_sub_ovf + (jit_uint *result, jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_mul_ovf + (jit_uint *result, jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_neg(jit_uint value1) JIT_NOTHROW; +jit_uint jit_uint_and(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_or(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_xor(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_not(jit_uint value1) JIT_NOTHROW; +jit_uint jit_uint_shl(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_shr(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_eq(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_ne(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_lt(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_le(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_gt(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_ge(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_uint_cmp(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_min(jit_uint value1, jit_uint value2) JIT_NOTHROW; +jit_uint jit_uint_max(jit_uint value1, jit_uint value2) JIT_NOTHROW; + +/* + * Perform operations on signed 64-bit integers. + */ +jit_long jit_long_add(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_sub(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_mul(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_div + (jit_long *result, jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_rem + (jit_long *result, jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_add_ovf + (jit_long *result, jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_sub_ovf + (jit_long *result, jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_mul_ovf + (jit_long *result, jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_neg(jit_long value1) JIT_NOTHROW; +jit_long jit_long_and(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_or(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_xor(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_not(jit_long value1) JIT_NOTHROW; +jit_long jit_long_shl(jit_long value1, jit_uint value2) JIT_NOTHROW; +jit_long jit_long_shr(jit_long value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_long_eq(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_ne(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_lt(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_le(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_gt(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_ge(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_cmp(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_abs(jit_long value1) JIT_NOTHROW; +jit_long jit_long_min(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_long jit_long_max(jit_long value1, jit_long value2) JIT_NOTHROW; +jit_int jit_long_sign(jit_long value1) JIT_NOTHROW; + +/* + * Perform operations on unsigned 64-bit integers. + */ +jit_ulong jit_ulong_add(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_sub(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_mul(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_div + (jit_ulong *result, jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_rem + (jit_ulong *result, jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_add_ovf + (jit_ulong *result, jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_sub_ovf + (jit_ulong *result, jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_mul_ovf + (jit_ulong *result, jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_neg(jit_ulong value1) JIT_NOTHROW; +jit_ulong jit_ulong_and(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_or(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_xor(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_not(jit_ulong value1) JIT_NOTHROW; +jit_ulong jit_ulong_shl(jit_ulong value1, jit_uint value2) JIT_NOTHROW; +jit_ulong jit_ulong_shr(jit_ulong value1, jit_uint value2) JIT_NOTHROW; +jit_int jit_ulong_eq(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_ne(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_lt(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_le(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_gt(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_ge(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_int jit_ulong_cmp(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_min(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; +jit_ulong jit_ulong_max(jit_ulong value1, jit_ulong value2) JIT_NOTHROW; + +/* + * Perform operations on 32-bit floating-point values. + */ +jit_float32 jit_float32_add + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_sub + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_mul + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_div + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_rem + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_ieee_rem + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_neg(jit_float32 value1) JIT_NOTHROW; +jit_int jit_float32_eq(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_ne(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_lt(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_le(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_gt(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_ge(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_cmpl(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_cmpg(jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_acos(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_asin(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_atan(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_atan2 + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_ceil(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_cos(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_cosh(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_exp(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_floor(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_log(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_log10(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_pow + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_rint(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_round(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_sin(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_sinh(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_sqrt(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_tan(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_tanh(jit_float32 value1) JIT_NOTHROW; +jit_int jit_float32_is_finite(jit_float32 value) JIT_NOTHROW; +jit_int jit_float32_is_nan(jit_float32 value) JIT_NOTHROW; +jit_int jit_float32_is_inf(jit_float32 value) JIT_NOTHROW; +jit_float32 jit_float32_abs(jit_float32 value1) JIT_NOTHROW; +jit_float32 jit_float32_min + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_float32 jit_float32_max + (jit_float32 value1, jit_float32 value2) JIT_NOTHROW; +jit_int jit_float32_sign(jit_float32 value1) JIT_NOTHROW; + +/* + * Perform operations on 64-bit floating-point values. + */ +jit_float64 jit_float64_add + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_sub + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_mul + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_div + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_rem + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_ieee_rem + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_neg(jit_float64 value1) JIT_NOTHROW; +jit_int jit_float64_eq(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_ne(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_lt(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_le(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_gt(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_ge(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_cmpl(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_cmpg(jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_acos(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_asin(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_atan(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_atan2 + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_ceil(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_cos(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_cosh(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_exp(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_floor(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_log(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_log10(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_pow + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_rint(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_round(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_sin(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_sinh(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_sqrt(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_tan(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_tanh(jit_float64 value1) JIT_NOTHROW; +jit_int jit_float64_is_finite(jit_float64 value) JIT_NOTHROW; +jit_int jit_float64_is_nan(jit_float64 value) JIT_NOTHROW; +jit_int jit_float64_is_inf(jit_float64 value) JIT_NOTHROW; +jit_float64 jit_float64_abs(jit_float64 value1) JIT_NOTHROW; +jit_float64 jit_float64_min + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_float64 jit_float64_max + (jit_float64 value1, jit_float64 value2) JIT_NOTHROW; +jit_int jit_float64_sign(jit_float64 value1) JIT_NOTHROW; + +/* + * Perform operations on native floating-point values. + */ +jit_nfloat jit_nfloat_add(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_sub(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_mul(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_div(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_rem(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_ieee_rem + (jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_neg(jit_nfloat value1) JIT_NOTHROW; +jit_int jit_nfloat_eq(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_ne(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_lt(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_le(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_gt(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_ge(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_cmpl(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_cmpg(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_acos(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_asin(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_atan(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_atan2(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_ceil(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_cos(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_cosh(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_exp(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_floor(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_log(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_log10(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_pow(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_rint(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_round(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_sin(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_sinh(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_sqrt(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_tan(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_tanh(jit_nfloat value1) JIT_NOTHROW; +jit_int jit_nfloat_is_finite(jit_nfloat value) JIT_NOTHROW; +jit_int jit_nfloat_is_nan(jit_nfloat value) JIT_NOTHROW; +jit_int jit_nfloat_is_inf(jit_nfloat value) JIT_NOTHROW; +jit_nfloat jit_nfloat_abs(jit_nfloat value1) JIT_NOTHROW; +jit_nfloat jit_nfloat_min(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_nfloat jit_nfloat_max(jit_nfloat value1, jit_nfloat value2) JIT_NOTHROW; +jit_int jit_nfloat_sign(jit_nfloat value1) JIT_NOTHROW; + +/* + * Convert between integer types. + */ +jit_int jit_int_to_sbyte(jit_int value) JIT_NOTHROW; +jit_int jit_int_to_ubyte(jit_int value) JIT_NOTHROW; +jit_int jit_int_to_short(jit_int value) JIT_NOTHROW; +jit_int jit_int_to_ushort(jit_int value) JIT_NOTHROW; +jit_int jit_int_to_int(jit_int value) JIT_NOTHROW; +jit_uint jit_int_to_uint(jit_int value) JIT_NOTHROW; +jit_long jit_int_to_long(jit_int value) JIT_NOTHROW; +jit_ulong jit_int_to_ulong(jit_int value) JIT_NOTHROW; +jit_int jit_uint_to_int(jit_uint value) JIT_NOTHROW; +jit_uint jit_uint_to_uint(jit_uint value) JIT_NOTHROW; +jit_long jit_uint_to_long(jit_uint value) JIT_NOTHROW; +jit_ulong jit_uint_to_ulong(jit_uint value) JIT_NOTHROW; +jit_int jit_long_to_int(jit_long value) JIT_NOTHROW; +jit_uint jit_long_to_uint(jit_long value) JIT_NOTHROW; +jit_long jit_long_to_long(jit_long value) JIT_NOTHROW; +jit_ulong jit_long_to_ulong(jit_long value) JIT_NOTHROW; +jit_int jit_ulong_to_int(jit_ulong value) JIT_NOTHROW; +jit_uint jit_ulong_to_uint(jit_ulong value) JIT_NOTHROW; +jit_long jit_ulong_to_long(jit_ulong value) JIT_NOTHROW; +jit_ulong jit_ulong_to_ulong(jit_ulong value) JIT_NOTHROW; + +/* + * Convert between integer types with overflow detection. + */ +jit_int jit_int_to_sbyte_ovf(jit_int *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_ubyte_ovf(jit_int *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_short_ovf(jit_int *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_ushort_ovf(jit_int *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_int_ovf(jit_int *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_uint_ovf(jit_uint *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_long_ovf(jit_long *result, jit_int value) JIT_NOTHROW; +jit_int jit_int_to_ulong_ovf(jit_ulong *result, jit_int value) JIT_NOTHROW; +jit_int jit_uint_to_int_ovf(jit_int *result, jit_uint value) JIT_NOTHROW; +jit_int jit_uint_to_uint_ovf(jit_uint *result, jit_uint value) JIT_NOTHROW; +jit_int jit_uint_to_long_ovf(jit_long *result, jit_uint value) JIT_NOTHROW; +jit_int jit_uint_to_ulong_ovf(jit_ulong *result, jit_uint value) JIT_NOTHROW; +jit_int jit_long_to_int_ovf(jit_int *result, jit_long value) JIT_NOTHROW; +jit_int jit_long_to_uint_ovf(jit_uint *result, jit_long value) JIT_NOTHROW; +jit_int jit_long_to_long_ovf(jit_long *result, jit_long value) JIT_NOTHROW; +jit_int jit_long_to_ulong_ovf(jit_ulong *result, jit_long value) JIT_NOTHROW; +jit_int jit_ulong_to_int_ovf(jit_int *result, jit_ulong value) JIT_NOTHROW; +jit_int jit_ulong_to_uint_ovf(jit_uint *result, jit_ulong value) JIT_NOTHROW; +jit_int jit_ulong_to_long_ovf(jit_long *result, jit_ulong value) JIT_NOTHROW; +jit_int jit_ulong_to_ulong_ovf(jit_ulong *result, jit_ulong value) JIT_NOTHROW; + +/* + * Convert a native floating-point value into various integer types. + */ +jit_int jit_nfloat_to_int(jit_nfloat value) JIT_NOTHROW; +jit_uint jit_nfloat_to_uint(jit_nfloat value) JIT_NOTHROW; +jit_long jit_nfloat_to_long(jit_nfloat value) JIT_NOTHROW; +jit_ulong jit_nfloat_to_ulong(jit_nfloat value) JIT_NOTHROW; + +/* + * Convert a native floating-point value into various integer types, + * with overflow detection. + */ +jit_int jit_nfloat_to_int_ovf(jit_int *result, jit_nfloat value) JIT_NOTHROW; +jit_int jit_nfloat_to_uint_ovf(jit_uint *result, jit_nfloat value) JIT_NOTHROW; +jit_int jit_nfloat_to_long_ovf(jit_long *result, jit_nfloat value) JIT_NOTHROW; +jit_int jit_nfloat_to_ulong_ovf + (jit_ulong *result, jit_nfloat value) JIT_NOTHROW; + +/* + * Convert integer types into floating-point values. + */ +jit_nfloat jit_int_to_nfloat(jit_int value) JIT_NOTHROW; +jit_nfloat jit_uint_to_nfloat(jit_uint value) JIT_NOTHROW; +jit_nfloat jit_long_to_nfloat(jit_long value) JIT_NOTHROW; +jit_nfloat jit_ulong_to_nfloat(jit_ulong value) JIT_NOTHROW; + +/* + * Convert between floating-point types. + */ +jit_nfloat jit_float32_to_nfloat(jit_float32 value) JIT_NOTHROW; +jit_nfloat jit_float64_to_nfloat(jit_float64 value) JIT_NOTHROW; +jit_float32 jit_nfloat_to_float32(jit_nfloat value) JIT_NOTHROW; +jit_float64 jit_nfloat_to_float64(jit_nfloat value) JIT_NOTHROW; + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_INTRINSIC_H */ diff --git a/include/jit/jit-meta.h b/include/jit/jit-meta.h new file mode 100644 index 0000000..b113a79 --- /dev/null +++ b/include/jit/jit-meta.h @@ -0,0 +1,40 @@ +/* + * jit-meta.h - Manipulate lists of metadata values. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_META_H +#define _JIT_META_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _jit_meta *jit_meta_t; + +int jit_meta_set(jit_meta_t *list, int type, void *data, + jit_meta_free_func free_data, jit_function_t pool_owner); +void *jit_meta_get(jit_meta_t list, int type); +void jit_meta_free(jit_meta_t *list, int type); +void jit_meta_destroy(jit_meta_t *list); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_META_H */ diff --git a/include/jit/jit-opcode.h b/include/jit/jit-opcode.h new file mode 100644 index 0000000..fc7e7d1 --- /dev/null +++ b/include/jit/jit-opcode.h @@ -0,0 +1,553 @@ +/* + * jit-opcode.h - List of primitive opcodes for JIT instructions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_OPCODE_H +#define _JIT_OPCODE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Simple opcodes. + */ +#define JIT_OP_NOP 0x0000 + +/* + * Conversion opcodes. + */ +#define JIT_OP_TRUNC_SBYTE 0x0001 +#define JIT_OP_TRUNC_UBYTE 0x0002 +#define JIT_OP_TRUNC_SHORT 0x0003 +#define JIT_OP_TRUNC_USHORT 0x0004 +#define JIT_OP_TRUNC_INT 0x0005 +#define JIT_OP_TRUNC_UINT 0x0006 +#define JIT_OP_CHECK_SBYTE 0x0007 +#define JIT_OP_CHECK_UBYTE 0x0008 +#define JIT_OP_CHECK_SHORT 0x0009 +#define JIT_OP_CHECK_USHORT 0x000A +#define JIT_OP_CHECK_INT 0x000B +#define JIT_OP_CHECK_UINT 0x000C +#define JIT_OP_LOW_WORD 0x000D +#define JIT_OP_EXPAND_INT 0x000E +#define JIT_OP_EXPAND_UINT 0x000F +#define JIT_OP_CHECK_LOW_WORD 0x0010 +#define JIT_OP_CHECK_SIGNED_LOW_WORD 0x0011 +#define JIT_OP_CHECK_LONG 0x0012 +#define JIT_OP_CHECK_ULONG 0x0013 +#define JIT_OP_NFLOAT_TO_INT 0x0014 +#define JIT_OP_NFLOAT_TO_UINT 0x0015 +#define JIT_OP_NFLOAT_TO_LONG 0x0016 +#define JIT_OP_NFLOAT_TO_ULONG 0x0017 +#define JIT_OP_CHECK_NFLOAT_TO_INT 0x0018 +#define JIT_OP_CHECK_NFLOAT_TO_UINT 0x0019 +#define JIT_OP_CHECK_NFLOAT_TO_LONG 0x001A +#define JIT_OP_CHECK_NFLOAT_TO_ULONG 0x001B +#define JIT_OP_INT_TO_NFLOAT 0x001C +#define JIT_OP_UINT_TO_NFLOAT 0x001D +#define JIT_OP_LONG_TO_NFLOAT 0x001E +#define JIT_OP_ULONG_TO_NFLOAT 0x001F +#define JIT_OP_NFLOAT_TO_FLOAT32 0x0020 +#define JIT_OP_NFLOAT_TO_FLOAT64 0x0021 +#define JIT_OP_FLOAT32_TO_NFLOAT 0x0022 +#define JIT_OP_FLOAT64_TO_NFLOAT 0x0023 + +/* + * Arithmetic opcodes. + */ +#define JIT_OP_IADD 0x0024 +#define JIT_OP_IADD_OVF 0x0025 +#define JIT_OP_IADD_OVF_UN 0x0026 +#define JIT_OP_ISUB 0x0027 +#define JIT_OP_ISUB_OVF 0x0028 +#define JIT_OP_ISUB_OVF_UN 0x0029 +#define JIT_OP_IMUL 0x002A +#define JIT_OP_IMUL_OVF 0x002B +#define JIT_OP_IMUL_OVF_UN 0x002C +#define JIT_OP_IDIV 0x002D +#define JIT_OP_IDIV_UN 0x002E +#define JIT_OP_IREM 0x002F +#define JIT_OP_IREM_UN 0x0030 +#define JIT_OP_INEG 0x0031 +#define JIT_OP_LADD 0x0032 +#define JIT_OP_LADD_OVF 0x0033 +#define JIT_OP_LADD_OVF_UN 0x0034 +#define JIT_OP_LSUB 0x0035 +#define JIT_OP_LSUB_OVF 0x0036 +#define JIT_OP_LSUB_OVF_UN 0x0037 +#define JIT_OP_LMUL 0x0038 +#define JIT_OP_LMUL_OVF 0x0039 +#define JIT_OP_LMUL_OVF_UN 0x003A +#define JIT_OP_LDIV 0x003B +#define JIT_OP_LDIV_UN 0x003C +#define JIT_OP_LREM 0x003D +#define JIT_OP_LREM_UN 0x003E +#define JIT_OP_LNEG 0x003F +#define JIT_OP_FADD 0x0040 +#define JIT_OP_FSUB 0x0041 +#define JIT_OP_FMUL 0x0042 +#define JIT_OP_FDIV 0x0043 +#define JIT_OP_FREM 0x0044 +#define JIT_OP_FREM_IEEE 0x0045 +#define JIT_OP_FNEG 0x0046 +#define JIT_OP_DADD 0x0047 +#define JIT_OP_DSUB 0x0048 +#define JIT_OP_DMUL 0x0049 +#define JIT_OP_DDIV 0x004A +#define JIT_OP_DREM 0x004B +#define JIT_OP_DREM_IEEE 0x004C +#define JIT_OP_DNEG 0x004D +#define JIT_OP_NFADD 0x004E +#define JIT_OP_NFSUB 0x004F +#define JIT_OP_NFMUL 0x0050 +#define JIT_OP_NFDIV 0x0051 +#define JIT_OP_NFREM 0x0052 +#define JIT_OP_NFREM_IEEE 0x0053 +#define JIT_OP_NFNEG 0x0054 + +/* + * Bitwise opcodes. + */ +#define JIT_OP_IAND 0x0055 +#define JIT_OP_IOR 0x0056 +#define JIT_OP_IXOR 0x0057 +#define JIT_OP_INOT 0x0058 +#define JIT_OP_ISHL 0x0059 +#define JIT_OP_ISHR 0x005A +#define JIT_OP_ISHR_UN 0x005B +#define JIT_OP_LAND 0x005C +#define JIT_OP_LOR 0x005D +#define JIT_OP_LXOR 0x005E +#define JIT_OP_LNOT 0x005F +#define JIT_OP_LSHL 0x0060 +#define JIT_OP_LSHR 0x0061 +#define JIT_OP_LSHR_UN 0x0062 + +/* + * Branch opcodes. + */ +#define JIT_OP_BR 0x0063 +#define JIT_OP_BR_IFALSE 0x0064 +#define JIT_OP_BR_ITRUE 0x0065 +#define JIT_OP_BR_IEQ 0x0066 +#define JIT_OP_BR_INE 0x0067 +#define JIT_OP_BR_ILT 0x0068 +#define JIT_OP_BR_ILT_UN 0x0069 +#define JIT_OP_BR_ILE 0x006A +#define JIT_OP_BR_ILE_UN 0x006B +#define JIT_OP_BR_IGT 0x006C +#define JIT_OP_BR_IGT_UN 0x006D +#define JIT_OP_BR_IGE 0x006E +#define JIT_OP_BR_IGE_UN 0x006F +#define JIT_OP_BR_LFALSE 0x0070 +#define JIT_OP_BR_LTRUE 0x0071 +#define JIT_OP_BR_LEQ 0x0072 +#define JIT_OP_BR_LNE 0x0073 +#define JIT_OP_BR_LLT 0x0074 +#define JIT_OP_BR_LLT_UN 0x0075 +#define JIT_OP_BR_LLE 0x0076 +#define JIT_OP_BR_LLE_UN 0x0077 +#define JIT_OP_BR_LGT 0x0078 +#define JIT_OP_BR_LGT_UN 0x0079 +#define JIT_OP_BR_LGE 0x007A +#define JIT_OP_BR_LGE_UN 0x007B +#define JIT_OP_BR_FEQ 0x007C +#define JIT_OP_BR_FNE 0x007D +#define JIT_OP_BR_FLT 0x007E +#define JIT_OP_BR_FLE 0x007F +#define JIT_OP_BR_FGT 0x0080 +#define JIT_OP_BR_FGE 0x0081 +#define JIT_OP_BR_FEQ_INV 0x0082 +#define JIT_OP_BR_FNE_INV 0x0083 +#define JIT_OP_BR_FLT_INV 0x0084 +#define JIT_OP_BR_FLE_INV 0x0085 +#define JIT_OP_BR_FGT_INV 0x0086 +#define JIT_OP_BR_FGE_INV 0x0087 +#define JIT_OP_BR_DEQ 0x0088 +#define JIT_OP_BR_DNE 0x0089 +#define JIT_OP_BR_DLT 0x008A +#define JIT_OP_BR_DLE 0x008B +#define JIT_OP_BR_DGT 0x008C +#define JIT_OP_BR_DGE 0x008D +#define JIT_OP_BR_DEQ_INV 0x008E +#define JIT_OP_BR_DNE_INV 0x008F +#define JIT_OP_BR_DLT_INV 0x0090 +#define JIT_OP_BR_DLE_INV 0x0091 +#define JIT_OP_BR_DGT_INV 0x0092 +#define JIT_OP_BR_DGE_INV 0x0093 +#define JIT_OP_BR_NFEQ 0x0094 +#define JIT_OP_BR_NFNE 0x0095 +#define JIT_OP_BR_NFLT 0x0096 +#define JIT_OP_BR_NFLE 0x0097 +#define JIT_OP_BR_NFGT 0x0098 +#define JIT_OP_BR_NFGE 0x0099 +#define JIT_OP_BR_NFEQ_INV 0x009A +#define JIT_OP_BR_NFNE_INV 0x009B +#define JIT_OP_BR_NFLT_INV 0x009C +#define JIT_OP_BR_NFLE_INV 0x009D +#define JIT_OP_BR_NFGT_INV 0x009E +#define JIT_OP_BR_NFGE_INV 0x009F + +/* + * Comparison opcodes. + */ +#define JIT_OP_ICMP 0x00A0 +#define JIT_OP_ICMP_UN 0x00A1 +#define JIT_OP_LCMP 0x00A2 +#define JIT_OP_LCMP_UN 0x00A3 +#define JIT_OP_FCMPL 0x00A4 +#define JIT_OP_FCMPG 0x00A5 +#define JIT_OP_DCMPL 0x00A6 +#define JIT_OP_DCMPG 0x00A7 +#define JIT_OP_NFCMPL 0x00A8 +#define JIT_OP_NFCMPG 0x00A9 +#define JIT_OP_IEQ 0x00AA +#define JIT_OP_INE 0x00AB +#define JIT_OP_ILT 0x00AC +#define JIT_OP_ILT_UN 0x00AD +#define JIT_OP_ILE 0x00AE +#define JIT_OP_ILE_UN 0x00AF +#define JIT_OP_IGT 0x00B0 +#define JIT_OP_IGT_UN 0x00B1 +#define JIT_OP_IGE 0x00B2 +#define JIT_OP_IGE_UN 0x00B3 +#define JIT_OP_LEQ 0x00B4 +#define JIT_OP_LNE 0x00B5 +#define JIT_OP_LLT 0x00B6 +#define JIT_OP_LLT_UN 0x00B7 +#define JIT_OP_LLE 0x00B8 +#define JIT_OP_LLE_UN 0x00B9 +#define JIT_OP_LGT 0x00BA +#define JIT_OP_LGT_UN 0x00BB +#define JIT_OP_LGE 0x00BC +#define JIT_OP_LGE_UN 0x00BD +#define JIT_OP_FEQ 0x00BE +#define JIT_OP_FNE 0x00BF +#define JIT_OP_FLT 0x00C0 +#define JIT_OP_FLE 0x00C1 +#define JIT_OP_FGT 0x00C2 +#define JIT_OP_FGE 0x00C3 +#define JIT_OP_FEQ_INV 0x00C4 +#define JIT_OP_FNE_INV 0x00C5 +#define JIT_OP_FLT_INV 0x00C6 +#define JIT_OP_FLE_INV 0x00C7 +#define JIT_OP_FGT_INV 0x00C8 +#define JIT_OP_FGE_INV 0x00C9 +#define JIT_OP_DEQ 0x00CA +#define JIT_OP_DNE 0x00CB +#define JIT_OP_DLT 0x00CC +#define JIT_OP_DLE 0x00CD +#define JIT_OP_DGT 0x00CE +#define JIT_OP_DGE 0x00CF +#define JIT_OP_DEQ_INV 0x00D0 +#define JIT_OP_DNE_INV 0x00D1 +#define JIT_OP_DLT_INV 0x00D2 +#define JIT_OP_DLE_INV 0x00D3 +#define JIT_OP_DGT_INV 0x00D4 +#define JIT_OP_DGE_INV 0x00D5 +#define JIT_OP_NFEQ 0x00D6 +#define JIT_OP_NFNE 0x00D7 +#define JIT_OP_NFLT 0x00D8 +#define JIT_OP_NFLE 0x00D9 +#define JIT_OP_NFGT 0x00DA +#define JIT_OP_NFGE 0x00DB +#define JIT_OP_NFEQ_INV 0x00DC +#define JIT_OP_NFNE_INV 0x00DD +#define JIT_OP_NFLT_INV 0x00DE +#define JIT_OP_NFLE_INV 0x00DF +#define JIT_OP_NFGT_INV 0x00E0 +#define JIT_OP_NFGE_INV 0x00E1 +#define JIT_OP_IS_FNAN 0x00E2 +#define JIT_OP_IS_FINF 0x00E3 +#define JIT_OP_IS_FFINITE 0x00E4 +#define JIT_OP_IS_DNAN 0x00E5 +#define JIT_OP_IS_DINF 0x00E6 +#define JIT_OP_IS_DFINITE 0x00E7 +#define JIT_OP_IS_NFNAN 0x00E8 +#define JIT_OP_IS_NFINF 0x00E9 +#define JIT_OP_IS_NFFINITE 0x00EA + +/* + * Mathematical functions. + */ +#define JIT_OP_FACOS 0x00EB +#define JIT_OP_FASIN 0x00EC +#define JIT_OP_FATAN 0x00ED +#define JIT_OP_FATAN2 0x00EE +#define JIT_OP_FCEIL 0x00EF +#define JIT_OP_FCOS 0x00F0 +#define JIT_OP_FCOSH 0x00F1 +#define JIT_OP_FEXP 0x00F2 +#define JIT_OP_FFLOOR 0x00F3 +#define JIT_OP_FLOG 0x00F4 +#define JIT_OP_FLOG10 0x00F5 +#define JIT_OP_FPOW 0x00F6 +#define JIT_OP_FRINT 0x00F7 +#define JIT_OP_FROUND 0x00F8 +#define JIT_OP_FSIN 0x00F9 +#define JIT_OP_FSINH 0x00FA +#define JIT_OP_FSQRT 0x00FB +#define JIT_OP_FTAN 0x00FC +#define JIT_OP_FTANH 0x00FD +#define JIT_OP_DACOS 0x00FE +#define JIT_OP_DASIN 0x00FF +#define JIT_OP_DATAN 0x0100 +#define JIT_OP_DATAN2 0x0101 +#define JIT_OP_DCEIL 0x0102 +#define JIT_OP_DCOS 0x0103 +#define JIT_OP_DCOSH 0x0104 +#define JIT_OP_DEXP 0x0105 +#define JIT_OP_DFLOOR 0x0106 +#define JIT_OP_DLOG 0x0107 +#define JIT_OP_DLOG10 0x0108 +#define JIT_OP_DPOW 0x0109 +#define JIT_OP_DRINT 0x010A +#define JIT_OP_DROUND 0x010B +#define JIT_OP_DSIN 0x010C +#define JIT_OP_DSINH 0x010D +#define JIT_OP_DSQRT 0x010E +#define JIT_OP_DTAN 0x010F +#define JIT_OP_DTANH 0x0110 +#define JIT_OP_NFACOS 0x0111 +#define JIT_OP_NFASIN 0x0112 +#define JIT_OP_NFATAN 0x0113 +#define JIT_OP_NFATAN2 0x0114 +#define JIT_OP_NFCEIL 0x0115 +#define JIT_OP_NFCOS 0x0116 +#define JIT_OP_NFCOSH 0x0117 +#define JIT_OP_NFEXP 0x0118 +#define JIT_OP_NFFLOOR 0x0119 +#define JIT_OP_NFLOG 0x011A +#define JIT_OP_NFLOG10 0x011B +#define JIT_OP_NFPOW 0x011C +#define JIT_OP_NFRINT 0x011D +#define JIT_OP_NFROUND 0x011E +#define JIT_OP_NFSIN 0x011F +#define JIT_OP_NFSINH 0x0120 +#define JIT_OP_NFSQRT 0x0121 +#define JIT_OP_NFTAN 0x0122 +#define JIT_OP_NFTANH 0x0123 + +/* + * Absolute, minimum, maximum, and sign. + */ +#define JIT_OP_IABS 0x0124 +#define JIT_OP_LABS 0x0125 +#define JIT_OP_FABS 0x0126 +#define JIT_OP_DABS 0x0127 +#define JIT_OP_NFABS 0x0128 +#define JIT_OP_IMIN 0x0129 +#define JIT_OP_IMIN_UN 0x012A +#define JIT_OP_LMIN 0x012B +#define JIT_OP_LMIN_UN 0x012C +#define JIT_OP_FMIN 0x012D +#define JIT_OP_DMIN 0x012E +#define JIT_OP_NFMIN 0x012F +#define JIT_OP_IMAX 0x0130 +#define JIT_OP_IMAX_UN 0x0131 +#define JIT_OP_LMAX 0x0132 +#define JIT_OP_LMAX_UN 0x0133 +#define JIT_OP_FMAX 0x0134 +#define JIT_OP_DMAX 0x0135 +#define JIT_OP_NFMAX 0x0136 +#define JIT_OP_ISIGN 0x0137 +#define JIT_OP_LSIGN 0x0138 +#define JIT_OP_FSIGN 0x0139 +#define JIT_OP_DSIGN 0x013A +#define JIT_OP_NFSIGN 0x013B + +/* + * Pointer check opcodes. + */ +#define JIT_OP_CHECK_NULL 0x013C + +/* + * Function calls. + */ +#define JIT_OP_CALL 0x013D +#define JIT_OP_CALL_TAIL 0x013E +#define JIT_OP_CALL_INDIRECT 0x013F +#define JIT_OP_CALL_VTABLE_PTR 0x0140 +#define JIT_OP_CALL_EXTERNAL 0x0141 +#define JIT_OP_RETURN 0x0142 +#define JIT_OP_RETURN_INT 0x0143 +#define JIT_OP_RETURN_LONG 0x0144 +#define JIT_OP_RETURN_FLOAT32 0x0145 +#define JIT_OP_RETURN_FLOAT64 0x0146 +#define JIT_OP_RETURN_NFLOAT 0x0147 +#define JIT_OP_RETURN_SMALL_STRUCT 0x0148 +#define JIT_OP_SETUP_FOR_NESTED 0x0149 +#define JIT_OP_SETUP_FOR_SIBLING 0x014A +#define JIT_OP_IMPORT 0x014B + +/* + * Exception handling. + */ +#define JIT_OP_THROW 0x014C +#define JIT_OP_LOAD_PC 0x014D +#define JIT_OP_ENTER_CATCH 0x014E +#define JIT_OP_ENTER_FINALLY 0x014F +#define JIT_OP_LEAVE_FINALLY 0x0150 +#define JIT_OP_ENTER_FILTER 0x0151 +#define JIT_OP_LEAVE_FILTER 0x0152 +#define JIT_OP_CALL_FILTER 0x0153 +#define JIT_OP_CALL_FILTER_RETURN 0x0154 +#define JIT_OP_PREPARE_FOR_LEAVE 0x0155 +#define JIT_OP_PREPARE_FOR_RETURN 0x0156 + +/* + * Data manipulation. + */ +#define JIT_OP_COPY_LOAD_SBYTE 0x0157 +#define JIT_OP_COPY_LOAD_UBYTE 0x0158 +#define JIT_OP_COPY_LOAD_SHORT 0x0159 +#define JIT_OP_COPY_LOAD_USHORT 0x015A +#define JIT_OP_COPY_INT 0x015B +#define JIT_OP_COPY_LONG 0x015C +#define JIT_OP_COPY_FLOAT32 0x015D +#define JIT_OP_COPY_FLOAT64 0x015E +#define JIT_OP_COPY_NFLOAT 0x015F +#define JIT_OP_COPY_STRUCT 0x0160 +#define JIT_OP_COPY_STORE_BYTE 0x0161 +#define JIT_OP_COPY_STORE_SHORT 0x0162 +#define JIT_OP_ADDRESS_OF 0x0163 + +/* + * Incoming registers, outgoing registers, and stack pushes. + */ +#define JIT_OP_INCOMING_REG 0x0164 +#define JIT_OP_INCOMING_FRAME_POSN 0x0165 +#define JIT_OP_OUTGOING_REG 0x0166 +#define JIT_OP_RETURN_REG 0x0167 +#define JIT_OP_PUSH_INT 0x0168 +#define JIT_OP_PUSH_LONG 0x0169 +#define JIT_OP_PUSH_FLOAT32 0x016A +#define JIT_OP_PUSH_FLOAT64 0x016B +#define JIT_OP_PUSH_NFLOAT 0x016C +#define JIT_OP_PUSH_STRUCT 0x016D +#define JIT_OP_POP_STACK 0x016E +#define JIT_OP_FLUSH_SMALL_STRUCT 0x016F + +/* + * Pointer-relative loads and stores. + */ +#define JIT_OP_LOAD_RELATIVE_SBYTE 0x0170 +#define JIT_OP_LOAD_RELATIVE_UBYTE 0x0171 +#define JIT_OP_LOAD_RELATIVE_SHORT 0x0172 +#define JIT_OP_LOAD_RELATIVE_USHORT 0x0173 +#define JIT_OP_LOAD_RELATIVE_INT 0x0174 +#define JIT_OP_LOAD_RELATIVE_LONG 0x0175 +#define JIT_OP_LOAD_RELATIVE_FLOAT32 0x0176 +#define JIT_OP_LOAD_RELATIVE_FLOAT64 0x0177 +#define JIT_OP_LOAD_RELATIVE_NFLOAT 0x0178 +#define JIT_OP_LOAD_RELATIVE_STRUCT 0x0179 +#define JIT_OP_STORE_RELATIVE_BYTE 0x017A +#define JIT_OP_STORE_RELATIVE_SHORT 0x017B +#define JIT_OP_STORE_RELATIVE_INT 0x017C +#define JIT_OP_STORE_RELATIVE_LONG 0x017D +#define JIT_OP_STORE_RELATIVE_FLOAT32 0x017E +#define JIT_OP_STORE_RELATIVE_FLOAT64 0x017F +#define JIT_OP_STORE_RELATIVE_NFLOAT 0x0180 +#define JIT_OP_STORE_RELATIVE_STRUCT 0x0181 +#define JIT_OP_ADD_RELATIVE 0x0182 + +/* + * The number of opcodes in the above list. + */ +#define JIT_OP_NUM_OPCODES 0x0183 + +/* + * Opcode information. + */ +typedef struct jit_opcode_info jit_opcode_info_t; +struct jit_opcode_info +{ + const char *name; + int flags; +}; +#define JIT_OPCODE_DEST_MASK 0x0000000F +#define JIT_OPCODE_DEST_EMPTY 0x00000000 +#define JIT_OPCODE_DEST_INT 0x00000001 +#define JIT_OPCODE_DEST_LONG 0x00000002 +#define JIT_OPCODE_DEST_FLOAT32 0x00000003 +#define JIT_OPCODE_DEST_FLOAT64 0x00000004 +#define JIT_OPCODE_DEST_NFLOAT 0x00000005 +#define JIT_OPCODE_DEST_ANY 0x00000006 +#define JIT_OPCODE_SRC1_MASK 0x000000F0 +#define JIT_OPCODE_SRC1_EMPTY 0x00000000 +#define JIT_OPCODE_SRC1_INT 0x00000010 +#define JIT_OPCODE_SRC1_LONG 0x00000020 +#define JIT_OPCODE_SRC1_FLOAT32 0x00000030 +#define JIT_OPCODE_SRC1_FLOAT64 0x00000040 +#define JIT_OPCODE_SRC1_NFLOAT 0x00000050 +#define JIT_OPCODE_SRC1_ANY 0x00000060 +#define JIT_OPCODE_SRC2_MASK 0x00000F00 +#define JIT_OPCODE_SRC2_EMPTY 0x00000000 +#define JIT_OPCODE_SRC2_INT 0x00000100 +#define JIT_OPCODE_SRC2_LONG 0x00000200 +#define JIT_OPCODE_SRC2_FLOAT32 0x00000300 +#define JIT_OPCODE_SRC2_FLOAT64 0x00000400 +#define JIT_OPCODE_SRC2_NFLOAT 0x00000500 +#define JIT_OPCODE_SRC2_ANY 0x00000600 +#define JIT_OPCODE_IS_BRANCH 0x00001000 +#define JIT_OPCODE_IS_CALL 0x00002000 +#define JIT_OPCODE_IS_CALL_EXTERNAL 0x00004000 +#define JIT_OPCODE_IS_REG 0x00008000 +#define JIT_OPCODE_OPER_MASK 0x01F00000 +#define JIT_OPCODE_OPER_NONE 0x00000000 +#define JIT_OPCODE_OPER_ADD 0x00100000 +#define JIT_OPCODE_OPER_SUB 0x00200000 +#define JIT_OPCODE_OPER_MUL 0x00300000 +#define JIT_OPCODE_OPER_DIV 0x00400000 +#define JIT_OPCODE_OPER_REM 0x00500000 +#define JIT_OPCODE_OPER_NEG 0x00600000 +#define JIT_OPCODE_OPER_AND 0x00700000 +#define JIT_OPCODE_OPER_OR 0x00800000 +#define JIT_OPCODE_OPER_XOR 0x00900000 +#define JIT_OPCODE_OPER_NOT 0x00A00000 +#define JIT_OPCODE_OPER_EQ 0x00B00000 +#define JIT_OPCODE_OPER_NE 0x00C00000 +#define JIT_OPCODE_OPER_LT 0x00D00000 +#define JIT_OPCODE_OPER_LE 0x00E00000 +#define JIT_OPCODE_OPER_GT 0x00F00000 +#define JIT_OPCODE_OPER_GE 0x01000000 +#define JIT_OPCODE_OPER_SHL 0x01100000 +#define JIT_OPCODE_OPER_SHR 0x01200000 +#define JIT_OPCODE_OPER_SHR_UN 0x01300000 +#define JIT_OPCODE_OPER_COPY 0x01400000 +#define JIT_OPCODE_OPER_ADDRESS_OF 0x01500000 +#ifdef JIT_NATIVE_INT32 +#define JIT_OPCODE_DEST_PTR JIT_OPCODE_DEST_INT +#define JIT_OPCODE_SRC1_PTR JIT_OPCODE_SRC1_INT +#define JIT_OPCODE_SRC2_PTR JIT_OPCODE_SRC2_INT +#else +#define JIT_OPCODE_DEST_PTR JIT_OPCODE_DEST_LONG +#define JIT_OPCODE_SRC1_PTR JIT_OPCODE_SRC1_LONG +#define JIT_OPCODE_SRC2_PTR JIT_OPCODE_SRC2_LONG +#endif +extern jit_opcode_info_t const jit_opcodes[JIT_OP_NUM_OPCODES]; + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_OPCODE_H */ diff --git a/include/jit/jit-plus.h b/include/jit/jit-plus.h new file mode 100644 index 0000000..920600e --- /dev/null +++ b/include/jit/jit-plus.h @@ -0,0 +1,314 @@ +/* + * jit-plus.h - C++ binding for the JIT library. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_PLUS_H +#define _JIT_PLUS_H + +#include + +#ifdef __cplusplus + +class jit_value +{ +public: + jit_value() { this->value = 0; } + jit_value(jit_value_t value) { this->value = value; } + jit_value(const jit_value& value) { this->value = value.value; } + ~jit_value() {} + + jit_value& operator=(const jit_value& value) + { this->value = value.value; return *this; } + + jit_value_t raw() const { return value; } + int is_valid() const { return (value != 0); } + + int is_temporary() const { return jit_value_is_temporary(value); } + int is_local() const { return jit_value_is_local(value); } + int is_constant() const { return jit_value_is_constant(value); } + + void set_volatile() { jit_value_set_volatile(value); } + int is_volatile() const { return jit_value_is_volatile(value); } + + void set_addressable() { jit_value_set_addressable(value); } + int is_addressable() const { return jit_value_is_addressable(value); } + + jit_type_t type() const { return jit_value_get_type(value); } + jit_function_t function() const { return jit_value_get_function(value); } + jit_block_t block() const { return jit_value_get_block(value); } + jit_context_t context() const { return jit_value_get_context(value); } + + jit_constant_t constant() const + { return jit_value_get_constant(value); } + jit_nint nint_constant() const + { return jit_value_get_nint_constant(value); } + jit_long long_constant() const + { return jit_value_get_long_constant(value); } + jit_float32 float32_constant() const + { return jit_value_get_float32_constant(value); } + jit_float64 float64_constant() const + { return jit_value_get_float64_constant(value); } + jit_nfloat nfloat_constant() const + { return jit_value_get_nfloat_constant(value); } + +private: + jit_value_t value; +}; + +jit_value operator+(const jit_value& value1, const jit_value& value2); +jit_value operator-(const jit_value& value1, const jit_value& value2); +jit_value operator*(const jit_value& value1, const jit_value& value2); +jit_value operator/(const jit_value& value1, const jit_value& value2); +jit_value operator%(const jit_value& value1, const jit_value& value2); +jit_value operator-(const jit_value& value1); +jit_value operator&(const jit_value& value1, const jit_value& value2); +jit_value operator|(const jit_value& value1, const jit_value& value2); +jit_value operator^(const jit_value& value1, const jit_value& value2); +jit_value operator~(const jit_value& value1); +jit_value operator<<(const jit_value& value1, const jit_value& value2); +jit_value operator>>(const jit_value& value1, const jit_value& value2); +jit_value operator==(const jit_value& value1, const jit_value& value2); +jit_value operator!=(const jit_value& value1, const jit_value& value2); +jit_value operator<(const jit_value& value1, const jit_value& value2); +jit_value operator<=(const jit_value& value1, const jit_value& value2); +jit_value operator>(const jit_value& value1, const jit_value& value2); +jit_value operator>=(const jit_value& value1, const jit_value& value2); + +class jit_label +{ +public: + jit_label() { label = jit_label_undefined; } + jit_label(jit_label_t label) { this->label = label; } + jit_label(const jit_label& label) { this->label = label.label; } + ~jit_label() {} + + jit_label_t raw() const { return label; } + jit_label_t *rawp() { return &label; } + int is_valid() const { return (label != jit_label_undefined); } + + jit_label& operator=(const jit_label& value) + { this->label = value.label; return *this; } + +private: + jit_label_t label; +}; + +class jit_context +{ +public: + jit_context(); + jit_context(jit_context_t context); + ~jit_context(); + + jit_context_t raw() const { return context; } + +private: + jit_context_t context; + int copied; +}; + +class jit_function +{ +public: + jit_function(jit_context& context, jit_type_t signature); + jit_function(jit_context& context); + jit_function(jit_function_t func); + virtual ~jit_function(); + + jit_function_t raw() const { return func; } + int is_valid() const { return (func != 0); } + + static jit_function *from_raw(jit_function_t func); + + jit_type_t signature() const { return jit_function_get_signature(func); } + + void create(jit_type_t signature); + void create(); + + int compile(); + + int is_compiled() const { return jit_function_is_compiled(func); } + + int recompile(); + + int is_recompilable() const { return jit_function_is_recompilable(func); } + + void set_recompilable() { jit_function_set_recompilable(func); } + void clear_recompilable() { jit_function_set_recompilable(func); } + void set_recompilable(int flag) + { if(flag) set_recompilable(); else clear_recompilable(); } + + void set_optimization_level(unsigned int level) + { jit_function_set_optimization_level(func, level); } + unsigned int optimization_level() const + { return jit_function_get_optimization_level(func); } + static unsigned int max_optimization_level() + { return jit_function_get_max_optimization_level(); } + + void *closure() const { return jit_function_to_closure(func); } + void *vtable_pointer() const + { return jit_function_to_vtable_pointer(func); } + + int apply(void **args, void *result) + { return jit_function_apply(func, args, result); } + int apply(jit_type_t signature, void **args, void *return_area) + { return jit_function_apply_vararg + (func, signature, args, return_area); } + + static jit_type_t const end_params; + static jit_type_t signature_helper(jit_type_t return_type, ...); + +protected: + virtual void build(); + virtual jit_type_t create_signature(); + void fail(); + void out_of_memory(); + +public: + void build_start() + { jit_context_build_start(jit_function_get_context(func)); } + void build_end() + { jit_context_build_end(jit_function_get_context(func)); } + + jit_value new_value(jit_type_t type); + jit_value new_constant(jit_sbyte value, jit_type_t type=0); + jit_value new_constant(jit_ubyte value, jit_type_t type=0); + jit_value new_constant(jit_short value, jit_type_t type=0); + jit_value new_constant(jit_ushort value, jit_type_t type=0); + jit_value new_constant(jit_int value, jit_type_t type=0); + jit_value new_constant(jit_uint value, jit_type_t type=0); + jit_value new_constant(jit_long value, jit_type_t type=0); + jit_value new_constant(jit_ulong value, jit_type_t type=0); + jit_value new_constant(jit_float32 value, jit_type_t type=0); + jit_value new_constant(jit_float64 value, jit_type_t type=0); +#ifndef JIT_NFLOAT_IS_DOUBLE + jit_value new_constant(jit_nfloat value, jit_type_t type=0); +#endif + jit_value new_constant(void *value, jit_type_t type=0); + jit_value new_constant(const jit_constant_t& value); + jit_value get_param(unsigned int param); + jit_value get_struct_pointer(); + + void insn_label(jit_label& label); + jit_value insn_load(const jit_value& value); + jit_value insn_dup(const jit_value& value); + jit_value insn_load_small(const jit_value& value); + void store(const jit_value& dest, const jit_value& value); + jit_value insn_load_relative + (const jit_value& value, jit_nint offset, jit_type_t type); + void insn_store_relative + (const jit_value& dest, jit_nint offset, const jit_value& value); + jit_value insn_add_relative(const jit_value& value, jit_nint offset); + void insn_check_null(const jit_value& value); + jit_value insn_add(const jit_value& value1, const jit_value& value2); + jit_value insn_add_ovf(const jit_value& value1, const jit_value& value2); + jit_value insn_sub(const jit_value& value1, const jit_value& value2); + jit_value insn_sub_ovf(const jit_value& value1, const jit_value& value2); + jit_value insn_mul(const jit_value& value1, const jit_value& value2); + jit_value insn_mul_ovf(const jit_value& value1, const jit_value& value2); + jit_value insn_div(const jit_value& value1, const jit_value& value2); + jit_value insn_rem(const jit_value& value1, const jit_value& value2); + jit_value insn_rem_ieee(const jit_value& value1, const jit_value& value2); + jit_value insn_neg(const jit_value& value1); + jit_value insn_and(const jit_value& value1, const jit_value& value2); + jit_value insn_or(const jit_value& value1, const jit_value& value2); + jit_value insn_xor(const jit_value& value1, const jit_value& value2); + jit_value insn_not(const jit_value& value1); + jit_value insn_shl(const jit_value& value1, const jit_value& value2); + jit_value insn_shr(const jit_value& value1, const jit_value& value2); + jit_value insn_ushr(const jit_value& value1, const jit_value& value2); + jit_value insn_sshr(const jit_value& value1, const jit_value& value2); + jit_value insn_eq(const jit_value& value1, const jit_value& value2); + jit_value insn_ne(const jit_value& value1, const jit_value& value2); + jit_value insn_lt(const jit_value& value1, const jit_value& value2); + jit_value insn_le(const jit_value& value1, const jit_value& value2); + jit_value insn_gt(const jit_value& value1, const jit_value& value2); + jit_value insn_ge(const jit_value& value1, const jit_value& value2); + jit_value insn_cmpl(const jit_value& value1, const jit_value& value2); + jit_value insn_cmpg(const jit_value& value1, const jit_value& value2); + jit_value insn_to_bool(const jit_value& value1); + jit_value insn_to_not_bool(const jit_value& value1); + jit_value insn_acos(const jit_value& value1); + jit_value insn_asin(const jit_value& value1); + jit_value insn_atan(const jit_value& value1); + jit_value insn_atan2(const jit_value& value1, const jit_value& value2); + jit_value insn_ceil(const jit_value& value1); + jit_value insn_cos(const jit_value& value1); + jit_value insn_cosh(const jit_value& value1); + jit_value insn_exp(const jit_value& value1); + jit_value insn_floor(const jit_value& value1); + jit_value insn_log(const jit_value& value1); + jit_value insn_log10(const jit_value& value1); + jit_value insn_pow(const jit_value& value1, const jit_value& value2); + jit_value insn_rint(const jit_value& value1); + jit_value insn_round(const jit_value& value1); + jit_value insn_sin(const jit_value& value1); + jit_value insn_sinh(const jit_value& value1); + jit_value insn_sqrt(const jit_value& value1); + jit_value insn_tan(const jit_value& value1); + jit_value insn_tanh(const jit_value& value1); + jit_value insn_is_nan(const jit_value& value1); + jit_value insn_is_finite(const jit_value& value1); + jit_value insn_is_inf(const jit_value& value1); + jit_value insn_abs(const jit_value& value1); + jit_value insn_min(const jit_value& value1, const jit_value& value2); + jit_value insn_max(const jit_value& value1, const jit_value& value2); + jit_value insn_sign(const jit_value& value1); + void insn_branch(jit_label& label); + void insn_branch_if(const jit_value& value, jit_label& label); + void insn_branch_if_not(const jit_value& value, jit_label& label); + jit_value insn_address_of(const jit_value& value1); + jit_value insn_convert + (const jit_value& value, jit_type_t type, int overflow_check=0); + jit_value insn_call + (const char *name, jit_function_t jit_func, + jit_type_t signature, jit_value_t *args, unsigned int num_args, + int flags=0); + jit_value insn_call_indirect + (const jit_value& value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags=0); + jit_value insn_call_indirect_vtable + (const jit_value& value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags=0); + jit_value insn_call_native + (const char *name, void *native_func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags=0); + jit_value insn_call_intrinsic + (const char *name, void *intrinsic_func, + const jit_intrinsic_descr_t& descriptor, + const jit_value& arg1, const jit_value& arg2); + jit_value insn_import(jit_value value); + void insn_return(const jit_value& value); + void insn_return(); + void insn_default_return(); + void insn_throw(const jit_value& value); + jit_value insn_get_call_stack(); + +private: + jit_function_t func; + jit_context_t context; + + void register_on_demand(); + static int on_demand_compiler(jit_function_t func); + static void free_mapping(void *data); +}; + +#endif /* __cplusplus */ + +#endif /* _JIT_PLUS_H */ diff --git a/include/jit/jit-type.h b/include/jit/jit-type.h new file mode 100644 index 0000000..324f14c --- /dev/null +++ b/include/jit/jit-type.h @@ -0,0 +1,136 @@ +/* + * jit-type.h - Functions for manipulating type descriptors. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_TYPE_H +#define _JIT_TYPE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Pre-defined type descriptors. + */ +extern jit_type_t const jit_type_void; +extern jit_type_t const jit_type_sbyte; +extern jit_type_t const jit_type_ubyte; +extern jit_type_t const jit_type_short; +extern jit_type_t const jit_type_ushort; +extern jit_type_t const jit_type_int; +extern jit_type_t const jit_type_uint; +extern jit_type_t const jit_type_nint; +extern jit_type_t const jit_type_nuint; +extern jit_type_t const jit_type_long; +extern jit_type_t const jit_type_ulong; +extern jit_type_t const jit_type_float32; +extern jit_type_t const jit_type_float64; +extern jit_type_t const jit_type_nfloat; +extern jit_type_t const jit_type_void_ptr; + +/* + * Type descriptors for the system "char", "int", "long", etc types. + * These are defined to one of the above values. + */ +extern jit_type_t const jit_type_sys_char; +extern jit_type_t const jit_type_sys_schar; +extern jit_type_t const jit_type_sys_uchar; +extern jit_type_t const jit_type_sys_short; +extern jit_type_t const jit_type_sys_ushort; +extern jit_type_t const jit_type_sys_int; +extern jit_type_t const jit_type_sys_uint; +extern jit_type_t const jit_type_sys_long; +extern jit_type_t const jit_type_sys_ulong; +extern jit_type_t const jit_type_sys_longlong; +extern jit_type_t const jit_type_sys_ulonglong; +extern jit_type_t const jit_type_sys_float; +extern jit_type_t const jit_type_sys_double; +extern jit_type_t const jit_type_sys_long_double; + +/* + * ABI types for function signatures. + */ +typedef enum +{ + jit_abi_cdecl, /* Native C calling conventions */ + jit_abi_vararg, /* Native C with optional variable arguments */ + jit_abi_stdcall, /* Win32 STDCALL (same as cdecl if not Win32) */ + jit_abi_fastcall /* Win32 FASTCALL (same as cdecl if not Win32) */ + +} jit_abi_t; + +/* + * External function declarations. + */ +jit_type_t jit_type_copy(jit_type_t type); +void jit_type_free(jit_type_t type); +jit_type_t jit_type_create_struct(jit_type_t *fields, unsigned int num_fields, + int incref); +jit_type_t jit_type_create_union(jit_type_t *fields, unsigned int num_fields, + int incref); +jit_type_t jit_type_create_signature(jit_abi_t abi, jit_type_t return_type, + jit_type_t *params, + unsigned int num_params, int incref); +jit_type_t jit_type_create_pointer(jit_type_t type, int incref); +jit_type_t jit_type_create_tagged(jit_type_t type, int kind, void *data, + jit_meta_free_func free_func, int incref); +int jit_type_set_names(jit_type_t type, char **names, unsigned int num_names); +void jit_type_set_size_and_alignment(jit_type_t type, jit_nint size, + jit_nint alignment); +void jit_type_set_offset(jit_type_t type, unsigned int field_index, + jit_nuint offset); +jit_nuint jit_type_get_size(jit_type_t type); +jit_nuint jit_type_get_alignment(jit_type_t type); +unsigned int jit_type_num_fields(jit_type_t type); +jit_type_t jit_type_get_field(jit_type_t type, unsigned int field_index); +jit_nuint jit_type_get_offset(jit_type_t type, unsigned int field_index); +const char *jit_type_get_name(jit_type_t type, unsigned int index); +#define JIT_INVALID_NAME (~((unsigned int)0)) +unsigned int jit_type_find_name(jit_type_t type, const char *name); +unsigned int jit_type_num_params(jit_type_t type); +jit_type_t jit_type_get_return(jit_type_t type); +jit_type_t jit_type_get_param(jit_type_t type, unsigned int param_index); +jit_abi_t jit_type_get_abi(jit_type_t type); +jit_type_t jit_type_get_ref(jit_type_t type); +jit_type_t jit_type_get_tagged_type(jit_type_t type); +void jit_type_set_tagged_type(jit_type_t type, jit_type_t underlying, + int incref); +int jit_type_get_tagged_kind(jit_type_t type); +void *jit_type_get_tagged_data(jit_type_t type); +void jit_type_set_tagged_data(jit_type_t type, void *data, + jit_meta_free_func free_func); +int jit_type_is_primitive(jit_type_t type); +int jit_type_is_struct(jit_type_t type); +int jit_type_is_union(jit_type_t type); +int jit_type_is_signature(jit_type_t type); +int jit_type_is_pointer(jit_type_t type); +int jit_type_is_tagged(jit_type_t type); +jit_nuint jit_type_best_alignment(void); +jit_type_t jit_type_normalize(jit_type_t type); +jit_type_t jit_type_remove_tags(jit_type_t type); +jit_type_t jit_type_promote_int(jit_type_t type); +int jit_type_return_via_pointer(jit_type_t type); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_TYPE_H */ diff --git a/include/jit/jit-util.h b/include/jit/jit-util.h new file mode 100644 index 0000000..ea9a056 --- /dev/null +++ b/include/jit/jit-util.h @@ -0,0 +1,90 @@ +/* + * jit-util.h - Utility functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_UTIL_H +#define _JIT_UTIL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Memory allocation routines. + */ +void *jit_malloc(unsigned int size); +void *jit_calloc(unsigned int num, unsigned int size); +void *jit_realloc(void *ptr, unsigned int size); +void jit_free(void *ptr); +void *jit_malloc_exec(unsigned int size); +void jit_free_exec(void *ptr, unsigned int size); +void jit_flush_exec(void *ptr, unsigned int size); +unsigned int jit_exec_page_size(void); +#define jit_new(type) ((type *)jit_malloc(sizeof(type))) +#define jit_cnew(type) ((type *)jit_calloc(1, sizeof(type))) + +/* + * Memory set/copy/compare routines. + */ +void *jit_memset(void *dest, int ch, unsigned int len); +void *jit_memcpy(void *dest, const void *src, unsigned int len); +void *jit_memmove(void *dest, const void *src, unsigned int len); +int jit_memcmp(const void *s1, const void *s2, unsigned int len); +void *jit_memchr(const void *str, int ch, unsigned int len); + +/* + * String routines. Note: jit_stricmp uses fixed ASCII rules for case + * comparison, whereas jit_stricoll uses localized rules. + */ +unsigned int jit_strlen(const char *str); +char *jit_strcpy(char *dest, const char *src); +char *jit_strcat(char *dest, const char *src); +char *jit_strncpy(char *dest, const char *src, unsigned int len); +char *jit_strdup(const char *str); +char *jit_strndup(const char *str, unsigned int len); +int jit_strcmp(const char *str1, const char *str2); +int jit_strncmp(const char *str1, const char *str2, unsigned int len); +int jit_stricmp(const char *str1, const char *str2); +int jit_strnicmp(const char *str1, const char *str2, unsigned int len); +int jit_strcoll(const char *str1, const char *str2); +int jit_strncoll(const char *str1, const char *str2, unsigned int len); +int jit_stricoll(const char *str1, const char *str2); +int jit_strnicoll(const char *str1, const char *str2, unsigned int len); +char *jit_strchr(const char *str, int ch); +char *jit_strrchr(const char *str, int ch); +int jit_sprintf(char *str, const char *format, ...); +int jit_snprintf(char *str, unsigned int len, const char *format, ...); + +/* + * Dynamic library routines. + */ +typedef void *jit_dynlib_handle_t; +jit_dynlib_handle_t jit_dynlib_open(const char *name); +void jit_dynlib_close(jit_dynlib_handle_t handle); +void *jit_dynlib_get_symbol(jit_dynlib_handle_t handle, const char *symbol); +const char *jit_dynlib_get_suffix(void); +void jit_dynlib_set_debug(int flag); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_UTIL_H */ diff --git a/include/jit/jit-value.h b/include/jit/jit-value.h new file mode 100644 index 0000000..c72f0f2 --- /dev/null +++ b/include/jit/jit-value.h @@ -0,0 +1,101 @@ +/* + * jit-value.h - Functions for manipulating temporary values. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_VALUE_H +#define _JIT_VALUE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Full struction that can hold a constant of any type. + */ +typedef struct +{ + jit_type_t type; + union + { + void *ptr_value; + jit_int int_value; + jit_uint uint_value; + jit_nint nint_value; + jit_nuint nuint_value; + jit_long long_value; + jit_ulong ulong_value; + jit_float32 float32_value; + jit_float64 float64_value; + jit_nfloat nfloat_value; + + } un; + +} jit_constant_t; + +/* + * External function declarations. + */ +jit_value_t jit_value_create(jit_function_t func, jit_type_t type) JIT_NOTHROW; +jit_value_t jit_value_create_nint_constant + (jit_function_t func, jit_type_t type, jit_nint const_value) JIT_NOTHROW; +jit_value_t jit_value_create_long_constant + (jit_function_t func, jit_type_t type, jit_long const_value) JIT_NOTHROW; +jit_value_t jit_value_create_float32_constant + (jit_function_t func, jit_type_t type, + jit_float32 const_value) JIT_NOTHROW; +jit_value_t jit_value_create_float64_constant + (jit_function_t func, jit_type_t type, + jit_float64 const_value) JIT_NOTHROW; +jit_value_t jit_value_create_nfloat_constant + (jit_function_t func, jit_type_t type, + jit_nfloat const_value) JIT_NOTHROW; +jit_value_t jit_value_create_constant + (jit_function_t func, const jit_constant_t *const_value) JIT_NOTHROW; +jit_value_t jit_value_get_param + (jit_function_t func, unsigned int param) JIT_NOTHROW; +jit_value_t jit_value_get_struct_pointer(jit_function_t func) JIT_NOTHROW; +int jit_value_is_temporary(jit_value_t value) JIT_NOTHROW; +int jit_value_is_local(jit_value_t value) JIT_NOTHROW; +int jit_value_is_constant(jit_value_t value) JIT_NOTHROW; +void jit_value_ref(jit_function_t func, jit_value_t value) JIT_NOTHROW; +void jit_value_set_volatile(jit_value_t value) JIT_NOTHROW; +int jit_value_is_volatile(jit_value_t value) JIT_NOTHROW; +void jit_value_set_addressable(jit_value_t value) JIT_NOTHROW; +int jit_value_is_addressable(jit_value_t value) JIT_NOTHROW; +jit_type_t jit_value_get_type(jit_value_t value) JIT_NOTHROW; +jit_function_t jit_value_get_function(jit_value_t value) JIT_NOTHROW; +jit_block_t jit_value_get_block(jit_value_t value) JIT_NOTHROW; +jit_context_t jit_value_get_context(jit_value_t value) JIT_NOTHROW; +jit_constant_t jit_value_get_constant(jit_value_t value) JIT_NOTHROW; +jit_nint jit_value_get_nint_constant(jit_value_t value) JIT_NOTHROW; +jit_long jit_value_get_long_constant(jit_value_t value) JIT_NOTHROW; +jit_float32 jit_value_get_float32_constant(jit_value_t value) JIT_NOTHROW; +jit_float64 jit_value_get_float64_constant(jit_value_t value) JIT_NOTHROW; +jit_nfloat jit_value_get_nfloat_constant(jit_value_t value) JIT_NOTHROW; +int jit_constant_convert + (jit_constant_t *result, const jit_constant_t *value, + jit_type_t type, int overflow_check) JIT_NOTHROW; + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_VALUE_H */ diff --git a/include/jit/jit-walk.h b/include/jit/jit-walk.h new file mode 100644 index 0000000..0c2df9c --- /dev/null +++ b/include/jit/jit-walk.h @@ -0,0 +1,98 @@ +/* + * jit-walk.h - Functions for walking stack frames. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_WALK_H +#define _JIT_WALK_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Get the frame address for a frame which is "n" levels up the stack. + * A level value of zero indicates the current frame. + */ +void *_jit_get_frame_address(void *start, unsigned int n); +#if defined(__GNUC__) +#define jit_get_frame_address(n) \ + (_jit_get_frame_address(__builtin_frame_address(0), (n))) +#else +#define jit_get_frame_address(n) (_jit_get_frame_address(0, (n))) +#endif + +/* + * Get the frame address for the current frame. May be more efficient + * than using "jit_get_frame_address(0)". + */ +#if defined(__GNUC__) +#define jit_get_current_frame() (__builtin_frame_address(0)) +#else +#define jit_get_current_frame() (jit_get_frame_address(0)) +#endif + +/* + * Get the next frame up the stack from a specified frame. + * Returns NULL if it isn't possible to retrieve the next frame. + */ +void *jit_get_next_frame_address(void *frame); + +/* + * Get the return address for a specific frame. + */ +void *_jit_get_return_address(void *frame, void *frame0, void *return0); +#if defined(__GNUC__) +#define jit_get_return_address(frame) \ + (_jit_get_return_address \ + ((frame), __builtin_frame_address(0), __builtin_return_address(0))) +#else +#define jit_get_return_address(frame) \ + (_jit_get_return_address((frame), 0, 0)) +#endif + +/* + * Get the return address for the current frame. May be more efficient + * than using "jit_get_return_address(0)". + */ +#if defined(__GNUC__) +#define jit_get_current_return() (__builtin_return_address(0)) +#else +#define jit_get_current_return() \ + (jit_get_return_address(jit_get_current_frame())) +#endif + +/* + * Declare a stack crawl mark variable. The address of this variable + * can be passed to "jit_frame_contains_crawl_mark" to determine + * if a frame contains the mark. + */ +typedef struct { void * volatile mark; } jit_crawl_mark_t; +#define jit_declare_crawl_mark(name) jit_crawl_mark_t name = {0} + +/* + * Determine if the stack frame just above "frame" contains a + * particular crawl mark. + */ +int jit_frame_contains_crawl_mark(void *frame, jit_crawl_mark_t *mark); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_WALK_H */ diff --git a/include/jit/jit.h b/include/jit/jit.h new file mode 100644 index 0000000..60c3769 --- /dev/null +++ b/include/jit/jit.h @@ -0,0 +1,50 @@ +/* + * jit.h - General definitions for JIT back-ends. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_H +#define _JIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_H */ diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# 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 M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/jit/.cvsignore b/jit/.cvsignore new file mode 100644 index 0000000..3bfe1a5 --- /dev/null +++ b/jit/.cvsignore @@ -0,0 +1,4 @@ +Makefile +Makefile.in +.deps +jit-apply-rules.h diff --git a/jit/Makefile.am b/jit/Makefile.am new file mode 100644 index 0000000..1002134 --- /dev/null +++ b/jit/Makefile.am @@ -0,0 +1,52 @@ + +lib_LIBRARIES = libjit.a + +libjit_a_SOURCES = \ + jit-alloc.c \ + jit-apply.c \ + jit-apply-func.h \ + jit-apply-arm.h \ + jit-apply-arm.c \ + jit-apply-x86.h \ + jit-apply-x86.c \ + jit-block.c \ + jit-cache.c \ + jit-context.c \ + jit-dump.c \ + jit-dynlib.c \ + jit-elf-defs.h \ + jit-elf-read.c \ + jit-elf-write.c \ + jit-except.cpp \ + jit-function.c \ + jit-gen-arm.h \ + jit-gen-arm.c \ + jit-gen-x86.h \ + jit-insn.c \ + jit-init.c \ + jit-internal.h \ + jit-interp.cpp \ + jit-intrinsic.c \ + jit-live.c \ + jit-memory.c \ + jit-memory.h \ + jit-meta.c \ + jit-opcode.c \ + jit-pool.c \ + jit-reg-alloc.h \ + jit-reg-alloc.c \ + jit-rules.h \ + jit-rules.c \ + jit-rules-interp.c \ + jit-rules-arm.h \ + jit-rules-arm.c \ + jit-rules-x86.h \ + jit-rules-x86.c \ + jit-string.c \ + jit-thread.c \ + jit-type.c \ + jit-value.c \ + jit-walk.c + +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir) +AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir) diff --git a/jit/jit-alloc.c b/jit/jit-alloc.c new file mode 100644 index 0000000..730944b --- /dev/null +++ b/jit/jit-alloc.c @@ -0,0 +1,255 @@ +/* + * jit-alloc.c - Memory allocation routines. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include +#ifdef HAVE_STDLIB_H + #include +#endif +#ifndef JIT_WIN32_PLATFORM +#ifdef HAVE_SYS_TYPES_H + #include +#endif +#ifdef HAVE_UNISTD_H + #include +#endif +#ifdef HAVE_SYS_MMAN_H + #include +#endif +#ifdef HAVE_FCNTL_H + #include +#endif +#else /* JIT_WIN32_PLATFORM */ + #include + #include +#endif + +/*@ + * @section Memory allocation + * + * The @code{libjit} library provides an interface to the traditional + * system @code{malloc} routines. All heap allocation in @code{libjit} + * goes through these functions. If you need to perform some other kind + * of memory allocation, you can replace these functions with your + * own versions. +@*/ + +/*@ + * @deftypefun {void *} jit_malloc ({unsigned int} size) + * Allocate @code{size} bytes of memory from the heap. + * @end deftypefun + * + * @deftypefun {type *} jit_new (type) + * Allocate @code{sizeof(type)} bytes of memory from the heap and + * cast the return pointer to @code{type *}. This is a macro that + * wraps up the underlying @code{jit_malloc} function and is less + * error-prone when allocating structures. + * @end deftypefun +@*/ +void *jit_malloc(unsigned int size) +{ + return malloc(size); +} + +/*@ + * @deftypefun {void *} jit_calloc ({unsigned int} num, {unsigned int} size) + * Allocate @code{num * size} bytes of memory from the heap and clear + * them to zero. + * @end deftypefun + * + * @deftypefun {type *} jit_cnew (type) + * Allocate @code{sizeof(type)} bytes of memory from the heap and + * cast the return pointer to @code{type *}. The memory is cleared + * to zero. + * @end deftypefun +@*/ +void *jit_calloc(unsigned int num, unsigned int size) +{ + return calloc(num, size); +} + +/*@ + * @deftypefun {void *} jit_realloc ({void *} ptr, {unsigned int} size) + * Re-allocate the memory at @code{ptr} to be @code{size} bytes in size. + * The memory block at @code{ptr} must have been allocated by a previous + * call to @code{jit_malloc}, @code{jit_calloc}, or @code{jit_realloc}. + * @end deftypefun +@*/ +void *jit_realloc(void *ptr, unsigned int size) +{ + return realloc(ptr, size); +} + +/*@ + * @deftypefun void jit_free ({void *} ptr) + * Free the memory at @code{ptr}. It is safe to pass a NULL pointer. + * @end deftypefun +@*/ +void jit_free(void *ptr) +{ + if(ptr) + { + free(ptr); + } +} + +/*@ + * @deftypefun {void *} jit_malloc_exec ({unsigned int} size) + * Allocate a block of memory that is read/write/executable. Such blocks + * are used to store JIT'ed code, function closures, and other trampolines. + * The size should be a multiple of @code{jit_exec_page_size()}. + * + * This will usually be identical to @code{jit_malloc}. However, + * some systems may need special handling to create executable code + * segments, so this function must be used instead. + * + * You must never mix regular and executable segment allocation. That is, + * do not use @code{jit_free} to free the result of @code{jit_malloc_exec}. + * @end deftypefun +@*/ +void *jit_malloc_exec(unsigned int size) +{ + return malloc(size); +} + +/*@ + * @deftypefun void jit_free_exec ({void *} ptr, {unsigned int} size) + * Free a block of memory that was previously allocated by + * @code{jit_malloc_exec}. The @code{size} must be identical to the + * original allocated size, as some systems need to know this information + * to be able to free the block. + * @end deftypefun +@*/ +void jit_free_exec(void *ptr, unsigned int size) +{ + if(ptr) + { + free(ptr); + } +} + +/*@ + * @deftypefun void jit_flush_exec ({void *} ptr, {unsigned int} size) + * Flush the contents of the block at @code{ptr} from the CPU's + * data and instruction caches. This must be used after the code is + * written to an executable code segment, but before the code is + * executed, to prepare it for execution. + * @end deftypefun +@*/ +void jit_flush_exec(void *ptr, unsigned int size) +{ +#if defined(__GNUC__) +#if defined(PPC) + + /* Flush the CPU cache on PPC platforms */ + register unsigned char *p; + + /* Flush the data out of the data cache */ + p = (unsigned char *)ptr; + while(size > 0) + { + __asm__ __volatile__ ("dcbst 0,%0" :: "r"(p)); + p += 4; + size -= 4; + } + __asm__ __volatile__ ("sync"); + + /* Invalidate the cache lines in the instruction cache */ + p = (unsigned char *)ptr; + while(size > 0) + { + __asm__ __volatile__ ("icbi 0,%0; isync" :: "r"(p)); + p += 4; + size -= 4; + } + __asm__ __volatile__ ("isync"); + +#elif defined(__sparc) + + /* Flush the CPU cache on sparc platforms */ + register unsigned char *p = (unsigned char *)ptr; + __asm__ __volatile__ ("stbar"); + while(size > 0) + { + __asm__ __volatile__ ("flush %0" :: "r"(p)); + p += 4; + size -= 4; + } + __asm__ __volatile__ ("nop; nop; nop; nop; nop"); + +#elif (defined(__arm__) || defined(__arm)) && defined(linux) + + /* ARM Linux has a "cacheflush" system call */ + /* R0 = start of range, R1 = end of range, R3 = flags */ + /* flags = 0 indicates data cache, flags = 1 indicates both caches */ + __asm __volatile ("mov r0, %0\n" + "mov r1, %1\n" + "mov r2, %2\n" + "swi 0x9f0002 @ sys_cacheflush" + : /* no outputs */ + : "r" (ptr), + "r" (((int)ptr) + (int)size), + "r" (0) + : "r0", "r1", "r3" ); + +#elif (defined(__ia64) || defined(__ia64__)) && defined(linux) + void *end = (char*)ptr + size; + while(ptr < end) + { + asm volatile("fc %0" :: "r"(ptr)); + ptr = (char*)ptr + 32; + } + asm volatile(";;sync.i;;srlz.i;;"); +#endif +#endif /* __GNUC__ */ +} + +/*@ + * @deftypefun {unsigned int} jit_exec_page_size (void) + * Get the page allocation size for the system. This is the preferred + * unit when making calls to @code{jit_malloc_exec}. It is not + * required that you supply a multiple of this size when allocating, + * but it can lead to better performance on some systems. + * @end deftypefun +@*/ +unsigned int jit_exec_page_size(void) +{ +#ifndef JIT_WIN32_PLATFORM + /* Get the page size using a Unix-like sequence */ + #ifdef HAVE_GETPAGESIZE + return (unsigned long)getpagesize(); + #else + #ifdef NBPG + return NBPG; + #else + #ifdef PAGE_SIZE + return PAGE_SIZE; + #else + return 4096; + #endif + #endif + #endif +#else + /* Get the page size from a Windows-specific API */ + SYSTEM_INFO sysInfo; + GetSystemInfo(&sysInfo); + return (unsigned long)(sysInfo.dwPageSize); +#endif +} diff --git a/jit/jit-apply-arm.c b/jit/jit-apply-arm.c new file mode 100644 index 0000000..dd16e23 --- /dev/null +++ b/jit/jit-apply-arm.c @@ -0,0 +1,95 @@ +/* + * jit-apply-arm.c - Apply support routines for ARM. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" + +#if defined(__arm) || defined(__arm__) + +#include "jit-gen-arm.h" + +void _jit_create_closure(unsigned char *buf, void *func, + void *closure, void *_type) +{ + arm_inst_ptr inst = (arm_inst_ptr)buf; + + /* Set up the local stack frame */ + arm_setup_frame(inst, 0); + arm_alu_reg_imm8(inst, ARM_SUB, ARM_SP, ARM_SP, 24); + + /* Create the apply argument block on the stack */ + arm_store_membase(inst, ARM_R0, ARM_FP, -28); + arm_store_membase(inst, ARM_R1, ARM_FP, -24); + arm_store_membase(inst, ARM_R2, ARM_FP, -20); + arm_store_membase(inst, ARM_R3, ARM_FP, -16); + arm_alu_reg_imm(inst, ARM_ADD, ARM_R3, ARM_FP, 4); + arm_store_membase(inst, ARM_R3, ARM_FP, -36); + arm_store_membase(inst, ARM_R0, ARM_FP, -32); + + /* Set up the arguments for calling "func" */ + arm_mov_reg_imm(inst, ARM_R0, (int)(jit_nint)closure); + arm_mov_reg_reg(inst, ARM_R1, ARM_SP); + + /* Call the closure handling function */ + arm_call(inst, func); + + /* Pop the current stack frame and return */ + arm_pop_frame(inst, 0); +} + +void *_jit_create_redirector(unsigned char *buf, void *func, + void *user_data, int abi) +{ + arm_inst_ptr inst; + + /* Align "buf" on an appropriate boundary */ + if((((jit_nint)buf) % jit_closure_align) != 0) + { + buf += jit_closure_align - (((jit_nint)buf) % jit_closure_align); + } + + /* Set up the instruction output pointer */ + inst = (arm_inst_ptr)buf; + + /* Set up the local stack frame, and save R0-R3 */ + arm_setup_frame(inst, 0x000F); + + /* Set up the arguments for calling "func" */ + arm_mov_reg_imm(inst, ARM_R0, (int)(jit_nint)user_data); + + /* Call the redirector handling function */ + arm_call(inst, func); + + /* Shift the result into R12, because we are about to restore R0 */ + arm_mov_reg_reg(inst, ARM_R12, ARM_R0); + + /* Pop the current stack frame, but don't change PC yet */ + arm_pop_frame_tail(inst, 0x000F); + + /* Jump to the function that the redirector indicated */ + arm_mov_reg_reg(inst, ARM_PC, ARM_R12); + + /* Flush the cache lines that we just wrote */ + jit_flush_exec(buf, ((unsigned char *)inst) - buf); + + /* Return the aligned start of the buffer as the entry point */ + return (void *)buf; +} + +#endif /* arm */ diff --git a/jit/jit-apply-arm.h b/jit/jit-apply-arm.h new file mode 100644 index 0000000..ce0fbc4 --- /dev/null +++ b/jit/jit-apply-arm.h @@ -0,0 +1,37 @@ +/* + * jit-apply-arm.h - Special definitions for ARM function application. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_APPLY_ARM_H +#define _JIT_APPLY_ARM_H + +/* + * The maximum number of bytes that are needed to represent a closure, + * and the alignment to use for the closure. + */ +#define jit_closure_size 128 +#define jit_closure_align 16 + +/* + * The number of bytes that are needed for a redirector stub. + * This includes any extra bytes that are needed for alignment. + */ +#define jit_redirector_size 128 + +#endif /* _JIT_APPLY_ARM_H */ diff --git a/jit/jit-apply-func.h b/jit/jit-apply-func.h new file mode 100644 index 0000000..54424d5 --- /dev/null +++ b/jit/jit-apply-func.h @@ -0,0 +1,83 @@ +/* + * jit-apply-func.h - Definition of "__builtin_apply" and friends. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_APPLY_FUNC_H +#define _JIT_APPLY_FUNC_H + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) + +#include "jit-apply-x86.h" + +#elif defined(__arm) || defined(__arm__) + +#include "jit-apply-arm.h" + +#endif + +#if !defined(jit_builtin_apply) + +#define jit_builtin_apply(func,args,size,return_float,return_buf) \ + do { \ + (return_buf) = __builtin_apply \ + ((void (*)())(func), (args), (size)); \ + } while (0) + +#define jit_builtin_apply_args(type,args) \ + do { \ + (args) = (type)__builtin_apply_args(); \ + } while (0) + +#define jit_builtin_return_int(return_buf) \ + do { \ + __builtin_return((return_buf)); \ + } while (0) + +#define jit_builtin_return_float(return_buf) \ + do { \ + __builtin_return((return_buf)); \ + } while (0) + +#endif + +/* + * Create a closure for the underlying platform in the given buffer. + * The closure must arrange to call "func" with two arguments: + * "closure" and a pointer to an apply structure. + */ +void _jit_create_closure(unsigned char *buf, void *func, + void *closure, void *type); + +/* + * Create a redirector stub for the underlying platform in the given buffer. + * The redirector arranges to call "func" with the "user_data" argument. + * It is assumed that "func" returns a pointer to the actual function. + * Returns a pointer to the position in "buf" where the redirector starts, + * which may be different than "buf" if alignment occurred. + */ +void *_jit_create_redirector(unsigned char *buf, void *func, + void *user_data, int abi); + +/* + * Pad a buffer with NOP instructions. Used to align code. + * This will only be called if "jit_should_pad" is defined. + */ +void _jit_pad_buffer(unsigned char *buf, int len); + +#endif /* _JIT_APPLY_FUNC_H */ diff --git a/jit/jit-apply-x86.c b/jit/jit-apply-x86.c new file mode 100644 index 0000000..04d20c8 --- /dev/null +++ b/jit/jit-apply-x86.c @@ -0,0 +1,244 @@ +/* + * jit-apply-x86.c - Apply support routines for x86. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-apply-rules.h" +#include "jit-apply-func.h" + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) + +#include "jit-gen-x86.h" + +void _jit_create_closure(unsigned char *buf, void *func, + void *closure, void *_type) +{ + jit_type_t signature = (jit_type_t)_type; + jit_type_t type; +#if JIT_APPLY_X86_FASTCALL == 1 + jit_abi_t abi = jit_type_get_abi(signature); +#endif + unsigned int num_bytes = 0; + int struct_return_offset = 0; + + /* Set up the local stack frame */ + x86_push_reg(buf, X86_EBP); + x86_mov_reg_reg(buf, X86_EBP, X86_ESP, 4); + + /* Create the apply argument block on the stack */ +#if JIT_APPLY_X86_FASTCALL == 1 + if(abi == jit_abi_fastcall) + { + x86_push_reg(buf, X86_EDX); + x86_push_reg(buf, X86_ECX); + } +#endif + x86_lea_membase(buf, X86_EAX, X86_EBP, 8); + x86_push_reg(buf, X86_EAX); + + /* Push the arguments for calling "func" */ + x86_mov_reg_reg(buf, X86_EAX, X86_ESP, 4); + x86_push_reg(buf, X86_EAX); + x86_push_imm(buf, (int)closure); + + /* Call the closure handling function */ + x86_call_code(buf, func); + + /* Determine the number of bytes to pop when we return */ +#if JIT_APPLY_X86_FASTCALL == 1 + if(abi == jit_abi_stdcall || abi == jit_abi_fastcall) + { + unsigned int word_regs; + unsigned int size; + unsigned int num_params; + unsigned int param; + if(abi == jit_abi_stdcall) + { + word_regs = 0; + } + else + { + word_regs = 2; + } + type = jit_type_normalize(jit_type_get_return(signature)); + if(jit_type_return_via_pointer(type)) + { + if(word_regs > 0) + { + --word_regs; + } + else + { + num_bytes += sizeof(void *); + struct_return_offset = 2 * sizeof(void *); + } + } + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + type = jit_type_normalize(jit_type_get_param(signature, param)); + size = jit_type_get_size(type); + if(word_regs > 0) + { + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + case JIT_TYPE_SIGNATURE: + case JIT_TYPE_PTR: + { + --word_regs; + } + continue; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + if(word_regs == 1) + { + num_bytes += sizeof(void *); + } + word_regs = 0; + } + continue; + } + word_regs = 0; + } + num_bytes += (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1); + } + } + else +#endif + { + type = jit_type_normalize(jit_type_get_return(signature)); + if(jit_type_return_via_pointer(type)) + { +#if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 + /* Pop the structure return pointer as we return back */ + num_bytes += sizeof(void *); +#endif + struct_return_offset = 2 * sizeof(void *); + } + } + + /* If we are returning a structure via a pointer, then load + the address of the structure into the EAX register */ + if(struct_return_offset != 0) + { + x86_mov_reg_membase(buf, X86_EAX, X86_EBP, struct_return_offset, 4); + } + + /* Pop the current stack frame */ + x86_mov_reg_reg(buf, X86_ESP, X86_EBP, 4); + x86_pop_reg(buf, X86_EBP); + + /* Return from the closure */ + if(num_bytes > 0) + { + x86_ret_imm(buf, num_bytes); + } + else + { + x86_ret(buf); + } +} + +void *_jit_create_redirector(unsigned char *buf, void *func, + void *user_data, int abi) +{ + void *start = (void *)buf; + + /* Set up a new stack frame */ + x86_push_reg(buf, X86_EBP); + x86_mov_reg_reg(buf, X86_EBP, X86_ESP, 4); + + /* Save the fastcall registers, if necessary */ +#if JIT_APPLY_X86_FASTCALL == 1 + if(abi == (int)jit_abi_fastcall) + { + x86_push_reg(buf, X86_EDX); + x86_push_reg(buf, X86_ECX); + } +#endif + + /* Push the user data onto the stack */ + x86_push_imm(buf, (int)user_data); + + /* Call "func" (the pointer result will be in EAX) */ + x86_call_code(buf, func); + + /* Remove the user data from the stack */ + x86_pop_reg(buf, X86_ECX); + + /* Restore the fastcall registers, if necessary */ +#if JIT_APPLY_X86_FASTCALL == 1 + if(abi == (int)jit_abi_fastcall) + { + x86_pop_reg(buf, X86_ECX); + x86_pop_reg(buf, X86_EDX); + } +#endif + + /* Restore the value of EBP */ + x86_pop_reg(buf, X86_EBP); + + /* Jump to the function that the redirector indicated */ + x86_jump_reg(buf, X86_EAX); + + /* Return the start of the buffer as the redirector entry point */ + return start; +} + +void _jit_pad_buffer(unsigned char *buf, int len) +{ + while(len >= 6) + { + /* "leal 0(%esi), %esi" with 32-bit displacement */ + *buf++ = (unsigned char)0x8D; + x86_address_byte(buf, 2, X86_ESI, X86_ESI); + x86_imm_emit32(buf, 0); + len -= 6; + } + if(len >= 3) + { + /* "leal 0(%esi), %esi" with 8-bit displacement */ + *buf++ = (unsigned char)0x8D; + x86_address_byte(buf, 1, X86_ESI, X86_ESI); + x86_imm_emit8(buf, 0); + len -= 3; + } + if(len == 1) + { + /* Traditional x86 NOP */ + x86_nop(buf); + } + else if(len == 2) + { + /* movl %esi, %esi */ + x86_mov_reg_reg(buf, X86_ESI, X86_ESI, 4); + } +} + +#endif /* x86 */ diff --git a/jit/jit-apply-x86.h b/jit/jit-apply-x86.h new file mode 100644 index 0000000..1467e1d --- /dev/null +++ b/jit/jit-apply-x86.h @@ -0,0 +1,320 @@ +/* + * jit-apply-x86.h - Special definitions for x86 function application. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_APPLY_X86_H +#define _JIT_APPLY_X86_H + +/* + * The "__builtin_apply" functionality in gcc has some problems + * dealing with floating-point values, and it also doesn't handle + * STDCALL and FASTCALL functions well. Therefore, we use assembly + * code instead. + * + * There are three versions here: gcc/non-Win32, gcc/Win32, and msvc/Win32. + */ + +#if defined(__GNUC__) + +#if !defined(__CYGWIN__) && !defined(__CYGWIN32__) && \ + !defined(_WIN32) && !defined(WIN32) + +#define jit_builtin_apply(func,args,size,return_float,return_buf) \ + do { \ + void *__func = (void *)(func); \ + void *__args = (void *)(args); \ + void *__size = (void *)(size); \ + void *__return_buf = alloca(20); \ + (return_buf) = __return_buf; \ + __asm__ ( \ + "pushl %%esi\n\t" \ + "movl %%esp, %%esi\n\t" \ + "subl %2, %%esp\n\t" \ + "movl %%esp, %%eax\n\t" \ + "movl %1, %%ecx\n\t" \ + "movl (%%ecx), %%ecx\n\t" \ + "pushl %2\n\t" \ + "pushl %%ecx\n\t" \ + "pushl %%eax\n\t" \ + "call memcpy\n\t" \ + "addl $12, %%esp\n\t" \ + "movl %1, %%ecx\n\t" \ + "movl %0, %%eax\n\t" \ + "call *%%eax\n\t" \ + "movl %3, %%ecx\n\t" \ + "movl %%eax, (%%ecx)\n\t" \ + "movl %%edx, 4(%%ecx)\n\t" \ + "movl %%esi, %%esp\n\t" \ + "popl %%esi\n\t" \ + : : "m"(__func), "m"(__args), "m"(__size), "m"(__return_buf) \ + : "eax", "ecx", "edx" \ + ); \ + if((return_float)) \ + { \ + __asm__ ( \ + "movl %0, %%ecx\n\t" \ + "fstpt 8(%%ecx)\n\t" \ + : : "m"(__return_buf) \ + : "ecx", "st" \ + ); \ + } \ + } while (0) + +#define jit_builtin_apply_args(type,args) \ + do { \ + void *__args = alloca(4); \ + __asm__ ( \ + "leal 8(%%ebp), %%eax\n\t" \ + "movl %0, %%ecx\n\t" \ + "movl %%eax, (%%ecx)\n\t" \ + : : "m"(__args) \ + : "eax", "ecx" \ + ); \ + (args) = (type)__args; \ + } while (0) + +#define jit_builtin_return_int(return_buf) \ + do { \ + __asm__ ( \ + "movl %0, %%ecx\n\t" \ + "movl (%%ecx), %%eax\n\t" \ + "movl 4(%%ecx), %%edx\n\t" \ + : : "m"((return_buf)) \ + : "eax", "ecx", "edx" \ + ); \ + return; \ + } while (0) + +#define jit_builtin_return_float(return_buf) \ + do { \ + jit_nfloat __value = \ + ((jit_apply_return *)(return_buf))-> \ + f_value.inner_value.nfloat_value; \ + if(sizeof(jit_nfloat) == sizeof(double)) \ + { \ + __asm__ ( \ + "leal %0, %%ecx\n\t" \ + "fldl (%%ecx)\n\t" \ + : : "m"(__value) \ + : "ecx", "st" \ + ); \ + } \ + else \ + { \ + __asm__ ( \ + "leal %0, %%ecx\n\t" \ + "fldt (%%ecx)\n\t" \ + : : "m"(__value) \ + : "ecx", "st" \ + ); \ + } \ + return; \ + } while (0) + +#else /* Win32 */ + +#define jit_builtin_apply(func,args,size,return_float,return_buf) \ + do { \ + void *__func = (void *)(func); \ + void *__args = (void *)(args); \ + void *__size = (void *)(size); \ + void *__return_buf = alloca(20); \ + (return_buf) = __return_buf; \ + __asm__ ( \ + "pushl %%esi\n\t" \ + "movl %%esp, %%esi\n\t" \ + "subl %2, %%esp\n\t" \ + "movl %%esp, %%eax\n\t" \ + "movl %1, %%ecx\n\t" \ + "movl (%%ecx), %%ecx\n\t" \ + "pushl %2\n\t" \ + "pushl %%ecx\n\t" \ + "pushl %%eax\n\t" \ + "call _memcpy\n\t" \ + "addl $12, %%esp\n\t" \ + "movl %1, %%ecx\n\t" \ + "movl 8(%%ecx), %%edx\n\t" \ + "movl 4(%%ecx), %%ecx\n\t" \ + "movl %0, %%eax\n\t" \ + "call *%%eax\n\t" \ + "movl %3, %%ecx\n\t" \ + "movl %%eax, (%%ecx)\n\t" \ + "movl %%edx, 4(%%ecx)\n\t" \ + "movl %%esi, %%esp\n\t" \ + "popl %%esi\n\t" \ + : : "m"(__func), "m"(__args), "m"(__size), "m"(__return_buf) \ + : "eax", "ecx", "edx" \ + ); \ + if((return_float)) \ + { \ + __asm__ ( \ + "movl %0, %%ecx\n\t" \ + "fstpt 8(%%ecx)\n\t" \ + : : "m"(__return_buf) \ + : "ecx", "st" \ + ); \ + } \ + } while (0) + +#define jit_builtin_apply_args(type,args) \ + do { \ + void *__args = alloca(12); \ + __asm__ ( \ + "movl %0, %%eax\n\t" \ + "movl %%ecx, 4(%%eax)\n\t" \ + "movl %%edx, 8(%%eax)\n\t" \ + "leal 8(%%ebp), %%ecx\n\t" \ + "movl %%ecx, (%%eax)\n\t" \ + : : "m"(__args) \ + : "eax", "ecx", "edx" \ + ); \ + (args) = (type)__args; \ + } while (0) + +#define jit_builtin_return_int(return_buf) \ + do { \ + __asm__ ( \ + "movl %0, %%ecx\n\t" \ + "movl (%%ecx), %%eax\n\t" \ + "movl 4(%%ecx), %%edx\n\t" \ + : : "m"((return_buf)) \ + : "eax", "ecx", "edx" \ + ); \ + return; \ + } while (0) + +#define jit_builtin_return_float(return_buf) \ + do { \ + double __value = \ + ((jit_apply_return *)(return_buf))-> \ + f_value.inner_value.nfloat_value; \ + __asm__ ( \ + "leal %0, %%ecx\n\t" \ + "fldl (%%ecx)\n\t" \ + : : "m"(__value) \ + : "ecx", "st" \ + ); \ + return; \ + } while (0) + +#endif /* Win32 */ + +#elif defined(_MSC_VER) + +#define jit_builtin_apply(func,args,size,return_float,return_buf) \ + do { \ + void *__func = (void *)(func); \ + void *__args = (void *)(args); \ + void *__size = (void *)(size); \ + void *__return_buf = alloca(20); \ + (return_buf) = __return_buf; \ + __asm { \ + __asm push esi \ + __asm mov esi, esp \ + __asm sub esp, dword ptr __size \ + __asm mov eax, esp \ + __asm mov ecx, dword ptr __args \ + __asm mov ecx, [ecx] \ + __asm push dword ptr __size \ + __asm push ecx \ + __asm push eax \ + __asm call jit_memcpy \ + __asm add esp, 12 \ + __asm mov ecx, dword ptr __args \ + __asm mov edx, [ecx + 8] \ + __asm mov ecx, [ecx + 4] \ + __asm mov eax, dword ptr __func \ + __asm call eax \ + __asm mov ecx, dword ptr __return_buf \ + __asm mov [ecx], eax \ + __asm mov [ecx + 4], edx \ + __asm mov esp, esi \ + __asm pop esi \ + } \ + if((return_float)) \ + { \ + __asm { \ + __asm mov ecx, dword ptr __return_buf \ + /*__asm fstpt [ecx + 8]*/ \ + __asm _emit 0xDB \ + __asm _emit 0x79 \ + __asm _emit 0x08 \ + } \ + } \ + } while (0) + +#define jit_builtin_apply_args(type,args) \ + do { \ + void *__args = alloca(12); \ + __asm { \ + __asm mov eax, dword ptr __args \ + __asm mov [eax + 4], ecx \ + __asm mov [eax + 8], edx \ + __asm lea ecx, [ebp + 8] \ + __asm mov [eax], ecx \ + } \ + (args) = (type)(__args); \ + } while (0) + +#define jit_builtin_return_int(return_buf) \ + do { \ + void *__return_buf = (void *)(return_buf); \ + __asm { \ + __asm mov ecx, dword ptr __return_buf \ + __asm mov eax, [ecx] \ + __asm mov edx, [ecx + 4] \ + } \ + return; \ + } while (0) + +#define jit_builtin_return_float(return_buf) \ + do { \ + double __value = \ + ((jit_apply_return *)(return_buf))-> \ + f_value.inner_value.nfloat_value; \ + __asm { \ + __asm lea ecx, dword ptr __value \ + /* __asm fldl [ecx] */ \ + __asm _emit 0xDD \ + __asm _emit 0x01 \ + } \ + return; \ + } while (0) + +#endif /* MSC_VER */ + +/* + * The maximum number of bytes that are needed to represent a closure, + * and the alignment to use for the closure. + */ +#define jit_closure_size 64 +#define jit_closure_align 32 + +/* + * The number of bytes that are needed for a redirector stub. + * This includes any extra bytes that are needed for alignment. + */ +#define jit_redirector_size 32 + +/* + * We should pad unused code space with NOP's. + */ +#define jit_should_pad 1 + +#endif /* _JIT_APPLY_X86_H */ diff --git a/jit/jit-apply.c b/jit/jit-apply.c new file mode 100644 index 0000000..3876f22 --- /dev/null +++ b/jit/jit-apply.c @@ -0,0 +1,980 @@ +/* + * jit-apply.c - Dynamic invocation and closure support functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-memory.h" +#include "jit-apply-rules.h" +#include "jit-apply-func.h" +#include "jit-cache.h" +#if HAVE_ALLOCA_H + #include +#endif +#ifdef JIT_WIN32_PLATFORM + #include + #ifndef alloca + #define alloca _alloca + #endif +#endif + +/* + +If you need to tweak the way that this code behaves for a specific +platform, then you would normally do it in "tools/gen-apply.c" or +the CPU-specific "jit-apply-XXX.h" file, not here. + +*/ + +/*@ + +@section Function application and closures +@cindex Function application +@cindex Closures +@cindex jit-apply.h + +Sometimes all you have for a function is a pointer to it and a dynamic +description of its arguments. Calling such a function can be extremely +difficult in standard C. The routines in this section, particularly +@code{jit_apply}, provide a convenient interface for doing this. + +At other times, you may wish to wrap up one of your own dynamic functions +in such a way that it appears to be a regular C function. This is +performed with @code{jit_closure_create}. + +@*/ + +/* + * Flags that indicate which structure sizes are returned in registers. + */ +unsigned char const _jit_apply_return_in_reg[] = + JIT_APPLY_STRUCT_RETURN_IN_REG_INIT; + +/* + * Get the maximum argument stack size of a signature type. + */ +static unsigned int jit_type_get_max_arg_size(jit_type_t signature) +{ + unsigned int size; + unsigned int typeSize; + unsigned int param; + jit_type_t type; + if(signature->size) + { + /* We have a cached argument size from last time */ + return signature->size; + } + size = 0; + param = jit_type_num_params(signature); + while(param > 0) + { + --param; + type = jit_type_normalize(jit_type_get_param(signature, param)); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + case JIT_TYPE_PTR: + case JIT_TYPE_SIGNATURE: + { + size += sizeof(jit_nint); + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + #ifdef JIT_NATIVE_INT32 + /* Add one extra word for possible alignment padding */ + size += sizeof(jit_long) + sizeof(jit_nint); + #else + size += sizeof(jit_nint); + #endif + } + break; + + case JIT_TYPE_FLOAT32: + case JIT_TYPE_FLOAT64: + case JIT_TYPE_NFLOAT: + { + /* Allocate space for an "nfloat" and an alignment word */ + size += (sizeof(jit_nfloat) + sizeof(jit_nint) * 2 - 1) & + ~(sizeof(jit_nint) - 1); + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + /* Allocate space for the structure and an alignment word */ + typeSize = jit_type_get_size(type); + size += (typeSize + sizeof(jit_nint) * 2 - 1) & + ~(sizeof(jit_nint) - 1); + } + break; + } + } + type = jit_type_get_return(signature); + if(jit_type_is_struct(type) || jit_type_is_union(type)) + { + /* Add one extra word for the possibility of a structure pointer */ + size += sizeof(jit_nint); + } + signature->size = size; + return size; +} + +/* + * Copy apply arguments into position. + */ +static void jit_apply_builder_add_arguments + (jit_apply_builder *builder, jit_type_t signature, + void **args, unsigned int index, unsigned int num_args) +{ + unsigned int param; + jit_type_t type; + for(param = 0; param < num_args; ++param) + { + type = jit_type_normalize + (jit_type_get_param(signature, index + param)); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + jit_apply_builder_add_sbyte + (builder, *((jit_sbyte *)(args[param]))); + } + break; + + case JIT_TYPE_UBYTE: + { + jit_apply_builder_add_ubyte + (builder, *((jit_ubyte *)(args[param]))); + } + break; + + case JIT_TYPE_SHORT: + { + jit_apply_builder_add_short + (builder, *((jit_short *)(args[param]))); + } + break; + + case JIT_TYPE_USHORT: + { + jit_apply_builder_add_ushort + (builder, *((jit_ushort *)(args[param]))); + } + break; + + case JIT_TYPE_INT: + { + jit_apply_builder_add_int + (builder, *((jit_int *)(args[param]))); + } + break; + + case JIT_TYPE_UINT: + { + jit_apply_builder_add_uint + (builder, *((jit_uint *)(args[param]))); + } + break; + + case JIT_TYPE_NINT: + case JIT_TYPE_PTR: + case JIT_TYPE_SIGNATURE: + { + jit_apply_builder_add_nint + (builder, *((jit_nint *)(args[param]))); + } + break; + + case JIT_TYPE_NUINT: + { + jit_apply_builder_add_nuint + (builder, *((jit_nuint *)(args[param]))); + } + break; + + case JIT_TYPE_LONG: + { + jit_apply_builder_add_long + (builder, *((jit_long *)(args[param]))); + } + break; + + case JIT_TYPE_ULONG: + { + jit_apply_builder_add_ulong + (builder, *((jit_ulong *)(args[param]))); + } + break; + + case JIT_TYPE_FLOAT32: + { + jit_apply_builder_add_float32 + (builder, *((jit_float32 *)(args[param]))); + } + break; + + case JIT_TYPE_FLOAT64: + { + jit_apply_builder_add_float64 + (builder, *((jit_float64 *)(args[param]))); + } + break; + + case JIT_TYPE_NFLOAT: + { + jit_apply_builder_add_nfloat + (builder, *((jit_nfloat *)(args[param]))); + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + jit_apply_builder_add_struct + (builder, args[param], jit_type_get_size(type), + jit_type_get_alignment(type)); + } + break; + } + } +} + +/* + * Get the return value after calling a function using "__builtin_apply". + */ +static void jit_apply_builder_get_return + (jit_apply_builder *builder, void *return_value, + jit_type_t type, jit_apply_return *result) +{ + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + *((jit_sbyte *)return_value) = + jit_apply_return_get_sbyte(result); + } + break; + + case JIT_TYPE_UBYTE: + { + *((jit_ubyte *)return_value) = + jit_apply_return_get_ubyte(result); + } + break; + + case JIT_TYPE_SHORT: + { + *((jit_short *)return_value) = + jit_apply_return_get_short(result); + } + break; + + case JIT_TYPE_USHORT: + { + *((jit_ushort *)return_value) = + jit_apply_return_get_ushort(result); + } + break; + + case JIT_TYPE_INT: + { + *((jit_int *)return_value) = + jit_apply_return_get_int(result); + } + break; + + case JIT_TYPE_UINT: + { + *((jit_uint *)return_value) = + jit_apply_return_get_uint(result); + } + break; + + case JIT_TYPE_NINT: + case JIT_TYPE_PTR: + case JIT_TYPE_SIGNATURE: + { + *((jit_nint *)return_value) = + jit_apply_return_get_nint(result); + } + break; + + case JIT_TYPE_NUINT: + { + *((jit_nuint *)return_value) = + jit_apply_return_get_nuint(result); + } + break; + + case JIT_TYPE_LONG: + { + *((jit_long *)return_value) = + jit_apply_return_get_long(result); + } + break; + + case JIT_TYPE_ULONG: + { + *((jit_ulong *)return_value) = + jit_apply_return_get_ulong(result); + } + break; + + case JIT_TYPE_FLOAT32: + { + *((jit_float32 *)return_value) = + jit_apply_return_get_float32(result); + } + break; + + case JIT_TYPE_FLOAT64: + { + *((jit_float64 *)return_value) = + jit_apply_return_get_float64(result); + } + break; + + case JIT_TYPE_NFLOAT: + { + *((jit_nfloat *)return_value) = + jit_apply_return_get_nfloat(result); + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + unsigned int size = jit_type_get_size(type); + jit_apply_builder_get_struct_return + (builder, size, return_value, result); + } + break; + } +} + +/*@ + * @deftypefun void jit_apply (jit_type_t signature, {void *} func, {void **} args, {unsigned int} num_fixed_args, {void *} return_value) + * Call a function that has a particular function signature. + * If the signature has more than @code{num_fixed_args} arguments, + * then it is assumed to be a vararg call, with the additional + * arguments passed in the vararg argument area on the stack. + * The @code{signature} must specify the type of all arguments, + * including those in the vararg argument area. + * @end deftypefun +@*/ +void jit_apply(jit_type_t signature, void *func, + void **args, unsigned int num_fixed_args, + void *return_value) +{ + jit_apply_builder builder; + unsigned int size; + jit_apply_return *apply_return; + jit_type_t type; + + /* Initialize the argument builder */ + jit_apply_builder_init(&builder, signature); + + /* Handle the structure return argument */ + type = jit_type_normalize(jit_type_get_return(signature)); + if(jit_type_is_struct(type) || jit_type_is_union(type)) + { + size = jit_type_get_size(type); + jit_apply_builder_add_struct_return(&builder, size, return_value); + } + + /* Copy the arguments into position */ + jit_apply_builder_add_arguments + (&builder, signature, args, 0, num_fixed_args); + jit_apply_builder_start_varargs(&builder); + jit_apply_builder_add_arguments + (&builder, signature, args + num_fixed_args, num_fixed_args, + jit_type_num_params(signature) - num_fixed_args); + + /* Call the function using "__builtin_apply" or something similar */ + if(type->kind < JIT_TYPE_FLOAT32 || type->kind > JIT_TYPE_NFLOAT) + { + jit_builtin_apply(func, builder.apply_args, + builder.stack_used, 0, apply_return); + } + else + { + jit_builtin_apply(func, builder.apply_args, + builder.stack_used, 1, apply_return); + } + + /* Copy the return value into position */ + if(return_value != 0 && type != jit_type_void) + { + jit_apply_builder_get_return + (&builder, return_value, type, apply_return); + } +} + +/*@ + * @deftypefun void jit_apply_raw (jit_type_t signature, {void *} func, {void *} args, {void *} return_value) + * Call a function, passing a set of raw arguments. This can only + * be used if @code{jit_raw_supported} returns non-zero for the signature. + * The @code{args} value is assumed to be an array of @code{jit_nint} values + * that correspond to each of the arguments. Raw function calls + * are slightly faster than their non-raw counterparts, but can + * only be used in certain circumstances. + * @end deftypefun +@*/ +void jit_apply_raw(jit_type_t signature, void *func, + void *args, void *return_value) +{ + jit_apply_return *apply_return; + unsigned int size; + jit_type_t type; + + /* Call the function using "__builtin_apply" or something similar */ + type = jit_type_normalize(jit_type_get_return(signature)); + size = jit_type_num_params(signature) * sizeof(jit_nint); + if(type->kind < JIT_TYPE_FLOAT32 || type->kind > JIT_TYPE_NFLOAT) + { + jit_builtin_apply(func, args, size, 0, apply_return); + } + else + { + jit_builtin_apply(func, args, size, 1, apply_return); + } + + /* Copy the return value into position */ + if(return_value != 0 && type != jit_type_void) + { + jit_apply_builder_get_return + (0, return_value, type, apply_return); + } +} + +/*@ + * @deftypefun int jit_raw_supported (jit_type_t signature) + * Determine if @code{jit_apply_raw} can be used to call functions + * with a particular signature. Returns zero if not. + * @end deftypefun +@*/ +int jit_raw_supported(jit_type_t signature) +{ +#if JIT_APPLY_NUM_WORD_REGS == 0 && JIT_APPLY_NUM_FLOAT_REGS == 0 && \ + JIT_APPLY_STRUCT_RETURN_SPECIAL_REG == 0 + + unsigned int param; + jit_type_t type; + +#if JIT_APPLY_X86_FASTCALL != 0 + /* Cannot use raw calls with fastcall functions */ + if(jit_type_get_abi(signature) == jit_abi_fastcall) + { + return 0; + } +#endif + + /* Check that all of the arguments are word-sized */ + param = jit_type_num_params(signature); + while(param > 0) + { + --param; + type = jit_type_normalize(jit_type_get_param(signature, param)); + if(type->kind < JIT_TYPE_SBYTE || type->kind > JIT_TYPE_NUINT) + { + return 0; + } + } + + /* Check that the return value does not involve structures */ + type = jit_type_get_return(signature); + if(jit_type_is_struct(type) || jit_type_is_union(type)) + { + return 0; + } + + /* The signature is suitable for use with "jit_apply_raw" */ + return 1; + +#else + /* We cannot use raw calls if we need to use registers in applys */ + return 0; +#endif +} + +/* + * Define the structure of a vararg list for closures. + */ +struct jit_closure_va_list +{ + jit_apply_builder builder; +}; + +#ifdef jit_closure_size + +/* + * Define the closure structure. + */ +typedef struct jit_closure *jit_closure_t; +struct jit_closure +{ + unsigned char buf[jit_closure_size]; + jit_type_t signature; + jit_closure_func func; + void *user_data; +}; + +/* + * Handler that is called when a closure is invoked. + */ +static void closure_handler(jit_closure_t closure, void *apply_args) +{ + jit_type_t signature = closure->signature; + jit_type_t type; + jit_apply_builder parser; + void *return_buffer; + void **args; + void *temp_arg; + unsigned int num_params; + unsigned int param; + jit_apply_return apply_return; + int is_float_return; + + /* Initialize the argument parser */ + jit_apply_parser_init(&parser, closure->signature, apply_args); + + /* Allocate space for the return value */ + type = jit_type_normalize(jit_type_get_return(signature)); + if(!type || type == jit_type_void) + { + return_buffer = 0; + } + else if(jit_type_return_via_pointer(type)) + { + jit_apply_parser_get_struct_return(&parser, return_buffer); + } + else + { + return_buffer = alloca(jit_type_get_size(type)); + } + + /* Allocate space for the argument buffer. We allow for one + extra argument to hold the "va" list */ + num_params = jit_type_num_params(signature); + args = (void **)alloca((num_params + 1) * sizeof(void *)); + + /* Extract the fixed arguments */ + for(param = 0; param < num_params; ++param) + { + type = jit_type_normalize(jit_type_get_param(signature, param)); + if(!type) + { + args[param] = 0; + continue; + } + temp_arg = alloca(jit_type_get_size(type)); + args[param] = temp_arg; + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + jit_apply_parser_get_sbyte + (&parser, *((jit_sbyte *)temp_arg)); + } + break; + + case JIT_TYPE_UBYTE: + { + jit_apply_parser_get_ubyte + (&parser, *((jit_ubyte *)temp_arg)); + } + break; + + case JIT_TYPE_SHORT: + { + jit_apply_parser_get_short + (&parser, *((jit_short *)temp_arg)); + } + break; + + case JIT_TYPE_USHORT: + { + jit_apply_parser_get_ushort + (&parser, *((jit_ushort *)temp_arg)); + } + break; + + case JIT_TYPE_INT: + { + jit_apply_parser_get_int + (&parser, *((jit_int *)temp_arg)); + } + break; + + case JIT_TYPE_UINT: + { + jit_apply_parser_get_uint + (&parser, *((jit_uint *)temp_arg)); + } + break; + + case JIT_TYPE_LONG: + { + jit_apply_parser_get_long + (&parser, *((jit_long *)temp_arg)); + } + break; + + case JIT_TYPE_ULONG: + { + jit_apply_parser_get_ulong + (&parser, *((jit_ulong *)temp_arg)); + } + break; + + case JIT_TYPE_FLOAT32: + { + jit_apply_parser_get_float32 + (&parser, *((jit_float32 *)temp_arg)); + } + break; + + case JIT_TYPE_FLOAT64: + { + jit_apply_parser_get_float64 + (&parser, *((jit_float64 *)temp_arg)); + } + break; + + case JIT_TYPE_NFLOAT: + { + jit_apply_parser_get_nfloat + (&parser, *((jit_nfloat *)temp_arg)); + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + jit_apply_parser_get_struct + (&parser, jit_type_get_size(type), + jit_type_get_alignment(type), temp_arg); + } + break; + } + } + + /* Adjust the argument parser for the start of the va arguments */ + jit_apply_parser_start_varargs(&parser); + + /* Record the address of the va handler in the last argument slot. + Not all functions will need this, but it doesn't hurt to include it */ + args[num_params] = &parser; + + /* Call the user's closure handling function */ + (*(closure->func))(signature, return_buffer, args, closure->user_data); + + /* Set up the "apply return" buffer */ + jit_memzero(&apply_return, sizeof(apply_return)); + type = jit_type_normalize(jit_type_get_return(signature)); + is_float_return = 0; + if(type) + { + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + jit_apply_return_set_sbyte + (&apply_return, *((jit_sbyte *)return_buffer)); + } + break; + + case JIT_TYPE_UBYTE: + { + jit_apply_return_set_ubyte + (&apply_return, *((jit_ubyte *)return_buffer)); + } + break; + + case JIT_TYPE_SHORT: + { + jit_apply_return_set_short + (&apply_return, *((jit_short *)return_buffer)); + } + break; + + case JIT_TYPE_USHORT: + { + jit_apply_return_set_ushort + (&apply_return, *((jit_ushort *)return_buffer)); + } + break; + + case JIT_TYPE_INT: + { + jit_apply_return_set_int + (&apply_return, *((jit_int *)return_buffer)); + } + break; + + case JIT_TYPE_UINT: + { + jit_apply_return_set_uint + (&apply_return, *((jit_uint *)return_buffer)); + } + break; + + case JIT_TYPE_LONG: + { + jit_apply_return_set_long + (&apply_return, *((jit_long *)return_buffer)); + } + break; + + case JIT_TYPE_ULONG: + { + jit_apply_return_set_ulong + (&apply_return, *((jit_ulong *)return_buffer)); + } + break; + + case JIT_TYPE_FLOAT32: + { + jit_apply_return_set_float32 + (&apply_return, *((jit_float32 *)return_buffer)); + is_float_return = 1; + } + break; + + case JIT_TYPE_FLOAT64: + { + jit_apply_return_set_float64 + (&apply_return, *((jit_float64 *)return_buffer)); + is_float_return = 1; + } + break; + + case JIT_TYPE_NFLOAT: + { + jit_apply_return_set_nfloat + (&apply_return, *((jit_nfloat *)return_buffer)); + is_float_return = 1; + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + if(!jit_type_return_via_pointer(type)) + { + jit_memcpy(&apply_return, return_buffer, + jit_type_get_size(type)); + } + } + break; + } + } + + /* Return the result to the caller */ + if(!is_float_return) + { + jit_builtin_return_int(&apply_return); + } + else + { + jit_builtin_return_float(&apply_return); + } +} + +#endif /* jit_closure_size */ + +/*@ + * @deftypefun {void *} jit_closure_create (jit_context_t context, jit_type_t signature, {jit_closure_func} func, {void *} user_data) + * Create a closure from a function signature, a closure handling function, + * and a user data value. Returns NULL if out of memory, or if closures are + * not supported. The @code{func} argument should have the following + * prototype: + * + * @example + * void func (jit_type_t signature, void *result, + * void **args, void *user_data); + * @end example + * + * If the closure signature includes variable arguments, then @code{args} + * will contain pointers to the fixed arguments, followed by a + * @code{jit_closure_va_list_t} value for accessing the remainder of + * the arguments. + * + * The memory for the closure will be reclaimed when the @code{context} + * is destroyed. + * @end deftypefun +@*/ +void *jit_closure_create(jit_context_t context, jit_type_t signature, + jit_closure_func func, void *user_data) +{ +#ifdef jit_closure_size + jit_cache_t cache; + jit_closure_t closure; + + /* Validate the parameters */ + if(!context || !signature || !func) + { + return 0; + } + + /* Acquire the cache lock while we do this */ + jit_mutex_lock(&(context->cache_lock)); + + /* Allocate space for the closure within the context's function cache */ + cache = _jit_context_get_cache(context); + if(!cache) + { + jit_mutex_unlock(&(context->cache_lock)); + return 0; + } + closure = (jit_closure_t)_jit_cache_alloc_no_method + (cache, sizeof(struct jit_closure), jit_closure_align); + if(!closure) + { + jit_mutex_unlock(&(context->cache_lock)); + return 0; + } + + /* Fill in the closure fields */ + _jit_create_closure + (closure->buf, (void *)closure_handler, closure, signature); + closure->signature = signature; + closure->func = func; + closure->user_data = user_data; + + /* Perform a cache flush on the closure's code */ + jit_flush_exec(closure->buf, sizeof(closure->buf)); + + /* Unlock the cache, as we are finished with it */ + jit_mutex_unlock(&(context->cache_lock)); + + /* Return the completed closure to the caller */ + return closure; + +#else + /* Closures are not supported on this platform */ + return 0; +#endif +} + +/*@ + * @deftypefun int jit_closures_supported (void) + * Determine if this platform has support for closures. + * @end deftypefun +@*/ +int jit_closures_supported(void) +{ +#ifdef jit_closure_size + return 1; +#else + return 0; +#endif +} + +/*@ + * @deftypefun jit_nint jit_closure_va_get_nint (jit_closure_va_list_t va) + * @deftypefunx jit_nuint jit_closure_va_get_nuint (jit_closure_va_list_t va) + * @deftypefunx jit_long jit_closure_va_get_long (jit_closure_va_list_t va) + * @deftypefunx jit_ulong jit_closure_va_get_ulong (jit_closure_va_list_t va) + * @deftypefunx jit_float32 jit_closure_va_get_float32 (jit_closure_va_list_t va) + * @deftypefunx jit_float64 jit_closure_va_get_float64 (jit_closure_va_list_t va) + * @deftypefunx jit_nfloat jit_closure_va_get_nfloat (jit_closure_va_list_t va) + * @deftypefunx {void *} jit_closure_va_get_ptr (jit_closure_va_list_t va) + * Get the next value of a specific type from a closure's variable arguments. + * @end deftypefun +@*/ +jit_nint jit_closure_va_get_nint(jit_closure_va_list_t va) +{ + jit_nint value; + jit_apply_parser_get_nint(&(va->builder), value); + return value; +} + +jit_nuint jit_closure_va_get_nuint(jit_closure_va_list_t va) +{ + jit_nuint value; + jit_apply_parser_get_nuint(&(va->builder), value); + return value; +} + +jit_long jit_closure_va_get_long(jit_closure_va_list_t va) +{ + jit_long value; + jit_apply_parser_get_long(&(va->builder), value); + return value; +} + +jit_ulong jit_closure_va_get_ulong(jit_closure_va_list_t va) +{ + jit_ulong value; + jit_apply_parser_get_ulong(&(va->builder), value); + return value; +} + +jit_float32 jit_closure_va_get_float32(jit_closure_va_list_t va) +{ + jit_float32 value; + jit_apply_parser_get_float32(&(va->builder), value); + return value; +} + +jit_float64 jit_closure_va_get_float64(jit_closure_va_list_t va) +{ + jit_float64 value; + jit_apply_parser_get_float64(&(va->builder), value); + return value; +} + +jit_nfloat jit_closure_va_get_nfloat(jit_closure_va_list_t va) +{ + jit_nfloat value; + jit_apply_parser_get_nfloat(&(va->builder), value); + return value; +} + +void *jit_closure_va_get_ptr(jit_closure_va_list_t va) +{ + jit_nint value; + jit_apply_parser_get_nint(&(va->builder), value); + return (void *)value; +} + +/*@ + * @deftypefun void jit_closure_va_get_struct (jit_closure_va_list_t va, void *buf, jit_type_t type) + * Get a structure or union value of a specific @code{type} from a closure's + * variable arguments, and copy it into @code{buf}. + * @end deftypefun +@*/ +void jit_closure_va_get_struct + (jit_closure_va_list_t va, void *buf, jit_type_t type) +{ + jit_apply_parser_get_struct + (&(va->builder), jit_type_get_size(type), + jit_type_get_alignment(type), buf); +} diff --git a/jit/jit-block.c b/jit/jit-block.c new file mode 100644 index 0000000..d6f6960 --- /dev/null +++ b/jit/jit-block.c @@ -0,0 +1,373 @@ +/* + * jit-block.c - Functions for manipulating blocks. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-memory.h" + +/*@ + +@cindex jit-block.h + +@*/ + +int _jit_block_init(jit_function_t func) +{ + func->builder->entry = _jit_block_create(func, 0); + if(!(func->builder->entry)) + { + return 0; + } + func->builder->entry->entered_via_top = 1; + func->builder->current_block = func->builder->entry; + return 1; +} + +void _jit_block_free(jit_function_t func) +{ + jit_block_t current = func->builder->first_block; + jit_block_t next; + jit_block_eh_t current_eh; + jit_block_eh_t next_eh; + while(current != 0) + { + next = current->next; + jit_meta_destroy(&(current->meta)); + jit_free(current); + current = next; + } + current_eh = func->builder->exception_handlers; + while(current_eh != 0) + { + next_eh = current_eh->next; + jit_free(current_eh); + current_eh = next_eh; + } + func->builder->first_block = 0; + func->builder->last_block = 0; + func->builder->entry = 0; + func->builder->current_block = 0; + func->builder->exception_handlers = 0; + func->builder->current_handler = 0; +} + +jit_block_t _jit_block_create(jit_function_t func, jit_label_t *label) +{ + jit_block_t block; + + /* Allocate memory for the block */ + block = jit_cnew(struct _jit_block); + if(!block) + { + return 0; + } + + /* Initialize the block and set its label */ + block->func = func; + block->first_insn = func->builder->num_insns; + block->last_insn = block->first_insn - 1; + if(label) + { + if(*label == jit_label_undefined) + { + *label = (func->builder->next_label)++; + } + block->label = *label; + if(!_jit_block_record_label(block)) + { + jit_free(block); + return 0; + } + } + else + { + block->label = jit_label_undefined; + } + + /* Set the exception handling context for this block */ + block->block_eh = func->builder->current_handler; + + /* Add the block to the end of the function's list */ + block->next = 0; + block->prev = func->builder->last_block; + if(func->builder->last_block) + { + func->builder->last_block->next = block; + } + else + { + func->builder->first_block = block; + } + func->builder->last_block = block; + return block; +} + +int _jit_block_record_label(jit_block_t block) +{ + jit_builder_t builder = block->func->builder; + jit_label_t num; + jit_block_t *blocks; + if(block->label >= builder->max_label_blocks) + { + num = builder->max_label_blocks; + if(num < 64) + { + num = 64; + } + while(num <= block->label) + { + num *= 2; + } + blocks = (jit_block_t *)jit_realloc + (builder->label_blocks, num * sizeof(jit_block_t)); + if(!blocks) + { + return 0; + } + jit_memzero(blocks + builder->max_label_blocks, + sizeof(jit_block_t) * (num - builder->max_label_blocks)); + builder->label_blocks = blocks; + builder->max_label_blocks = num; + } + builder->label_blocks[block->label] = block; + return 1; +} + +/*@ + * @deftypefun jit_function_t jit_block_get_function (jit_block_t block) + * Get the function that a particular @code{block} belongs to. + * @end deftypefun +@*/ +jit_function_t jit_block_get_function(jit_block_t block) +{ + if(block) + { + return block->func; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_context_t jit_block_get_context (jit_block_t block) + * Get the context that a particular @code{block} belongs to. + * @end deftypefun +@*/ +jit_context_t jit_block_get_context(jit_block_t block) +{ + if(block) + { + return block->func->context; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_label_t jit_block_get_label (jit_block_t block) + * Get the label associated with a block. + * @end deftypefun +@*/ +jit_label_t jit_block_get_label(jit_block_t block) +{ + if(block) + { + return block->label; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_block_t jit_block_next (jit_function_t func, jit_block_t previous) + * Iterate over the blocks in a function, in order of their creation. + * The @code{previous} argument should be NULL on the first call. + * This function will return NULL if there are no further blocks to iterate. + * @end deftypefun +@*/ +jit_block_t jit_block_next(jit_function_t func, jit_block_t previous) +{ + if(previous) + { + return previous->next; + } + else if(func && func->builder) + { + return func->builder->first_block; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_block_t jit_block_previous (jit_function_t func, jit_block_t previous) + * Iterate over the blocks in a function, in reverse order of their creation. + * The @code{previous} argument should be NULL on the first call. + * This function will return NULL if there are no further blocks to iterate. + * @end deftypefun +@*/ +jit_block_t jit_block_previous(jit_function_t func, jit_block_t previous) +{ + if(previous) + { + return previous->prev; + } + else if(func && func->builder) + { + return func->builder->last_block; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_block_t jit_block_from_label (jit_function_t func, jit_label_t label) + * Get the block that corresponds to a particular @code{label}. + * Returns NULL if there is no block associated with the label. + * @end deftypefun +@*/ +jit_block_t jit_block_from_label(jit_function_t func, jit_label_t label) +{ + if(func && func->builder && label < func->builder->max_label_blocks) + { + return func->builder->label_blocks[label]; + } + else + { + return 0; + } +} + +jit_insn_t _jit_block_add_insn(jit_block_t block) +{ + jit_builder_t builder = block->func->builder; + jit_insn_t insn; + int num; + jit_insn_t *insns; + + /* Allocate the instruction from the builder's memory pool */ + insn = jit_memory_pool_alloc(&(builder->insn_pool), struct _jit_insn); + if(!insn) + { + return 0; + } + + /* Make space for the instruction in the function's instruction list */ + if(builder->num_insns >= builder->max_insns) + { + num = builder->max_insns * 2; + if(num < 64) + { + num = 64; + } + insns = (jit_insn_t *)jit_realloc + (builder->insns, num * sizeof(jit_insn_t)); + if(!insns) + { + return 0; + } + builder->insns = insns; + builder->max_insns = num; + } + else + { + insns = builder->insns; + } + insns[builder->num_insns] = insn; + block->last_insn = (builder->num_insns)++; + + /* Return the instruction, which is now ready to fill in */ + return insn; +} + +jit_insn_t _jit_block_get_last(jit_block_t block) +{ + if(block->first_insn <= block->last_insn) + { + return block->func->builder->insns[block->last_insn]; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_block_set_meta (jit_block_t block, int type, {void *} data, jit_meta_free_func free_data) + * Tag a block with some metadata. Returns zero if out of memory. + * If the @code{type} already has some metadata associated with it, then + * the previous value will be freed. Metadata may be used to store + * dependency graphs, branch prediction information, or any other + * information that is useful to optimizers or code generators. + * + * Metadata type values of 10000 or greater are reserved for internal use. + * @end deftypefun +@*/ +int jit_block_set_meta(jit_block_t block, int type, void *data, + jit_meta_free_func free_data) +{ + return jit_meta_set(&(block->meta), type, data, free_data, block->func); +} + +/*@ + * @deftypefun {void *} jit_block_get_meta (jit_block_t block, int type) + * Get the metadata associated with a particular tag. Returns NULL + * if @code{type} does not have any metadata associated with it. + * @end deftypefun +@*/ +void *jit_block_get_meta(jit_block_t block, int type) +{ + return jit_meta_get(block->meta, type); +} + +/*@ + * @deftypefun void jit_block_free_meta (jit_block_t block, int type) + * Free metadata of a specific type on a block. Does nothing if + * the @code{type} does not have any metadata associated with it. + * @end deftypefun +@*/ +void jit_block_free_meta(jit_block_t block, int type) +{ + jit_meta_free(&(block->meta), type); +} + +/*@ + * @deftypefun int jit_block_is_reachable (jit_block_t block) + * Determine if a block is reachable from some other point in + * its function. Unreachable blocks can be discarded in their + * entirety. If the JIT is uncertain as to whether a block is + * reachable, or it does not wish to perform expensive flow + * analysis to find out, then it will err on the side of caution + * and assume that it is reachable. + * @end deftypefun +@*/ +int jit_block_is_reachable(jit_block_t block) +{ + return (block->entered_via_top || block->entered_via_branch); +} diff --git a/jit/jit-cache.c b/jit/jit-cache.c new file mode 100644 index 0000000..e27debc --- /dev/null +++ b/jit/jit-cache.c @@ -0,0 +1,1351 @@ +/* + * jit-cache.c - Translated function cache implementation. + * + * Copyright (C) 2002, 2003 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +/* +See the bottom of this file for documentation on the cache system. +*/ + +#include "jit-internal.h" +#include "jit-cache.h" +#include "jit-apply-func.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Tune the default size of a cache page. Memory is allocated from + * the system in chunks of this size. This will also determine + * the maximum method size that can be translated. + */ +#ifndef JIT_CACHE_PAGE_SIZE +#define JIT_CACHE_PAGE_SIZE (128 * 1024) +#endif + +/* + * Structure of a debug information header for a method. + * This header is followed by the debug data, which is + * stored as compressed metadata integers. + */ +typedef struct jit_cache_debug *jit_cache_debug_t; +struct jit_cache_debug +{ + jit_cache_debug_t next; /* Next block for the method */ + +}; + +/* + * Method information block, organised as a red-black tree node. + * There may be more than one such block associated with a method + * if the method contains exception regions. + */ +typedef struct jit_cache_method *jit_cache_method_t; +struct jit_cache_method +{ + void *method; /* Method containing the region */ + void *cookie; /* Cookie value for the region */ + unsigned char *start; /* Start of the region */ + unsigned char *end; /* End of the region */ + jit_cache_debug_t debug; /* Debug information for method */ + jit_cache_method_t left; /* Left sub-tree and red/black bit */ + jit_cache_method_t right; /* Right sub-tree */ + +}; + +/* + * Structure of the method cache. + */ +#define JIT_CACHE_DEBUG_SIZE 64 +struct jit_cache +{ + void **pages; /* List of pages currently in the cache */ + unsigned long numPages; /* Number of pages currently in the cache */ + unsigned long pageSize; /* Size of a page for allocation */ + unsigned char *freeStart; /* Start of the current free region */ + unsigned char *freeEnd; /* End of the current free region */ + int outOfMemory; /* True when cache is out of memory */ + int needRestart; /* True when page restart is required */ + long pagesLeft; /* Number of pages left to allocate */ + jit_cache_method_t method; /* Information for the current method */ + struct jit_cache_method head; /* Head of the lookup tree */ + struct jit_cache_method nil; /* Nil pointer for the lookup tree */ + unsigned char *start; /* Start of the current method */ + unsigned char debugData[JIT_CACHE_DEBUG_SIZE]; + int debugLen; /* Length of temporary debug data */ + jit_cache_debug_t firstDebug; /* First debug block for method */ + jit_cache_debug_t lastDebug; /* Last debug block for method */ + +}; + +/* + * Compress a "long" value so that it takes up less bytes. + * This is used to store offsets within functions and + * debug line numbers, which are usually small integers. + */ +static int CompressInt(unsigned char *buf, long data) +{ + if(data >= 0) + { + if(data < (long)0x40) + { + buf[0] = (unsigned char)(data << 1); + return 1; + } + else if(data < (long)(1 << 13)) + { + buf[0] = (unsigned char)(((data >> 7) & 0x3F) | 0x80); + buf[1] = (unsigned char)(data << 1); + return 2; + } + else if(data < (unsigned long)(1L << 28)) + { + buf[0] = (unsigned char)((data >> 23) | 0xC0); + buf[1] = (unsigned char)(data >> 15); + buf[2] = (unsigned char)(data >> 7); + buf[3] = (unsigned char)(data << 1); + return 4; + } + else + { + buf[0] = (unsigned char)0xE0; + buf[1] = (unsigned char)(data >> 23); + buf[2] = (unsigned char)(data >> 15); + buf[3] = (unsigned char)(data >> 7); + buf[4] = (unsigned char)(data << 1); + return 5; + } + } + else + { + if(data >= ((long)-0x40)) + { + buf[0] = ((((unsigned char)(data << 1)) & 0x7E) | 0x01); + return 1; + } + else if(data >= ((long)-(1 << 13))) + { + buf[0] = (unsigned char)(((data >> 7) & 0x3F) | 0x80); + buf[1] = (unsigned char)((data << 1) | 0x01); + return 2; + } + else if(data >= ((long)-(1L << 29))) + { + buf[0] = (unsigned char)(((data >> 23) & 0x1F) | 0xC0); + buf[1] = (unsigned char)(data >> 15); + buf[2] = (unsigned char)(data >> 7); + buf[3] = (unsigned char)((data << 1) | 0x01); + return 4; + } + else + { + buf[0] = (unsigned char)0xE1; + buf[1] = (unsigned char)(data >> 23); + buf[2] = (unsigned char)(data >> 15); + buf[3] = (unsigned char)(data >> 7); + buf[4] = (unsigned char)((data << 1) | 0x01); + return 5; + } + } +} + +/* + * Control data structure that is used by "UncompressInt". + */ +typedef struct +{ + const unsigned char *data; /* Current data position */ + unsigned long len; /* Length remaining to read */ + int error; /* Set to non-zero if error encountered */ + +} UncompressReader; + +/* + * Uncompress a value that was compressed by "CompressInt". + */ +static long UncompressInt(UncompressReader *meta) +{ + unsigned char ch; + unsigned char ch2; + unsigned char ch3; + unsigned char ch4; + unsigned long value; + + if(meta->len > 0) + { + ch = *((meta->data)++); + --(meta->len); + if((ch & 0x80) == 0x00) + { + /* One-byte form of the item */ + if((ch & 0x01) == 0x00) + return (long)(ch >> 1); + else + return (long)(signed char)((ch >> 1) | 0xC0); + } + else if((ch & 0xC0) == 0x80) + { + /* Two-byte form of the item */ + if(meta->len > 0) + { + --(meta->len); + value = (((unsigned long)(ch & 0x3F)) << 8) | + ((unsigned long)(*((meta->data)++))); + if((value & 0x01) == 0x00) + return (long)(value >> 1); + else + return (long)(jit_int)((value >> 1) | 0xFFFFE000); + } + else + { + meta->error = 1; + return 0; + } + } + else if((ch & 0xE0) == 0xC0) + { + /* Four-byte form of the item */ + if(meta->len >= 3) + { + ch2 = meta->data[0]; + ch3 = meta->data[1]; + ch4 = meta->data[2]; + meta->len -= 3; + meta->data += 3; + value = (((unsigned long)(ch & 0x1F)) << 24) | + (((unsigned long)ch2) << 16) | + (((unsigned long)ch3) << 8) | + ((unsigned long)ch4); + if((value & 0x01) == 0x00) + return (long)(value >> 1); + else + return (long)(jit_int)((value >> 1) | 0xF0000000); + } + else + { + meta->len = 0; + meta->error = 1; + return 0; + } + } + else + { + /* Five-byte form of the item */ + if(meta->len >= 4) + { + ch = meta->data[0]; + ch2 = meta->data[1]; + ch3 = meta->data[2]; + ch4 = meta->data[3]; + meta->len -= 4; + meta->data += 4; + value = (((unsigned long)ch) << 24) | + (((unsigned long)ch2) << 16) | + (((unsigned long)ch3) << 8) | + ((unsigned long)ch4); + return (long)(jit_int)value; + } + else + { + meta->len = 0; + meta->error = 1; + return 0; + } + } + } + else + { + meta->error = 1; + return 0; + } +} + +/* + * Allocate a cache page and add it to the cache. + */ +static void AllocCachePage(jit_cache_t cache) +{ + void *ptr; + void **list; + + /* If we are already out of memory, then bail out */ + if(cache->outOfMemory || !(cache->pagesLeft)) + { + goto failAlloc; + } + + /* Try to allocate a physical page */ + ptr = jit_malloc_exec((unsigned int)(cache->pageSize)); + if(!ptr) + { + goto failAlloc; + } + + /* Add the page to the page list. We keep this in an array + that is separate from the pages themselves so that we don't + have to "touch" the pages to free them. Touching the pages + may cause them to be swapped in if they are currently out. + There's no point doing that if we are trying to free them */ + list = (void **)jit_realloc + (cache->pages, sizeof(void *) * (cache->numPages + 1)); + if(!list) + { + jit_free_exec(ptr, cache->pageSize); + failAlloc: + cache->outOfMemory = 1; + cache->freeStart = 0; + cache->freeEnd = 0; + return; + } + cache->pages = list; + list[(cache->numPages)++] = ptr; + + /* One less page before we hit the limit */ + if(cache->pagesLeft > 0) + { + --(cache->pagesLeft); + } + + /* Set up the working region within the new page */ + cache->freeStart = ptr; + cache->freeEnd = (void *)(((char *)ptr) + (int)(cache->pageSize)); +} + +/* + * Get or set the sub-trees of a node. + */ +#define GetLeft(node) \ + ((jit_cache_method_t)(((jit_nuint)((node)->left)) & ~((jit_nuint)1))) +#define GetRight(node) ((node)->right) +#define SetLeft(node,value) \ + ((node)->left = (jit_cache_method_t)(((jit_nuint)(value)) | \ + (((jit_nuint)((node)->left)) & ((jit_nuint)1)))) +#define SetRight(node,value) \ + ((node)->right = (value)) + +/* + * Get or set the red/black state of a node. + */ +#define GetRed(node) \ + ((((jit_nuint)((node)->left)) & ((jit_nuint)1)) != 0) +#define SetRed(node) \ + ((node)->left = (jit_cache_method_t)(((jit_nuint)((node)->left)) | \ + ((jit_nuint)1))) +#define SetBlack(node) \ + ((node)->left = (jit_cache_method_t)(((jit_nuint)((node)->left)) & \ + ~((jit_nuint)1))) + +/* + * Compare a key against a node, being careful of sentinel nodes. + */ +static int CacheCompare(jit_cache_t cache, unsigned char *key, + jit_cache_method_t node) +{ + if(node == &(cache->nil) || node == &(cache->head)) + { + /* Every key is greater than the sentinel nodes */ + return 1; + } + else + { + /* Compare a regular node */ + if(key < node->start) + { + return -1; + } + else if(key > node->start) + { + return 1; + } + else + { + return 0; + } + } +} + +/* + * Rotate a sub-tree around a specific node. + */ +static jit_cache_method_t CacheRotate(jit_cache_t cache, unsigned char *key, + jit_cache_method_t around) +{ + jit_cache_method_t child, grandChild; + int setOnLeft; + if(CacheCompare(cache, key, around) < 0) + { + child = GetLeft(around); + setOnLeft = 1; + } + else + { + child = GetRight(around); + setOnLeft = 0; + } + if(CacheCompare(cache, key, child) < 0) + { + grandChild = GetLeft(child); + SetLeft(child, GetRight(grandChild)); + SetRight(grandChild, child); + } + else + { + grandChild = GetRight(child); + SetRight(child, GetLeft(grandChild)); + SetLeft(grandChild, child); + } + if(setOnLeft) + { + SetLeft(around, grandChild); + } + else + { + SetRight(around, grandChild); + } + return grandChild; +} + +/* + * Split a red-black tree at the current position. + */ +#define Split() \ + do { \ + SetRed(temp); \ + SetBlack(GetLeft(temp)); \ + SetBlack(GetRight(temp)); \ + if(GetRed(parent)) \ + { \ + SetRed(grandParent); \ + if((CacheCompare(cache, key, grandParent) < 0) != \ + (CacheCompare(cache, key, parent) < 0)) \ + { \ + parent = CacheRotate(cache, key, grandParent); \ + } \ + temp = CacheRotate(cache, key, greatGrandParent); \ + SetBlack(temp); \ + } \ + } while (0) + +/* + * Add a method region block to the red-black lookup tree + * that is associated with a method cache. + */ +static void AddToLookupTree(jit_cache_t cache, jit_cache_method_t method) +{ + unsigned char *key = method->start; + jit_cache_method_t temp; + jit_cache_method_t greatGrandParent; + jit_cache_method_t grandParent; + jit_cache_method_t parent; + jit_cache_method_t nil = &(cache->nil); + int cmp; + + /* Search for the insert position */ + temp = &(cache->head); + greatGrandParent = temp; + grandParent = temp; + parent = temp; + while(temp != nil) + { + /* Adjust our ancestor pointers */ + greatGrandParent = grandParent; + grandParent = parent; + parent = temp; + + /* Compare the key against the current node */ + cmp = CacheCompare(cache, key, temp); + if(cmp == 0) + { + /* This is a duplicate, which normally shouldn't happen. + If it does happen, then ignore the node and bail out */ + return; + } + else if(cmp < 0) + { + temp = GetLeft(temp); + } + else + { + temp = GetRight(temp); + } + + /* Do we need to split this node? */ + if(GetRed(GetLeft(temp)) && GetRed(GetRight(temp))) + { + Split(); + } + } + + /* Insert the new node into the current position */ + method->left = (jit_cache_method_t)(((jit_nuint)nil) | ((jit_nuint)1)); + method->right = nil; + if(CacheCompare(cache, key, parent) < 0) + { + SetLeft(parent, method); + } + else + { + SetRight(parent, method); + } + Split(); + SetBlack(cache->head.right); +} + +/* + * Flush the current debug buffer. + */ +static void FlushCacheDebug(jit_cache_posn *posn) +{ + jit_cache_t cache = posn->cache; + jit_cache_debug_t debug; + + /* Allocate a new jit_cache_debug structure to hold the data */ + debug = _jit_cache_alloc(posn, + (unsigned long)(sizeof(struct jit_cache_debug) + cache->debugLen)); + if(!debug) + { + cache->debugLen = 0; + return; + } + + /* Copy the temporary debug data into the new structure */ + jit_memcpy(debug + 1, cache->debugData, cache->debugLen); + + /* Link the structure into the debug list */ + debug->next = 0; + if(cache->lastDebug) + { + cache->lastDebug->next = debug; + } + else + { + cache->firstDebug = debug; + } + cache->lastDebug = debug; + + /* Reset the temporary debug buffer */ + cache->debugLen = 0; +} + +/* + * Write a debug pair to the cache. The pair (-1, -1) + * terminates the debug information for a method. + */ +static void WriteCacheDebug(jit_cache_posn *posn, long offset, long nativeOffset) +{ + jit_cache_t cache = posn->cache; + + /* Write the two values to the temporary debug buffer */ + cache->debugLen += CompressInt + (cache->debugData + cache->debugLen, offset); + cache->debugLen += CompressInt + (cache->debugData + cache->debugLen, nativeOffset); + if((cache->debugLen + 5 * 2 + 1) > (int)(sizeof(cache->debugData))) + { + /* Overflow occurred: write -2 to mark the end of this buffer */ + cache->debugLen += CompressInt + (cache->debugData + cache->debugLen, -2); + + /* Flush the debug data that we have collected so far */ + FlushCacheDebug(posn); + } +} + +jit_cache_t _jit_cache_create(long limit, long cache_page_size) +{ + jit_cache_t cache; + unsigned long size; + + /* Allocate space for the cache control structure */ + if((cache = (jit_cache_t )jit_malloc(sizeof(struct jit_cache))) == 0) + { + return 0; + } + + /* Initialize the rest of the cache fields */ + cache->pages = 0; + cache->numPages = 0; + size = jit_exec_page_size(); + if(!cache_page_size) + { + cache_page_size = JIT_CACHE_PAGE_SIZE; + } + size = (cache_page_size / size) * size; + if(!size) + { + size = jit_exec_page_size(); + } + cache->pageSize = size; + cache->freeStart = 0; + cache->freeEnd = 0; + cache->outOfMemory = 0; + cache->needRestart = 0; + if(limit > 0) + { + cache->pagesLeft = limit / size; + if(cache->pagesLeft < 1) + { + cache->pagesLeft = 1; + } + } + else + { + cache->pagesLeft = -1; + } + cache->method = 0; + cache->nil.method = 0; + cache->nil.cookie = 0; + cache->nil.start = 0; + cache->nil.end = 0; + cache->nil.debug = 0; + cache->nil.left = &(cache->nil); + cache->nil.right = &(cache->nil); + cache->head.method = 0; + cache->head.cookie = 0; + cache->head.start = 0; + cache->head.end = 0; + cache->head.debug = 0; + cache->head.left = 0; + cache->head.right = &(cache->nil); + cache->start = 0; + cache->debugLen = 0; + cache->firstDebug = 0; + cache->lastDebug = 0; + + /* Allocate the initial cache page */ + AllocCachePage(cache); + if(cache->outOfMemory) + { + _jit_cache_destroy(cache); + return 0; + } + + /* Ready to go */ + return cache; +} + +void _jit_cache_destroy(jit_cache_t cache) +{ + unsigned long page; + + /* Free all of the cache pages */ + for(page = 0; page < cache->numPages; ++page) + { + jit_free_exec(cache->pages[page], cache->pageSize); + } + if(cache->pages) + { + jit_free(cache->pages); + } + + /* Free the cache object itself */ + jit_free(cache); +} + +int _jit_cache_is_full(jit_cache_t cache, jit_cache_posn *posn) +{ + return (cache->outOfMemory || (posn && posn->ptr >= posn->limit)); +} + +void *_jit_cache_start_method(jit_cache_t cache, jit_cache_posn *posn, + int align, void *method) +{ + jit_nuint temp; + + /* Do we need to allocate a new cache page? */ + if(cache->needRestart) + { + cache->needRestart = 0; + AllocCachePage(cache); + } + + /* Bail out if the cache is already full */ + if(cache->outOfMemory) + { + return 0; + } + + /* Set up the initial cache position */ + posn->cache = cache; + posn->ptr = cache->freeStart; + posn->limit = cache->freeEnd; + + /* Align the method start */ + if(align <= 1) + { + align = 1; + } + temp = (((jit_nuint)(posn->ptr)) + ((jit_nuint)align) - 1) & + ~(((jit_nuint)align) - 1); + if(((unsigned char *)temp) >= posn->limit) + { + /* There is insufficient space in this page, so create a new one */ + AllocCachePage(cache); + if(cache->outOfMemory) + { + return 0; + } + + /* Set up the cache position again and align it */ + posn->ptr = cache->freeStart; + posn->limit = cache->freeEnd; + temp = (((jit_nuint)(posn->ptr)) + ((jit_nuint)align) - 1) & + ~(((jit_nuint)align) - 1); + } +#ifdef jit_should_pad + if(temp > (jit_nuint)(posn->ptr)) + { + _jit_pad_buffer(posn->ptr, (int)(((jit_nuint)(posn->ptr)) - temp)); + } +#endif + posn->ptr = (unsigned char *)temp; + + /* Allocate memory for the method information block */ + cache->method = (jit_cache_method_t) + _jit_cache_alloc(posn, sizeof(struct jit_cache_method)); + if(cache->method) + { + cache->method->method = method; + cache->method->cookie = 0; + cache->method->start = posn->ptr; + cache->method->end = posn->ptr; + cache->method->debug = 0; + cache->method->left = 0; + cache->method->right = 0; + } + cache->start = posn->ptr; + + /* Clear the debug data */ + cache->debugLen = 0; + cache->firstDebug = 0; + cache->lastDebug = 0; + + /* Return the method entry point to the caller */ + return (void *)(posn->ptr); +} + +int _jit_cache_end_method(jit_cache_posn *posn) +{ + jit_cache_t cache = posn->cache; + jit_cache_method_t method; + jit_cache_method_t next; + + /* Determine if we ran out of space while writing the method */ + if(posn->ptr >= posn->limit) + { + /* Determine if the method was too big, or we need a restart. + The method is judged to be too big if we had a new page and + yet it was insufficent to hold the method */ + if(cache->freeStart == + ((unsigned char *)(cache->pages[cache->numPages - 1])) && + cache->freeEnd == (cache->freeStart + cache->pageSize)) + { + return JIT_CACHE_END_TOO_BIG; + } + else + { + cache->needRestart = 1; + return JIT_CACHE_END_RESTART; + } + } + + /* Terminate the debug information and flush it */ + if(cache->firstDebug || cache->debugLen) + { + WriteCacheDebug(posn, -1, -1); + if(cache->debugLen) + { + FlushCacheDebug(posn); + } + } + + /* Flush the position information back to the cache */ + cache->freeStart = posn->ptr; + cache->freeEnd = posn->limit; + + /* Update the last method region block and then + add all method regions to the lookup tree */ + method = cache->method; + if(method) + { + method->end = posn->ptr; + do + { + method->debug = cache->firstDebug; + next = method->right; + AddToLookupTree(cache, method); + method = next; + } + while(method != 0); + cache->method = 0; + } + + /* The method is ready to go */ + return JIT_CACHE_END_OK; +} + +void *_jit_cache_alloc(jit_cache_posn *posn, unsigned long size) +{ + unsigned char *ptr; + + /* Bail out if the request is too big to ever be satisfiable */ + if(size > (unsigned long)(posn->limit - posn->ptr)) + { + posn->ptr = posn->limit; + return 0; + } + + /* Allocate memory from the top of the free region, so that it + does not overlap with the method code being written at the + bottom of the free region */ + ptr = (unsigned char *)(((jit_nuint)(posn->limit - size)) & + ~(((jit_nuint)JIT_BEST_ALIGNMENT) - 1)); + if(ptr < posn->ptr) + { + /* When we aligned the block, it caused an overflow */ + posn->ptr = posn->limit; + return 0; + } + + /* Allocate the block and return it */ + posn->limit = ptr; + return (void *)ptr; +} + +void *_jit_cache_alloc_no_method + (jit_cache_t cache, unsigned long size, unsigned long align) +{ + unsigned char *ptr; + + /* Bail out if the request is too big to ever be satisfiable */ + if(size > (unsigned long)(cache->freeEnd - cache->freeStart)) + { + AllocCachePage(cache); + if(size > (unsigned long)(cache->freeEnd - cache->freeStart)) + { + return 0; + } + } + + /* Allocate memory from the top of the free region, so that it + does not overlap with the method code being written at the + bottom of the free region */ + ptr = (unsigned char *)(((jit_nuint)(cache->freeEnd - size)) & + ~(((jit_nuint)align) - 1)); + if(ptr < cache->freeStart) + { + /* When we aligned the block, it caused an overflow */ + return 0; + } + + /* Allocate the block and return it */ + cache->freeEnd = ptr; + return (void *)ptr; +} + +void _jit_cache_align(jit_cache_posn *posn, int align, int diff, int nop) +{ + jit_nuint current; + jit_nuint next; + + /* Determine the location of the next alignment boundary */ + if(align <= 1) + { + align = 1; + } + current = (jit_nuint)(posn->ptr); + next = (current + ((jit_nuint)align) - 1) & + ~(((jit_nuint)align) - 1); + if(current == next || (next - current) >= (jit_nuint)diff) + { + return; + } + + /* Detect overflow of the free memory region */ + if(next > ((jit_nuint)(posn->limit))) + { + posn->ptr = posn->limit; + return; + } + +#ifndef jit_should_pad + /* Fill from "current" to "next" with nop bytes */ + while(current < next) + { + *((posn->ptr)++) = (unsigned char)nop; + ++current; + } +#else + /* Use CPU-specific padding, because it may be more efficient */ + _jit_pad_buffer((unsigned char *)current, (int)(next - current)); +#endif +} + +void _jit_cache_mark_bytecode(jit_cache_posn *posn, unsigned long offset) +{ + WriteCacheDebug(posn, (long)offset, + (long)(posn->ptr - posn->cache->start)); +} + +void _jit_cache_new_region(jit_cache_posn *posn, void *cookie) +{ + jit_cache_method_t method; + jit_cache_method_t newMethod; + + /* Fetch the current method information block */ + method = posn->cache->method; + if(!method) + { + return; + } + + /* If the current region starts here, then simply update it */ + if(method->start == posn->ptr) + { + method->cookie = cookie; + return; + } + + /* Close off the current method region */ + method->end = posn->ptr; + + /* Allocate a new method region block and initialise it */ + newMethod = (jit_cache_method_t) + _jit_cache_alloc(posn, sizeof(struct jit_cache_method)); + if(!newMethod) + { + return; + } + newMethod->method = method->method; + newMethod->cookie = cookie; + newMethod->start = posn->ptr; + newMethod->end = posn->ptr; + + /* Attach the new region to the cache */ + newMethod->left = 0; + newMethod->right = method; + posn->cache->method = newMethod; +} + +void _jit_cache_set_cookie(jit_cache_posn *posn, void *cookie) +{ + if(posn->cache->method) + { + posn->cache->method->cookie = cookie; + } +} + +void *_jit_cache_get_method(jit_cache_t cache, void *pc, void **cookie) +{ + jit_cache_method_t node = cache->head.right; + while(node != &(cache->nil)) + { + if(((unsigned char *)pc) < node->start) + { + node = GetLeft(node); + } + else if(((unsigned char *)pc) >= node->end) + { + node = GetRight(node); + } + else + { + if(cookie) + { + *cookie = node->cookie; + } + return node->method; + } + } + return 0; +} + +/* + * Count the number of methods in a sub-tree. + */ +static unsigned long CountMethods(jit_cache_method_t node, + jit_cache_method_t nil, + void **prev) +{ + unsigned long num; + + /* Bail out if we've reached a leaf */ + if(node == nil) + { + return 0; + } + + /* Count the number of methods in the left sub-tree */ + num = CountMethods(GetLeft(node), nil, prev); + + /* Process the current node */ + if(node->method != 0 && node->method != *prev) + { + ++num; + *prev = node->method; + } + + /* Count the number of methods in the right sub-tree */ + return num + CountMethods(GetRight(node), nil, prev); +} + +/* + * Fill a list with methods. + */ +static unsigned long FillMethodList(void **list, + jit_cache_method_t node, + jit_cache_method_t nil, + void **prev) +{ + unsigned long num; + + /* Bail out if we've reached a leaf */ + if(node == nil) + { + return 0; + } + + /* Process the methods in the left sub-tree */ + num = FillMethodList(list, GetLeft(node), nil, prev); + + /* Process the current node */ + if(node->method != 0 && node->method != *prev) + { + list[num] = node->method; + ++num; + *prev = node->method; + } + + /* Process the methods in the right sub-tree */ + return num + FillMethodList(list + num, GetRight(node), nil, prev); +} + +void **_jit_cache_get_method_list(jit_cache_t cache) +{ + void *prev; + unsigned long num; + void **list; + + /* Count the number of distinct methods in the tree */ + prev = 0; + num = CountMethods(cache->head.right, &(cache->nil), &prev); + + /* Allocate a list to hold all of the method descriptors */ + list = (void **)jit_malloc((num + 1) * sizeof(void *)); + if(!list) + { + return 0; + } + + /* Fill the list with methods and then return it */ + prev = 0; + FillMethodList(list, cache->head.right, &(cache->nil), &prev); + list[num] = 0; + return list; +} + +/* + * Temporary structure for iterating over a method's debug list. + */ +typedef struct +{ + jit_cache_debug_t list; + UncompressReader reader; + +} jit_cache_debug_iter; + +/* + * Initialize a debug information list iterator for a method. + */ +static void InitDebugIter(jit_cache_debug_iter *iter, + jit_cache_t cache, void *start) +{ + jit_cache_method_t node = cache->head.right; + while(node != &(cache->nil)) + { + if(((unsigned char *)start) < node->start) + { + node = GetLeft(node); + } + else if(((unsigned char *)start) >= node->end) + { + node = GetRight(node); + } + else + { + iter->list = node->debug; + if(iter->list) + { + iter->reader.data = (unsigned char *)(iter->list + 1); + iter->reader.len = JIT_CACHE_DEBUG_SIZE; + iter->reader.error = 0; + } + return; + } + } + iter->list = 0; +} + +/* + * Get the next debug offset pair from a debug information list. + * Returns non-zero if OK, or zero at the end of the list. + */ +static int GetNextDebug(jit_cache_debug_iter *iter, unsigned long *offset, + unsigned long *nativeOffset) +{ + long value; + while(iter->list) + { + value = UncompressInt(&(iter->reader)); + if(value == -1) + { + return 0; + } + else if(value != -2) + { + *offset = (unsigned long)value; + *nativeOffset = (unsigned long)(UncompressInt(&(iter->reader))); + return 1; + } + iter->list = iter->list->next; + if(iter->list) + { + iter->reader.data = (unsigned char *)(iter->list + 1); + iter->reader.len = JIT_CACHE_DEBUG_SIZE; + iter->reader.error = 0; + } + } + return 0; +} + +unsigned long _jit_cache_get_native(jit_cache_t cache, void *start, + unsigned long offset, int exact) +{ + jit_cache_debug_iter iter; + unsigned long ofs, nativeOfs; + unsigned long prevNativeOfs = JIT_CACHE_NO_OFFSET; + + /* Search for the bytecode offset */ + InitDebugIter(&iter, cache, start); + while(GetNextDebug(&iter, &ofs, &nativeOfs)) + { + if(exact) + { + if(ofs == offset) + { + return nativeOfs; + } + } + else if(ofs > offset) + { + return prevNativeOfs; + } + prevNativeOfs = nativeOfs; + } + return JIT_CACHE_NO_OFFSET; +} + +unsigned long _jit_cache_get_bytecode(jit_cache_t cache, void *start, + unsigned long offset, int exact) +{ + jit_cache_debug_iter iter; + unsigned long ofs, nativeOfs; + unsigned long prevOfs = JIT_CACHE_NO_OFFSET; + + /* Search for the native offset */ + InitDebugIter(&iter, cache, start); + while(GetNextDebug(&iter, &ofs, &nativeOfs)) + { + if(exact) + { + if(nativeOfs == offset) + { + return ofs; + } + } + else if(nativeOfs > offset) + { + return prevOfs; + } + prevOfs = ofs; + } + return JIT_CACHE_NO_OFFSET; +} + +unsigned long _jit_cache_get_size(jit_cache_t cache) +{ + return (cache->numPages * cache->pageSize) - + (cache->freeEnd - cache->freeStart); +} + +/* + +Using the cache +--------------- + +To output the code for a method, first call _jit_cache_start_method: + + jit_cache_posn posn; + void *start; + + start = _jit_cache_start_method(cache, &posn, METHOD_ALIGNMENT, method); + +"METHOD_ALIGNMENT" is used to align the start of the method on an +appropriate boundary for the target CPU. Use the value 1 if no +special alignment is required. Note: this value is a hint to the +cache - it may alter the alignment value. + +"method" is a value that uniquely identifies the method that is being +translated. Usually this is the "jit_function_t" pointer. + +The function initializes the "posn" structure, and returns the starting +address for the method. If the function returns NULL, then it indicates +that the cache is full and further method translation is not possible. + +To write code to the method, use the following: + + jit_cache_byte(&posn, value); + jit_cache_word16(&posn, value); + jit_cache_word32(&posn, value); + jit_cache_native(&posn, value); + jit_cache_word64(&posn, value); + +These macros write the value to cache and then update the current +position. If the macros detect the end of the current cache page, +they will flag overflow, but otherwise do nothing (overflow is +flagged when posn->ptr == posn->limit). The current position +in the method can be obtained using "jit_cache_get_posn". + +Some CPU optimization guides recommend that labels should be aligned. +This can be achieved using _jit_cache_align. + +Once the method code has been output, call _jit_cache_end_method to finalize +the process. This function returns one of three result codes: + + JIT_CACHE_END_OK The translation process was successful. + JIT_CACHE_END_RESTART The cache page overflowed. It is necessary + to restart the translation process from + the beginning (_jit_cache_start_method). + JIT_CACHE_END_TOO_BIG The cache page overflowed, but the method + is too big to fit and a restart won't help. + +The caller should repeatedly translate the method while _jit_cache_end_method +continues to return JIT_CACHE_END_RESTART. Normally there will be no +more than a single request to restart, but the caller should not rely +upon this. The cache algorithm guarantees that the restart loop will +eventually terminate. + +Cache data structure +-------------------- + +The cache consists of one or more "cache pages", which contain method +code and auxillary data. The default size for a cache page is 128k +(JIT_CACHE_PAGE_SIZE). The size is adjusted to be a multiple +of the system page size (usually 4k), and then stored in "pageSize". + +Method code is written into a cache page starting at the bottom of the +page, and growing upwards. Auxillary data is written into a cache page +starting at the top of the page, and growing downwards. When the two +regions meet, a new cache page is allocated and the process restarts. + +No method, plus its auxillary data, can be greater in size than one +cache page. The default should be sufficient for normal applications, +but is easy to increase should the need arise. + +Each method has one or more jit_cache_method auxillary data blocks associated +with it. These blocks indicate the start and end of regions within the +method. Normally these regions correspond to exception "try" blocks, or +regular code between "try" blocks. + +The jit_cache_method blocks are organised into a red-black tree, which +is used to perform fast lookups by address (_jit_cache_get_method). These +lookups are used when walking the stack during exceptions or security +processing. + +Each method can also have offset information associated with it, to map +between native code addresses and offsets within the original bytecode. +This is typically used to support debugging. Offset information is stored +as auxillary data, attached to the jit_cache_method block. + +Threading issues +---------------- + +Writing a method to the cache, querying a method by address, or querying +offset information for a method, are not thread-safe. The caller should +arrange for a cache lock to be acquired prior to performing these +operations. + +Executing methods from the cache is thread-safe, as the method code is +fixed in place once it has been written. + +Note: some CPU's require that a special cache flush instruction be +performed before executing method code that has just been written. +This is especially important in SMP environments. It is the caller's +responsibility to perform this flush operation. + +We do not provide locking or CPU flush capabilities in the cache +implementation itself, because the caller may need to perform other +duties before flushing the CPU cache or releasing the lock. + +The following is the recommended way to map an "jit_function_t" pointer +to a starting address for execution: + + Look in "jit_function_t" to see if we already have a starting address. + If so, then bail out. + Acquire the cache lock. + Check again to see if we already have a starting address, just + in case another thread got here first. If so, then release + the cache lock and bail out. + Translate the method. + Update the "jit_function_t" structure to contain the starting address. + Force a CPU cache line flush. + Release the cache lock. + +Why aren't methods flushed when the cache fills up? +--------------------------------------------------- + +In this cache implementation, methods are never "flushed" when the +cache becomes full. Instead, all translation stops. This is not a bug. +It is a feature. + +In a multi-threaded environment, it is impossible to know if some +other thread is executing the code of a method that may be a candidate +for flushing. Impossible that is unless one introduces a huge number +of read-write locks, one per method, to prevent a method from being +flushed. The read locks must be acquired on entry to a method, and +released on exit. The write locks are acquired prior to translation. + +The overhead of introducing all of these locks and the associated cache +data structures is very high. The only safe thing to do is to assume +that once a method has been translated, its code must be fixed in place +for all time. + +We've looked at the code for other Free Software and Open Source JIT's, +and they all use a constantly-growing method cache. No one has found +a solution to this problem, it seems. Suggestions are welcome. + +To prevent the cache from chewing up all of system memory, it is possible +to set a limit on how far it will grow. Once the limit is reached, out +of memory will be reported and there is no way to recover. + +*/ + +#ifdef __cplusplus +}; +#endif diff --git a/jit/jit-cache.h b/jit/jit-cache.h new file mode 100644 index 0000000..03b339b --- /dev/null +++ b/jit/jit-cache.h @@ -0,0 +1,271 @@ +/* + * jit-cache.h - Translated method cache implementation. + * + * Copyright (C) 2002, 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_CACHE_H +#define _JIT_CACHE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Opaque method cache type. + */ +typedef struct jit_cache *jit_cache_t; + +/* + * Writing position within a cache. + */ +typedef struct +{ + jit_cache_t cache; /* Cache this position is attached to */ + unsigned char *ptr; /* Current code pointer */ + unsigned char *limit; /* Limit of the current page */ + +} jit_cache_posn; + +/* + * Create a method cache. Returns NULL if out of memory. + * If "limit" is non-zero, then it specifies the maximum + * size of the cache in bytes. If "cache_page_size" is + * non-zero, then it indicates the size of each cache page. + */ +jit_cache_t _jit_cache_create(long limit, long cache_page_size); + +/* + * Destroy a method cache. + */ +void _jit_cache_destroy(jit_cache_t cache); + +/* + * Determine if the cache is full. The "posn" value should + * be supplied while translating a method, or be NULL otherwise. + */ +int _jit_cache_is_full(jit_cache_t cache, jit_cache_posn *posn); + +/* + * Start output of a method, returning a cache position. + * The "align" value indicates the default alignment for + * the start of the method. The "cookie" value is a + * cookie for referring to the method. Returns the + * method entry point, or NULL if the cache is full. + */ +void *_jit_cache_start_method(jit_cache_t cache, jit_cache_posn *posn, + int align, void *cookie); + +/* + * Return values for "_jit_cache_end_method". + */ +#define JIT_CACHE_END_OK 0 /* Function is OK */ +#define JIT_CACHE_END_RESTART 1 /* Restart is required */ +#define JIT_CACHE_END_TOO_BIG 2 /* Function is too big for the cache */ + +/* + * End output of a method. Returns zero if a restart. + */ +int _jit_cache_end_method(jit_cache_posn *posn); + +/* + * Allocate "size" bytes of storage in the method cache's + * auxillary data area. Returns NULL if insufficient space + * to satisfy the request. It may be possible to satisfy + * the request after a restart. + */ +void *_jit_cache_alloc(jit_cache_posn *posn, unsigned long size); + +/* + * Allocate "size" bytes of storage when we aren't currently + * translating a method. + */ +void *_jit_cache_alloc_no_method + (jit_cache_t cache, unsigned long size, unsigned long align); + +/* + * Align the method code on a particular boundary if the + * difference between the current position and the aligned + * boundary is less than "diff". The "nop" value is used + * to pad unused bytes. + */ +void _jit_cache_align(jit_cache_posn *posn, int align, int diff, int nop); + +/* + * Mark the current position with a bytecode offset value. + */ +void _jit_cache_mark_bytecode(jit_cache_posn *posn, unsigned long offset); + +/* + * Change to a new exception region within the current method. + * The cookie will typically be NULL if no exception region. + */ +void _jit_cache_new_region(jit_cache_posn *posn, void *cookie); + +/* + * Set the exception region cookie for the current region. + */ +void _jit_cache_set_cookie(jit_cache_posn *posn, void *cookie); + +/* + * Find the method that is associated with a particular + * program counter. Returns NULL if the PC is not associated + * with a method within the cache. The exception region + * cookie is returned in "*cookie", if "cookie" is not NULL. + */ +void *_jit_cache_get_method(jit_cache_t cache, void *pc, void **cookie); + +/* + * Get a list of all method that are presently in the cache. + * The list is terminated by a NULL, and must be free'd with + * "ILFree". Returns NULL if out of memory. + */ +void **_jit_cache_get_method_list(jit_cache_t cache); + +/* + * Get the native offset that is associated with a bytecode + * offset within a method. The value "start" indicates the + * entry point for the method. Returns JIT_CACHE_NO_OFFSET + * if the native offset could not be determined. + */ +#define JIT_CACHE_NO_OFFSET (~((unsigned long)0)) +unsigned long _jit_cache_get_native(jit_cache_t cache, void *start, + unsigned long offset, int exact); + +/* + * Get the bytecode offset that is associated with a native + * offset within a method. The value "start" indicates the + * entry point for the method. Returns JIT_CACHE_NO_OFFSET + * if the bytecode offset could not be determined. + */ +unsigned long _jit_cache_get_bytecode(jit_cache_t cache, void *start, + unsigned long offset, int exact); + +/* + * Get the number of bytes currently in use in the method cache. + */ +unsigned long _jit_cache_get_size(jit_cache_t cache); + +/* + * Convert a return address into a program counter value + * that can be used with "_jit_cache_get_method". Normally + * return addresses point to the next instruction after + * an instruction that falls within a method region. This + * macro corrects for the "off by 1" address. + */ +#define jit_cache_return_to_pc(addr) \ + ((void *)(((unsigned char *)(addr)) - 1)) + +/* + * Output a single byte to the current method. + */ +#define jit_cache_byte(posn,value) \ + do { \ + if((posn)->ptr < (posn)->limit) \ + { \ + *(((posn)->ptr)++) = (unsigned char)(value); \ + } \ + } while (0) + +/* + * Output a 16-bit word to the current method. + */ +#define jit_cache_word16(posn,value) \ + do { \ + if(((posn)->ptr + 1) < (posn)->limit) \ + { \ + *((jit_ushort *)((posn)->ptr)) = (jit_ushort)(value); \ + (posn)->ptr += 2; \ + } \ + else \ + { \ + (posn)->ptr = (posn)->limit; \ + } \ + } while (0) + +/* + * Output a 32-bit word to the current method. + */ +#define jit_cache_word32(posn,value) \ + do { \ + if(((posn)->ptr + 3) < (posn)->limit) \ + { \ + *((jit_uint *)((posn)->ptr)) = (jit_uint)(value); \ + (posn)->ptr += 4; \ + } \ + else \ + { \ + (posn)->ptr = (posn)->limit; \ + } \ + } while (0) + +/* + * Output a native word to the current method. + */ +#define jit_cache_native(posn,value) \ + do { \ + if(((posn)->ptr + sizeof(jit_nuint) - 1) < (posn)->limit) \ + { \ + *((jit_nuint *)((posn)->ptr)) = (jit_nuint)(value); \ + (posn)->ptr += sizeof(jit_nuint); \ + } \ + else \ + { \ + (posn)->ptr = (posn)->limit; \ + } \ + } while (0) + +/* + * Output a 64-bit word to the current method. + */ +#define jit_cache_word64(posn,value) \ + do { \ + if(((posn)->ptr + 7) < (posn)->limit) \ + { \ + *((jit_ulong *)((posn)->ptr)) = (jit_ulong)(value); \ + (posn)->ptr += 8; \ + } \ + else \ + { \ + (posn)->ptr = (posn)->limit; \ + } \ + } while (0) + +/* + * Get the output position within the current method. + */ +#define jit_cache_get_posn(posn) ((posn)->ptr) + +/* + * Determine if there is sufficient space for N bytes in the current method. + */ +#define jit_cache_check_for_n(posn,n) \ + (((posn)->ptr + (n)) <= (posn)->limit) + +/* + * Mark the cache as full. + */ +#define jit_cache_mark_full(posn) \ + do { \ + (posn)->ptr = (posn)->limit; \ + } while (0) + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_CACHE_H */ diff --git a/jit/jit-context.c b/jit/jit-context.c new file mode 100644 index 0000000..5c8abbc --- /dev/null +++ b/jit/jit-context.c @@ -0,0 +1,260 @@ +/* + * jit-context.c - Functions for manipulating JIT contexts. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-cache.h" + +/*@ + +Everything that is done with @code{libjit} is done relative to a context. +It is possible to have more than one context at a time - each acts as an +independent environment for compiling and managing code. + +When you want to compile a function, you create it with +@code{jit_function_create}, and then populate its body with +calls to the value and instruction functions. See @xref{Values}, and +@ref{Instructions} for more information on how to do this. + +@section Using libjit in a multi-threaded environment + +The library does not handle the creation, management, and destruction +of threads itself. It is up to the front-end environment to take +care of that. But the library is thread-aware, as long as you take +some very simple steps. + +In a multi-threaded environment, you must ensure that only one +thread can build functions at any one time. Otherwise the +JIT's context may become corrupted. To protect the system, +you should call @code{jit_context_build_start} before +creating the function. And then call @code{jit_context_build_end} +once the function has been fully compiled. + +You can compile multiple functions during the one build process +if you wish, which is the normal case when compiling a class. + +It is usually a good idea to suspend the finalization of +garbage-collected objects while function building is in progress. +Otherwise you may get a deadlock when the finalizer thread tries +to call the builder to compile a finalization routine. Suspension +of finalization is the responsibility of the caller. + +@section Context functions +@cindex jit-context.h + +The following functions are available to create, manage, and +ultimately destroy JIT contexts: + +@*/ + +/*@ + * @deftypefun jit_context_t jit_context_create (void) + * Create a new context block for the JIT. Returns NULL + * if out of memory. + * @end deftypefun +@*/ +jit_context_t jit_context_create(void) +{ + jit_context_t context; + + /* Make sure that the JIT is initialized */ + jit_init(); + + /* Allocate memory for the context */ + context = jit_cnew(struct _jit_context); + if(!context) + { + return 0; + } + + /* Initialize the context and return it */ + jit_mutex_create(&(context->builder_lock)); + jit_mutex_create(&(context->cache_lock)); + context->functions = 0; + context->last_function = 0; + return context; +} + +/*@ + * @deftypefun void jit_context_destroy (jit_context_t context) + * Destroy a JIT context block and everything that is associated with it. + * It is very important that no threads within the program are currently + * running compiled code when this function is called. + * @end deftypefun +@*/ +void jit_context_destroy(jit_context_t context) +{ + if(context) + { + while(context->functions != 0) + { + _jit_function_destroy(context->functions); + } + if(context->cache) + { + _jit_cache_destroy(context->cache); + } + jit_mutex_destroy(&(context->cache_lock)); + jit_mutex_destroy(&(context->builder_lock)); + jit_free(context); + } +} + +/*@ + * @deftypefun int jit_context_supports_threads (jit_context_t context) + * Determine if the JIT supports threads. + * @end deftypefun +@*/ +int jit_context_supports_threads(jit_context_t context) +{ + return JIT_THREADS_SUPPORTED; +} + +/*@ + * @deftypefun void jit_context_build_start (jit_context_t context) + * This routine should be called before you start building a function + * to be JIT'ed. It acquires a lock on the context to prevent other + * threads from accessing the build process, since only one thread + * can be performing build operations at any one time. + * @end deftypefun +@*/ +void jit_context_build_start(jit_context_t context) +{ + jit_mutex_lock(&(context->builder_lock)); +} + +/*@ + * @deftypefun void jit_context_build_end (jit_context_t context) + * This routine should be called once you have finished building + * and compiling a function and are ready to resume normal execution. + * This routine will release the build lock, allowing other threads + * that are waiting on the builder to proceed. + * @end deftypefun +@*/ +void jit_context_build_end(jit_context_t context) +{ + jit_mutex_unlock(&(context->builder_lock)); +} + +/*@ + * @deftypefun int jit_context_set_meta (jit_context_t context, int type, {void *} data, jit_meta_free_func free_data) + * Tag a context with some metadata. Returns zero if out of memory. + * + * Metadata may be used to store dependency graphs, branch prediction + * information, or any other information that is useful to optimizers + * or code generators. It can also be used by higher level user code + * to store information about the context that is specific to the + * virtual machine or language. + * + * If the @code{type} already has some metadata associated with it, then + * the previous value will be freed. + * @end deftypefun +@*/ +int jit_context_set_meta + (jit_context_t context, int type, void *data, + jit_meta_free_func free_data) +{ + return jit_meta_set(&(context->meta), type, data, free_data, 0); +} + +/*@ + * @deftypefun int jit_context_set_meta_numeric (jit_context_t context, int type, jit_nuint data) + * Tag a context with numeric metadata. Returns zero if out of memory. + * This function is more convenient for accessing the context's + * special option values: + * + * @table @code + * @vindex JIT_OPTION_CACHE_LIMIT + * @item JIT_OPTION_CACHE_LIMIT + * A numeric option that indicates the maximum size in bytes of the function + * cache. If set to zero (the default), the function cache is unlimited + * in size. + * + * @vindex JIT_OPTION_CACHE_PAGE_SIZE + * @item JIT_OPTION_CACHE_PAGE_SIZE + * A numeric option that indicates the size in bytes of a single page in the + * function cache. Memory is allocated for the cache in chunks of + * this size. If set to zero, the cache page size is set to an + * internally-determined default (usually 128k). The cache page size + * also determines the maximum size of a single compiled function. + * + * @vindex JIT_OPTION_PRE_COMPILE + * @item JIT_OPTION_PRE_COMPILE + * A numeric option that indicates that this context is being used + * for pre-compilation if it is set to a non-zero value. Code within + * pre-compiled contexts cannot be executed directly. Instead, they + * can be written out to disk in ELF format to be reloaded at + * some future time. + * @end table + * + * Metadata type values of 10000 or greater are reserved for internal use. + * @end deftypefun +@*/ +int jit_context_set_meta_numeric + (jit_context_t context, int type, jit_nuint data) +{ + return jit_meta_set(&(context->meta), type, (void *)data, 0, 0); +} + +/*@ + * @deftypefun {void *} jit_context_get_meta (jit_context_t context, int type) + * Get the metadata associated with a particular tag. Returns NULL + * if @code{type} does not have any metadata associated with it. + * @end deftypefun +@*/ +void *jit_context_get_meta(jit_context_t context, int type) +{ + return jit_meta_get(context->meta, type); +} + +/*@ + * @deftypefun jit_nuint jit_context_get_meta_numeric (jit_context_t context, int type) + * Get the metadata associated with a particular tag. Returns zero + * if @code{type} does not have any metadata associated with it. + * This version is more convenient for the pre-defined numeric option values. + * @end deftypefun +@*/ +jit_nuint jit_context_get_meta_numeric(jit_context_t context, int type) +{ + return (jit_nuint)jit_meta_get(context->meta, type); +} + +/*@ + * @deftypefun void jit_context_free_meta (jit_context_t context, int type) + * Free metadata of a specific type on a context. Does nothing if + * the @code{type} does not have any metadata associated with it. + * @end deftypefun +@*/ +void jit_context_free_meta(jit_context_t context, int type) +{ + jit_meta_free(&(context->meta), type); +} + +struct jit_cache *_jit_context_get_cache(jit_context_t context) +{ + if(!(context->cache)) + { + context->cache = _jit_cache_create + ((long)jit_context_get_meta_numeric + (context, JIT_OPTION_CACHE_LIMIT), + (long)jit_context_get_meta_numeric + (context, JIT_OPTION_CACHE_PAGE_SIZE)); + } + return context->cache; +} diff --git a/jit/jit-dump.c b/jit/jit-dump.c new file mode 100644 index 0000000..7d66792 --- /dev/null +++ b/jit/jit-dump.c @@ -0,0 +1,759 @@ +/* + * jit-dump.c - Functions for dumping JIT structures, for debugging. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" +#include + +#if defined(JIT_BACKEND_INTERP) + #include "jit-interp.h" +#endif + +/*@ + * @deftypefun void jit_dump_type ({FILE *} stream, jit_type_t type) + * Dump the name of a type to a stdio stream. + * @end deftypefun +@*/ +void jit_dump_type(FILE *stream, jit_type_t type) +{ + const char *name; + type = jit_type_remove_tags(type); + if(!type || !stream) + { + return; + } + switch(type->kind) + { + case JIT_TYPE_VOID: name = "void"; break; + case JIT_TYPE_SBYTE: name = "sbyte"; break; + case JIT_TYPE_UBYTE: name = "ubyte"; break; + case JIT_TYPE_SHORT: name = "short"; break; + case JIT_TYPE_USHORT: name = "ushort"; break; + case JIT_TYPE_INT: name = "int"; break; + case JIT_TYPE_UINT: name = "uint"; break; + case JIT_TYPE_NINT: name = "nint"; break; + case JIT_TYPE_NUINT: name = "nuint"; break; + case JIT_TYPE_LONG: name = "long"; break; + case JIT_TYPE_ULONG: name = "ulong"; break; + case JIT_TYPE_FLOAT32: name = "float32"; break; + case JIT_TYPE_FLOAT64: name = "float64"; break; + case JIT_TYPE_NFLOAT: name = "nfloat"; break; + + case JIT_TYPE_STRUCT: + { + fprintf(stream, "struct<%u>", + (unsigned int)(jit_type_get_size(type))); + return; + } + /* Not reached */ + + case JIT_TYPE_UNION: + { + fprintf(stream, "union<%u>", + (unsigned int)(jit_type_get_size(type))); + return; + } + /* Not reached */ + + case JIT_TYPE_SIGNATURE: name = "signature"; break; + case JIT_TYPE_PTR: name = "ptr"; break; + default: name = ""; break; + } + fputs(name, stream); +} + +/* + * Format an integer value of arbitrary precision. + */ +static char *format_integer(char *buf, int is_neg, jit_ulong value) +{ + buf += 64; + *(--buf) = '\0'; + if(value == 0) + { + *(--buf) = '0'; + } + else + { + while(value != 0) + { + *(--buf) = '0' + (int)(value % 10); + value /= 10; + } + } + if(is_neg) + { + *(--buf) = '-'; + } + return buf; +} + +/*@ + * @deftypefun void jit_dump_value ({FILE *} stream, jit_function_t func, jit_value_t value, const char *prefix) + * Dump the name of a value to a stdio stream. If @code{prefix} is not + * NULL, then it indicates a type prefix to add to the value name. + * If @code{prefix} is NULL, then this function intuits the type prefix. + * @end deftypefun +@*/ +void jit_dump_value(FILE *stream, jit_function_t func, jit_value_t value, const char *prefix) +{ + jit_pool_block_t block; + unsigned int block_size; + unsigned int posn; + + /* Bail out if we have insufficient informaition for the dump */ + if(!stream || !func || !(func->builder) || !value) + { + return; + } + + /* Handle constants and non-local variables */ + if(value->is_constant) + { + jit_constant_t const_value; + char buf[64]; + char *name; + const_value = jit_value_get_constant(value); + switch((jit_type_promote_int + (jit_type_normalize(const_value.type)))->kind) + { + case JIT_TYPE_INT: + { + if(const_value.un.int_value < 0) + { + name = format_integer + (buf, 1, (jit_ulong)(jit_uint) + (-(const_value.un.int_value))); + } + else + { + name = format_integer + (buf, 0, (jit_ulong)(jit_uint) + (const_value.un.int_value)); + } + } + break; + + case JIT_TYPE_UINT: + { + name = format_integer + (buf, 0, (jit_ulong)(const_value.un.uint_value)); + } + break; + + case JIT_TYPE_LONG: + { + if(const_value.un.long_value < 0) + { + name = format_integer + (buf, 1, (jit_ulong)(-(const_value.un.long_value))); + } + else + { + name = format_integer + (buf, 0, (jit_ulong)(const_value.un.long_value)); + } + } + break; + + case JIT_TYPE_ULONG: + { + name = format_integer(buf, 0, const_value.un.ulong_value); + } + break; + + case JIT_TYPE_FLOAT32: + { + jit_snprintf(buf, sizeof(buf), "%g", + (double)(const_value.un.float32_value)); + name = buf; + } + break; + + case JIT_TYPE_FLOAT64: + { + jit_snprintf(buf, sizeof(buf), "%g", + (double)(const_value.un.float64_value)); + name = buf; + } + break; + + case JIT_TYPE_NFLOAT: + { + jit_snprintf(buf, sizeof(buf), "%g", + (double)(const_value.un.nfloat_value)); + name = buf; + } + break; + + default: + { + name = ""; + } + break; + } + fputs(name, stream); + return; + } + else if(value->is_local && value->block->func != func) + { + /* Accessing a local variable in an outer function frame */ + int scope = 0; + while(func && func->builder && func != value->block->func) + { + ++scope; + func = func->nested_parent; + } + fprintf(stream, "{%d}", scope); + if(!func || !(func->builder)) + { + return; + } + } + + /* Intuit the prefix if one was not supplied */ + if(!prefix) + { + switch(jit_type_normalize(jit_value_get_type(value))->kind) + { + case JIT_TYPE_VOID: prefix = "v"; break; + case JIT_TYPE_SBYTE: prefix = "i"; break; + case JIT_TYPE_UBYTE: prefix = "i"; break; + case JIT_TYPE_SHORT: prefix = "i"; break; + case JIT_TYPE_USHORT: prefix = "i"; break; + case JIT_TYPE_INT: prefix = "i"; break; + case JIT_TYPE_UINT: prefix = "i"; break; + case JIT_TYPE_LONG: prefix = "l"; break; + case JIT_TYPE_ULONG: prefix = "l"; break; + case JIT_TYPE_FLOAT32: prefix = "f"; break; + case JIT_TYPE_FLOAT64: prefix = "d"; break; + case JIT_TYPE_NFLOAT: prefix = "D"; break; + case JIT_TYPE_STRUCT: prefix = "s"; break; + case JIT_TYPE_UNION: prefix = "u"; break; + default: prefix = "?"; break; + } + } + + /* Get the position of the value within the function's value pool */ + block = func->builder->value_pool.blocks; + block_size = func->builder->value_pool.elem_size * + func->builder->value_pool.elems_per_block; + posn = 1; + while(block != 0) + { + if(((char *)value) >= block->data && + ((char *)value) < (block->data + block_size)) + { + posn += (((char *)value) - block->data) / + func->builder->value_pool.elem_size; + break; + } + posn += func->builder->value_pool.elems_per_block; + block = block->next; + } + + /* Dump the prefix and the position, as the value's final name */ + fprintf(stream, "%s%u", prefix, posn); +} + +/* + * Dump a temporary value, prefixed by its type. + */ +static void dump_value(FILE *stream, jit_function_t func, + jit_value_t value, int type) +{ + /* Normalize the type, so that it reflects JIT_OPCODE_DEST_xxx values */ + if((type & JIT_OPCODE_SRC1_MASK) != 0) + { + type >>= 4; + } + if((type & JIT_OPCODE_SRC2_MASK) != 0) + { + type >>= 8; + } + + /* Dump the value, prefixed appropriately */ + switch(type) + { + case JIT_OPCODE_DEST_INT: + { + jit_dump_value(stream, func, value, "i"); + } + break; + + case JIT_OPCODE_DEST_LONG: + { + jit_dump_value(stream, func, value, "l"); + } + break; + + case JIT_OPCODE_DEST_FLOAT32: + { + jit_dump_value(stream, func, value, "f"); + } + break; + + case JIT_OPCODE_DEST_FLOAT64: + { + jit_dump_value(stream, func, value, "d"); + } + break; + + case JIT_OPCODE_DEST_NFLOAT: + { + jit_dump_value(stream, func, value, "D"); + } + break; + + case JIT_OPCODE_DEST_ANY: + { + /* Intuit the prefix from the value if the type is "any" */ + jit_dump_value(stream, func, value, 0); + } + break; + } +} + +/*@ + * @deftypefun void jit_dump_insn ({FILE *} stream, jit_function_t func, jit_value_t value) + * Dump the contents of an instruction to a stdio stream. + * @end deftypefun +@*/ +void jit_dump_insn(FILE *stream, jit_function_t func, jit_insn_t insn) +{ + const char *name; + const char *infix_name; + int opcode, flags; + jit_nint reg; + + /* Bail out if we have insufficient information for the dump */ + if(!stream || !func || !insn) + { + return; + } + + /* Get the opcode details */ + opcode = insn->opcode; + if(opcode < JIT_OP_NOP || opcode >= JIT_OP_NUM_OPCODES) + { + fprintf(stream, "unknown opcode %d\n", opcode); + return; + } + name = jit_opcodes[opcode].name; + flags = jit_opcodes[opcode].flags; + infix_name = 0; + + /* Dump branch, call, or register information */ + if((flags & JIT_OPCODE_IS_BRANCH) != 0) + { + if(opcode == JIT_OP_BR) + { + fprintf(stream, "goto .L%ld", (long)(jit_insn_get_label(insn))); + return; + } + fprintf(stream, "if "); + } + else if((flags & JIT_OPCODE_IS_CALL) != 0) + { + if(insn->value1) + fprintf(stream, "call %s", (const char *)(insn->value1)); + else + fprintf(stream, "call 0x08%lx", (long)(jit_nuint)(insn->dest)); + return; + } + else if((flags & JIT_OPCODE_IS_CALL_EXTERNAL) != 0) + { + if(insn->value1) + fprintf(stream, "call_external %s", + (const char *)(insn->value1)); + else + fprintf(stream, "call_external 0x08%lx", + (long)(jit_nuint)(insn->dest)); + return; + } + else if((flags & JIT_OPCODE_IS_REG) != 0) + { + reg = jit_value_get_nint_constant(jit_insn_get_value2(insn)); + fputs(name, stream); + putc('(', stream); + jit_dump_value(stream, func, jit_insn_get_value1(insn), 0); + fputs(", ", stream); + fputs(_jit_reg_info[(int)reg].name, stream); + putc(')', stream); + return; + } + + /* Output the destination information */ + if((flags & JIT_OPCODE_DEST_MASK) != JIT_OPCODE_DEST_EMPTY && + !jit_insn_dest_is_value(insn)) + { + dump_value(stream, func, jit_insn_get_dest(insn), + flags & JIT_OPCODE_DEST_MASK); + fprintf(stream, " = "); + } + + /* Dump the details of the operation */ + switch(flags & JIT_OPCODE_OPER_MASK) + { + case JIT_OPCODE_OPER_ADD: infix_name = " + "; break; + case JIT_OPCODE_OPER_SUB: infix_name = " - "; break; + case JIT_OPCODE_OPER_MUL: infix_name = " * "; break; + case JIT_OPCODE_OPER_DIV: infix_name = " / "; break; + case JIT_OPCODE_OPER_REM: infix_name = " % "; break; + case JIT_OPCODE_OPER_NEG: infix_name = "-"; break; + case JIT_OPCODE_OPER_AND: infix_name = " & "; break; + case JIT_OPCODE_OPER_OR: infix_name = " | "; break; + case JIT_OPCODE_OPER_XOR: infix_name = " ^ "; break; + case JIT_OPCODE_OPER_NOT: infix_name = "~"; break; + case JIT_OPCODE_OPER_EQ: infix_name = " == "; break; + case JIT_OPCODE_OPER_NE: infix_name = " != "; break; + case JIT_OPCODE_OPER_LT: infix_name = " < "; break; + case JIT_OPCODE_OPER_LE: infix_name = " <= "; break; + case JIT_OPCODE_OPER_GT: infix_name = " > "; break; + case JIT_OPCODE_OPER_GE: infix_name = " >= "; break; + case JIT_OPCODE_OPER_SHL: infix_name = " << "; break; + case JIT_OPCODE_OPER_SHR: infix_name = " >> "; break; + case JIT_OPCODE_OPER_SHR_UN: infix_name = " >>> "; break; + case JIT_OPCODE_OPER_COPY: infix_name = ""; break; + case JIT_OPCODE_OPER_ADDRESS_OF: infix_name = "&"; break; + } + if(infix_name) + { + if((flags & JIT_OPCODE_SRC2_MASK) != 0) + { + /* Binary operation with a special operator name */ + dump_value(stream, func, jit_insn_get_value1(insn), + flags & JIT_OPCODE_SRC1_MASK); + fputs(infix_name, stream); + dump_value(stream, func, jit_insn_get_value2(insn), + flags & JIT_OPCODE_SRC2_MASK); + } + else + { + /* Unary operation with a special operator name */ + fputs(infix_name, stream); + dump_value(stream, func, jit_insn_get_value1(insn), + flags & JIT_OPCODE_SRC1_MASK); + } + } + else + { + /* Not a special operator, so use the opcode name */ + if(!jit_strncmp(name, "br_", 3)) + { + name += 3; + } + fputs(name, stream); + if((flags & (JIT_OPCODE_SRC1_MASK | JIT_OPCODE_SRC2_MASK)) != 0) + { + putc('(', stream); + if(jit_insn_dest_is_value(insn)) + { + dump_value(stream, func, jit_insn_get_dest(insn), + flags & JIT_OPCODE_DEST_MASK); + fputs(", ", stream); + } + dump_value(stream, func, jit_insn_get_value1(insn), + flags & JIT_OPCODE_SRC1_MASK); + if((flags & JIT_OPCODE_SRC2_MASK) != 0) + { + fputs(", ", stream); + dump_value(stream, func, jit_insn_get_value2(insn), + flags & JIT_OPCODE_SRC2_MASK); + } + putc(')', stream); + } + } + + /* Dump the "then" information on a conditional branch */ + if((flags & JIT_OPCODE_IS_BRANCH) != 0) + { + fprintf(stream, " then goto .L%ld", (long)(jit_insn_get_label(insn))); + } +} + +#if defined(JIT_BACKEND_INTERP) + +extern jit_opcode_info_t const _jit_interp_opcodes[JIT_OP_NUM_INTERP_OPCODES]; + +/* + * Dump the interpreted bytecode representation of a function. + */ +static void dump_interp_code(FILE *stream, void **pc) +{ + int opcode; + const jit_opcode_info_t *info; + for(;;) + { + /* Fetch the next opcode */ + opcode = (int)(jit_nint)(*pc); + if(opcode == JIT_OP_END_MARKER) + { + break; + } + + /* Dump the address of the opcode */ + fprintf(stream, "\t%08lX: ", (long)(jit_nint)pc); + ++pc; + + /* Get information about this opcode */ + if(opcode < JIT_OP_NUM_OPCODES) + { + info = &(jit_opcodes[opcode]); + } + else + { + info = &(_jit_interp_opcodes[opcode - JIT_OP_NUM_OPCODES]); + } + + /* Dump the name of the opcode */ + fputs(info->name, stream); + + /* Dump additional parameters from the opcode stream */ + switch(info->flags & JIT_OPCODE_INTERP_ARGS_MASK) + { + case JIT_OPCODE_NINT_ARG: + { + fprintf(stream, " %ld", (long)(jit_nint)(*pc)); + ++pc; + } + break; + + case JIT_OPCODE_NINT_ARG_TWO: + { + fprintf(stream, " %ld, %ld", + (long)(jit_nint)(pc[0]), (long)(jit_nint)(pc[1])); + pc += 2; + } + break; + + case JIT_OPCODE_CONST_LONG: + { + /* TODO */ + } + break; + + case JIT_OPCODE_CONST_FLOAT32: + { + /* TODO */ + } + break; + + case JIT_OPCODE_CONST_FLOAT64: + { + /* TODO */ + } + break; + + case JIT_OPCODE_CONST_NFLOAT: + { + /* TODO */ + } + break; + + case JIT_OPCODE_CALL_INDIRECT_ARGS: + { + /* TODO */ + } + break; + + default: + { + if((info->flags & JIT_OPCODE_IS_BRANCH) != 0) + { + fprintf(stream, " %08lX", + (long)(jit_nint)((pc - 1) + (jit_nint)(*pc))); + ++pc; + } + else if((info->flags & JIT_OPCODE_IS_CALL) != 0) + { + fprintf(stream, " 0x%lX", (long)(jit_nint)(*pc)); + ++pc; + } + else if((info->flags & JIT_OPCODE_IS_CALL_EXTERNAL) != 0) + { + putc(' ', stream); + jit_dump_type(stream, (jit_type_t)(pc[0])); + fprintf(stream, ", 0x%lX, %ld", + (long)(jit_nint)(pc[1]), (long)(jit_nint)(pc[2])); + pc += 3; + } + } + break; + } + + /* Terminate the current disassembly line */ + putc('\n', stream); + } +} + +#endif /* JIT_BACKEND_INTERP */ + +/*@ + * @deftypefun void jit_dump_function ({FILE *} stream, jit_function_t func, {const char *} name) + * Dump the three-address instructions within a function to a stdio stream. + * The @code{name} is attached to the output as a friendly label, but + * has no other significance. + * + * If the function has not been compiled yet, then this will dump the + * three address instructions from the build process. Otherwise it will + * disassemble and dump the compiled native code. + * @end deftypefun +@*/ +void jit_dump_function(FILE *stream, jit_function_t func, const char *name) +{ + jit_block_t block; + jit_insn_iter_t iter; + jit_insn_t insn; + int prev_block; + jit_type_t signature; + unsigned int param; + unsigned int num_params; + jit_value_t value; + + /* Bail out if we don't have sufficient information to dump */ + if(!stream || !func) + { + return; + } + + /* Output the function header */ + if(name) + fprintf(stream, "function %s(", name); + else + fprintf(stream, "function 0x%08lX(", (long)(jit_nuint)func); + signature = func->signature; + num_params = jit_type_num_params(signature); + if(func->builder) + { + value = jit_value_get_struct_pointer(func); + if(value || func->nested_parent) + { + /* We have extra hidden parameters */ + putc('[', stream); + if(func->nested_parent) + { + fputs("parent_frame", stream); + if(value) + { + fputs(", ", stream); + } + } + if(value) + { + jit_dump_value(stream, func, value, 0); + fputs(" : struct_ptr", stream); + } + putc(']', stream); + if(num_params > 0) + { + fputs(", ", stream); + } + } + for(param = 0; param < num_params; ++param) + { + if(param != 0) + { + fputs(", ", stream); + } + value = jit_value_get_param(func, param); + if(value) + { + jit_dump_value(stream, func, value, 0); + } + else + { + fputs("???", stream); + } + fputs(" : ", stream); + jit_dump_type(stream, jit_type_get_param(signature, param)); + } + } + else + { + for(param = 0; param < num_params; ++param) + { + if(param != 0) + { + fputs(", ", stream); + } + jit_dump_type(stream, jit_type_get_param(signature, param)); + } + } + fprintf(stream, ") : "); + jit_dump_type(stream, jit_type_get_return(signature)); + putc('\n', stream); + + /* Should we dump the three address code or the native code? */ + if(func->builder) + { + /* Output each of the three address blocks in turn */ + block = 0; + prev_block = 0; + while((block = jit_block_next(func, block)) != 0) + { + /* Output the block's label, if it has one */ + if(prev_block && block->label == jit_label_undefined) + { + /* A new block was started, but it doesn't have a label yet */ + if(_jit_block_get_last(block) != 0) + { + block->label = (func->builder->next_label)++; + } + } + if(block->label != jit_label_undefined) + { + fprintf(stream, ".L%ld:\n", (long)(block->label)); + } + prev_block = 1; + + /* Dump the instructions in the block */ + jit_insn_iter_init(&iter, block); + while((insn = jit_insn_iter_next(&iter)) != 0) + { + putc('\t', stream); + jit_dump_insn(stream, func, insn); + putc('\n', stream); + } + if(block->ends_in_dead) + { + fputs("\tends_in_dead\n", stream); + } + } + } + else if(func->is_compiled) + { +#if defined(JIT_BACKEND_INTERP) + /* Dump the interpreter's bytecode representation */ + jit_function_interp_t interp; + interp = (jit_function_interp_t)(func->entry_point); + fprintf(stream, "\t%08lX: prolog(0x%lX, %d, %d, %d)\n", + (long)(jit_nint)interp, (long)(jit_nint)func, + (int)(interp->args_size), (int)(interp->frame_size), + (int)(interp->working_area)); + dump_interp_code(stream, (void **)(interp + 1)); +#else + /* TODO: use objdump to dump native code */ +#endif + } + + /* Output the function footer */ + fprintf(stream, "end\n\n"); +} diff --git a/jit/jit-dynlib.c b/jit/jit-dynlib.c new file mode 100644 index 0000000..b0ea6d4 --- /dev/null +++ b/jit/jit-dynlib.c @@ -0,0 +1,424 @@ +/* + * jit-dynlib.c - Dynamic library support routines. + * + * Copyright (C) 2001-2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include +#include "jit-internal.h" +#ifdef JIT_WIN32_PLATFORM + #include + #ifndef JIT_WIN32_NATIVE + #ifdef HAVE_SYS_CYGWIN_H + #include + #endif + #endif +#else +#ifdef HAVE_DLFCN_H + #include +#endif +#endif + +/*@ + +@section Dynamic libraries +@cindex Dynamic libraries + +The following routines are supplied to help load and inspect dynamic +libraries. They should be used in place of the traditional +@code{dlopen}, @code{dlclose}, and @code{dlsym} functions, which +are not portable across operating systems. + +@deftypefun jit_dynlib_handle_t jit_dynlib_open ({const char *} name) +Opens the dynamic library called @code{name}, returning a handle for it. +@end deftypefun + +@deftypefun void jit_dynlib_close (jit_dynlib_handle_t handle) +Close a dynamic library. +@end deftypefun + +@deftypefun {void *} jit_dynlib_get_symbol (jit_dynlib_handle_t handle, {const char *} symbol) +Retrieve the symbol @code{name} from the specified dynamic library. +Returns NULL if the symbol could not be found. This will try both +non-prefixed and underscore-prefixed forms of @code{name} on platforms +where it makes sense to do so, so there is no need for the caller +to perform prefixing. +@end deftypefun + +@deftypefun void jit_dynlib_set_debug (int flag) +Enable or disable additional debug messages to stderr. Debugging is +disabled by default. Normally the dynamic library routines will silently +report errors via NULL return values, leaving reporting up to the caller. +However, it can be useful to turn on additional diagnostics when tracking +down problems with dynamic loading. +@end deftypefun + +@deftypefun {const char *} jit_dynlib_get_suffix (void) +Get the preferred dynamic library suffix for this platform. +Usually something like @code{so}, @code{dll}, or @code{dylib}. +@end deftypefun + +@*/ + +#ifdef __cplusplus +extern "C" { +#endif + +static int dynlib_debug = 0; + +void jit_dynlib_set_debug(int flag) +{ + dynlib_debug = flag; +} + +#if defined(__APPLE__) && defined(__MACH__) /* MacOS X */ + +#include + +jit_dynlib_handle_t jit_dynlib_open(const char *name) +{ + NSObjectFileImage file; + NSObjectFileImageReturnCode result; + NSModule module; + void *image; + const char *msg; + + /* Attempt to open the dylib file */ + result = NSCreateObjectFileImageFromFile(name, &file); + if(result == NSObjectFileImageInappropriateFile) + { + /* May be an image, and not a bundle */ + image = (void *)NSAddImage(name, NSADDIMAGE_OPTION_RETURN_ON_ERROR); + if(image) + { + return image; + } + } + if(result != NSObjectFileImageSuccess) + { + switch(result) + { + case NSObjectFileImageFailure: + msg = " (NSObjectFileImageFailure)"; break; + case NSObjectFileImageInappropriateFile: + msg = " (NSObjectFileImageInappropriateFile)"; break; + case NSObjectFileImageArch: + msg = " (NSObjectFileImageArch)"; break; + case NSObjectFileImageFormat: + msg = " (NSObjectFileImageFormat)"; break; + case NSObjectFileImageAccess: + msg = " (NSObjectFileImageAccess)"; break; + default: + msg = ""; break; + } + if(dynlib_debug) + { + fprintf(stderr, "%s: could not load dynamic library%s\n", + name, msg); + } + return 0; + } + + /* Link the module dependencies */ + module = NSLinkModule(file, name, + NSLINKMODULE_OPTION_BINDNOW | + NSLINKMODULE_OPTION_PRIVATE | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + return (void *)module; +} + +void jit_dynlib_close(jit_dynlib_handle_t handle) +{ + if((((struct mach_header *)handle)->magic == MH_MAGIC) || + (((struct mach_header *)handle)->magic == MH_CIGAM)) + { + /* Cannot remove dynamic images once they've been loaded */ + return; + } + NSUnLinkModule((NSModule)handle, NSUNLINKMODULE_OPTION_NONE); +} + +static void *GetSymbol(jit_dynlib_handle_t handle, const char *symbol) +{ + NSSymbol sym; + + /* We have to use a different lookup approach for images and modules */ + if((((struct mach_header *)handle)->magic == MH_MAGIC) || + (((struct mach_header *)handle)->magic == MH_CIGAM)) + { + if(NSIsSymbolNameDefinedInImage((struct mach_header *)handle, symbol)) + { + sym = NSLookupSymbolInImage((struct mach_header *)handle, symbol, + NSLOOKUPSYMBOLINIMAGE_OPTION_BIND | + NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + } + else + { + sym = 0; + } + } + else + { + sym = NSLookupSymbolInModule((NSModule)handle, symbol); + } + + /* Did we find the symbol? */ + if(sym == 0) + { + return 0; + } + + /* Convert the symbol into the address that we require */ + return (void *)NSAddressOfSymbol(sym); +} + +void *jit_dynlib_get_symbol(jit_dynlib_handle_t handle, const char *symbol) +{ + void *value = GetSymbol(handle, (char *)symbol); + char *newName; + if(value) + { + return value; + } + newName = (char *)jit_malloc(jit_strlen(symbol) + 2); + if(newName) + { + /* Try again with '_' prepended to the name */ + newName[0] = '_'; + jit_strcpy(newName + 1, symbol); + value = GetSymbol(handle, newName); + if(value) + { + jit_free(newName); + return value; + } + jit_free(newName); + } + if(dynlib_debug) + { + fprintf(stderr, "%s: could not find the specified symbol\n", symbol); + } + return 0; +} + +const char *jit_dynlib_get_suffix(void) +{ + return "dylib"; +} + +#elif defined(JIT_WIN32_PLATFORM) /* Native Win32 or Cygwin */ + +jit_dynlib_handle_t jit_dynlib_open(const char *name) +{ + void *libHandle; + char *newName = 0; + +#if defined(JIT_WIN32_CYGWIN) && defined(HAVE_SYS_CYGWIN_H) && \ + defined(HAVE_CYGWIN_CONV_TO_WIN32_PATH) + + /* Use Cygwin to expand the path */ + { + char buf[4096]; + if(cygwin_conv_to_win32_path(name, buf) == 0) + { + newName = jit_strdup(buf); + if(!newName) + { + return 0; + } + } + } + +#endif + + /* Attempt to load the library */ + libHandle = (void *)LoadLibrary((newName ? newName : name)); + if(libHandle == 0) + { + if(dynlib_debug) + { + fprintf(stderr, "%s: could not load dynamic library\n", + (newName ? newName : name)); + } + if(newName) + { + jit_free(newName); + } + return 0; + } + if(newName) + { + jit_free(newName); + } + return libHandle; +} + +void jit_dynlib_close(jit_dynlib_handle_t handle) +{ + FreeLibrary((HINSTANCE)handle); +} + +void *jit_dynlib_get_symbol(jit_dynlib_handle_t handle, const char *symbol) +{ + void *procAddr; + procAddr = (void *)GetProcAddress((HINSTANCE)handle, symbol); + if(procAddr == 0) + { + if(dynlib_debug) + { + fprintf(stderr, "%s: could not resolve symbol", symbol); + } + return 0; + } + return procAddr; +} + +const char *jit_dynlib_get_suffix(void) +{ + return "dll"; +} + +#elif defined(HAVE_DLFCN_H) && defined(HAVE_DLOPEN) + +jit_dynlib_handle_t jit_dynlib_open(const char *name) +{ + jit_dynlib_handle_t handle; + const char *error; + handle = (jit_dynlib_handle_t)dlopen(name, RTLD_LAZY | RTLD_GLOBAL); + if(!handle) + { + /* If the name does not start with "lib" and does not + contain a path, then prepend "lib" and try again */ + if(jit_strncmp(name, "lib", 3) != 0) + { + error = name; + while(*error != '\0' && *error != '/' && *error != '\\') + { + ++error; + } + if(*error == '\0') + { + /* Try adding "lib" to the start */ + char *temp = (char *)jit_malloc(jit_strlen(name) + 4); + if(temp) + { + jit_strcpy(temp, "lib"); + jit_strcat(temp, name); + handle = dlopen(temp, RTLD_LAZY | RTLD_GLOBAL); + jit_free(temp); + if(handle) + { + return handle; + } + } + + /* Reload the original error state */ + handle = dlopen(name, RTLD_LAZY | RTLD_GLOBAL); + } + } + + /* Report the error, or just clear the error state */ + if(dynlib_debug) + { + error = dlerror(); + fprintf(stderr, "%s: %s\n", name, + (error ? error : "could not load dynamic library")); + } + else + { + dlerror(); + } + return 0; + } + else + { + return handle; + } +} + +void jit_dynlib_close(jit_dynlib_handle_t handle) +{ + dlclose(handle); +} + +void *jit_dynlib_get_symbol(jit_dynlib_handle_t handle, const char *symbol) +{ + void *value = dlsym(handle, (char *)symbol); + const char *error = dlerror(); + char *newName; + if(error == 0) + { + return value; + } + newName = (char *)jit_malloc(jit_strlen(symbol) + 2); + if(newName) + { + /* Try again with '_' prepended to the name in case + we are running on a system with a busted "dlsym" */ + newName[0] = '_'; + jit_strcpy(newName + 1, symbol); + value = dlsym(handle, newName); + error = dlerror(); + if(error == 0) + { + jit_free(newName); + return value; + } + jit_free(newName); + } + if(dynlib_debug) + { + fprintf(stderr, "%s: %s\n", symbol, error); + } + return 0; +} + +const char *jit_dynlib_get_suffix(void) +{ + return "so"; +} + +#else /* No dynamic library support */ + +jit_dynlib_handle_t jit_dynlib_open(const char *name) +{ + if(dynlib_debug) + { + fprintf(stderr, "%s: dynamic libraries are not available\n", name); + } + return 0; +} + +void jit_dynlib_close(jit_dynlib_handle_t handle) +{ +} + +void *jit_dynlib_get_symbol(jit_dynlib_handle_t handle, const char *symbol) +{ + return 0; +} + +const char *jit_dynlib_get_suffix(void) +{ + return "so"; +} + +#endif /* No dynamic library support */ + +#ifdef __cplusplus +}; +#endif diff --git a/jit/jit-elf-defs.h b/jit/jit-elf-defs.h new file mode 100644 index 0000000..11cb6be --- /dev/null +++ b/jit/jit-elf-defs.h @@ -0,0 +1,2134 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-1999, 2000, 2001 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _JIT_ELF_DEFS_H +#define _JIT_ELF_DEFS_H 1 + +/* This is the GNU/Linux file, with slight modifications + to compile within the libjit environment */ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Standard ELF types. */ + +/* Type for a 16-bit quantity. */ +typedef jit_ushort Elf32_Half; +typedef jit_ushort Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef jit_uint Elf32_Word; +typedef jit_int Elf32_Sword; +typedef jit_uint Elf64_Word; +typedef jit_int Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef jit_ulong Elf32_Xword; +typedef jit_long Elf32_Sxword; +typedef jit_ulong Elf64_Xword; +typedef jit_long Elf64_Sxword; + +/* Type of addresses. */ +typedef jit_uint Elf32_Addr; +typedef jit_ulong Elf64_Addr; + +/* Type of file offsets. */ +typedef jit_uint Elf32_Off; +typedef jit_ulong Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef jit_ushort Elf32_Section; +typedef jit_ushort Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_LINUX 3 /* Linux. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_AT19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* Special section index. */ + +#define SHN_UNDEF 0 /* No section, undefined symbol. */ + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_NUM 6 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct*/ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x60000000 /* Start of OS-specific */ +#define DT_HIOS 0x6fffffff /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + long int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored */ + + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_68K_NUM 23 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +/* Keep this the last entry. */ +#define R_386_NUM 11 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +/* Keep this the last entry. */ +#define R_SPARC_NUM 56 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* Bits present in AT_HWCAP, primarily for Sparc32. */ + +#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ +#define HWCAP_SPARC_STBAR 2 +#define HWCAP_SPARC_SWAP 4 +#define HWCAP_SPARC_MULDIV 8 +#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ +#define HWCAP_SPARC_ULTRA3 32 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ +#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +/* Keep this the last entry. */ +#define R_MIPS_NUM 38 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +#define DT_MIPS_NUM 0x32 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_ALPHA_NUM 28 + + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +/* ARM relocs. */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ + +/* Keep this the last entry. */ +#define R_390_NUM 27 + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ + +#define R_X86_64_NUM 16 + +#ifdef __cplusplus +}; +#endif + +#endif /* elf.h */ diff --git a/jit/jit-elf-read.c b/jit/jit-elf-read.c new file mode 100644 index 0000000..223f36f --- /dev/null +++ b/jit/jit-elf-read.c @@ -0,0 +1,1558 @@ +/* + * jit-elf-read.c - Routines to read ELF-format binaries. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" +#include "jit-elf-defs.h" +#include "jit-memory.h" +#include +#ifdef JIT_WIN32_PLATFORM + #ifdef HAVE_SYS_TYPES_H + #include + #endif + #include + #include + #include +#else +#ifdef HAVE_SYS_TYPES_H + #include +#endif +#ifdef HAVE_SYS_STAT_H + #include +#endif +#ifdef HAVE_FCNTL_H + #include +#endif +#ifdef HAVE_UNISTD_H + #include +#endif +#ifdef HAVE_SYS_MMAN_H + #include + #if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(HAVE_MPROTECT) + #define JIT_USE_MMAP_TO_LOAD 1 + #ifndef MAP_ANON + #ifdef MAP_ANONYMOUS + #define MAP_ANON MAP_ANONYMOUS + #else + #define MAP_ANON 0 + #endif + #endif + #ifndef MAP_FIXED + #define MAP_FIXED 0 + #endif + #ifndef MAP_COPY + #define MAP_COPY MAP_PRIVATE + #endif + #endif +#endif +#endif +#include + +/*@ + +The @code{libjit} library contains routines that permit pre-compiling +JIT'ed functions into an on-disk representation. This representation +can be loaded at some future time, to avoid the overhead of compiling +the functions at runtime. + +We use the ELF format for this purpose, which is a common binary format +used by modern operating systems and compilers. + +It isn't necessary for your operating system to be based on ELF natively. +We use our own routines to read and write ELF binaries. We chose ELF +because it has all of the features that we require, and reusing an +existing format was better than inventing a completely new one. + +@section Reading ELF binaries + +@*/ + +/* + * Determine whether we should be using the 32-bit or 64-bit ELF structures. + */ +#ifdef JIT_NATIVE_INT32 + typedef Elf32_Ehdr Elf_Ehdr; + typedef Elf32_Shdr Elf_Shdr; + typedef Elf32_Phdr Elf_Phdr; + typedef Elf32_Addr Elf_Addr; + typedef Elf32_Word Elf_Word; + typedef Elf32_Xword Elf_Xword; + typedef Elf32_Off Elf_Off; + typedef Elf32_Dyn Elf_Dyn; + typedef Elf32_Sym Elf_Sym; +#else + typedef Elf64_Ehdr Elf_Ehdr; + typedef Elf64_Shdr Elf_Shdr; + typedef Elf64_Phdr Elf_Phdr; + typedef Elf64_Addr Elf_Addr; + typedef Elf64_Word Elf_Word; + typedef Elf64_Xword Elf_Xword; + typedef Elf64_Off Elf_Off; + typedef Elf64_Dyn Elf_Dyn; + typedef Elf64_Sym Elf_Sym; +#endif + +/* + * Deal with platform differences in the file descriptor routines. + */ +#ifdef JIT_WIN32_NATIVE + #define sys_open _open + #define sys_close _close + #define sys_read _read + #define sys_lseek _lseek +#else + #define sys_open open + #define sys_close close + #define sys_read read + #define sys_lseek lseek +#endif +#ifndef O_BINARY + #define O_BINARY 0 +#endif + +/* + * Define the relocation function type. + */ +typedef int (*jit_reloc_func)(jit_readelf_t readelf, void *address, + int type, jit_nuint value, int has_addend, + jit_nuint addend); + +/* + * Get the relocation function for a particular machine type. + */ +static jit_reloc_func get_reloc(unsigned int machine); + +/* + * Structure of an ELF binary once it has been loaded into memory. + */ +struct jit_readelf +{ + Elf_Ehdr ehdr; + unsigned char *phdrs; + unsigned char *shdrs; + char *regular_strings; + jit_nuint regular_strings_size; + char *dynamic_strings; + jit_nuint dynamic_strings_size; + Elf_Sym *symbol_table; + jit_nuint symbol_table_size; + Elf_Word *symbol_hash; + jit_nuint symbol_hash_size; + Elf_Word symbol_hash_buckets; + jit_reloc_func reloc_func; + void *map_address; + jit_nuint map_size; + int free_with_munmap; +}; + +/* + * Flag that indicates that an auxillary section was malloc'ed, + * and isn't part of the main memory range at "map_address". + */ +#define JIT_ELF_IS_MALLOCED 0x01000000 + +/* + * Get the address of a particular phdr. + */ +static Elf_Phdr *get_phdr(jit_readelf_t readelf, unsigned int index) +{ + if(index < readelf->ehdr.e_phnum && + readelf->ehdr.e_phentsize >= sizeof(Elf_Phdr)) + { + return (Elf_Phdr *) + (readelf->phdrs + + index * ((unsigned int)(readelf->ehdr.e_phentsize))); + } + else + { + return 0; + } +} + +/* + * Get the address of a particular shdr. + */ +static Elf_Shdr *get_shdr(jit_readelf_t readelf, unsigned int index) +{ + if(index < readelf->ehdr.e_shnum && + readelf->ehdr.e_shentsize >= sizeof(Elf_Shdr)) + { + return (Elf_Shdr *) + (readelf->shdrs + + index * ((unsigned int)(readelf->ehdr.e_shentsize))); + } + else + { + return 0; + } +} + +/* + * Find a specific string in the regular string table. + */ +static const char *get_string(jit_readelf_t readelf, Elf_Word _index) +{ + jit_nuint index = (jit_nuint)_index; + if(index < readelf->regular_strings_size) + { + return readelf->regular_strings + index; + } + else + { + return 0; + } +} + +/* + * Find a specific string in the dynamic string table. + */ +static const char *get_dyn_string(jit_readelf_t readelf, Elf_Addr _index) +{ + jit_nuint index = (jit_nuint)_index; + if(index < readelf->dynamic_strings_size) + { + return readelf->dynamic_strings + index; + } + else + { + return 0; + } +} + +/* + * Map all of the program segments into memory and set up the bss section. + */ +static int map_program(jit_readelf_t readelf, int fd) +{ + Elf_Off file_size; + Elf_Off memory_size; + Elf_Off start, end; + Elf_Phdr *phdr; + unsigned int index; + void *base_address; + unsigned char *segment_address; + + /* Get the maximum file and memory sizes for the program. + The bytes between "file_size" and "memory_size" are bss */ + file_size = 0; + memory_size = 0; + for(index = 0; index < readelf->ehdr.e_phnum; ++index) + { + phdr = get_phdr(readelf, index); + if(!phdr) + { + continue; + } + start = phdr->p_offset; + end = start + phdr->p_filesz; + if(end > file_size) + { + file_size = end; + } + start = phdr->p_vaddr; + end = start + phdr->p_memsz; + if(end > memory_size) + { + memory_size = end; + } + } + if(memory_size < file_size) + { + memory_size = file_size; + } + + /* Try to map the program segments into memory using mmap */ + base_address = 0; +#ifdef JIT_USE_MMAP_TO_LOAD + { + Elf_Off page_size; + Elf_Off rounded_file_size; + Elf_Off temp_start; + Elf_Off temp_end; + int zero_fd, prot; + + /* Round the total memory and file sizes up to the CPU page size */ + page_size = (Elf_Off)(jit_exec_page_size()); + end = memory_size; + if((end % page_size) != 0) + { + end += page_size - (end % page_size); + } + rounded_file_size = file_size; + if((rounded_file_size % page_size) != 0) + { + rounded_file_size += page_size - (rounded_file_size % page_size); + } + + /* Allocate memory for the program from /dev/zero. Once we have + the memory, we will overlay the program segments on top */ + zero_fd = sys_open("/dev/zero", O_RDWR, 0); + if(zero_fd < -1) + { + goto failed_mmap; + } + base_address = mmap(0, (size_t)end, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, zero_fd, 0); + close(zero_fd); + if(base_address == (void *)(jit_nint)(-1)) + { + base_address = 0; + goto failed_mmap; + } + + /* Lay down the program sections at their mapped locations */ + for(index = 0; index < readelf->ehdr.e_phnum; ++index) + { + phdr = get_phdr(readelf, index); + if(phdr) + { + temp_start = phdr->p_offset; + temp_end = temp_start + phdr->p_filesz; + temp_start -= (temp_start % page_size); + if((temp_end % page_size) != 0) + { + temp_end += page_size - (temp_end % page_size); + } + start = phdr->p_vaddr; + start -= (start % page_size); + if(temp_start < temp_end) + { + segment_address = + ((unsigned char *)base_address) + (jit_nuint)start; + prot = 0; + if((phdr->p_flags & PF_X) != 0) + { + prot |= PROT_EXEC; + } + if((phdr->p_flags & PF_W) != 0) + { + prot |= PROT_WRITE; + } + if((phdr->p_flags & PF_R) != 0) + { + prot |= PROT_READ; + } + if(mmap(segment_address, (size_t)(temp_end - temp_start), + prot, MAP_COPY | MAP_FILE | MAP_FIXED, fd, + (off_t)temp_start) == (void *)(jit_nint)(-1)) + { + munmap(base_address, (size_t)end); + base_address = 0; + goto failed_mmap; + } + } + } + } + + /* We need to free the memory with munmap when the program is closed */ + readelf->free_with_munmap = 1; + + /* Clear the left-over ".bss" bits that did not get cleared above */ + for(index = 0; index < readelf->ehdr.e_phnum; ++index) + { + phdr = get_phdr(readelf, index); + if(phdr && phdr->p_filesz < phdr->p_memsz) + { + temp_start = phdr->p_vaddr + phdr->p_filesz; + start = (temp_start % page_size); + temp_start -= start; + if(start != 0) + { + segment_address = + ((unsigned char *)base_address) + + (jit_nuint)temp_start; + mprotect(segment_address, (size_t)page_size, + PROT_READ | PROT_WRITE); + jit_memzero(segment_address + (jit_nuint)start, + (unsigned int)(page_size - start)); + prot = 0; + if((phdr->p_flags & PF_X) != 0) + { + prot |= PROT_EXEC; + } + if((phdr->p_flags & PF_W) != 0) + { + prot |= PROT_WRITE; + } + if((phdr->p_flags & PF_R) != 0) + { + prot |= PROT_READ; + } + mprotect(segment_address, (size_t)page_size, prot); + } + } + } + } +failed_mmap: +#endif /* JIT_USE_MMAP_TO_LOAD */ + + /* If we haven't mapped the file yet, then fall back to "malloc" */ + if(!base_address) + { + base_address = jit_malloc_exec(memory_size); + if(!base_address) + { + return 0; + } + for(index = 0; index < readelf->ehdr.e_phnum; ++index) + { + phdr = get_phdr(readelf, index); + if(phdr) + { + segment_address = ((unsigned char *)base_address) + + (jit_nuint)(phdr->p_vaddr); + if(lseek(fd, (off_t)(phdr->p_offset), 0) != phdr->p_offset || + read(fd, segment_address, (size_t)(phdr->p_filesz)) + != (size_t)(phdr->p_filesz)) + { + jit_free_exec(base_address, memory_size); + return 0; + } + } + } + } + + /* Record the mapped address and size for later */ + readelf->map_address = base_address; + readelf->map_size = memory_size; + return 1; +} + +/* + * Map an auxillary section into memory and return its base address. + * Returns NULL if we ran out of memory. + */ +static void *map_section(int fd, Elf_Off offset, Elf_Xword file_size, + Elf_Xword memory_size, Elf_Word flags) +{ + void *address; + if(memory_size < file_size) + { + memory_size = file_size; + } + address = jit_malloc_exec(memory_size); + if(!address) + { + return 0; + } + if(lseek(fd, offset, 0) != offset) + { + jit_free_exec(address, memory_size); + return 0; + } + if(read(fd, address, (size_t)file_size) != file_size) + { + jit_free_exec(address, memory_size); + return 0; + } + return address; +} + +/* + * Unmap an auxillary section from memory. + */ +static void unmap_section(void *address, Elf_Xword file_size, + Elf_Xword memory_size, Elf_Word flags) +{ + if(memory_size < file_size) + { + memory_size = file_size; + } + if((flags & JIT_ELF_IS_MALLOCED) != 0) + { + jit_free_exec(address, (unsigned int)memory_size); + } +} + +/* + * Iterate over the contents of the ".dynamic" section. + */ +typedef struct +{ + Elf_Dyn *dyn; + jit_nuint size; + +} jit_dynamic_iter_t; +static void dynamic_iter_init(jit_dynamic_iter_t *iter, jit_readelf_t readelf) +{ + iter->dyn = jit_readelf_get_section_by_type + (readelf, SHT_DYNAMIC, &(iter->size)); +} +static int dynamic_iter_next + (jit_dynamic_iter_t *iter, jit_uint *type, Elf_Addr *value) +{ + if(iter->size >= sizeof(Elf_Dyn)) + { + *type = (jit_uint)(iter->dyn->d_tag); + *value = iter->dyn->d_un.d_ptr; + if(*type == DT_NULL) + { + /* Explicitly-marked end of the list */ + return 0; + } + ++(iter->dyn); + iter->size -= sizeof(Elf_Dyn); + return 1; + } + else + { + /* Implicitly-marked end of the list */ + return 0; + } +} +static int dynamic_for_type + (jit_readelf_t readelf, jit_uint type, Elf_Addr *value) +{ + Elf_Addr temp_value; + jit_dynamic_iter_t iter; + jit_uint iter_type; + dynamic_iter_init(&iter, readelf); + while(dynamic_iter_next(&iter, &iter_type, &temp_value)) + { + if(iter_type == type) + { + if(value) + { + *value = temp_value; + } + return 1; + } + } + return 0; +} + +/* + * Load interesting values from the ".dynamic" section, for quicker lookups. + */ +static void load_dynamic_section(jit_readelf_t readelf, int flags) +{ + Elf_Addr value; + Elf_Addr value2; + jit_dynamic_iter_t iter; + jit_uint type; + jit_nuint size; + + /* Get the position and size of the dynamic string table */ + if(dynamic_for_type(readelf, DT_STRTAB, &value) && + dynamic_for_type(readelf, DT_STRSZ, &value2)) + { + readelf->dynamic_strings = jit_readelf_map_vaddr + (readelf, (jit_nuint)value); + if(readelf->dynamic_strings) + { + readelf->dynamic_strings_size = (jit_nuint)value2; + } + } + + /* Get the position and size of the dynamic symbol table */ + readelf->symbol_table = jit_readelf_get_section_by_type + (readelf, SHT_DYNSYM, &size); + if(readelf->symbol_table) + { + if(dynamic_for_type(readelf, DT_SYMENT, &value) && + value == sizeof(Elf_Sym)) + { + readelf->symbol_table_size = size / sizeof(Elf_Sym); + readelf->symbol_hash = jit_readelf_get_section_by_type + (readelf, SHT_HASH, &size); + if(readelf->symbol_hash) + { + readelf->symbol_hash_size = size / sizeof(Elf_Word); + if(readelf->symbol_hash_size >= 2) + { + readelf->symbol_hash_buckets = readelf->symbol_hash[0]; + } + } + } + else + { + readelf->symbol_table = 0; + } + } + + /* Bail out if we don't need to print debugging information */ + if((flags & JIT_READELF_FLAG_DEBUG) == 0) + { + return; + } + + /* Iterate through the ".dynamic" section, dumping all that we find */ + dynamic_iter_init(&iter, readelf); + while(dynamic_iter_next(&iter, &type, &value)) + { + switch(type) + { + case DT_NEEDED: + { + printf("needed library: %s\n", get_dyn_string(readelf, value)); + } + break; + + case DT_PLTRELSZ: + { + printf("total size of PLT relocs: %ld\n", (long)value); + } + break; + + case DT_PLTGOT: + { + printf("address of PLTGOT table: 0x%lx\n", (long)value); + } + break; + + case DT_HASH: + { + printf("address of symbol hash table: 0x%lx\n", (long)value); + } + break; + + case DT_STRTAB: + { + printf("address of string table: 0x%lx\n", (long)value); + } + break; + + case DT_SYMTAB: + { + printf("address of symbol table: 0x%lx\n", (long)value); + } + break; + + case DT_STRSZ: + { + printf("size of string table: %ld\n", (long)value); + } + break; + + case DT_SYMENT: + { + printf("size of one symbol table entry: %ld\n", (long)value); + } + break; + + case DT_INIT: + { + printf("address of init function: 0x%lx\n", (long)value); + } + break; + + case DT_FINI: + { + printf("address of fini function: 0x%lx\n", (long)value); + } + break; + + case DT_SONAME: + { + printf("library name: %s\n", get_dyn_string(readelf, value)); + } + break; + + case DT_REL: + { + printf("address of Rel relocs: 0x%lx\n", (long)value); + } + break; + + case DT_RELSZ: + { + printf("total size of Rel relocs: %ld\n", (long)value); + } + break; + + case DT_RELENT: + { + printf("size of one Rel reloc: %ld\n", (long)value); + } + break; + + case DT_PLTREL: + { + printf("type of PLT relocs: %ld\n", (long)value); + } + break; + + case DT_JMPREL: + { + printf("address of PLT relocs: 0x%lx\n", (long)value); + } + break; + + default: + { + printf("dynamic info of type 0x%x: 0x%lx\n", + (int)type, (long)value); + } + break; + } + } + + /* Iterate through the symbol table, dumping all of the entries */ + for(size = 0; size < readelf->symbol_table_size; ++size) + { + printf("%08lX %02X%02X %2d %s\n", + (long)(readelf->symbol_table[size].st_value), + (int)(readelf->symbol_table[size].st_info), + (int)(readelf->symbol_table[size].st_other), + (int)(readelf->symbol_table[size].st_shndx), + get_dyn_string(readelf, readelf->symbol_table[size].st_name)); + } + printf("number of symbols: %ld\n", (long)(readelf->symbol_table_size)); + printf("number of symbol hash entries: %ld\n", + (long)(readelf->symbol_hash_size)); +} + +/*@ + * @deftypefun int jit_readelf_open ({jit_readelf_t *} readelf, {const char *} filename, int force) + * Open the specified @code{filename} and load the ELF binary that is + * contained within it. Returns one of the following result codes: + * + * @table @code + * @vindex JIT_READELF_OK + * @item JIT_READELF_OK + * The ELF binary was opened successfully. + * + * @vindex JIT_READELF_CANNOT_OPEN + * @item JIT_READELF_CANNOT_OPEN + * Could not open the file at the filesystem level (reason in @code{errno}). + * + * @vindex JIT_READELF_NOT_ELF + * @item JIT_READELF_NOT_ELF + * The file was opened, but it is not an ELF binary. + * + * @vindex JIT_READELF_WRONG_ARCH + * @item JIT_READELF_WRONG_ARCH + * The file is an ELF binary, but it does not pertain to the architecture + * of this machine. + * + * @vindex JIT_READELF_BAD_FORMAT + * @item JIT_READELF_BAD_FORMAT + * The file is an ELF binary, but the format is corrupted in some fashion. + * + * @vindex JIT_READELF_MEMORY + * @item JIT_READELF_MEMORY + * There is insufficient memory to open the ELF binary. + * @end table + * + * The following flags may be supplied to alter the manner in which + * the ELF binary is loaded: + * + * @table @code + * @vindex JIT_READELF_FLAG_FORCE + * @item JIT_READELF_FLAG_FORCE + * Force @code{jit_readelf_open} to open the ELF binary, even if + * the architecture does not match this machine. Useful for debugging. + * + * @vindex JIT_READELF_FLAG_DEBUG + * @item JIT_READELF_FLAG_DEBUG + * Print additional debug information to stdout. + * @end table + * @end deftypefun +@*/ +int jit_readelf_open(jit_readelf_t *_readelf, const char *filename, int flags) +{ + int fd; + Elf_Ehdr ehdr; + Elf_Phdr *phdr; + Elf_Shdr *shdr; + jit_elf_info_t elf_info; + jit_readelf_t readelf; + unsigned int phdr_size; + unsigned int shdr_size; + unsigned int index; + void *address; + union + { + jit_ushort value; + unsigned char bytes[2]; + + } un; + + /* Get the machine and ABI values that we expect in the header */ + _jit_gen_get_elf_info(&elf_info); + + /* Open the file and read the ELF magic number information */ + if((fd = sys_open(filename, O_RDONLY | O_BINARY, 0)) < 0) + { + return JIT_READELF_CANNOT_OPEN; + } + if(sys_read(fd, ehdr.e_ident, EI_NIDENT) != EI_NIDENT) + { + sys_close(fd); + return JIT_READELF_NOT_ELF; + } + + /* Determine if the magic number matches what we expect to see */ + if(ehdr.e_ident[EI_MAG0] != ELFMAG0 || ehdr.e_ident[EI_MAG1] != ELFMAG1 || + ehdr.e_ident[EI_MAG2] != ELFMAG2 || ehdr.e_ident[EI_MAG3] != ELFMAG3) + { + sys_close(fd); + return JIT_READELF_NOT_ELF; + } +#ifdef JIT_NATIVE_INT32 + if(ehdr.e_ident[EI_CLASS] != ELFCLASS32) + { + sys_close(fd); + return JIT_READELF_WRONG_ARCH; + } +#else + if(ehdr.e_ident[EI_CLASS] != ELFCLASS64) + { + sys_close(fd); + return JIT_READELF_WRONG_ARCH; + } +#endif + un.value = 0x0102; + if(un.bytes[0] == 0x01) + { + /* Looking for a big-endian binary */ + if(ehdr.e_ident[EI_DATA] != ELFDATA2MSB) + { + sys_close(fd); + return JIT_READELF_WRONG_ARCH; + } + } + else + { + /* Looking for a little-endian binary */ + if(ehdr.e_ident[EI_DATA] != ELFDATA2LSB) + { + sys_close(fd); + return JIT_READELF_WRONG_ARCH; + } + } + if(ehdr.e_ident[EI_VERSION] != EV_CURRENT) + { + sys_close(fd); + return JIT_READELF_BAD_FORMAT; + } + + /* Read the rest of the ELF header and validate it */ + if(sys_read(fd, &(ehdr.e_type), sizeof(Elf_Ehdr) - EI_NIDENT) + != (sizeof(Elf_Ehdr) - EI_NIDENT)) + { + sys_close(fd); + return JIT_READELF_BAD_FORMAT; + } + if(ehdr.e_type != ET_DYN) + { + /* We can only load files that are marked as dynamic shared objects */ + sys_close(fd); + return JIT_READELF_WRONG_ARCH; + } + if((flags & JIT_READELF_FLAG_FORCE) == 0) + { + if(ehdr.e_machine != elf_info.machine || + ehdr.e_ident[EI_OSABI] != elf_info.abi || + ehdr.e_ident[EI_ABIVERSION] != elf_info.abi_version) + { + /* The ELF binary does not pertain to this machine or ABI type */ + sys_close(fd); + return JIT_READELF_WRONG_ARCH; + } + } + if(ehdr.e_version != EV_CURRENT) + { + sys_close(fd); + return JIT_READELF_BAD_FORMAT; + } + if(ehdr.e_ehsize < sizeof(ehdr)) + { + sys_close(fd); + return JIT_READELF_BAD_FORMAT; + } + + /* Allocate space for the ELF reader object */ + if((readelf = jit_cnew(struct jit_readelf)) == 0) + { + sys_close(fd); + return JIT_READELF_MEMORY; + } + readelf->ehdr = ehdr; + phdr_size = ((unsigned int)(ehdr.e_phnum)) * + ((unsigned int)(ehdr.e_phentsize)); + shdr_size = ((unsigned int)(ehdr.e_shnum)) * + ((unsigned int)(ehdr.e_shentsize)); + if(phdr_size > 0) + { + readelf->phdrs = (unsigned char *)jit_malloc(phdr_size); + if(!(readelf->phdrs)) + { + jit_free(readelf); + sys_close(fd); + return JIT_READELF_MEMORY; + } + } + if(shdr_size > 0) + { + readelf->shdrs = (unsigned char *)jit_malloc(shdr_size); + if(!(readelf->shdrs)) + { + jit_free(readelf->phdrs); + jit_free(readelf); + sys_close(fd); + return JIT_READELF_MEMORY; + } + } + + /* Seek to the program and section header tables and read them */ + if(phdr_size > 0) + { + if(lseek(fd, ehdr.e_phoff, 0) != ehdr.e_phoff || + read(fd, readelf->phdrs, phdr_size) != phdr_size) + { + jit_free(readelf->shdrs); + jit_free(readelf->phdrs); + jit_free(readelf); + sys_close(fd); + return JIT_READELF_BAD_FORMAT; + } + } + if(shdr_size > 0) + { + if(lseek(fd, ehdr.e_shoff, 0) != ehdr.e_shoff || + read(fd, readelf->shdrs, shdr_size) != shdr_size) + { + jit_free(readelf->shdrs); + jit_free(readelf->phdrs); + jit_free(readelf); + sys_close(fd); + return JIT_READELF_BAD_FORMAT; + } + } + + /* Load the program segments */ + if(!map_program(readelf, fd)) + { + jit_readelf_close(readelf); + sys_close(fd); + return JIT_READELF_MEMORY; + } + + /* Load the auxillary sections */ + if(shdr_size > 0) + { + for(index = 0; index < ehdr.e_shnum; ++index) + { + shdr = get_shdr(readelf, index); + if(!shdr) + { + continue; + } + if((shdr->sh_flags & SHF_ALLOC) != 0 || shdr->sh_addr != 0) + { + /* This may be mapped inside one of the program segments. + If so, we don't want to load a second copy of it */ + address = jit_readelf_map_vaddr(readelf, shdr->sh_addr); + if(address) + { + continue; + } + } + if(shdr->sh_size == 0) + { + /* Ignore zero-sized segments */ + continue; + } + address = map_section + (fd, shdr->sh_offset, shdr->sh_size, shdr->sh_size, + ((shdr->sh_flags & SHF_WRITE) != 0 ? (PF_W | PF_R) : PF_R)); + if(!address) + { + jit_readelf_close(readelf); + sys_close(fd); + return JIT_READELF_MEMORY; + } + shdr->sh_offset = (Elf_Off)(jit_nuint)address; + shdr->sh_flags |= JIT_ELF_IS_MALLOCED; + } + } + + /* Close the file descriptor because we don't need it any more */ + sys_close(fd); + + /* Find the regular string table */ + shdr = get_shdr(readelf, ehdr.e_shstrndx); + if(shdr) + { + if((shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0) + { + readelf->regular_strings = (char *)(jit_nuint)(shdr->sh_offset); + } + else + { + readelf->regular_strings = + (char *)jit_readelf_map_vaddr(readelf, shdr->sh_addr); + } + if(readelf->regular_strings) + { + readelf->regular_strings_size = (jit_nuint)(shdr->sh_size); + } + } + + /* Dump debug information about the program segments and sections */ + if((flags & JIT_READELF_FLAG_DEBUG) != 0) + { + printf("header: machine=%d, abi=%d, abi_version=%d\n", + (int)(ehdr.e_machine), (int)(ehdr.e_ident[EI_OSABI]), + (int)(ehdr.e_ident[EI_ABIVERSION])); + for(index = 0; index < ehdr.e_phnum; ++index) + { + phdr = get_phdr(readelf, index); + if(phdr) + { + printf("program segment: type=%d, flags=0x%x, " + "vaddr=0x%lx, file_size=%ld, memory_size=%ld\n", + (int)(phdr->p_type), + (int)(phdr->p_flags & ~JIT_ELF_IS_MALLOCED), + (long)(phdr->p_vaddr), + (long)(phdr->p_filesz), + (long)(phdr->p_memsz)); + } + } + for(index = 0; index < ehdr.e_shnum; ++index) + { + shdr = get_shdr(readelf, index); + if(shdr) + { + printf("section %2d: name=\"%s\", type=%d, flags=0x%x, " + "vaddr=0x%lx, size=%ld\n", + index, + get_string(readelf, shdr->sh_name), + (int)(shdr->sh_type), + (int)(shdr->sh_flags & ~JIT_ELF_IS_MALLOCED), + (long)(shdr->sh_addr), + (long)(shdr->sh_size)); + } + } + } + + /* Get the relocation function for this machine type */ + readelf->reloc_func = get_reloc((unsigned int)(ehdr.e_machine)); + + /* Load useful values from the dynamic section that we want to cache */ + load_dynamic_section(readelf, flags); + + /* The ELF binary is loaded and ready to go */ + *_readelf = readelf; + return JIT_READELF_OK; +} + +/*@ + * @deftypefun void jit_readelf_close (jit_readelf_t readelf) + * Close an ELF reader, reclaiming all of the memory that was used. + * @end deftypefun +@*/ +void jit_readelf_close(jit_readelf_t readelf) +{ + unsigned int index; + Elf_Shdr *shdr; + if(!readelf) + { + return; + } +#ifdef JIT_USE_MMAP_TO_LOAD + if(readelf->free_with_munmap) + { + munmap(readelf->map_address, (size_t)(readelf->map_size)); + } + else +#endif + { + jit_free_exec(readelf->map_address, readelf->map_size); + } + for(index = 0; index < readelf->ehdr.e_shnum; ++index) + { + shdr = get_shdr(readelf, index); + if(shdr && (shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0) + { + unmap_section + ((void *)(jit_nuint)(shdr->sh_offset), + shdr->sh_size, shdr->sh_size, shdr->sh_flags); + } + } + jit_free(readelf->phdrs); + jit_free(readelf->shdrs); + jit_free(readelf); +} + +/*@ + * @deftypefun {const char *} jit_readelf_get_name (jit_readelf_t readelf) + * Get the library name that is embedded inside an ELF binary. + * ELF binaries can refer to each other using this name. + * @end deftypefun +@*/ +const char *jit_readelf_get_name(jit_readelf_t readelf) +{ + Elf_Addr value; + if(dynamic_for_type(readelf, DT_SONAME, &value)) + { + return get_dyn_string(readelf, value); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun void *jit_readelf_get_symbol (jit_readelf_t readelf, {const char *} name) + * Look up the symbol called @code{name} in the ELF binary represented + * by @code{readelf}. Returns NULL if the symbol is not present. + * + * External references from this ELF binary to others are not resolved + * until the ELF binary is loaded into a JIT context using + * @code{jit_readelf_add_to_context} and @code{jit_readelf_resolve_all}. + * You should not call functions within this ELF binary until after you + * have fully resolved it. + * @end deftypefun +@*/ +void *jit_readelf_get_symbol(jit_readelf_t readelf, const char *name) +{ + unsigned long hash; + unsigned long temp; + unsigned int index; + jit_nuint num_symbols; + Elf_Sym *symbol; + const char *symbol_name; + + /* Bail out if we have insufficient information to resolve the name */ + if(!readelf || !name || !(readelf->symbol_table)) + { + return 0; + } + + /* Hash the name to get the starting index in the symbol hash */ + hash = 0; + index = 0; + while(name[index] != 0) + { + hash = (hash << 4) + (unsigned long)(name[index] & 0xFF); + temp = (hash & 0xF0000000); + if(temp != 0) + { + hash ^= temp | (temp >> 24); + } + ++index; + } + + /* Look in the hash table for the name */ + if(readelf->symbol_hash_buckets != 0) + { + hash %= (unsigned long)(readelf->symbol_hash_buckets); + temp = (unsigned long)(readelf->symbol_hash[hash + 2]); + while(temp != 0 && temp < readelf->symbol_table_size) + { + symbol = &(readelf->symbol_table[temp]); + symbol_name = get_dyn_string(readelf, symbol->st_name); + if(symbol_name && !jit_strcmp(symbol_name, name)) + { + /* Ignore symbols in section 0, as they are external */ + if(symbol->st_shndx) + { + return jit_readelf_map_vaddr + (readelf, (jit_nuint)(symbol->st_value)); + } + break; + } + temp = (unsigned long)(readelf->symbol_hash + [temp + readelf->symbol_hash_buckets + 2]); + } + return 0; + } + + /* There is no hash table, so search for the symbol the hard way */ + symbol = readelf->symbol_table; + for(num_symbols = readelf->symbol_table_size; + num_symbols > 0; --num_symbols) + { + symbol_name = get_dyn_string(readelf, symbol->st_name); + if(symbol_name && !jit_strcmp(symbol_name, name)) + { + /* Ignore symbols in section 0, as they are external */ + if(symbol->st_shndx) + { + return jit_readelf_map_vaddr + (readelf, (jit_nuint)(symbol->st_value)); + } + } + ++symbol; + } + return 0; +} + +/*@ + * @deftypefun {void *} jit_readelf_get_section (jit_readelf_t readelf, {const char *} name, {jit_nuint *} size) + * Get the address and size of a particular section from an ELF binary. + * Returns NULL if the section is not present in the ELF binary. + * + * The virtual machine may have stored auxillary information + * in the section when the binary was first generated. This function + * allows the virtual machine to retrieve its auxillary information. + * + * Examples of such information may be version numbers, timestamps, + * checksums, and other identifying information for the bytecode that + * was previously compiled by the virtual machine. The virtual machine + * can use this to determine if the ELF binary is up to date and + * relevant to its needs. + * + * It is recommended that virtual machines prefix their special sections + * with a unique string (e.g. @code{.foovm}) to prevent clashes with + * system-defined section names. The prefix @code{.libjit} is reserved + * for use by @code{libjit} itself. + * @end deftypefun +@*/ +void *jit_readelf_get_section + (jit_readelf_t readelf, const char *name, jit_nuint *size) +{ + unsigned int index; + Elf_Shdr *shdr; + const char *temp_name; + if(!readelf || !name) + { + return 0; + } + for(index = 0; index < readelf->ehdr.e_shnum; ++index) + { + shdr = get_shdr(readelf, index); + if(shdr) + { + temp_name = get_string(readelf, shdr->sh_name); + if(temp_name && !jit_strcmp(name, temp_name)) + { + if(size) + { + *size = (jit_nuint)(shdr->sh_size); + } + if((shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0) + { + return (void *)(jit_nuint)(shdr->sh_offset); + } + else + { + return jit_readelf_map_vaddr + (readelf, (jit_nuint)(shdr->sh_addr)); + } + } + } + } + return 0; +} + +/*@ + * @deftypefun {void *} jit_readelf_get_section_by_type (jit_readelf_t readelf, jit_int type, {jit_nuint *} size) + * Get a particular section using its raw ELF section type (i.e. one of + * the @code{SHT_*} constants in @code{jit-elf-defs.h}). This is mostly + * for internal use, but some virtual machines may find it useful for + * debugging purposes. + * @end deftypefun +@*/ +void *jit_readelf_get_section_by_type + (jit_readelf_t readelf, jit_int type, jit_nuint *size) +{ + unsigned int index; + Elf_Shdr *shdr; + if(!readelf) + { + return 0; + } + for(index = 0; index < readelf->ehdr.e_shnum; ++index) + { + shdr = get_shdr(readelf, index); + if(shdr && type == (jit_int)(shdr->sh_type)) + { + if(size) + { + *size = (jit_nuint)(shdr->sh_size); + } + if((shdr->sh_flags & JIT_ELF_IS_MALLOCED) != 0) + { + return (void *)(jit_nuint)(shdr->sh_offset); + } + else + { + return jit_readelf_map_vaddr + (readelf, (jit_nuint)(shdr->sh_addr)); + } + } + } + return 0; +} + +/*@ + * @deftypefun {void *} jit_readelf_map_vaddr (jit_readelf_t readelf, jit_nuint vaddr) + * Map a virtual address to an actual address in a loaded ELF binary. + * Returns NULL if @code{vaddr} could not be mapped. + * @end deftypefun +@*/ +void *jit_readelf_map_vaddr(jit_readelf_t readelf, jit_nuint vaddr) +{ + unsigned int index; + Elf_Phdr *phdr; + if(!readelf) + { + return 0; + } + for(index = 0; index < readelf->ehdr.e_phnum; ++index) + { + phdr = get_phdr(readelf, index); + if(phdr && vaddr >= phdr->p_vaddr && + vaddr < (phdr->p_vaddr + phdr->p_memsz)) + { + return (void *)(((unsigned char *)(readelf->map_address)) + vaddr); + } + } + return 0; +} + +/*@ + * @deftypefun {unsigned int} jit_readelf_num_needed (jit_readelf_t readelf) + * Get the number of dependent libraries that are needed by this + * ELF binary. The virtual machine will normally need to arrange + * to load these libraries with @code{jit_readelf_open} as well, + * so that all of the necessary symbols can be resolved. + * @end deftypefun +@*/ +unsigned int jit_readelf_num_needed(jit_readelf_t readelf) +{ + jit_dynamic_iter_t iter; + unsigned int count = 0; + jit_int type; + Elf_Addr value; + dynamic_iter_init(&iter, readelf); + while(dynamic_iter_next(&iter, &type, &value)) + { + if(type == DT_NEEDED) + { + ++count; + } + } + return count; +} + +/*@ + * @deftypefun {const char *} jit_readelf_get_needed (jit_readelf_t readelf, {unsigned int} index) + * Get the name of the dependent library at position @code{index} within + * the needed libraries list of this ELF binary. Returns NULL if + * the @code{index} is invalid. + * @end deftypefun +@*/ +const char *jit_readelf_get_needed(jit_readelf_t readelf, unsigned int index) +{ + jit_dynamic_iter_t iter; + jit_int type; + Elf_Addr value; + dynamic_iter_init(&iter, readelf); + while(dynamic_iter_next(&iter, &type, &value)) + { + if(type == DT_NEEDED) + { + if(index == 0) + { + return get_dyn_string(readelf, value); + } + --index; + } + } + return 0; +} + +/*@ + * @deftypefun void jit_readelf_add_to_context (jit_readelf_t readelf, jit_context_t context) + * Add this ELF binary to a JIT context, so that its contents can be used + * when executing JIT-managed code. The binary will be closed automatically + * if the context is destroyed and @code{jit_readelf_close} has not been + * called explicitly yet. + * + * The functions in the ELF binary cannot be used until you also call + * @code{jit_readelf_resolve_all} to resolve cross-library symbol references. + * The reason why adding and resolution are separate steps is to allow for + * resolving circular dependencies between ELF binaries. + * @end deftypefun +@*/ +void jit_readelf_add_to_context(jit_readelf_t readelf, jit_context_t context) +{ + /* TODO */ +} + +/*@ + * @deftypefun int jit_readelf_resolve_all (jit_context_t context, int print_failures) + * Resolve all of the cross-library symbol references in ELF binaries + * that have been added to @code{context} but which were not resolved + * in the previous call to this function. If @code{print_failures} + * is non-zero, then diagnostic messages will be written to stdout + * for any symbol resolutions that fail. + * + * Returns zero on failure, or non-zero if all symbols were successfully + * resolved. If there are no ELF binaries awaiting resolution, then + * this function will return a non-zero result. + * @end deftypefun +@*/ +int jit_readelf_resolve_all(jit_context_t context, int print_failures) +{ + /* TODO */ + return 0; +} + +/************************************************************************ + + Warning! Warning! Warning! + +The following code is very system-dependent, as every ELF target has its +own peculiar mechanism for performing relocations. Consult your target's +documentation for the precise details. + +To make things a little easier, you only need to support the relocation +types that you intend to use in the JIT's ELF writer. And many types +only pertain to ELF executable or object files, which we don't use. + +************************************************************************/ + +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) + +/* + * Apply relocations for i386 platforms. + */ +static int i386_reloc(jit_readelf_t readelf, void *address, int type, + jit_nuint value, int has_addend, jit_nuint addend) +{ + if(type == R_386_32) + { + if(has_addend) + { + *((jit_nuint *)address) = value + addend; + } + else + { + *((jit_nuint *)address) += value; + } + return 1; + } + else if(type == R_386_PC32) + { + value -= (jit_nuint)address; + if(has_addend) + { + *((jit_nuint *)address) = value + addend; + } + else + { + *((jit_nuint *)address) += value; + } + return 1; + } + return 0; +} + +#endif /* i386 */ + +#if defined(__arm) || defined(__arm__) + +/* + * Apply relocations for ARM platforms. + */ +static int arm_reloc(jit_readelf_t readelf, void *address, int type, + jit_nuint value, int has_addend, jit_nuint addend) +{ + if(type == R_ARM_PC24) + { + value -= (jit_nuint)address; + if(has_addend) + { + *((jit_nuint *)address) = + (*((jit_nuint *)address) & 0xFF000000) + value + addend; + } + else + { + *((jit_nuint *)address) += value; + } + return 1; + } + else if(type == R_ARM_ABS32) + { + if(has_addend) + { + *((jit_nuint *)address) = value + addend; + } + else + { + *((jit_nuint *)address) += value; + } + return 1; + } + else if(type == R_ARM_REL32) + { + value -= (jit_nuint)address; + if(has_addend) + { + *((jit_nuint *)address) = value + addend; + } + else + { + *((jit_nuint *)address) += value; + } + return 1; + } + return 0; +} + +#endif /* arm */ + +/* + * Apply relocations for the interpreted platform. + */ +static int interp_reloc(jit_readelf_t readelf, void *address, int type, + jit_nuint value, int has_addend, jit_nuint addend) +{ + /* We only have one type of relocation for the interpreter: direct */ + if(type == 1) + { + *((jit_nuint *)address) = value; + return 1; + } + else + { + return 0; + } +} + +/* + * Get the relocation function for a particular machine type. + */ +static jit_reloc_func get_reloc(unsigned int machine) +{ +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) + if(machine == EM_386) + { + return i386_reloc; + } +#endif +#if defined(__arm) || defined(__arm__) + if(machine == EM_ARM) + { + return arm_reloc; + } +#endif + if(machine == 0x4C6A) /* "Lj" for the libjit interpreter */ + { + return interp_reloc; + } + return 0; +} diff --git a/jit/jit-elf-write.c b/jit/jit-elf-write.c new file mode 100644 index 0000000..0e654a2 --- /dev/null +++ b/jit/jit-elf-write.c @@ -0,0 +1,541 @@ +/* + * jit-elf-write.c - Routines to write ELF-format binaries. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-elf-defs.h" +#include "jit-memory.h" +#include "jit-rules.h" + +/*@ + +@section Writing ELF binaries + +@*/ + +/* + * Determine whether we should be using the 32-bit or 64-bit ELF structures. + */ +#ifdef JIT_NATIVE_INT32 + typedef Elf32_Ehdr Elf_Ehdr; + typedef Elf32_Shdr Elf_Shdr; + typedef Elf32_Phdr Elf_Phdr; + typedef Elf32_Addr Elf_Addr; + typedef Elf32_Half Elf_Half; + typedef Elf32_Word Elf_Word; + typedef Elf32_Xword Elf_Xword; + typedef Elf32_Off Elf_Off; + typedef Elf32_Dyn Elf_Dyn; + typedef Elf32_Sym Elf_Sym; +#else + typedef Elf64_Ehdr Elf_Ehdr; + typedef Elf64_Shdr Elf_Shdr; + typedef Elf64_Phdr Elf_Phdr; + typedef Elf64_Addr Elf_Addr; + typedef Elf64_Half Elf_Half; + typedef Elf64_Word Elf_Word; + typedef Elf64_Xword Elf_Xword; + typedef Elf64_Off Elf_Off; + typedef Elf64_Dyn Elf_Dyn; + typedef Elf64_Sym Elf_Sym; +#endif + +/* + * Information about the contents of a section. + */ +typedef struct jit_section *jit_section_t; +struct jit_section +{ + Elf_Shdr shdr; + char *data; + unsigned int data_len; +}; + +/* + * Control structure for writing an ELF binary. + */ +struct jit_writeelf +{ + Elf_Ehdr ehdr; + jit_section_t sections; + int num_sections; + int regular_string_section; + int dynamic_string_section; +}; + +/* + * Get a string from the regular string section. + */ +static const char *get_string(jit_writeelf_t writeelf, Elf_Word index) +{ + if(writeelf->regular_string_section < 0) + { + /* The regular string section has not been created yet */ + return 0; + } + else + { + /* Retrieve the pointer to the string's starting point */ + return writeelf->sections[writeelf->regular_string_section].data + + (jit_nuint)index; + } +} + +/* + * Add a string to the regular string section. We don't worry about + * duplicate names because we only store section names here. And + * section names are only added when a new section is created. + */ +static Elf_Word add_string(jit_writeelf_t writeelf, const char *name) +{ + jit_section_t section; + char *data; + Elf_Word index; + unsigned int name_len = jit_strlen(name) + 1; + section = &(writeelf->sections[writeelf->regular_string_section]); + data = (char *)jit_realloc(section->data, section->data_len + name_len); + if(!data) + { + return 0; + } + section->data = data; + jit_strcpy(data + section->data_len, name); + index = (Elf_Word)(section->data_len); + section->data_len += name_len; + return index; +} + +/* + * Get a string from the dynamic string section. + */ +static const char *get_dyn_string(jit_writeelf_t writeelf, Elf_Word index) +{ + if(writeelf->dynamic_string_section < 0) + { + /* The dynamic string section has not been created yet */ + return 0; + } + else + { + /* Retrieve the pointer to the string's starting point */ + return writeelf->sections[writeelf->dynamic_string_section].data + + (jit_nuint)index; + } +} + +/* + * Add a string to the dynamic string section. + * + * TODO: use a hash table to cache previous names. + */ +static Elf_Word add_dyn_string(jit_writeelf_t writeelf, const char *name) +{ + jit_section_t section; + char *data; + Elf_Word index; + unsigned int name_len = jit_strlen(name) + 1; + section = &(writeelf->sections[writeelf->dynamic_string_section]); + data = (char *)jit_realloc(section->data, section->data_len + name_len); + if(!data) + { + return 0; + } + section->data = data; + jit_strcpy(data + section->data_len, name); + index = (Elf_Word)(section->data_len); + section->data_len += name_len; + return index; +} + +/* + * Get or add a section. + */ +static jit_section_t get_section + (jit_writeelf_t writeelf, const char *name, jit_int type, + Elf_Word flags, Elf_Word entry_size, Elf_Word alignment) +{ + int index; + jit_section_t section; + + /* Search the section table for an existing section by this name */ + for(index = 0; index < writeelf->num_sections; ++index) + { + section = &(writeelf->sections[index]); + if(!jit_strcmp(get_string(writeelf, section->shdr.sh_name), name)) + { + return section; + } + } + + /* Create a new section and clear it */ + section = (jit_section_t)jit_realloc + (writeelf->sections, + (writeelf->num_sections + 1) * sizeof(struct jit_section)); + if(!section) + { + return 0; + } + writeelf->sections = section; + section += writeelf->num_sections; + jit_memzero(section, sizeof(struct jit_section)); + + /* Set the section's name. If this is the first section created, + then it is the string table itself, and we have to add the + name to the section itself to start the ball rolling */ + if(writeelf->regular_string_section < 0) + { + section->data = (char *)jit_malloc(jit_strlen(name) + 2); + if(!(section->data)) + { + return 0; + } + section->data_len = jit_strlen(name) + 2; + section->data[0] = '\0'; /* Empty string is always first */ + jit_strcpy(section->data + 1, name); + section->shdr.sh_name = 1; + writeelf->regular_string_section = writeelf->num_sections; + } + else + { + section->shdr.sh_name = add_string(writeelf, name); + if(!(section->shdr.sh_name)) + { + return 0; + } + } + + /* Set the other section properties */ + section->shdr.sh_type = (Elf_Word)type; + section->shdr.sh_flags = flags; + section->shdr.sh_entsize = entry_size; + section->shdr.sh_addralign = alignment; + + /* Increase the section count and return */ + ++(writeelf->num_sections); + return section; +} + +/* + * Append data to a section. + */ +static int add_to_section + (jit_section_t section, const void *buf, unsigned int len) +{ + char *data = (char *)jit_realloc(section->data, section->data_len + len); + if(!data) + { + return 0; + } + section->data = data; + jit_memcpy(data + section->data_len, buf, len); + section->data_len += len; + return 1; +} + +/* + * Add an entry to the dynamic linking information section. + */ +static int add_dyn_info + (jit_writeelf_t writeelf, int type, Elf_Addr value, int modify_existing) +{ + jit_section_t section; + Elf_Dyn dyn; + + /* Get or create the ".dynamic" section */ + section = get_section(writeelf, ".dynamic", SHT_DYNAMIC, + SHF_WRITE | SHF_ALLOC, + sizeof(Elf_Dyn), sizeof(Elf_Dyn)); + if(!section) + { + return 0; + } + + /* See if we already have this entry, and modify it as appropriate */ + if(modify_existing) + { + Elf_Dyn *existing = (Elf_Dyn *)(section->data); + unsigned int num = section->data_len / sizeof(Elf_Dyn); + while(num > 0) + { + if(existing->d_tag == type) + { + existing->d_un.d_ptr = value; + return 1; + } + ++existing; + --num; + } + } + + /* Format the dynamic entry */ + jit_memzero(&dyn, sizeof(dyn)); + dyn.d_tag = type; + dyn.d_un.d_ptr = value; + + /* Add the entry to the section's contents */ + return add_to_section(section, &dyn, sizeof(dyn)); +} + +/*@ + * @deftypefun jit_writeelf_t jit_writeelf_create ({const char *} library_name) + * Create an object to assist with the process of writing an ELF binary. + * The @code{library_name} will be embedded into the binary. Returns NULL + * if out of memory. + * @end deftypefun +@*/ +jit_writeelf_t jit_writeelf_create(const char *library_name) +{ + jit_writeelf_t writeelf; + Elf_Word name_index; + union + { + jit_ushort value; + unsigned char bytes[2]; + + } un; + jit_elf_info_t elf_info; + + /* Create the writer control structure */ + writeelf = jit_cnew(struct jit_writeelf); + if(!writeelf) + { + return 0; + } + writeelf->regular_string_section = -1; + writeelf->dynamic_string_section = -1; + + /* Create the regular string section for section names, + which must be the first section that we create */ + if(!get_section(writeelf, ".shstrtab", SHT_STRTAB, 0, 0, 0)) + { + jit_writeelf_destroy(writeelf); + return 0; + } + + /* Create the dynamic string section, for dynamic linking symbols */ + if(!get_section(writeelf, ".dynstr", SHT_STRTAB, SHF_ALLOC, 0, 0)) + { + jit_writeelf_destroy(writeelf); + return 0; + } + writeelf->dynamic_string_section = writeelf->num_sections - 1; + if(!add_dyn_string(writeelf, "")) + { + jit_writeelf_destroy(writeelf); + return 0; + } + + /* Add the library name to the dynamic linking information section */ + name_index = add_dyn_string(writeelf, library_name); + if(!name_index) + { + jit_writeelf_destroy(writeelf); + return 0; + } + if(!add_dyn_info(writeelf, DT_SONAME, (Elf_Addr)name_index, 0)) + { + jit_writeelf_destroy(writeelf); + return 0; + } + + /* Fill in the Ehdr fields */ + writeelf->ehdr.e_ident[EI_MAG0] = ELFMAG0; + writeelf->ehdr.e_ident[EI_MAG1] = ELFMAG1; + writeelf->ehdr.e_ident[EI_MAG2] = ELFMAG2; + writeelf->ehdr.e_ident[EI_MAG3] = ELFMAG3; +#ifdef JIT_NATIVE_INT32 + writeelf->ehdr.e_ident[EI_CLASS] = ELFCLASS32; +#else + writeelf->ehdr.e_ident[EI_CLASS] = ELFCLASS64; +#endif + un.value = 0x0102; + if(un.bytes[0] == 0x01) + { + writeelf->ehdr.e_ident[EI_DATA] = ELFDATA2MSB; + } + else + { + writeelf->ehdr.e_ident[EI_DATA] = ELFDATA2LSB; + } + writeelf->ehdr.e_ident[EI_VERSION] = EV_CURRENT; + _jit_gen_get_elf_info(&elf_info); + writeelf->ehdr.e_ident[EI_OSABI] = (unsigned char)(elf_info.abi); + writeelf->ehdr.e_ident[EI_ABIVERSION] = + (unsigned char)(elf_info.abi_version); + writeelf->ehdr.e_machine = (Elf_Half)(elf_info.machine); + writeelf->ehdr.e_version = EV_CURRENT; + writeelf->ehdr.e_ehsize = sizeof(writeelf->ehdr); + + /* Every ELF binary that we generate will need "libjit.so" */ + if(!jit_writeelf_add_needed(writeelf, "libjit.so")) + { + jit_writeelf_destroy(writeelf); + return 0; + } + + /* We are ready to go */ + return writeelf; +} + +/*@ + * @deftypefun void jit_writeelf_destroy (jit_writeelf_t writeelf) + * Destroy the memory structures that were used while @code{writeelf} + * was being built. + * @end deftypefun +@*/ +void jit_writeelf_destroy(jit_writeelf_t writeelf) +{ + int index; + if(!writeelf) + { + return; + } + for(index = 0; index < writeelf->num_sections; ++index) + { + jit_free(writeelf->sections[index].data); + } + jit_free(writeelf->sections); + jit_free(writeelf); +} + +/*@ + * @deftypefun int jit_writeelf_write (jit_writeelf_t writeelf, {const char *} filename) + * Write a fully-built ELF binary to @code{filename}. Returns zero + * if an error occurred (reason in @code{errno}). + * @end deftypefun +@*/ +int jit_writeelf_write(jit_writeelf_t writeelf, const char *filename) +{ + /* TODO */ + return 1; +} + +/*@ + * @deftypefun int jit_writeelf_add_function (jit_writeelf_t writeelf, jit_function_t func, {const char *} name) + * Write the code for @code{func} to the ELF binary represented by + * @code{writeelf}. The function must already be compiled, and its + * context must have the @code{JIT_OPTION_PRE_COMPILE} option set + * to a non-zero value. Returns zero if out of memory or the + * parameters are invalid. + * @end deftypefun +@*/ +int jit_writeelf_add_function + (jit_writeelf_t writeelf, jit_function_t func, const char *name) +{ + /* TODO */ + return 1; +} + +/*@ + * @deftypefun int jit_writeelf_add_needed (jit_writeelf_t writeelf, {const char *} library_name) + * Add @code{library_name} to the list of dependent libraries that are needed + * when the ELF binary is reloaded. If @code{library_name} is already on + * the list, then this request will be silently ignored. Returns + * zero if out of memory or the parameters are invalid. + * @end deftypefun +@*/ +int jit_writeelf_add_needed(jit_writeelf_t writeelf, const char *library_name) +{ + jit_section_t section; + Elf_Dyn *dyn; + unsigned int num_dyn; + Elf_Word name_index; + if(!writeelf || !library_name) + { + return 0; + } + section = get_section(writeelf, ".dynamic", SHT_DYNAMIC, + SHF_WRITE | SHF_ALLOC, + sizeof(Elf_Dyn), sizeof(Elf_Dyn)); + if(!section) + { + return 0; + } + dyn = (Elf_Dyn *)(section->data); + num_dyn = section->data_len / sizeof(Elf_Dyn); + while(num_dyn > 0) + { + if(dyn->d_tag == DT_NEEDED && + !jit_strcmp(get_dyn_string(writeelf, (Elf_Word)(dyn->d_un.d_ptr)), + library_name)) + { + return 1; + } + ++dyn; + --num_dyn; + } + name_index = add_dyn_string(writeelf, library_name); + if(!name_index) + { + return 0; + } + if(!add_dyn_info(writeelf, DT_NEEDED, (Elf_Addr)name_index, 0)) + { + return 0; + } + return 1; +} + +/*@ + * @deftypefun int jit_writeelf_write_section (jit_writeelf_t writeelf, {const char *} name, jit_int type, {const void *} buf, {unsigned int} len, int discardable) + * Write auxillary data to a section called @code{name}. If @code{type} + * is not zero, then it indicates an ELF section type. This is used + * by virtual machines to store auxillary data that can be retrieved + * later using @code{jit_readelf_get_section}. If the section already + * contains data, then this will append the new data. If @code{discardable} + * is non-zero, then it is OK for this section to be discarded when the + * ELF binary is stripped. Returns zero if out of memory or the + * parameters are invalid. + * @end deftypefun +@*/ +int jit_writeelf_write_section + (jit_writeelf_t writeelf, const char *name, jit_int type, + const void *buf, unsigned int len, int discardable) +{ + jit_section_t section; + if(!writeelf || !name) + { + return 0; + } + if(!type) + { + /* Application-specific section type, for storing unspecified data */ + type = (jit_int)(SHT_LOUSER + 0x1234); + } + if(discardable) + { + section = get_section(writeelf, name, type, 0, 1, 1); + } + else + { + section = get_section(writeelf, name, type, SHF_ALLOC, 1, 1); + } + if(!section) + { + return 0; + } + if(len > 0) + { + return add_to_section(section, buf, len);; + } + else + { + return 1; + } +} diff --git a/jit/jit-except.cpp b/jit/jit-except.cpp new file mode 100644 index 0000000..ee527cb --- /dev/null +++ b/jit/jit-except.cpp @@ -0,0 +1,491 @@ +/* + * jit-except.cpp - Exception handling functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +/* + +This file must be compiled with a C++ compiler, because it uses +C++ exceptions to manage JIT exception throws. It is otherwise +straight vanilla ANSI C. + +*/ + +#include "jit-internal.h" +#include "jit-rules.h" +#include +#ifdef HAVE_STDLIB_H + #include +#endif +#if defined(JIT_BACKEND_INTERP) + #include "jit-interp.h" +#endif +#include +#include + +/*@ + +@cindex jit-except.h + +@*/ + +/*@ + * @deftypefun {void *} jit_exception_get_last (void) + * Get the last exception object that occurred on this thread, or NULL + * if there is no exception object on this thread. As far as @code{libjit} + * is concerned, an exception is just a pointer. The precise meaning of the + * data at the pointer is determined by the front end. + * @end deftypefun +@*/ +extern "C" void *jit_exception_get_last(void) +{ + jit_thread_control_t control = _jit_thread_get_control(); + if(control) + { + return control->last_exception; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun void jit_exception_set_last (void *object) + * Set the last exception object that occurred on this thread, so that + * it can be retrieved by a later call to @code{jit_exception_get_last}. + * This is normally used by @code{jit_function_apply} to save the + * exception object before returning to regular code. + * @end deftypefun +@*/ +extern "C" void jit_exception_set_last(void *object) +{ + jit_thread_control_t control = _jit_thread_get_control(); + if(control) + { + control->last_exception = object; + } +} + +/*@ + * @deftypefun void jit_exception_clear_last (void) + * Clear the last exception object that occurred on this thread. + * This is equivalent to calling @code{jit_exception_set_last} + * with a parameter of NULL. + * @end deftypefun +@*/ +extern "C" void jit_exception_clear_last(void) +{ + jit_exception_set_last(0); +} + +/*@ + * @deftypefun void jit_exception_throw ({void *} object) + * Throw an exception object within the current thread. As far as + * @code{libjit} is concerned, the exception object is just a pointer. + * The precise meaning of the data at the pointer is determined + * by the front end. + * + * Note: as an exception object works its way back up the stack, + * it may be temporarily stored in memory that is not normally visible + * to a garbage collector. The front-end is responsible for taking steps + * to "pin" the object so that it is uncollectable until explicitly + * copied back into a location that is visible to the collector once more. + * @end deftypefun +@*/ +extern "C" void jit_exception_throw(void *object) +{ +#if defined(JIT_BACKEND_INTERP) + throw new jit_exception(object); +#else + jit_thread_control_t control = _jit_thread_get_control(); + jit_backtrace_t trace; + if(control) + { + trace = control->backtrace_head; + while(trace != 0 && trace->pc != 0 && trace->catch_pc == 0) + { + trace = trace->parent; + } + if(trace) + { + if(trace->catch_pc) + { + /* We have a native "catch" clause at this level */ + _jit_backtrace_set(trace->parent); + _jit_gen_unwind_stack(trace->sp, trace->catch_pc, object); + } + else + { + /* The next higher level is "jit_function_apply_vararg", + so use "longjmp" to unwind the stack to that position */ + jit_exception_set_last(object); + longjmp(*((jmp_buf *)(trace->sp)), 1); + } + } + } +#endif +} + +/*@ + * @deftypefun void jit_exception_builtin (int exception_type) + * This function is called to report a builtin exception. + * The JIT will automatically embed calls to this function wherever a + * builtin exception needs to be reported. + * + * When a builtin exception occurs, the current thread's exception + * handler is called to construct an appropriate object, which is + * then thrown. + * + * If there is no exception handler set, or the handler returns NULL, + * then @code{libjit} will print an error message to stderr and cause + * the program to exit with a status of 1. You normally don't want + * this behavior and you should override it if possible. + * + * The following builtin exception types are currently supported: + * + * @table @code + * @vindex JIT_RESULT_OK + * @item JIT_RESULT_OK + * The operation was performed successfully (value is 1). + * + * @vindex JIT_RESULT_OVERFLOW + * @item JIT_RESULT_OVERFLOW + * The operation resulted in an overflow exception (value is 0). + * + * @vindex JIT_RESULT_ARITHMETIC + * @item JIT_RESULT_ARITHMETIC + * The operation resulted in an arithmetic exception. i.e. an attempt was + * made to divide the minimum integer value by -1 (value is -1). + * + * @vindex JIT_RESULT_DIVISION_BY_ZERO + * @item JIT_RESULT_DIVISION_BY_ZERO + * The operation resulted in a division by zero exception (value is -2). + * + * @vindex JIT_RESULT_COMPILE_ERROR + * @item JIT_RESULT_COMPILE_ERROR + * An error occurred when attempting to dynamically compile a function + * (value is -3). + * + * @vindex JIT_RESULT_OUT_OF_MEMORY + * @item JIT_RESULT_OUT_OF_MEMORY + * The system ran out of memory while performing an operation (value is -4). + * + * @vindex JIT_RESULT_NULL_REFERENCE + * @item JIT_RESULT_NULL_REFERENCE + * An attempt was made to dereference a NULL pointer (value is -5). + * + * @vindex JIT_RESULT_NULL_FUNCTION + * @item JIT_RESULT_NULL_FUNCTION + * An attempt was made to call a function with a NULL function pointer + * (value is -6). + * + * @vindex JIT_RESULT_CALLED_NESTED + * @item JIT_RESULT_CALLED_NESTED + * An attempt was made to call a nested function from a non-nested context + * (value is -7). + * @end table + * @end deftypefun +@*/ +extern "C" void jit_exception_builtin(int exception_type) +{ + jit_exception_func handler; + void *object; + static const char * const messages[9] = { + "Success", + "Overflow during checked arithmetic operation", + "Arithmetic exception (dividing the minimum integer by -1)", + "Division by zero", + "Error during function compilation", + "Out of memory", + "Null pointer dereferenced", + "Null function pointer called", + "Nested function called from non-nested context" + }; + #define num_messages (sizeof(messages) / sizeof(const char *)) + + /* Get the exception handler for this thread */ + handler = jit_exception_get_handler(); + + /* Invoke the exception handler to create an appropriate object */ + if(handler) + { + object = (*handler)(exception_type); + if(object) + { + jit_exception_throw(object); + } + } + + /* We don't have an exception handler, so print a message and exit */ + fputs("A builtin JIT exception could not be handled:\n", stderr); + exception_type = -(exception_type - 1); + if(exception_type >= 0 && exception_type < (int)num_messages) + { + fputs(messages[exception_type], stderr); + } + else + { + fprintf(stderr, "Unknown builtin exception %d", + (-exception_type) + 1); + } + putc('\n', stderr); + exit(1); +} + +/*@ + * @deftypefun jit_exception_func jit_exception_set_handler (jit_exception_func handler) + * Set the builtin exception handler for the current thread. + * Returns the previous exception handler. + * @end deftypefun +@*/ +extern "C" jit_exception_func jit_exception_set_handler + (jit_exception_func handler) +{ + jit_exception_func previous; + jit_thread_control_t control = _jit_thread_get_control(); + if(control) + { + previous = control->exception_handler; + control->exception_handler = handler; + return previous; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_exception_func jit_exception_get_handler (void) + * Get the builtin exception handler for the current thread. + * @end deftypefun +@*/ +extern "C" jit_exception_func jit_exception_get_handler(void) +{ + jit_thread_control_t control = _jit_thread_get_control(); + if(control) + { + return control->exception_handler; + } + else + { + return 0; + } +} + +/* + * Structure of a stack trace. + */ +struct jit_stack_trace +{ + unsigned int size; + void *items[1]; +}; + +/*@ + * @deftypefun jit_stack_trace_t jit_exception_get_stack_trace (void) + * Create an object that represents the current call stack. + * This is normally used to indicate the location of an exception. + * Returns NULL if a stack trace is not available, or there is + * insufficient memory to create it. + * @end deftypefun +@*/ +extern "C" jit_stack_trace_t jit_exception_get_stack_trace(void) +{ + jit_thread_control_t control; + jit_backtrace_t top; + jit_backtrace_t item; + unsigned int size; + jit_stack_trace_t trace; + + /* Count the number of items in the current thread's call stack */ + control = _jit_thread_get_control(); + if(!control) + { + return 0; + } + size = 0; + top = control->backtrace_head; + item = top; + while(item != 0) + { + ++size; + item = item->parent; + } + + /* Allocate memory for the stack trace */ + trace = (jit_stack_trace_t)jit_malloc + (sizeof(struct jit_stack_trace) + + size * sizeof(void *) - sizeof(void *)); + if(!trace) + { + return 0; + } + trace->size = size; + + /* Populate the stack trace with the items we counted earlier */ + size = 0; + item = top; + while(item != 0) + { + trace->items[size] = item->pc; + ++size; + item = item->parent; + } + return trace; +} + +/*@ + * @deftypefun {unsigned int} jit_stack_trace_get_size (jit_stack_trace_t trace) + * Get the size of a stack trace. + * @end deftypefun +@*/ +extern "C" unsigned int jit_stack_trace_get_size(jit_stack_trace_t trace) +{ + if(trace) + { + return trace->size; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_function_t jit_stack_trace_get_function (jit_context_t context, jit_stack_trace_t trace, {unsigned int} posn) + * Get the function that is at position @code{posn} within a stack trace. + * Position 0 is the function that created the stack trace. If this + * returns NULL, then it indicates that there is a native callout at + * @code{posn} within the stack trace. + * @end deftypefun +@*/ +extern "C" jit_function_t jit_stack_trace_get_function + (jit_context_t context, jit_stack_trace_t trace, unsigned int posn) +{ + if(trace && posn < trace->size) + { + jit_cache_t cache = _jit_context_get_cache(context); + if(cache) + { + return (jit_function_t)_jit_cache_get_method + (cache, trace->items[posn], 0); + } + } + return 0; +} + +/*@ + * @deftypefun {void *} jit_stack_trace_get_pc (jit_stack_trace_t trace, {unsigned int} posn) + * Get the program counter that corresponds to position @code{posn} + * within a stack trace. This is the point within the function + * where execution had reached at the time of the trace. + * @end deftypefun +@*/ +extern "C" void *jit_stack_trace_get_pc + (jit_stack_trace_t trace, unsigned int posn) +{ + if(trace && posn < trace->size) + { + return trace->items[posn]; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun {unsigned int} jit_stack_trace_get_offset (jit_stack_trace_t trace, {unsigned int} posn) + * Get the bytecode offset that is recorded for position @code{posn} + * within a stack trace. This will be @code{JIT_NO_OFFSET} if there + * is no bytecode offset associated with @code{posn}. + * @end deftypefun +@*/ +extern "C" unsigned int jit_stack_trace_get_offset + (jit_context_t context, jit_stack_trace_t trace, unsigned int posn) +{ + /* TODO */ + return 0; +} + +/*@ + * @deftypefun void jit_stack_trace_free (jit_stack_trace_t trace) + * Free the memory associated with a stack trace. + * @end deftypefun +@*/ +extern "C" void jit_stack_trace_free(jit_stack_trace_t trace) +{ + if(trace) + { + jit_free(trace); + } +} + +extern "C" void _jit_backtrace_push + (jit_backtrace_t trace, void *pc, void *catch_pc, void *sp) +{ + jit_thread_control_t control = _jit_thread_get_control(); + if(control) + { + trace->parent = control->backtrace_head; + trace->pc = pc; + trace->catch_pc = catch_pc; + trace->sp = sp; + trace->security_object = 0; + trace->free_security_object = 0; + control->backtrace_head = trace; + } + else + { + trace->parent = 0; + trace->pc = pc; + trace->catch_pc = catch_pc; + trace->sp = sp; + trace->security_object = 0; + trace->free_security_object = 0; + } +} + +extern "C" void _jit_backtrace_pop(void) +{ + jit_thread_control_t control = _jit_thread_get_control(); + jit_backtrace_t trace; + if(control) + { + trace = control->backtrace_head; + if(trace) + { + control->backtrace_head = trace->parent; + if(trace->security_object && trace->free_security_object) + { + (*(trace->free_security_object))(trace->security_object); + } + } + } +} + +extern "C" void _jit_backtrace_set(jit_backtrace_t trace) +{ + jit_thread_control_t control = _jit_thread_get_control(); + if(control) + { + control->backtrace_head = trace; + } +} diff --git a/jit/jit-function.c b/jit/jit-function.c new file mode 100644 index 0000000..0fc1cf0 --- /dev/null +++ b/jit/jit-function.c @@ -0,0 +1,1380 @@ +/* + * jit-function.c - Functions for manipulating function blocks. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-memory.h" +#include "jit-rules.h" +#include "jit-reg-alloc.h" +#include "jit-apply-func.h" +#include + +/*@ + * @deftypefun jit_function_t jit_function_create (jit_context_t context, jit_type_t signature) + * Create a new function block and associate it with a JIT context. + * Returns NULL if out of memory. + * + * A function persists for the lifetime of its containing context. + * It initially starts life in the "building" state, where the user + * constructs instructions that represents the function body. + * Once the build process is complete, the user calls + * @code{jit_function_compile} to convert it into its executable form. + * + * It is recommended that you call @code{jit_context_build_start} before + * calling @code{jit_function_create}, and then call + * @code{jit_context_build_end} after you have called + * @code{jit_function_compile}. This will protect the JIT's internal + * data structures within a multi-threaded environment. + * @end deftypefun +@*/ +jit_function_t jit_function_create(jit_context_t context, jit_type_t signature) +{ + jit_function_t func; + + /* Allocate memory for the function and clear it */ + func = jit_cnew(struct _jit_function); + if(!func) + { + return 0; + } + + /* Initialize the function block */ + func->context = context; + func->signature = jit_type_copy(signature); + +#if defined(jit_redirector_size) && !defined(JIT_BACKEND_INTERP) + /* If we aren't using interpretation, then point the function's + initial entry point at the redirector, which in turn will + invoke the on-demand compiler */ + func->entry_point = _jit_create_redirector + (func->redirector, (void *)_jit_function_compile_on_demand, + func, jit_type_get_abi(signature)); + func->closure_entry = func->entry_point; +#endif + + /* Add the function to the context list */ + func->next = 0; + func->prev = context->last_function; + if(context->last_function) + { + context->last_function->next = func; + } + else + { + context->functions = func; + } + context->last_function = func; + + /* Return the function to the caller */ + return func; +} + +/*@ + * @deftypefun jit_function_t jit_function_create_nested (jit_context_t context, jit_type_t signature, jit_function_t parent) + * Create a new function block and associate it with a JIT context. + * In addition, this function is nested inside the specified + * @code{parent} function and is able to access its parent's + * (and grandparent's) local variables. + * + * The front end is responsible for ensuring that the nested function can + * never be called by anyone except its parent and sibling functions. + * The front end is also responsible for ensuring that the nested function + * is compiled before its parent. + * @end deftypefun +@*/ +jit_function_t jit_function_create_nested + (jit_context_t context, jit_type_t signature, jit_function_t parent) +{ + jit_function_t func; + func = jit_function_create(context, signature); + if(!func) + { + return 0; + } + func->nested_parent = parent; + return func; +} + +int _jit_function_ensure_builder(jit_function_t func) +{ + /* Handle the easy cases first */ + if(!func) + { + return 0; + } + if(func->builder) + { + return 1; + } + + /* Allocate memory for the builder and clear it */ + func->builder = jit_cnew(struct _jit_builder); + if(!(func->builder)) + { + return 0; + } + + /* Initialize the function builder */ + jit_memory_pool_init(&(func->builder->value_pool), struct _jit_value); + jit_memory_pool_init(&(func->builder->insn_pool), struct _jit_insn); + jit_memory_pool_init(&(func->builder->meta_pool), struct _jit_meta); + + /* Create the initial entry block */ + if(!_jit_block_init(func)) + { + _jit_function_free_builder(func); + return 0; + } + + /* Create instructions to initialize the incoming arguments */ + if(!_jit_create_entry_insns(func)) + { + _jit_function_free_builder(func); + return 0; + } + + /* The builder is ready to go */ + return 1; +} + +void _jit_function_free_builder(jit_function_t func) +{ + if(func->builder) + { + _jit_block_free(func); + jit_memory_pool_free(&(func->builder->insn_pool), 0); + jit_memory_pool_free(&(func->builder->value_pool), _jit_value_free); + jit_memory_pool_free(&(func->builder->meta_pool), _jit_meta_free_one); + jit_free(func->builder->param_values); + jit_free(func->builder->insns); + jit_free(func->builder->label_blocks); + jit_free(func->builder); + func->builder = 0; + } +} + +void _jit_function_destroy(jit_function_t func) +{ + if(!func) + { + return; + } + if(func->next) + { + func->next->prev = func->prev; + } + else + { + func->context->last_function = func->prev; + } + if(func->prev) + { + func->prev->next = func->next; + } + else + { + func->context->functions = func->next; + } + _jit_function_free_builder(func); + jit_meta_destroy(&(func->meta)); + jit_free(func); +} + +/*@ + * @deftypefun void jit_function_abandon (jit_function_t func) + * Abandon this function during the build process. This should be called + * when you detect a fatal error that prevents the function from being + * properly built. The @code{func} object is completely destroyed and + * detached from its owning context. The function is left alone if + * it was already compiled. + * @end deftypefun +@*/ +void jit_function_abandon(jit_function_t func) +{ + if(func && func->builder) + { + if(func->is_compiled) + { + /* We already compiled this function previously, but we + have tried to recompile it with new contents. Throw + away the builder, but keep the original version */ + _jit_function_free_builder(func); + } + else + { + /* This function was never compiled, so abandon entirely */ + _jit_function_destroy(func); + } + } +} + +/*@ + * @deftypefun jit_context_t jit_function_get_context (jit_function_t func) + * Get the context associated with a function. + * @end deftypefun +@*/ +jit_context_t jit_function_get_context(jit_function_t func) +{ + if(func) + { + return func->context; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_type_t jit_function_get_signature (jit_function_t func) + * Get the signature associated with a function. + * @end deftypefun +@*/ +jit_type_t jit_function_get_signature(jit_function_t func) +{ + if(func) + { + return func->signature; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_function_set_meta (jit_function_t func, int type, {void *} data, jit_meta_free_func free_data, int build_only) + * Tag a function with some metadata. Returns zero if out of memory. + * + * Metadata may be used to store dependency graphs, branch prediction + * information, or any other information that is useful to optimizers + * or code generators. It can also be used by higher level user code + * to store information about the function that is specific to the + * virtual machine or language. + * + * If the @code{type} already has some metadata associated with it, then + * the previous value will be freed. + * + * If @code{build_only} is non-zero, then the metadata will be freed + * when the function is compiled with @code{jit_function_compile}. + * Otherwise the metadata will persist until the JIT context is destroyed, + * or @code{jit_function_free_meta} is called for the specified @code{type}. + * + * Metadata type values of 10000 or greater are reserved for internal use. + * @end deftypefun +@*/ +int jit_function_set_meta(jit_function_t func, int type, void *data, + jit_meta_free_func free_data, int build_only) +{ + if(build_only) + { + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + return jit_meta_set(&(func->builder->meta), type, data, + free_data, func); + } + else + { + return jit_meta_set(&(func->meta), type, data, free_data, 0); + } +} + +/*@ + * @deftypefun {void *} jit_function_get_meta (jit_function_t func, int type) + * Get the metadata associated with a particular tag. Returns NULL + * if @code{type} does not have any metadata associated with it. + * @end deftypefun +@*/ +void *jit_function_get_meta(jit_function_t func, int type) +{ + void *data = jit_meta_get(func->meta, type); + if(!data && func->builder) + { + data = jit_meta_get(func->builder->meta, type); + } + return data; +} + +/*@ + * @deftypefun void jit_function_free_meta (jit_function_t func, int type) + * Free metadata of a specific type on a function. Does nothing if + * the @code{type} does not have any metadata associated with it. + * @end deftypefun +@*/ +void jit_function_free_meta(jit_function_t func, int type) +{ + jit_meta_free(&(func->meta), type); + if(func->builder) + { + jit_meta_free(&(func->builder->meta), type); + } +} + +/*@ + * @deftypefun jit_function_t jit_function_next (jit_context_t context, jit_function_t prev) + * Iterate over the defined functions in creation order. The @code{prev} + * argument should be NULL on the first call. Returns NULL at the end. + * @end deftypefun +@*/ +jit_function_t jit_function_next(jit_context_t context, jit_function_t prev) +{ + if(prev) + { + return prev->next; + } + else if(context) + { + return context->functions; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_function_t jit_function_previous (jit_context_t context, jit_function_t prev) + * Iterate over the defined functions in reverse creation order. + * @end deftypefun +@*/ +jit_function_t jit_function_previous(jit_context_t context, + jit_function_t prev) +{ + if(prev) + { + return prev->prev; + } + else if(context) + { + return context->last_function; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_block_t jit_function_get_entry (jit_function_t func) + * Get the entry block for a function. This is always the first block + * created by @code{jit_function_create}. + * @end deftypefun +@*/ +jit_block_t jit_function_get_entry(jit_function_t func) +{ + if(func && func->builder) + { + return func->builder->entry; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_block_t jit_function_get_current (jit_function_t func) + * Get the current block for a function. New blocks are created by + * certain @code{jit_insn_xxx} calls. + * @end deftypefun +@*/ +jit_block_t jit_function_get_current(jit_function_t func) +{ + if(func && func->builder) + { + return func->builder->current_block; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_function_t jit_function_get_nested_parent (jit_function_t func) + * Get the nested parent for a function, or NULL if @code{func} + * does not have a nested parent. + * @end deftypefun +@*/ +jit_function_t jit_function_get_nested_parent(jit_function_t func) +{ + if(func) + { + return func->nested_parent; + } + else + { + return 0; + } +} + +/* + * Call "finally" blocks that are relevant for a transition from + * "src" to "dest". "dest" should be NULL for a return instruction. + */ +static void call_finally_blocks(jit_gencode_t gen, jit_function_t func, + jit_block_eh_t src, jit_block_eh_t dest) +{ + jit_block_eh_t temp; + + /* Bail out if we are branching to an inner scope, because we + don't need to call "finally" clauses to go further in */ + temp = dest; + while(temp != 0) + { + if(temp == src) + { + return; + } + temp = temp->parent; + } + + /* Find the common ancestor of "src" and "dest" */ + while(dest != 0) + { + temp = src; + while(temp != 0 && temp != dest) + { + temp = temp->parent; + } + if(temp != 0) + { + break; + } + dest = dest->parent; + } + + /* Output all finally blocks between "src" and "dest" */ + while(src != dest) + { + if(src->catch_label != jit_label_undefined && + !(src->finally_on_fault)) + { + /* Call "finally" from the body of the "try" */ + if(src->finally_label != jit_label_undefined) + { + _jit_gen_call_finally(gen, func, src->finally_label); + } + } + else if(src->catch_label == jit_label_undefined) + { + /* Call "finally" from the body of the "catch" */ + if(src->finally_label != jit_label_undefined) + { + _jit_gen_call_finally(gen, func, src->finally_label); + } + } + src = src->parent; + } +} + +/* + * Compile a single basic block within a function. + */ +static void compile_block(jit_gencode_t gen, jit_function_t func, + jit_block_t block) +{ + jit_insn_iter_t iter; + jit_insn_t insn; + jit_block_t branch_block; + + /* Iterate over all blocks in the function */ + jit_insn_iter_init(&iter, block); + while((insn = jit_insn_iter_next(&iter)) != 0) + { + switch(insn->opcode) + { + case JIT_OP_NOP: break; /* Ignore NOP's */ + + case JIT_OP_CHECK_NULL: + { + /* Determine if we can optimize the null check away */ + if(!_jit_insn_check_is_redundant(&iter)) + { + _jit_gen_insn(gen, func, block, insn); + } + } + break; + + case JIT_OP_PREPARE_FOR_LEAVE: + { + /* Call the finally clauses that are relevant to the + "JIT_OP_BRANCH" instruction that follows */ + _jit_regs_spill_all(gen); + insn = jit_insn_iter_next(&iter); + branch_block = jit_block_from_label + (func, (jit_label_t)(insn->dest)); + call_finally_blocks + (gen, func, block->block_eh, branch_block->block_eh); + _jit_gen_insn(gen, func, block, insn); + } + break; + + case JIT_OP_PREPARE_FOR_RETURN: + { + /* Call all finally clauses on the way out of the function */ + _jit_regs_spill_all(gen); + call_finally_blocks(gen, func, block->block_eh, 0); + } + break; + + case JIT_OP_INCOMING_REG: + { + /* Assign a register to an incoming value */ + _jit_regs_set_incoming + (gen, (int)jit_value_get_nint_constant(insn->value2), + insn->value1); + } + break; + + case JIT_OP_INCOMING_FRAME_POSN: + { + /* Set the frame position for an incoming value */ + insn->value1->frame_offset = + jit_value_get_nint_constant(insn->value2); + insn->value1->in_register = 0; + insn->value1->in_frame = 1; + insn->value1->has_frame_offset = 1; + } + break; + + case JIT_OP_OUTGOING_REG: + { + /* Copy a value into an outgoing register */ + _jit_regs_set_outgoing + (gen, (int)jit_value_get_nint_constant(insn->value2), + insn->value1); + } + break; + + case JIT_OP_RETURN_REG: + { + /* Assign a register to a return value */ + _jit_regs_set_incoming + (gen, (int)jit_value_get_nint_constant(insn->value2), + insn->value1); + _jit_gen_insn(gen, func, block, insn); + } + break; + + default: + { + /* Generate code for the instruction with the back end */ + _jit_gen_insn(gen, func, block, insn); + } + break; + } + } +} + +/* + * Information that is stored for an exception region in the cache. + */ +typedef struct jit_cache_eh *jit_cache_eh_t; +struct jit_cache_eh +{ + jit_label_t handler_label; + unsigned char *handler; + jit_cache_eh_t previous; +}; + +/*@ + * @deftypefun int jit_function_compile (jit_function_t func) + * Compile a function to its executable form. If the function was + * already compiled, then do nothing. Returns zero on error. + * + * If an error occurs, you can use @code{jit_function_abandon} to + * completely destroy the function. Once the function has been compiled + * successfully, it can no longer be abandoned. + * + * Sometimes you may wish to recompile a function, to apply greater + * levels of optimization the second time around. You must call + * @code{jit_function_set_recompilable} before you compile the function + * the first time. On the second time around, build the function's + * instructions again, and call @code{jit_function_compile} + * a second time. + * @end deftypefun +@*/ +int jit_function_compile(jit_function_t func) +{ + struct jit_gencode gen; + jit_cache_t cache; + void *start; + void *recompilable_start = 0; + void *end; + jit_block_t block; + jit_block_eh_t eh; + jit_block_eh_t new_eh; + jit_cache_eh_t cache_eh; + jit_cache_eh_t cache_new_eh; + int result; +#ifdef JIT_PROLOG_SIZE + int have_prolog; +#endif + + /* Bail out if we have nothing to do */ + if(!func) + { + return 0; + } + if(func->is_compiled && !(func->builder)) + { + /* The function is already compiled, and we don't need to recompile */ + return 1; + } + if(!(func->builder)) + { + /* We don't have anything to compile at all */ + return 0; + } + + /* We need the cache lock while we are compiling the function */ + jit_mutex_lock(&(func->context->cache_lock)); + + /* Get the method cache */ + cache = _jit_context_get_cache(func->context); + if(!cache) + { + jit_mutex_unlock(&(func->context->cache_lock)); + return 0; + } + + /* Initialize the code generation state */ + jit_memzero(&gen, sizeof(gen)); + + /* Compute liveness and "next use" information for this function */ + _jit_function_compute_liveness(func); + + /* We may need to perform output twice, if the first attempt fails + due to a lack of space in the current method cache page */ + do + { + /* Start function output to the cache */ + start = _jit_cache_start_method + (cache, &(gen.posn), JIT_FUNCTION_ALIGNMENT, func); + if(!start) + { +#ifdef jit_extra_gen_cleanup + /* Clean up the extra code generation state */ + jit_extra_gen_cleanup(gen); +#endif + jit_mutex_unlock(&(func->context->cache_lock)); + return 0; + } + +#ifdef jit_extra_gen_init + /* Initialize information that may need to be reset each loop */ + jit_extra_gen_init(&gen); +#endif + +#ifdef JIT_PROLOG_SIZE + /* Output space for the function prolog */ + if(jit_cache_check_for_n(&(gen.posn), JIT_PROLOG_SIZE)) + { + gen.posn.ptr += JIT_PROLOG_SIZE; + have_prolog = 1; + } + else + { + have_prolog = 0; + } +#endif + + /* Clear the register assignments for the first block */ + _jit_regs_init_for_block(&gen); + + /* Generate code for the blocks in the function */ + block = 0; + eh = 0; + cache_eh = 0; + while((block = jit_block_next(func, block)) != 0) + { + /* If this block is never entered, then discard it */ + if(!(block->entered_via_top) && !(block->entered_via_branch)) + { + continue; + } + + /* Start a new exception region in the cache if necessary */ + new_eh = block->block_eh; + if(new_eh && new_eh->catch_label == jit_label_undefined) + { + new_eh = 0; + } + if(new_eh != eh) + { + eh = new_eh; + cache_new_eh = _jit_cache_alloc + (&(gen.posn), sizeof(struct jit_cache_eh)); + if(cache_new_eh) + { + if(eh) + { + cache_new_eh->handler_label = eh->catch_label; + } + else + { + cache_new_eh->handler_label = jit_label_undefined; + } + cache_new_eh->handler = 0; + cache_new_eh->previous = cache_eh; + _jit_cache_new_region(&(gen.posn), cache_new_eh); + cache_eh = cache_new_eh; + } + } + + /* Notify the back end that the block is starting */ + _jit_gen_start_block(&gen, block); + + /* Generate the block's code */ + compile_block(&gen, func, block); + + /* Spill all live register values back to their frame positions */ + _jit_regs_spill_all(&gen); + + /* Notify the back end that the block is finished */ + _jit_gen_end_block(&gen, block); + + /* Clear the local register assignments, ready for the next block */ + _jit_regs_init_for_block(&gen); + } + + /* Fix up the labels for the exception regions */ + while(cache_eh != 0) + { + if(cache_eh->handler_label != jit_label_undefined) + { + block = jit_block_from_label(func, cache_eh->handler_label); + if(block) + { + cache_eh->handler = (unsigned char *)(block->address); + } + } + cache_eh = cache_eh->previous; + } + + /* Output the function epilog. All return paths will jump to here */ + _jit_gen_epilog(&gen, func); + end = gen.posn.ptr; + +#ifdef JIT_PROLOG_SIZE + /* Back-patch the function prolog and get the real entry point */ + if(have_prolog) + { + start = _jit_gen_prolog(&gen, func, start); + } +#endif + + /* If the function is recompilable, then we need an extra entry + point to properly redirect previous references to the function */ + if(func->is_recompilable) + { + recompilable_start = _jit_gen_redirector(&gen, func); + } + + /* End the function's output process */ + result = _jit_cache_end_method(&(gen.posn)); + } + while(result == JIT_CACHE_END_RESTART); + +#ifdef jit_extra_gen_cleanup + /* Clean up the extra code generation state */ + jit_extra_gen_cleanup(gen); +#endif + + /* Bail out if we ran out of memory while translating the function */ + if(result != JIT_CACHE_END_OK) + { + jit_mutex_unlock(&(func->context->cache_lock)); + return 0; + } + +#ifndef JIT_BACKEND_INTERP + /* Perform a CPU cache flush, to make the code executable */ + jit_flush_exec(start, (unsigned int)(((unsigned char *)end) - + ((unsigned char *)start))); +#endif + + /* Intuit "nothrow" and "noreturn" flags for this function */ + if(!(func->builder->may_throw)) + { + func->no_throw = 1; + } + if(!(func->builder->ordinary_return)) + { + func->no_return = 1; + } + + /* Record the entry point */ + func->entry_point = start; + if(recompilable_start) + { + func->closure_entry = recompilable_start; + } + else + { + func->closure_entry = start; + } + func->is_compiled = 1; + + /* Free the builder structure, which we no longer require */ + _jit_function_free_builder(func); + + /* The function has been compiled successfully */ + jit_mutex_unlock(&(func->context->cache_lock)); + return 1; +} + +/*@ + * @deftypefun int jit_function_recompile (jit_function_t func) + * Force @code{func} to be recompiled, by calling its on-demand + * compiler again. It is highly recommended that you set the + * recompilable flag with @code{jit_function_set_recompilable} + * when you initially create the function. + * + * This function returns one of @code{JIT_RESULT_OK}, + * @code{JIT_RESULT_COMPILE_ERROR}, or @code{JIT_RESULT_OUT_OF_MEMORY}. + * @end deftypefun +@*/ +int jit_function_recompile(jit_function_t func) +{ + int result; + + /* Lock down the context */ + jit_context_build_start(func->context); + + /* Call the user's on-demand compiler if we don't have a builder yet. + Bail out with an error if there is no on-demand compiler */ + if(!(func->builder)) + { + if(func->on_demand) + { + result = (*(func->on_demand))(func); + if(result != JIT_RESULT_OK) + { + _jit_function_free_builder(func); + jit_context_build_end(func->context); + return result; + } + } + else + { + jit_context_build_end(func->context); + return JIT_RESULT_COMPILE_ERROR; + } + } + + /* Compile the function */ + if(!jit_function_compile(func)) + { + _jit_function_free_builder(func); + jit_context_build_end(func->context); + return JIT_RESULT_OUT_OF_MEMORY; + } + + /* Unlock the context and report that we are ready to go */ + jit_context_build_end(func->context); + return JIT_RESULT_OK; +} + +/*@ + * @deftypefun int jit_function_is_compiled (jit_function_t func) + * Determine if a function has already been compiled. + * @end deftypefun +@*/ +int jit_function_is_compiled(jit_function_t func) +{ + if(func) + { + return func->is_compiled; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_function_set_recompilable (jit_function_t func) + * Mark this function as a candidate for recompilation. That is, + * it is possible that we may call @code{jit_function_compile} + * more than once, to re-optimize an existing function. + * + * It is very important that this be called before the first time that + * you call @code{jit_function_compile}. Functions that are recompilable + * are invoked in a slightly different way to non-recompilable functions. + * If you don't set this flag, then existing invocations of the function + * may continue to be sent to the original compiled version, not the new + * version. + * @end deftypefun +@*/ +void jit_function_set_recompilable(jit_function_t func) +{ + if(func) + { + func->is_recompilable = 1; + } +} + +/*@ + * @deftypefun void jit_function_clear_recompilable (jit_function_t func) + * Clear the recompilable flag on this function. Normally you would use + * this once you have decided that the function has been optimized enough, + * and that you no longer intend to call @code{jit_function_compile} again. + * + * Future uses of the function with @code{jit_insn_call} will output a + * direct call to the function, which is more efficient than calling + * its recompilable version. Pre-existing calls to the function may still + * use redirection stubs, and will remain so until the pre-existing + * functions are themselves recompiled. + * @end deftypefun +@*/ +void jit_function_clear_recompilable(jit_function_t func) +{ + if(func) + { + func->is_recompilable = 0; + } +} + +/*@ + * @deftypefun int jit_function_is_recompilable (jit_function_t func) + * Determine if this function is recompilable. + * @end deftypefun +@*/ +int jit_function_is_recompilable(jit_function_t func) +{ + if(func) + { + return func->is_recompilable; + } + else + { + return 0; + } +} + +#ifdef JIT_BACKEND_INTERP + +/* + * Closure handling function for "jit_function_to_closure". + */ +static void function_closure(jit_type_t signature, void *result, + void **args, void *user_data) +{ + if(!jit_function_apply((jit_function_t)user_data, args, result)) + { + /* We cannot report the exception through the closure, + so we have no choice but to rethrow it up the stack */ + jit_exception_throw(jit_exception_get_last()); + } +} + +#endif /* JIT_BACKEND_INTERP */ + +/*@ + * @deftypefun {void *} jit_function_to_closure (jit_function_t func) + * Convert a compiled function into a closure that can called directly + * from C. Returns NULL if out of memory, or if closures are not + * supported on this platform. + * + * If the function has not been compiled yet, then this will return + * a pointer to a redirector that will arrange for the function to be + * compiled on-demand when it is called. + * + * Creating a closure for a nested function is not recommended as + * C does not have any way to call such closures directly. + * @end deftypefun +@*/ +void *jit_function_to_closure(jit_function_t func) +{ + if(!func) + { + return 0; + } +#ifdef JIT_BACKEND_INTERP + return jit_closure_create(func->context, func->signature, + function_closure, (void *)func); +#else + /* On native platforms, use the closure entry point */ + return func->closure_entry; +#endif +} + +/*@ + * @deftypefun jit_function_t jit_function_from_closure (jit_context_t context, {void *} closure) + * Convert a closure back into a function. Returns NULL if the + * closure does not correspond to a function in the specified context. + * @end deftypefun +@*/ +jit_function_t jit_function_from_closure(jit_context_t context, void *closure) +{ + void *cookie; + if(!context || !(context->cache)) + { + return 0; + } + return (jit_function_t)_jit_cache_get_method + (context->cache, closure, &cookie); +} + +/*@ + * @deftypefun jit_function_t jit_function_from_pc (jit_context_t context, {void *} pc, {void **} handler) + * Get the function that contains the specified program counter location. + * Also return the address of the @code{catch} handler for the same location. + * Returns NULL if the program counter does not correspond to a function + * under the control of @code{context}. + * @end deftypefun +@*/ +jit_function_t jit_function_from_pc + (jit_context_t context, void *pc, void **handler) +{ + jit_function_t func; + void *cookie; + + /* Bail out if we don't have a function cache yet */ + if(!context || !(context->cache)) + { + return 0; + } + + /* Get the function and the exception handler cookie */ + func = (jit_function_t)_jit_cache_get_method(context->cache, pc, &cookie); + if(!func) + { + return 0; + } + + /* Convert the cookie into a handler address */ + if(handler) + { + if(cookie) + { + *handler = ((jit_cache_eh_t)cookie)->handler; + } + else + { + *handler = 0; + } + } + return func; +} + +/*@ + * @deftypefun {void *} jit_function_to_vtable_pointer (jit_function_t func) + * Return a pointer that is suitable for referring to this function + * from a vtable. Such pointers should only be used with the + * @code{jit_insn_call_vtable} instruction. + * + * Using @code{jit_insn_call_vtable} is generally more efficient than + * @code{jit_insn_call_indirect} for calling virtual methods. + * + * The vtable pointer might be the same as the closure, but this isn't + * guaranteed. Closures can be used with @code{jit_insn_call_indirect}. + * @end deftypefun +@*/ +void *jit_function_to_vtable_pointer(jit_function_t func) +{ +#ifdef JIT_BACKEND_INTERP + /* In the interpreted version, the function pointer is used in vtables */ + return func; +#else + /* On native platforms, the closure entry point is the vtable pointer */ + if(func) + { + return func->closure_entry; + } + else + { + return 0; + } +#endif +} + +/*@ + * @deftypefun void jit_function_set_on_demand_compiler (jit_function_t func, jit_on_demand_func on_demand) + * Specify the C function to be called when @code{func} needs to be + * compiled on-demand. This should be set just after the function + * is created, before any build or compile processes begin. + * + * You won't need an on-demand compiler if you always build and compile + * your functions before you call them. But if you can call a function + * before it is built, then you must supply an on-demand compiler. + * + * When on-demand compilation is requested, @code{libjit} takes the following + * actions: + * + * @enumerate + * @item + * The context is locked by calling @code{jit_context_build_start}. + * + * @item + * If the function has already been compiled, @code{libjit} unlocks + * the context and returns immediately. This can happen because of race + * conditions between threads: some other thread may have beaten us + * to the on-demand compiler. + * + * @item + * The user's on-demand compiler is called. It is responsible for building + * the instructions in the function's body. It should return one of the + * result codes @code{JIT_RESULT_OK}, @code{JIT_RESULT_COMPILE_ERROR}, + * or @code{JIT_RESULT_OUT_OF_MEMORY}. + * + * @item + * If the user's on-demand function hasn't already done so, @code{libjit} + * will call @code{jit_function_compile} to compile the function. + * + * @item + * The context is unlocked by calling @code{jit_context_build_end} and + * @code{libjit} jumps to the newly-compiled entry point. If an error + * occurs, a built-in exception of type @code{JIT_RESULT_COMPILE_ERROR} + * or @code{JIT_RESULT_OUT_OF_MEMORY} will be thrown. + * @end enumerate + * + * Normally you will need some kind of context information to tell you + * which higher-level construct is being compiled. You can use the + * metadata facility to add this context information to the function + * just after you create it with @code{jit_function_create}. + * @end deftypefun +@*/ +void jit_function_set_on_demand_compiler + (jit_function_t func, jit_on_demand_func on_demand) +{ + func->on_demand = on_demand; +} + +void *_jit_function_compile_on_demand(jit_function_t func) +{ + void *entry = 0; + int result = JIT_RESULT_OK; + + /* Lock down the context */ + jit_context_build_start(func->context); + + /* If we are already compiled, then bail out */ + if(func->is_compiled) + { + entry = func->entry_point; + jit_context_build_end(func->context); + return entry; + } + + /* Call the user's on-demand compiler. Bail out with an error + if the user didn't supply an on-demand compiler */ + if(func->on_demand) + { + result = (*(func->on_demand))(func); + if(result == JIT_RESULT_OK) + { + /* Compile the function if the user didn't do so */ + if(!(func->is_compiled)) + { + if(jit_function_compile(func)) + { + entry = func->entry_point; + } + else + { + result = JIT_RESULT_OUT_OF_MEMORY; + } + } + else + { + entry = func->entry_point; + } + } + _jit_function_free_builder(func); + } + else + { + result = JIT_RESULT_COMPILE_ERROR; + } + + /* Unlock the context and report the result */ + jit_context_build_end(func->context); + if(result != JIT_RESULT_OK) + { + jit_exception_builtin(result); + } + return entry; +} + +/*@ + * @deftypefun int jit_function_apply (jit_function_t func, {void **} args, {void *} return_area) + * Call the function @code{func} with the supplied arguments. Each element + * in @code{args} is a pointer to one of the arguments, and @code{return_area} + * points to a buffer to receive the return value. Returns zero if an + * exception occurred. + * + * This is the primary means for executing a function from ordinary + * C code without creating a closure first with @code{jit_function_to_closure}. + * Closures may not be supported on all platforms, but function application + * is guaranteed to be supported everywhere. + * + * Function applications acts as an exception blocker. If any exceptions + * occur during the execution of @code{func}, they won't travel up the + * stack any further than this point. This prevents ordinary C code + * from being accidentally presented with a situation that it cannot handle. + * This blocking protection is not present when a function is invoked + * via its closure. + * @end deftypefun + * + * @deftypefun int jit_function_apply_vararg (jit_function_t func, jit_type_t signature, {void **} args, {void *} return_area) + * Call the function @code{func} with the supplied arguments. There may + * be more arguments than are specified in the function's original signature, + * in which case the additional values are passed as variable arguments. + * This function is otherwise identical to @code{jit_function_apply}. + * @end deftypefun +@*/ +#if !defined(JIT_BACKEND_INTERP) +/* The interpreter version is in "jit-interp.cpp" */ + +int jit_function_apply(jit_function_t func, void **args, void *return_area) +{ + if(!func) + { + return 0; + } + else + { + return jit_function_apply_vararg + (func, func->signature, args, return_area); + } +} + +int jit_function_apply_vararg + (jit_function_t func, jit_type_t signature, void **args, void *return_area) +{ + struct jit_backtrace call_trace; + void *entry; + jmp_buf buf; + + /* Create a backtrace entry that blocks exceptions from + flowing further than this up the stack */ + _jit_backtrace_push(&call_trace, 0, 0, &jbuf); + + /* Establish a "setjmp" point here so that we can unwind the + stack when an exception occurs that is blocked by us */ + if(setjmp(jbuf) != 0) + { + _jit_backtrace_set(call_trace.parent); + return 1; + } + + /* Get the function's entry point */ + if(!func) + { + jit_exception_builtin(JIT_RESULT_NULL_FUNCTION); + return 0; + } + if(func->nested_parent) + { + jit_exception_builtin(JIT_RESULT_CALLED_NESTED); + return 0; + } + if(func->is_compiled) + { + entry = func->entry_point; + } + else + { + entry = _jit_function_compile_on_demand(func); + } + + /* Get the default signature if necessary */ + if(!signature) + { + signature = func->signature; + } + + /* Clear the exception state */ + jit_exception_clear_last(); + + /* Apply the function. If it returns, then there is no exception */ + jit_apply(signature, func->entry_point, args, + jit_type_num_params(func->signature), return_area); + + /* Restore the backtrace context and exit */ + _jit_backtrace_set(call_trace.parent); + return 0; +} + +#endif /* !JIT_BACKEND_INTERP */ + +/*@ + * @deftypefun void jit_function_set_optimization_level (jit_function_t func, {unsigned int} level) + * Set the optimization level for @code{func}. Increasing values indicate + * that the @code{libjit} dynamic compiler should expend more effort to + * generate better code for this function. Usually you would increase + * this value just before forcing @code{func} to recompile. + * + * When the optimization level reaches the value returned by + * @code{jit_function_get_max_optimization_level()}, there is usually + * little point in continuing to recompile the function because + * @code{libjit} may not be able to do any better. + * + * The front end is usually responsible for choosing candidates for + * function inlining. If it has identified more such candidates, then + * it may still want to recompile @code{func} again even once it has + * reached the maximum optimization level. + * @end deftypefun +@*/ +void jit_function_set_optimization_level + (jit_function_t func, unsigned int level) +{ + unsigned int max_level = jit_function_get_max_optimization_level(); + if(level > max_level) + { + level = max_level; + } + if(func) + { + func->optimization_level = (int)level; + } +} + +/*@ + * @deftypefun {unsigned int} jit_function_get_optimization_level (jit_function_t func) + * Get the current optimization level for @code{func}. + * @end deftypefun +@*/ +unsigned int jit_function_get_optimization_level(jit_function_t func) +{ + if(func) + { + return (unsigned int)(func->optimization_level); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun {unsigned int} jit_function_get_max_optimization_level (void) + * Get the maximum optimization level that is supported by @code{libjit}. + * @end deftypefun +@*/ +unsigned int jit_function_get_max_optimization_level(void) +{ + /* TODO - implement more than basic optimization */ + return 0; +} diff --git a/jit/jit-gen-arm.c b/jit/jit-gen-arm.c new file mode 100644 index 0000000..938f592 --- /dev/null +++ b/jit/jit-gen-arm.c @@ -0,0 +1,159 @@ +/* + * jit-gen-arm.c - Machine-dependent definitions for ARM. + * + * Copyright (C) 2003, 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-gen-arm.h" + +#if defined(__arm) || defined(__arm__) + +arm_inst_ptr _arm_mov_reg_imm(arm_inst_ptr inst, int reg, int value) +{ + /* Handle bytes in various positions */ + if((value & 0x000000FF) == value) + { + arm_mov_reg_imm8(inst, reg, value); + return inst; + } + else if((value & 0x0000FF00) == value) + { + arm_mov_reg_imm8_rotate(inst, reg, (value >> 8), 12); + return inst; + } + else if((value & 0x00FF0000) == value) + { + arm_mov_reg_imm8_rotate(inst, reg, (value >> 16), 8); + return inst; + } + else if((value & 0xFF000000) == value) + { + arm_mov_reg_imm8_rotate(inst, reg, ((value >> 24) & 0xFF), 4); + return inst; + } + + /* Handle inverted bytes in various positions */ + value = ~value; + if((value & 0x000000FF) == value) + { + arm_mov_reg_imm8(inst, reg, value); + arm_alu_reg(inst, ARM_MVN, reg, reg); + return inst; + } + else if((value & 0x0000FF00) == value) + { + arm_mov_reg_imm8_rotate(inst, reg, (value >> 8), 12); + arm_alu_reg(inst, ARM_MVN, reg, reg); + return inst; + } + else if((value & 0x00FF0000) == value) + { + arm_mov_reg_imm8_rotate(inst, reg, (value >> 16), 8); + arm_alu_reg(inst, ARM_MVN, reg, reg); + return inst; + } + else if((value & 0xFF000000) == value) + { + arm_mov_reg_imm8_rotate(inst, reg, ((value >> 24) & 0xFF), 4); + arm_alu_reg(inst, ARM_MVN, reg, reg); + return inst; + } + + /* Build the value the hard way, byte by byte */ + value = ~value; + if((value & 0xFF000000) != 0) + { + arm_mov_reg_imm8_rotate(inst, reg, ((value >> 24) & 0xFF), 4); + if((value & 0x00FF0000) != 0) + { + arm_alu_reg_imm8_rotate + (inst, ARM_ADD, reg, reg, ((value >> 16) & 0xFF), 8); + } + if((value & 0x0000FF00) != 0) + { + arm_alu_reg_imm8_rotate + (inst, ARM_ADD, reg, reg, ((value >> 8) & 0xFF), 12); + } + if((value & 0x000000FF) != 0) + { + arm_alu_reg_imm8(inst, ARM_ADD, reg, reg, (value & 0xFF)); + } + } + else if((value & 0x00FF0000) != 0) + { + arm_mov_reg_imm8_rotate(inst, reg, ((value >> 16) & 0xFF), 8); + if((value & 0x0000FF00) != 0) + { + arm_alu_reg_imm8_rotate + (inst, ARM_ADD, reg, reg, ((value >> 8) & 0xFF), 12); + } + if((value & 0x000000FF) != 0) + { + arm_alu_reg_imm8(inst, ARM_ADD, reg, reg, (value & 0xFF)); + } + } + else if((value & 0x0000FF00) != 0) + { + arm_mov_reg_imm8_rotate(inst, reg, ((value >> 8) & 0xFF), 12); + if((value & 0x000000FF) != 0) + { + arm_alu_reg_imm8(inst, ARM_ADD, reg, reg, (value & 0xFF)); + } + } + else + { + arm_mov_reg_imm8(inst, reg, (value & 0xFF)); + } + return inst; +} + +arm_inst_ptr _arm_alu_reg_imm(arm_inst_ptr inst, int opc, + int dreg, int sreg, int imm, + int saveWork) +{ + int tempreg; + if(saveWork) + { + if(dreg != ARM_R2 && sreg != ARM_R2) + { + tempreg = ARM_R2; + } + else if(dreg != ARM_R3 && sreg != ARM_R3) + { + tempreg = ARM_R3; + } + else + { + tempreg = ARM_R4; + } + arm_push_reg(inst, tempreg); + } + else + { + tempreg = ARM_WORK; + } + _arm_mov_reg_imm(inst, tempreg, imm); + arm_alu_reg_reg(inst, opc, dreg, sreg, tempreg); + if(saveWork) + { + arm_pop_reg(inst, tempreg); + } + return inst; +} + +#endif /* arm */ diff --git a/jit/jit-gen-arm.h b/jit/jit-gen-arm.h new file mode 100644 index 0000000..0b0f368 --- /dev/null +++ b/jit/jit-gen-arm.h @@ -0,0 +1,761 @@ +/* + * arm_codegen.h - Code generation macros for the ARM processor. + * + * Copyright (C) 2003, 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _ARM_CODEGEN_H +#define _ARM_CODEGEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Register numbers. + */ +typedef enum +{ + ARM_R0 = 0, + ARM_R1 = 1, + ARM_R2 = 2, + ARM_R3 = 3, + ARM_R4 = 4, + ARM_R5 = 5, + ARM_R6 = 6, + ARM_R7 = 7, + ARM_R8 = 8, + ARM_R9 = 9, + ARM_R10 = 10, + ARM_R11 = 11, + ARM_R12 = 12, + ARM_R13 = 13, + ARM_R14 = 14, + ARM_R15 = 15, + ARM_FP = ARM_R11, /* Frame pointer */ + ARM_LINK = ARM_R14, /* Link register */ + ARM_PC = ARM_R15, /* Program counter */ + ARM_WORK = ARM_R12, /* Work register that we can destroy */ + ARM_SP = ARM_R13, /* Stack pointer */ + +} ARM_REG; + +/* + * Condition codes. + */ +typedef enum +{ + ARM_CC_EQ = 0, /* Equal */ + ARM_CC_NE = 1, /* Not equal */ + ARM_CC_CS = 2, /* Carry set */ + ARM_CC_CC = 3, /* Carry clear */ + ARM_CC_MI = 4, /* Negative */ + ARM_CC_PL = 5, /* Positive */ + ARM_CC_VS = 6, /* Overflow set */ + ARM_CC_VC = 7, /* Overflow clear */ + ARM_CC_HI = 8, /* Higher */ + ARM_CC_LS = 9, /* Lower or same */ + ARM_CC_GE = 10, /* Signed greater than or equal */ + ARM_CC_LT = 11, /* Signed less than */ + ARM_CC_GT = 12, /* Signed greater than */ + ARM_CC_LE = 13, /* Signed less than or equal */ + ARM_CC_AL = 14, /* Always */ + ARM_CC_NV = 15, /* Never */ + ARM_CC_GE_UN = ARM_CC_CS, /* Unsigned greater than or equal */ + ARM_CC_LT_UN = ARM_CC_CC, /* Unsigned less than */ + ARM_CC_GT_UN = ARM_CC_HI, /* Unsigned greater than */ + ARM_CC_LE_UN = ARM_CC_LS, /* Unsigned less than or equal */ + +} ARM_CC; + +/* + * Arithmetic and logical operations. + */ +typedef enum +{ + ARM_AND = 0, /* Bitwise AND */ + ARM_EOR = 1, /* Bitwise XOR */ + ARM_SUB = 2, /* Subtract */ + ARM_RSB = 3, /* Reverse subtract */ + ARM_ADD = 4, /* Add */ + ARM_ADC = 5, /* Add with carry */ + ARM_SBC = 6, /* Subtract with carry */ + ARM_RSC = 7, /* Reverse subtract with carry */ + ARM_TST = 8, /* Test with AND */ + ARM_TEQ = 9, /* Test with XOR */ + ARM_CMP = 10, /* Test with SUB (compare) */ + ARM_CMN = 11, /* Test with ADD */ + ARM_ORR = 12, /* Bitwise OR */ + ARM_MOV = 13, /* Move */ + ARM_BIC = 14, /* Test with Op1 & ~Op2 */ + ARM_MVN = 15, /* Bitwise NOT */ + +} ARM_OP; + +/* + * Shift operators. + */ +typedef enum +{ + ARM_SHL = 0, /* Logical left */ + ARM_SHR = 1, /* Logical right */ + ARM_SAR = 2, /* Arithmetic right */ + ARM_ROR = 3, /* Rotate right */ + +} ARM_SHIFT; + +/* + * Number of registers that are used for parameters (r0-r3). + */ +#define ARM_NUM_PARAM_REGS 4 + +/* + * Type for instruction pointers (word-based, not byte-based). + */ +typedef unsigned int *arm_inst_ptr; + +/* + * Build an instruction prefix from a condition code and a mask value. + */ +#define arm_build_prefix(cond,mask) \ + ((((unsigned int)(cond)) << 28) | ((unsigned int)(mask))) + +/* + * Build an "always" instruction prefix for a regular instruction. + */ +#define arm_prefix(mask) (arm_build_prefix(ARM_CC_AL, (mask))) + +/* + * Build special "always" prefixes. + */ +#define arm_always (arm_build_prefix(ARM_CC_AL, 0)) +#define arm_always_cc (arm_build_prefix(ARM_CC_AL, (1 << 20))) +#define arm_always_imm (arm_build_prefix(ARM_CC_AL, (1 << 25))) + +/* + * Arithmetic or logical operation which doesn't set condition codes. + */ +#define arm_alu_reg_reg(inst,opc,dreg,sreg1,sreg2) \ + do { \ + *(inst)++ = arm_always | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg1)) << 16) | \ + ((unsigned int)(sreg2)); \ + } while (0) +#define arm_alu_reg_imm8(inst,opc,dreg,sreg,imm) \ + do { \ + *(inst)++ = arm_always_imm | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg)) << 16) | \ + ((unsigned int)((imm) & 0xFF)); \ + } while (0) +#define arm_alu_reg_imm8_cond(inst,opc,dreg,sreg,imm,cond) \ + do { \ + *(inst)++ = arm_build_prefix((cond), (1 << 25)) | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg)) << 16) | \ + ((unsigned int)((imm) & 0xFF)); \ + } while (0) +#define arm_alu_reg_imm8_rotate(inst,opc,dreg,sreg,imm,rotate) \ + do { \ + *(inst)++ = arm_always_imm | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg)) << 16) | \ + (((unsigned int)(rotate)) << 8) | \ + ((unsigned int)((imm) & 0xFF)); \ + } while (0) +extern arm_inst_ptr _arm_alu_reg_imm(arm_inst_ptr inst, int opc, int dreg, + int sreg, int imm, int saveWork); +#define arm_alu_reg_imm(inst,opc,dreg,sreg,imm) \ + do { \ + int __alu_imm = (int)(imm); \ + if(__alu_imm >= 0 && __alu_imm < 256) \ + { \ + arm_alu_reg_imm8 \ + ((inst), (opc), (dreg), (sreg), __alu_imm); \ + } \ + else \ + { \ + (inst) = _arm_alu_reg_imm \ + ((inst), (opc), (dreg), (sreg), __alu_imm, 0); \ + } \ + } while (0) +#define arm_alu_reg_imm_save_work(inst,opc,dreg,sreg,imm) \ + do { \ + int __alu_imm_save = (int)(imm); \ + if(__alu_imm_save >= 0 && __alu_imm_save < 256) \ + { \ + arm_alu_reg_imm8 \ + ((inst), (opc), (dreg), (sreg), __alu_imm_save); \ + } \ + else \ + { \ + (inst) = _arm_alu_reg_imm \ + ((inst), (opc), (dreg), (sreg), __alu_imm_save, 1); \ + } \ + } while (0) +#define arm_alu_reg(inst,opc,dreg,sreg) \ + do { \ + *(inst)++ = arm_always | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + ((unsigned int)(sreg)); \ + } while (0) +#define arm_alu_reg_cond(inst,opc,dreg,sreg,cond) \ + do { \ + *(inst)++ = arm_build_prefix((cond), 0) | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + ((unsigned int)(sreg)); \ + } while (0) + +/* + * Arithmetic or logical operation which sets condition codes. + */ +#define arm_alu_cc_reg_reg(inst,opc,dreg,sreg1,sreg2) \ + do { \ + *(inst)++ = arm_always_cc | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg1)) << 16) | \ + ((unsigned int)(sreg2)); \ + } while (0) +#define arm_alu_cc_reg_imm8(inst,opc,dreg,sreg,imm) \ + do { \ + *(inst)++ = arm_always_imm | arm_always_cc | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg)) << 16) | \ + ((unsigned int)((imm) & 0xFF)); \ + } while (0) +#define arm_alu_cc_reg(inst,opc,dreg,sreg) \ + do { \ + *(inst)++ = arm_always_cc | \ + (((unsigned int)(opc)) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + ((unsigned int)(sreg)); \ + } while (0) + +/* + * Test operation, which sets the condition codes but has no other result. + */ +#define arm_test_reg_reg(inst,opc,sreg1,sreg2) \ + do { \ + arm_alu_cc_reg_reg((inst), (opc), 0, (sreg1), (sreg2)); \ + } while (0) +#define arm_test_reg_imm8(inst,opc,sreg,imm) \ + do { \ + arm_alu_cc_reg_imm8((inst), (opc), 0, (sreg), (imm)); \ + } while (0) +#define arm_test_reg_imm(inst,opc,sreg,imm) \ + do { \ + int __test_imm = (int)(imm); \ + if(__test_imm >= 0 && __test_imm < 256) \ + { \ + arm_alu_cc_reg_imm8((inst), (opc), 0, (sreg), __test_imm); \ + } \ + else \ + { \ + arm_mov_reg_imm((inst), ARM_WORK, __test_imm); \ + arm_test_reg_reg((inst), (opc), (sreg), ARM_WORK); \ + } \ + } while (0) + +/* + * Move a value between registers. + */ +#define arm_mov_reg_reg(inst,dreg,sreg) \ + do { \ + arm_alu_reg((inst), ARM_MOV, (dreg), (sreg)); \ + } while (0) + +/* + * Move an immediate value into a register. This is hard because + * ARM lacks an instruction to load a 32-bit immediate value directly. + * We handle the simple cases and then bail out to a function for the rest. + */ +#define arm_mov_reg_imm8(inst,reg,imm) \ + do { \ + arm_alu_reg_imm8((inst), ARM_MOV, (reg), 0, (imm)); \ + } while (0) +#define arm_mov_reg_imm8_rotate(inst,reg,imm,rotate) \ + do { \ + arm_alu_reg_imm8_rotate((inst), ARM_MOV, (reg), \ + 0, (imm), (rotate)); \ + } while (0) +extern arm_inst_ptr _arm_mov_reg_imm(arm_inst_ptr inst, int reg, int value); +#define arm_mov_reg_imm(inst,reg,imm) \ + do { \ + int __imm = (int)(imm); \ + if(__imm >= 0 && __imm < 256) \ + { \ + arm_mov_reg_imm8((inst), (reg), __imm); \ + } \ + else if((reg) == ARM_PC) \ + { \ + (inst) = _arm_mov_reg_imm((inst), ARM_WORK, __imm); \ + arm_mov_reg_reg((inst), ARM_PC, ARM_WORK); \ + } \ + else if(__imm > -256 && __imm < 0) \ + { \ + arm_mov_reg_imm8((inst), (reg), ~(__imm)); \ + arm_alu_reg((inst), ARM_MVN, (reg), (reg)); \ + } \ + else \ + { \ + (inst) = _arm_mov_reg_imm((inst), (reg), __imm); \ + } \ + } while (0) + +/* + * Clear a register to zero. + */ +#define arm_clear_reg(inst,reg) \ + do { \ + arm_mov_reg_imm8((inst), (reg), 0); \ + } while (0) + +/* + * No-operation instruction. + */ +#define arm_nop(inst) arm_mov_reg_reg((inst), ARM_R0, ARM_R0) + +/* + * Perform a shift operation. + */ +#define arm_shift_reg_reg(inst,opc,dreg,sreg1,sreg2) \ + do { \ + *(inst)++ = arm_always | \ + (((unsigned int)ARM_MOV) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(sreg2)) << 8) | \ + (((unsigned int)(opc)) << 5) | \ + ((unsigned int)(1 << 4)) | \ + ((unsigned int)(sreg1)); \ + } while (0) +#define arm_shift_reg_imm8(inst,opc,dreg,sreg,imm) \ + do { \ + *(inst)++ = arm_always | \ + (((unsigned int)ARM_MOV) << 21) | \ + (((unsigned int)(dreg)) << 12) | \ + (((unsigned int)(opc)) << 5) | \ + (((unsigned int)(imm)) << 7) | \ + ((unsigned int)(sreg)); \ + } while (0) + +/* + * Perform a multiplication instruction. Note: ARM instruction rules + * say that dreg should not be the same as sreg2, so we swap the order + * of the arguments if that situation occurs. We assume that sreg1 + * and sreg2 are distinct registers. + */ +#define arm_mul_reg_reg(inst,dreg,sreg1,sreg2) \ + do { \ + if((dreg) != (sreg2)) \ + { \ + *(inst)++ = arm_prefix(0x00000090) | \ + (((unsigned int)(dreg)) << 16) | \ + (((unsigned int)(sreg1)) << 8) | \ + ((unsigned int)(sreg2)); \ + } \ + else \ + { \ + *(inst)++ = arm_prefix(0x00000090) | \ + (((unsigned int)(dreg)) << 16) | \ + (((unsigned int)(sreg2)) << 8) | \ + ((unsigned int)(sreg1)); \ + } \ + } while (0) + +/* + * Branch or jump immediate by a byte offset. The offset is + * assumed to be +/- 32 Mbytes. + */ +#define arm_branch_imm(inst,cond,imm) \ + do { \ + *(inst)++ = arm_build_prefix((cond), 0x0A000000) | \ + (((unsigned int)(((int)(imm)) >> 2)) & \ + 0x00FFFFFF); \ + } while (0) +#define arm_jump_imm(inst,imm) arm_branch_imm((inst), ARM_CC_AL, (imm)) + +/* + * Branch or jump to a specific target location. The offset is + * assumed to be +/- 32 Mbytes. + */ +#define arm_branch(inst,cond,target) \ + do { \ + int __br_offset = (int)(((unsigned char *)(target)) - \ + (((unsigned char *)(inst)) + 8)); \ + arm_branch_imm((inst), (cond), __br_offset); \ + } while (0) +#define arm_jump(inst,target) arm_branch((inst), ARM_CC_AL, (target)) + +/* + * Jump to a specific target location that may be greater than + * 32 Mbytes away from the current location. + */ +#define arm_jump_long(inst,target) \ + do { \ + int __jmp_offset = (int)(((unsigned char *)(target)) - \ + (((unsigned char *)(inst)) + 8)); \ + if(__jmp_offset >= -0x04000000 && __jmp_offset < 0x04000000) \ + { \ + arm_jump_imm((inst), __jmp_offset); \ + } \ + else \ + { \ + arm_mov_reg_imm((inst), ARM_PC, (int)(target)); \ + } \ + } while (0) + +/* + * Back-patch a branch instruction. + */ +#define arm_patch(inst,target) \ + do { \ + int __p_offset = (int)(((unsigned char *)(target)) - \ + (((unsigned char *)(inst)) + 8)); \ + __p_offset = (__p_offset >> 2) & 0x00FFFFFF; \ + *((int *)(inst)) = (*((int *)(inst)) & 0xFF000000) | \ + __p_offset; \ + } while (0) + +/* + * Call a subroutine immediate by a byte offset. + */ +#define arm_call_imm(inst,imm) \ + do { \ + *(inst)++ = arm_prefix(0x0B000000) | \ + (((unsigned int)(((int)(imm)) >> 2)) & \ + 0x00FFFFFF); \ + } while (0) + +/* + * Call a subroutine at a specific target location. + */ +#define arm_call(inst,target) \ + do { \ + int __call_offset = (int)(((unsigned char *)(target)) - \ + (((unsigned char *)(inst)) + 8)); \ + if(__call_offset >= -0x04000000 && __call_offset < 0x04000000) \ + { \ + arm_call_imm((inst), __call_offset); \ + } \ + else \ + { \ + arm_mov_reg_imm((inst), ARM_WORK, (int)(target)); \ + arm_mov_reg_reg((inst), ARM_LINK, ARM_PC); \ + arm_mov_reg_reg((inst), ARM_PC, ARM_WORK); \ + } \ + } while (0) + +/* + * Return from a subroutine, where the return address is in the link register. + */ +#define arm_return(inst) \ + do { \ + arm_mov_reg_reg((inst), ARM_PC, ARM_LINK); \ + } while (0) + +/* + * Push a register onto the system stack. + */ +#define arm_push_reg(inst,reg) \ + do { \ + *(inst)++ = arm_prefix(0x05200004) | \ + (((unsigned int)ARM_SP) << 16) | \ + (((unsigned int)(reg)) << 12); \ + } while (0) + +/* + * Pop a register from the system stack. + */ +#define arm_pop_reg(inst,reg) \ + do { \ + *(inst)++ = arm_prefix(0x04900004) | \ + (((unsigned int)ARM_SP) << 16) | \ + (((unsigned int)(reg)) << 12); \ + } while (0) + +/* + * Set up a local variable frame, and save the registers in "regset". + */ +#define arm_setup_frame(inst,regset) \ + do { \ + arm_mov_reg_reg((inst), ARM_WORK, ARM_SP); \ + *(inst)++ = arm_prefix(0x0920D800) | \ + (((unsigned int)ARM_SP) << 16) | \ + (((unsigned int)(regset))); \ + arm_alu_reg_imm8((inst), ARM_SUB, ARM_FP, ARM_WORK, 4); \ + } while (0) + +/* + * Pop a local variable frame, restore the registers in "regset", + * and return to the caller. + */ +#define arm_pop_frame(inst,regset) \ + do { \ + *(inst)++ = arm_prefix(0x0910A800) | \ + (((unsigned int)ARM_FP) << 16) | \ + (((unsigned int)(regset))); \ + } while (0) + +/* + * Pop a local variable frame, in preparation for a tail call. + * This restores "lr" to its original value, but does not set "pc". + */ +#define arm_pop_frame_tail(inst,regset) \ + do { \ + *(inst)++ = arm_prefix(0x09106800) | \ + (((unsigned int)ARM_FP) << 16) | \ + (((unsigned int)(regset))); \ + } while (0) + +/* + * Load a word value from a pointer and then advance the pointer. + */ +#define arm_load_advance(inst,dreg,sreg) \ + do { \ + *(inst)++ = arm_prefix(0x04900004) | \ + (((unsigned int)(sreg)) << 16) | \ + (((unsigned int)(dreg)) << 12); \ + } while (0) + +/* + * Load a value from an address into a register. + */ +#define arm_load_membase_either(inst,reg,basereg,imm,mask) \ + do { \ + int __mb_offset = (int)(imm); \ + if(__mb_offset >= 0 && __mb_offset < (1 << 12)) \ + { \ + *(inst)++ = arm_prefix(0x05900000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + ((unsigned int)__mb_offset); \ + } \ + else if(__mb_offset > -(1 << 12) && __mb_offset < 0) \ + { \ + *(inst)++ = arm_prefix(0x05100000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + ((unsigned int)(-__mb_offset)); \ + } \ + else \ + { \ + arm_mov_reg_imm((inst), ARM_WORK, __mb_offset); \ + *(inst)++ = arm_prefix(0x07900000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + ((unsigned int)ARM_WORK); \ + } \ + } while (0) +#define arm_load_membase(inst,reg,basereg,imm) \ + do { \ + arm_load_membase_either((inst), (reg), (basereg), (imm), 0); \ + } while (0) +#define arm_load_membase_byte(inst,reg,basereg,imm) \ + do { \ + arm_load_membase_either((inst), (reg), (basereg), (imm), \ + 0x00400000); \ + } while (0) +#define arm_load_membase_sbyte(inst,reg,basereg,imm) \ + do { \ + arm_load_membase_either((inst), (reg), (basereg), (imm), \ + 0x00400000); \ + arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 24); \ + arm_shift_reg_imm8((inst), ARM_SAR, (reg), (reg), 24); \ + } while (0) +#define arm_load_membase_ushort(inst,reg,basereg,imm) \ + do { \ + arm_load_membase_byte((inst), ARM_WORK, (basereg), (imm)); \ + arm_load_membase_byte((inst), (reg), (basereg), (imm) + 1); \ + arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 8); \ + arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \ + } while (0) +#define arm_load_membase_short(inst,reg,basereg,imm) \ + do { \ + arm_load_membase_byte((inst), ARM_WORK, (basereg), (imm)); \ + arm_load_membase_byte((inst), (reg), (basereg), (imm) + 1); \ + arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 24); \ + arm_shift_reg_imm8((inst), ARM_SAR, (reg), (reg), 16); \ + arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \ + } while (0) + +/* + * Store a value from a register into an address. + * + * Note: storing a 16-bit value destroys the value in the register. + */ +#define arm_store_membase_either(inst,reg,basereg,imm,mask) \ + do { \ + int __sm_offset = (int)(imm); \ + if(__sm_offset >= 0 && __sm_offset < (1 << 12)) \ + { \ + *(inst)++ = arm_prefix(0x05800000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + ((unsigned int)__sm_offset); \ + } \ + else if(__sm_offset > -(1 << 12) && __sm_offset < 0) \ + { \ + *(inst)++ = arm_prefix(0x05000000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + ((unsigned int)(-__sm_offset)); \ + } \ + else \ + { \ + arm_mov_reg_imm((inst), ARM_WORK, __sm_offset); \ + *(inst)++ = arm_prefix(0x07800000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + ((unsigned int)ARM_WORK); \ + } \ + } while (0) +#define arm_store_membase(inst,reg,basereg,imm) \ + do { \ + arm_store_membase_either((inst), (reg), (basereg), (imm), 0); \ + } while (0) +#define arm_store_membase_byte(inst,reg,basereg,imm) \ + do { \ + arm_store_membase_either((inst), (reg), (basereg), (imm), \ + 0x00400000); \ + } while (0) +#define arm_store_membase_sbyte(inst,reg,basereg,imm) \ + do { \ + arm_store_membase_byte((inst), (reg), (basereg), (imm)); \ + } while (0) +#define arm_store_membase_short(inst,reg,basereg,imm) \ + do { \ + arm_store_membase_either((inst), (reg), (basereg), (imm), \ + 0x00400000); \ + arm_shift_reg_imm8((inst), ARM_SHR, (reg), (reg), 8); \ + arm_store_membase_either((inst), (reg), (basereg), \ + (imm) + 1, 0x00400000); \ + } while (0) +#define arm_store_membase_ushort(inst,reg,basereg,imm) \ + do { \ + arm_store_membase_short((inst), (reg), (basereg), (imm)); \ + } while (0) + +/* + * Load a value from an indexed address into a register. + */ +#define arm_load_memindex_either(inst,reg,basereg,indexreg,shift,mask) \ + do { \ + *(inst)++ = arm_prefix(0x07900000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + (((unsigned int)(shift)) << 7) | \ + ((unsigned int)(indexreg)); \ + } while (0) +#define arm_load_memindex(inst,reg,basereg,indexreg) \ + do { \ + arm_load_memindex_either((inst), (reg), (basereg), \ + (indexreg), 2, 0); \ + } while (0) +#define arm_load_memindex_byte(inst,reg,basereg,indexreg) \ + do { \ + arm_load_memindex_either((inst), (reg), (basereg), \ + (indexreg), 0, 0x00400000); \ + } while (0) +#define arm_load_memindex_sbyte(inst,reg,basereg,indexreg) \ + do { \ + arm_load_memindex_either((inst), (reg), (basereg), \ + (indexreg), 0, 0x00400000); \ + arm_shift_reg_imm8((inst), ARM_SHL, (reg), (reg), 24); \ + arm_shift_reg_imm8((inst), ARM_SAR, (reg), (reg), 24); \ + } while (0) +#define arm_load_memindex_ushort(inst,reg,basereg,indexreg) \ + do { \ + arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, (basereg), \ + (indexreg)); \ + arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, ARM_WORK, \ + (indexreg)); \ + arm_load_membase_byte((inst), (reg), ARM_WORK, 0); \ + arm_load_membase_byte((inst), ARM_WORK, ARM_WORK, 1); \ + arm_shift_reg_imm8((inst), ARM_SHL, ARM_WORK, ARM_WORK, 8); \ + arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \ + } while (0) +#define arm_load_memindex_short(inst,reg,basereg,indexreg) \ + do { \ + arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, (basereg), \ + (indexreg)); \ + arm_alu_reg_reg((inst), ARM_ADD, ARM_WORK, ARM_WORK, \ + (indexreg)); \ + arm_load_membase_byte((inst), (reg), ARM_WORK, 0); \ + arm_load_membase_byte((inst), ARM_WORK, ARM_WORK, 1); \ + arm_shift_reg_imm8((inst), ARM_SHL, ARM_WORK, ARM_WORK, 24); \ + arm_shift_reg_imm8((inst), ARM_SAR, ARM_WORK, ARM_WORK, 16); \ + arm_alu_reg_reg((inst), ARM_ORR, (reg), (reg), ARM_WORK); \ + } while (0) + +/* + * Store a value from a register into an indexed address. + * + * Note: storing a 16-bit value destroys the values in the base + * register and the source register. + */ +#define arm_store_memindex_either(inst,reg,basereg,indexreg,shift,mask) \ + do { \ + *(inst)++ = arm_prefix(0x07800000 | (mask)) | \ + (((unsigned int)(basereg)) << 16) | \ + (((unsigned int)(reg)) << 12) | \ + (((unsigned int)(shift)) << 7) | \ + ((unsigned int)(indexreg)); \ + } while (0) +#define arm_store_memindex(inst,reg,basereg,indexreg) \ + do { \ + arm_store_memindex_either((inst), (reg), (basereg), \ + (indexreg), 2, 0); \ + } while (0) +#define arm_store_memindex_byte(inst,reg,basereg,indexreg) \ + do { \ + arm_store_memindex_either((inst), (reg), (basereg), \ + (indexreg), 0, 0x00400000); \ + } while (0) +#define arm_store_memindex_sbyte(inst,reg,basereg,indexreg) \ + do { \ + arm_store_memindex_byte((inst), (reg), (basereg), \ + (indexreg)); \ + } while (0) +#define arm_store_memindex_short(inst,reg,basereg,indexreg) \ + do { \ + arm_store_memindex_either((inst), (reg), (basereg), \ + (indexreg), 1, 0x00400000); \ + arm_alu_reg_imm8((inst), ARM_ADD, (basereg), (basereg), 1); \ + arm_shift_reg_imm8((inst), ARM_SHR, (reg), (reg), 8); \ + arm_store_memindex_either((inst), (reg), (basereg), \ + (indexreg), 1, 0x00400000); \ + } while (0) +#define arm_store_memindex_ushort(inst,reg,basereg,indexreg) \ + do { \ + arm_store_memindex_short((inst), (reg), \ + (basereg), (indexreg)); \ + } while (0) + +#ifdef __cplusplus +}; +#endif + +#endif /* _ARM_CODEGEN_H */ diff --git a/jit/jit-gen-x86.h b/jit/jit-gen-x86.h new file mode 100644 index 0000000..9bb99a5 --- /dev/null +++ b/jit/jit-gen-x86.h @@ -0,0 +1,1592 @@ +/* + * jit-gen-x86.h: Macros for generating x86 code + * + * Authors: + * Paolo Molaro (lupus@ximian.com) + * Intel Corporation (ORP Project) + * Sergey Chaban (serge@wildwestsoftware.com) + * Dietmar Maurer (dietmar@ximian.com) + * + * Copyright (C) 2000 Intel Corporation. All rights reserved. + * Copyright (C) 2001, 2002 Ximian, Inc. + * + * This file originated with the Mono project (www.go-mono.com), and may + * be redistributed under the terms of the Lesser General Public License. + */ + +#ifndef JIT_GEN_X86_H +#define JIT_GEN_X86_H +#define jit_assert(x) break +/* +// x86 register numbers +*/ +typedef enum { + X86_EAX = 0, + X86_ECX = 1, + X86_EDX = 2, + X86_EBX = 3, + X86_ESP = 4, + X86_EBP = 5, + X86_ESI = 6, + X86_EDI = 7, + X86_NREG +} X86_Reg_No; +/* +// opcodes for alu instructions +*/ +typedef enum { + X86_ADD = 0, + X86_OR = 1, + X86_ADC = 2, + X86_SBB = 3, + X86_AND = 4, + X86_SUB = 5, + X86_XOR = 6, + X86_CMP = 7, + X86_NALU +} X86_ALU_Opcode; +/* +// opcodes for shift instructions +*/ +typedef enum { + X86_SHLD, + X86_SHLR, + X86_ROL = 0, + X86_ROR = 1, + X86_RCL = 2, + X86_RCR = 3, + X86_SHL = 4, + X86_SHR = 5, + X86_SAR = 7, + X86_NSHIFT = 8 +} X86_Shift_Opcode; +/* +// opcodes for floating-point instructions +*/ +typedef enum { + X86_FADD = 0, + X86_FMUL = 1, + X86_FCOM = 2, + X86_FCOMP = 3, + X86_FSUB = 4, + X86_FSUBR = 5, + X86_FDIV = 6, + X86_FDIVR = 7, + X86_NFP = 8 +} X86_FP_Opcode; +/* +// integer conditions codes +*/ +typedef enum { + X86_CC_EQ = 0, X86_CC_E = 0, X86_CC_Z = 0, + X86_CC_NE = 1, X86_CC_NZ = 1, + X86_CC_LT = 2, X86_CC_B = 2, X86_CC_C = 2, X86_CC_NAE = 2, + X86_CC_LE = 3, X86_CC_BE = 3, X86_CC_NA = 3, + X86_CC_GT = 4, X86_CC_A = 4, X86_CC_NBE = 4, + X86_CC_GE = 5, X86_CC_AE = 5, X86_CC_NB = 5, X86_CC_NC = 5, + X86_CC_LZ = 6, X86_CC_S = 6, + X86_CC_GEZ = 7, X86_CC_NS = 7, + X86_CC_P = 8, X86_CC_PE = 8, + X86_CC_NP = 9, X86_CC_PO = 9, + X86_CC_O = 10, + X86_CC_NO = 11, + X86_NCC +} X86_CC; +/* +// prefix code +*/ +typedef enum { + X86_LOCK_PREFIX = 0xF0, + X86_REPNZ_PREFIX = 0xF2, + X86_REPZ_PREFIX = 0xF3, + X86_REP_PREFIX = 0xF3, + X86_CS_PREFIX = 0x2E, + X86_SS_PREFIX = 0x36, + X86_DS_PREFIX = 0x3E, + X86_ES_PREFIX = 0x26, + X86_FS_PREFIX = 0x64, + X86_GS_PREFIX = 0x65, + X86_OPERAND_PREFIX = 0x66, + X86_ADDRESS_PREFIX = 0x67 +} X86_Prefix; + +static const unsigned char +x86_cc_unsigned_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x72, /* lt */ + 0x76, /* le */ + 0x77, /* gt */ + 0x73, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +static const unsigned char +x86_cc_signed_map [X86_NCC] = { + 0x74, /* eq */ + 0x75, /* ne */ + 0x7c, /* lt */ + 0x7e, /* le */ + 0x7f, /* gt */ + 0x7d, /* ge */ + 0x78, /* lz */ + 0x79, /* gez */ + 0x7a, /* p */ + 0x7b, /* np */ + 0x70, /* o */ + 0x71, /* no */ +}; + +typedef union { + int val; + unsigned char b [4]; +} x86_imm_buf; + +#define X86_NOBASEREG (-1) + +/* +// bitvector mask for callee-saved registers +*/ +#define X86_ESI_MASK (1<= -128 && (int)(imm) <= 127)) +#define x86_is_imm16(imm) (((int)(imm) >= -(1<<16) && (int)(imm) <= ((1<<16)-1))) + +#define x86_reg_emit(inst,r,regno) do { x86_address_byte ((inst), 3, (r), (regno)); } while (0) +#define x86_reg8_emit(inst,r,regno,is_rh,is_rnoh) do {x86_address_byte ((inst), 3, (is_rh)?((r)|4):(r), (is_rnoh)?((regno)|4):(regno));} while (0) +#define x86_regp_emit(inst,r,regno) do { x86_address_byte ((inst), 0, (r), (regno)); } while (0) +#define x86_mem_emit(inst,r,disp) do { x86_address_byte ((inst), 0, (r), 5); x86_imm_emit32((inst), (disp)); } while (0) + +#define x86_membase_emit(inst,r,basereg,disp) do {\ + if ((basereg) == X86_ESP) { \ + if ((disp) == 0) { \ + x86_address_byte ((inst), 0, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), X86_ESP); \ + x86_address_byte ((inst), 0, X86_ESP, X86_ESP); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + break; \ + } \ + if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), (basereg)); \ + break; \ + } \ + if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 2, (r), (basereg)); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +#define x86_memindex_emit(inst,r,basereg,disp,indexreg,shift) \ + do { \ + if ((basereg) == X86_NOBASEREG) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } else if ((disp) == 0 && (basereg) != X86_EBP) { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + } else if (x86_is_imm8((disp))) { \ + x86_address_byte ((inst), 1, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), (basereg)); \ + x86_imm_emit8 ((inst), (disp)); \ + } else { \ + x86_address_byte ((inst), 0, (r), 4); \ + x86_address_byte ((inst), (shift), (indexreg), 5); \ + x86_imm_emit32 ((inst), (disp)); \ + } \ + } while (0) + +/* + * target is the position in the code where to jump to: + * target = code; + * .. output loop code... + * x86_mov_reg_imm (code, X86_EAX, 0); + * loop = code; + * x86_loop (code, -1); + * ... finish method + * + * patch displacement + * x86_patch (loop, target); + * + * ins should point at the start of the instruction that encodes a target. + * the instruction is inspected for validity and the correct displacement + * is inserted. + */ +#define x86_patch(ins,target) \ + do { \ + unsigned char* pos = (ins) + 1; \ + int disp, size = 0; \ + switch (*(unsigned char*)(ins)) { \ + case 0xe8: case 0xe9: ++size; break; /* call, jump32 */ \ + case 0x0f: if (!(*pos >= 0x70 && *pos <= 0x8f)) jit_assert (0); \ + ++size; ++pos; break; /* prefix for 32-bit disp */ \ + case 0xe0: case 0xe1: case 0xe2: /* loop */ \ + case 0xeb: /* jump8 */ \ + /* conditional jump opcodes */ \ + case 0x70: case 0x71: case 0x72: case 0x73: \ + case 0x74: case 0x75: case 0x76: case 0x77: \ + case 0x78: case 0x79: case 0x7a: case 0x7b: \ + case 0x7c: case 0x7d: case 0x7e: case 0x7f: \ + break; \ + default: jit_assert (0); \ + } \ + disp = (target) - pos; \ + if (size) x86_imm_emit32 (pos, disp - 4); \ + else if (x86_is_imm8 (disp - 1)) x86_imm_emit8 (pos, disp - 1); \ + else jit_assert (0); \ + } while (0) + +#define x86_breakpoint(inst) \ + do { \ + *(inst)++ = 0xcc; \ + } while (0) + +#define x86_cld(inst) do { *(inst)++ =(unsigned char)0xfc; } while (0) +#define x86_stosb(inst) do { *(inst)++ =(unsigned char)0xaa; } while (0) +#define x86_stosl(inst) do { *(inst)++ =(unsigned char)0xab; } while (0) + +#define x86_prefix(inst,p) do { *(inst)++ =(unsigned char) (p); } while (0) + +#define x86_rdtsc(inst) \ + do { \ + *(inst)++ = 0x0f; \ + *(inst)++ = 0x31; \ + } while (0) + +#define x86_cmpxchg_reg_reg(inst,dreg,reg) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_cmpxchg_mem_reg(inst,mem,reg) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmpxchg_membase_reg(inst,basereg,disp,reg) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xb1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_xchg_reg_reg(inst,dreg,reg,size) \ + do { \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_xchg_mem_reg(inst,mem,reg,size) \ + do { \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_xchg_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + if ((size) == 1) \ + *(inst)++ = (unsigned char)0x86; \ + else \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_inc_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_inc_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_inc_reg(inst,reg) do { *(inst)++ = (unsigned char)0x40 + (reg); } while (0) + +#define x86_dec_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 1, (mem)); \ + } while (0) + +#define x86_dec_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 1, (basereg), (disp)); \ + } while (0) + +#define x86_dec_reg(inst,reg) do { *(inst)++ = (unsigned char)0x48 + (reg); } while (0) + +#define x86_not_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_not_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) + +#define x86_not_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_neg_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 3, (mem)); \ + } while (0) + +#define x86_neg_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } while (0) + +#define x86_neg_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 3, (reg)); \ + } while (0) + +#define x86_nop(inst) do { *(inst)++ = (unsigned char)0x90; } while (0) + +#define x86_alu_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((reg) == X86_EAX) { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 5; \ + x86_imm_emit32 ((inst), (imm)); \ + break; \ + } \ + if (x86_is_imm8((imm))) { \ + *(inst)++ = (unsigned char)0x83; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0x81; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_mem_imm(inst,opc,mem,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + *(inst)++ = (unsigned char)0x83; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0x81; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if (x86_is_imm8((imm))) { \ + *(inst)++ = (unsigned char)0x83; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0x81; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_alu_mem_reg(inst,opc,mem,reg) \ + do { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_membase_reg(inst,opc,basereg,disp,reg) \ + do { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 1; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_alu_reg_reg(inst,opc,dreg,reg) \ + do { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +/** + * @x86_alu_reg8_reg8: + * Supports ALU operations between two 8-bit registers. + * dreg := dreg opc reg + * X86_Reg_No enum is used to specify the registers. + * Additionally is_*_h flags are used to specify what part + * of a given 32-bit register is used - high (TRUE) or low (FALSE). + * For example: dreg = X86_EAX, is_dreg_h = TRUE -> use AH + */ +#define x86_alu_reg8_reg8(inst,opc,dreg,reg,is_dreg_h,is_reg_h) \ + do { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 2; \ + x86_reg8_emit ((inst), (dreg), (reg), (is_dreg_h), (is_reg_h)); \ + } while (0) + +#define x86_alu_reg_mem(inst,opc,reg,mem) \ + do { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_alu_reg_membase(inst,opc,reg,basereg,disp) \ + do { \ + *(inst)++ = (((unsigned char)(opc)) << 3) + 3; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_test_reg_imm(inst,reg,imm) \ + do { \ + if ((reg) == X86_EAX) { \ + *(inst)++ = (unsigned char)0xa9; \ + } else { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 0, (reg)); \ + } \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_mem_imm(inst,mem,imm) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_membase_imm(inst,basereg,disp,imm) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_test_reg_reg(inst,dreg,reg) \ + do { \ + *(inst)++ = (unsigned char)0x85; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_test_mem_reg(inst,mem,reg) \ + do { \ + *(inst)++ = (unsigned char)0x85; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_test_membase_reg(inst,basereg,disp,reg) \ + do { \ + *(inst)++ = (unsigned char)0x85; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_shift_reg_imm(inst,opc,reg,imm) \ + do { \ + if ((imm) == 1) { \ + *(inst)++ = (unsigned char)0xd1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } else { \ + *(inst)++ = (unsigned char)0xc1; \ + x86_reg_emit ((inst), (opc), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_mem_imm(inst,opc,mem,imm) \ + do { \ + if ((imm) == 1) { \ + *(inst)++ = (unsigned char)0xd1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xc1; \ + x86_mem_emit ((inst), (opc), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_membase_imm(inst,opc,basereg,disp,imm) \ + do { \ + if ((imm) == 1) { \ + *(inst)++ = (unsigned char)0xd1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xc1; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_shift_reg(inst,opc,reg) \ + do { \ + *(inst)++ = (unsigned char)0xd3; \ + x86_reg_emit ((inst), (opc), (reg)); \ + } while (0) + +#define x86_shift_mem(inst,opc,mem) \ + do { \ + *(inst)++ = (unsigned char)0xd3; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_shift_membase(inst,opc,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xd3; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +/* + * Multi op shift missing. + */ + +#define x86_shrd_reg(inst,dreg,reg) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xad; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shrd_reg_imm(inst,dreg,reg,shamt) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xac; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +#define x86_shld_reg(inst,dreg,reg) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa5; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + } while (0) + +#define x86_shld_reg_imm(inst,dreg,reg,shamt) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xa4; \ + x86_reg_emit ((inst), (reg), (dreg)); \ + x86_imm_emit8 ((inst), (shamt)); \ + } while (0) + +/* + * EDX:EAX = EAX * rm + */ +#define x86_mul_reg(inst,reg,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 4 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_mul_mem(inst,mem,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 4 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_mul_membase(inst,basereg,disp,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 4 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +/* + * r *= rm + */ +#define x86_imul_reg_reg(inst,dreg,reg) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_imul_reg_mem(inst,reg,mem) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_imul_reg_membase(inst,reg,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + *(inst)++ = (unsigned char)0xaf; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +/* + * dreg = rm * imm + */ +#define x86_imul_reg_reg_imm(inst,dreg,reg,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + *(inst)++ = (unsigned char)0x6b; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_mem_imm(inst,reg,mem,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + *(inst)++ = (unsigned char)0x6b; \ + x86_mem_emit ((inst), (reg), (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0x69; \ + x86_reg_emit ((inst), (reg), (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_imul_reg_membase_imm(inst,reg,basereg,disp,imm) \ + do { \ + if (x86_is_imm8 ((imm))) { \ + *(inst)++ = (unsigned char)0x6b; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0x69; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +/* + * divide EDX:EAX by rm; + * eax = quotient, edx = remainder + */ + +#define x86_div_reg(inst,reg,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_reg_emit ((inst), 6 + ((is_signed) ? 1 : 0), (reg)); \ + } while (0) + +#define x86_div_mem(inst,mem,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_mem_emit ((inst), 6 + ((is_signed) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_div_membase(inst,basereg,disp,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0xf7; \ + x86_membase_emit ((inst), 6 + ((is_signed) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_mov_mem_reg(inst,mem,reg,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: jit_assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_mov_regp_reg(inst,regp,reg,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: jit_assert (0); \ + } \ + x86_regp_emit ((inst), (reg), (regp)); \ + } while (0) + +#define x86_mov_membase_reg(inst,basereg,disp,reg,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: jit_assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_memindex_reg(inst,basereg,disp,indexreg,shift,reg,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x88; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x89; break; \ + default: jit_assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_mov_reg_reg(inst,dreg,reg,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: jit_assert (0); \ + } \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_mov_reg_mem(inst,reg,mem,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: jit_assert (0); \ + } \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_mov_reg_membase(inst,reg,basereg,disp,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: jit_assert (0); \ + } \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_mov_reg_memindex(inst,reg,basereg,disp,indexreg,shift,size) \ + do { \ + switch ((size)) { \ + case 1: *(inst)++ = (unsigned char)0x8a; break; \ + case 2: *(inst)++ = (unsigned char)0x66; /* fall through */ \ + case 4: *(inst)++ = (unsigned char)0x8b; break; \ + default: jit_assert (0); \ + } \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +/* + * Note: x86_clear_reg () chacnges the condition code! + */ +#define x86_clear_reg(inst,reg) x86_alu_reg_reg((inst), X86_XOR, (reg), (reg)) + +#define x86_mov_reg_imm(inst,reg,imm) \ + do { \ + *(inst)++ = (unsigned char)0xb8 + (reg); \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_mov_mem_imm(inst,mem,imm,size) \ + do { \ + if ((size) == 1) { \ + *(inst)++ = (unsigned char)0xc6; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_mem_emit ((inst), 0, (mem)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_membase_imm(inst,basereg,disp,imm,size) \ + do { \ + if ((size) == 1) { \ + *(inst)++ = (unsigned char)0xc6; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_mov_memindex_imm(inst,basereg,disp,indexreg,shift,imm,size) \ + do { \ + if ((size) == 1) { \ + *(inst)++ = (unsigned char)0xc6; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit8 ((inst), (imm)); \ + } else if ((size) == 2) { \ + *(inst)++ = (unsigned char)0x66; \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit16 ((inst), (imm)); \ + } else { \ + *(inst)++ = (unsigned char)0xc7; \ + x86_memindex_emit ((inst), 0, (basereg), (disp), (indexreg), (shift)); \ + x86_imm_emit32 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_lea_mem(inst,reg,mem) \ + do { \ + *(inst)++ = (unsigned char)0x8d; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_lea_membase(inst,reg,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0x8d; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_lea_memindex(inst,reg,basereg,disp,indexreg,shift) \ + do { \ + *(inst)++ = (unsigned char)0x8d; \ + x86_memindex_emit ((inst), (reg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_widen_reg(inst,dreg,reg,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_widen_mem(inst,dreg,mem,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_mem_emit ((inst), (dreg), (mem)); \ + } while (0) + +#define x86_widen_membase(inst,dreg,basereg,disp,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_membase_emit ((inst), (dreg), (basereg), (disp)); \ + } while (0) + +#define x86_widen_memindex(inst,dreg,basereg,disp,indexreg,shift,is_signed,is_half) \ + do { \ + unsigned char op = 0xb6; \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) op += 0x08; \ + if ((is_half)) op += 0x01; \ + *(inst)++ = op; \ + x86_memindex_emit ((inst), (dreg), (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_cdq(inst) do { *(inst)++ = (unsigned char)0x99; } while (0) +#define x86_wait(inst) do { *(inst)++ = (unsigned char)0x9b; } while (0) + +#define x86_fp_op_mem(inst,opc,mem,is_double) \ + do { \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_mem_emit ((inst), (opc), (mem)); \ + } while (0) + +#define x86_fp_op_membase(inst,opc,basereg,disp,is_double) \ + do { \ + *(inst)++ = (is_double) ? (unsigned char)0xdc : (unsigned char)0xd8; \ + x86_membase_emit ((inst), (opc), (basereg), (disp)); \ + } while (0) + +#define x86_fp_op(inst,opc,index) \ + do { \ + *(inst)++ = (unsigned char)0xd8; \ + *(inst)++ = (unsigned char)0xc0+((opc)<<3)+((index)&0x07); \ + } while (0) + +#define x86_fp_op_reg(inst,opc,index,pop_stack) \ + do { \ + static const unsigned char map[] = { 0, 1, 2, 3, 5, 4, 7, 6, 8}; \ + *(inst)++ = (pop_stack) ? (unsigned char)0xde : (unsigned char)0xdc; \ + *(inst)++ = (unsigned char)0xc0+(map[(opc)]<<3)+((index)&0x07); \ + } while (0) + +/** + * @x86_fp_int_op_membase + * Supports FPU operations between ST(0) and integer operand in memory. + * Operation encoded using X86_FP_Opcode enum. + * Operand is addressed by [basereg + disp]. + * is_int specifies whether operand is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fp_int_op_membase(inst,opc,basereg,disp,is_int) \ + do { \ + *(inst)++ = (is_int) ? (unsigned char)0xda : (unsigned char)0xde; \ + x86_membase_emit ((inst), opc, (basereg), (disp)); \ + } while (0) + +#define x86_fstp(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xdd; \ + *(inst)++ = (unsigned char)0xd8+(index); \ + } while (0) + +#define x86_fcompp(inst) \ + do { \ + *(inst)++ = (unsigned char)0xde; \ + *(inst)++ = (unsigned char)0xd9; \ + } while (0) + +#define x86_fucompp(inst) \ + do { \ + *(inst)++ = (unsigned char)0xda; \ + *(inst)++ = (unsigned char)0xe9; \ + } while (0) + +#define x86_fnstsw(inst) \ + do { \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_fnstcw(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + +#define x86_fnstcw_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + +#define x86_fldcw(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fldcw_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fchs(inst) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe0; \ + } while (0) + +#define x86_frem(inst) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xf8; \ + } while (0) + +#define x86_fxch(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc8 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomi(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fcomip(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xf0 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomi(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xdb; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fucomip(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xdf; \ + *(inst)++ = (unsigned char)0xe8 + ((index) & 0x07); \ + } while (0) + +#define x86_fld(inst,mem,is_double) \ + do { \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_fld_membase(inst,basereg,disp,is_double) \ + do { \ + *(inst)++ = (is_double) ? (unsigned char)0xdd : (unsigned char)0xd9; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_fld80_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 5, (mem)); \ + } while (0) + +#define x86_fld80_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } while (0) + +#define x86_fild(inst,mem,is_long) \ + do { \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 5, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 0, (mem)); \ + } \ + } while (0) + +#define x86_fild_membase(inst,basereg,disp,is_long) \ + do { \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 5, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } \ + } while (0) + +#define x86_fld_reg(inst,index) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xc0 + ((index) & 0x07); \ + } while (0) + +#define x86_fldz(inst) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xee; \ + } while (0) + +#define x86_fld1(inst) \ + do { \ + *(inst)++ = (unsigned char)0xd9; \ + *(inst)++ = (unsigned char)0xe8; \ + } while (0) + +#define x86_fst(inst,mem,is_double,pop_stack) \ + do { \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_mem_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (mem)); \ + } while (0) + +#define x86_fst_membase(inst,basereg,disp,is_double,pop_stack) \ + do { \ + *(inst)++ = (is_double) ? (unsigned char)0xdd: (unsigned char)0xd9; \ + x86_membase_emit ((inst), 2 + ((pop_stack) ? 1 : 0), (basereg), (disp)); \ + } while (0) + +#define x86_fst80_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 7, (mem)); \ + } while (0) + + +#define x86_fst80_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } while (0) + + +#define x86_fist_pop(inst,mem,is_long) \ + do { \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_mem_emit ((inst), 7, (mem)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_mem_emit ((inst), 3, (mem)); \ + } \ + } while (0) + +#define x86_fist_pop_membase(inst,basereg,disp,is_long) \ + do { \ + if ((is_long)) { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 7, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 3, (basereg), (disp)); \ + } \ + } while (0) + +/** + * @x86_fist_membase + * Converts content of ST(0) to integer and stores it at memory location + * addressed by [basereg + disp]. + * is_int specifies whether destination is int32 (TRUE) or int16 (FALSE). + */ +#define x86_fist_membase(inst,basereg,disp,is_int) \ + do { \ + if ((is_int)) { \ + *(inst)++ = (unsigned char)0xdb; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } else { \ + *(inst)++ = (unsigned char)0xdf; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } \ + } while (0) + + +#define x86_push_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x50 + (reg); \ + } while (0) + +#define x86_push_regp(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_regp_emit ((inst), 6, (reg)); \ + } while (0) + +#define x86_push_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 6, (mem)); \ + } while (0) + +#define x86_push_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 6, (basereg), (disp)); \ + } while (0) + +#define x86_push_memindex(inst,basereg,disp,indexreg,shift) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_memindex_emit ((inst), 6, (basereg), (disp), (indexreg), (shift)); \ + } while (0) + +#define x86_push_imm(inst,imm) \ + do { \ + *(inst)++ = (unsigned char)0x68; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_pop_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0x58 + (reg); \ + } while (0) + +#define x86_pop_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0x87; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_pop_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0x87; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_pushad(inst) do { *(inst)++ = (unsigned char)0x60; } while (0) +#define x86_pushfd(inst) do { *(inst)++ = (unsigned char)0x9c; } while (0) +#define x86_popad(inst) do { *(inst)++ = (unsigned char)0x61; } while (0) +#define x86_popfd(inst) do { *(inst)++ = (unsigned char)0x9d; } while (0) + +#define x86_loop(inst,imm) \ + do { \ + *(inst)++ = (unsigned char)0xe2; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loope(inst,imm) \ + do { \ + *(inst)++ = (unsigned char)0xe1; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_loopne(inst,imm) \ + do { \ + *(inst)++ = (unsigned char)0xe0; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_jump32(inst,imm) \ + do { \ + *(inst)++ = (unsigned char)0xe9; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_jump8(inst,imm) \ + do { \ + *(inst)++ = (unsigned char)0xeb; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_jump_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 4, (reg)); \ + } while (0) + +#define x86_jump_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 4, (mem)); \ + } while (0) + +#define x86_jump_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 4, (basereg), (disp)); \ + } while (0) + +/* + * target is a pointer in our buffer. + */ +#define x86_jump_code(inst,target) \ + do { \ + int t = (unsigned char*)(target) - (inst) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + t -= 3; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#define x86_jump_disp(inst,disp) \ + do { \ + int t = (disp) - 2; \ + if (x86_is_imm8(t)) { \ + x86_jump8 ((inst), t); \ + } else { \ + t -= 3; \ + x86_jump32 ((inst), t); \ + } \ + } while (0) + +#define x86_branch8(inst,cond,imm,is_signed) \ + do { \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)]; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)]; \ + x86_imm_emit8 ((inst), (imm)); \ + } while (0) + +#define x86_branch32(inst,cond,imm,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x10; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x10; \ + x86_imm_emit32 ((inst), (imm)); \ + } while (0) + +#define x86_branch(inst,cond,target,is_signed) \ + do { \ + int offset = (target) - (inst) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset -= 4; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#define x86_branch_disp(inst,cond,disp,is_signed) \ + do { \ + int offset = (disp) - 2; \ + if (x86_is_imm8 ((offset))) \ + x86_branch8 ((inst), (cond), offset, (is_signed)); \ + else { \ + offset -= 4; \ + x86_branch32 ((inst), (cond), offset, (is_signed)); \ + } \ + } while (0) + +#define x86_set_reg(inst,cond,reg,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_reg_emit ((inst), 0, (reg)); \ + } while (0) + +#define x86_set_mem(inst,cond,mem,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_mem_emit ((inst), 0, (mem)); \ + } while (0) + +#define x86_set_membase(inst,cond,basereg,disp,is_signed) \ + do { \ + *(inst)++ = (unsigned char)0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] + 0x20; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] + 0x20; \ + x86_membase_emit ((inst), 0, (basereg), (disp)); \ + } while (0) + +#define x86_call_imm(inst,disp) \ + do { \ + *(inst)++ = (unsigned char)0xe8; \ + x86_imm_emit32 ((inst), (int)(disp)); \ + } while (0) + +#define x86_call_reg(inst,reg) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_reg_emit ((inst), 2, (reg)); \ + } while (0) + +#define x86_call_mem(inst,mem) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_mem_emit ((inst), 2, (mem)); \ + } while (0) + +#define x86_call_membase(inst,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char)0xff; \ + x86_membase_emit ((inst), 2, (basereg), (disp)); \ + } while (0) + +#define x86_call_code(inst,target) \ + do { \ + int _x86_offset = (unsigned char*)(target) - (inst); \ + _x86_offset -= 5; \ + x86_call_imm ((inst), _x86_offset); \ + } while (0) + +#define x86_ret(inst) do { *(inst)++ = (unsigned char)0xc3; } while (0) + +#define x86_ret_imm(inst,imm) \ + do { \ + if ((imm) == 0) { \ + x86_ret ((inst)); \ + } else { \ + *(inst)++ = (unsigned char)0xc2; \ + x86_imm_emit16 ((inst), (imm)); \ + } \ + } while (0) + +#define x86_cmov_reg(inst,cond,is_signed,dreg,reg) \ + do { \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_reg_emit ((inst), (dreg), (reg)); \ + } while (0) + +#define x86_cmov_mem(inst,cond,is_signed,reg,mem) \ + do { \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_mem_emit ((inst), (reg), (mem)); \ + } while (0) + +#define x86_cmov_membase(inst,cond,is_signed,reg,basereg,disp) \ + do { \ + *(inst)++ = (unsigned char) 0x0f; \ + if ((is_signed)) \ + *(inst)++ = x86_cc_signed_map [(cond)] - 0x30; \ + else \ + *(inst)++ = x86_cc_unsigned_map [(cond)] - 0x30; \ + x86_membase_emit ((inst), (reg), (basereg), (disp)); \ + } while (0) + +#define x86_enter(inst,framesize) \ + do { \ + *(inst)++ = (unsigned char)0xc8; \ + x86_imm_emit16 ((inst), (framesize)); \ + *(inst)++ = 0; \ + } while (0) + +#define x86_leave(inst) do { *(inst)++ = (unsigned char)0xc9; } while (0) +#define x86_sahf(inst) do { *(inst)++ = (unsigned char)0x9e; } while (0) + +#define x86_fsin(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfe; } while (0) +#define x86_fcos(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xff; } while (0) +#define x86_fabs(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xe1; } while (0) +#define x86_fpatan(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf3; } while (0) +#define x86_fprem(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf8; } while (0) +#define x86_fprem1(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf5; } while (0) +#define x86_frndint(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfc; } while (0) +#define x86_fsqrt(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xfa; } while (0) +#define x86_fptan(inst) do { *(inst)++ = (unsigned char)0xd9; *(inst)++ = (unsigned char)0xf2; } while (0) + +#define x86_padding(inst,size) \ + do { \ + switch ((size)) { \ + case 1: x86_nop ((inst)); break; \ + case 2: *(inst)++ = 0x8b; \ + *(inst)++ = 0xc0; break; \ + case 3: *(inst)++ = 0x8d; *(inst)++ = 0x6d; \ + *(inst)++ = 0x00; break; \ + case 4: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + break; \ + case 5: *(inst)++ = 0x8d; *(inst)++ = 0x64; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + x86_nop ((inst)); break; \ + case 6: *(inst)++ = 0x8d; *(inst)++ = 0xad; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + break; \ + case 7: *(inst)++ = 0x8d; *(inst)++ = 0xa4; \ + *(inst)++ = 0x24; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; *(inst)++ = 0x00; \ + *(inst)++ = 0x00; break; \ + default: jit_assert (0); \ + } \ + } while (0) + +#define x86_prolog(inst,frame_size,reg_mask) \ + do { \ + unsigned i, m = 1; \ + x86_enter ((inst), (frame_size)); \ + for (i = 0; i < X86_NREG; ++i, m <<= 1) { \ + if ((reg_mask) & m) \ + x86_push_reg ((inst), i); \ + } \ + } while (0) + +#define x86_epilog(inst,reg_mask) \ + do { \ + unsigned i, m = 1 << X86_EDI; \ + for (i = X86_EDI; m != 0; i--, m=m>>1) { \ + if ((reg_mask) & m) \ + x86_pop_reg ((inst), i); \ + } \ + x86_leave ((inst)); \ + x86_ret ((inst)); \ + } while (0) + +#endif /* JIT_GEN_X86_H */ diff --git a/jit/jit-init.c b/jit/jit-init.c new file mode 100644 index 0000000..5146d05 --- /dev/null +++ b/jit/jit-init.c @@ -0,0 +1,61 @@ +/* + * jit-init.c - Initialization routines for the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" + +/*@ + * @deftypefun void jit_init (void) + * This is normally the first function that you call when using + * @code{libjit}. It initializes the library and prepares for + * JIT operations. + * + * The @code{jit_context_create} function also calls this, so you can + * avoid using @code{jit_init} if @code{jit_context_create} is the first + * JIT function that you use. + * + * It is safe to initialize the JIT multiple times. Subsequent + * initializations are quietly ignored. + * @end deftypefun +@*/ +void jit_init(void) +{ + /* Make sure that the thread subsystem is initialized */ + _jit_thread_init(); + + /* Initialize the backend */ + _jit_init_backend(); +} + +/*@ + * @deftypefun int jit_uses_interpreter (void) + * Determine if the JIT uses a fall-back interpreter to execute code + * rather than generating and executing native code. This can be + * called prior to @code{jit_init}. + * @end deftypefun +@*/ +int jit_uses_interpreter(void) +{ +#if defined(JIT_BACKEND_INTERP) + return 1; +#else + return 0; +#endif +} diff --git a/jit/jit-insn.c b/jit/jit-insn.c new file mode 100644 index 0000000..efafa90 --- /dev/null +++ b/jit/jit-insn.c @@ -0,0 +1,6237 @@ +/* + * jit-insn.c - Functions for manipulating instructions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" +#include +#if HAVE_ALLOCA_H + #include +#endif +#ifdef JIT_WIN32_PLATFORM + #include + #ifndef alloca + #define alloca _alloca + #endif +#endif + +/*@ + +@cindex jit-insn.h + +@*/ + +/* + * Opcode description blocks. These describe the alternative opcodes + * and intrinsic functions to use for various kinds of arguments. + */ +typedef struct +{ + unsigned short ioper; /* Primary operator for "int" */ + unsigned short iuoper; /* Primary operator for "uint" */ + unsigned short loper; /* Primary operator for "long" */ + unsigned short luoper; /* Primary operator for "ulong" */ + unsigned short foper; /* Primary operator for "float32" */ + unsigned short doper; /* Primary operator for "float64" */ + unsigned short nfoper; /* Primary operator for "nfloat" */ + void *ifunc; /* Function for "int" */ + const char *iname; /* Intrinsic name for "int" */ + const jit_intrinsic_descr_t *idesc; /* Descriptor for "int" */ + void *iufunc; /* Function for "uint" */ + const char *iuname; /* Intrinsic name for "uint" */ + const jit_intrinsic_descr_t *iudesc; /* Descriptor for "uint" */ + void *lfunc; /* Function for "long" */ + const char *lname; /* Intrinsic name for "long" */ + const jit_intrinsic_descr_t *ldesc; /* Descriptor for "long" */ + void *lufunc; /* Function for "ulong" */ + const char *luname; /* Intrinsic name for "ulong" */ + const jit_intrinsic_descr_t *ludesc; /* Descriptor for "ulong" */ + void *ffunc; /* Function for "float32" */ + const char *fname; /* Intrinsic name for "float32" */ + const jit_intrinsic_descr_t *fdesc; /* Descriptor for "float32" */ + void *dfunc; /* Function for "float64" */ + const char *dname; /* Intrinsic name for "float64" */ + const jit_intrinsic_descr_t *ddesc; /* Descriptor for "float64" */ + void *nffunc; /* Function for "nfloat" */ + const char *nfname; /* Intrinsic name for "nfloat" */ + const jit_intrinsic_descr_t *nfdesc; /* Descriptor for "nfloat" */ + +} jit_opcode_descr; +#define jit_intrinsic(name,descr) (void *)name, #name, &descr +#define jit_no_intrinsic 0, 0, 0 + +/* + * Some common intrinsic descriptors that are used in this file. + */ +static jit_intrinsic_descr_t const descr_i_ii = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_int_def, + (jit_type_t)&_jit_type_int_def +}; +static jit_intrinsic_descr_t const descr_e_pi_ii = { + (jit_type_t)&_jit_type_int_def, + (jit_type_t)&_jit_type_int_def, + (jit_type_t)&_jit_type_int_def, + (jit_type_t)&_jit_type_int_def +}; +static jit_intrinsic_descr_t const descr_i_iI = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_int_def, + (jit_type_t)&_jit_type_uint_def +}; +static jit_intrinsic_descr_t const descr_i_i = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_int_def, + 0 +}; +static jit_intrinsic_descr_t const descr_I_II = { + (jit_type_t)&_jit_type_uint_def, + 0, + (jit_type_t)&_jit_type_uint_def, + (jit_type_t)&_jit_type_uint_def +}; +static jit_intrinsic_descr_t const descr_e_pI_II = { + (jit_type_t)&_jit_type_uint_def, + (jit_type_t)&_jit_type_uint_def, + (jit_type_t)&_jit_type_uint_def, + (jit_type_t)&_jit_type_uint_def +}; +static jit_intrinsic_descr_t const descr_I_I = { + (jit_type_t)&_jit_type_uint_def, + 0, + (jit_type_t)&_jit_type_uint_def, + 0 +}; +static jit_intrinsic_descr_t const descr_i_II = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_uint_def, + (jit_type_t)&_jit_type_uint_def +}; +static jit_intrinsic_descr_t const descr_l_ll = { + (jit_type_t)&_jit_type_long_def, + 0, + (jit_type_t)&_jit_type_long_def, + (jit_type_t)&_jit_type_long_def +}; +static jit_intrinsic_descr_t const descr_e_pl_ll = { + (jit_type_t)&_jit_type_long_def, + (jit_type_t)&_jit_type_long_def, + (jit_type_t)&_jit_type_long_def, + (jit_type_t)&_jit_type_long_def +}; +static jit_intrinsic_descr_t const descr_l_lI = { + (jit_type_t)&_jit_type_long_def, + 0, + (jit_type_t)&_jit_type_long_def, + (jit_type_t)&_jit_type_uint_def +}; +static jit_intrinsic_descr_t const descr_l_l = { + (jit_type_t)&_jit_type_long_def, + 0, + (jit_type_t)&_jit_type_long_def, + 0 +}; +static jit_intrinsic_descr_t const descr_i_ll = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_long_def, + (jit_type_t)&_jit_type_long_def +}; +static jit_intrinsic_descr_t const descr_i_l = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_long_def, + 0 +}; +static jit_intrinsic_descr_t const descr_L_LL = { + (jit_type_t)&_jit_type_ulong_def, + 0, + (jit_type_t)&_jit_type_ulong_def, + (jit_type_t)&_jit_type_ulong_def +}; +static jit_intrinsic_descr_t const descr_e_pL_LL = { + (jit_type_t)&_jit_type_ulong_def, + (jit_type_t)&_jit_type_ulong_def, + (jit_type_t)&_jit_type_ulong_def, + (jit_type_t)&_jit_type_ulong_def +}; +static jit_intrinsic_descr_t const descr_L_LI = { + (jit_type_t)&_jit_type_ulong_def, + 0, + (jit_type_t)&_jit_type_ulong_def, + (jit_type_t)&_jit_type_uint_def +}; +static jit_intrinsic_descr_t const descr_L_L = { + (jit_type_t)&_jit_type_ulong_def, + 0, + (jit_type_t)&_jit_type_ulong_def, + 0 +}; +static jit_intrinsic_descr_t const descr_i_LL = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_ulong_def, + (jit_type_t)&_jit_type_ulong_def +}; +static jit_intrinsic_descr_t const descr_f_ff = { + (jit_type_t)&_jit_type_float32_def, + 0, + (jit_type_t)&_jit_type_float32_def, + (jit_type_t)&_jit_type_float32_def +}; +static jit_intrinsic_descr_t const descr_f_f = { + (jit_type_t)&_jit_type_float32_def, + 0, + (jit_type_t)&_jit_type_float32_def, + 0 +}; +static jit_intrinsic_descr_t const descr_i_ff = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_float32_def, + (jit_type_t)&_jit_type_float32_def +}; +static jit_intrinsic_descr_t const descr_i_f = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_float32_def, + 0 +}; +static jit_intrinsic_descr_t const descr_d_dd = { + (jit_type_t)&_jit_type_float64_def, + 0, + (jit_type_t)&_jit_type_float64_def, + (jit_type_t)&_jit_type_float64_def +}; +static jit_intrinsic_descr_t const descr_d_d = { + (jit_type_t)&_jit_type_float64_def, + 0, + (jit_type_t)&_jit_type_float64_def, + 0 +}; +static jit_intrinsic_descr_t const descr_i_dd = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_float64_def, + (jit_type_t)&_jit_type_float64_def +}; +static jit_intrinsic_descr_t const descr_i_d = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_float64_def, + 0 +}; +static jit_intrinsic_descr_t const descr_D_DD = { + (jit_type_t)&_jit_type_nfloat_def, + 0, + (jit_type_t)&_jit_type_nfloat_def, + (jit_type_t)&_jit_type_nfloat_def +}; +static jit_intrinsic_descr_t const descr_D_D = { + (jit_type_t)&_jit_type_nfloat_def, + 0, + (jit_type_t)&_jit_type_nfloat_def, + 0 +}; +static jit_intrinsic_descr_t const descr_i_DD = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_nfloat_def, + (jit_type_t)&_jit_type_nfloat_def +}; +static jit_intrinsic_descr_t const descr_i_D = { + (jit_type_t)&_jit_type_int_def, + 0, + (jit_type_t)&_jit_type_nfloat_def, + 0 +}; + +/* + * Apply a unary operator. + */ +static jit_value_t apply_unary + (jit_function_t func, int oper, jit_value_t value1, + jit_type_t result_type) +{ + jit_value_t dest; + jit_insn_t insn; + if(!value1) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + dest = jit_value_create(func, result_type); + if(!dest) + { + return 0; + } + jit_value_ref(func, value1); + insn->opcode = (short)oper; + insn->dest = dest; + insn->value1 = value1; + return dest; +} + +/* + * Apply a binary operator. + */ +static jit_value_t apply_binary + (jit_function_t func, int oper, jit_value_t value1, + jit_value_t value2, jit_type_t result_type) +{ + jit_value_t dest; + jit_insn_t insn; + if(!value1 || !value2) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + dest = jit_value_create(func, result_type); + if(!dest) + { + return 0; + } + jit_value_ref(func, value1); + jit_value_ref(func, value2); + insn->opcode = (short)oper; + insn->dest = dest; + insn->value1 = value1; + insn->value2 = value2; + return dest; +} + +/* + * Create a note instruction, which doesn't have a result. + */ +static int create_note + (jit_function_t func, int oper, jit_value_t value1, + jit_value_t value2) +{ + jit_insn_t insn; + if(!value1 || !value2) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value1); + jit_value_ref(func, value2); + insn->opcode = (short)oper; + insn->value1 = value1; + insn->value2 = value2; + return 1; +} + +/* + * Create a unary note instruction, which doesn't have a result. + */ +static int create_unary_note + (jit_function_t func, int oper, jit_value_t value1) +{ + jit_insn_t insn; + if(!value1) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value1); + insn->opcode = (short)oper; + insn->value1 = value1; + return 1; +} + +/* + * Create a note instruction with no arguments, which doesn't have a result. + */ +static int create_noarg_note(jit_function_t func, int oper) +{ + jit_insn_t insn; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + insn->opcode = (short)oper; + return 1; +} + +/* + * Create a note instruction with only a destination. + */ +static jit_value_t create_dest_note + (jit_function_t func, int oper, jit_type_t type) +{ + jit_insn_t insn; + jit_value_t value; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + value = jit_value_create(func, type); + if(!value) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value); + insn->opcode = (short)oper; + insn->dest = value; + return value; +} + +/* + * Get the common type to use for a binary operator. + */ +static jit_type_t common_binary(jit_type_t type1, jit_type_t type2, + int int_only, int float_only) +{ + type1 = jit_type_promote_int(jit_type_normalize(type1)); + type2 = jit_type_promote_int(jit_type_normalize(type2)); + if(!float_only) + { + if(type1 == jit_type_int) + { + if(type2 == jit_type_int || type2 == jit_type_uint) + { + return jit_type_int; + } + else if(type2 == jit_type_long || type2 == jit_type_ulong) + { + return jit_type_long; + } + } + else if(type1 == jit_type_uint) + { + if(type2 == jit_type_int || type2 == jit_type_uint || + type2 == jit_type_long || type2 == jit_type_ulong) + { + return type2; + } + } + else if(type1 == jit_type_long) + { + if(type2 == jit_type_int || type2 == jit_type_uint || + type2 == jit_type_long || type2 == jit_type_ulong) + { + return jit_type_long; + } + } + else if(type1 == jit_type_ulong) + { + if(type2 == jit_type_int || type2 == jit_type_long) + { + return jit_type_long; + } + else if(type2 == jit_type_uint || type2 == jit_type_ulong) + { + return jit_type_ulong; + } + } + if(int_only) + { + return jit_type_long; + } + } + if(type1 == jit_type_nfloat || type2 == jit_type_nfloat) + { + return jit_type_nfloat; + } + else if(type1 == jit_type_float64 || type2 == jit_type_float64) + { + return jit_type_float64; + } + else if(type1 == jit_type_float32 || type2 == jit_type_float32) + { + return jit_type_float32; + } + else + { + /* Probably integer arguments when "float_only" is set */ + return jit_type_nfloat; + } +} + +/* + * Apply an intrinsic. + */ +static jit_value_t apply_intrinsic + (jit_function_t func, const jit_opcode_descr *descr, + jit_value_t value1, jit_value_t value2, jit_type_t result_type) +{ + if(result_type == jit_type_int) + { + return jit_insn_call_intrinsic + (func, descr->iname, descr->ifunc, descr->idesc, value1, value2); + } + else if(result_type == jit_type_uint) + { + return jit_insn_call_intrinsic + (func, descr->iuname, descr->iufunc, descr->iudesc, value1, value2); + } + else if(result_type == jit_type_long) + { + return jit_insn_call_intrinsic + (func, descr->lname, descr->lfunc, descr->ldesc, value1, value2); + } + else if(result_type == jit_type_ulong) + { + return jit_insn_call_intrinsic + (func, descr->luname, descr->lufunc, descr->ludesc, value1, value2); + } + else if(result_type == jit_type_float32) + { + return jit_insn_call_intrinsic + (func, descr->fname, descr->ffunc, descr->fdesc, value1, value2); + } + else if(result_type == jit_type_float64) + { + return jit_insn_call_intrinsic + (func, descr->dname, descr->dfunc, descr->ddesc, value1, value2); + } + else + { + return jit_insn_call_intrinsic + (func, descr->nfname, descr->nffunc, descr->nfdesc, value1, value2); + } +} + +/* + * Apply a unary arithmetic operator, after coercing the + * argument to a suitable numeric type. + */ +static jit_value_t apply_unary_arith + (jit_function_t func, const jit_opcode_descr *descr, + jit_value_t value1, int int_only, int float_only, + int overflow_check) +{ + int oper; + jit_type_t result_type; + if(!value1) + { + return 0; + } + result_type = common_binary + (value1->type, value1->type, int_only, float_only); + if(result_type == jit_type_int) + { + oper = descr->ioper; + } + else if(result_type == jit_type_uint) + { + oper = descr->iuoper; + } + else if(result_type == jit_type_long) + { + oper = descr->loper; + } + else if(result_type == jit_type_ulong) + { + oper = descr->luoper; + } + else if(result_type == jit_type_float32) + { + oper = descr->foper; + } + else if(result_type == jit_type_float64) + { + oper = descr->doper; + } + else + { + oper = descr->nfoper; + } + value1 = jit_insn_convert(func, value1, result_type, overflow_check); + if(_jit_opcode_is_supported(oper)) + { + return apply_unary(func, oper, value1, result_type); + } + else + { + return apply_intrinsic(func, descr, value1, 0, result_type); + } +} + +/* + * Apply a binary arithmetic operator, after coercing both + * arguments to a common type. + */ +static jit_value_t apply_arith + (jit_function_t func, const jit_opcode_descr *descr, + jit_value_t value1, jit_value_t value2, + int int_only, int float_only, int overflow_check) +{ + int oper; + jit_type_t result_type; + if(!value1 || !value2) + { + return 0; + } + result_type = common_binary + (value1->type, value2->type, int_only, float_only); + if(result_type == jit_type_int) + { + oper = descr->ioper; + } + else if(result_type == jit_type_uint) + { + oper = descr->iuoper; + } + else if(result_type == jit_type_long) + { + oper = descr->loper; + } + else if(result_type == jit_type_ulong) + { + oper = descr->luoper; + } + else if(result_type == jit_type_float32) + { + oper = descr->foper; + } + else if(result_type == jit_type_float64) + { + oper = descr->doper; + } + else + { + oper = descr->nfoper; + } + value1 = jit_insn_convert(func, value1, result_type, overflow_check); + value2 = jit_insn_convert(func, value2, result_type, overflow_check); + if(_jit_opcode_is_supported(oper)) + { + return apply_binary(func, oper, value1, value2, result_type); + } + else + { + return apply_intrinsic(func, descr, value1, value2, result_type); + } +} + +/* + * Apply a binary shift operator, after coercing both + * arguments to suitable types. + */ +static jit_value_t apply_shift + (jit_function_t func, const jit_opcode_descr *descr, + jit_value_t value1, jit_value_t value2) +{ + int oper; + jit_type_t result_type; + jit_type_t count_type; + if(!value1 || !value2) + { + return 0; + } + result_type = common_binary(value1->type, value1->type, 1, 0); + if(result_type == jit_type_int) + { + oper = descr->ioper; + } + else if(result_type == jit_type_uint) + { + oper = descr->iuoper; + } + else if(result_type == jit_type_long) + { + oper = descr->loper; + } + else if(result_type == jit_type_ulong) + { + oper = descr->luoper; + } + else + { + /* Shouldn't happen */ + oper = descr->loper; + } + count_type = jit_type_promote_int(jit_type_normalize(value2->type)); + if(count_type != jit_type_int) + { + count_type = jit_type_uint; + } + value1 = jit_insn_convert(func, value1, result_type, 0); + value2 = jit_insn_convert(func, value2, count_type, 0); + if(_jit_opcode_is_supported(oper)) + { + return apply_binary(func, oper, value1, value2, result_type); + } + else + { + return apply_intrinsic(func, descr, value1, value2, result_type); + } +} + +/* + * Apply a binary comparison operator, after coercing both + * arguments to a common type. + */ +static jit_value_t apply_compare + (jit_function_t func, const jit_opcode_descr *descr, + jit_value_t value1, jit_value_t value2, int float_only) +{ + int oper; + jit_type_t result_type; + if(!value1 || !value2) + { + return 0; + } + result_type = common_binary(value1->type, value2->type, 0, float_only); + if(result_type == jit_type_int) + { + oper = descr->ioper; + } + else if(result_type == jit_type_uint) + { + oper = descr->iuoper; + } + else if(result_type == jit_type_long) + { + oper = descr->loper; + } + else if(result_type == jit_type_ulong) + { + oper = descr->luoper; + } + else if(result_type == jit_type_float32) + { + oper = descr->foper; + } + else if(result_type == jit_type_float64) + { + oper = descr->doper; + } + else + { + oper = descr->nfoper; + } + value1 = jit_insn_convert(func, value1, result_type, 0); + value2 = jit_insn_convert(func, value2, result_type, 0); + if(_jit_opcode_is_supported(oper)) + { + return apply_binary(func, oper, value1, value2, jit_type_int); + } + else + { + return apply_intrinsic(func, descr, value1, value2, result_type); + } +} + +/* + * Apply a unary test operator, after coercing the + * argument to an appropriate type. + */ +static jit_value_t apply_test + (jit_function_t func, const jit_opcode_descr *descr, + jit_value_t value1, int float_only) +{ + int oper; + jit_type_t result_type; + if(!value1) + { + return 0; + } + result_type = common_binary(value1->type, value1->type, 0, float_only); + if(result_type == jit_type_int) + { + oper = descr->ioper; + } + else if(result_type == jit_type_uint) + { + oper = descr->iuoper; + } + else if(result_type == jit_type_long) + { + oper = descr->loper; + } + else if(result_type == jit_type_ulong) + { + oper = descr->luoper; + } + else if(result_type == jit_type_float32) + { + oper = descr->foper; + } + else if(result_type == jit_type_float64) + { + oper = descr->doper; + } + else + { + oper = descr->nfoper; + } + value1 = jit_insn_convert(func, value1, result_type, 0); + if(_jit_opcode_is_supported(oper)) + { + return apply_unary(func, oper, value1, jit_type_int); + } + else + { + return apply_intrinsic(func, descr, value1, 0, result_type); + } +} + +/*@ + * @deftypefun int jit_insn_get_opcode (jit_insn_t insn) + * Get the opcode that is associated with an instruction. + * @end deftypefun +@*/ +int jit_insn_get_opcode(jit_insn_t insn) +{ + if(insn) + { + return insn->opcode; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_get_dest (jit_insn_t insn) + * Get the destination value that is associated with an instruction. + * Returns NULL if the instruction does not have a destination. + * @end deftypefun +@*/ +jit_value_t jit_insn_get_dest(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_DEST_OTHER_FLAGS) == 0) + { + return insn->dest; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_get_value1 (jit_insn_t insn) + * Get the first argument value that is associated with an instruction. + * Returns NULL if the instruction does not have a first argument value. + * @end deftypefun +@*/ +jit_value_t jit_insn_get_value1(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) + { + return insn->value1; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_get_value2 (jit_insn_t insn) + * Get the second argument value that is associated with an instruction. + * Returns NULL if the instruction does not have a second argument value. + * @end deftypefun +@*/ +jit_value_t jit_insn_get_value2(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) + { + return insn->value2; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_label_t jit_insn_get_label (jit_insn_t insn) + * Get the label for a branch target from an instruction. + * Returns NULL if the instruction does not have a branch target. + * @end deftypefun +@*/ +jit_label_t jit_insn_get_label(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_DEST_IS_LABEL) != 0) + { + return (jit_label_t)(insn->dest); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_function_t jit_insn_get_function (jit_insn_t insn) + * Get the function for a call instruction. Returns NULL if the + * instruction does not refer to a called function. + * @end deftypefun +@*/ +jit_function_t jit_insn_get_function(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_DEST_IS_FUNCTION) != 0) + { + return (jit_function_t)(insn->dest); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun {void *} jit_insn_get_native (jit_insn_t insn) + * Get the function pointer for a native call instruction. + * Returns NULL if the instruction does not refer to a native + * function call. + * @end deftypefun +@*/ +void *jit_insn_get_native(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_DEST_IS_NATIVE) != 0) + { + return (void *)(insn->dest); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun {const char *} jit_insn_get_name (jit_insn_t insn) + * Get the diagnostic name for a function call. Returns NULL + * if the instruction does not have a diagnostic name. + * @end deftypefun +@*/ +const char *jit_insn_get_name(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_VALUE1_IS_NAME) != 0) + { + return (const char *)(insn->value1); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_type_t jit_insn_get_signature (jit_insn_t insn) + * Get the signature for a function call instruction. Returns NULL + * if the instruction is not a function call. + * @end deftypefun +@*/ +jit_type_t jit_insn_get_signature(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_VALUE2_IS_SIGNATURE) != 0) + { + return (jit_type_t)(insn->value2); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_insn_dest_is_value (jit_insn_t insn) + * Returns a non-zero value if the destination for @code{insn} is + * actually a source value. This can happen with instructions + * such as @code{jit_insn_store_relative} where the instruction + * needs three source operands, and the real destination is a + * side-effect on one of the sources. + * @end deftypefun +@*/ +int jit_insn_dest_is_value(jit_insn_t insn) +{ + if(insn && (insn->flags & JIT_INSN_DEST_IS_VALUE) != 0) + { + return 1; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun void jit_insn_label (jit_function_t func, {jit_label_t *} label) + * Start a new block within the function @code{func} and give it the + * specified @code{label}. Returns zero if out of memory. + * + * If the contents of @code{label} are @code{jit_label_undefined}, then this + * function will allocate a new label for this block. Otherwise it will + * reuse the specified label from a previous branch instruction. + * @end deftypefun +@*/ +int jit_insn_label(jit_function_t func, jit_label_t *label) +{ + jit_block_t current; + jit_insn_t last; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + current = func->builder->current_block; + last = _jit_block_get_last(current); + if(current->label == jit_label_undefined && !last) + { + /* We just started a new block after a branch instruction, + so don't bother creating another new block */ + if(*label == jit_label_undefined) + { + *label = (func->builder->next_label)++; + } + current->label = *label; + current->entered_via_branch = 1; + if(!_jit_block_record_label(current)) + { + return 0; + } + } + else + { + /* Create a new block */ + jit_block_t block = _jit_block_create(func, label); + if(!block) + { + return 0; + } + + /* The label indicates that something is branching to us */ + block->entered_via_branch = 1; + + /* Does the last block contain instructions? */ + if(last) + { + /* We will be entered via the top if the last block + did not end in an unconditional branch and it + is not explicitly marked as "dead" */ + if(last->opcode != JIT_OP_BR && !(current->ends_in_dead)) + { + block->entered_via_top = 1; + } + } + else + { + /* We will be entered via the top if the last empty + block was entered via any mechanism */ + block->entered_via_top = + (current->entered_via_top || + current->entered_via_branch); + } + + /* Set the new block as the current one */ + func->builder->current_block = block; + } + return 1; +} + +int _jit_load_opcode(int base_opcode, jit_type_t type, + jit_value_t value, int no_temps) +{ + type = jit_type_normalize(type); + if(!type) + { + return 0; + } + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + return base_opcode; + } + /* Not reached */ + + case JIT_TYPE_UBYTE: + { + return base_opcode + 1; + } + /* Not reached */ + + case JIT_TYPE_SHORT: + { + return base_opcode + 2; + } + /* Not reached */ + + case JIT_TYPE_USHORT: + { + return base_opcode + 3; + } + /* Not reached */ + + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + if(no_temps && value && (value->is_temporary || value->is_local)) + { + return 0; + } + return base_opcode + 4; + } + /* Not reached */ + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + if(no_temps && value && (value->is_temporary || value->is_local)) + { + return 0; + } + return base_opcode + 5; + } + /* Not reached */ + + case JIT_TYPE_FLOAT32: + { + if(no_temps && value && (value->is_temporary || value->is_local)) + { + return 0; + } + return base_opcode + 6; + } + /* Not reached */ + + case JIT_TYPE_FLOAT64: + { + if(no_temps && value && (value->is_temporary || value->is_local)) + { + return 0; + } + return base_opcode + 7; + } + /* Not reached */ + + case JIT_TYPE_NFLOAT: + { + if(no_temps && value && (value->is_temporary || value->is_local)) + { + return 0; + } + return base_opcode + 8; + } + /* Not reached */ + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + return base_opcode + 9; + } + /* Not reached */ + } + return 0; +} + +int _jit_store_opcode(int base_opcode, int small_base, jit_type_t type) +{ + /* Copy instructions are in two ranges: adjust for them */ + if(small_base) + { + base_opcode -= 2; + } + else + { + small_base = base_opcode; + } + + /* Determine which opcode to use */ + type = jit_type_normalize(type); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + { + return small_base; + } + /* Not reached */ + + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + { + return small_base + 1; + } + /* Not reached */ + + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + return base_opcode + 2; + } + /* Not reached */ + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + return base_opcode + 3; + } + /* Not reached */ + + case JIT_TYPE_FLOAT32: + { + return base_opcode + 4; + } + break; + + case JIT_TYPE_FLOAT64: + { + return base_opcode + 5; + } + break; + /* Not reached */ + + case JIT_TYPE_NFLOAT: + { + return base_opcode + 6; + } + /* Not reached */ + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + return base_opcode + 7; + } + /* Not reached */ + + default: + { + /* Shouldn't happen, but do something sane anyway */ + return base_opcode + 2; + } + /* Not reached */ + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_load (jit_function_t func, jit_value_t value) + * Load the contents of @code{value} into a new temporary, essentially + * duplicating the value. Constants are not duplicated. + * @end deftypefun +@*/ +jit_value_t jit_insn_load(jit_function_t func, jit_value_t value) +{ + if(!value) + { + return 0; + } + else if(value->is_constant) + { + return value; + } + else + { + int opcode = _jit_load_opcode + (JIT_OP_COPY_LOAD_SBYTE, value->type, value, 0); + return apply_unary(func, opcode, value, value->type); + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_dup (jit_function_t func, jit_value_t value) + * This is the same as @code{jit_insn_load}, but the name may better + * reflect how it is used in some front ends. + * @end deftypefun +@*/ +jit_value_t jit_insn_dup(jit_function_t func, jit_value_t value) +{ + return jit_insn_load(func, value); +} + +/*@ + * @deftypefun jit_value_t jit_insn_load_small (jit_function_t func, jit_value_t value) + * If @code{value} is of type @code{sbyte}, @code{byte}, @code{short}, + * @code{ushort}, a structure, or a union, then make a copy of it and + * return the temporary copy. Otherwise return @code{value} as-is. + * + * This is useful where you want to use @code{value} directly without + * duplicating it first. However, certain types usually cannot + * be operated on directly without first copying them elsewhere. + * This function will do that whenever necessary. + * @end deftypefun +@*/ +jit_value_t jit_insn_load_small(jit_function_t func, jit_value_t value) +{ + if(!value) + { + return 0; + } + else if(value->is_constant) + { + return value; + } + else + { + int opcode = _jit_load_opcode + (JIT_OP_COPY_LOAD_SBYTE, value->type, value, 1); + if(opcode) + { + return apply_unary(func, opcode, value, value->type); + } + else + { + return value; + } + } +} + +/*@ + * @deftypefun void jit_insn_store (jit_function_t func, jit_value_t dest, jit_value_t value) + * Store the contents of @code{value} at the location referred to by + * @code{dest}. + * @end deftypefun +@*/ +int jit_insn_store(jit_function_t func, jit_value_t dest, jit_value_t value) +{ + jit_insn_t insn; + if(!dest || !value) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + value = jit_insn_convert(func, value, dest->type, 0); + if(!value) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, dest); + jit_value_ref(func, value); + insn->opcode = (short)_jit_store_opcode + (JIT_OP_COPY_INT, JIT_OP_COPY_STORE_BYTE, value->type); + insn->dest = dest; + insn->value1 = value; + return 1; +} + +/* + * Scan back through the current block, looking for a relative adjustment + * that involves "value" as its destination. Returns NULL if no such + * instruction was found, or it is blocked by a later use of "value". + * "addrof" will be set to a non-NULL value if the instruction just before + * the relative adjustment took the address of a local frame variable. + * This instruction is a candidate for being moved down to where the + * "load_relative" or "store_relative" occurs. + */ +static jit_insn_t previous_relative(jit_function_t func, jit_value_t value, + jit_insn_t *addrof) +{ + jit_insn_iter_t iter; + jit_insn_t insn; + jit_insn_t insn2; + jit_insn_t insn3; + + /* Clear "addrof" first */ + *addrof = 0; + + /* If the value is not temporary, then it isn't a candidate */ + if(!(value->is_temporary)) + { + return 0; + } + + /* Iterate back through the block looking for a suitable adjustment */ + jit_insn_iter_init_last(&iter, func->builder->current_block); + while((insn = jit_insn_iter_previous(&iter)) != 0) + { + if(insn->opcode == JIT_OP_ADD_RELATIVE && insn->dest == value) + { + /* See if the instruction just before the "add_relative" + is an "address_of" that is being used by the add */ + insn3 = jit_insn_iter_previous(&iter); + if(insn3) + { + jit_insn_iter_next(&iter); + if(insn3->opcode != JIT_OP_ADDRESS_OF || + insn3->dest != insn->value1 || + !(insn3->dest->is_temporary)) + { + insn3 = 0; + } + } + + /* Scan forwards to ensure that "insn->value1" is not + used anywhere in the instructions that follow */ + jit_insn_iter_next(&iter); + while((insn2 = jit_insn_iter_next(&iter)) != 0) + { + if(insn2->dest == insn->value1 || + insn2->value1 == insn->value1 || + insn2->value2 == insn->value1) + { + return 0; + } + if(insn3) + { + /* We may need to disable the "address_of" instruction + if any of its values are used further on */ + if(insn2->dest == insn3->dest || + insn2->value1 == insn3->dest || + insn2->value2 == insn3->dest || + insn2->dest == insn3->value1 || + insn2->value1 == insn3->value1 || + insn2->value2 == insn3->value1) + { + insn3 = 0; + } + } + } + if(insn3) + { + *addrof = insn3; + } + return insn; + } + if(insn->dest == value || insn->value1 == value || + insn->value2 == value) + { + /* This instruction uses "value" in some way, so it + blocks any previous "add_relative" instructions */ + return 0; + } + } + return 0; +} + +/*@ + * @deftypefun jit_value_t jit_insn_load_relative (jit_function_t func, jit_value_t value, jit_nint offset, jit_type_t type) + * Load a value of the specified @code{type} from the effective address + * @code{(value + offset)}, where @code{value} is a pointer. + * @end deftypefun +@*/ +jit_value_t jit_insn_load_relative + (jit_function_t func, jit_value_t value, + jit_nint offset, jit_type_t type) +{ + jit_insn_t insn; + jit_insn_t addrof; + if(!value) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = previous_relative(func, value, &addrof); + if(insn) + { + /* We have a previous "add_relative" instruction for this + pointer. Remove it from the instruction stream and + adjust the current offset accordingly */ + offset += jit_value_get_nint_constant(insn->value2); + value = insn->value1; + insn->opcode = JIT_OP_NOP; + insn->dest = 0; + insn->value1 = 0; + insn->value2 = 0; + if(addrof) + { + /* Shift the "address_of" instruction down too, to make + it easier for the code generator to handle field + accesses within local and global variables */ + value = jit_insn_address_of(func, addrof->value1); + if(!value) + { + return 0; + } + addrof->opcode = JIT_OP_NOP; + addrof->dest = 0; + addrof->value1 = 0; + addrof->value2 = 0; + } + } + return apply_binary + (func, _jit_load_opcode(JIT_OP_LOAD_RELATIVE_SBYTE, type, 0, 0), value, + jit_value_create_nint_constant(func, jit_type_nint, offset), type); +} + +/*@ + * @deftypefun int jit_insn_store_relative (jit_function_t func, jit_value_t dest, jit_nint offset, jit_value_t value) + * Store @code{value} at the effective address @code{(dest + offset)}, + * where @code{dest} is a pointer. + * @end deftypefun +@*/ +int jit_insn_store_relative + (jit_function_t func, jit_value_t dest, + jit_nint offset, jit_value_t value) +{ + jit_insn_t insn; + jit_insn_t addrof; + jit_value_t offset_value; + if(!dest || !value) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = previous_relative(func, dest, &addrof); + if(insn) + { + /* We have a previous "add_relative" instruction for this + pointer. Remove it from the instruction stream and + adjust the current offset accordingly */ + offset += jit_value_get_nint_constant(insn->value2); + dest = insn->value1; + insn->opcode = JIT_OP_NOP; + insn->dest = 0; + insn->value1 = 0; + insn->value2 = 0; + if(addrof) + { + /* Shift the "address_of" instruction down too, to make + it easier for the code generator to handle field + accesses within local and global variables */ + dest = jit_insn_address_of(func, addrof->value1); + if(!dest) + { + return 0; + } + addrof->opcode = JIT_OP_NOP; + addrof->dest = 0; + addrof->value1 = 0; + addrof->value2 = 0; + } + } + offset_value = jit_value_create_nint_constant(func, jit_type_nint, offset); + if(!offset_value) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, dest); + jit_value_ref(func, value); + insn->opcode = (short)_jit_store_opcode + (JIT_OP_STORE_RELATIVE_BYTE, 0, value->type); + insn->flags = JIT_INSN_DEST_IS_VALUE; + insn->dest = dest; + insn->value1 = value; + insn->value2 = offset_value; + return 1; +} + +/*@ + * @deftypefun jit_value_t jit_insn_add_relative (jit_function_t func, jit_value_t value, jit_nint offset) + * Add the constant @code{offset} to the specified pointer @code{value}. + * This is functionally identical to calling @code{jit_insn_add}, but + * the JIT can optimize the code better if it knows that the addition + * is being used to perform a relative adjustment on a pointer. + * In particular, multiple relative adjustments on the same pointer + * can be collapsed into a single adjustment. + * @end deftypefun +@*/ +jit_value_t jit_insn_add_relative + (jit_function_t func, jit_value_t value, jit_nint offset) +{ + jit_insn_t insn; + jit_insn_t addrof; + if(!value) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + insn = previous_relative(func, value, &addrof); + if(insn) + { + /* Back-patch the "add_relative" instruction to adjust the offset */ + insn->value2 = jit_value_create_nint_constant + (func, jit_type_nint, + jit_value_get_nint_constant(insn->value2) + offset); + return value; + } + else + { + /* Create a new "add_relative" instruction */ + return apply_binary(func, JIT_OP_ADD_RELATIVE, value, + jit_value_create_nint_constant + (func, jit_type_nint, offset), + jit_type_void_ptr); + } +} + +/*@ + * @deftypefun int jit_insn_check_null (jit_function_t func, jit_value_t value) + * Check @code{value} to see if it is NULL. If it is, then throw the + * built-in @code{JIT_RESULT_NULL_REFERENCE} exception. + * @end deftypefun +@*/ +int jit_insn_check_null(jit_function_t func, jit_value_t value) +{ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + func->builder->may_throw = 1; + return create_unary_note(func, JIT_OP_CHECK_NULL, value); +} + +int _jit_insn_check_is_redundant(const jit_insn_iter_t *iter) +{ + jit_insn_iter_t new_iter = *iter; + jit_insn_t insn; + jit_value_t value; + + /* Back up to find the "check_null" instruction of interest */ + insn = jit_insn_iter_previous(&new_iter); + value = insn->value1; + + /* The value must be temporary or local, and not volatile or addressable. + Otherwise the value could be vulnerable to aliasing side-effects that + could make it NULL again even after we have checked it */ + if(!(value->is_temporary) || !(value->is_local)) + { + return 0; + } + if(value->is_volatile || value->is_addressable) + { + return 0; + } + + /* Search back for a previous "check_null" instruction */ + while((insn = jit_insn_iter_previous(&new_iter)) != 0) + { + if(insn->opcode == JIT_OP_CHECK_NULL && insn->value1 == value) + { + /* This is the previous "check_null" that we were looking for */ + return 1; + } + if(insn->opcode >= JIT_OP_STORE_RELATIVE_BYTE && + insn->opcode <= JIT_OP_STORE_RELATIVE_STRUCT) + { + /* This stores to the memory referenced by the destination, + not to the destination itself, so it cannot affect "value" */ + continue; + } + if(insn->dest == value) + { + /* The value was used as a destination, so we must check */ + return 0; + } + } + + /* There was no previous "check_null" instruction for this value */ + return 0; +} + +/*@ + * @deftypefun jit_value_t jit_insn_add (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Add two values together and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_add + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const add_descr = { + JIT_OP_IADD, + JIT_OP_IADD, + JIT_OP_LADD, + JIT_OP_LADD, + JIT_OP_FADD, + JIT_OP_DADD, + JIT_OP_NFADD, + jit_intrinsic(jit_int_add, descr_i_ii), + jit_intrinsic(jit_uint_add, descr_I_II), + jit_intrinsic(jit_long_add, descr_l_ll), + jit_intrinsic(jit_ulong_add, descr_L_LL), + jit_intrinsic(jit_float32_add, descr_f_ff), + jit_intrinsic(jit_float64_add, descr_d_dd), + jit_intrinsic(jit_nfloat_add, descr_D_DD) + }; + return apply_arith(func, &add_descr, value1, value2, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_add_ovf (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Add two values together and return the result in a new temporary value. + * Throw an exception if overflow occurs. + * @end deftypefun +@*/ +jit_value_t jit_insn_add_ovf + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const add_ovf_descr = { + JIT_OP_IADD_OVF, + JIT_OP_IADD_OVF_UN, + JIT_OP_LADD_OVF, + JIT_OP_LADD_OVF_UN, + JIT_OP_FADD, + JIT_OP_DADD, + JIT_OP_NFADD, + jit_intrinsic(jit_int_add_ovf, descr_e_pi_ii), + jit_intrinsic(jit_uint_add_ovf, descr_e_pI_II), + jit_intrinsic(jit_long_add_ovf, descr_e_pl_ll), + jit_intrinsic(jit_ulong_add_ovf, descr_e_pL_LL), + jit_intrinsic(jit_float32_add, descr_f_ff), + jit_intrinsic(jit_float64_add, descr_d_dd), + jit_intrinsic(jit_nfloat_add, descr_D_DD) + }; + return apply_arith(func, &add_ovf_descr, value1, value2, 0, 0, 1); +} + +/*@ + * @deftypefun jit_value_t jit_insn_sub (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Subtract two values and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_sub + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const sub_descr = { + JIT_OP_ISUB, + JIT_OP_ISUB, + JIT_OP_LSUB, + JIT_OP_LSUB, + JIT_OP_FSUB, + JIT_OP_DSUB, + JIT_OP_NFSUB, + jit_intrinsic(jit_int_sub, descr_i_ii), + jit_intrinsic(jit_uint_sub, descr_I_II), + jit_intrinsic(jit_long_sub, descr_l_ll), + jit_intrinsic(jit_ulong_sub, descr_L_LL), + jit_intrinsic(jit_float32_sub, descr_f_ff), + jit_intrinsic(jit_float64_sub, descr_d_dd), + jit_intrinsic(jit_nfloat_sub, descr_D_DD) + }; + return apply_arith(func, &sub_descr, value1, value2, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_sub_ovf (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Subtract two values and return the result in a new temporary value. + * Throw an exception if overflow occurs. + * @end deftypefun +@*/ +jit_value_t jit_insn_sub_ovf + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const sub_ovf_descr = { + JIT_OP_ISUB_OVF, + JIT_OP_ISUB_OVF_UN, + JIT_OP_LSUB_OVF, + JIT_OP_LSUB_OVF_UN, + JIT_OP_FSUB, + JIT_OP_DSUB, + JIT_OP_NFSUB, + jit_intrinsic(jit_int_sub_ovf, descr_e_pi_ii), + jit_intrinsic(jit_uint_sub_ovf, descr_e_pI_II), + jit_intrinsic(jit_long_sub_ovf, descr_e_pl_ll), + jit_intrinsic(jit_ulong_sub_ovf, descr_e_pL_LL), + jit_intrinsic(jit_float32_sub, descr_f_ff), + jit_intrinsic(jit_float64_sub, descr_d_dd), + jit_intrinsic(jit_nfloat_sub, descr_D_DD) + }; + return apply_arith(func, &sub_ovf_descr, value1, value2, 0, 0, 1); +} + +/*@ + * @deftypefun jit_value_t jit_insn_mul (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Multiply two values and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_mul + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const mul_descr = { + JIT_OP_IMUL, + JIT_OP_IMUL, + JIT_OP_LMUL, + JIT_OP_LMUL, + JIT_OP_FMUL, + JIT_OP_DMUL, + JIT_OP_NFMUL, + jit_intrinsic(jit_int_mul, descr_i_ii), + jit_intrinsic(jit_uint_mul, descr_I_II), + jit_intrinsic(jit_long_mul, descr_l_ll), + jit_intrinsic(jit_ulong_mul, descr_L_LL), + jit_intrinsic(jit_float32_mul, descr_f_ff), + jit_intrinsic(jit_float64_mul, descr_d_dd), + jit_intrinsic(jit_nfloat_mul, descr_D_DD) + }; + return apply_arith(func, &mul_descr, value1, value2, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_mul_ovf (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Multiply two values and return the result in a new temporary value. + * Throw an exception if overflow occurs. + * @end deftypefun +@*/ +jit_value_t jit_insn_mul_ovf + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const mul_ovf_descr = { + JIT_OP_IMUL_OVF, + JIT_OP_IMUL_OVF_UN, + JIT_OP_LMUL_OVF, + JIT_OP_LMUL_OVF_UN, + JIT_OP_FMUL, + JIT_OP_DMUL, + JIT_OP_NFMUL, + jit_intrinsic(jit_int_mul_ovf, descr_e_pi_ii), + jit_intrinsic(jit_uint_mul_ovf, descr_e_pI_II), + jit_intrinsic(jit_long_mul_ovf, descr_e_pl_ll), + jit_intrinsic(jit_ulong_mul_ovf, descr_e_pL_LL), + jit_intrinsic(jit_float32_mul, descr_f_ff), + jit_intrinsic(jit_float64_mul, descr_d_dd), + jit_intrinsic(jit_nfloat_mul, descr_D_DD) + }; + return apply_arith(func, &mul_ovf_descr, value1, value2, 0, 0, 1); +} + +/*@ + * @deftypefun jit_value_t jit_insn_div (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Divide two values and return the quotient in a new temporary value. + * Throws an exception on division by zero or arithmetic error + * (an arithmetic error is one where the minimum possible signed + * integer value is divided by -1). + * @end deftypefun +@*/ +jit_value_t jit_insn_div + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const div_descr = { + JIT_OP_IDIV, + JIT_OP_IDIV_UN, + JIT_OP_LDIV, + JIT_OP_LDIV_UN, + JIT_OP_FDIV, + JIT_OP_DDIV, + JIT_OP_NFDIV, + jit_intrinsic(jit_int_div, descr_e_pi_ii), + jit_intrinsic(jit_uint_div, descr_e_pI_II), + jit_intrinsic(jit_long_div, descr_e_pl_ll), + jit_intrinsic(jit_ulong_div, descr_e_pL_LL), + jit_intrinsic(jit_float32_div, descr_f_ff), + jit_intrinsic(jit_float64_div, descr_d_dd), + jit_intrinsic(jit_nfloat_div, descr_D_DD) + }; + return apply_arith(func, &div_descr, value1, value2, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_rem (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Divide two values and return the remainder in a new temporary value. + * Throws an exception on division by zero or arithmetic error + * (an arithmetic error is one where the minimum possible signed + * integer value is divided by -1). + * @end deftypefun +@*/ +jit_value_t jit_insn_rem + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const rem_descr = { + JIT_OP_IREM, + JIT_OP_IREM_UN, + JIT_OP_LREM, + JIT_OP_LREM_UN, + JIT_OP_FREM, + JIT_OP_DREM, + JIT_OP_NFREM, + jit_intrinsic(jit_int_rem, descr_e_pi_ii), + jit_intrinsic(jit_uint_rem, descr_e_pI_II), + jit_intrinsic(jit_long_rem, descr_e_pl_ll), + jit_intrinsic(jit_ulong_rem, descr_e_pL_LL), + jit_intrinsic(jit_float32_rem, descr_f_ff), + jit_intrinsic(jit_float64_rem, descr_d_dd), + jit_intrinsic(jit_nfloat_rem, descr_D_DD) + }; + return apply_arith(func, &rem_descr, value1, value2, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_rem_ieee (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Divide two values and return the remainder in a new temporary value. + * Throws an exception on division by zero or arithmetic error + * (an arithmetic error is one where the minimum possible signed + * integer value is divided by -1). This function is identical to + * @code{jit_insn_rem}, except that it uses IEEE rules for computing + * the remainder of floating-point values. + * @end deftypefun +@*/ +jit_value_t jit_insn_rem_ieee + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const rem_ieee_descr = { + JIT_OP_IREM, + JIT_OP_IREM_UN, + JIT_OP_LREM, + JIT_OP_LREM_UN, + JIT_OP_FREM_IEEE, + JIT_OP_DREM_IEEE, + JIT_OP_NFREM_IEEE, + jit_intrinsic(jit_int_rem, descr_e_pi_ii), + jit_intrinsic(jit_uint_rem, descr_e_pI_II), + jit_intrinsic(jit_long_rem, descr_e_pl_ll), + jit_intrinsic(jit_ulong_rem, descr_e_pL_LL), + jit_intrinsic(jit_float32_ieee_rem, descr_f_ff), + jit_intrinsic(jit_float64_ieee_rem, descr_d_dd), + jit_intrinsic(jit_nfloat_ieee_rem, descr_D_DD) + }; + return apply_arith(func, &rem_ieee_descr, value1, value2, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_neg (jit_function_t func, jit_value_t value1) + * Negate a value and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_neg + (jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const neg_descr = { + JIT_OP_INEG, + JIT_OP_INEG, + JIT_OP_LNEG, + JIT_OP_LNEG, + JIT_OP_FNEG, + JIT_OP_DNEG, + JIT_OP_NFNEG, + jit_intrinsic(jit_int_neg, descr_i_i), + jit_intrinsic(jit_uint_neg, descr_I_I), + jit_intrinsic(jit_long_neg, descr_l_l), + jit_intrinsic(jit_ulong_neg, descr_L_L), + jit_intrinsic(jit_float32_neg, descr_f_f), + jit_intrinsic(jit_float64_neg, descr_d_d), + jit_intrinsic(jit_nfloat_neg, descr_D_D) + }; + return apply_unary_arith(func, &neg_descr, value1, 0, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_and (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Bitwise AND two values and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_and + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const and_descr = { + JIT_OP_IAND, + JIT_OP_IAND, + JIT_OP_LAND, + JIT_OP_LAND, + 0, 0, 0, + jit_intrinsic(jit_int_and, descr_i_ii), + jit_intrinsic(jit_uint_and, descr_I_II), + jit_intrinsic(jit_long_and, descr_l_ll), + jit_intrinsic(jit_ulong_and, descr_L_LL), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_arith(func, &and_descr, value1, value2, 1, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_or (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Bitwise OR two values and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_or + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const or_descr = { + JIT_OP_IOR, + JIT_OP_IOR, + JIT_OP_LOR, + JIT_OP_LOR, + 0, 0, 0, + jit_intrinsic(jit_int_or, descr_i_ii), + jit_intrinsic(jit_uint_or, descr_I_II), + jit_intrinsic(jit_long_or, descr_l_ll), + jit_intrinsic(jit_ulong_or, descr_L_LL), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_arith(func, &or_descr, value1, value2, 1, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_xor (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Bitwise XOR two values and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_xor + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const xor_descr = { + JIT_OP_IXOR, + JIT_OP_IXOR, + JIT_OP_LXOR, + JIT_OP_LXOR, + 0, 0, 0, + jit_intrinsic(jit_int_xor, descr_i_ii), + jit_intrinsic(jit_uint_xor, descr_I_II), + jit_intrinsic(jit_long_xor, descr_l_ll), + jit_intrinsic(jit_ulong_xor, descr_L_LL), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_arith(func, &xor_descr, value1, value2, 1, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_not (jit_function_t func, jit_value_t value1) + * Bitwise NOT a value and return the result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_not + (jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const not_descr = { + JIT_OP_INOT, + JIT_OP_INOT, + JIT_OP_LNOT, + JIT_OP_LNOT, + 0, 0, 0, + jit_intrinsic(jit_int_not, descr_i_i), + jit_intrinsic(jit_uint_not, descr_I_I), + jit_intrinsic(jit_long_not, descr_l_l), + jit_intrinsic(jit_ulong_not, descr_L_L), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_unary_arith(func, ¬_descr, value1, 1, 0, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_shl (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Perform a bitwise left shift on two values and return the + * result in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_shl + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const shl_descr = { + JIT_OP_ISHL, + JIT_OP_ISHL, + JIT_OP_LSHL, + JIT_OP_LSHL, + 0, 0, 0, + jit_intrinsic(jit_int_shl, descr_i_iI), + jit_intrinsic(jit_uint_shl, descr_I_II), + jit_intrinsic(jit_long_shl, descr_l_lI), + jit_intrinsic(jit_ulong_shl, descr_L_LI), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_shift(func, &shl_descr, value1, value2); +} + +/*@ + * @deftypefun jit_value_t jit_insn_shr (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Perform a bitwise right shift on two values and return the + * result in a new temporary value. This performs a signed shift + * on signed operators, and an unsigned shift on unsigned operands. + * @end deftypefun +@*/ +jit_value_t jit_insn_shr + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const shr_descr = { + JIT_OP_ISHR, + JIT_OP_ISHR_UN, + JIT_OP_LSHR, + JIT_OP_LSHR_UN, + 0, 0, 0, + jit_intrinsic(jit_int_shr, descr_i_iI), + jit_intrinsic(jit_uint_shr, descr_I_II), + jit_intrinsic(jit_long_shr, descr_l_lI), + jit_intrinsic(jit_ulong_shr, descr_L_LI), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_shift(func, &shr_descr, value1, value2); +} + +/*@ + * @deftypefun jit_value_t jit_insn_ushr (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Perform a bitwise right shift on two values and return the + * result in a new temporary value. This performs an unsigned + * shift on both signed and unsigned operands. + * @end deftypefun +@*/ +jit_value_t jit_insn_ushr + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const ushr_descr = { + JIT_OP_ISHR_UN, + JIT_OP_ISHR_UN, + JIT_OP_LSHR_UN, + JIT_OP_LSHR_UN, + 0, 0, 0, + jit_intrinsic(jit_uint_shr, descr_I_II), + jit_intrinsic(jit_uint_shr, descr_I_II), + jit_intrinsic(jit_ulong_shr, descr_L_LI), + jit_intrinsic(jit_ulong_shr, descr_L_LI), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_shift(func, &ushr_descr, value1, value2); +} + +/*@ + * @deftypefun jit_value_t jit_insn_sshr (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Perform a bitwise right shift on two values and return the + * result in a new temporary value. This performs an signed + * shift on both signed and unsigned operands. + * @end deftypefun +@*/ +jit_value_t jit_insn_sshr + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const sshr_descr = { + JIT_OP_ISHR, + JIT_OP_ISHR, + JIT_OP_LSHR, + JIT_OP_LSHR, + 0, 0, 0, + jit_intrinsic(jit_int_shr, descr_i_iI), + jit_intrinsic(jit_int_shr, descr_i_iI), + jit_intrinsic(jit_long_shr, descr_l_lI), + jit_intrinsic(jit_long_shr, descr_l_lI), + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic + }; + return apply_shift(func, &sshr_descr, value1, value2); +} + +/*@ + * @deftypefun jit_value_t jit_insn_eq (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values for equality and return the result + * in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_eq + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const eq_descr = { + JIT_OP_IEQ, + JIT_OP_IEQ, + JIT_OP_LEQ, + JIT_OP_LEQ, + JIT_OP_FEQ, + JIT_OP_DEQ, + JIT_OP_NFEQ, + jit_intrinsic(jit_int_eq, descr_i_ii), + jit_intrinsic(jit_uint_eq, descr_i_II), + jit_intrinsic(jit_long_eq, descr_i_ll), + jit_intrinsic(jit_ulong_eq, descr_i_LL), + jit_intrinsic(jit_float32_eq, descr_i_ff), + jit_intrinsic(jit_float64_eq, descr_i_dd), + jit_intrinsic(jit_nfloat_eq, descr_i_DD) + }; + return apply_compare(func, &eq_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_ne (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values for inequality and return the result + * in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_ne + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const ne_descr = { + JIT_OP_INE, + JIT_OP_INE, + JIT_OP_LNE, + JIT_OP_LNE, + JIT_OP_FNE, + JIT_OP_DNE, + JIT_OP_NFNE, + jit_intrinsic(jit_int_ne, descr_i_ii), + jit_intrinsic(jit_uint_ne, descr_i_II), + jit_intrinsic(jit_long_ne, descr_i_ll), + jit_intrinsic(jit_ulong_ne, descr_i_LL), + jit_intrinsic(jit_float32_ne, descr_i_ff), + jit_intrinsic(jit_float64_ne, descr_i_dd), + jit_intrinsic(jit_nfloat_ne, descr_i_DD) + }; + return apply_compare(func, &ne_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_lt (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values for less than and return the result + * in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_lt + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const lt_descr = { + JIT_OP_ILT, + JIT_OP_ILT_UN, + JIT_OP_LLT, + JIT_OP_LLT_UN, + JIT_OP_FLT, + JIT_OP_DLT, + JIT_OP_NFLT, + jit_intrinsic(jit_int_lt, descr_i_ii), + jit_intrinsic(jit_uint_lt, descr_i_II), + jit_intrinsic(jit_long_lt, descr_i_ll), + jit_intrinsic(jit_ulong_lt, descr_i_LL), + jit_intrinsic(jit_float32_lt, descr_i_ff), + jit_intrinsic(jit_float64_lt, descr_i_dd), + jit_intrinsic(jit_nfloat_lt, descr_i_DD) + }; + return apply_compare(func, <_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_le (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values for less than or equal and return the result + * in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_le + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const le_descr = { + JIT_OP_ILE, + JIT_OP_ILE_UN, + JIT_OP_LLE, + JIT_OP_LLE_UN, + JIT_OP_FLE, + JIT_OP_DLE, + JIT_OP_NFLE, + jit_intrinsic(jit_int_le, descr_i_ii), + jit_intrinsic(jit_uint_le, descr_i_II), + jit_intrinsic(jit_long_le, descr_i_ll), + jit_intrinsic(jit_ulong_le, descr_i_LL), + jit_intrinsic(jit_float32_le, descr_i_ff), + jit_intrinsic(jit_float64_le, descr_i_dd), + jit_intrinsic(jit_nfloat_le, descr_i_DD) + }; + return apply_compare(func, &le_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_gt (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values for greater than and return the result + * in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_gt + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const gt_descr = { + JIT_OP_IGT, + JIT_OP_IGT_UN, + JIT_OP_LGT, + JIT_OP_LGT_UN, + JIT_OP_FGT, + JIT_OP_DGT, + JIT_OP_NFGT, + jit_intrinsic(jit_int_gt, descr_i_ii), + jit_intrinsic(jit_uint_gt, descr_i_II), + jit_intrinsic(jit_long_gt, descr_i_ll), + jit_intrinsic(jit_ulong_gt, descr_i_LL), + jit_intrinsic(jit_float32_gt, descr_i_ff), + jit_intrinsic(jit_float64_gt, descr_i_dd), + jit_intrinsic(jit_nfloat_gt, descr_i_DD) + }; + return apply_compare(func, >_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_ge (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values for greater than or equal and return the result + * in a new temporary value. + * @end deftypefun +@*/ +jit_value_t jit_insn_ge + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const ge_descr = { + JIT_OP_IGE, + JIT_OP_IGE_UN, + JIT_OP_LGE, + JIT_OP_LGE_UN, + JIT_OP_FGE, + JIT_OP_DGE, + JIT_OP_NFGE, + jit_intrinsic(jit_int_ge, descr_i_ii), + jit_intrinsic(jit_uint_ge, descr_i_II), + jit_intrinsic(jit_long_ge, descr_i_ll), + jit_intrinsic(jit_ulong_ge, descr_i_LL), + jit_intrinsic(jit_float32_ge, descr_i_ff), + jit_intrinsic(jit_float64_ge, descr_i_dd), + jit_intrinsic(jit_nfloat_ge, descr_i_DD) + }; + return apply_compare(func, &ge_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_cmpl (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values, and return a -1, 0, or 1 result. If either + * value is "not a number", then -1 is returned. + * @end deftypefun +@*/ +jit_value_t jit_insn_cmpl + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const cmpl_descr = { + JIT_OP_ICMP, + JIT_OP_ICMP_UN, + JIT_OP_LCMP, + JIT_OP_LCMP_UN, + JIT_OP_FCMPL, + JIT_OP_DCMPL, + JIT_OP_NFCMPL, + jit_intrinsic(jit_int_cmp, descr_i_ii), + jit_intrinsic(jit_uint_cmp, descr_i_II), + jit_intrinsic(jit_long_cmp, descr_i_ll), + jit_intrinsic(jit_ulong_cmp, descr_i_LL), + jit_intrinsic(jit_float32_cmpl, descr_i_ff), + jit_intrinsic(jit_float64_cmpl, descr_i_dd), + jit_intrinsic(jit_nfloat_cmpl, descr_i_DD) + }; + return apply_compare(func, &cmpl_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_cmpg (jit_function_t func, jit_value_t value1, jit_value_t value2) + * Compare two values, and return a -1, 0, or 1 result. If either + * value is "not a number", then 1 is returned. + * @end deftypefun +@*/ +jit_value_t jit_insn_cmpg + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const cmpg_descr = { + JIT_OP_ICMP, + JIT_OP_ICMP_UN, + JIT_OP_LCMP, + JIT_OP_LCMP_UN, + JIT_OP_FCMPG, + JIT_OP_DCMPG, + JIT_OP_NFCMPG, + jit_intrinsic(jit_int_cmp, descr_i_ii), + jit_intrinsic(jit_uint_cmp, descr_i_II), + jit_intrinsic(jit_long_cmp, descr_i_ll), + jit_intrinsic(jit_ulong_cmp, descr_i_LL), + jit_intrinsic(jit_float32_cmpg, descr_i_ff), + jit_intrinsic(jit_float64_cmpg, descr_i_dd), + jit_intrinsic(jit_nfloat_cmpg, descr_i_DD) + }; + return apply_compare(func, &cmpg_descr, value1, value2, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_to_bool (jit_function_t func, jit_value_t value1) + * Convert a value into a boolean 0 or 1 result of type @code{jit_type_int}. + * @end deftypefun +@*/ +jit_value_t jit_insn_to_bool(jit_function_t func, jit_value_t value1) +{ + jit_type_t type; + jit_block_t block; + jit_insn_t last; + int opcode; + + /* Bail out if the parameters are invalid */ + if(!value1) + { + return 0; + } + + /* Ensure that we have a builder for this function */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* If the previous instruction was a comparison, then there is + nothing that we need to do to make the value boolean */ + block = func->builder->current_block; + last = _jit_block_get_last(block); + if(value1->is_temporary && last && last->dest == value1) + { + opcode = last->opcode; + if(opcode >= JIT_OP_IEQ && opcode <= JIT_OP_NFGE_INV) + { + return value1; + } + } + + /* Perform a comparison to determine if the value is non-zero */ + type = jit_type_promote_int(jit_type_normalize(value1->type)); + if(type == jit_type_int || type == jit_type_uint) + { + return jit_insn_ne + (func, value1, + jit_value_create_nint_constant(func, jit_type_int, 0)); + } + else if(type == jit_type_long || type == jit_type_ulong) + { + return jit_insn_ne + (func, value1, + jit_value_create_long_constant(func, jit_type_long, 0)); + } + else if(type == jit_type_float32) + { + return jit_insn_ne + (func, value1, + jit_value_create_float32_constant + (func, jit_type_float32, (jit_float32)0.0)); + } + else if(type == jit_type_float64) + { + return jit_insn_ne + (func, value1, + jit_value_create_float64_constant + (func, jit_type_float64, (jit_float64)0.0)); + } + else + { + return jit_insn_ne + (func, value1, + jit_value_create_nfloat_constant + (func, jit_type_nfloat, (jit_nfloat)0.0)); + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_to_not_bool (jit_function_t func, jit_value_t value1) + * Convert a value into a boolean 1 or 0 result of type @code{jit_type_int} + * (i.e. the inverse of @code{jit_insn_to_bool}). + * @end deftypefun +@*/ +jit_value_t jit_insn_to_not_bool(jit_function_t func, jit_value_t value1) +{ + jit_type_t type; + jit_block_t block; + jit_insn_t last; + int opcode; + + /* Bail out if the parameters are invalid */ + if(!value1) + { + return 0; + } + + /* Ensure that we have a builder for this function */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* If the previous instruction was a comparison, then all + we have to do is invert the comparison opcode */ + block = func->builder->current_block; + last = _jit_block_get_last(block); + if(value1->is_temporary && last && last->dest == value1) + { + opcode = last->opcode; + if(opcode >= JIT_OP_IEQ && opcode <= JIT_OP_NFGE_INV) + { + switch(opcode) + { + case JIT_OP_IEQ: opcode = JIT_OP_INE; break; + case JIT_OP_INE: opcode = JIT_OP_IEQ; break; + case JIT_OP_ILT: opcode = JIT_OP_IGE; break; + case JIT_OP_ILT_UN: opcode = JIT_OP_IGE_UN; break; + case JIT_OP_ILE: opcode = JIT_OP_IGT; break; + case JIT_OP_ILE_UN: opcode = JIT_OP_IGT_UN; break; + case JIT_OP_IGT: opcode = JIT_OP_ILE; break; + case JIT_OP_IGT_UN: opcode = JIT_OP_ILE_UN; break; + case JIT_OP_IGE: opcode = JIT_OP_ILT; break; + case JIT_OP_IGE_UN: opcode = JIT_OP_ILT_UN; break; + case JIT_OP_LEQ: opcode = JIT_OP_LNE; break; + case JIT_OP_LNE: opcode = JIT_OP_LEQ; break; + case JIT_OP_LLT: opcode = JIT_OP_LGE; break; + case JIT_OP_LLT_UN: opcode = JIT_OP_LGE_UN; break; + case JIT_OP_LLE: opcode = JIT_OP_LGT; break; + case JIT_OP_LLE_UN: opcode = JIT_OP_LGT_UN; break; + case JIT_OP_LGT: opcode = JIT_OP_LLE; break; + case JIT_OP_LGT_UN: opcode = JIT_OP_LLE_UN; break; + case JIT_OP_LGE: opcode = JIT_OP_LLT; break; + case JIT_OP_LGE_UN: opcode = JIT_OP_LLT_UN; break; + case JIT_OP_FEQ: opcode = JIT_OP_FNE_INV; break; + case JIT_OP_FNE: opcode = JIT_OP_FEQ_INV; break; + case JIT_OP_FLT: opcode = JIT_OP_FGE_INV; break; + case JIT_OP_FLE: opcode = JIT_OP_FGT_INV; break; + case JIT_OP_FGT: opcode = JIT_OP_FLE_INV; break; + case JIT_OP_FGE: opcode = JIT_OP_FLT_INV; break; + case JIT_OP_FEQ_INV: opcode = JIT_OP_FNE; break; + case JIT_OP_FNE_INV: opcode = JIT_OP_FEQ; break; + case JIT_OP_FLT_INV: opcode = JIT_OP_FGE; break; + case JIT_OP_FLE_INV: opcode = JIT_OP_FGT; break; + case JIT_OP_FGT_INV: opcode = JIT_OP_FLE; break; + case JIT_OP_FGE_INV: opcode = JIT_OP_FLT; break; + case JIT_OP_DEQ: opcode = JIT_OP_DNE_INV; break; + case JIT_OP_DNE: opcode = JIT_OP_DEQ_INV; break; + case JIT_OP_DLT: opcode = JIT_OP_DGE_INV; break; + case JIT_OP_DLE: opcode = JIT_OP_DGT_INV; break; + case JIT_OP_DGT: opcode = JIT_OP_DLE_INV; break; + case JIT_OP_DGE: opcode = JIT_OP_DLT_INV; break; + case JIT_OP_DEQ_INV: opcode = JIT_OP_DNE; break; + case JIT_OP_DNE_INV: opcode = JIT_OP_DEQ; break; + case JIT_OP_DLT_INV: opcode = JIT_OP_DGE; break; + case JIT_OP_DLE_INV: opcode = JIT_OP_DGT; break; + case JIT_OP_DGT_INV: opcode = JIT_OP_DLE; break; + case JIT_OP_DGE_INV: opcode = JIT_OP_DLT; break; + case JIT_OP_NFEQ: opcode = JIT_OP_NFNE_INV; break; + case JIT_OP_NFNE: opcode = JIT_OP_NFEQ_INV; break; + case JIT_OP_NFLT: opcode = JIT_OP_NFGE_INV; break; + case JIT_OP_NFLE: opcode = JIT_OP_NFGT_INV; break; + case JIT_OP_NFGT: opcode = JIT_OP_NFLE_INV; break; + case JIT_OP_NFGE: opcode = JIT_OP_NFLT_INV; break; + case JIT_OP_NFEQ_INV: opcode = JIT_OP_NFNE; break; + case JIT_OP_NFNE_INV: opcode = JIT_OP_NFEQ; break; + case JIT_OP_NFLT_INV: opcode = JIT_OP_NFGE; break; + case JIT_OP_NFLE_INV: opcode = JIT_OP_NFGT; break; + case JIT_OP_NFGT_INV: opcode = JIT_OP_NFLE; break; + case JIT_OP_NFGE_INV: opcode = JIT_OP_NFLT; break; + } + last->opcode = (short)opcode; + return value1; + } + } + + /* Perform a comparison to determine if the value is zero */ + type = jit_type_promote_int(jit_type_normalize(value1->type)); + if(type == jit_type_int || type == jit_type_uint) + { + return jit_insn_eq + (func, value1, + jit_value_create_nint_constant(func, jit_type_int, 0)); + } + else if(type == jit_type_long || type == jit_type_ulong) + { + return jit_insn_eq + (func, value1, + jit_value_create_long_constant(func, jit_type_long, 0)); + } + else if(type == jit_type_float32) + { + return jit_insn_eq + (func, value1, + jit_value_create_float32_constant + (func, jit_type_float32, (jit_float32)0.0)); + } + else if(type == jit_type_float64) + { + return jit_insn_eq + (func, value1, + jit_value_create_float64_constant + (func, jit_type_float64, (jit_float64)0.0)); + } + else + { + return jit_insn_eq + (func, value1, + jit_value_create_nfloat_constant + (func, jit_type_nfloat, (jit_nfloat)0.0)); + } +} + +/*@ + * @deftypefun jit_value_t jit_insn_acos (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_asin (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_atan (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_atan2 (jit_function_t func, jit_value_t value1, jit_value_t value2) + * @deftypefunx jit_value_t jit_insn_ceil (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_cos (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_cosh (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_exp (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_floor (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_log (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_log10 (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_pow (jit_function_t func, jit_value_t value1, jit_value_t value2) + * @deftypefunx jit_value_t jit_insn_rint (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_round (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_sin (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_sinh (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_sqrt (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_tan (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_tanh (jit_function_t func, jit_value_t value1) + * Apply a mathematical function to floating-point arguments. + * @end deftypefun +@*/ +jit_value_t jit_insn_acos(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const acos_descr = { + 0, 0, 0, 0, + JIT_OP_FACOS, + JIT_OP_DACOS, + JIT_OP_NFACOS, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_acos, descr_f_f), + jit_intrinsic(jit_float64_acos, descr_d_d), + jit_intrinsic(jit_nfloat_acos, descr_D_D) + }; + return apply_unary_arith(func, &acos_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_asin(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const asin_descr = { + 0, 0, 0, 0, + JIT_OP_FASIN, + JIT_OP_DASIN, + JIT_OP_NFASIN, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_asin, descr_f_f), + jit_intrinsic(jit_float64_asin, descr_d_d), + jit_intrinsic(jit_nfloat_asin, descr_D_D) + }; + return apply_unary_arith(func, &asin_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_atan(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const atan_descr = { + 0, 0, 0, 0, + JIT_OP_FATAN, + JIT_OP_DATAN, + JIT_OP_NFATAN, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_atan, descr_f_f), + jit_intrinsic(jit_float64_atan, descr_d_d), + jit_intrinsic(jit_nfloat_atan, descr_D_D) + }; + return apply_unary_arith(func, &atan_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_atan2 + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const atan2_descr = { + 0, 0, 0, 0, + JIT_OP_FATAN2, + JIT_OP_DATAN2, + JIT_OP_NFATAN2, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_atan2, descr_f_ff), + jit_intrinsic(jit_float64_atan2, descr_d_dd), + jit_intrinsic(jit_nfloat_atan2, descr_D_DD) + }; + return apply_arith(func, &atan2_descr, value1, value2, 0, 1, 0); +} + +jit_value_t jit_insn_ceil(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const ceil_descr = { + 0, 0, 0, 0, + JIT_OP_FCEIL, + JIT_OP_DCEIL, + JIT_OP_NFCEIL, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_ceil, descr_f_f), + jit_intrinsic(jit_float64_ceil, descr_d_d), + jit_intrinsic(jit_nfloat_ceil, descr_D_D) + }; + return apply_unary_arith(func, &ceil_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_cos(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const cos_descr = { + 0, 0, 0, 0, + JIT_OP_FCOS, + JIT_OP_DCOS, + JIT_OP_NFCOS, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_cos, descr_f_f), + jit_intrinsic(jit_float64_cos, descr_d_d), + jit_intrinsic(jit_nfloat_cos, descr_D_D) + }; + return apply_unary_arith(func, &cos_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_cosh(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const cosh_descr = { + 0, 0, 0, 0, + JIT_OP_FCOSH, + JIT_OP_DCOSH, + JIT_OP_NFCOSH, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_cosh, descr_f_f), + jit_intrinsic(jit_float64_cosh, descr_d_d), + jit_intrinsic(jit_nfloat_cosh, descr_D_D) + }; + return apply_unary_arith(func, &cosh_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_exp(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const exp_descr = { + 0, 0, 0, 0, + JIT_OP_FEXP, + JIT_OP_DEXP, + JIT_OP_NFEXP, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_exp, descr_f_f), + jit_intrinsic(jit_float64_exp, descr_d_d), + jit_intrinsic(jit_nfloat_exp, descr_D_D) + }; + return apply_unary_arith(func, &exp_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_floor(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const floor_descr = { + 0, 0, 0, 0, + JIT_OP_FFLOOR, + JIT_OP_DFLOOR, + JIT_OP_NFFLOOR, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_floor, descr_f_f), + jit_intrinsic(jit_float64_floor, descr_d_d), + jit_intrinsic(jit_nfloat_floor, descr_D_D) + }; + return apply_unary_arith(func, &floor_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_log(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const log_descr = { + 0, 0, 0, 0, + JIT_OP_FLOG, + JIT_OP_DLOG, + JIT_OP_NFLOG, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_log, descr_f_f), + jit_intrinsic(jit_float64_log, descr_d_d), + jit_intrinsic(jit_nfloat_log, descr_D_D) + }; + return apply_unary_arith(func, &log_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_log10(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const log10_descr = { + 0, 0, 0, 0, + JIT_OP_FLOG10, + JIT_OP_DLOG10, + JIT_OP_NFLOG10, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_log10, descr_f_f), + jit_intrinsic(jit_float64_log10, descr_d_d), + jit_intrinsic(jit_nfloat_log10, descr_D_D) + }; + return apply_unary_arith(func, &log10_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_pow + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const pow_descr = { + 0, 0, 0, 0, + JIT_OP_FPOW, + JIT_OP_DPOW, + JIT_OP_NFPOW, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_pow, descr_f_ff), + jit_intrinsic(jit_float64_pow, descr_d_dd), + jit_intrinsic(jit_nfloat_pow, descr_D_DD) + }; + return apply_arith(func, &pow_descr, value1, value2, 0, 1, 0); +} + +jit_value_t jit_insn_rint(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const rint_descr = { + 0, 0, 0, 0, + JIT_OP_FRINT, + JIT_OP_DRINT, + JIT_OP_NFRINT, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_rint, descr_f_f), + jit_intrinsic(jit_float64_rint, descr_d_d), + jit_intrinsic(jit_nfloat_rint, descr_D_D) + }; + return apply_unary_arith(func, &rint_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_round(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const round_descr = { + 0, 0, 0, 0, + JIT_OP_FROUND, + JIT_OP_DROUND, + JIT_OP_NFROUND, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_round, descr_f_f), + jit_intrinsic(jit_float64_round, descr_d_d), + jit_intrinsic(jit_nfloat_round, descr_D_D) + }; + return apply_unary_arith(func, &round_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_sin(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const sin_descr = { + 0, 0, 0, 0, + JIT_OP_FSIN, + JIT_OP_DSIN, + JIT_OP_NFSIN, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_sin, descr_f_f), + jit_intrinsic(jit_float64_sin, descr_d_d), + jit_intrinsic(jit_nfloat_sin, descr_D_D) + }; + return apply_unary_arith(func, &sin_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_sinh(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const sinh_descr = { + 0, 0, 0, 0, + JIT_OP_FSINH, + JIT_OP_DSINH, + JIT_OP_NFSINH, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_sinh, descr_f_f), + jit_intrinsic(jit_float64_sinh, descr_d_d), + jit_intrinsic(jit_nfloat_sinh, descr_D_D) + }; + return apply_unary_arith(func, &sinh_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_sqrt(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const sqrt_descr = { + 0, 0, 0, 0, + JIT_OP_FSQRT, + JIT_OP_DSQRT, + JIT_OP_NFSQRT, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_sqrt, descr_f_f), + jit_intrinsic(jit_float64_sqrt, descr_d_d), + jit_intrinsic(jit_nfloat_sqrt, descr_D_D) + }; + return apply_unary_arith(func, &sqrt_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_tan(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const tan_descr = { + 0, 0, 0, 0, + JIT_OP_FTAN, + JIT_OP_DTAN, + JIT_OP_NFTAN, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_tan, descr_f_f), + jit_intrinsic(jit_float64_tan, descr_d_d), + jit_intrinsic(jit_nfloat_tan, descr_D_D) + }; + return apply_unary_arith(func, &tan_descr, value1, 0, 1, 0); +} + +jit_value_t jit_insn_tanh(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const tanh_descr = { + 0, 0, 0, 0, + JIT_OP_FTANH, + JIT_OP_DTANH, + JIT_OP_NFTANH, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_tanh, descr_f_f), + jit_intrinsic(jit_float64_tanh, descr_d_d), + jit_intrinsic(jit_nfloat_tanh, descr_D_D) + }; + return apply_unary_arith(func, &tanh_descr, value1, 0, 1, 0); +} + +/*@ + * @deftypefun jit_value_t jit_insn_is_nan (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_is_finite (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_is_inf (jit_function_t func, jit_value_t value1) + * Test a floating point value for not a number, finite, or infinity. + * @end deftypefun +@*/ +jit_value_t jit_insn_is_nan(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const is_nan_descr = { + 0, 0, 0, 0, + JIT_OP_IS_FNAN, + JIT_OP_IS_DNAN, + JIT_OP_IS_NFNAN, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_is_nan, descr_i_f), + jit_intrinsic(jit_float64_is_nan, descr_i_d), + jit_intrinsic(jit_nfloat_is_nan, descr_i_D) + }; + return apply_test(func, &is_nan_descr, value1, 1); +} + +jit_value_t jit_insn_is_finite(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const is_finite_descr = { + 0, 0, 0, 0, + JIT_OP_IS_FFINITE, + JIT_OP_IS_DFINITE, + JIT_OP_IS_NFFINITE, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_is_finite, descr_i_f), + jit_intrinsic(jit_float64_is_finite, descr_i_d), + jit_intrinsic(jit_nfloat_is_finite, descr_i_D) + }; + return apply_test(func, &is_finite_descr, value1, 1); +} + +jit_value_t jit_insn_is_inf(jit_function_t func, jit_value_t value1) +{ + static jit_opcode_descr const is_inf_descr = { + 0, 0, 0, 0, + JIT_OP_IS_FINF, + JIT_OP_IS_DINF, + JIT_OP_IS_NFINF, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_no_intrinsic, + jit_intrinsic(jit_float32_is_inf, descr_i_f), + jit_intrinsic(jit_float64_is_inf, descr_i_d), + jit_intrinsic(jit_nfloat_is_inf, descr_i_D) + }; + return apply_test(func, &is_inf_descr, value1, 1); +} + +/*@ + * @deftypefun jit_value_t jit_insn_abs (jit_function_t func, jit_value_t value1) + * @deftypefunx jit_value_t jit_insn_min (jit_function_t func, jit_value_t value1, jit_value_t value2) + * @deftypefunx jit_value_t jit_insn_max (jit_function_t func, jit_value_t value1, jit_value_t value2) + * @deftypefunx jit_value_t jit_insn_sign (jit_function_t func, jit_value_t value1) + * Calculate the absolute value, minimum, maximum, or sign of the + * specified values. + * @end deftypefun +@*/ +jit_value_t jit_insn_abs(jit_function_t func, jit_value_t value1) +{ + int oper; + void *intrinsic; + const char *name; + jit_type_t result_type; + const jit_intrinsic_descr_t *descr; + if(!value1) + { + return 0; + } + result_type = common_binary(value1->type, value1->type, 0, 0); + if(result_type == jit_type_int) + { + oper = JIT_OP_IABS; + intrinsic = (void *)jit_int_abs; + name = "jit_int_abs"; + descr = &descr_i_i; + } + else if(result_type == jit_type_uint) + { + oper = 0; + intrinsic = (void *)0; + name = 0; + descr = 0; + } + else if(result_type == jit_type_long) + { + oper = JIT_OP_LABS; + intrinsic = (void *)jit_long_abs; + name = "jit_long_abs"; + descr = &descr_l_l; + } + else if(result_type == jit_type_ulong) + { + oper = 0; + intrinsic = (void *)0; + name = 0; + descr = 0; + } + else if(result_type == jit_type_float32) + { + oper = JIT_OP_FABS; + intrinsic = (void *)jit_float32_abs; + name = "jit_float32_abs"; + descr = &descr_f_f; + } + else if(result_type == jit_type_float64) + { + oper = JIT_OP_DABS; + intrinsic = (void *)jit_float64_abs; + name = "jit_float64_abs"; + descr = &descr_d_d; + } + else + { + oper = JIT_OP_NFABS; + intrinsic = (void *)jit_nfloat_abs; + name = "jit_nfloat_abs"; + descr = &descr_D_D; + } + value1 = jit_insn_convert(func, value1, result_type, 0); + if(!oper) + { + /* Absolute value of an unsigned value */ + return value1; + } + if(_jit_opcode_is_supported(oper)) + { + return apply_unary(func, oper, value1, result_type); + } + else + { + return jit_insn_call_intrinsic + (func, name, intrinsic, descr, value1, 0); + } +} + +jit_value_t jit_insn_min + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const min_descr = { + JIT_OP_IMIN, + JIT_OP_IMIN_UN, + JIT_OP_LMIN, + JIT_OP_LMIN_UN, + JIT_OP_FMIN, + JIT_OP_DMIN, + JIT_OP_NFMIN, + jit_intrinsic(jit_int_min, descr_i_ii), + jit_intrinsic(jit_uint_min, descr_I_II), + jit_intrinsic(jit_long_min, descr_l_ll), + jit_intrinsic(jit_ulong_min, descr_L_LL), + jit_intrinsic(jit_float32_min, descr_f_ff), + jit_intrinsic(jit_float64_min, descr_d_dd), + jit_intrinsic(jit_nfloat_min, descr_D_DD) + }; + return apply_arith(func, &min_descr, value1, value2, 0, 0, 0); +} + +jit_value_t jit_insn_max + (jit_function_t func, jit_value_t value1, jit_value_t value2) +{ + static jit_opcode_descr const max_descr = { + JIT_OP_IMAX, + JIT_OP_IMAX_UN, + JIT_OP_LMAX, + JIT_OP_LMAX_UN, + JIT_OP_FMAX, + JIT_OP_DMAX, + JIT_OP_NFMAX, + jit_intrinsic(jit_int_max, descr_i_ii), + jit_intrinsic(jit_uint_max, descr_I_II), + jit_intrinsic(jit_long_max, descr_l_ll), + jit_intrinsic(jit_ulong_max, descr_L_LL), + jit_intrinsic(jit_float32_max, descr_f_ff), + jit_intrinsic(jit_float64_max, descr_d_dd), + jit_intrinsic(jit_nfloat_max, descr_D_DD) + }; + return apply_arith(func, &max_descr, value1, value2, 0, 0, 0); +} + +jit_value_t jit_insn_sign(jit_function_t func, jit_value_t value1) +{ + int oper; + void *intrinsic; + const char *name; + jit_type_t result_type; + const jit_intrinsic_descr_t *descr; + if(!value1) + { + return 0; + } + result_type = common_binary(value1->type, value1->type, 0, 0); + if(result_type == jit_type_int) + { + oper = JIT_OP_ISIGN; + intrinsic = (void *)jit_int_sign; + name = "jit_int_sign"; + descr = &descr_i_i; + } + else if(result_type == jit_type_uint) + { + return jit_insn_ne + (func, value1, + jit_value_create_nint_constant(func, jit_type_uint, 0)); + } + else if(result_type == jit_type_long) + { + oper = JIT_OP_LSIGN; + intrinsic = (void *)jit_long_sign; + name = "jit_long_sign"; + descr = &descr_i_l; + } + else if(result_type == jit_type_ulong) + { + return jit_insn_ne + (func, value1, + jit_value_create_long_constant(func, jit_type_ulong, 0)); + } + else if(result_type == jit_type_float32) + { + oper = JIT_OP_FSIGN; + intrinsic = (void *)jit_float32_sign; + name = "jit_float32_sign"; + descr = &descr_i_f; + } + else if(result_type == jit_type_float64) + { + oper = JIT_OP_DSIGN; + intrinsic = (void *)jit_float64_sign; + name = "jit_float64_sign"; + descr = &descr_i_d; + } + else + { + oper = JIT_OP_NFSIGN; + intrinsic = (void *)jit_nfloat_sign; + name = "jit_nfloat_sign"; + descr = &descr_i_D; + } + value1 = jit_insn_convert(func, value1, result_type, 0); + if(_jit_opcode_is_supported(oper)) + { + return apply_unary(func, oper, value1, result_type); + } + else + { + return jit_insn_call_intrinsic + (func, name, intrinsic, descr, value1, 0); + } +} + +/* + * Output instructions to handle "finally" clauses in the current context. + */ +static int handle_finally(jit_function_t func, int opcode) +{ + jit_block_eh_t eh = func->builder->current_handler; + while(eh != 0) + { + if(eh->finally_on_fault) + { + /* The "finally" clause only applies to the "catch" block */ + if(!(eh->in_try_body) && + eh->finally_label != jit_label_undefined) + { + return create_noarg_note(func, opcode); + } + } + else if(eh->finally_label != jit_label_undefined) + { + /* The "finally" clause applies to "try" body and the "catch" */ + return create_noarg_note(func, opcode); + } + eh = eh->parent; + } + return 1; +} + +/*@ + * @deftypefun int jit_insn_branch (jit_function_t func, {jit_label_t *} label) + * Terminate the current block by branching unconditionally + * to a specific label. Returns zero if out of memory. + * @end deftypefun +@*/ +int jit_insn_branch(jit_function_t func, jit_label_t *label) +{ + jit_insn_t insn; + jit_block_t block; + if(!label) + { + return 0; + } + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + if(!handle_finally(func, JIT_OP_PREPARE_FOR_LEAVE)) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + if(*label == jit_label_undefined) + { + *label = (func->builder->next_label)++; + } + insn->opcode = (short)JIT_OP_BR; + insn->flags = JIT_INSN_DEST_IS_LABEL; + insn->dest = (jit_value_t)(*label); + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + func->builder->current_block = block; + return 1; +} + +/*@ + * @deftypefun int jit_insn_branch_if (jit_function_t func, jit_value_t value, {jit_label_t *} label) + * Terminate the current block by branching to a specific label if + * the specified value is non-zero. Returns zero if out of memory. + * + * If @code{value} refers to a conditional expression that was created + * by @code{jit_insn_eq}, @code{jit_insn_ne}, etc, then the conditional + * expression will be replaced by an appropriate conditional branch + * instruction. + * @end deftypefun +@*/ +int jit_insn_branch_if + (jit_function_t func, jit_value_t value, jit_label_t *label) +{ + jit_insn_t insn; + jit_block_t block; + jit_type_t type; + int opcode; + jit_value_t value2; + + /* Bail out if the parameters are invalid */ + if(!value || !label) + { + return 0; + } + + /* Ensure that we have a function builder */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* Allocate a new label identifier, if necessary */ + if(*label == jit_label_undefined) + { + *label = (func->builder->next_label)++; + } + + /* Determine if we can replace a previous comparison instruction */ + block = func->builder->current_block; + insn = _jit_block_get_last(block); + if(value->is_temporary && insn && insn->dest == value) + { + opcode = insn->opcode; + if(opcode >= JIT_OP_IEQ && opcode <= JIT_OP_NFGE_INV) + { + switch(opcode) + { + case JIT_OP_IEQ: opcode = JIT_OP_BR_IEQ; break; + case JIT_OP_INE: opcode = JIT_OP_BR_INE; break; + case JIT_OP_ILT: opcode = JIT_OP_BR_ILT; break; + case JIT_OP_ILT_UN: opcode = JIT_OP_BR_ILT_UN; break; + case JIT_OP_ILE: opcode = JIT_OP_BR_ILE; break; + case JIT_OP_ILE_UN: opcode = JIT_OP_BR_ILE_UN; break; + case JIT_OP_IGT: opcode = JIT_OP_BR_IGT; break; + case JIT_OP_IGT_UN: opcode = JIT_OP_BR_IGT_UN; break; + case JIT_OP_IGE: opcode = JIT_OP_BR_IGE; break; + case JIT_OP_IGE_UN: opcode = JIT_OP_BR_IGE_UN; break; + case JIT_OP_LEQ: opcode = JIT_OP_BR_LEQ; break; + case JIT_OP_LNE: opcode = JIT_OP_BR_LNE; break; + case JIT_OP_LLT: opcode = JIT_OP_BR_LLT; break; + case JIT_OP_LLT_UN: opcode = JIT_OP_BR_LLT_UN; break; + case JIT_OP_LLE: opcode = JIT_OP_BR_LLE; break; + case JIT_OP_LLE_UN: opcode = JIT_OP_BR_LLE_UN; break; + case JIT_OP_LGT: opcode = JIT_OP_BR_LGT; break; + case JIT_OP_LGT_UN: opcode = JIT_OP_BR_LGT_UN; break; + case JIT_OP_LGE: opcode = JIT_OP_BR_LGE; break; + case JIT_OP_LGE_UN: opcode = JIT_OP_BR_LGE_UN; break; + case JIT_OP_FEQ: opcode = JIT_OP_BR_FEQ; break; + case JIT_OP_FNE: opcode = JIT_OP_BR_FNE; break; + case JIT_OP_FLT: opcode = JIT_OP_BR_FLT; break; + case JIT_OP_FLE: opcode = JIT_OP_BR_FLE; break; + case JIT_OP_FGT: opcode = JIT_OP_BR_FGT; break; + case JIT_OP_FGE: opcode = JIT_OP_BR_FGE; break; + case JIT_OP_FEQ_INV: opcode = JIT_OP_BR_FEQ_INV; break; + case JIT_OP_FNE_INV: opcode = JIT_OP_BR_FNE_INV; break; + case JIT_OP_FLT_INV: opcode = JIT_OP_BR_FLT_INV; break; + case JIT_OP_FLE_INV: opcode = JIT_OP_BR_FLE_INV; break; + case JIT_OP_FGT_INV: opcode = JIT_OP_BR_FGT_INV; break; + case JIT_OP_FGE_INV: opcode = JIT_OP_BR_FGE_INV; break; + case JIT_OP_DEQ: opcode = JIT_OP_BR_DEQ; break; + case JIT_OP_DNE: opcode = JIT_OP_BR_DNE; break; + case JIT_OP_DLT: opcode = JIT_OP_BR_DLT; break; + case JIT_OP_DLE: opcode = JIT_OP_BR_DLE; break; + case JIT_OP_DGT: opcode = JIT_OP_BR_DGT; break; + case JIT_OP_DGE: opcode = JIT_OP_BR_DGE; break; + case JIT_OP_DEQ_INV: opcode = JIT_OP_BR_DEQ_INV; break; + case JIT_OP_DNE_INV: opcode = JIT_OP_BR_DNE_INV; break; + case JIT_OP_DLT_INV: opcode = JIT_OP_BR_DLT_INV; break; + case JIT_OP_DLE_INV: opcode = JIT_OP_BR_DLE_INV; break; + case JIT_OP_DGT_INV: opcode = JIT_OP_BR_DGT_INV; break; + case JIT_OP_DGE_INV: opcode = JIT_OP_BR_DGE_INV; break; + case JIT_OP_NFEQ: opcode = JIT_OP_BR_NFEQ; break; + case JIT_OP_NFNE: opcode = JIT_OP_BR_NFNE; break; + case JIT_OP_NFLT: opcode = JIT_OP_BR_NFLT; break; + case JIT_OP_NFLE: opcode = JIT_OP_BR_NFLE; break; + case JIT_OP_NFGT: opcode = JIT_OP_BR_NFGT; break; + case JIT_OP_NFGE: opcode = JIT_OP_BR_NFGE; break; + case JIT_OP_NFEQ_INV: opcode = JIT_OP_BR_NFEQ_INV; break; + case JIT_OP_NFNE_INV: opcode = JIT_OP_BR_NFNE_INV; break; + case JIT_OP_NFLT_INV: opcode = JIT_OP_BR_NFLT_INV; break; + case JIT_OP_NFLE_INV: opcode = JIT_OP_BR_NFLE_INV; break; + case JIT_OP_NFGT_INV: opcode = JIT_OP_BR_NFGT_INV; break; + case JIT_OP_NFGE_INV: opcode = JIT_OP_BR_NFGE_INV; break; + } + insn->opcode = (short)opcode; + insn->flags = JIT_INSN_DEST_IS_LABEL; + insn->dest = (jit_value_t)(*label); + goto add_block; + } + } + + /* Coerce the result to something comparable and determine the opcode */ + type = jit_type_promote_int(jit_type_normalize(value->type)); + if(type == jit_type_int || type == jit_type_uint) + { + opcode = JIT_OP_BR_ITRUE; + value2 = 0; + } + else if(type == jit_type_long || type == jit_type_ulong) + { + opcode = JIT_OP_BR_LTRUE; + value2 = 0; + } + else if(type == jit_type_float32) + { + opcode = JIT_OP_BR_FNE; + value2 = jit_value_create_float32_constant + (func, jit_type_float32, (jit_float32)0.0); + if(!value2) + { + return 0; + } + } + else if(type == jit_type_float64) + { + opcode = JIT_OP_BR_DNE; + value2 = jit_value_create_float64_constant + (func, jit_type_float64, (jit_float64)0.0); + if(!value2) + { + return 0; + } + } + else + { + type = jit_type_nfloat; + opcode = JIT_OP_BR_NFNE; + value2 = jit_value_create_nfloat_constant + (func, jit_type_nfloat, (jit_nfloat)0.0); + if(!value2) + { + return 0; + } + } + value = jit_insn_convert(func, value, type, 0); + if(!value) + { + return 0; + } + + /* Add a new branch instruction */ + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value); + jit_value_ref(func, value2); + insn->opcode = (short)opcode; + insn->flags = JIT_INSN_DEST_IS_LABEL; + insn->dest = (jit_value_t)(*label); + insn->value1 = value; + insn->value2 = value2; + +add_block: + /* Add a new block for the fall-through case */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + return 1; +} + +/*@ + * @deftypefun int jit_insn_branch_if_not (jit_function_t func, jit_value_t value, {jit_label_t *} label) + * Terminate the current block by branching to a specific label if + * the specified value is zero. Returns zero if out of memory. + * + * If @code{value} refers to a conditional expression that was created + * by @code{jit_insn_eq}, @code{jit_insn_ne}, etc, then the conditional + * expression will be replaced by an appropriate conditional branch + * instruction. + * @end deftypefun +@*/ +int jit_insn_branch_if_not + (jit_function_t func, jit_value_t value, jit_label_t *label) +{ + jit_insn_t insn; + jit_block_t block; + jit_type_t type; + int opcode; + jit_value_t value2; + + /* Bail out if the parameters are invalid */ + if(!value || !label) + { + return 0; + } + + /* Ensure that we have a function builder */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* Allocate a new label identifier, if necessary */ + if(*label == jit_label_undefined) + { + *label = (func->builder->next_label)++; + } + + /* Determine if we can replace a previous comparison instruction */ + block = func->builder->current_block; + insn = _jit_block_get_last(block); + if(value->is_temporary && insn && insn->dest == value) + { + opcode = insn->opcode; + if(opcode >= JIT_OP_IEQ && opcode <= JIT_OP_NFGE_INV) + { + switch(opcode) + { + case JIT_OP_IEQ: opcode = JIT_OP_BR_INE; break; + case JIT_OP_INE: opcode = JIT_OP_BR_IEQ; break; + case JIT_OP_ILT: opcode = JIT_OP_BR_IGE; break; + case JIT_OP_ILT_UN: opcode = JIT_OP_BR_IGE_UN; break; + case JIT_OP_ILE: opcode = JIT_OP_BR_IGT; break; + case JIT_OP_ILE_UN: opcode = JIT_OP_BR_IGT_UN; break; + case JIT_OP_IGT: opcode = JIT_OP_BR_ILE; break; + case JIT_OP_IGT_UN: opcode = JIT_OP_BR_ILE_UN; break; + case JIT_OP_IGE: opcode = JIT_OP_BR_ILT; break; + case JIT_OP_IGE_UN: opcode = JIT_OP_BR_ILT_UN; break; + case JIT_OP_LEQ: opcode = JIT_OP_BR_LNE; break; + case JIT_OP_LNE: opcode = JIT_OP_BR_LEQ; break; + case JIT_OP_LLT: opcode = JIT_OP_BR_LGE; break; + case JIT_OP_LLT_UN: opcode = JIT_OP_BR_LGE_UN; break; + case JIT_OP_LLE: opcode = JIT_OP_BR_LGT; break; + case JIT_OP_LLE_UN: opcode = JIT_OP_BR_LGT_UN; break; + case JIT_OP_LGT: opcode = JIT_OP_BR_LLE; break; + case JIT_OP_LGT_UN: opcode = JIT_OP_BR_LLE_UN; break; + case JIT_OP_LGE: opcode = JIT_OP_BR_LLT; break; + case JIT_OP_LGE_UN: opcode = JIT_OP_BR_LLT_UN; break; + case JIT_OP_FEQ: opcode = JIT_OP_BR_FNE_INV; break; + case JIT_OP_FNE: opcode = JIT_OP_BR_FEQ_INV; break; + case JIT_OP_FLT: opcode = JIT_OP_BR_FGE_INV; break; + case JIT_OP_FLE: opcode = JIT_OP_BR_FGT_INV; break; + case JIT_OP_FGT: opcode = JIT_OP_BR_FLE_INV; break; + case JIT_OP_FGE: opcode = JIT_OP_BR_FLT_INV; break; + case JIT_OP_FEQ_INV: opcode = JIT_OP_BR_FNE; break; + case JIT_OP_FNE_INV: opcode = JIT_OP_BR_FEQ; break; + case JIT_OP_FLT_INV: opcode = JIT_OP_BR_FGE; break; + case JIT_OP_FLE_INV: opcode = JIT_OP_BR_FGT; break; + case JIT_OP_FGT_INV: opcode = JIT_OP_BR_FLE; break; + case JIT_OP_FGE_INV: opcode = JIT_OP_BR_FLT; break; + case JIT_OP_DEQ: opcode = JIT_OP_BR_DNE_INV; break; + case JIT_OP_DNE: opcode = JIT_OP_BR_DEQ_INV; break; + case JIT_OP_DLT: opcode = JIT_OP_BR_DGE_INV; break; + case JIT_OP_DLE: opcode = JIT_OP_BR_DGT_INV; break; + case JIT_OP_DGT: opcode = JIT_OP_BR_DLE_INV; break; + case JIT_OP_DGE: opcode = JIT_OP_BR_DLT_INV; break; + case JIT_OP_DEQ_INV: opcode = JIT_OP_BR_DNE; break; + case JIT_OP_DNE_INV: opcode = JIT_OP_BR_DEQ; break; + case JIT_OP_DLT_INV: opcode = JIT_OP_BR_DGE; break; + case JIT_OP_DLE_INV: opcode = JIT_OP_BR_DGT; break; + case JIT_OP_DGT_INV: opcode = JIT_OP_BR_DLE; break; + case JIT_OP_DGE_INV: opcode = JIT_OP_BR_DLT; break; + case JIT_OP_NFEQ: opcode = JIT_OP_BR_NFNE_INV; break; + case JIT_OP_NFNE: opcode = JIT_OP_BR_NFEQ_INV; break; + case JIT_OP_NFLT: opcode = JIT_OP_BR_NFGE_INV; break; + case JIT_OP_NFLE: opcode = JIT_OP_BR_NFGT_INV; break; + case JIT_OP_NFGT: opcode = JIT_OP_BR_NFLE_INV; break; + case JIT_OP_NFGE: opcode = JIT_OP_BR_NFLT_INV; break; + case JIT_OP_NFEQ_INV: opcode = JIT_OP_BR_NFNE; break; + case JIT_OP_NFNE_INV: opcode = JIT_OP_BR_NFEQ; break; + case JIT_OP_NFLT_INV: opcode = JIT_OP_BR_NFGE; break; + case JIT_OP_NFLE_INV: opcode = JIT_OP_BR_NFGT; break; + case JIT_OP_NFGT_INV: opcode = JIT_OP_BR_NFLE; break; + case JIT_OP_NFGE_INV: opcode = JIT_OP_BR_NFLT; break; + } + insn->opcode = (short)opcode; + insn->flags = JIT_INSN_DEST_IS_LABEL; + insn->dest = (jit_value_t)(*label); + goto add_block; + } + } + + /* Coerce the result to something comparable and determine the opcode */ + type = jit_type_promote_int(jit_type_normalize(value->type)); + if(type == jit_type_int || type == jit_type_uint) + { + opcode = JIT_OP_BR_IFALSE; + value2 = 0; + } + else if(type == jit_type_long || type == jit_type_ulong) + { + opcode = JIT_OP_BR_LFALSE; + value2 = 0; + } + else if(type == jit_type_float32) + { + opcode = JIT_OP_BR_FEQ_INV; + value2 = jit_value_create_float32_constant + (func, jit_type_float32, (jit_float32)0.0); + if(!value2) + { + return 0; + } + } + else if(type == jit_type_float64) + { + opcode = JIT_OP_BR_DEQ_INV; + value2 = jit_value_create_float64_constant + (func, jit_type_float64, (jit_float64)0.0); + if(!value2) + { + return 0; + } + } + else + { + type = jit_type_nfloat; + opcode = JIT_OP_BR_NFEQ_INV; + value2 = jit_value_create_nfloat_constant + (func, jit_type_nfloat, (jit_nfloat)0.0); + if(!value2) + { + return 0; + } + } + value = jit_insn_convert(func, value, type, 0); + if(!value) + { + return 0; + } + + /* Add a new branch instruction */ + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value); + jit_value_ref(func, value2); + insn->opcode = (short)opcode; + insn->flags = JIT_INSN_DEST_IS_LABEL; + insn->dest = (jit_value_t)(*label); + insn->value1 = value; + insn->value2 = value2; + +add_block: + /* Add a new block for the fall-through case */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + return 1; +} + +/*@ + * @deftypefun jit_value_t jit_insn_address_of (jit_function_t func, jit_value_t value1) + * Get the address of a value into a new temporary. + * @end deftypefun +@*/ +jit_value_t jit_insn_address_of(jit_function_t func, jit_value_t value1) +{ + jit_type_t type; + jit_value_t result; + if(!value1) + { + return 0; + } + type = jit_type_create_pointer(jit_value_get_type(value1), 1); + if(!type) + { + return 0; + } + jit_value_set_addressable(value1); + result = apply_unary(func, JIT_OP_ADDRESS_OF, value1, type); + jit_type_free(type); + return result; +} + +/* + * Information about the opcodes for a particular conversion. + */ +typedef struct jit_convert_info +{ + int cvt1; + jit_type_t type1; + int cvt2; + jit_type_t type2; + int cvt3; + jit_type_t type3; + +} jit_convert_info_t; +#define CVT(opcode,name) opcode, (jit_type_t)&_jit_type_##name##_def +#define CVT_NONE 0, 0 + +/*@ + * @deftypefun jit_value_t jit_insn_convert (jit_function_t func, jit_value_t value, jit_type_t type, int overflow_check) + * Convert the contents of a value into a new type, with optional + * overflow checking. + * @end deftypefun +@*/ +jit_value_t jit_insn_convert(jit_function_t func, jit_value_t value, + jit_type_t type, int overflow_check) +{ + jit_type_t vtype; + const jit_convert_info_t *opcode_map; + + /* Bail out if we previously ran out of memory on this function */ + if(!value) + { + return 0; + } + + /* Normalize the source and destination types */ + type = jit_type_normalize(type); + vtype = jit_type_normalize(value->type); + + /* If the types are identical, then return the source value as-is */ + if(type == vtype) + { + return value; + } + + /* If the source is a constant, then perform a constant conversion. + If an overflow might result, we perform the computation at runtime */ + if(jit_value_is_constant(value)) + { + jit_constant_t const_value; + const_value = jit_value_get_constant(value); + if(jit_constant_convert(&const_value, &const_value, + type, overflow_check)) + { + return jit_value_create_constant(func, &const_value); + } + } + + /* Promote the source type, to reduce the number of cases in + the switch statement below */ + vtype = jit_type_promote_int(vtype); + + /* Determine how to perform the conversion */ + opcode_map = 0; + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + /* Convert the value into a signed byte */ + static jit_convert_info_t const to_sbyte[] = { + {CVT(JIT_OP_TRUNC_SBYTE, sbyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_SBYTE, sbyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_TRUNC_SBYTE, sbyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_INT, int), + CVT(JIT_OP_CHECK_SBYTE, sbyte), + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_SBYTE, sbyte), + CVT_NONE}, + {CVT(JIT_OP_CHECK_SIGNED_LOW_WORD, int), + CVT(JIT_OP_CHECK_SBYTE, sbyte), + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_SBYTE, sbyte), + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT(JIT_OP_CHECK_INT, int), + CVT(JIT_OP_CHECK_SBYTE, sbyte)}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_SBYTE, sbyte)}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_SBYTE, sbyte)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_SBYTE, sbyte)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_SBYTE, sbyte)}, + {CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_SBYTE, sbyte), + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_SBYTE, sbyte), + CVT_NONE} + }; + opcode_map = to_sbyte; + } + break; + + case JIT_TYPE_UBYTE: + { + /* Convert the value into an unsigned byte */ + static jit_convert_info_t const to_ubyte[] = { + {CVT(JIT_OP_TRUNC_UBYTE, ubyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_UBYTE, ubyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_TRUNC_UBYTE, ubyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_UBYTE, ubyte), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_UBYTE, ubyte), + CVT_NONE}, + {CVT(JIT_OP_CHECK_SIGNED_LOW_WORD, int), + CVT(JIT_OP_CHECK_UBYTE, ubyte), + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_UBYTE, ubyte), + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT(JIT_OP_CHECK_UBYTE, ubyte), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_UBYTE, ubyte)}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_UBYTE, ubyte)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_UBYTE, ubyte)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_UBYTE, ubyte)}, + {CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_UBYTE, ubyte), + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_UBYTE, ubyte), + CVT_NONE} + }; + opcode_map = to_ubyte; + } + break; + + case JIT_TYPE_SHORT: + { + /* Convert the value into a signed short */ + static jit_convert_info_t const to_short[] = { + {CVT(JIT_OP_TRUNC_SHORT, short), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_SHORT, short), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_TRUNC_SHORT, short), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_INT, int), + CVT(JIT_OP_CHECK_SHORT, short), + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_SHORT, short), + CVT_NONE}, + {CVT(JIT_OP_CHECK_SIGNED_LOW_WORD, int), + CVT(JIT_OP_CHECK_SHORT, short), + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_SHORT, short), + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT(JIT_OP_CHECK_INT, int), + CVT(JIT_OP_CHECK_SHORT, short)}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_SHORT, short)}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_SHORT, short)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_SHORT, short)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_SHORT, short)}, + {CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_SHORT, short), + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_SHORT, short), + CVT_NONE} + }; + opcode_map = to_short; + } + break; + + case JIT_TYPE_USHORT: + { + /* Convert the value into an unsigned short */ + static jit_convert_info_t const to_ushort[] = { + {CVT(JIT_OP_TRUNC_USHORT, ushort), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_USHORT, ushort), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_TRUNC_USHORT, ushort), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_USHORT, ushort), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_USHORT, ushort), + CVT_NONE}, + {CVT(JIT_OP_CHECK_SIGNED_LOW_WORD, int), + CVT(JIT_OP_CHECK_USHORT, ushort), + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_USHORT, ushort), + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT(JIT_OP_CHECK_USHORT, ushort), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_USHORT, ushort)}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_USHORT, ushort)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_USHORT, ushort)}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_USHORT, ushort)}, + {CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT(JIT_OP_TRUNC_USHORT, ushort), + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT(JIT_OP_CHECK_USHORT, ushort), + CVT_NONE} + }; + opcode_map = to_ushort; + } + break; + + case JIT_TYPE_INT: + { + /* Convert the value into a signed int */ + static jit_convert_info_t const to_int[] = { + {CVT(JIT_OP_COPY_INT, int), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_INT, int), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_TRUNC_INT, int), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_INT, int), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_INT, int), + CVT_NONE}, + {CVT(JIT_OP_CHECK_SIGNED_LOW_WORD, int), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, int), + CVT(JIT_OP_TRUNC_INT, int), + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT(JIT_OP_CHECK_INT, int), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_INT, int), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_INT, int), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_int; + } + break; + + case JIT_TYPE_UINT: + { + /* Convert the value into an unsigned int */ + static jit_convert_info_t const to_uint[] = { + {CVT(JIT_OP_TRUNC_UINT, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_UINT, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_INT, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_INT, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LOW_WORD, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_LOW_WORD, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_UINT, uint), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_UINT, uint), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_UINT, uint), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_UINT, uint), + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_UINT, uint), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_UINT, uint), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_uint; + } + break; + + case JIT_TYPE_LONG: + { + /* Convert the value into a signed long */ + static jit_convert_info_t const to_long[] = { + {CVT(JIT_OP_EXPAND_INT, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_EXPAND_INT, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_EXPAND_UINT, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_EXPAND_UINT, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_LONG, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_LONG, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_LONG, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_LONG, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_LONG, long), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_LONG, long), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_LONG, long), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_LONG, long), + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_LONG, long), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_LONG, long), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_long; + } + break; + + case JIT_TYPE_ULONG: + { + /* Convert the value into an unsigned long */ + static jit_convert_info_t const to_ulong[] = { + {CVT(JIT_OP_EXPAND_INT, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_UINT, uint), + CVT(JIT_OP_EXPAND_UINT, ulong), + CVT_NONE}, + {CVT(JIT_OP_EXPAND_UINT, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_EXPAND_UINT, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_LONG, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_ULONG, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_LONG, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_LONG, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_ULONG, ulong), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_ULONG, ulong), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_ULONG, ulong), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_CHECK_NFLOAT_TO_ULONG, ulong), + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_ULONG, ulong), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_CHECK_NFLOAT_TO_ULONG, ulong), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_ulong; + } + break; + + case JIT_TYPE_FLOAT32: + { + /* Convert the value into a 32-bit float */ + static jit_convert_info_t const to_float32[] = { + {CVT(JIT_OP_INT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_INT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_UINT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_UINT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_LONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_LONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_ULONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_ULONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_COPY_FLOAT32, float32), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_FLOAT32, float32), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_FLOAT32, float32), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_float32; + } + break; + + case JIT_TYPE_FLOAT64: + { + /* Convert the value into a 64-bit float */ + static jit_convert_info_t const to_float64[] = { + {CVT(JIT_OP_INT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_INT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_UINT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_UINT_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_LONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_LONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_ULONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_ULONG_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE}, + {CVT(JIT_OP_COPY_FLOAT64, float64), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_FLOAT64, float64), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_NFLOAT_TO_FLOAT64, float64), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_float64; + } + break; + + case JIT_TYPE_NFLOAT: + { + /* Convert the value into a native floating-point value */ + static jit_convert_info_t const to_nfloat[] = { + {CVT(JIT_OP_INT_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_INT_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_UINT_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_UINT_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LONG_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_LONG_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_ULONG_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_ULONG_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT32_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_FLOAT64_TO_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE}, + {CVT(JIT_OP_COPY_NFLOAT, nfloat), + CVT_NONE, + CVT_NONE} + }; + opcode_map = to_nfloat; + } + break; + } + if(opcode_map) + { + switch(vtype->kind) + { + case JIT_TYPE_UINT: opcode_map += 2; break; + case JIT_TYPE_LONG: opcode_map += 4; break; + case JIT_TYPE_ULONG: opcode_map += 6; break; + case JIT_TYPE_FLOAT32: opcode_map += 8; break; + case JIT_TYPE_FLOAT64: opcode_map += 10; break; + case JIT_TYPE_NFLOAT: opcode_map += 12; break; + } + if(overflow_check) + { + opcode_map += 1; + } + value = apply_unary + (func, opcode_map->cvt1, value, opcode_map->type1); + if(opcode_map->cvt2) + { + value = apply_unary + (func, opcode_map->cvt2, value, opcode_map->type2); + } + if(opcode_map->cvt3) + { + value = apply_unary + (func, opcode_map->cvt3, value, opcode_map->type3); + } + } + return value; +} + +/* + * Convert the parameters for a function call into their final types. + */ +static int convert_call_parameters + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + jit_value_t *new_args) +{ + unsigned int param; + for(param = 0; param < num_args; ++param) + { + new_args[param] = jit_insn_convert + (func, args[param], + jit_type_get_param(signature, param), 0); + } + return 1; +} + +/* + * Set up the exception frame information before a function call out. + */ +static int setup_eh_frame_for_call(jit_function_t func, int flags) +{ +#if !defined(JIT_BACKEND_INTERP) + jit_type_t type; + jit_value_t eh_frame_info; + jit_block_t block; + jit_value_t args[2]; + jit_insn_t insn; + jit_type_t params[2]; + jit_value_t struct_return; + + /* If the "nothrow" or "tail" flags are set, then we don't + need to worry about this */ + if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) != 0) + { + return 1; + } + + /* This function may throw an exception */ + func->builder->may_throw = 1; + + /* Get the value that holds the exception frame information */ + if((eh_frame_info = func->builder->eh_frame_info) == 0) + { + type = jit_type_create_struct(0, 0, 0); + if(!type) + { + return 0; + } + jit_type_set_size_and_alignment + (type, sizeof(struct jit_backtrace), sizeof(void *)); + eh_frame_info = jit_value_create(func, type); + jit_type_free(type); + if(!eh_frame_info) + { + return 0; + } + func->builder->eh_frame_info = eh_frame_info; + } + + /* Output an instruction to load the "pc" into a value */ + args[1] = jit_value_create(func, jit_type_void_ptr); + if(!(args[1])) + { + return 0; + } + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, args[1]); + insn->opcode = JIT_OP_LOAD_PC; + insn->dest = args[1]; + + /* Load the address of "eh_frame_info" into another value */ + args[0] = jit_insn_address_of(func, eh_frame_info); + if(!(args[0])) + { + return 0; + } + + /* Create a signature for the prototype "void (void *, void *)" */ + params[0] = jit_type_void_ptr; + params[1] = jit_type_void_ptr; + type = jit_type_create_signature + (jit_abi_cdecl, jit_type_void, params, 2, 1); + if(!type) + { + return 0; + } + + /* Set up to call the "_jit_backtrace_push" intrinsic */ + if(!_jit_create_call_setup_insns + (func, type, args, 2, 0, 0, &struct_return)) + { + jit_type_free(type); + return 0; + } + + /* Terminate the current block and then call "_jit_backtrace_push" */ + block = _jit_block_create(func, 0); + if(!block) + { + jit_type_free(type); + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + insn = _jit_block_add_insn(block); + if(!insn) + { + jit_type_free(type); + return 0; + } + insn->opcode = JIT_OP_CALL_EXTERNAL; + insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME; + insn->dest = (jit_value_t)(void *)_jit_backtrace_push; + insn->value1 = (jit_value_t)"_jit_backtrace_push"; + + /* Clean up after the function call */ + if(!_jit_create_call_return_insns(func, type, args, 2, 0, 0)) + { + jit_type_free(type); + return 0; + } + + /* We are now ready to make the actual function call */ + jit_type_free(type); + return 1; +#else /* JIT_BACKEND_INTERP */ + /* The interpreter handles exception frames for us */ + if((flags & (JIT_CALL_NOTHROW | JIT_CALL_TAIL)) == 0) + { + func->builder->may_throw = 1; + } + return 1; +#endif +} + +/* + * Restore the exception handling frame after a function call. + */ +static int restore_eh_frame_after_call(jit_function_t func, int flags) +{ +#if !defined(JIT_BACKEND_INTERP) + jit_type_t type; + jit_value_t struct_return; + jit_block_t block; + jit_insn_t insn; + + /* If the "nothrow", "noreturn", or "tail" flags are set, then we + don't need to worry about this */ + if((flags & (JIT_CALL_NOTHROW | JIT_CALL_NORETURN | JIT_CALL_TAIL)) != 0) + { + return 1; + } + + /* Create the signature prototype "void (void)" */ + type = jit_type_create_signature + (jit_abi_cdecl, jit_type_void, 0, 0, 0); + if(!type) + { + return 0; + } + + /* Set up to call the "_jit_backtrace_pop" intrinsic */ + if(!_jit_create_call_setup_insns + (func, type, 0, 0, 0, 0, &struct_return)) + { + jit_type_free(type); + return 0; + } + + /* Terminate the current block and then call "_jit_backtrace_pop" */ + block = _jit_block_create(func, 0); + if(!block) + { + jit_type_free(type); + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + insn = _jit_block_add_insn(block); + if(!insn) + { + jit_type_free(type); + return 0; + } + insn->opcode = JIT_OP_CALL_EXTERNAL; + insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME; + insn->dest = (jit_value_t)(void *)_jit_backtrace_pop; + insn->value1 = (jit_value_t)"_jit_backtrace_pop"; + + /* Clean up after the function call */ + if(!_jit_create_call_return_insns(func, type, 0, 0, 0, 0)) + { + jit_type_free(type); + return 0; + } + + /* Everything is back to where it should be */ + jit_type_free(type); + return 1; +#else /* JIT_BACKEND_INTERP */ + /* The interpreter handles exception frames for us */ + return 1; +#endif +} + +/*@ + * @deftypefun jit_value_t jit_insn_call (jit_function_t func, {const char *} name, jit_function_t jit_func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * Call the function @code{jit_func}, which may or may not be translated yet. + * The @code{name} is for diagnostic purposes only, and can be NULL. + * + * If @code{signature} is NULL, then the actual signature of @code{jit_func} + * is used in its place. This is the usual case. However, if the function + * takes a variable number of arguments, then you may need to construct + * an explicit signature for the non-fixed argument values. + * + * The @code{flags} parameter specifies additional information about the + * type of call to perform: + * + * @table @code + * @vindex JIT_CALL_NOTHROW + * @item JIT_CALL_NOTHROW + * The function never throws exceptions. + * + * @vindex JIT_CALL_NORETURN + * @item JIT_CALL_NORETURN + * The function will never return directly to its caller. It may however + * return to the caller indirectly by throwing an exception that the + * caller catches. + * + * @vindex JIT_CALL_TAIL + * @item JIT_CALL_TAIL + * Apply tail call optimizations, as the result of this function + * call will be immediately returned from the containing function. + * Tail calls are only appropriate when the signature of the called + * function matches the callee, and none of the parameters point + * to local variables. + * @end table + * + * If @code{jit_func} has already been compiled, then @code{jit_insn_call} + * may be able to intuit some of the above flags for itself. Otherwise + * it is up to the caller to determine when the flags may be appropriate. + * @end deftypefun +@*/ +jit_value_t jit_insn_call + (jit_function_t func, const char *name, jit_function_t jit_func, + jit_type_t signature, jit_value_t *args, unsigned int num_args, int flags) +{ + int is_nested; + int nesting_level; + jit_function_t temp_func; + jit_value_t *new_args; + jit_value_t return_value; + jit_block_t block; + jit_insn_t insn; + + /* Bail out if there is something wrong with the parameters */ + if(!func || !jit_func) + { + return 0; + } + + /* Get the default signature from "jit_func" */ + if(!signature) + { + signature = jit_func->signature; + } + + /* Determine the nesting relationship with the current function */ + if(jit_func->nested_parent) + { + is_nested = 1; + if(jit_func->nested_parent == func) + { + /* We are calling one of our children */ + nesting_level = -1; + } + else if(jit_func->nested_parent == func->nested_parent) + { + /* We are calling one of our direct siblings */ + nesting_level = 0; + } + else + { + /* Search up to find the actual nesting level */ + temp_func = func->nested_parent; + nesting_level = 1; + while(temp_func != 0 && temp_func != jit_func) + { + ++nesting_level; + temp_func = temp_func->nested_parent; + } + } + } + else + { + is_nested = 0; + nesting_level = 0; + } + + /* Convert the arguments to the actual parameter types */ + if(num_args > 0) + { + new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args); + if(!convert_call_parameters(func, signature, args, num_args, new_args)) + { + return 0; + } + } + else + { + new_args = args; + } + + /* Intuit additional flags from "jit_func" if it was already compiled */ + if(func->no_throw) + { + flags |= JIT_CALL_NOTHROW; + } + if(func->no_return) + { + flags |= JIT_CALL_NORETURN; + } + + /* Set up exception frame information for the call */ + if(!setup_eh_frame_for_call(func, flags)) + { + return 0; + } + + /* Create the instructions to push the parameters onto the stack */ + if(!_jit_create_call_setup_insns + (func, signature, new_args, num_args, + is_nested, nesting_level, &return_value)) + { + return 0; + } + + /* Functions that call out are not leaves */ + func->builder->non_leaf = 1; + + /* Start a new block and output the "call" instruction */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + insn = _jit_block_add_insn(block); + if(!insn) + { + return 0; + } + insn->opcode = JIT_OP_CALL; + insn->flags = JIT_INSN_DEST_IS_FUNCTION | JIT_INSN_VALUE1_IS_NAME; + insn->dest = (jit_value_t)jit_func; + insn->value1 = (jit_value_t)name; + + /* If the function does not return, then end the current block. + The next block does not have "entered_via_top" set so that + it will be eliminated during later code generation */ + if((flags & JIT_CALL_NORETURN) != 0) + { + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + func->builder->current_block = block; + } + + /* Create space for the return value, if we don't already have one */ + if(!return_value) + { + return_value = jit_value_create(func, jit_type_get_return(signature)); + if(!return_value) + { + return 0; + } + } + + /* Create the instructions necessary to move the return value into place */ + if(!_jit_create_call_return_insns + (func, signature, new_args, num_args, return_value, is_nested)) + { + return 0; + } + + /* Restore exception frame information after the call */ + if(!restore_eh_frame_after_call(func, flags)) + { + return 0; + } + + /* Return the value containing the result to the caller */ + return return_value; +} + +/*@ + * @deftypefun jit_value_t jit_insn_call_indirect (jit_function_t func, jit_value_t value, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * Call a function via an indirect pointer. + * @end deftypefun +@*/ +jit_value_t jit_insn_call_indirect + (jit_function_t func, jit_value_t value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) +{ + jit_value_t *new_args; + jit_value_t return_value; + jit_block_t block; + jit_insn_t insn; + + /* Bail out if there is something wrong with the parameters */ + if(!func || !value || !signature) + { + return 0; + } + + /* Convert the arguments to the actual parameter types */ + if(num_args > 0) + { + new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args); + if(!convert_call_parameters(func, signature, args, num_args, new_args)) + { + return 0; + } + } + else + { + new_args = args; + } + + /* Set up exception frame information for the call */ + if(!setup_eh_frame_for_call(func, flags)) + { + return 0; + } + + /* Create the instructions to push the parameters onto the stack */ + if(!_jit_create_call_setup_insns + (func, signature, new_args, num_args, 0, 0, &return_value)) + { + return 0; + } + + /* Move the indirect pointer value into an appropriate register */ + if(!_jit_setup_indirect_pointer(func, value)) + { + return 0; + } + + /* Functions that call out are not leaves */ + func->builder->non_leaf = 1; + + /* Start a new block and output the "call_indirect" instruction */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + insn = _jit_block_add_insn(block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value); + insn->opcode = JIT_OP_CALL_INDIRECT; + insn->value1 = value; + + /* If the function does not return, then end the current block. + The next block does not have "entered_via_top" set so that + it will be eliminated during later code generation */ + if((flags & JIT_CALL_NORETURN) != 0) + { + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + func->builder->current_block = block; + } + + /* Create space for the return value, if we don't already have one */ + if(!return_value) + { + return_value = jit_value_create(func, jit_type_get_return(signature)); + if(!return_value) + { + return 0; + } + } + + /* Create the instructions necessary to move the return value into place */ + if(!_jit_create_call_return_insns + (func, signature, new_args, num_args, return_value, 0)) + { + return 0; + } + + /* Restore exception frame information after the call */ + if(!restore_eh_frame_after_call(func, flags)) + { + return 0; + } + + /* Return the value containing the result to the caller */ + return return_value; +} + +/*@ + * @deftypefun jit_value_t jit_insn_call_indirect_vtable (jit_function_t func, jit_value_t value, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * Call a function via an indirect pointer. This version differs from + * @code{jit_insn_call_indirect} in that we assume that @code{value} + * contains a pointer that resulted from calling + * @code{jit_function_to_vtable_pointer}. Indirect vtable pointer + * calls may be more efficient on some platforms than regular indirect calls. + * @end deftypefun +@*/ +jit_value_t jit_insn_call_indirect_vtable + (jit_function_t func, jit_value_t value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) +{ + jit_value_t *new_args; + jit_value_t return_value; + jit_block_t block; + jit_insn_t insn; + + /* Bail out if there is something wrong with the parameters */ + if(!func || !value || !signature) + { + return 0; + } + + /* Convert the arguments to the actual parameter types */ + if(num_args > 0) + { + new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args); + if(!convert_call_parameters(func, signature, args, num_args, new_args)) + { + return 0; + } + } + else + { + new_args = args; + } + + /* Set up exception frame information for the call */ + if(!setup_eh_frame_for_call(func, flags)) + { + return 0; + } + + /* Create the instructions to push the parameters onto the stack */ + if(!_jit_create_call_setup_insns + (func, signature, new_args, num_args, 0, 0, &return_value)) + { + return 0; + } + + /* Move the indirect pointer value into an appropriate register */ + if(!_jit_setup_indirect_pointer(func, value)) + { + return 0; + } + + /* Functions that call out are not leaves */ + func->builder->non_leaf = 1; + + /* Start a new block and output the "call_vtable_ptr" instruction */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + insn = _jit_block_add_insn(block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value); + insn->opcode = JIT_OP_CALL_VTABLE_PTR; + insn->value1 = value; + + /* If the function does not return, then end the current block. + The next block does not have "entered_via_top" set so that + it will be eliminated during later code generation */ + if((flags & JIT_CALL_NORETURN) != 0) + { + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + func->builder->current_block = block; + } + + /* Create space for the return value, if we don't already have one */ + if(!return_value) + { + return_value = jit_value_create(func, jit_type_get_return(signature)); + if(!return_value) + { + return 0; + } + } + + /* Create the instructions necessary to move the return value into place */ + if(!_jit_create_call_return_insns + (func, signature, new_args, num_args, return_value, 0)) + { + return 0; + } + + /* Restore exception frame information after the call */ + if(!restore_eh_frame_after_call(func, flags)) + { + return 0; + } + + /* Return the value containing the result to the caller */ + return return_value; +} + +/*@ + * @deftypefun jit_value_t jit_insn_call_native (jit_function_t func, {const char *} name, {void *} native_func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int exception_return, int flags) + * Output an instruction that calls an external native function. + * The @code{name} is for diagnostic purposes only, and can be NULL. + * @end deftypefun +@*/ +jit_value_t jit_insn_call_native + (jit_function_t func, const char *name, void *native_func, + jit_type_t signature, jit_value_t *args, unsigned int num_args, int flags) +{ + jit_value_t *new_args; + jit_value_t return_value; + jit_block_t block; + jit_insn_t insn; + + /* Bail out if there is something wrong with the parameters */ + if(!func || !native_func || !signature) + { + return 0; + } + + /* Convert the arguments to the actual parameter types */ + if(num_args > 0) + { + new_args = (jit_value_t *)alloca(sizeof(jit_value_t) * num_args); + if(!convert_call_parameters(func, signature, args, num_args, new_args)) + { + return 0; + } + } + else + { + new_args = args; + } + + /* Set up exception frame information for the call */ + if(!setup_eh_frame_for_call(func, flags)) + { + return 0; + } + + /* Create the instructions to push the parameters onto the stack */ + if(!_jit_create_call_setup_insns + (func, signature, new_args, num_args, 0, 0, &return_value)) + { + return 0; + } + + /* Functions that call out are not leaves */ + func->builder->non_leaf = 1; + + /* Start a new block and output the "call_external" instruction */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + func->builder->current_block = block; + insn = _jit_block_add_insn(block); + if(!insn) + { + return 0; + } + insn->opcode = JIT_OP_CALL_EXTERNAL; + insn->flags = JIT_INSN_DEST_IS_NATIVE | JIT_INSN_VALUE1_IS_NAME; + insn->dest = (jit_value_t)native_func; + insn->value1 = (jit_value_t)name; + + /* If the function does not return, then end the current block. + The next block does not have "entered_via_top" set so that + it will be eliminated during later code generation */ + if((flags & JIT_CALL_NORETURN) != 0) + { + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + func->builder->current_block = block; + } + + /* Create space for the return value, if we don't already have one */ + if(!return_value) + { + return_value = jit_value_create(func, jit_type_get_return(signature)); + if(!return_value) + { + return 0; + } + } + + /* Create the instructions necessary to move the return value into place */ + if(!_jit_create_call_return_insns + (func, signature, new_args, num_args, return_value, 0)) + { + return 0; + } + + /* Restore exception frame information after the call */ + if(!restore_eh_frame_after_call(func, flags)) + { + return 0; + } + + /* Return the value containing the result to the caller */ + return return_value; +} + +/*@ + * @deftypefun jit_value_t jit_insn_call_intrinsic (jit_function_t func, {const char *} name, {void *} intrinsic_func, {const jit_intrinsic_descr_t *} descriptor, jit_value_t arg1, jit_value_t arg2) + * Output an instruction that calls an intrinsic function. The descriptor + * contains the following fields: + * + * @table @code + * @item return_type + * The type of value that is returned from the intrinsic. + * + * @item ptr_result_type + * This should be NULL for an ordinary intrinsic, or the result type + * if the intrinsic reports exceptions. + * + * @item arg1_type + * The type of the first argument. + * + * @item arg2_type + * The type of the second argument, or NULL for a unary intrinsic. + * @end table + * + * If all of the arguments are constant, then @code{jit_insn_call_intrinsic} + * will call the intrinsic directly to calculate the constant result. + * If the constant computation will result in an exception, then + * code is output to cause the exception at runtime. + * + * The @code{name} is for diagnostic purposes only, and can be NULL. + * @end deftypefun +@*/ +jit_value_t jit_insn_call_intrinsic + (jit_function_t func, const char *name, void *intrinsic_func, + const jit_intrinsic_descr_t *descriptor, + jit_value_t arg1, jit_value_t arg2) +{ + jit_type_t signature; + jit_type_t param_types[3]; + jit_value_t param_values[3]; + unsigned int num_params; + jit_value_t return_value; + jit_value_t temp_value; + jit_value_t cond_value; + jit_label_t label; + jit_constant_t const1; + jit_constant_t const2; + jit_constant_t return_const; + jit_constant_t temp_const; + void *apply_args[3]; + + /* Ensure that we have a builder for this function */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* Coerce the arguments to the desired types */ + arg1 = jit_insn_convert(func, arg1, descriptor->arg1_type, 0); + if(!arg1) + { + return 0; + } + if(arg2) + { + arg2 = jit_insn_convert(func, arg2, descriptor->arg2_type, 0); + if(!arg2) + { + return 0; + } + } + + /* Allocate space for a return value if the intrinsic reports exceptions */ + if(descriptor->ptr_result_type) + { + return_value = jit_value_create(func, descriptor->ptr_result_type); + if(!return_value) + { + return 0; + } + } + else + { + return_value = 0; + } + + /* Construct the signature for the intrinsic */ + num_params = 0; + if(return_value) + { + /* Pass a pointer to "return_value" as the first argument */ + temp_value = jit_insn_address_of(func, return_value); + if(!temp_value) + { + return 0; + } + param_types[num_params] = jit_value_get_type(temp_value); + param_values[num_params] = temp_value; + ++num_params; + } + param_types[num_params] = jit_value_get_type(arg1); + param_values[num_params] = arg1; + ++num_params; + if(arg2) + { + param_types[num_params] = jit_value_get_type(arg2); + param_values[num_params] = arg2; + ++num_params; + } + signature = jit_type_create_signature + (jit_abi_cdecl, descriptor->return_type, param_types, num_params, 1); + if(!signature) + { + return 0; + } + + /* If the arguments are constant, then invoke the intrinsic now */ + if(jit_value_is_constant(arg1) && (!arg2 || jit_value_is_constant(arg2))) + { + const1 = jit_value_get_constant(arg1); + const2 = jit_value_get_constant(arg2); + return_const.type = descriptor->ptr_result_type; + if(return_value) + { + jit_int result; + temp_const.un.ptr_value = &return_const.un; + apply_args[0] = &temp_const.un; + apply_args[1] = &const1.un; + apply_args[2] = &const2.un; + jit_apply(signature, intrinsic_func, apply_args, + num_params, &result); + if(result >= 1) + { + /* No exception occurred, so return the constant value */ + jit_type_free(signature); + return jit_value_create_constant(func, &return_const); + } + } + else + { + apply_args[0] = &const1.un; + apply_args[1] = &const2.un; + jit_apply(signature, intrinsic_func, apply_args, + num_params, &return_const.un); + jit_type_free(signature); + return jit_value_create_constant(func, &return_const); + } + } + + /* Call the intrinsic as a native function */ + temp_value = jit_insn_call_native + (func, name, intrinsic_func, signature, param_values, + num_params, JIT_CALL_NOTHROW); + if(!temp_value) + { + jit_type_free(signature); + return 0; + } + jit_type_free(signature); + + /* If no exceptions to report, then return "temp_value" as the result */ + if(!return_value) + { + return temp_value; + } + + /* Determine if an exception was reported */ + cond_value = jit_insn_ge(func, temp_value, + jit_value_create_nint_constant(func, jit_type_int, 1)); + if(!cond_value) + { + return 0; + } + label = jit_label_undefined; + if(!jit_insn_branch_if(func, cond_value, &label)) + { + return 0; + } + + /* Call the "jit_exception_builtin" function to report the exception */ + param_types[0] = jit_type_int; + signature = jit_type_create_signature + (jit_abi_cdecl, jit_type_void, param_types, 1, 1); + if(!signature) + { + return 0; + } + param_values[0] = temp_value; + jit_insn_call_native + (func, "jit_exception_builtin", + (void *)jit_exception_builtin, signature, param_values, 1, + JIT_CALL_NORETURN); + jit_type_free(signature); + + /* The "jit_exception_builtin" function will never return */ + func->builder->current_block->ends_in_dead = 1; + + /* Execution continues here if there was no exception */ + if(!jit_insn_label(func, &label)) + { + return 0; + } + + /* Return the temporary that contains the result value */ + return return_value; +} + +/*@ + * @deftypefun int jit_insn_incoming_reg (jit_function_t func, jit_value_t value, int reg) + * Output an instruction that notes that the contents of @code{value} + * can be found in the register @code{reg} at this point in the code. + * + * You normally wouldn't call this yourself - it is used internally + * by the CPU back ends to set up the function's entry frame and the + * values of registers on return from a subroutine call. + * @end deftypefun +@*/ +int jit_insn_incoming_reg(jit_function_t func, jit_value_t value, int reg) +{ + return create_note(func, JIT_OP_INCOMING_REG, value, + jit_value_create_nint_constant + (func, jit_type_int, (jit_nint)reg)); +} + +/*@ + * @deftypefun int jit_insn_incoming_frame_posn (jit_function_t func, jit_value_t value, jit_nint frame_offset) + * Output an instruction that notes that the contents of @code{value} + * can be found in the stack frame at @code{frame_offset} at this point + * in the code. + * + * You normally wouldn't call this yourself - it is used internally + * by the CPU back ends to set up the function's entry frame. + * @end deftypefun +@*/ +int jit_insn_incoming_frame_posn + (jit_function_t func, jit_value_t value, jit_nint frame_offset) +{ + return create_note(func, JIT_OP_INCOMING_FRAME_POSN, value, + jit_value_create_nint_constant + (func, jit_type_int, frame_offset)); +} + +/*@ + * @deftypefun int jit_insn_outgoing_reg (jit_function_t func, jit_value_t value, int reg) + * Output an instruction that copies the contents of @code{value} + * into the register @code{reg} at this point in the code. This is + * typically used just before making an outgoing subroutine call. + * + * You normally wouldn't call this yourself - it is used internally + * by the CPU back ends to set up the registers for a subroutine call. + * @end deftypefun +@*/ +int jit_insn_outgoing_reg(jit_function_t func, jit_value_t value, int reg) +{ + return create_note(func, JIT_OP_OUTGOING_REG, value, + jit_value_create_nint_constant + (func, jit_type_int, (jit_nint)reg)); +} + +/*@ + * @deftypefun int jit_insn_return_reg (jit_function_t func, jit_value_t value, int reg) + * Output an instruction that notes that the contents of @code{value} + * can be found in the register @code{reg} at this point in the code. + * This is similar to @code{jit_insn_incoming_reg}, except that it + * refers to return values, not parameter values. + * + * You normally wouldn't call this yourself - it is used internally + * by the CPU back ends to handle returns from subroutine calls. + * @end deftypefun +@*/ +int jit_insn_return_reg(jit_function_t func, jit_value_t value, int reg) +{ + return create_note(func, JIT_OP_RETURN_REG, value, + jit_value_create_nint_constant + (func, jit_type_int, (jit_nint)reg)); +} + +/*@ + * @deftypefun int jit_insn_setup_for_nested (jit_function_t func, int nested_level, int reg) + * Output an instruction to set up for a nested function call. + * The @code{nested_level} value will be -1 to call a child, zero to call a + * sibling of @code{func}, 1 to call a sibling of the parent, 2 to call + * a sibling of the grandparent, etc. If @code{reg} is not -1, then + * it indicates the register to receive the parent frame information. + * If @code{reg} is -1, then the frame information will be pushed on the stack. + * + * You normally wouldn't call this yourself - it is used internally by the + * CPU back ends to set up the parameters for a nested subroutine call. + * @end deftypefun +@*/ +int jit_insn_setup_for_nested(jit_function_t func, int nested_level, int reg) +{ + if(nested_level < 0) + { + return create_unary_note + (func, JIT_OP_SETUP_FOR_NESTED, + jit_value_create_nint_constant + (func, jit_type_int, (jit_nint)reg)); + } + else + { + return create_note + (func, JIT_OP_SETUP_FOR_SIBLING, + jit_value_create_nint_constant + (func, jit_type_int, (jit_nint)nested_level), + jit_value_create_nint_constant + (func, jit_type_int, (jit_nint)reg)); + } +} + +/*@ + * @deftypefun int jit_insn_flush_struct (jit_function_t func, jit_value_t value) + * Flush a small structure return value out of registers and back + * into the local variable frame. You normally wouldn't call this + * yourself - it is used internally by the CPU back ends to handle + * structure returns from functions. + * @end deftypefun +@*/ +int jit_insn_flush_struct(jit_function_t func, jit_value_t value) +{ + if(value) + { + jit_value_set_addressable(value); + } + return create_unary_note(func, JIT_OP_FLUSH_SMALL_STRUCT, value); +} + +/*@ + * @deftypefun jit_value_t jit_insn_import (jit_function_t func, jit_value_t value) + * Import @code{value} from an outer nested scope into @code{func}. Returns + * the effective address of the value for local access via a pointer. + * If @code{value} is local to @code{func}, then it is returned as-is. + * Returns NULL if out of memory or the value is not accessible via a + * parent, grandparent, or other ancestor of @code{func}. + * @end deftypefun +@*/ +jit_value_t jit_insn_import(jit_function_t func, jit_value_t value) +{ + jit_function_t value_func; + jit_function_t current_func; + int level; + + /* Make sure that we have a builder before we start */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* If the value is already local, then there is nothing to do */ + value_func = jit_value_get_function(value); + if(value_func == func) + { + return value; + } + + /* Find the nesting level of the value, where 1 is our parent */ + level = 1; + current_func = func->nested_parent; + while(current_func != 0 && current_func != value_func) + { + ++level; + current_func = current_func->nested_parent; + } + if(!current_func) + { + /* The value is not accessible from this scope */ + return 0; + } + + /* Output the relevant import instruction, which will also cause + it to be marked as a non-local addressable by "jit_value_ref" */ + return apply_binary + (func, JIT_OP_IMPORT, value, + jit_value_create_nint_constant(func, jit_type_int, (jit_nint)value), + jit_type_void_ptr); +} + +/*@ + * @deftypefun int jit_insn_push (jit_function_t func, jit_value_t value) + * Push a value onto the function call stack, in preparation for a call. + * You normally wouldn't call this yourself - it is used internally + * by the CPU back ends to set up the stack for a subroutine call. + * @end deftypefun +@*/ +int jit_insn_push(jit_function_t func, jit_value_t value) +{ + jit_type_t type; + if(!value) + { + return 0; + } + type = jit_type_promote_int(jit_type_normalize(jit_value_get_type(value))); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + return create_unary_note(func, JIT_OP_PUSH_INT, value); + } + /* Not reached */ + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + return create_unary_note(func, JIT_OP_PUSH_LONG, value); + } + /* Not reached */ + + case JIT_TYPE_FLOAT32: + { + return create_unary_note(func, JIT_OP_PUSH_FLOAT32, value); + } + /* Not reached */ + + case JIT_TYPE_FLOAT64: + { + return create_unary_note(func, JIT_OP_PUSH_FLOAT64, value); + } + /* Not reached */ + + case JIT_TYPE_NFLOAT: + { + return create_unary_note(func, JIT_OP_PUSH_NFLOAT, value); + } + /* Not reached */ + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + return create_unary_note(func, JIT_OP_PUSH_STRUCT, value); + } + /* Not reached */ + } + return 1; +} + +/*@ + * @deftypefun int jit_insn_pop_stack (jit_function_t func, jit_nint num_items) + * Pop @code{num_items} items from the function call stack. You normally + * wouldn't call this yourself - it is used by CPU back ends to clean up + * the stack after calling a subroutine. The size of an item is specific + * to the back end (it could be bytes, words, or some other measurement). + * @end deftypefun +@*/ +int jit_insn_pop_stack(jit_function_t func, jit_nint num_items) +{ + return create_unary_note + (func, JIT_OP_POP_STACK, + jit_value_create_nint_constant(func, jit_type_nint, num_items)); +} + +/*@ + * @deftypefun int jit_insn_return (jit_function_t func, jit_value_t value) + * Output an instruction to return @code{value} as the function's result. + * If @code{value} is NULL, then the function is assumed to return + * @code{void}. If the function returns a structure, this will copy + * the value into the memory at the structure return address. + * @end deftypefun +@*/ +int jit_insn_return(jit_function_t func, jit_value_t value) +{ + jit_type_t type; + + /* Ensure that we have a builder for this function */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* This function has an ordinary return path */ + func->builder->ordinary_return = 1; + + /* Output an appropriate instruction to return to the caller */ + type = jit_type_normalize(jit_type_get_return(func->signature)); + type = jit_type_promote_int(type); + if(!value || type == jit_type_void) + { + /* Handle "finally" clauses if necessary */ + if(!handle_finally(func, JIT_OP_PREPARE_FOR_RETURN)) + { + return 0; + } + + /* This function returns "void" */ + if(!create_noarg_note(func, JIT_OP_RETURN)) + { + return 0; + } + } + else + { + /* Convert the value into the desired return type */ + value = jit_insn_convert(func, value, type, 0); + if(!value) + { + return 0; + } + + /* Handle "finally" clauses if necessary */ + if(!handle_finally(func, JIT_OP_PREPARE_FOR_RETURN)) + { + return 0; + } + + /* Create the "return" instruction */ + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + if(!create_unary_note(func, JIT_OP_RETURN_INT, value)) + { + return 0; + } + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + if(!create_unary_note(func, JIT_OP_RETURN_LONG, value)) + { + return 0; + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(!create_unary_note(func, JIT_OP_RETURN_FLOAT32, value)) + { + return 0; + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(!create_unary_note(func, JIT_OP_RETURN_FLOAT64, value)) + { + return 0; + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(!create_unary_note(func, JIT_OP_RETURN_NFLOAT, value)) + { + return 0; + } + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + jit_value_t return_ptr = jit_value_get_struct_pointer(func); + if(return_ptr) + { + /* Return the structure via the supplied pointer */ + /* TODO */ + } + else + { + /* Return the structure via registers */ + if(!create_unary_note + (func, JIT_OP_RETURN_SMALL_STRUCT, value)) + { + break; + } + } + } + break; + } + } + + /* Mark the current block as "ends in dead" */ + func->builder->current_block->ends_in_dead = 1; + + /* Start a new block just after the "return" instruction */ + return jit_insn_label(func, 0); +} + +/*@ + * @deftypefun int jit_insn_default_return (jit_function_t func) + * Add an instruction to return a default value if control reaches this point. + * This is typically used at the end of a function to ensure that all paths + * return to the caller. Returns zero if out of memory, 1 if a default + * return was added, and 2 if a default return was not needed. + * + * Note: if this returns 1, but the function signature does not return + * @code{void}, then it indicates that a higher-level language error + * has occurred and the function should be abandoned. + * @end deftypefun +@*/ +int jit_insn_default_return(jit_function_t func) +{ + jit_block_t current; + jit_insn_t last; + + /* Ensure that we have a builder for this function */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* If the last block ends in an unconditional branch, or is dead, + then we don't need to add a default return */ + current = func->builder->current_block; + if(current->ends_in_dead) + { + /* The block ends in dead */ + return 2; + } + last = _jit_block_get_last(current); + if(last) + { + if(last->opcode == JIT_OP_BR) + { + /* This block ends in an unconditional branch */ + return 2; + } + } + else if(!(current->entered_via_top) && !(current->entered_via_branch)) + { + /* This block is never entered */ + return 2; + } + + /* Add a simple "void" return to terminate the function */ + return jit_insn_return(func, 0); +} + +/*@ + * @deftypefun int jit_insn_throw (jit_function_t func, jit_value_t value) + * Throw a pointer @code{value} as an exception object. This can also + * be used to "rethrow" an object from a catch handler that is not + * interested in handling the exception. + * @end deftypefun +@*/ +int jit_insn_throw(jit_function_t func, jit_value_t value) +{ + jit_block_t block; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + func->builder->may_throw = 1; + func->builder->non_leaf = 1; /* May have to call out to throw */ + if(!create_unary_note(func, JIT_OP_THROW, value)) + { + return 0; + } + func->builder->current_block->ends_in_dead = 1; + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + func->builder->current_block = block; + return 1; +} + +/*@ + * @deftypefun jit_value_t jit_insn_get_call_stack (jit_function_t func) + * Get an object that represents the current position in the code, + * and all of the functions that are currently on the call stack. + * This is equivalent to calling @code{jit_exception_get_stack_trace}, + * and is normally used just prior to @code{jit_insn_throw} to record + * the location of the exception that is being thrown. + * @end deftypefun +@*/ +jit_value_t jit_insn_get_call_stack(jit_function_t func) +{ + jit_type_t type; + jit_value_t value; + + /* Create a signature prototype for "void *()" */ + type = jit_type_create_signature + (jit_abi_cdecl, jit_type_void_ptr, 0, 0, 1); + if(!type) + { + return 0; + } + + /* Call "jit_exception_get_stack_trace" to obtain the stack trace */ + value = jit_insn_call_native + (func, "jit_exception_get_stack_trace", + (void *)jit_exception_get_stack_trace, type, 0, 0, 0); + + /* Clean up and exit */ + jit_type_free(type); + return value; +} + +/*@ + * @deftypefun int jit_insn_start_try (jit_function_t func, {jit_label_t *} catch_label, {jit_label_t *} finally_label, int finally_on_fault) + * Start an exception-handling @code{try} block at the current position + * within @code{func}. + * + * Whenever an exception occurs in the block, execution will jump to + * @code{catch_label}. When execution exits the @code{try} block's scope, + * @code{finally_label} will be called. If @code{finally_on_fault} is + * non-zero, then @code{finally_label} will be called for exceptions, + * but not when control exits the @code{try} block normally. + * + * The @code{finally_label} parameter may be NULL if the @code{try} block + * does not have a @code{finally} clause associated with it. + * + * All of the blocks between @code{jit_insn_start_try} and + * @code{jit_insn_start_catch} are covered by the @code{catch} + * clause and the @code{finally} clause. Blocks between + * @code{jit_insn_start_catch} and @code{jit_insn_end_try} are + * covered by the @code{finally} clause. + * + * Calls to @code{jit_insn_branch}, @code{jit_insn_return}, + * @code{jit_insn_throw} and @code{jit_insn_rethrow} will cause + * additional code to be output to invoke the relevant @code{finally} + * clauses. + * + * The destinations for @code{jit_insn_branch_if} and + * @code{jit_insn_branch_if_not} must never be outside the current + * @code{finally} context. You should always use @code{jit_insn_branch} + * to branch out of @code{finally} contexts. + * + * Note: you don't need to output calls to @code{finally} clauses + * yourself as @code{libjit} can detect when it is necessary on its own. + * @end deftypefun +@*/ +int jit_insn_start_try + (jit_function_t func, jit_label_t *catch_label, + jit_label_t *finally_label, int finally_on_fault) +{ + jit_block_eh_t eh; + jit_block_t block; + + /* Ensure that we have a function builder */ + if(!_jit_function_ensure_builder(func) || !catch_label) + { + return 0; + } + + /* Allocate the label numbers */ + if(*catch_label == jit_label_undefined) + { + *catch_label = (func->builder->next_label)++; + } + if(finally_label && *finally_label == jit_label_undefined) + { + *finally_label = (func->builder->next_label)++; + } + + /* This function has a "try" block. This flag helps native + back ends know when they must be careful about global + register allocation */ + func->builder->has_try = 1; + + /* Anything with a finally handler makes the function not a leaf, + because we may need to do a native "call" to invoke the handler */ + if(finally_label) + { + func->builder->non_leaf = 1; + } + + /* Create a new exception handling context and populate it */ + eh = jit_cnew(struct jit_block_eh); + if(!eh) + { + return 0; + } + eh->parent = func->builder->current_handler; + eh->next = func->builder->exception_handlers; + func->builder->exception_handlers = eh; + eh->catch_label = *catch_label; + eh->finally_label = (finally_label ? *finally_label : jit_label_undefined); + eh->finally_on_fault = finally_on_fault; + eh->in_try_body = 1; + func->builder->current_handler = eh; + + /* Start a new block, for the body of the "try" */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + return 1; +} + +/*@ + * @deftypefun jit_value_t jit_insn_start_catch (jit_function_t func, {jit_label_t *} catch_label) + * Start the @code{catch} clause for the currently active @code{try} + * block. Returns a pointer @code{value} that indicates the exception + * that is thrown. + * + * There can be only one @code{catch} clause per @code{try} block. + * The front end is responsible for outputting instructions that + * inspect the exception object, determine if it is appropriate to + * the clause, and then either handle it or rethrow it. + * + * Every @code{try} block must have an associated @code{catch} clause, + * even if all it does is rethrow the exception immediately. Without a + * @code{catch} clause, the correct @code{finally} logic will not be + * performed for thrown exceptions. + * @end deftypefun +@*/ +jit_value_t jit_insn_start_catch + (jit_function_t func, jit_label_t *catch_label) +{ + jit_block_eh_t eh; + jit_block_eh_t new_eh; + + /* Ensure that we have a function builder */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* Create a new exception handler to indicate that we are in a "catch" */ + eh = func->builder->current_handler; + if(!eh) + { + return 0; + } + new_eh = jit_cnew(struct jit_block_eh); + if(!new_eh) + { + return 0; + } + new_eh->parent = eh->parent; + new_eh->next = func->builder->exception_handlers; + func->builder->exception_handlers = new_eh; + if(eh->finally_label != jit_label_undefined) + { + /* We will need a mini "catch" block to call the "finally" + clause when an exception occurs within the "catch" */ + new_eh->catch_label = (func->builder->next_label)++; + } + else + { + new_eh->catch_label = jit_label_undefined; + } + new_eh->finally_label = eh->finally_label; + new_eh->finally_on_fault = eh->finally_on_fault; + new_eh->in_try_body = 0; + func->builder->current_handler = new_eh; + + /* Start a new block for the "catch" clause */ + if(!jit_insn_label(func, catch_label)) + { + return 0; + } + + /* Output an instruction to notice the caught value */ + return create_dest_note(func, JIT_OP_ENTER_CATCH, jit_type_void_ptr); +} + +/*@ + * @deftypefun int jit_insn_end_try (jit_function_t func) + * Mark the end of the @code{try} block and its associated @code{catch} + * clause. This is normally followed by a call to + * @code{jit_insn_start_finally} to define the @code{finally} clause. + * @end deftypefun +@*/ +int jit_insn_end_try(jit_function_t func) +{ + jit_block_eh_t eh; + jit_block_t block; + jit_value_t value; + + /* Ensure that we have a function builder */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* Get the current exception handling context */ + eh = func->builder->current_handler; + if(!eh) + { + return 0; + } + + /* We may need another mini "catch" block to invoke the "finally" + clause for exceptions that happen during the "catch" */ + if(eh->catch_label != jit_label_undefined) + { + /* TODO: not quite right */ + if(!jit_insn_label(func, &(eh->catch_label))) + { + return 0; + } + value = create_dest_note(func, JIT_OP_ENTER_CATCH, jit_type_void_ptr); + if(!value) + { + return 0; + } + if(!jit_insn_throw(func, value)) + { + return 0; + } + } + + /* Pop the current exception context */ + func->builder->current_handler = eh->parent; + + /* Start a new block, covered by the next outer "try" context */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + return 1; +} + +/*@ + * @deftypefun int jit_insn_start_finally (jit_function_t func, {jit_label_t *} finally_label) + * Start the @code{finally} clause for the preceding @code{try} block. + * @end deftypefun +@*/ +int jit_insn_start_finally(jit_function_t func, jit_label_t *finally_label) +{ + if(!jit_insn_label(func, finally_label)) + { + return 0; + } + return create_noarg_note(func, JIT_OP_ENTER_FINALLY); +} + +/*@ + * @deftypefun int jit_insn_return_from_finally (jit_function_t func) + * Return from the @code{finally} clause to where it was called from. + * This is usually the last instruction in a @code{finally} clause. + * @end deftypefun +@*/ +int jit_insn_return_from_finally(jit_function_t func) +{ + jit_block_t block; + + /* Mark the end of the "finally" clause */ + if(!create_noarg_note(func, JIT_OP_LEAVE_FINALLY)) + { + return 0; + } + + /* The current block ends in a dead instruction */ + func->builder->current_block->ends_in_dead = 1; + + /* Create a new block for the following code */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + return 1; +} + +/*@ + * @deftypefun jit_value_t jit_insn_start_filter (jit_function_t func, {jit_label_t *} label, jit_type_t type) + * Define the start of a filter. Filters are embedded subroutines within + * functions that are used to filter exceptions in @code{catch} blocks. + * + * A filter subroutine takes a single argument (usually a pointer) and + * returns a single result (usually a boolean). The filter has complete + * access to the local variables of the function, and can use any of + * them in the filtering process. + * + * This function returns a temporary value of the specified @code{type}, + * indicating the parameter that is supplied to the filter. + * @end deftypefun +@*/ +jit_value_t jit_insn_start_filter + (jit_function_t func, jit_label_t *label, jit_type_t type) +{ + /* Set a label at this point to start a new block */ + if(!jit_insn_label(func, label)) + { + return 0; + } + + /* Create a note to load the filter's parameter at runtime */ + return create_dest_note(func, JIT_OP_ENTER_FILTER, type); +} + +/*@ + * @deftypefun int jit_insn_return_from_filter (jit_function_t func, jit_value_t value) + * Return from a filter subroutine with the specified @code{value} as + * its result. + * @end deftypefun +@*/ +int jit_insn_return_from_filter(jit_function_t func, jit_value_t value) +{ + jit_block_t block; + + /* Mark the end of the "filter" clause */ + if(!create_unary_note(func, JIT_OP_LEAVE_FILTER, value)) + { + return 0; + } + + /* The current block ends in a dead instruction */ + func->builder->current_block->ends_in_dead = 1; + + /* Create a new block for the following code */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + return 1; +} + +/*@ + * @deftypefun jit_value_t jit_insn_call_filter (jit_function_t func, {jit_label_t *} label, jit_value_t value, jit_type_t type) + * Call the filter subroutine at @code{label}, passing it @code{value} as + * its argument. This function returns a value of the specified + * @code{type}, indicating the filter's result. + * @end deftypefun +@*/ +jit_value_t jit_insn_call_filter + (jit_function_t func, jit_label_t *label, + jit_value_t value, jit_type_t type) +{ + jit_block_t block; + jit_insn_t insn; + + /* Ensure that we have a function builder */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* Allocate the label number if necessary */ + if(*label == jit_label_undefined) + { + *label = (func->builder->next_label)++; + } + + /* Calling a filter makes the function not a leaf because we may + need to do a native "call" to invoke the handler */ + func->builder->non_leaf = 1; + + /* Add a new branch instruction to branch to the filter */ + insn = _jit_block_add_insn(func->builder->current_block); + if(!insn) + { + return 0; + } + jit_value_ref(func, value); + insn->opcode = (short)JIT_OP_CALL_FILTER; + insn->flags = JIT_INSN_DEST_IS_LABEL; + insn->dest = (jit_value_t)(*label); + insn->value1 = value; + + /* Create a new block, and add the filter return logic to it */ + block = _jit_block_create(func, 0); + if(!block) + { + return 0; + } + block->entered_via_top = 1; + return create_dest_note(func, JIT_OP_CALL_FILTER_RETURN, type); +} + +/*@ + * @deftypefun void jit_insn_iter_init ({jit_insn_iter_t *} iter, jit_block_t block) + * Initialize an iterator to point to the first instruction in @code{block}. + * @end deftypefun +@*/ +void jit_insn_iter_init(jit_insn_iter_t *iter, jit_block_t block) +{ + iter->block = block; + iter->posn = block->first_insn; +} + +/*@ + * @deftypefun void jit_insn_iter_init_last ({jit_insn_iter_t *} iter, jit_block_t block) + * Initialize an iterator to point to the last instruction in @code{block}. + * @end deftypefun +@*/ +void jit_insn_iter_init_last(jit_insn_iter_t *iter, jit_block_t block) +{ + iter->block = block; + iter->posn = block->last_insn + 1; +} + +/*@ + * @deftypefun jit_insn_t jit_insn_iter_next ({jit_insn_iter_t *} iter) + * Get the next instruction in an iterator's block. Returns NULL + * when there are no further instructions in the block. + * @end deftypefun +@*/ +jit_insn_t jit_insn_iter_next(jit_insn_iter_t *iter) +{ + if(iter->posn <= iter->block->last_insn) + { + return iter->block->func->builder->insns[(iter->posn)++]; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_insn_t jit_insn_iter_previous ({jit_insn_iter_t *} iter) + * Get the previous instruction in an iterator's block. Returns NULL + * when there are no further instructions in the block. + * @end deftypefun +@*/ +jit_insn_t jit_insn_iter_previous(jit_insn_iter_t *iter) +{ + if(iter->posn > iter->block->first_insn) + { + return iter->block->func->builder->insns[--(iter->posn)]; + } + else + { + return 0; + } +} diff --git a/jit/jit-internal.h b/jit/jit-internal.h new file mode 100644 index 0000000..a81f8c7 --- /dev/null +++ b/jit/jit-internal.h @@ -0,0 +1,616 @@ +/* + * jit-internal.h - Internal definitions for the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_INTERNAL_H +#define _JIT_INTERNAL_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Determine what kind of Win32 system we are running on. + */ +#if defined(__CYGWIN__) || defined(__CYGWIN32__) +#define JIT_WIN32_CYGWIN 1 +#define JIT_WIN32_PLATFORM 1 +#elif defined(_WIN32) || defined(WIN32) +#define JIT_WIN32_NATIVE 1 +#define JIT_WIN32_PLATFORM 1 +#endif + +/* + * We need the apply rules for "jit_redirector_size". + */ +#include "jit-apply-func.h" + +/* + * Include the thread routines. + */ +#include "jit-thread.h" + +/* + * The following is some macro magic that attempts to detect + * the best alignment to use on the target platform. The final + * value, "JIT_BEST_ALIGNMENT", will be a compile-time constant. + */ + +#define _JIT_ALIGN_CHECK_TYPE(type,name) \ + struct _JIT_align_##name { \ + jit_sbyte pad; \ + type field; \ + } + +#define _JIT_ALIGN_FOR_TYPE(name) \ + ((unsigned)(&(((struct _JIT_align_##name *)0)->field))) + +#define _JIT_ALIGN_MAX(a,b) \ + ((a) > (b) ? (a) : (b)) + +#define _JIT_ALIGN_MAX3(a,b,c) \ + (_JIT_ALIGN_MAX((a), _JIT_ALIGN_MAX((b), (c)))) + +_JIT_ALIGN_CHECK_TYPE(jit_sbyte, sbyte); +_JIT_ALIGN_CHECK_TYPE(jit_short, short); +_JIT_ALIGN_CHECK_TYPE(jit_int, int); +_JIT_ALIGN_CHECK_TYPE(jit_long, long); +_JIT_ALIGN_CHECK_TYPE(jit_ptr, ptr); +_JIT_ALIGN_CHECK_TYPE(jit_float32, float); +_JIT_ALIGN_CHECK_TYPE(jit_float64, double); +_JIT_ALIGN_CHECK_TYPE(jit_nfloat, nfloat); + +#if defined(JIT_X86) +/* Sometimes the code below guesses wrong on Win32 platforms */ +#define JIT_BEST_ALIGNMENT 4 +#else +#define JIT_BEST_ALIGNMENT \ + _JIT_ALIGN_MAX(_JIT_ALIGN_MAX3(_JIT_ALIGN_FOR_TYPE(int), \ + _JIT_ALIGN_FOR_TYPE(long), \ + _JIT_ALIGN_FOR_TYPE(ptr)), \ + _JIT_ALIGN_MAX3(_JIT_ALIGN_FOR_TYPE(float), \ + _JIT_ALIGN_FOR_TYPE(double), \ + _JIT_ALIGN_FOR_TYPE(nfloat))) +#endif + +/* + * Get the alignment values for various system types. + * These will also be compile-time constants. + */ +#define JIT_ALIGN_SBYTE _JIT_ALIGN_FOR_TYPE(sbyte) +#define JIT_ALIGN_UBYTE _JIT_ALIGN_FOR_TYPE(sbyte) +#define JIT_ALIGN_SHORT _JIT_ALIGN_FOR_TYPE(short) +#define JIT_ALIGN_USHORT _JIT_ALIGN_FOR_TYPE(short) +#define JIT_ALIGN_CHAR _JIT_ALIGN_FOR_TYPE(char) +#define JIT_ALIGN_INT _JIT_ALIGN_FOR_TYPE(int) +#define JIT_ALIGN_UINT _JIT_ALIGN_FOR_TYPE(int) +#define JIT_ALIGN_NINT _JIT_ALIGN_FOR_TYPE(ptr) +#define JIT_ALIGN_NUINT _JIT_ALIGN_FOR_TYPE(ptr) +#define JIT_ALIGN_LONG _JIT_ALIGN_FOR_TYPE(long) +#define JIT_ALIGN_ULONG _JIT_ALIGN_FOR_TYPE(long) +#define JIT_ALIGN_FLOAT32 _JIT_ALIGN_FOR_TYPE(float) +#define JIT_ALIGN_FLOAT64 _JIT_ALIGN_FOR_TYPE(double) +#define JIT_ALIGN_NFLOAT _JIT_ALIGN_FOR_TYPE(nfloat) +#define JIT_ALIGN_PTR _JIT_ALIGN_FOR_TYPE(ptr) + +/* + * Structure of a memory pool. + */ +typedef struct jit_pool_block *jit_pool_block_t; +struct jit_pool_block +{ + jit_pool_block_t next; + char data[1]; +}; +typedef struct +{ + unsigned int elem_size; + unsigned int elems_per_block; + unsigned int elems_in_last; + jit_pool_block_t blocks; + void *free_list; + +} jit_memory_pool; + +/* + * Initialize a memory pool. + */ +void _jit_memory_pool_init(jit_memory_pool *pool, unsigned int elem_size); +#define jit_memory_pool_init(pool,type) \ + _jit_memory_pool_init((pool), sizeof(type)) + +/* + * Free the contents of a memory pool. + */ +void _jit_memory_pool_free(jit_memory_pool *pool, jit_meta_free_func func); +#define jit_memory_pool_free(pool,func) _jit_memory_pool_free((pool), (func)) + +/* + * Allocate an item from a memory pool. + */ +void *_jit_memory_pool_alloc(jit_memory_pool *pool); +#define jit_memory_pool_alloc(pool,type) \ + ((type *)_jit_memory_pool_alloc((pool))) + +/* + * Deallocate an item back to a memory pool. + */ +void _jit_memory_pool_dealloc(jit_memory_pool *pool, void *item); +#define jit_memory_pool_dealloc(pool,item) \ + (_jit_memory_pool_dealloc((pool), (item))) + +/* + * Storage for metadata. + */ +struct _jit_meta +{ + int type; + void *data; + jit_meta_free_func free_data; + jit_meta_t next; + jit_function_t pool_owner; +}; + +/* + * Exception handling information that is attached to blocks. + */ +typedef struct jit_block_eh *jit_block_eh_t; +struct jit_block_eh +{ + jit_block_eh_t parent; + jit_block_eh_t next; + jit_label_t catch_label; + jit_label_t finally_label; + int finally_on_fault : 1; + int in_try_body : 1; +}; + +/* + * Internal structure of a block. + */ +struct _jit_block +{ + jit_function_t func; + jit_label_t label; + int first_insn; + int last_insn; + jit_block_t next; + jit_block_t prev; + jit_meta_t meta; + jit_block_eh_t block_eh; + int entered_via_top : 1; + int entered_via_branch : 1; + int ends_in_dead : 1; + void *address; + void *fixup_list; +}; + +/* + * Internal structure of a value. + */ +struct _jit_value +{ + jit_block_t block; + jit_type_t type; + short is_temporary : 1; + short is_local : 1; + short is_volatile : 1; + short is_addressable : 1; + short is_constant : 1; + short is_nint_constant : 1; + short has_address : 1; + short free_address : 1; + short in_register : 1; + short in_frame : 1; + short live : 1; + short next_use : 1; + short has_frame_offset : 1; + short reg; + jit_nint address; + jit_nint frame_offset; +}; +#define JIT_INVALID_FRAME_OFFSET ((jit_nint)0x7FFFFFFF) + +/* + * Free the structures that are associated with a value. + */ +void _jit_value_free(void *value); + +/* + * Internal structure of an instruction. + */ +struct _jit_insn +{ + short opcode; + short flags; + jit_value_t dest; + jit_value_t value1; + jit_value_t value2; +}; + +/* + * Instruction flags. + */ +#define JIT_INSN_DEST_LIVE 0x0001 +#define JIT_INSN_DEST_NEXT_USE 0x0002 +#define JIT_INSN_VALUE1_LIVE 0x0004 +#define JIT_INSN_VALUE1_NEXT_USE 0x0008 +#define JIT_INSN_VALUE2_LIVE 0x0010 +#define JIT_INSN_VALUE2_NEXT_USE 0x0020 +#define JIT_INSN_LIVENESS_FLAGS 0x003F +#define JIT_INSN_DEST_IS_LABEL 0x0040 +#define JIT_INSN_DEST_IS_FUNCTION 0x0080 +#define JIT_INSN_DEST_IS_NATIVE 0x0100 +#define JIT_INSN_DEST_OTHER_FLAGS 0x01C0 +#define JIT_INSN_VALUE1_IS_NAME 0x0200 +#define JIT_INSN_VALUE1_OTHER_FLAGS 0x0200 +#define JIT_INSN_VALUE2_IS_SIGNATURE 0x0400 +#define JIT_INSN_VALUE2_OTHER_FLAGS 0x0400 +#define JIT_INSN_DEST_IS_VALUE 0x0800 + +/* + * Information that is associated with a function for building + * the instructions and values. This structure can be discarded + * once the function has been fully compiled. + */ +typedef struct _jit_builder *jit_builder_t; +struct _jit_builder +{ + /* List of blocks within this function */ + jit_block_t first_block; + jit_block_t last_block; + + /* The next block label to be allocated */ + jit_label_t next_label; + + /* Mapping from label numbers to blocks */ + jit_block_t *label_blocks; + jit_label_t max_label_blocks; + + /* Entry point for the function */ + jit_block_t entry; + + /* The current block that is being constructed */ + jit_block_t current_block; + + /* Exception handlers for the function */ + jit_block_eh_t exception_handlers; + jit_block_eh_t current_handler; + + /* Flag that is set to indicate that this function is not a leaf */ + int non_leaf : 1; + + /* Flag that indicates if we've seen code that may throw an exception */ + int may_throw : 1; + + /* Flag that indicates if the function has an ordinary return */ + int ordinary_return : 1; + + /* Flag that indicates if we have "try" blocks */ + int has_try : 1; + + /* List of all instructions in this function */ + jit_insn_t *insns; + int num_insns; + int max_insns; + + /* Memory pools that contain values, instructions, and metadata blocks */ + jit_memory_pool value_pool; + jit_memory_pool insn_pool; + jit_memory_pool meta_pool; + + /* Common constants that have been cached */ + jit_value_t null_constant; + jit_value_t zero_constant; + + /* The values for the parameters, structure return, and parent frame */ + jit_value_t *param_values; + jit_value_t struct_return; + jit_value_t parent_frame; + + /* The value that holds the exception frame information for a callout */ + jit_value_t eh_frame_info; + + /* Metadata that is stored only while the function is being built */ + jit_meta_t meta; + + /* Current size of the local variable frame (used by the back end) */ + jit_nint frame_size; + +}; + +/* + * Internal structure of a function. + */ +struct _jit_function +{ + /* The context that the function is associated with */ + jit_context_t context; + jit_function_t next; + jit_function_t prev; + + /* Containing function in a nested context */ + jit_function_t nested_parent; + + /* Metadata that survives once the builder is discarded */ + jit_meta_t meta; + + /* The signature for this function */ + jit_type_t signature; + + /* The builder information for this function */ + jit_builder_t builder; + + /* Flag bits for this function */ + int is_recompilable : 1; + int no_throw : 1; + int no_return : 1; + int optimization_level : 8; + int volatile is_compiled; + + /* The entry point for the function's compiled code */ + void * volatile entry_point; + + /* The closure/vtable entry point for the function's compiled code */ + void * volatile closure_entry; + + /* The function to call to perform on-demand compilation */ + jit_on_demand_func on_demand; + +#ifdef jit_redirector_size + /* Buffer that contains the redirector for this function. + Redirectors are used to support on-demand compilation */ + unsigned char redirector[jit_redirector_size]; +#endif +}; + +/* + * Ensure that there is a builder associated with a function. + */ +int _jit_function_ensure_builder(jit_function_t func); + +/* + * Free the builder associated with a function. + */ +void _jit_function_free_builder(jit_function_t func); + +/* + * Destroy all memory associated with a function. + */ +void _jit_function_destroy(jit_function_t func); + +/* + * Compute value liveness and "next use" information for a function. + */ +void _jit_function_compute_liveness(jit_function_t func); + +/* + * Compile a function on-demand. Returns the entry point. + */ +void *_jit_function_compile_on_demand(jit_function_t func); + +/* + * Internal structure of a context. + */ +struct _jit_context +{ + /* Lock that controls access to the building process */ + jit_mutex_t builder_lock; + + /* Lock that controls access to the function code cache */ + jit_mutex_t cache_lock; + + /* List of functions that are currently registered with the context */ + jit_function_t functions; + jit_function_t last_function; + + /* Metadata that is associated with the context */ + jit_meta_t meta; + + /* The context's function code cache */ + struct jit_cache *cache; +}; + +/* + * Backtrace control structure, for managing stack traces. + * These structures must be allocated on the stack. + */ +typedef struct jit_backtrace *jit_backtrace_t; +struct jit_backtrace +{ + jit_backtrace_t parent; + void *pc; + void *catch_pc; + void *sp; + void *security_object; + jit_meta_free_func free_security_object; +}; + +/* + * Push a new backtrace onto the stack. The fields in "trace" are filled in. + */ +void _jit_backtrace_push + (jit_backtrace_t trace, void *pc, void *catch_pc, void *sp); + +/* + * Pop the top-most backtrace item. + */ +void _jit_backtrace_pop(void); + +/* + * Reset the backtrace stack to "trace". Used in exception catch + * blocks to fix up the backtrace information. + */ +void _jit_backtrace_set(jit_backtrace_t trace); + +/* + * Control information that is associated with a thread. + */ +struct jit_thread_control +{ + void *last_exception; + jit_exception_func exception_handler; + jit_backtrace_t backtrace_head; +}; + +/* + * Get the function code cache for a context, creating it if necessary. + */ +struct jit_cache *_jit_context_get_cache(jit_context_t context); + +/* + * Initialize the block list for a function. + */ +int _jit_block_init(jit_function_t func); + +/* + * Free all blocks that are associated with a function. + */ +void _jit_block_free(jit_function_t func); + +/* + * Create a new block and associate it with a function. + */ +jit_block_t _jit_block_create(jit_function_t func, jit_label_t *label); + +/* + * Record the label mapping for a block. + */ +int _jit_block_record_label(jit_block_t block); + +/* + * Add an instruction to a block. + */ +jit_insn_t _jit_block_add_insn(jit_block_t block); + +/* + * Get the last instruction in a block. NULL if the block is empty. + */ +jit_insn_t _jit_block_get_last(jit_block_t block); + +/* + * Free one element in a metadata list. + */ +void _jit_meta_free_one(void *meta); + +/* + * Determine if a NULL pointer check is redundant. The specified + * iterator is assumed to be positioned one place beyond the + * "check_null" instruction that we are testing. + */ +int _jit_insn_check_is_redundant(const jit_insn_iter_t *iter); + +/* + * Get the correct opcode to use for a "load" instruction, + * starting at a particular opcode base. We assume that the + * instructions are laid out as "sbyte", "ubyte", "short", + * "ushort", "int", "long", "float32", "float64", "nfloat", + * and "struct". + */ +int _jit_load_opcode(int base_opcode, jit_type_t type, + jit_value_t value, int no_temps); + +/* + * Get the correct opcode to use for a "store" instruction. + * We assume that the instructions are laid out as "byte", + * "short", "int", "long", "float32", "float64", "nfloat", + * and "struct". + */ +int _jit_store_opcode(int base_opcode, int small_base, jit_type_t type); + +/* + * Type descriptor kinds. + */ +#define JIT_TYPE_VOID 0 +#define JIT_TYPE_SBYTE 1 +#define JIT_TYPE_UBYTE 2 +#define JIT_TYPE_SHORT 3 +#define JIT_TYPE_USHORT 4 +#define JIT_TYPE_INT 5 +#define JIT_TYPE_UINT 6 +#define JIT_TYPE_NINT 7 +#define JIT_TYPE_NUINT 8 +#define JIT_TYPE_LONG 9 +#define JIT_TYPE_ULONG 10 +#define JIT_TYPE_FLOAT32 11 +#define JIT_TYPE_FLOAT64 12 +#define JIT_TYPE_NFLOAT 13 +#define JIT_TYPE_MAX_PRIMITIVE JIT_TYPE_NFLOAT +#define JIT_TYPE_STRUCT 14 +#define JIT_TYPE_UNION 15 +#define JIT_TYPE_SIGNATURE 16 +#define JIT_TYPE_PTR 17 +#define JIT_TYPE_FIRST_TAGGED 32 + +/* + * Internal structure of a type descriptor. + */ +struct jit_component +{ + jit_type_t type; + jit_nuint offset; + char *name; +}; +struct _jit_type +{ + unsigned int ref_count; + int kind : 19; + int abi : 8; + int is_fixed : 1; + int layout_flags : 4; + jit_nuint size; + jit_nuint alignment; + jit_type_t sub_type; + unsigned int num_components; + struct jit_component components[1]; +}; +struct jit_tagged_type +{ + struct _jit_type type; + void *data; + jit_meta_free_func free_func; + +}; + +/* + * Pre-defined type descriptors. + */ +extern struct _jit_type const _jit_type_void_def; +extern struct _jit_type const _jit_type_sbyte_def; +extern struct _jit_type const _jit_type_ubyte_def; +extern struct _jit_type const _jit_type_short_def; +extern struct _jit_type const _jit_type_ushort_def; +extern struct _jit_type const _jit_type_int_def; +extern struct _jit_type const _jit_type_uint_def; +extern struct _jit_type const _jit_type_nint_def; +extern struct _jit_type const _jit_type_nuint_def; +extern struct _jit_type const _jit_type_long_def; +extern struct _jit_type const _jit_type_ulong_def; +extern struct _jit_type const _jit_type_float32_def; +extern struct _jit_type const _jit_type_float64_def; +extern struct _jit_type const _jit_type_nfloat_def; +extern struct _jit_type const _jit_type_void_ptr_def; + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_INTERNAL_H */ diff --git a/jit/jit-interp.cpp b/jit/jit-interp.cpp new file mode 100644 index 0000000..fae091f --- /dev/null +++ b/jit/jit-interp.cpp @@ -0,0 +1,4503 @@ +/* + * jit-interp.cpp - Fallback interpreter implementation. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +/* + +This file must be compiled with a C++ compiler, because it uses +C++ exceptions to manage JIT exception throws. It is otherwise +straight vanilla ANSI C. + +*/ + +#include "jit-interp.h" +#include "jit-rules.h" +#include "jit-memory.h" +#include +#if HAVE_ALLOCA_H + #include +#endif +#ifdef JIT_WIN32_PLATFORM + #include + #ifndef alloca + #define alloca _alloca + #endif +#endif + +#if defined(JIT_BACKEND_INTERP) + +/* + * Macros that help with the definition of the interpreter switch loop. + */ +#define VMSWITCH(pc) \ + for(;;) \ + { \ + switch((int)*((jit_nint *)(pc))) +#define VMSWITCHEND \ + } +#define VMCASE(opcode) \ + case opcode +#define VMBREAK \ + break + +/* + * Modify the program counter and stack pointer. + */ +#define VM_MODIFY_PC_AND_STACK(pcmod,stkmod) \ + do { \ + pc += (jit_nint)(int)(pcmod); \ + stacktop += (jit_nint)(int)(stkmod); \ + } while (0) +#define VM_MODIFY_STACK(stkmod) \ + do { \ + stacktop += (jit_nint)(int)(stkmod); \ + } while (0) + +/* + * Fetch arguments of various types from the instruction stream. + */ +#define VM_NINT_ARG (((jit_nint *)(pc))[1]) +#define VM_NINT_ARG2 (((jit_nint *)(pc))[2]) +#define VM_NINT_ARG3 (((jit_nint *)(pc))[3]) +#define VM_BR_TARGET (pc + VM_NINT_ARG) + +/* + * Fetch stack items from various positions. + */ +#define VM_STK_INT0 (stacktop[0].int_value) +#define VM_STK_INT1 (stacktop[1].int_value) +#define VM_STK_INTP (stacktop[-1].int_value) +#define VM_STK_UINT0 (stacktop[0].uint_value) +#define VM_STK_UINT1 (stacktop[1].uint_value) +#define VM_STK_UINTP (stacktop[-1].uint_value) +#define VM_STK_LONG0 (stacktop[0].long_value) +#define VM_STK_LONG1 (stacktop[1].long_value) +#define VM_STK_LONGP (stacktop[-1].long_value) +#define VM_STK_ULONG0 (stacktop[0].ulong_value) +#define VM_STK_ULONG1 (stacktop[1].ulong_value) +#define VM_STK_ULONGP (stacktop[-1].ulong_value) +#define VM_STK_FLOAT320 (stacktop[0].float32_value) +#define VM_STK_FLOAT321 (stacktop[1].float32_value) +#define VM_STK_FLOAT32P (stacktop[-1].float32_value) +#define VM_STK_FLOAT640 (stacktop[0].float64_value) +#define VM_STK_FLOAT641 (stacktop[1].float64_value) +#define VM_STK_FLOAT64P (stacktop[-1].float64_value) +#define VM_STK_NFLOAT0 (stacktop[0].nfloat_value) +#define VM_STK_NFLOAT1 (stacktop[1].nfloat_value) +#define VM_STK_NFLOATP (stacktop[-1].nfloat_value) +#define VM_STK_PTR0 (stacktop[0].ptr_value) +#define VM_STK_PTR1 (stacktop[1].ptr_value) +#define VM_STK_PTRP (stacktop[-1].ptr_value) +#define VM_STK_PTRP2 (stacktop[-2].ptr_value) + +/* + * Apply a relative adjustment to a pointer and cast to a specific type. + */ +#define VM_REL(type,ptr) \ + ((type *)(((unsigned char *)(ptr)) + VM_NINT_ARG)) + +/* + * Get the address of an argument or local variable at a particular offset. + */ +#define VM_ARG(type) \ + ((type *)(((jit_item *)args) + VM_NINT_ARG)) +#define VM_LOC(type) \ + ((type *)(((jit_item *)frame) + VM_NINT_ARG)) + +/* + * Handle the return value from a function that reports a builtin exception. + */ +#define VM_BUILTIN(value) \ + do { \ + builtin_exception = (value); \ + if(builtin_exception < JIT_RESULT_OK) \ + { \ + goto handle_builtin; \ + } \ + } while (0) + +/* + * Call "jit_apply" from the interpreter, to invoke a native function. + */ +static void apply_from_interpreter + (jit_type_t signature, void *func, + jit_item *args, unsigned int num_fixed_args, + void *return_area) +{ + unsigned int num_params; + unsigned int param; + jit_type_t type; + void **apply_args; + + /* Allocate space for the apply arguments and initialize them */ + num_params = jit_type_num_params(signature); + apply_args = (void **)alloca(sizeof(void *) * num_params); + for(param = 0; param < num_params; ++param) + { + type = jit_type_normalize(jit_type_get_param(signature, param)); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + { + apply_args[param] = + ((unsigned char *)args) + _jit_int_lowest_byte(); + ++args; + } + break; + + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + { + apply_args[param] = + ((unsigned char *)args) + _jit_int_lowest_short(); + ++args; + } + break; + + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + case JIT_TYPE_FLOAT32: + case JIT_TYPE_FLOAT64: + case JIT_TYPE_NFLOAT: + { + apply_args[param] = args; + ++args; + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + apply_args[param] = args; + args += JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type)); + } + break; + + default: + { + /* Shouldn't happen, but do something sane */ + apply_args[param] = args; + } + break; + } + } + + /* Apply the function */ + jit_apply(signature, func, apply_args, num_fixed_args, return_area); +} + +void _jit_run_function(jit_function_interp *func, jit_item *args, + jit_item *return_area) +{ + jit_item *frame; + jit_item *stacktop; + void **pc; + jit_int builtin_exception; + jit_nint temparg; + void *tempptr; + void *tempptr2; + jit_function_t call_func; + struct jit_backtrace call_trace; + void *entry; + void *exception_object = 0; + void *handler; + + /* Set up the stack frame for this function */ + frame = (jit_item *)alloca(func->frame_size); + stacktop = frame + func->working_area; + frame = stacktop; + + /* Get the initial program counter */ + pc = jit_function_interp_entry_pc(func); + + /* Enter the instruction dispatch loop */ + VMSWITCH(pc) + { + /****************************************************************** + * Simple opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_NOP): + { + /* Nothing to do except move on to the next instruction */ + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Conversion opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_TRUNC_SBYTE): + { + /* Truncate an integer to a signed 8-bit value */ + VM_STK_INT0 = (jit_int)(jit_sbyte)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_TRUNC_UBYTE): + { + /* Truncate an integer to an unsigned 8-bit value */ + VM_STK_INT0 = (jit_int)(jit_ubyte)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_TRUNC_SHORT): + { + /* Truncate an integer to a signed 16-bit value */ + VM_STK_INT0 = (jit_int)(jit_short)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_TRUNC_USHORT): + { + /* Truncate an integer to an unsigned 16-bit value */ + VM_STK_INT0 = (jit_int)(jit_ushort)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_TRUNC_INT): + { + /* Truncate an integer to a signed 32-bit value */ + /* In the interpreter, this is a NOP */ + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_TRUNC_UINT): + { + /* Truncate an integer to an unsigned 32-bit value */ + /* In the interpreter, this is a NOP */ + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_SBYTE): + { + /* Truncate an integer to a signed 8-bit value, and check */ + VM_BUILTIN(jit_int_to_sbyte_ovf(&VM_STK_INT0, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_UBYTE): + { + /* Truncate an integer to an unsigned 8-bit value, and check */ + VM_BUILTIN(jit_int_to_ubyte_ovf(&VM_STK_INT0, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_SHORT): + { + /* Truncate an integer to a signed 16-bit value, and check */ + VM_BUILTIN(jit_int_to_short_ovf(&VM_STK_INT0, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_USHORT): + { + /* Truncate an integer to an unsigned 16-bit value, and check */ + VM_BUILTIN(jit_int_to_ushort_ovf(&VM_STK_INT0, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_INT): + { + /* Truncate an integer to a signed 32-bit value, and check */ + VM_BUILTIN(jit_uint_to_int_ovf(&VM_STK_INT0, VM_STK_UINT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_UINT): + { + /* Truncate an integer to an unsigned 32-bit value, and check */ + VM_BUILTIN(jit_int_to_uint_ovf(&VM_STK_UINT0, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOW_WORD): + { + /* Fetch the low word of a 64-bit value */ + VM_STK_UINT0 = (jit_uint)VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_EXPAND_INT): + { + /* Expand a signed 32-bit value to a 64-bit value */ + VM_STK_LONG0 = (jit_long)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_EXPAND_UINT): + { + /* Expand an unsigned 32-bit value to a 64-bit value */ + VM_STK_ULONG0 = (jit_ulong)VM_STK_UINT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_LOW_WORD): + { + /* Fetch the low word of a 64-bit value, and check */ + VM_BUILTIN(jit_long_to_uint_ovf(&VM_STK_UINT0, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_SIGNED_LOW_WORD): + { + /* Fetch the signed low word of a 64-bit value, and check */ + VM_BUILTIN(jit_long_to_int_ovf(&VM_STK_INT0, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_LONG): + { + /* Convert unsigned 64-bit into signed, and check */ + VM_BUILTIN(jit_ulong_to_long_ovf(&VM_STK_LONG0, VM_STK_ULONG0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_ULONG): + { + /* Convert signed 64-bit into unsigned, and check */ + VM_BUILTIN(jit_long_to_ulong_ovf(&VM_STK_ULONG0, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOAT_TO_INT): + { + /* Convert native float into 32-bit signed integer */ + VM_STK_INT0 = jit_nfloat_to_int(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOAT_TO_UINT): + { + /* Convert native float into 32-bit unsigned integer */ + VM_STK_UINT0 = jit_nfloat_to_uint(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOAT_TO_LONG): + { + /* Convert native float into 64-bit signed integer */ + VM_STK_LONG0 = jit_nfloat_to_long(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOAT_TO_ULONG): + { + /* Convert native float into 64-bit unsigned integer */ + VM_STK_ULONG0 = jit_nfloat_to_ulong(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_NFLOAT_TO_INT): + { + /* Convert native float into 32-bit signed integer, and check */ + VM_BUILTIN(jit_nfloat_to_int_ovf(&VM_STK_INT0, VM_STK_NFLOAT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_NFLOAT_TO_UINT): + { + /* Convert native float into 32-bit unsigned integer, and check */ + VM_BUILTIN(jit_nfloat_to_uint_ovf(&VM_STK_UINT0, VM_STK_NFLOAT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_NFLOAT_TO_LONG): + { + /* Convert native float into 64-bit signed integer, and check */ + VM_BUILTIN(jit_nfloat_to_long_ovf(&VM_STK_LONG0, VM_STK_NFLOAT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_NFLOAT_TO_ULONG): + { + /* Convert native float into 64-bit unsigned integer, and check */ + VM_BUILTIN(jit_nfloat_to_ulong_ovf(&VM_STK_ULONG0, VM_STK_NFLOAT0)); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_INT_TO_NFLOAT): + { + /* Convert 32-bit signed integer into native float */ + VM_STK_NFLOAT0 = jit_int_to_nfloat(VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_UINT_TO_NFLOAT): + { + /* Convert 32-bit unsigned integer into native float */ + VM_STK_NFLOAT0 = jit_uint_to_nfloat(VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LONG_TO_NFLOAT): + { + /* Convert 64-bit signed integer into native float */ + VM_STK_NFLOAT0 = jit_long_to_nfloat(VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_ULONG_TO_NFLOAT): + { + /* Convert 64-bit unsigned integer into native float */ + VM_STK_NFLOAT0 = jit_ulong_to_nfloat(VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOAT_TO_FLOAT32): + { + /* Convert native float into 32-bit float */ + VM_STK_FLOAT320 = jit_nfloat_to_float32(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOAT_TO_FLOAT64): + { + /* Convert native float into 64-bit float */ + VM_STK_FLOAT640 = jit_nfloat_to_float64(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FLOAT32_TO_NFLOAT): + { + /* Convert 32-bit float into native float */ + VM_STK_NFLOAT0 = jit_float32_to_nfloat(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FLOAT64_TO_NFLOAT): + { + /* Convert 64-bit float into native float */ + VM_STK_NFLOAT0 = jit_float64_to_nfloat(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Arithmetic opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_IADD): + { + /* Add signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 + VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IADD_OVF): + { + /* Add signed 32-bit integers, and check */ + VM_BUILTIN(jit_int_add_ovf + (&VM_STK_INT1, VM_STK_INT1, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IADD_OVF_UN): + { + /* Add unsigned 32-bit integers, and check */ + VM_BUILTIN(jit_uint_add_ovf + (&VM_STK_UINT1, VM_STK_UINT1, VM_STK_UINT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ISUB): + { + /* Subtract signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 - VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ISUB_OVF): + { + /* Subtract signed 32-bit integers, and check */ + VM_BUILTIN(jit_int_sub_ovf + (&VM_STK_INT1, VM_STK_INT1, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ISUB_OVF_UN): + { + /* Subtract unsigned 32-bit integers, and check */ + VM_BUILTIN(jit_uint_sub_ovf + (&VM_STK_UINT1, VM_STK_UINT1, VM_STK_UINT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IMUL): + { + /* Multiply signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 * VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IMUL_OVF): + { + /* Multiply signed 32-bit integers, and check */ + VM_BUILTIN(jit_int_mul_ovf + (&VM_STK_INT1, VM_STK_INT1, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IMUL_OVF_UN): + { + /* Multiply unsigned 32-bit integers, and check */ + VM_BUILTIN(jit_uint_mul_ovf + (&VM_STK_UINT1, VM_STK_UINT1, VM_STK_UINT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IDIV): + { + /* Divide signed 32-bit integers */ + VM_BUILTIN(jit_int_div + (&VM_STK_INT1, VM_STK_INT1, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IDIV_UN): + { + /* Divide unsigned 32-bit integers */ + VM_BUILTIN(jit_uint_div + (&VM_STK_UINT1, VM_STK_UINT1, VM_STK_UINT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IREM): + { + /* Remainder signed 32-bit integers */ + VM_BUILTIN(jit_int_rem + (&VM_STK_INT1, VM_STK_INT1, VM_STK_INT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IREM_UN): + { + /* Remainder unsigned 32-bit integers */ + VM_BUILTIN(jit_uint_rem + (&VM_STK_UINT1, VM_STK_UINT1, VM_STK_UINT0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_INEG): + { + /* Negate signed 32-bit integer */ + VM_STK_INT0 = -VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LADD): + { + /* Add signed 64-bit integers */ + VM_STK_LONG1 = VM_STK_LONG1 + VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LADD_OVF): + { + /* Add signed 64-bit integers, and check */ + VM_BUILTIN(jit_long_add_ovf + (&VM_STK_LONG1, VM_STK_LONG1, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LADD_OVF_UN): + { + /* Add unsigned 64-bit integers, and check */ + VM_BUILTIN(jit_ulong_add_ovf + (&VM_STK_ULONG1, VM_STK_ULONG1, VM_STK_ULONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LSUB): + { + /* Subtract signed 64-bit integers */ + VM_STK_LONG1 = VM_STK_LONG1 - VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LSUB_OVF): + { + /* Subtract signed 64-bit integers, and check */ + VM_BUILTIN(jit_long_sub_ovf + (&VM_STK_LONG1, VM_STK_LONG1, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LSUB_OVF_UN): + { + /* Subtract unsigned 64-bit integers, and check */ + VM_BUILTIN(jit_ulong_sub_ovf + (&VM_STK_ULONG1, VM_STK_ULONG1, VM_STK_ULONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMUL): + { + /* Multiply signed 64-bit integers */ + VM_STK_LONG1 = VM_STK_LONG1 * VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMUL_OVF): + { + /* Multiply signed 64-bit integers, and check */ + VM_BUILTIN(jit_long_mul_ovf + (&VM_STK_LONG1, VM_STK_LONG1, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMUL_OVF_UN): + { + /* Multiply unsigned 64-bit integers, and check */ + VM_BUILTIN(jit_ulong_mul_ovf + (&VM_STK_ULONG1, VM_STK_ULONG1, VM_STK_ULONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LDIV): + { + /* Divide signed 64-bit integers */ + VM_BUILTIN(jit_long_div + (&VM_STK_LONG1, VM_STK_LONG1, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LDIV_UN): + { + /* Divide unsigned 64-bit integers */ + VM_BUILTIN(jit_ulong_div + (&VM_STK_ULONG1, VM_STK_ULONG1, VM_STK_ULONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LREM): + { + /* Remainder signed 64-bit integers */ + VM_BUILTIN(jit_long_rem + (&VM_STK_LONG1, VM_STK_LONG1, VM_STK_LONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LREM_UN): + { + /* Remainder unsigned 64-bit integers */ + VM_BUILTIN(jit_ulong_rem + (&VM_STK_ULONG1, VM_STK_ULONG1, VM_STK_ULONG0)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LNEG): + { + /* Negate signed 64-bit integer */ + VM_STK_LONG0 = -VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FADD): + { + /* Add 32-bit floats */ + VM_STK_FLOAT321 = VM_STK_FLOAT321 + VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FSUB): + { + /* Subtract 32-bit floats */ + VM_STK_FLOAT321 = VM_STK_FLOAT321 - VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FMUL): + { + /* Multiply 32-bit floats */ + VM_STK_FLOAT321 = VM_STK_FLOAT321 * VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FDIV): + { + /* Divide 32-bit floats */ + VM_STK_FLOAT321 = VM_STK_FLOAT321 / VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FREM): + { + /* Remainder 32-bit floats */ + VM_STK_FLOAT321 = jit_float32_rem + (VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FREM_IEEE): + { + /* Remainder 32-bit floats, with IEEE rules */ + VM_STK_FLOAT321 = jit_float32_ieee_rem + (VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FNEG): + { + /* Negate 32-bit float */ + VM_STK_FLOAT320 = -VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DADD): + { + /* Add 64-bit floats */ + VM_STK_FLOAT641 = VM_STK_FLOAT641 + VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DSUB): + { + /* Subtract 64-bit floats */ + VM_STK_FLOAT641 = VM_STK_FLOAT641 - VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DMUL): + { + /* Multiply 64-bit floats */ + VM_STK_FLOAT641 = VM_STK_FLOAT641 * VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DDIV): + { + /* Divide 64-bit floats */ + VM_STK_FLOAT641 = VM_STK_FLOAT641 / VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DREM): + { + /* Remainder 64-bit floats */ + VM_STK_FLOAT641 = jit_float64_rem + (VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DREM_IEEE): + { + /* Remainder 64-bit floats, with IEEE rules */ + VM_STK_FLOAT641 = jit_float64_ieee_rem + (VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DNEG): + { + /* Negate 64-bit float */ + VM_STK_FLOAT640 = -VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFADD): + { + /* Add native floats */ + VM_STK_NFLOAT1 = VM_STK_NFLOAT1 + VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFSUB): + { + /* Subtract native floats */ + VM_STK_NFLOAT1 = VM_STK_NFLOAT1 - VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFMUL): + { + /* Multiply native floats */ + VM_STK_NFLOAT1 = VM_STK_NFLOAT1 * VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFDIV): + { + /* Divide native floats */ + VM_STK_NFLOAT1 = VM_STK_NFLOAT1 / VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFREM): + { + /* Remainder native floats */ + VM_STK_NFLOAT1 = jit_nfloat_rem + (VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFREM_IEEE): + { + /* Remainder native floats, with IEEE rules */ + VM_STK_NFLOAT1 = jit_nfloat_ieee_rem + (VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFNEG): + { + /* Negate native float */ + VM_STK_NFLOAT0 = -VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Bitwise opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_IAND): + { + /* Bitwise and signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 & VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IOR): + { + /* Bitwise or signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 | VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IXOR): + { + /* Bitwise xor signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 ^ VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_INOT): + { + /* Bitwise not signed 32-bit integers */ + VM_STK_INT0 = ~VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_ISHL): + { + /* Shift left signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 << (VM_STK_UINT0 & 0x1F); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ISHR): + { + /* Shift right signed 32-bit integers */ + VM_STK_INT1 = VM_STK_INT1 >> (VM_STK_UINT0 & 0x1F); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ISHR_UN): + { + /* Shift right unsigned 32-bit integers */ + VM_STK_UINT1 = VM_STK_UINT1 >> (VM_STK_UINT0 & 0x1F); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LAND): + { + /* Bitwise and signed 64-bit integers */ + VM_STK_LONG1 = VM_STK_LONG1 & VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LOR): + { + /* Bitwise or signed 64-bit integers */ + VM_STK_LONG1 = VM_STK_LONG1 | VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LXOR): + { + /* Bitwise xor signed 64-bit integers */ + VM_STK_LONG1 = VM_STK_LONG1 ^ VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LNOT): + { + /* Bitwise not signed 64-bit integers */ + VM_STK_LONG0 = ~VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LSHL): + { + /* Shift left signed 64-bit integers */ + VM_STK_LONG1 = (VM_STK_LONG1 << (VM_STK_UINT0 & 0x3F)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LSHR): + { + /* Shift right signed 64-bit integers */ + VM_STK_LONG1 = (VM_STK_LONG1 >> (VM_STK_UINT0 & 0x3F)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LSHR_UN): + { + /* Shift right signed 64-bit integers */ + VM_STK_ULONG1 = (VM_STK_ULONG1 >> (VM_STK_UINT0 & 0x3F)); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + /****************************************************************** + * Branch opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_BR): + { + /* Unconditional branch */ + pc = VM_BR_TARGET; + } + VMBREAK; + + VMCASE(JIT_OP_BR_IFALSE): + { + /* Branch if signed 32-bit integer is false */ + if(VM_STK_INT0 == 0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(1); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 1); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_ITRUE): + { + /* Branch if signed 32-bit integer is true */ + if(VM_STK_INT0 != 0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(1); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 1); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_IEQ): + { + /* Branch if signed 32-bit integers are equal */ + if(VM_STK_INT1 == VM_STK_INT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_INE): + { + /* Branch if signed 32-bit integers are not equal */ + if(VM_STK_INT1 != VM_STK_INT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_ILT): + { + /* Branch if signed 32-bit integers are less than */ + if(VM_STK_INT1 < VM_STK_INT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_ILT_UN): + { + /* Branch if unsigned 32-bit integers are less than */ + if(VM_STK_UINT1 < VM_STK_UINT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_ILE): + { + /* Branch if signed 32-bit integers are less than or equal */ + if(VM_STK_INT1 <= VM_STK_INT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_ILE_UN): + { + /* Branch if unsigned 32-bit integers are less than or equal */ + if(VM_STK_UINT1 <= VM_STK_UINT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_IGT): + { + /* Branch if signed 32-bit integers are greater than */ + if(VM_STK_INT1 > VM_STK_INT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_IGT_UN): + { + /* Branch if unsigned 32-bit integers are greater than */ + if(VM_STK_UINT1 > VM_STK_UINT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_IGE): + { + /* Branch if signed 32-bit integers are greater than or equal */ + if(VM_STK_INT1 >= VM_STK_INT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_IGE_UN): + { + /* Branch if unsigned 32-bit integers are greater than or equal */ + if(VM_STK_UINT1 >= VM_STK_UINT0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LFALSE): + { + /* Branch if signed 64-bit integer is false */ + if(VM_STK_LONG0 == 0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(1); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 1); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LTRUE): + { + /* Branch if signed 64-bit integer is true */ + if(VM_STK_LONG0 != 0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(1); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 1); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LEQ): + { + /* Branch if signed 64-bit integers are equal */ + if(VM_STK_LONG1 == VM_STK_LONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LNE): + { + /* Branch if signed 64-bit integers are not equal */ + if(VM_STK_LONG1 != VM_STK_LONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LLT): + { + /* Branch if signed 64-bit integers are less than */ + if(VM_STK_LONG1 < VM_STK_LONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LLT_UN): + { + /* Branch if unsigned 64-bit integers are less than */ + if(VM_STK_ULONG1 < VM_STK_ULONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LLE): + { + /* Branch if signed 64-bit integers are less than or equal */ + if(VM_STK_LONG1 <= VM_STK_LONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LLE_UN): + { + /* Branch if unsigned 64-bit integers are less than or equal */ + if(VM_STK_ULONG1 <= VM_STK_ULONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LGT): + { + /* Branch if signed 64-bit integers are greater than */ + if(VM_STK_LONG1 > VM_STK_LONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LGT_UN): + { + /* Branch if unsigned 64-bit integers are greater than */ + if(VM_STK_ULONG1 > VM_STK_ULONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LGE): + { + /* Branch if signed 64-bit integers are greater than or equal */ + if(VM_STK_LONG1 >= VM_STK_LONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_LGE_UN): + { + /* Branch if unsigned 64-bit integers are greater than or equal */ + if(VM_STK_ULONG1 >= VM_STK_ULONG0) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FEQ): + { + /* Branch if 32-bit floats are equal */ + if(jit_float32_eq(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FNE): + { + /* Branch if 32-bit floats are not equal */ + if(jit_float32_ne(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FLT): + { + /* Branch if 32-bit floats are less than */ + if(jit_float32_lt(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FLE): + { + /* Branch if 32-bit floats are less than or equal */ + if(jit_float32_le(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FGT): + { + /* Branch if 32-bit floats are greater than */ + if(jit_float32_gt(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FGE): + { + /* Branch if 32-bit floats are greater than or equal */ + if(jit_float32_ge(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FEQ_INV): + { + /* Branch if 32-bit floats are equal; invert nan test */ + if(!jit_float32_ne(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FNE_INV): + { + /* Branch if 32-bit floats are not equal; invert nan test */ + if(!jit_float32_eq(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FLT_INV): + { + /* Branch if 32-bit floats are less than; invert nan test */ + if(!jit_float32_ge(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FLE_INV): + { + /* Branch if 32-bit floats are less or equal; invert nan test */ + if(!jit_float32_gt(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FGT_INV): + { + /* Branch if 32-bit floats are greater than; invert nan test */ + if(!jit_float32_le(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_FGE_INV): + { + /* Branch if 32-bit floats are greater or equal; invert nan test */ + if(!jit_float32_lt(VM_STK_FLOAT321, VM_STK_FLOAT320)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DEQ): + { + /* Branch if 64-bit floats are equal */ + if(jit_float64_eq(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DNE): + { + /* Branch if 64-bit floats are not equal */ + if(jit_float64_ne(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DLT): + { + /* Branch if 64-bit floats are less than */ + if(jit_float64_lt(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DLE): + { + /* Branch if 64-bit floats are less than or equal */ + if(jit_float64_le(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DGT): + { + /* Branch if 64-bit floats are greater than */ + if(jit_float64_gt(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DGE): + { + /* Branch if 64-bit floats are greater than or equal */ + if(jit_float64_ge(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DEQ_INV): + { + /* Branch if 64-bit floats are equal; invert nan test */ + if(!jit_float64_ne(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DNE_INV): + { + /* Branch if 64-bit floats are not equal; invert nan test */ + if(!jit_float64_eq(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DLT_INV): + { + /* Branch if 64-bit floats are less than; invert nan test */ + if(!jit_float64_ge(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DLE_INV): + { + /* Branch if 64-bit floats are less or equal; invert nan test */ + if(!jit_float64_gt(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DGT_INV): + { + /* Branch if 64-bit floats are greater than; invert nan test */ + if(!jit_float64_le(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_DGE_INV): + { + /* Branch if 64-bit floats are greater or equal; invert nan test */ + if(!jit_float64_lt(VM_STK_FLOAT641, VM_STK_FLOAT640)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFEQ): + { + /* Branch if native floats are equal */ + if(jit_nfloat_eq(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFNE): + { + /* Branch if native floats are not equal */ + if(jit_nfloat_ne(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFLT): + { + /* Branch if native floats are less than */ + if(jit_nfloat_lt(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFLE): + { + /* Branch if native floats are less than or equal */ + if(jit_nfloat_le(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFGT): + { + /* Branch if native floats are greater than */ + if(jit_nfloat_gt(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFGE): + { + /* Branch if native floats are greater than or equal */ + if(jit_nfloat_ge(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFEQ_INV): + { + /* Branch if native floats are equal; invert nan test */ + if(!jit_nfloat_ne(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFNE_INV): + { + /* Branch if native floats are not equal; invert nan test */ + if(!jit_nfloat_eq(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFLT_INV): + { + /* Branch if native floats are less than; invert nan test */ + if(!jit_nfloat_ge(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFLE_INV): + { + /* Branch if native floats are less or equal; invert nan test */ + if(!jit_nfloat_gt(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFGT_INV): + { + /* Branch if native floats are greater than; invert nan test */ + if(!jit_nfloat_le(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + VMCASE(JIT_OP_BR_NFGE_INV): + { + /* Branch if native floats are greater or equal; invert nan test */ + if(!jit_nfloat_lt(VM_STK_NFLOAT1, VM_STK_NFLOAT0)) + { + pc = VM_BR_TARGET; + VM_MODIFY_STACK(2); + } + else + { + VM_MODIFY_PC_AND_STACK(2, 2); + } + } + VMBREAK; + + /****************************************************************** + * Comparison opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_ICMP): + { + /* Compare signed 32-bit integers */ + VM_STK_INT1 = jit_int_cmp(VM_STK_INT1, VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ICMP_UN): + { + /* Compare unsigned 32-bit integers */ + VM_STK_UINT1 = jit_uint_cmp(VM_STK_UINT1, VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LCMP): + { + /* Compare signed 64-bit integers */ + VM_STK_INT1 = jit_long_cmp(VM_STK_LONG1, VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LCMP_UN): + { + /* Compare unsigned 64-bit integers */ + VM_STK_INT1 = jit_long_cmp(VM_STK_ULONG1, VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FCMPL): + { + /* Compare 32-bit floats, with less nan */ + VM_STK_INT1 = jit_float32_cmpl(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FCMPG): + { + /* Compare 32-bit floats, with greater nan */ + VM_STK_INT1 = jit_float32_cmpg(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DCMPL): + { + /* Compare 64-bit floats, with less nan */ + VM_STK_INT1 = jit_float64_cmpl(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DCMPG): + { + /* Compare 64-bit floats, with greater nan */ + VM_STK_INT1 = jit_float64_cmpg(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFCMPL): + { + /* Compare native floats, with less nan */ + VM_STK_INT1 = jit_float64_cmpl(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFCMPG): + { + /* Compare native floats, with greater nan */ + VM_STK_INT1 = jit_float64_cmpg(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IEQ): + { + /* Compare signed 32-bit integers for equal */ + VM_STK_INT1 = (jit_int)(VM_STK_INT1 == VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_INE): + { + /* Compare signed 32-bit integers for not equal */ + VM_STK_INT1 = (jit_int)(VM_STK_INT1 != VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ILT): + { + /* Compare signed 32-bit integers for less than */ + VM_STK_INT1 = (jit_int)(VM_STK_INT1 < VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ILT_UN): + { + /* Compare unsigned 32-bit integers for less than */ + VM_STK_INT1 = (jit_int)(VM_STK_UINT1 < VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ILE): + { + /* Compare signed 32-bit integers for less than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_INT1 <= VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ILE_UN): + { + /* Compare unsigned 32-bit integers for less than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_UINT1 <= VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IGT): + { + /* Compare signed 32-bit integers for greater than */ + VM_STK_INT1 = (jit_int)(VM_STK_INT1 > VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IGT_UN): + { + /* Compare unsigned 32-bit integers for greater than */ + VM_STK_INT1 = (jit_int)(VM_STK_UINT1 > VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IGE): + { + /* Compare signed 32-bit integers for greater than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_INT1 >= VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IGE_UN): + { + /* Compare unsigned 32-bit integers for greater than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_UINT1 >= VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LEQ): + { + /* Compare signed 64-bit integers for equal */ + VM_STK_INT1 = (jit_int)(VM_STK_LONG1 == VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LNE): + { + /* Compare signed 64-bit integers for not equal */ + VM_STK_INT1 = (jit_int)(VM_STK_LONG1 != VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LLT): + { + /* Compare signed 64-bit integers for less than */ + VM_STK_INT1 = (jit_int)(VM_STK_LONG1 < VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LLT_UN): + { + /* Compare unsigned 64-bit integers for less than */ + VM_STK_INT1 = (jit_int)(VM_STK_ULONG1 < VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LLE): + { + /* Compare signed 64-bit integers for less than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_LONG1 <= VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LLE_UN): + { + /* Compare unsigned 64-bit integers for less than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_ULONG1 <= VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LGT): + { + /* Compare signed 64-bit integers for greater than */ + VM_STK_INT1 = (jit_int)(VM_STK_LONG1 > VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LGT_UN): + { + /* Compare unsigned 64-bit integers for greater than */ + VM_STK_INT1 = (jit_int)(VM_STK_ULONG1 > VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LGE): + { + /* Compare signed 64-bit integers for greater than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_LONG1 >= VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LGE_UN): + { + /* Compare unsigned 64-bit integers for greater than or equal */ + VM_STK_INT1 = (jit_int)(VM_STK_ULONG1 >= VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FEQ): + { + /* Compare 32-bit floats for equal */ + VM_STK_INT1 = jit_float32_eq(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FNE): + { + /* Compare 32-bit floats for not equal */ + VM_STK_INT1 = jit_float32_ne(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FLT): + { + /* Compare 32-bit floats for less than */ + VM_STK_INT1 = jit_float32_lt(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FLE): + { + /* Compare 32-bit floats for less than or equal */ + VM_STK_INT1 = jit_float32_le(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FGT): + { + /* Compare 32-bit floats for greater than */ + VM_STK_INT1 = jit_float32_gt(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FGE): + { + /* Compare 32-bit floats for greater than or equal */ + VM_STK_INT1 = jit_float32_ge(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FEQ_INV): + { + /* Compare 32-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float32_ne(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FNE_INV): + { + /* Compare 32-bit floats for not equal; invert nan test */ + VM_STK_INT1 = !jit_float32_eq(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FLT_INV): + { + /* Compare 32-bit floats for less than; invert nan test */ + VM_STK_INT1 = !jit_float32_ge(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FLE_INV): + { + /* Compare 32-bit floats for less than or equal; invert nan test */ + VM_STK_INT1 = !jit_float32_gt(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FGT_INV): + { + /* Compare 32-bit floats for greater than; invert nan test */ + VM_STK_INT1 = !jit_float32_le(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FGE_INV): + { + /* Compare 32-bit floats for greater or equal; invert nan test */ + VM_STK_INT1 = !jit_float32_lt(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DEQ): + { + /* Compare 64-bit floats for equal */ + VM_STK_INT1 = jit_float64_eq(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DNE): + { + /* Compare 64-bit floats for not equal */ + VM_STK_INT1 = jit_float64_ne(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DLT): + { + /* Compare 64-bit floats for less than */ + VM_STK_INT1 = jit_float64_lt(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DLE): + { + /* Compare 64-bit floats for less than or equal */ + VM_STK_INT1 = jit_float64_le(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DGT): + { + /* Compare 64-bit floats for greater than */ + VM_STK_INT1 = jit_float64_gt(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DGE): + { + /* Compare 64-bit floats for greater than or equal */ + VM_STK_INT1 = jit_float64_ge(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DEQ_INV): + { + /* Compare 64-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float64_ne(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DNE_INV): + { + /* Compare 64-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float64_eq(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DLT_INV): + { + /* Compare 64-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float64_ge(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DLE_INV): + { + /* Compare 64-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float64_gt(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DGT_INV): + { + /* Compare 64-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float64_le(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DGE_INV): + { + /* Compare 64-bit floats for equal; invert nan test */ + VM_STK_INT1 = !jit_float64_lt(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFEQ): + { + /* Compare native floats for equal */ + VM_STK_INT1 = jit_nfloat_eq(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFNE): + { + /* Compare native floats for not equal */ + VM_STK_INT1 = jit_nfloat_ne(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFLT): + { + /* Compare native floats for less than */ + VM_STK_INT1 = jit_nfloat_lt(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFLE): + { + /* Compare native floats for less than or equal */ + VM_STK_INT1 = jit_nfloat_le(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFGT): + { + /* Compare native floats for greater than */ + VM_STK_INT1 = jit_nfloat_gt(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFGE): + { + /* Compare native floats for greater than or equal */ + VM_STK_INT1 = jit_nfloat_ge(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFEQ_INV): + { + /* Compare native floats for equal; invert nan test */ + VM_STK_INT1 = !jit_nfloat_ne(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFNE_INV): + { + /* Compare native floats for not equal; invert nan test */ + VM_STK_INT1 = !jit_nfloat_eq(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFLT_INV): + { + /* Compare native floats for less than; invert nan test */ + VM_STK_INT1 = !jit_nfloat_ge(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFLE_INV): + { + /* Compare native floats for less than or equal; invert nan test */ + VM_STK_INT1 = !jit_nfloat_gt(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFGT_INV): + { + /* Compare native floats for greater than; invert nan test */ + VM_STK_INT1 = !jit_nfloat_le(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFGE_INV): + { + /* Compare native floats for greater or equal; invert nan test */ + VM_STK_INT1 = !jit_nfloat_lt(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IS_FNAN): + { + /* Check a 32-bit float for "not a number" */ + VM_STK_INT0 = jit_float32_is_nan(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_FINF): + { + /* Check a 32-bit float for "infinity" */ + VM_STK_INT0 = jit_float32_is_inf(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_FFINITE): + { + /* Check a 32-bit float for "finite" */ + VM_STK_INT0 = jit_float32_is_finite(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_DNAN): + { + /* Check a 64-bit float for "not a number" */ + VM_STK_INT0 = jit_float64_is_nan(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_DINF): + { + /* Check a 64-bit float for "infinity" */ + VM_STK_INT0 = jit_float64_is_inf(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_DFINITE): + { + /* Check a 64-bit float for "finite" */ + VM_STK_INT0 = jit_float64_is_finite(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_NFNAN): + { + /* Check a native float for "not a number" */ + VM_STK_INT0 = jit_nfloat_is_nan(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_NFINF): + { + /* Check a native float for "infinity" */ + VM_STK_INT0 = jit_nfloat_is_inf(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IS_NFFINITE): + { + /* Check a native float for "finite" */ + VM_STK_INT0 = jit_nfloat_is_finite(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Mathematical functions. + ******************************************************************/ + + VMCASE(JIT_OP_FACOS): + { + /* Compute 32-bit float "acos" */ + VM_STK_FLOAT320 = jit_float32_acos(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FASIN): + { + /* Compute 32-bit float "asin" */ + VM_STK_FLOAT320 = jit_float32_asin(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FATAN): + { + /* Compute 32-bit float "atan" */ + VM_STK_FLOAT320 = jit_float32_atan(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FATAN2): + { + /* Compute 32-bit float "atan2" */ + VM_STK_FLOAT321 = jit_float32_atan2 + (VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FCEIL): + { + /* Compute 32-bit float "ceil" */ + VM_STK_FLOAT320 = jit_float32_ceil(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FCOS): + { + /* Compute 32-bit float "cos" */ + VM_STK_FLOAT320 = jit_float32_cos(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FCOSH): + { + /* Compute 32-bit float "cosh" */ + VM_STK_FLOAT320 = jit_float32_cosh(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FEXP): + { + /* Compute 32-bit float "exp" */ + VM_STK_FLOAT320 = jit_float32_exp(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FFLOOR): + { + /* Compute 32-bit float "floor" */ + VM_STK_FLOAT320 = jit_float32_floor(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FLOG): + { + /* Compute 32-bit float "log" */ + VM_STK_FLOAT320 = jit_float32_log(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FLOG10): + { + /* Compute 32-bit float "log10" */ + VM_STK_FLOAT320 = jit_float32_log10(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FPOW): + { + /* Compute 32-bit float "pow" */ + VM_STK_FLOAT321 = jit_float32_pow + (VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FRINT): + { + /* Compute 32-bit float "rint" */ + VM_STK_FLOAT320 = jit_float32_rint(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FROUND): + { + /* Compute 32-bit float "round" */ + VM_STK_FLOAT320 = jit_float32_round(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FSIN): + { + /* Compute 32-bit float "sin" */ + VM_STK_FLOAT320 = jit_float32_sin(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FSINH): + { + /* Compute 32-bit float "sinh" */ + VM_STK_FLOAT320 = jit_float32_sinh(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FSQRT): + { + /* Compute 32-bit float "sqrt" */ + VM_STK_FLOAT320 = jit_float32_sqrt(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FTAN): + { + /* Compute 32-bit float "tan" */ + VM_STK_FLOAT320 = jit_float32_tan(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FTANH): + { + /* Compute 32-bit float "tanh" */ + VM_STK_FLOAT320 = jit_float32_tanh(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DACOS): + { + /* Compute 64-bit float "acos" */ + VM_STK_FLOAT640 = jit_float64_acos(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DASIN): + { + /* Compute 64-bit float "asin" */ + VM_STK_FLOAT640 = jit_float64_asin(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DATAN): + { + /* Compute 64-bit float "atan" */ + VM_STK_FLOAT640 = jit_float64_atan(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DATAN2): + { + /* Compute 64-bit float "atan2" */ + VM_STK_FLOAT641 = jit_float64_atan2 + (VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DCEIL): + { + /* Compute 64-bit float "ceil" */ + VM_STK_FLOAT640 = jit_float64_ceil(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DCOS): + { + /* Compute 64-bit float "cos" */ + VM_STK_FLOAT640 = jit_float64_cos(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DCOSH): + { + /* Compute 64-bit float "cosh" */ + VM_STK_FLOAT640 = jit_float64_cosh(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DEXP): + { + /* Compute 64-bit float "exp" */ + VM_STK_FLOAT640 = jit_float64_exp(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DFLOOR): + { + /* Compute 64-bit float "floor" */ + VM_STK_FLOAT640 = jit_float64_floor(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DLOG): + { + /* Compute 64-bit float "log" */ + VM_STK_FLOAT640 = jit_float64_log(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DLOG10): + { + /* Compute 64-bit float "log10" */ + VM_STK_FLOAT640 = jit_float64_log10(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DPOW): + { + /* Compute 64-bit float "pow" */ + VM_STK_FLOAT641 = jit_float64_pow + (VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DRINT): + { + /* Compute 64-bit float "rint" */ + VM_STK_FLOAT640 = jit_float64_rint(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DROUND): + { + /* Compute 64-bit float "round" */ + VM_STK_FLOAT640 = jit_float64_round(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DSIN): + { + /* Compute 64-bit float "sin" */ + VM_STK_FLOAT640 = jit_float64_sin(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DSINH): + { + /* Compute 64-bit float "sinh" */ + VM_STK_FLOAT640 = jit_float64_sinh(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DSQRT): + { + /* Compute 64-bit float "sqrt" */ + VM_STK_FLOAT640 = jit_float64_sqrt(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DTAN): + { + /* Compute 64-bit float "tan" */ + VM_STK_FLOAT640 = jit_float64_tan(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DTANH): + { + /* Compute 64-bit float "tanh" */ + VM_STK_FLOAT640 = jit_float64_tanh(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFACOS): + { + /* Compute native float "acos" */ + VM_STK_NFLOAT0 = jit_nfloat_acos(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFASIN): + { + /* Compute native float "asin" */ + VM_STK_NFLOAT0 = jit_nfloat_asin(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFATAN): + { + /* Compute native float "atan" */ + VM_STK_NFLOAT0 = jit_nfloat_atan(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFATAN2): + { + /* Compute native float "atan2" */ + VM_STK_NFLOAT1 = jit_nfloat_atan2 + (VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFCEIL): + { + /* Compute native float "ceil" */ + VM_STK_NFLOAT0 = jit_nfloat_ceil(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFCOS): + { + /* Compute native float "cos" */ + VM_STK_NFLOAT0 = jit_nfloat_cos(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFCOSH): + { + /* Compute native float "cosh" */ + VM_STK_NFLOAT0 = jit_nfloat_cosh(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFEXP): + { + /* Compute native float "exp" */ + VM_STK_NFLOAT0 = jit_nfloat_exp(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFFLOOR): + { + /* Compute native float "floor" */ + VM_STK_NFLOAT0 = jit_nfloat_floor(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOG): + { + /* Compute native float "log" */ + VM_STK_NFLOAT0 = jit_nfloat_log(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFLOG10): + { + /* Compute native float "log10" */ + VM_STK_NFLOAT0 = jit_nfloat_log10(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFPOW): + { + /* Compute native float "pow" */ + VM_STK_NFLOAT1 = jit_nfloat_pow + (VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFRINT): + { + /* Compute native float "rint" */ + VM_STK_NFLOAT0 = jit_nfloat_rint(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFROUND): + { + /* Compute native float "round" */ + VM_STK_NFLOAT0 = jit_nfloat_round(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFSIN): + { + /* Compute native float "sin" */ + VM_STK_NFLOAT0 = jit_nfloat_sin(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFSINH): + { + /* Compute native float "sinh" */ + VM_STK_NFLOAT0 = jit_nfloat_sinh(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFSQRT): + { + /* Compute native float "sqrt" */ + VM_STK_NFLOAT0 = jit_nfloat_sqrt(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFTAN): + { + /* Compute native float "tan" */ + VM_STK_NFLOAT0 = jit_nfloat_tan(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFTANH): + { + /* Compute native float "tanh" */ + VM_STK_NFLOAT0 = jit_nfloat_tanh(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Absolute, minimum, maximum, and sign. + ******************************************************************/ + + VMCASE(JIT_OP_IABS): + { + /* Compute the absolute value of a signed 32-bit integer value */ + VM_STK_INT0 = jit_int_abs(VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LABS): + { + /* Compute the absolute value of a signed 64-bit integer value */ + VM_STK_LONG0 = jit_long_abs(VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FABS): + { + /* Compute the absolute value of a 32-bit float value */ + VM_STK_FLOAT320 = jit_float32_abs(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DABS): + { + /* Compute the absolute value of a 64-bit float value */ + VM_STK_FLOAT640 = jit_float64_abs(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFABS): + { + /* Compute the absolute value of a native float value */ + VM_STK_NFLOAT0 = jit_nfloat_abs(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_IMIN): + { + /* Compute the minimum of two signed 32-bit integer values */ + VM_STK_INT1 = jit_int_min(VM_STK_INT1, VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IMIN_UN): + { + /* Compute the minimum of two unsigned 32-bit integer values */ + VM_STK_UINT1 = jit_uint_min(VM_STK_UINT1, VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMIN): + { + /* Compute the minimum of two signed 64-bit integer values */ + VM_STK_LONG1 = jit_long_min(VM_STK_LONG1, VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMIN_UN): + { + /* Compute the minimum of two unsigned 64-bit integer values */ + VM_STK_ULONG1 = jit_ulong_min(VM_STK_ULONG1, VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FMIN): + { + /* Compute the minimum of two 32-bit float values */ + VM_STK_FLOAT321 = jit_float32_min(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DMIN): + { + /* Compute the minimum of two 64-bit float values */ + VM_STK_FLOAT641 = jit_float64_min(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFMIN): + { + /* Compute the minimum of two native float values */ + VM_STK_NFLOAT1 = jit_nfloat_min(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IMAX): + { + /* Compute the maximum of two signed 32-bit integer values */ + VM_STK_INT1 = jit_int_max(VM_STK_INT1, VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_IMAX_UN): + { + /* Compute the maximum of two unsigned 32-bit integer values */ + VM_STK_UINT1 = jit_uint_max(VM_STK_UINT1, VM_STK_UINT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMAX): + { + /* Compute the maximum of two signed 64-bit integer values */ + VM_STK_LONG1 = jit_long_max(VM_STK_LONG1, VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_LMAX_UN): + { + /* Compute the maximum of two unsigned 64-bit integer values */ + VM_STK_ULONG1 = jit_ulong_max(VM_STK_ULONG1, VM_STK_ULONG0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_FMAX): + { + /* Compute the maximum of two 32-bit float values */ + VM_STK_FLOAT321 = jit_float32_max(VM_STK_FLOAT321, VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_DMAX): + { + /* Compute the maximum of two 64-bit float values */ + VM_STK_FLOAT641 = jit_float64_max(VM_STK_FLOAT641, VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_NFMAX): + { + /* Compute the maximum of two native float values */ + VM_STK_NFLOAT1 = jit_nfloat_max(VM_STK_NFLOAT1, VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ISIGN): + { + /* Compute the sign of a signed 32-bit integer value */ + VM_STK_INT0 = jit_int_sign(VM_STK_INT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LSIGN): + { + /* Compute the sign of a signed 64-bit integer value */ + VM_STK_INT0 = jit_long_sign(VM_STK_LONG0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_FSIGN): + { + /* Compute the sign of a 32-bit float value */ + VM_STK_INT0 = jit_float32_sign(VM_STK_FLOAT320); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_DSIGN): + { + /* Compute the sign of a 64-bit float value */ + VM_STK_INT0 = jit_float64_sign(VM_STK_FLOAT640); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_NFSIGN): + { + /* Compute the sign of a native float value */ + VM_STK_INT0 = jit_nfloat_sign(VM_STK_NFLOAT0); + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Pointer check opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_CHECK_NULL): + { + /* Check the top of stack to see if it is null */ + if(!VM_STK_PTR0) + { + VM_BUILTIN(JIT_RESULT_NULL_REFERENCE); + } + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + VMCASE(JIT_OP_CHECK_NULL_N): + { + /* Check a pointer for null "n" items down the stack. + This opcode is specific to the interpreter */ + if(!(stacktop[VM_NINT_ARG].ptr_value)) + { + VM_BUILTIN(JIT_RESULT_NULL_REFERENCE); + } + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + /****************************************************************** + * Function calls. + ******************************************************************/ + + VMCASE(JIT_OP_CALL): + { + /* Call a function that is under the control of the JIT */ + call_func = (jit_function_t)VM_NINT_ARG; + VM_MODIFY_PC_AND_STACK(2, 0); + entry = call_func->entry_point; + _jit_backtrace_push(&call_trace, pc, 0, 0); + try + { + if(!entry) + { + entry = _jit_function_compile_on_demand(call_func); + } + _jit_run_function((jit_function_interp_t)entry, stacktop, + return_area); + _jit_backtrace_pop(); + } + catch(jit_exception *e) + { + _jit_backtrace_set(call_trace.parent); + exception_object = e->object; + delete e; + goto handle_exception; + } + } + VMBREAK; + + VMCASE(JIT_OP_CALL_INDIRECT): + { + /* Call a native function via an indirect pointer */ + tempptr = (void *)VM_NINT_ARG; + temparg = VM_NINT_ARG2; + VM_MODIFY_PC_AND_STACK(3, 2); + _jit_backtrace_push(&call_trace, pc, 0, 0); + try + { + apply_from_interpreter((jit_type_t)tempptr, + (void *)VM_STK_PTRP2, + stacktop, + (unsigned int)temparg, + VM_STK_PTRP); + _jit_backtrace_pop(); + } + catch(jit_exception *e) + { + _jit_backtrace_set(call_trace.parent); + exception_object = e->object; + delete e; + goto handle_exception; + } + } + VMBREAK; + + VMCASE(JIT_OP_CALL_VTABLE_PTR): + { + /* Call a JIT-managed function via an indirect vtable pointer */ + call_func = (jit_function_t)(VM_STK_PTR0); + if(!call_func) + { + VM_BUILTIN(JIT_RESULT_NULL_FUNCTION); + } + VM_MODIFY_PC_AND_STACK(1, 1); + entry = call_func->entry_point; + _jit_backtrace_push(&call_trace, pc, 0, 0); + try + { + if(!entry) + { + entry = _jit_function_compile_on_demand(call_func); + } + _jit_run_function((jit_function_interp_t)entry, stacktop, + return_area); + _jit_backtrace_pop(); + } + catch(jit_exception *e) + { + _jit_backtrace_set(call_trace.parent); + exception_object = e->object; + delete e; + goto handle_exception; + } + } + VMBREAK; + + VMCASE(JIT_OP_CALL_EXTERNAL): + { + /* Call an external native function */ + tempptr = (void *)VM_NINT_ARG; + tempptr2 = (void *)VM_NINT_ARG2; + temparg = VM_NINT_ARG3; + VM_MODIFY_PC_AND_STACK(4, 1); + _jit_backtrace_push(&call_trace, pc, 0, 0); + try + { + apply_from_interpreter((jit_type_t)tempptr, + (void *)tempptr2, + stacktop, + (unsigned int)temparg, + VM_STK_PTRP); + _jit_backtrace_pop(); + } + catch(jit_exception *e) + { + _jit_backtrace_set(call_trace.parent); + exception_object = e->object; + delete e; + goto handle_exception; + } + } + VMBREAK; + + VMCASE(JIT_OP_RETURN): + { + /* Return from the current function, with no result */ + return; + } + /* Not reached */ + + VMCASE(JIT_OP_RETURN_INT): + { + /* Return from the current function, with an integer result */ + return_area->int_value = VM_STK_INT0; + return; + } + /* Not reached */ + + VMCASE(JIT_OP_RETURN_LONG): + { + /* Return from the current function, with a long result */ + return_area->long_value = VM_STK_LONG0; + return; + } + /* Not reached */ + + VMCASE(JIT_OP_RETURN_FLOAT32): + { + /* Return from the current function, with a 32-bit float result */ + return_area->float32_value = VM_STK_FLOAT320; + return; + } + /* Not reached */ + + VMCASE(JIT_OP_RETURN_FLOAT64): + { + /* Return from the current function, with a 64-bit float result */ + return_area->float64_value = VM_STK_FLOAT640; + return; + } + /* Not reached */ + + VMCASE(JIT_OP_RETURN_NFLOAT): + { + /* Return from the current function, with a native float result */ + return_area->nfloat_value = VM_STK_NFLOAT0; + return; + } + /* Not reached */ + + VMCASE(JIT_OP_RETURN_SMALL_STRUCT): + { + /* Return from the current function, with a small structure */ + #if JIT_APPLY_MAX_STRUCT_IN_REG != 0 + jit_memcpy(return_area->struct_value, stacktop, + (unsigned int)VM_NINT_ARG); + #endif + return; + } + /* Not reached */ + + VMCASE(JIT_OP_SETUP_FOR_NESTED): + { + /* Set up to call a nested function who is our child */ + stacktop[-1].ptr_value = args; + stacktop[-2].ptr_value = frame; + VM_MODIFY_PC_AND_STACK(1, -2); + } + VMBREAK; + + VMCASE(JIT_OP_SETUP_FOR_SIBLING): + { + /* Set up to call a nested function who is our sibling, a sibling + of one of our ancestors, or one of our ancestors directly */ + temparg = VM_NINT_ARG; + tempptr = &(args[0]); + while(temparg > 0) + { + tempptr = (((jit_item *)tempptr)[1]).ptr_value; + --temparg; + } + stacktop[-1].ptr_value = (((jit_item *)tempptr)[1]).ptr_value; + stacktop[-2].ptr_value = (((jit_item *)tempptr)[0]).ptr_value; + VM_MODIFY_PC_AND_STACK(1, -2); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_PARENT_LOCALS): + { + /* Push the pointer to the parent's local variables */ + VM_STK_PTRP = args[0].ptr_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_PARENT_ARGS): + { + /* Push the pointer to the parent's argument variables */ + VM_STK_PTRP = args[1].ptr_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + /****************************************************************** + * Exception handling. + ******************************************************************/ + + VMCASE(JIT_OP_THROW): + { + /* Throw an exception, which may be handled in this function */ + exception_object = VM_STK_PTR0; + handle_exception: + if(jit_function_from_pc(func->func->context, pc, &handler) + == func->func && handler != 0) + { + /* We have an appropriate "catch" handler in this function */ + pc = (void **)handler; + stacktop = frame - 1; + stacktop->ptr_value = exception_object; + } + else + { + /* Throw the exception up to the next level */ + throw new jit_exception(exception_object); + } + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_PC): + { + /* Load the current program counter onto the stack */ + VM_STK_PTRP = (void *)pc; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LEAVE_FINALLY): + { + /* Return from a "finally" handler */ + pc = (void **)VM_STK_PTR0; + VM_MODIFY_STACK(1); + } + VMBREAK; + + VMCASE(JIT_OP_LEAVE_FILTER): + { + /* Return from a "filter" handler: pc and value on stack */ + pc = (void **)(stacktop[1].ptr_value); + stacktop[1] = stacktop[0]; + VM_MODIFY_STACK(1); + } + VMBREAK; + + VMCASE(JIT_OP_CALL_FILTER): + { + /* Call a "filter" handler with pc and value on stack */ + stacktop[-1] = stacktop[0]; + stacktop[0].ptr_value = (void *)(pc + 2); + VM_MODIFY_STACK(-1); + pc = VM_BR_TARGET; + } + VMBREAK; + + VMCASE(JIT_OP_CALL_FINALLY): + { + /* Call a "finally" handler */ + VM_STK_PTRP = (void *)(pc + 2); + VM_MODIFY_STACK(-1); + pc = VM_BR_TARGET; + } + VMBREAK; + + /****************************************************************** + * Pointer-relative loads and stores. + ******************************************************************/ + + VMCASE(JIT_OP_LOAD_RELATIVE_SBYTE): + { + /* Load a signed 8-bit integer from a relative pointer */ + VM_STK_INT0 = *VM_REL(jit_sbyte, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_UBYTE): + { + /* Load an unsigned 8-bit integer from a relative pointer */ + VM_STK_INT0 = *VM_REL(jit_ubyte, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_SHORT): + { + /* Load a signed 16-bit integer from a relative pointer */ + VM_STK_INT0 = *VM_REL(jit_short, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_USHORT): + { + /* Load an unsigned 16-bit integer from a relative pointer */ + VM_STK_INT0 = *VM_REL(jit_ushort, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_INT): + { + /* Load a 32-bit integer from a relative pointer */ + VM_STK_INT0 = *VM_REL(jit_int, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_LONG): + { + /* Load a 64-bit integer from a relative pointer */ + VM_STK_LONG0 = *VM_REL(jit_long, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_FLOAT32): + { + /* Load a 32-bit float from a relative pointer */ + VM_STK_FLOAT320 = *VM_REL(jit_float32, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_FLOAT64): + { + /* Load a 64-bit float from a relative pointer */ + VM_STK_FLOAT640 = *VM_REL(jit_float64, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_NFLOAT): + { + /* Load a native float from a relative pointer */ + VM_STK_NFLOAT0 = *VM_REL(jit_nfloat, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LOAD_RELATIVE_STRUCT): + { + /* Load a structure from a relative pointer */ + tempptr = VM_REL(void, VM_STK_PTR0); + temparg = VM_NINT_ARG2; + stacktop -= (JIT_NUM_ITEMS_IN_STRUCT(temparg) - 1); + jit_memcpy(stacktop, tempptr, temparg); + VM_MODIFY_PC_AND_STACK(3, 0); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_BYTE): + { + /* Store an 8-bit integer value to a relative pointer */ + *VM_REL(jit_sbyte, VM_STK_PTR1) = (jit_sbyte)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_SHORT): + { + /* Store a 16-bit integer value to a relative pointer */ + *VM_REL(jit_short, VM_STK_PTR1) = (jit_short)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_INT): + { + /* Store a 32-bit integer value to a relative pointer */ + *VM_REL(jit_int, VM_STK_PTR1) = VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_LONG): + { + /* Store a 64-bit integer value to a relative pointer */ + *VM_REL(jit_long, VM_STK_PTR1) = VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_FLOAT32): + { + /* Store a 32-bit float value to a relative pointer */ + *VM_REL(jit_float32, VM_STK_PTR1) = VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_FLOAT64): + { + /* Store a 64-bit float value to a relative pointer */ + *VM_REL(jit_float64, VM_STK_PTR1) = VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_NFLOAT): + { + /* Store a native float value to a relative pointer */ + *VM_REL(jit_nfloat, VM_STK_PTR1) = VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(2, 2); + } + VMBREAK; + + VMCASE(JIT_OP_STORE_RELATIVE_STRUCT): + { + /* Store a structure value to a relative pointer */ + temparg = VM_NINT_ARG2; + tempptr = stacktop; + stacktop += JIT_NUM_ITEMS_IN_STRUCT(temparg); + jit_memcpy(VM_REL(void, VM_STK_PTR0), tempptr, temparg); + VM_MODIFY_PC_AND_STACK(3, 1); + } + VMBREAK; + + VMCASE(JIT_OP_ADD_RELATIVE): + { + /* Add a relative offset to a pointer */ + VM_STK_PTR0 = VM_REL(void, VM_STK_PTR0); + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + /****************************************************************** + * Argument variable access opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_LDARG_SBYTE): + { + /* Load a signed 8-bit integer argument onto the stack */ + VM_STK_INTP = *VM_ARG(jit_sbyte); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_UBYTE): + { + /* Load an unsigned 8-bit integer argument onto the stack */ + VM_STK_INTP = *VM_ARG(jit_ubyte); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_SHORT): + { + /* Load a signed 16-bit integer argument onto the stack */ + VM_STK_INTP = *VM_ARG(jit_short); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_USHORT): + { + /* Load an unsigned 16-bit integer argument onto the stack */ + VM_STK_INTP = *VM_ARG(jit_ushort); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_INT): + { + /* Load a 32-bit integer argument onto the stack */ + VM_STK_INTP = *VM_ARG(jit_int); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_LONG): + { + /* Load a 64-bit integer argument onto the stack */ + VM_STK_LONGP = *VM_ARG(jit_long); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_FLOAT32): + { + /* Load a 32-bit float argument onto the stack */ + VM_STK_FLOAT32P = *VM_ARG(jit_float32); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_FLOAT64): + { + /* Load a 64-bit float argument onto the stack */ + VM_STK_FLOAT64P = *VM_ARG(jit_float64); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_NFLOAT): + { + /* Load a native float argument onto the stack */ + VM_STK_NFLOATP = *VM_ARG(jit_float64); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDARG_STRUCT): + { + /* Load a structure argument onto the stack */ + temparg = VM_NINT_ARG2; + stacktop -= JIT_NUM_ITEMS_IN_STRUCT(temparg); + jit_memcpy(stacktop, VM_ARG(void), temparg); + VM_MODIFY_PC_AND_STACK(3, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LDARGA): + { + /* Load the address of an argument onto the stack */ + VM_STK_PTRP = VM_ARG(void); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_BYTE): + { + /* Store an 8-bit integer into a stack argument */ + *VM_ARG(jit_sbyte) = (jit_sbyte)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_SHORT): + { + /* Store a 16-bit integer into a stack argument */ + *VM_ARG(jit_short) = (jit_short)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_INT): + { + /* Store a 32-bit integer into a stack argument */ + *VM_ARG(jit_int) = VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_LONG): + { + /* Store a 64-bit integer into a stack argument */ + *VM_ARG(jit_long) = VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_FLOAT32): + { + /* Store a 32-bit float into a stack argument */ + *VM_ARG(jit_float32) = VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_FLOAT64): + { + /* Store a 64-bit float into a stack argument */ + *VM_ARG(jit_float64) = VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_NFLOAT): + { + /* Store a native float into a stack argument */ + *VM_ARG(jit_nfloat) = VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STARG_STRUCT): + { + /* Store a structure value into a stack argument */ + temparg = VM_NINT_ARG2; + jit_memcpy(VM_ARG(void), stacktop, temparg); + stacktop += JIT_NUM_ITEMS_IN_STRUCT(temparg); + VM_MODIFY_PC_AND_STACK(3, 0); + } + VMBREAK; + + /****************************************************************** + * Local variable frame access opcodes. + ******************************************************************/ + + VMCASE(JIT_OP_LDLOC_SBYTE): + { + /* Load a signed 8-bit integer local onto the stack */ + VM_STK_INTP = *VM_LOC(jit_sbyte); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_UBYTE): + { + /* Load an unsigned 8-bit integer local onto the stack */ + VM_STK_INTP = *VM_LOC(jit_ubyte); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_SHORT): + { + /* Load a signed 16-bit integer local onto the stack */ + VM_STK_INTP = *VM_LOC(jit_short); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_USHORT): + { + /* Load an unsigned 16-bit integer local onto the stack */ + VM_STK_INTP = *VM_LOC(jit_ushort); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_INT): + { + /* Load a 32-bit integer local onto the stack */ + VM_STK_INTP = *VM_LOC(jit_int); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_LONG): + { + /* Load a 64-bit integer local onto the stack */ + VM_STK_LONGP = *VM_LOC(jit_long); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_FLOAT32): + { + /* Load a 32-bit float local onto the stack */ + VM_STK_FLOAT32P = *VM_LOC(jit_float32); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_FLOAT64): + { + /* Load a 64-bit float local onto the stack */ + VM_STK_FLOAT64P = *VM_LOC(jit_float64); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_NFLOAT): + { + /* Load a native float local onto the stack */ + VM_STK_NFLOATP = *VM_LOC(jit_float64); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOC_STRUCT): + { + /* Load a structure local onto the stack */ + temparg = VM_NINT_ARG2; + stacktop -= JIT_NUM_ITEMS_IN_STRUCT(temparg); + jit_memcpy(stacktop, VM_LOC(void), temparg); + VM_MODIFY_PC_AND_STACK(3, 0); + } + VMBREAK; + + VMCASE(JIT_OP_LDLOCA): + { + /* Load the address of an local onto the stack */ + VM_STK_PTRP = VM_LOC(void); + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_BYTE): + { + /* Store an 8-bit integer into a stack local */ + *VM_LOC(jit_sbyte) = (jit_sbyte)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_SHORT): + { + /* Store a 16-bit integer into a stack local */ + *VM_LOC(jit_short) = (jit_short)VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_INT): + { + /* Store a 32-bit integer into a stack local */ + *VM_LOC(jit_int) = VM_STK_INT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_LONG): + { + /* Store a 64-bit integer into a stack local */ + *VM_LOC(jit_long) = VM_STK_LONG0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_FLOAT32): + { + /* Store a 32-bit float into a stack local */ + *VM_LOC(jit_float32) = VM_STK_FLOAT320; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_FLOAT64): + { + /* Store a 64-bit float into a stack local */ + *VM_LOC(jit_float64) = VM_STK_FLOAT640; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_NFLOAT): + { + /* Store a native float into a stack local */ + *VM_LOC(jit_nfloat) = VM_STK_NFLOAT0; + VM_MODIFY_PC_AND_STACK(2, 1); + } + VMBREAK; + + VMCASE(JIT_OP_STLOC_STRUCT): + { + /* Store a structure value into a stack local */ + temparg = VM_NINT_ARG2; + jit_memcpy(VM_LOC(void), stacktop, temparg); + stacktop += JIT_NUM_ITEMS_IN_STRUCT(temparg); + VM_MODIFY_PC_AND_STACK(3, 0); + } + VMBREAK; + + /****************************************************************** + * Stack management + ******************************************************************/ + + VMCASE(JIT_OP_POP_STACK): + { + /* Pop a specific number of items from the stack */ + temparg = VM_NINT_ARG; + VM_MODIFY_PC_AND_STACK(2, temparg); + } + VMBREAK; + + VMCASE(JIT_OP_POP): + { + /* Pop a single item from the stack */ + VM_MODIFY_PC_AND_STACK(1, 1); + } + VMBREAK; + + VMCASE(JIT_OP_POP_2): + { + /* Pop two items from the stack */ + VM_MODIFY_PC_AND_STACK(1, 2); + } + VMBREAK; + + VMCASE(JIT_OP_POP_3): + { + /* Pop three items from the stack */ + VM_MODIFY_PC_AND_STACK(1, 3); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_RETURN_INT): + { + /* Push an integer return value back onto the stack */ + VM_STK_INTP = return_area->int_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_RETURN_LONG): + { + /* Push a long integer return value back onto the stack */ + VM_STK_LONGP = return_area->long_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_RETURN_FLOAT32): + { + /* Push a 32-bit float return value back onto the stack */ + VM_STK_FLOAT32P = return_area->float32_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_RETURN_FLOAT64): + { + /* Push a 64-bit float return value back onto the stack */ + VM_STK_FLOAT64P = return_area->float64_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_RETURN_NFLOAT): + { + /* Push a native float return value back onto the stack */ + VM_STK_NFLOATP = return_area->nfloat_value; + VM_MODIFY_PC_AND_STACK(1, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_RETURN_SMALL_STRUCT): + { + /* Push a small structure return value back onto the stack */ + temparg = VM_NINT_ARG; + stacktop -= JIT_NUM_ITEMS_IN_STRUCT(temparg); + #if JIT_APPLY_MAX_STRUCT_IN_REG != 0 + jit_memcpy(stacktop, return_area->struct_value, temparg); + #endif + VM_MODIFY_PC_AND_STACK(2, 0); + } + VMBREAK; + + /****************************************************************** + * Stack management + ******************************************************************/ + + VMCASE(JIT_OP_PUSH_CONST_INT): + { + /* Push an integer constant onto the stack */ + VM_STK_INTP = (jit_int)VM_NINT_ARG; + VM_MODIFY_PC_AND_STACK(2, -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_CONST_LONG): + { + /* Push a long constant onto the stack */ + #ifdef JIT_NATIVE_INT64 + VM_STK_LONGP = (jit_long)VM_NINT_ARG; + VM_MODIFY_PC_AND_STACK(2, -1); + #else + jit_memcpy(stacktop - 1, pc + 1, sizeof(jit_long)); + VM_MODIFY_PC_AND_STACK + (1 + (sizeof(jit_long) / sizeof(void *)), -1); + #endif + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_CONST_FLOAT32): + { + /* Push a 32-bit float constant onto the stack */ + jit_memcpy(stacktop - 1, pc + 1, sizeof(jit_float32)); + VM_MODIFY_PC_AND_STACK + (1 + (sizeof(jit_float32) / sizeof(void *)), -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_CONST_FLOAT64): + { + /* Push a 64-bit float constant onto the stack */ + jit_memcpy(stacktop - 1, pc + 1, sizeof(jit_float64)); + VM_MODIFY_PC_AND_STACK + (1 + (sizeof(jit_float64) / sizeof(void *)), -1); + } + VMBREAK; + + VMCASE(JIT_OP_PUSH_CONST_NFLOAT): + { + /* Push a native float constant onto the stack */ + jit_memcpy(stacktop - 1, pc + 1, sizeof(jit_nfloat)); + VM_MODIFY_PC_AND_STACK + (1 + (sizeof(jit_nfloat) / sizeof(void *)), -1); + } + VMBREAK; + + /****************************************************************** + * Opcodes that aren't used by the interpreter. These are replaced + * by more specific instructions during function compilation. + ******************************************************************/ + + VMCASE(JIT_OP_IMPORT): + VMCASE(JIT_OP_COPY_LOAD_SBYTE): + VMCASE(JIT_OP_COPY_LOAD_UBYTE): + VMCASE(JIT_OP_COPY_LOAD_SHORT): + VMCASE(JIT_OP_COPY_LOAD_USHORT): + VMCASE(JIT_OP_COPY_INT): + VMCASE(JIT_OP_COPY_LONG): + VMCASE(JIT_OP_COPY_FLOAT32): + VMCASE(JIT_OP_COPY_FLOAT64): + VMCASE(JIT_OP_COPY_NFLOAT): + VMCASE(JIT_OP_COPY_STRUCT): + VMCASE(JIT_OP_COPY_STORE_BYTE): + VMCASE(JIT_OP_COPY_STORE_SHORT): + VMCASE(JIT_OP_ADDRESS_OF): + VMCASE(JIT_OP_INCOMING_REG): + VMCASE(JIT_OP_INCOMING_FRAME_POSN): + VMCASE(JIT_OP_OUTGOING_REG): + VMCASE(JIT_OP_RETURN_REG): + VMCASE(JIT_OP_PUSH_INT): + VMCASE(JIT_OP_PUSH_LONG): + VMCASE(JIT_OP_PUSH_FLOAT32): + VMCASE(JIT_OP_PUSH_FLOAT64): + VMCASE(JIT_OP_PUSH_NFLOAT): + VMCASE(JIT_OP_PUSH_STRUCT): + VMCASE(JIT_OP_FLUSH_SMALL_STRUCT): + VMCASE(JIT_OP_END_MARKER): + VMCASE(JIT_OP_ENTER_CATCH): + VMCASE(JIT_OP_ENTER_FINALLY): + VMCASE(JIT_OP_ENTER_FILTER): + VMCASE(JIT_OP_CALL_FILTER_RETURN): + VMCASE(JIT_OP_PREPARE_FOR_LEAVE): + VMCASE(JIT_OP_PREPARE_FOR_RETURN): + { + /* Shouldn't happen, but skip the instruction anyway */ + VM_MODIFY_PC_AND_STACK(1, 0); + } + VMBREAK; + + } + VMSWITCHEND + +handle_builtin: ; + /* TODO */ +} + +/* + * Class that helps restore the backtrace when C++ exceptions + * travel up the stack. Acts sort of like a "finally" clause. + */ +class jit_trace_restorer +{ +public: + jit_backtrace_t previous; + jit_trace_restorer(jit_backtrace_t previous) + { this->previous = previous; } + ~jit_trace_restorer() + { _jit_backtrace_set(previous); } +}; + +extern "C" int jit_function_apply + (jit_function_t func, void **args, void *return_area) +{ + if(func) + { + return jit_function_apply_vararg + (func, func->signature, args, return_area); + } + else + { + return jit_function_apply_vararg(func, 0, args, return_area); + } +} + +/* Imported from "jit-rules-interp.c" */ +extern "C" unsigned int _jit_interp_calculate_arg_size + (jit_function_t func, jit_type_t signature); + +extern "C" int jit_function_apply_vararg + (jit_function_t func, jit_type_t signature, void **args, void *return_area) +{ + struct jit_backtrace call_trace; + jit_function_interp *entry; + jit_item interp_return_area; + jit_item *arg_buffer; + jit_item *temp_arg; + jit_type_t type; + unsigned int num_params; + unsigned int param; + + _jit_backtrace_push(&call_trace, 0, 0, 0); + { + jit_trace_restorer restorer(call_trace.parent); + jit_exception_clear_last(); + try + { + /* Bail out if the function is NULL */ + if(!func) + { + jit_exception_builtin(JIT_RESULT_NULL_FUNCTION); + } + + /* Make sure that the function is compiled */ + if(func->is_compiled) + { + entry = (jit_function_interp *)(func->entry_point); + } + else + { + entry = (jit_function_interp *) + _jit_function_compile_on_demand(func); + } + + /* Populate the low-level argument buffer */ + if(!signature) + { + signature = func->signature; + arg_buffer = (jit_item *)alloca(entry->args_size); + } + else if(signature == func->signature) + { + arg_buffer = (jit_item *)alloca(entry->args_size); + } + else + { + arg_buffer = (jit_item *)alloca + (_jit_interp_calculate_arg_size(func, signature)); + } + temp_arg = arg_buffer; + if(func->nested_parent) + { + jit_exception_builtin(JIT_RESULT_CALLED_NESTED); + } + type = jit_type_get_return(signature); + if(jit_type_return_via_pointer(type)) + { + if(!return_area) + { + return_area = alloca(jit_type_get_size(type)); + } + temp_arg->ptr_value = return_area; + ++temp_arg; + } + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + type = jit_type_normalize + (jit_type_get_param(signature, param)); + if(!(args[param])) + { + jit_exception_builtin(JIT_RESULT_NULL_REFERENCE); + } + switch(type->kind) + { + case JIT_TYPE_SBYTE: + { + temp_arg->int_value = *((jit_sbyte *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_UBYTE: + { + temp_arg->int_value = *((jit_ubyte *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_SHORT: + { + temp_arg->int_value = *((jit_short *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_USHORT: + { + temp_arg->int_value = *((jit_ushort *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + temp_arg->int_value = *((jit_int *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + temp_arg->long_value = *((jit_long *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_FLOAT32: + { + temp_arg->float32_value = + *((jit_float32 *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_FLOAT64: + { + temp_arg->float64_value = + *((jit_float64 *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_NFLOAT: + { + temp_arg->nfloat_value = + *((jit_nfloat *)(args[param])); + ++temp_arg; + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + jit_memcpy(temp_arg, args[param], + jit_type_get_size(type)); + temp_arg += JIT_NUM_ITEMS_IN_STRUCT + (jit_type_get_size(type)); + } + break; + } + } + + /* Run the function */ + _jit_run_function(entry, arg_buffer, &interp_return_area); + + /* Copy the return value into place, if it isn't already there */ + if(return_area) + { + type = jit_type_normalize(jit_type_get_return(signature)); + if(type && type != jit_type_void) + { + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + { + *((jit_sbyte *)return_area) = + (jit_sbyte)(interp_return_area.int_value); + } + break; + + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + { + *((jit_short *)return_area) = + (jit_short)(interp_return_area.int_value); + } + break; + + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + *((jit_int *)return_area) = + interp_return_area.int_value; + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + *((jit_long *)return_area) = + interp_return_area.long_value; + } + break; + + case JIT_TYPE_FLOAT32: + { + *((jit_float32 *)return_area) = + interp_return_area.float32_value; + } + break; + + case JIT_TYPE_FLOAT64: + { + *((jit_float64 *)return_area) = + interp_return_area.float64_value; + } + break; + + case JIT_TYPE_NFLOAT: + { + *((jit_nfloat *)return_area) = + interp_return_area.nfloat_value; + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + if(!jit_type_return_via_pointer(type)) + { + jit_memcpy(return_area, &interp_return_area, + jit_type_get_size(type)); + } + } + break; + } + } + } + return 1; + } + catch(jit_exception *e) + { + /* Record the exception, but don't throw it any further yet */ + jit_exception_set_last(e->object); + delete e; + return 0; + } + } +} + +#endif /* JIT_BACKEND_INTERP */ diff --git a/jit/jit-interp.h b/jit/jit-interp.h new file mode 100644 index 0000000..5b93dc1 --- /dev/null +++ b/jit/jit-interp.h @@ -0,0 +1,225 @@ +/* + * jit-interp.h - Bytecode interpreter for platforms without native support. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_INTERP_H +#define _JIT_INTERP_H + +#include "jit-internal.h" +#include "jit-apply-rules.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Structure of a stack item. + */ +typedef union +{ + jit_int int_value; + jit_uint uint_value; + jit_long long_value; + jit_ulong ulong_value; + jit_float32 float32_value; + jit_float64 float64_value; + jit_nfloat nfloat_value; + void *ptr_value; +#if JIT_APPLY_MAX_STRUCT_IN_REG != 0 + char struct_value[JIT_APPLY_MAX_STRUCT_IN_REG]; +#endif + +} jit_item; + +/* + * Number of items that make up a struct or union value on the stack. + */ +#define JIT_NUM_ITEMS_IN_STRUCT(size) \ + (((size) + (sizeof(jit_item) - 1)) / sizeof(jit_item)) + +/* + * Information that is prefixed to a function that describes + * its interpretation context. The code starts just after this. + */ +typedef struct jit_function_interp *jit_function_interp_t; +struct jit_function_interp +{ + /* The function that this structure is associated with */ + jit_function_t func; + + /* Size of the argument area to allocate, in bytes */ + unsigned int args_size; + + /* Size of the local stack frame to allocate, in bytes */ + unsigned int frame_size; + + /* Size of the working stack area of the frame, in items */ + unsigned int working_area; +}; + +/* + * Get the size of the "jit_function_interp" structure, rounded + * up to a multiple of "void *". + */ +#define jit_function_interp_size \ + ((sizeof(struct jit_function_interp) + sizeof(void *) - 1) & \ + ~(sizeof(void *) - 1)) + +/* + * Get the entry point for a function, from its "jit_function_interp_t" block. + */ +#define jit_function_interp_entry_pc(info) \ + ((void **)(((unsigned char *)(info)) + jit_function_interp_size)) + +#if defined(__cplusplus) + +/* + * The JIT exception class. Instances of this object are thrown + * to simulate a JIT-level exception. + */ +class jit_exception +{ +public: + jit_exception(void *object) { this->object = object; } + + void *object; +}; + +#endif /* __cplusplus */ + +/* + * Argument variable access opcodes. + */ +#define JIT_OP_LDARG_SBYTE (JIT_OP_NUM_OPCODES + 0x0000) +#define JIT_OP_LDARG_UBYTE (JIT_OP_NUM_OPCODES + 0x0001) +#define JIT_OP_LDARG_SHORT (JIT_OP_NUM_OPCODES + 0x0002) +#define JIT_OP_LDARG_USHORT (JIT_OP_NUM_OPCODES + 0x0003) +#define JIT_OP_LDARG_INT (JIT_OP_NUM_OPCODES + 0x0004) +#define JIT_OP_LDARG_LONG (JIT_OP_NUM_OPCODES + 0x0005) +#define JIT_OP_LDARG_FLOAT32 (JIT_OP_NUM_OPCODES + 0x0006) +#define JIT_OP_LDARG_FLOAT64 (JIT_OP_NUM_OPCODES + 0x0007) +#define JIT_OP_LDARG_NFLOAT (JIT_OP_NUM_OPCODES + 0x0008) +#define JIT_OP_LDARG_STRUCT (JIT_OP_NUM_OPCODES + 0x0009) +#define JIT_OP_LDARGA (JIT_OP_NUM_OPCODES + 0x000A) +#define JIT_OP_STARG_BYTE (JIT_OP_NUM_OPCODES + 0x000B) +#define JIT_OP_STARG_SHORT (JIT_OP_NUM_OPCODES + 0x000C) +#define JIT_OP_STARG_INT (JIT_OP_NUM_OPCODES + 0x000D) +#define JIT_OP_STARG_LONG (JIT_OP_NUM_OPCODES + 0x000E) +#define JIT_OP_STARG_FLOAT32 (JIT_OP_NUM_OPCODES + 0x000F) +#define JIT_OP_STARG_FLOAT64 (JIT_OP_NUM_OPCODES + 0x0010) +#define JIT_OP_STARG_NFLOAT (JIT_OP_NUM_OPCODES + 0x0011) +#define JIT_OP_STARG_STRUCT (JIT_OP_NUM_OPCODES + 0x0012) + +/* + * Local variable frame access opcodes. + */ +#define JIT_OP_LDLOC_SBYTE (JIT_OP_NUM_OPCODES + 0x0013) +#define JIT_OP_LDLOC_UBYTE (JIT_OP_NUM_OPCODES + 0x0014) +#define JIT_OP_LDLOC_SHORT (JIT_OP_NUM_OPCODES + 0x0015) +#define JIT_OP_LDLOC_USHORT (JIT_OP_NUM_OPCODES + 0x0016) +#define JIT_OP_LDLOC_INT (JIT_OP_NUM_OPCODES + 0x0017) +#define JIT_OP_LDLOC_LONG (JIT_OP_NUM_OPCODES + 0x0018) +#define JIT_OP_LDLOC_FLOAT32 (JIT_OP_NUM_OPCODES + 0x0019) +#define JIT_OP_LDLOC_FLOAT64 (JIT_OP_NUM_OPCODES + 0x001A) +#define JIT_OP_LDLOC_NFLOAT (JIT_OP_NUM_OPCODES + 0x001B) +#define JIT_OP_LDLOC_STRUCT (JIT_OP_NUM_OPCODES + 0x001C) +#define JIT_OP_LDLOCA (JIT_OP_NUM_OPCODES + 0x001D) +#define JIT_OP_STLOC_BYTE (JIT_OP_NUM_OPCODES + 0x001E) +#define JIT_OP_STLOC_SHORT (JIT_OP_NUM_OPCODES + 0x001F) +#define JIT_OP_STLOC_INT (JIT_OP_NUM_OPCODES + 0x0020) +#define JIT_OP_STLOC_LONG (JIT_OP_NUM_OPCODES + 0x0021) +#define JIT_OP_STLOC_FLOAT32 (JIT_OP_NUM_OPCODES + 0x0022) +#define JIT_OP_STLOC_FLOAT64 (JIT_OP_NUM_OPCODES + 0x0023) +#define JIT_OP_STLOC_NFLOAT (JIT_OP_NUM_OPCODES + 0x0024) +#define JIT_OP_STLOC_STRUCT (JIT_OP_NUM_OPCODES + 0x0025) + +/* + * Pointer check opcodes (interpreter only). + */ +#define JIT_OP_CHECK_NULL_N (JIT_OP_NUM_OPCODES + 0x0026) + +/* + * Stack management. + */ +#define JIT_OP_POP (JIT_OP_NUM_OPCODES + 0x0027) +#define JIT_OP_POP_2 (JIT_OP_NUM_OPCODES + 0x0028) +#define JIT_OP_POP_3 (JIT_OP_NUM_OPCODES + 0x0029) +#define JIT_OP_PUSH_RETURN_INT (JIT_OP_NUM_OPCODES + 0x002A) +#define JIT_OP_PUSH_RETURN_LONG (JIT_OP_NUM_OPCODES + 0x002B) +#define JIT_OP_PUSH_RETURN_FLOAT32 (JIT_OP_NUM_OPCODES + 0x002C) +#define JIT_OP_PUSH_RETURN_FLOAT64 (JIT_OP_NUM_OPCODES + 0x002D) +#define JIT_OP_PUSH_RETURN_NFLOAT (JIT_OP_NUM_OPCODES + 0x002E) +#define JIT_OP_PUSH_RETURN_SMALL_STRUCT (JIT_OP_NUM_OPCODES + 0x002F) + +/* + * Nested function call handling. + */ +#define JIT_OP_PUSH_PARENT_LOCALS (JIT_OP_NUM_OPCODES + 0x0030) +#define JIT_OP_PUSH_PARENT_ARGS (JIT_OP_NUM_OPCODES + 0x0031) + +/* + * Push constant values onto the stack. + */ +#define JIT_OP_PUSH_CONST_INT (JIT_OP_NUM_OPCODES + 0x0032) +#define JIT_OP_PUSH_CONST_LONG (JIT_OP_NUM_OPCODES + 0x0033) +#define JIT_OP_PUSH_CONST_FLOAT32 (JIT_OP_NUM_OPCODES + 0x0034) +#define JIT_OP_PUSH_CONST_FLOAT64 (JIT_OP_NUM_OPCODES + 0x0035) +#define JIT_OP_PUSH_CONST_NFLOAT (JIT_OP_NUM_OPCODES + 0x0036) + +/* + * Exception handling (interpreter-only). + */ +#define JIT_OP_CALL_FINALLY (JIT_OP_NUM_OPCODES + 0x0037) + +/* + * Marker opcode for the end of a function. + */ +#define JIT_OP_END_MARKER (JIT_OP_NUM_OPCODES + 0x0038) + +/* + * Number of interpreter-specific opcodes. + */ +#define JIT_OP_NUM_INTERP_OPCODES \ + (JIT_OP_END_MARKER + 1 - JIT_OP_NUM_OPCODES) + +/* + * Opcode version. Should be increased whenever new opcodes + * are added to this list or the public list in "jit-opcode.h". + * This value is written to ELF binaries, to ensure that code + * for one version of libjit is not inadvertantly used in another. + */ +#define JIT_OPCODE_VERSION 0 + +/* + * Additional opcode definition flags. + */ +#define JIT_OPCODE_INTERP_ARGS_MASK 0x7E000000 +#define JIT_OPCODE_NINT_ARG 0x02000000 +#define JIT_OPCODE_NINT_ARG_TWO 0x04000000 +#define JIT_OPCODE_CONST_LONG 0x06000000 +#define JIT_OPCODE_CONST_FLOAT32 0x08000000 +#define JIT_OPCODE_CONST_FLOAT64 0x0A000000 +#define JIT_OPCODE_CONST_NFLOAT 0x0C000000 +#define JIT_OPCODE_CALL_INDIRECT_ARGS 0x0E000000 + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_INTERP_H */ diff --git a/jit/jit-intrinsic.c b/jit/jit-intrinsic.c new file mode 100644 index 0000000..04919b4 --- /dev/null +++ b/jit/jit-intrinsic.c @@ -0,0 +1,3482 @@ +/* + * jit-intrinsic.c - Support routines for JIT intrinsics. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include +#if defined(HAVE_TGMATH_H) && !defined(JIT_NFLOAT_IS_DOUBLE) + #include +#elif defined(HAVE_MATH_H) + #include +#endif +#ifdef JIT_WIN32_NATIVE +#include +#if !defined(isnan) +#define isnan(value) _isnan((value)) +#endif +#if !defined(isfinite) +#define isfinite(value) _finite((value)) +#endif +#ifndef HAVE_ISNAN +#define HAVE_ISNAN 1 +#endif +#undef HAVE_ISNANF +#undef HAVE_ISNANL +#else +#ifdef HAVE_IEEEFP_H +#include +#endif +#endif + +/*@ + +@cindex jit-intrinsic.h + +Intrinsics are functions that are provided to ease code generation +on platforms that may not be able to perform all operations natively. + +For example, on a CPU without a floating-point unit, the back end code +generator will output a call to an intrinsic function when a floating-point +operation is performed. CPU's with a floating-point unit would use +a native instruction instead. + +Some platforms may already have appropriate intrinsics (e.g. the ARM +floating-point emulation routines). The back end code generator may choose +to use either the system-supplied intrinsics or the ones supplied by +this library. We supply all of them in our library just in case a +particular platform lacks an appropriate intrinsic. + +Some intrinsics have no equivalent in existing system libraries; +particularly those that deal with overflow checking. + +Functions that perform overflow checking or which divide integer operands +return a built-in exception code to indicate the type of exception +to be thrown (the caller is responsible for throwing the actual +exception). @xref{Exceptions}, for a list of built-in exception codes. + +The following functions are defined in @code{}: + +@*/ + +/* + * Special values that indicate "not a number" for floating-point types. + * Visual C++ won't let us compute NaN's at compile time, so we have to + * work around it with a run-time hack. + */ +#if defined(_MSC_VER) && defined(_M_IX86) +static unsigned int const float32_nan = 0x7FC00000; +static unsigned __int64 const float64_nan = 0x7FF8000000000000LL; +#define jit_float32_nan (*((jit_float32 *)&float32_nan)) +#define jit_float64_nan (*((jit_float64 *)&float64_nan)) +#define jit_nfloat_nan ((jit_nfloat)(*((jit_float64 *)&float64_nan))) +#else +#define jit_float32_nan ((jit_float32)(0.0 / 0.0)) +#define jit_float64_nan ((jit_float64)(0.0 / 0.0)) +#define jit_nfloat_nan ((jit_nfloat)(0.0 / 0.0)) +#endif + +/*@ + * @deftypefun jit_int jit_int_add (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_sub (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_mul (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_neg (jit_int value1) + * @deftypefunx jit_int jit_int_and (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_or (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_xor (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_not (jit_int value1) + * @deftypefunx jit_int jit_int_not (jit_int value1) + * @deftypefunx jit_int jit_int_shl (jit_int value1, jit_uint value2) + * @deftypefunx jit_int jit_int_shr (jit_int value1, jit_uint value2) + * Perform an arithmetic operation on signed 32-bit integers. + * @end deftypefun + * + * @deftypefun jit_int jit_int_add_ovf ({jit_int *} result, jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_sub_ovf ({jit_int *} result, jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_mul_ovf ({jit_int *} result, jit_int value1, jit_int value2) + * Perform an arithmetic operation on two signed 32-bit integers, + * with overflow checking. Returns @code{JIT_RESULT_OK} + * or @code{JIT_RESULT_OVERFLOW}. + * @end deftypefun + * + * @deftypefun jit_int jit_int_div_ovf ({jit_int *} result, jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_rem_ovf ({jit_int *} result, jit_int value1, jit_int value2) + * Perform a division or remainder operation on two signed 32-bit integers. + * Returns @code{JIT_RESULT_OK}, @code{JIT_RESULT_DIVISION_BY_ZERO}, + * or @code{JIT_RESULT_ARITHMETIC}. + * @end deftypefun + * + * @deftypefun jit_int jit_int_eq (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_ne (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_lt (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_le (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_gt (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_ge (jit_int value1, jit_int value2) + * Compare two signed 32-bit integers, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_int_cmp (jit_int value1, jit_int value2) + * Compare two signed 32-bit integers and return -1, 0, or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_int_abs (jit_int value1) + * @deftypefunx jit_int jit_int_min (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_max (jit_int value1, jit_int value2) + * @deftypefunx jit_int jit_int_sign (jit_int value1) + * Calculate the absolute value, minimum, maximum, or sign for + * signed 32-bit integer values. + * @end deftypefun +@*/ +jit_int jit_int_add(jit_int value1, jit_int value2) +{ + return value1 + value2; +} + +jit_int jit_int_sub(jit_int value1, jit_int value2) +{ + return value1 - value2; +} + +jit_int jit_int_mul(jit_int value1, jit_int value2) +{ + return value1 * value2; +} + +jit_int jit_int_div(jit_int *result, jit_int value1, jit_int value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else if(value2 == (jit_int)(-1) && value1 == jit_min_int) + { + *result = 0; + return JIT_RESULT_ARITHMETIC; + } + else + { + *result = value1 / value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_int_rem(jit_int *result, jit_int value1, jit_int value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else if(value2 == (jit_int)(-1) && value1 == jit_min_int) + { + *result = 0; + return JIT_RESULT_ARITHMETIC; + } + else + { + *result = value1 % value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_int_add_ovf(jit_int *result, jit_int value1, jit_int value2) +{ + if(value1 >= 0 && value2 >= 0) + { + return ((*result = value1 + value2) >= value1); + } + else if(value1 < 0 && value2 < 0) + { + return ((*result = value1 + value2) < value1); + } + else + { + *result = value1 + value2; + return 1; + } +} + +jit_int jit_int_sub_ovf(jit_int *result, jit_int value1, jit_int value2) +{ + if(value1 >= 0 && value2 >= 0) + { + *result = value1 - value2; + return 1; + } + else if(value1 < 0 && value2 < 0) + { + *result = value1 - value2; + return 1; + } + else if(value1 < 0) + { + return ((*result = value1 - value2) <= value1); + } + else + { + return ((*result = value1 - value2) >= value1); + } +} + +jit_int jit_int_mul_ovf(jit_int *result, jit_int value1, jit_int value2) +{ + jit_long temp = ((jit_long)value1) * ((jit_long)value2); + *result = (jit_int)temp; + return (temp >= (jit_long)jit_min_int && temp <= (jit_long)jit_max_int); +} + +jit_int jit_int_neg(jit_int value1) +{ + return -value1; +} + +jit_int jit_int_and(jit_int value1, jit_int value2) +{ + return value1 & value2; +} + +jit_int jit_int_or(jit_int value1, jit_int value2) +{ + return value1 | value2; +} + +jit_int jit_int_xor(jit_int value1, jit_int value2) +{ + return value1 ^ value2; +} + +jit_int jit_int_not(jit_int value1) +{ + return ~value1; +} + +jit_int jit_int_shl(jit_int value1, jit_uint value2) +{ + return value1 << (value2 & 0x1F); +} + +jit_int jit_int_shr(jit_int value1, jit_uint value2) +{ + return value1 >> (value2 & 0x1F); +} + +jit_int jit_int_eq(jit_int value1, jit_int value2) +{ + return (value1 == value2); +} + +jit_int jit_int_ne(jit_int value1, jit_int value2) +{ + return (value1 != value2); +} + +jit_int jit_int_lt(jit_int value1, jit_int value2) +{ + return (value1 < value2); +} + +jit_int jit_int_le(jit_int value1, jit_int value2) +{ + return (value1 <= value2); +} + +jit_int jit_int_gt(jit_int value1, jit_int value2) +{ + return (value1 > value2); +} + +jit_int jit_int_ge(jit_int value1, jit_int value2) +{ + return (value1 >= value2); +} + +jit_int jit_int_cmp(jit_int value1, jit_int value2) +{ + if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_int jit_int_abs(jit_int value1) +{ + return ((value1 >= 0) ? value1 : -value1); +} + +jit_int jit_int_min(jit_int value1, jit_int value2) +{ + return ((value1 <= value2) ? value1 : value2); +} + +jit_int jit_int_max(jit_int value1, jit_int value2) +{ + return ((value1 >= value2) ? value1 : value2); +} + +jit_int jit_int_sign(jit_int value1) +{ + if(value1 < 0) + { + return -1; + } + else if(value1 > 0) + { + return 0; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_uint jit_uint_add (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_sub (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_mul (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_neg (jit_uint value1) + * @deftypefunx jit_uint jit_uint_and (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_or (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_xor (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_not (jit_uint value1) + * @deftypefunx jit_uint jit_uint_not (jit_uint value1) + * @deftypefunx jit_uint jit_uint_shl (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_shr (jit_uint value1, jit_uint value2) + * Perform an arithmetic operation on unsigned 32-bit integers. + * @end deftypefun + * + * @deftypefun jit_int jit_uint_add_ovf ({jit_uint *} result, jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_sub_ovf ({jit_uint *} result, jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_mul_ovf ({jit_uint *} result, jit_uint value1, jit_uint value2) + * Perform an arithmetic operation on two unsigned 32-bit integers, + * with overflow checking. Returns @code{JIT_RESULT_OK} + * or @code{JIT_RESULT_OVERFLOW}. + * @end deftypefun + * + * @deftypefun jit_int jit_uint_div_ovf ({jit_uint *} result, jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_rem_ovf ({jit_uint *} result, jit_uint value1, jit_uint value2) + * Perform a division or remainder operation on two unsigned 32-bit integers. + * Returns @code{JIT_RESULT_OK} or @code{JIT_RESULT_DIVISION_BY_ZERO} + * (@code{JIT_RESULT_ARITHMETIC} is not possible with unsigned integers). + * @end deftypefun + * + * @deftypefun jit_int jit_uint_eq (jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_ne (jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_lt (jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_le (jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_gt (jit_uint value1, jit_uint value2) + * @deftypefunx jit_int jit_uint_ge (jit_uint value1, jit_uint value2) + * Compare two unsigned 32-bit integers, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_uint_cmp (jit_uint value1, jit_uint value2) + * Compare two unsigned 32-bit integers and return -1, 0, or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_uint jit_uint_min (jit_uint value1, jit_uint value2) + * @deftypefunx jit_uint jit_uint_max (jit_uint value1, jit_uint value2) + * Calculate the minimum or maximum for unsigned 32-bit integer values. + * @end deftypefun +@*/ +jit_uint jit_uint_add(jit_uint value1, jit_uint value2) +{ + return value1 + value2; +} + +jit_uint jit_uint_sub(jit_uint value1, jit_uint value2) +{ + return value1 - value2; +} + +jit_uint jit_uint_mul(jit_uint value1, jit_uint value2) +{ + return value1 * value2; +} + +jit_int jit_uint_div(jit_uint *result, jit_uint value1, jit_uint value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else + { + *result = value1 / value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_uint_rem(jit_uint *result, jit_uint value1, jit_uint value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else + { + *result = value1 % value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_uint_add_ovf(jit_uint *result, jit_uint value1, jit_uint value2) +{ + return ((*result = value1 + value2) >= value1); +} + +jit_int jit_uint_sub_ovf(jit_uint *result, jit_uint value1, jit_uint value2) +{ + return ((*result = value1 - value2) <= value1); +} + +jit_int jit_uint_mul_ovf(jit_uint *result, jit_uint value1, jit_uint value2) +{ + jit_ulong temp = ((jit_ulong)value1) * ((jit_ulong)value2); + *result = (jit_uint)temp; + return (temp <= (jit_ulong)jit_max_uint); +} + +jit_uint jit_uint_neg(jit_uint value1) +{ + return (jit_uint)(-((jit_int)value1)); +} + +jit_uint jit_uint_and(jit_uint value1, jit_uint value2) +{ + return value1 & value2; +} + +jit_uint jit_uint_or(jit_uint value1, jit_uint value2) +{ + return value1 | value2; +} + +jit_uint jit_uint_xor(jit_uint value1, jit_uint value2) +{ + return value1 ^ value2; +} + +jit_uint jit_uint_not(jit_uint value1) +{ + return ~value1; +} + +jit_uint jit_uint_shl(jit_uint value1, jit_uint value2) +{ + return value1 << (value2 & 0x1F); +} + +jit_uint jit_uint_shr(jit_uint value1, jit_uint value2) +{ + return value1 >> (value2 & 0x1F); +} + +jit_int jit_uint_eq(jit_uint value1, jit_uint value2) +{ + return (value1 == value2); +} + +jit_int jit_uint_ne(jit_uint value1, jit_uint value2) +{ + return (value1 != value2); +} + +jit_int jit_uint_lt(jit_uint value1, jit_uint value2) +{ + return (value1 < value2); +} + +jit_int jit_uint_le(jit_uint value1, jit_uint value2) +{ + return (value1 <= value2); +} + +jit_int jit_uint_gt(jit_uint value1, jit_uint value2) +{ + return (value1 > value2); +} + +jit_int jit_uint_ge(jit_uint value1, jit_uint value2) +{ + return (value1 >= value2); +} + +jit_int jit_uint_cmp(jit_uint value1, jit_uint value2) +{ + if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_uint jit_uint_min(jit_uint value1, jit_uint value2) +{ + return ((value1 <= value2) ? value1 : value2); +} + +jit_uint jit_uint_max(jit_uint value1, jit_uint value2) +{ + return ((value1 >= value2) ? value1 : value2); +} + +/*@ + * @deftypefun jit_long jit_long_add (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_sub (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_mul (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_neg (jit_long value1) + * @deftypefunx jit_long jit_long_and (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_or (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_xor (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_not (jit_long value1) + * @deftypefunx jit_long jit_long_not (jit_long value1) + * @deftypefunx jit_long jit_long_shl (jit_long value1, jit_uint value2) + * @deftypefunx jit_long jit_long_shr (jit_long value1, jit_uint value2) + * Perform an arithmetic operation on signed 64-bit integers. + * @end deftypefun + * + * @deftypefun jit_int jit_long_add_ovf ({jit_long *} result, jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_sub_ovf ({jit_long *} result, jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_mul_ovf ({jit_long *} result, jit_long value1, jit_long value2) + * Perform an arithmetic operation on two signed 64-bit integers, + * with overflow checking. Returns @code{JIT_RESULT_OK} + * or @code{JIT_RESULT_OVERFLOW}. + * @end deftypefun + * + * @deftypefun jit_int jit_long_div_ovf ({jit_long *} result, jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_rem_ovf ({jit_long *} result, jit_long value1, jit_long value2) + * Perform a division or remainder operation on two signed 64-bit integers. + * Returns @code{JIT_RESULT_OK}, @code{JIT_RESULT_DIVISION_BY_ZERO}, + * or @code{JIT_RESULT_ARITHMETIC}. + * @end deftypefun + * + * @deftypefun jit_int jit_long_eq (jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_ne (jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_lt (jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_le (jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_gt (jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_ge (jit_long value1, jit_long value2) + * Compare two signed 64-bit integers, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_long_cmp (jit_long value1, jit_long value2) + * Compare two signed 64-bit integers and return -1, 0, or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_long jit_long_abs (jit_long value1) + * @deftypefunx jit_long jit_long_min (jit_long value1, jit_long value2) + * @deftypefunx jit_long jit_long_max (jit_long value1, jit_long value2) + * @deftypefunx jit_int jit_long_sign (jit_long value1) + * Calculate the absolute value, minimum, maximum, or sign for + * signed 64-bit integer values. + * @end deftypefun +@*/ +jit_long jit_long_add(jit_long value1, jit_long value2) +{ + return value1 + value2; +} + +jit_long jit_long_sub(jit_long value1, jit_long value2) +{ + return value1 - value2; +} + +jit_long jit_long_mul(jit_long value1, jit_long value2) +{ + return value1 * value2; +} + +jit_int jit_long_div(jit_long *result, jit_long value1, jit_long value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else if(value2 == (jit_long)(-1) && value1 == jit_min_long) + { + *result = 0; + return JIT_RESULT_ARITHMETIC; + } + else + { + *result = value1 / value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_long_rem(jit_long *result, jit_long value1, jit_long value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else if(value2 == (jit_long)(-1) && value1 == jit_min_long) + { + *result = 0; + return JIT_RESULT_ARITHMETIC; + } + else + { + *result = value1 % value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_long_add_ovf(jit_long *result, jit_long value1, jit_long value2) +{ + if(value1 >= 0 && value2 >= 0) + { + return ((*result = value1 + value2) >= value1); + } + else if(value1 < 0 && value2 < 0) + { + return ((*result = value1 + value2) < value1); + } + else + { + *result = value1 + value2; + return 1; + } +} + +jit_int jit_long_sub_ovf(jit_long *result, jit_long value1, jit_long value2) +{ + if(value1 >= 0 && value2 >= 0) + { + *result = value1 - value2; + return 1; + } + else if(value1 < 0 && value2 < 0) + { + *result = value1 - value2; + return 1; + } + else if(value1 < 0) + { + return ((*result = value1 - value2) <= value1); + } + else + { + return ((*result = value1 - value2) >= value1); + } +} + +jit_int jit_long_mul_ovf(jit_long *result, jit_long value1, jit_long value2) +{ + jit_ulong temp; + if(value1 >= 0 && value2 >= 0) + { + /* Both values are positive */ + if(!jit_ulong_mul_ovf(&temp, (jit_ulong)value1, (jit_ulong)value2)) + { + *result = jit_max_long; + return 0; + } + if(temp > ((jit_ulong)jit_max_long)) + { + *result = jit_max_long; + return 0; + } + *result = (jit_long)temp; + return 1; + } + else if(value1 >= 0) + { + /* The first value is positive */ + if(!jit_ulong_mul_ovf(&temp, (jit_ulong)value1, (jit_ulong)-value2)) + { + *result = jit_min_long; + return 0; + } + if(temp > (((jit_ulong)jit_max_long) + 1)) + { + *result = jit_min_long; + return 0; + } + *result = -((jit_long)temp); + return 1; + } + else if(value2 >= 0) + { + /* The second value is positive */ + if(!jit_ulong_mul_ovf(&temp, (jit_ulong)-value1, (jit_ulong)value2)) + { + *result = jit_min_long; + return 0; + } + if(temp > (((jit_ulong)jit_max_long) + 1)) + { + *result = jit_min_long; + return 0; + } + *result = -((jit_long)temp); + return 1; + } + else + { + /* Both values are negative */ + if(!jit_ulong_mul_ovf(&temp, (jit_ulong)-value1, (jit_ulong)-value2)) + { + *result = jit_max_long; + return 0; + } + if(temp > ((jit_ulong)jit_max_long)) + { + *result = jit_max_long; + return 0; + } + *result = (jit_long)temp; + return 1; + } +} + +jit_long jit_long_neg(jit_long value1) +{ + return -value1; +} + +jit_long jit_long_and(jit_long value1, jit_long value2) +{ + return value1 & value2; +} + +jit_long jit_long_or(jit_long value1, jit_long value2) +{ + return value1 | value2; +} + +jit_long jit_long_xor(jit_long value1, jit_long value2) +{ + return value1 ^ value2; +} + +jit_long jit_long_not(jit_long value1) +{ + return ~value1; +} + +jit_long jit_long_shl(jit_long value1, jit_uint value2) +{ + return value1 << (value2 & 0x3F); +} + +jit_long jit_long_shr(jit_long value1, jit_uint value2) +{ + return value1 >> (value2 & 0x3F); +} + +jit_int jit_long_eq(jit_long value1, jit_long value2) +{ + return (value1 == value2); +} + +jit_int jit_long_ne(jit_long value1, jit_long value2) +{ + return (value1 != value2); +} + +jit_int jit_long_lt(jit_long value1, jit_long value2) +{ + return (value1 < value2); +} + +jit_int jit_long_le(jit_long value1, jit_long value2) +{ + return (value1 <= value2); +} + +jit_int jit_long_gt(jit_long value1, jit_long value2) +{ + return (value1 > value2); +} + +jit_int jit_long_ge(jit_long value1, jit_long value2) +{ + return (value1 >= value2); +} + +jit_int jit_long_cmp(jit_long value1, jit_long value2) +{ + if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_long jit_long_abs(jit_long value1) +{ + return ((value1 >= 0) ? value1 : -value1); +} + +jit_long jit_long_min(jit_long value1, jit_long value2) +{ + return ((value1 <= value2) ? value1 : value2); +} + +jit_long jit_long_max(jit_long value1, jit_long value2) +{ + return ((value1 >= value2) ? value1 : value2); +} + +jit_int jit_long_sign(jit_long value1) +{ + if(value1 < 0) + { + return -1; + } + else if(value1 > 0) + { + return 0; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_ulong jit_ulong_add (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_sub (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_mul (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_neg (jit_ulong value1) + * @deftypefunx jit_ulong jit_ulong_and (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_or (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_xor (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_not (jit_ulong value1) + * @deftypefunx jit_ulong jit_ulong_not (jit_ulong value1) + * @deftypefunx jit_ulong jit_ulong_shl (jit_ulong value1, jit_uint value2) + * @deftypefunx jit_ulong jit_ulong_shr (jit_ulong value1, jit_uint value2) + * Perform an arithmetic operation on unsigned 64-bit integers. + * @end deftypefun + * + * @deftypefun jit_int jit_ulong_add_ovf ({jit_ulong *} result, jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_sub_ovf ({jit_ulong *} result, jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_mul_ovf ({jit_ulong *} result, jit_ulong value1, jit_ulong value2) + * Perform an arithmetic operation on two unsigned 64-bit integers, + * with overflow checking. Returns @code{JIT_RESULT_OK} + * or @code{JIT_RESULT_OVERFLOW}. + * @end deftypefun + * + * @deftypefun jit_int jit_ulong_div_ovf ({jit_ulong *} result, jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_rem_ovf ({jit_ulong *} result, jit_ulong value1, jit_ulong value2) + * Perform a division or remainder operation on two unsigned 64-bit integers. + * Returns @code{JIT_RESULT_OK} or @code{JIT_RESULT_DIVISION_BY_ZERO} + * (@code{JIT_RESULT_ARITHMETIC} is not possible with unsigned integers). + * @end deftypefun + * + * @deftypefun jit_int jit_ulong_eq (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_ne (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_lt (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_le (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_gt (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_int jit_ulong_ge (jit_ulong value1, jit_ulong value2) + * Compare two unsigned 64-bit integers, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_ulong_cmp (jit_ulong value1, jit_ulong value2) + * Compare two unsigned 64-bit integers and return -1, 0, or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_ulong jit_ulong_min (jit_ulong value1, jit_ulong value2) + * @deftypefunx jit_ulong jit_ulong_max (jit_ulong value1, jit_ulong value2) + * Calculate the minimum or maximum for unsigned 64-bit integer values. + * @end deftypefun +@*/ +jit_ulong jit_ulong_add(jit_ulong value1, jit_ulong value2) +{ + return value1 + value2; +} + +jit_ulong jit_ulong_sub(jit_ulong value1, jit_ulong value2) +{ + return value1 - value2; +} + +jit_ulong jit_ulong_mul(jit_ulong value1, jit_ulong value2) +{ + return value1 - value2; +} + +jit_int jit_ulong_div(jit_ulong *result, jit_ulong value1, jit_ulong value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else + { + *result = value1 / value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_ulong_rem(jit_ulong *result, jit_ulong value1, jit_ulong value2) +{ + if(value2 == 0) + { + *result = 0; + return JIT_RESULT_DIVISION_BY_ZERO; + } + else + { + *result = value1 % value2; + return JIT_RESULT_OK; + } +} + +jit_int jit_ulong_add_ovf(jit_ulong *result, jit_ulong value1, jit_ulong value2) +{ + return ((*result = value1 + value2) >= value1); +} + +jit_int jit_ulong_sub_ovf(jit_ulong *result, jit_ulong value1, jit_ulong value2) +{ + return ((*result = value1 - value2) <= value1); +} + +jit_int jit_ulong_mul_ovf(jit_ulong *result, jit_ulong value1, jit_ulong value2) +{ + jit_uint high1, low1, high2, low2, orig; + jit_ulong temp; + jit_uint result1, result2, result3, result4; + high1 = (jit_uint)(value1 >> 32); + low1 = (jit_uint)value1; + high2 = (jit_uint)(value2 >> 32); + low2 = (jit_uint)value2; + temp = ((jit_ulong)low1) * ((jit_ulong)low2); + result1 = (jit_uint)temp; + result2 = (jit_uint)(temp >> 32); + temp = ((jit_ulong)low1) * ((jit_ulong)high2); + orig = result2; + result2 += (jit_uint)temp; + if(result2 < orig) + result3 = (((jit_uint)(temp >> 32)) + 1); + else + result3 = ((jit_uint)(temp >> 32)); + temp = ((jit_ulong)high1) * ((jit_ulong)low2); + orig = result2; + result2 += (jit_uint)temp; + if(result2 < orig) + { + orig = result3; + result3 += (((jit_uint)(temp >> 32)) + 1); + if(result3 < orig) + result4 = 1; + else + result4 = 0; + } + else + { + orig = result3; + result3 += ((jit_uint)(temp >> 32)); + if(result3 < orig) + result4 = 1; + else + result4 = 0; + } + temp = ((jit_ulong)high1) * ((jit_ulong)high2); + orig = result3; + result3 += (jit_uint)temp; + if(result3 < orig) + result4 += ((jit_uint)(temp >> 32)) + 1; + else + result4 += ((jit_uint)(temp >> 32)); + if(result3 != 0 || result4 != 0) + { + *result = jit_max_ulong; + return 0; + } + *result = (((jit_ulong)result2) << 32) | ((jit_ulong)result1); + return 1; +} + +jit_ulong jit_ulong_neg(jit_ulong value1) +{ + return (jit_ulong)(-((jit_long)value1)); +} + +jit_ulong jit_ulong_and(jit_ulong value1, jit_ulong value2) +{ + return value1 & value2; +} + +jit_ulong jit_ulong_or(jit_ulong value1, jit_ulong value2) +{ + return value1 | value2; +} + +jit_ulong jit_ulong_xor(jit_ulong value1, jit_ulong value2) +{ + return value1 ^ value2; +} + +jit_ulong jit_ulong_not(jit_ulong value1) +{ + return ~value1; +} + +jit_ulong jit_ulong_shl(jit_ulong value1, jit_uint value2) +{ + return value1 << (value2 & 0x3F); +} + +jit_ulong jit_ulong_shr(jit_ulong value1, jit_uint value2) +{ + return value1 >> (value2 & 0x3F); +} + +jit_int jit_ulong_eq(jit_ulong value1, jit_ulong value2) +{ + return (value1 == value2); +} + +jit_int jit_ulong_ne(jit_ulong value1, jit_ulong value2) +{ + return (value1 != value2); +} + +jit_int jit_ulong_lt(jit_ulong value1, jit_ulong value2) +{ + return (value1 < value2); +} + +jit_int jit_ulong_le(jit_ulong value1, jit_ulong value2) +{ + return (value1 <= value2); +} + +jit_int jit_ulong_gt(jit_ulong value1, jit_ulong value2) +{ + return (value1 > value2); +} + +jit_int jit_ulong_ge(jit_ulong value1, jit_ulong value2) +{ + return (value1 >= value2); +} + +jit_int jit_ulong_cmp(jit_ulong value1, jit_ulong value2) +{ + if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_ulong jit_ulong_min(jit_ulong value1, jit_ulong value2) +{ + return ((value1 <= value2) ? value1 : value2); +} + +jit_ulong jit_ulong_max(jit_ulong value1, jit_ulong value2) +{ + return ((value1 >= value2) ? value1 : value2); +} + +/*@ + * @deftypefun jit_float32 jit_float32_add (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_sub (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_mul (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_div (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_rem (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_ieee_rem (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_neg (jit_float32 value1) + * Perform an arithmetic operation on 32-bit floating-point values. + * @end deftypefun + * + * @deftypefun jit_int jit_float32_eq (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_int jit_float32_ne (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_int jit_float32_lt (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_int jit_float32_le (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_int jit_float32_gt (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_int jit_float32_ge (jit_float32 value1, jit_float32 value2) + * Compare two 32-bit floating-point values, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_float32_cmpl (jit_float32 value1, jit_float32 value2) + * Compare two 32-bit floating-point values and return -1, 0, or 1 based + * on their relationship. If either value is "not a number", + * then -1 is returned. + * @end deftypefun + * + * @deftypefun jit_int jit_float32_cmpg (jit_float32 value1, jit_float32 value2) + * Compare two 32-bit floating-point values and return -1, 0, or 1 based + * on their relationship. If either value is "not a number", + * then 1 is returned. + * @end deftypefun + * + * @deftypefun jit_float32 jit_float32_abs (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_min (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_max (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_int jit_float32_sign (jit_float32 value1) + * Calculate the absolute value, minimum, maximum, or sign for + * 32-bit floating point values. + * @end deftypefun +@*/ +jit_float32 jit_float32_add(jit_float32 value1, jit_float32 value2) +{ + return value1 + value2; +} + +jit_float32 jit_float32_sub(jit_float32 value1, jit_float32 value2) +{ + return value1 - value2; +} + +jit_float32 jit_float32_mul(jit_float32 value1, jit_float32 value2) +{ + return value1 * value2; +} + +jit_float32 jit_float32_div(jit_float32 value1, jit_float32 value2) +{ + return value1 / value2; +} + +jit_float32 jit_float32_rem(jit_float32 value1, jit_float32 value2) +{ +#if defined(HAVE_FMODF) + return fmod(value1, value2); +#elif defined(HAVE_FMOD) + return fmod(value1, value2); +#elif defined(HAVE_CEILF) && defined(HAVE_FLOORF) + jit_float32 temp = value1 / value2; + if(jit_float32_is_nan(temp)) + { + return temp; + } + if(temp < (jit_float32)0.0) + { + temp = ceilf(temp); + } + else + { + temp = floorf(temp); + } + return value1 - temp * value2; +#elif defined(HAVE_CEIL) && defined(HAVE_FLOOR) + jit_float32 temp = value1 / value2; + if(jit_float32_is_nan(temp)) + { + return temp; + } + if(temp < (jit_float32)0.0) + { + temp = ceil(temp); + } + else + { + temp = floor(temp); + } + return value1 - temp * value2; +#else + /* Don't know how to compute remainders on this platform */ + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_ieee_rem(jit_float32 value1, jit_float32 value2) +{ +#if defined(HAVE_REMAINDERF) + return remainderf(value1, value2); +#elif defined(HAVE_REMAINDER) + return remainder(value1, value2); +#elif defined(HAVE_DREMF) + return dremf(value1, value2); +#elif defined(HAVE_DREM) + return drem(value1, value2); +#elif defined(HAVE_CEILF) && defined(HAVE_FLOORF) + jit_float32 temp = value1 / value2; + jit_float32 ceil_value, floor_value; + if(jit_float32_is_nan(temp)) + { + return temp; + } + ceil_value = ceilf(temp); + floor_value = floorf(temp); + if((temp - floor_value) < (jit_float32)0.5) + { + temp = floor_value; + } + else if((temp - floor_value) > (jit_float32)0.5) + { + temp = ceil_value; + } + else if((floor(ceil_value / (jit_float32)2.0) * (jit_float32)2.0) + == ceil_value) + { + temp = ceil_value; + } + else + { + temp = floor_value; + } + return value1 - temp * value2; +#elif defined(HAVE_CEIL) && defined(HAVE_FLOOR) + jit_float32 temp = value1 / value2; + jit_float32 ceil_value, floor_value; + if(jit_float32_is_nan(temp)) + { + return temp; + } + ceil_value = ceil(temp); + floor_value = floor(temp); + if((temp - floor_value) < (jit_float32)0.5) + { + temp = floor_value; + } + else if((temp - floor_value) > (jit_float32)0.5) + { + temp = ceil_value; + } + else if((floor(ceil_value / (jit_float32)2.0) * (jit_float32)2.0) + == ceil_value) + { + temp = ceil_value; + } + else + { + temp = floor_value; + } + return value1 - temp * value2; +#else + /* Don't know how to compute remainders on this platform */ + return (jit_float32)(0.0 / 0.0); +#endif +} + +jit_float32 jit_float32_neg(jit_float32 value1) +{ + return -value1; +} + +jit_int jit_float32_eq(jit_float32 value1, jit_float32 value2) +{ + return (value1 == value2); +} + +jit_int jit_float32_ne(jit_float32 value1, jit_float32 value2) +{ + return (value1 != value2); +} + +jit_int jit_float32_lt(jit_float32 value1, jit_float32 value2) +{ + return (value1 < value2); +} + +jit_int jit_float32_le(jit_float32 value1, jit_float32 value2) +{ + return (value1 <= value2); +} + +jit_int jit_float32_gt(jit_float32 value1, jit_float32 value2) +{ + return (value1 > value2); +} + +jit_int jit_float32_ge(jit_float32 value1, jit_float32 value2) +{ + return (value1 >= value2); +} + +jit_int jit_float32_cmpl(jit_float32 value1, jit_float32 value2) +{ + if(jit_float32_is_nan(value1) || jit_float32_is_nan(value2)) + { + return -1; + } + else if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_int jit_float32_cmpg(jit_float32 value1, jit_float32 value2) +{ + if(jit_float32_is_nan(value1) || jit_float32_is_nan(value2)) + { + return 1; + } + else if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_float32 jit_float32_abs(jit_float32 value1) +{ + if(jit_float32_is_nan(value1)) + { + return jit_float32_nan; + } + return ((value1 >= 0) ? value1 : -value1); +} + +jit_float32 jit_float32_min(jit_float32 value1, jit_float32 value2) +{ + if(jit_float32_is_nan(value1) || jit_float32_is_nan(value2)) + { + return jit_float32_nan; + } + return ((value1 <= value2) ? value1 : value2); +} + +jit_float32 jit_float32_max(jit_float32 value1, jit_float32 value2) +{ + if(jit_float32_is_nan(value1) || jit_float32_is_nan(value2)) + { + return jit_float32_nan; + } + return ((value1 >= value2) ? value1 : value2); +} + +jit_int jit_float32_sign(jit_float32 value1) +{ + if(jit_float32_is_nan(value1)) + { + return 0; + } + else if(value1 < 0) + { + return -1; + } + else if(value1 > 0) + { + return 0; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_float32 jit_float32_acos (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_asin (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_atan (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_atan2 (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_ceil (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_cos (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_cosh (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_exp (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_floor (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_log (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_log10 (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_pow (jit_float32 value1, jit_float32 value2) + * @deftypefunx jit_float32 jit_float32_sin (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_sinh (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_sqrt (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_tan (jit_float32 value1) + * @deftypefunx jit_float32 jit_float32_tanh (jit_float32 value1) + * Apply a mathematical function to one or two 32-bit floating-point values. + * @end deftypefun +@*/ +jit_float32 jit_float32_acos(jit_float32 value1) +{ +#if defined(HAVE_ACOSF) + return (jit_float32)(acosf(value1)); +#elif defined(HAVE_ACOS) + return (jit_float32)(acos(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_asin(jit_float32 value1) +{ +#if defined(HAVE_ASINF) + return (jit_float32)(asinf(value1)); +#elif defined(HAVE_ASIN) + return (jit_float32)(asin(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_atan(jit_float32 value1) +{ +#if defined(HAVE_ATANF) + return (jit_float32)(atanf(value1)); +#elif defined(HAVE_ATAN) + return (jit_float32)(atan(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_atan2(jit_float32 value1, jit_float32 value2) +{ +#if defined(HAVE_ATAN2F) + return (jit_float32)(atan2f(value1, value2)); +#elif defined(HAVE_ATAN2) + return (jit_float32)(atan2(value1, value2)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_ceil(jit_float32 value1) +{ +#if defined(HAVE_CEILF) + return (jit_float32)(ceilf(value1)); +#elif defined(HAVE_CEIL) + return (jit_float32)(ceil(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_cos(jit_float32 value1) +{ +#if defined(HAVE_COSF) + return (jit_float32)(cosf(value1)); +#elif defined(HAVE_COS) + return (jit_float32)(cos(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_cosh(jit_float32 value1) +{ +#if defined(HAVE_COSHF) + return (jit_float32)(coshf(value1)); +#elif defined(HAVE_COSH) + return (jit_float32)(cosh(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_exp(jit_float32 value1) +{ +#if defined(HAVE_EXPF) + return (jit_float32)(expf(value1)); +#elif defined(HAVE_EXP) + return (jit_float32)(exp(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_floor(jit_float32 value1) +{ +#if defined(HAVE_FLOORF) + return (jit_float32)(floorf(value1)); +#elif defined(HAVE_FLOOR) + return (jit_float32)(floor(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_log(jit_float32 value1) +{ +#if defined(HAVE_LOGF) + return (jit_float32)(logf(value1)); +#elif defined(HAVE_LOG) + return (jit_float32)(log(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_log10(jit_float32 value1) +{ +#if defined(HAVE_LOG10F) + return (jit_float32)(log10f(value1)); +#elif defined(HAVE_LOG10) + return (jit_float32)(log10(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_pow(jit_float32 value1, jit_float32 value2) +{ +#if defined(HAVE_POWF) + return (jit_float32)(powf(value1, value2)); +#elif defined(HAVE_POW) + return (jit_float32)(pow(value1, value2)); +#else + return jit_float32_nan; +#endif +} + +/*@ + * @deftypefun jit_float32 jit_float32_rint (jit_float32 value1) + * Round @code{value1} to the nearest integer. Half-way cases + * are rounded to an even number. + * @end deftypefun +@*/ +jit_float32 jit_float32_rint(jit_float32 value1) +{ + jit_float32 above, below; + if(!jit_float32_is_finite(value1)) + { + return value1; + } + above = jit_float32_ceil(value1); + below = jit_float32_floor(value1); + if((above - value1) < (jit_float32)0.5) + { + return above; + } + else if((value1 - below) < (jit_float32)0.5) + { + return below; + } + else if(jit_float32_ieee_rem(above, (jit_float32)2.0) == (jit_float32)0.0) + { + return above; + } + else + { + return below; + } +} + +/*@ + * @deftypefun jit_float32 jit_float32_round (jit_float32 value1) + * Round @code{value1} to the nearest integer. Half-way cases + * are rounded away from zero. + * @end deftypefun +@*/ +jit_float32 jit_float32_round(jit_float32 value1) +{ + jit_float32 above, below; + if(!jit_float32_is_finite(value1)) + { + return value1; + } + above = jit_float32_ceil(value1); + below = jit_float32_floor(value1); + if((above - value1) < (jit_float32)0.5) + { + return above; + } + else if((value1 - below) < (jit_float32)0.5) + { + return below; + } + else if(above >= (jit_float32)0.0) + { + return above; + } + else + { + return below; + } +} + +jit_float32 jit_float32_sin(jit_float32 value1) +{ +#if defined(HAVE_SINF) + return (jit_float32)(sinf(value1)); +#elif defined(HAVE_SIN) + return (jit_float32)(sin(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_sinh(jit_float32 value1) +{ +#if defined(HAVE_SINHF) + return (jit_float32)(sinhf(value1)); +#elif defined(HAVE_SINH) + return (jit_float32)(sinh(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_sqrt(jit_float32 value1) +{ +#if defined(HAVE_SQRTF) + return (jit_float32)(sqrtf(value1)); +#elif defined(HAVE_SQRT) + return (jit_float32)(sqrt(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_tan(jit_float32 value1) +{ +#if defined(HAVE_TANF) + return (jit_float32)(tanf(value1)); +#elif defined(HAVE_TAN) + return (jit_float32)(tan(value1)); +#else + return jit_float32_nan; +#endif +} + +jit_float32 jit_float32_tanh(jit_float32 value1) +{ +#if defined(HAVE_TANHF) + return (jit_float32)(tanhf(value1)); +#elif defined(HAVE_TANH) + return (jit_float32)(tanh(value1)); +#else + return jit_float32_nan; +#endif +} + +/*@ + * @deftypefun jit_int jit_float32_is_finite (jit_float32 value) + * Determine if a 32-bit floating point value is finite, returning + * non-zero if it is, or zero if it is not. If the value is + * "not a number", this function returns zero. + * @end deftypefun +@*/ +jit_int jit_float32_is_finite(jit_float32 value) +{ +#if defined(hpux) || defined(JIT_WIN32_NATIVE) + return isfinite(value); +#else /* !hpux */ +#if defined(HAVE_FINITEF) + return finitef(value); +#elif defined(HAVE_FINITE) + return finite(value); +#else /* !HAVE_FINITE */ +#if defined(HAVE_ISNANF) && defined(HAVE_ISINFF) + return (!isnanf(value) && isinff(value) == 0); +#elif defined(HAVE_ISNAN) && defined(HAVE_ISINF) + return (!isnan(value) && isinf(value) == 0); +#else + #error "Don't know how to determine if floating point numbers are finite" + return 1; +#endif +#endif /* !HAVE_FINITE */ +#endif /* !hpux */ +} + +/*@ + * @deftypefun jit_int jit_float32_is_nan (jit_float32 value) + * Determine if a 32-bit floating point value is "not a number", returning + * non-zero if it is, or zero if it is not. + * @end deftypefun +@*/ +jit_int jit_float32_is_nan(jit_float32 value) +{ +#if defined(HAVE_ISNANF) + return isnanf(value); +#elif defined(HAVE_ISNAN) + return isnan(value); +#else + return (value != value); +#endif +} + +/*@ + * @deftypefun jit_int jit_float32_is_inf (jit_float32 value) + * Determine if a 32-bit floating point value is infinite or not. + * Returns -1 for negative infinity, 1 for positive infinity, + * and 0 for everything else. + * + * Note: this function is preferable to the system @code{isinf} intrinsic + * because some systems have a broken @code{isinf} function that returns + * 1 for both positive and negative infinity. + * @end deftypefun +@*/ +jit_int jit_float32_is_inf(jit_float32 value) +{ + /* The code below works around broken "isinf" implementations */ +#if defined(HAVE_ISINFF) + if(isinff(value) == 0) + { + return 0; + } +#elif defined(HAVE_ISINF) + if(isinf(value) == 0) + { + return 0; + } +#else + if(jit_float32_is_nan(value) || jit_float32_is_finite(value)) + { + return 0; + } +#endif + if(value < (jit_float32)0.0) + { + return -1; + } + else + { + return 1; + } +} + +/*@ + * @deftypefun jit_float64 jit_float64_add (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_sub (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_mul (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_div (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_rem (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_ieee_rem (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_neg (jit_float64 value1) + * Perform an arithmetic operation on 64-bit floating-point values. + * @end deftypefun + * + * @deftypefun jit_int jit_float64_eq (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_int jit_float64_ne (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_int jit_float64_lt (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_int jit_float64_le (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_int jit_float64_gt (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_int jit_float64_ge (jit_float64 value1, jit_float64 value2) + * Compare two 64-bit floating-point values, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_float64_cmpl (jit_float64 value1, jit_float64 value2) + * Compare two 64-bit floating-point values and return -1, 0, or 1 based + * on their relationship. If either value is "not a number", + * then -1 is returned. + * @end deftypefun + * + * @deftypefun jit_int jit_float64_cmpg (jit_float64 value1, jit_float64 value2) + * Compare two 64-bit floating-point values and return -1, 0, or 1 based + * on their relationship. If either value is "not a number", + * then 1 is returned. + * @end deftypefun + * + * @deftypefun jit_float64 jit_float64_abs (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_min (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_max (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_int jit_float64_sign (jit_float64 value1) + * Calculate the absolute value, minimum, maximum, or sign for + * 64-bit floating point values. + * @end deftypefun +@*/ +jit_float64 jit_float64_add(jit_float64 value1, jit_float64 value2) +{ + return value1 + value2; +} + +jit_float64 jit_float64_sub(jit_float64 value1, jit_float64 value2) +{ + return value1 - value2; +} + +jit_float64 jit_float64_mul(jit_float64 value1, jit_float64 value2) +{ + return value1 * value2; +} + +jit_float64 jit_float64_div(jit_float64 value1, jit_float64 value2) +{ + return value1 / value2; +} + +jit_float64 jit_float64_rem(jit_float64 value1, jit_float64 value2) +{ +#if defined(HAVE_FMOD) + return fmod(value1, value2); +#elif defined(HAVE_CEIL) && defined(HAVE_FLOOR) + jit_float64 temp = value1 / value2; + if(jit_float64_is_nan(temp)) + { + return temp; + } + if(temp < (jit_float64)0.0) + { + temp = ceil(temp); + } + else + { + temp = floor(temp); + } + return value1 - temp * value2; +#else + /* Don't know how to compute remainders on this platform */ + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_ieee_rem(jit_float64 value1, jit_float64 value2) +{ +#if defined(HAVE_REMAINDER) + return remainder(value1, value2); +#elif defined(HAVE_DREM) + return drem(value1, value2); +#elif defined(HAVE_CEIL) && defined(HAVE_FLOOR) + jit_float64 temp = value1 / value2; + jit_float64 ceil_value, floor_value; + if(jit_float64_is_nan(temp)) + { + return temp; + } + ceil_value = ceil(temp); + floor_value = floor(temp); + if((temp - floor_value) < (jit_float64)0.5) + { + temp = floor_value; + } + else if((temp - floor_value) > (jit_float64)0.5) + { + temp = ceil_value; + } + else if((floor(ceil_value / (jit_float64)2.0) * (jit_float64)2.0) + == ceil_value) + { + temp = ceil_value; + } + else + { + temp = floor_value; + } + return value1 - temp * value2; +#else + /* Don't know how to compute remainders on this platform */ + return (jit_float64)(0.0 / 0.0); +#endif +} + +jit_float64 jit_float64_neg(jit_float64 value1) +{ + return -value1; +} + +jit_int jit_float64_eq(jit_float64 value1, jit_float64 value2) +{ + return (value1 == value2); +} + +jit_int jit_float64_ne(jit_float64 value1, jit_float64 value2) +{ + return (value1 != value2); +} + +jit_int jit_float64_lt(jit_float64 value1, jit_float64 value2) +{ + return (value1 < value2); +} + +jit_int jit_float64_le(jit_float64 value1, jit_float64 value2) +{ + return (value1 <= value2); +} + +jit_int jit_float64_gt(jit_float64 value1, jit_float64 value2) +{ + return (value1 > value2); +} + +jit_int jit_float64_ge(jit_float64 value1, jit_float64 value2) +{ + return (value1 >= value2); +} + +jit_int jit_float64_cmpl(jit_float64 value1, jit_float64 value2) +{ + if(jit_float64_is_nan(value1) || jit_float64_is_nan(value2)) + { + return -1; + } + else if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_int jit_float64_cmpg(jit_float64 value1, jit_float64 value2) +{ + if(jit_float64_is_nan(value1) || jit_float64_is_nan(value2)) + { + return 1; + } + else if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_float64 jit_float64_abs(jit_float64 value1) +{ + if(jit_float64_is_nan(value1)) + { + return jit_float64_nan; + } + return ((value1 >= 0) ? value1 : -value1); +} + +jit_float64 jit_float64_min(jit_float64 value1, jit_float64 value2) +{ + if(jit_float64_is_nan(value1) || jit_float64_is_nan(value2)) + { + return jit_float64_nan; + } + return ((value1 <= value2) ? value1 : value2); +} + +jit_float64 jit_float64_max(jit_float64 value1, jit_float64 value2) +{ + if(jit_float64_is_nan(value1) || jit_float64_is_nan(value2)) + { + return jit_float64_nan; + } + return ((value1 >= value2) ? value1 : value2); +} + +jit_int jit_float64_sign(jit_float64 value1) +{ + if(jit_float64_is_nan(value1)) + { + return 0; + } + else if(value1 < 0) + { + return -1; + } + else if(value1 > 0) + { + return 0; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_float64 jit_float64_acos (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_asin (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_atan (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_atan2 (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_ceil (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_cos (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_cosh (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_exp (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_floor (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_log (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_log10 (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_pow (jit_float64 value1, jit_float64 value2) + * @deftypefunx jit_float64 jit_float64_sin (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_sinh (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_sqrt (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_tan (jit_float64 value1) + * @deftypefunx jit_float64 jit_float64_tanh (jit_float64 value1) + * Apply a mathematical function to one or two 64-bit floating-point values. + * @end deftypefun +@*/ +jit_float64 jit_float64_acos(jit_float64 value1) +{ +#if defined(HAVE_ACOS) + return (jit_float64)(acos(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_asin(jit_float64 value1) +{ +#if defined(HAVE_ASIN) + return (jit_float64)(asin(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_atan(jit_float64 value1) +{ +#if defined(HAVE_ATAN) + return (jit_float64)(atan(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_atan2(jit_float64 value1, jit_float64 value2) +{ +#if defined(HAVE_ATAN2) + return (jit_float64)(atan2(value1, value2)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_ceil(jit_float64 value1) +{ +#if defined(HAVE_CEIL) + return (jit_float64)(ceil(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_cos(jit_float64 value1) +{ +#if defined(HAVE_COS) + return (jit_float64)(cos(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_cosh(jit_float64 value1) +{ +#if defined(HAVE_COSH) + return (jit_float64)(cosh(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_exp(jit_float64 value1) +{ +#if defined(HAVE_EXP) + return (jit_float64)(exp(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_floor(jit_float64 value1) +{ +#if defined(HAVE_FLOOR) + return (jit_float64)(floor(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_log(jit_float64 value1) +{ +#if defined(HAVE_LOG) + return (jit_float64)(log(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_log10(jit_float64 value1) +{ +#if defined(HAVE_LOG10) + return (jit_float64)(log10(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_pow(jit_float64 value1, jit_float64 value2) +{ +#if defined(HAVE_POW) + return (jit_float64)(pow(value1, value2)); +#else + return jit_float64_nan; +#endif +} + +/*@ + * @deftypefun jit_float64 jit_float64_rint (jit_float64 value1) + * Round @code{value1} to the nearest integer. Half-way cases + * are rounded to an even number. + * @end deftypefun +@*/ +jit_float64 jit_float64_rint(jit_float64 value1) +{ + jit_float64 above, below; + if(!jit_float64_is_finite(value1)) + { + return value1; + } + above = jit_float64_ceil(value1); + below = jit_float64_floor(value1); + if((above - value1) < (jit_float64)0.5) + { + return above; + } + else if((value1 - below) < (jit_float64)0.5) + { + return below; + } + else if(jit_float64_ieee_rem(above, (jit_float64)2.0) == (jit_float64)0.0) + { + return above; + } + else + { + return below; + } +} + +/*@ + * @deftypefun jit_float64 jit_float64_round (jit_float64 value1) + * Round @code{value1} to the nearest integer. Half-way cases + * are rounded away from zero. + * @end deftypefun +@*/ +jit_float64 jit_float64_round(jit_float64 value1) +{ + jit_float64 above, below; + if(!jit_float64_is_finite(value1)) + { + return value1; + } + above = jit_float64_ceil(value1); + below = jit_float64_floor(value1); + if((above - value1) < (jit_float64)0.5) + { + return above; + } + else if((value1 - below) < (jit_float64)0.5) + { + return below; + } + else if(above >= (jit_float64)0.0) + { + return above; + } + else + { + return below; + } +} + +jit_float64 jit_float64_sin(jit_float64 value1) +{ +#if defined(HAVE_SIN) + return (jit_float64)(sin(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_sinh(jit_float64 value1) +{ +#if defined(HAVE_SINH) + return (jit_float64)(sinh(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_sqrt(jit_float64 value1) +{ +#if defined(HAVE_SQRT) + return (jit_float64)(sqrt(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_tan(jit_float64 value1) +{ +#if defined(HAVE_TAN) + return (jit_float64)(tan(value1)); +#else + return jit_float64_nan; +#endif +} + +jit_float64 jit_float64_tanh(jit_float64 value1) +{ +#if defined(HAVE_TANH) + return (jit_float64)(tanh(value1)); +#else + return jit_float64_nan; +#endif +} + +/*@ + * @deftypefun jit_int jit_float64_is_finite (jit_float64 value) + * Determine if a 64-bit floating point value is finite, returning + * non-zero if it is, or zero if it is not. If the value is + * "not a number", this function returns zero. + * @end deftypefun +@*/ +jit_int jit_float64_is_finite(jit_float64 value) +{ +#if defined(hpux) || defined(JIT_WIN32_NATIVE) + return isfinite(value); +#else /* !hpux */ +#if defined(HAVE_FINITE) + return finite(value); +#else /* !HAVE_FINITE */ +#if defined(HAVE_ISNAN) && defined(HAVE_ISINF) + return (!isnan(value) && isinf(value) == 0); +#else + #error "Don't know how to determine if floating point numbers are finite" + return 1; +#endif +#endif /* !HAVE_FINITE */ +#endif /* !hpux */ +} + +/*@ + * @deftypefun jit_int jit_float64_is_nan (jit_float64 value) + * Determine if a 64-bit floating point value is "not a number", returning + * non-zero if it is, or zero if it is not. + * @end deftypefun +@*/ +jit_int jit_float64_is_nan(jit_float64 value) +{ +#if defined(HAVE_ISNAN) + return isnan(value); +#else + return (value != value); +#endif +} + +/*@ + * @deftypefun jit_int jit_float64_is_inf (jit_float64 value) + * Determine if a 64-bit floating point value is infinite or not. + * Returns -1 for negative infinity, 1 for positive infinity, + * and 0 for everything else. + * + * Note: this function is preferable to the system @code{isinf} intrinsic + * because some systems have a broken @code{isinf} function that returns + * 1 for both positive and negative infinity. + * @end deftypefun +@*/ +jit_int jit_float64_is_inf(jit_float64 value) +{ + /* The code below works around broken "isinf" implementations */ +#if defined(HAVE_ISINF) + if(isinf(value) == 0) + { + return 0; + } +#else + if(jit_float64_is_nan(value) || jit_float64_is_finite(value)) + { + return 0; + } +#endif + if(value < (jit_float64)0.0) + { + return -1; + } + else + { + return 1; + } +} + +/*@ + * @deftypefun jit_nfloat jit_nfloat_add (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_sub (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_mul (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_div (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_rem (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_ieee_rem (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_neg (jit_nfloat value1) + * Perform an arithmetic operation on native floating-point values. + * @end deftypefun + * + * @deftypefun jit_int jit_nfloat_eq (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_int jit_nfloat_ne (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_int jit_nfloat_lt (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_int jit_nfloat_le (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_int jit_nfloat_gt (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_int jit_nfloat_ge (jit_nfloat value1, jit_nfloat value2) + * Compare two native floating-point values, returning 0 or 1 based + * on their relationship. + * @end deftypefun + * + * @deftypefun jit_int jit_nfloat_cmpl (jit_nfloat value1, jit_nfloat value2) + * Compare two native floating-point values and return -1, 0, or 1 based + * on their relationship. If either value is "not a number", + * then -1 is returned. + * @end deftypefun + * + * @deftypefun jit_int jit_nfloat_cmpg (jit_nfloat value1, jit_nfloat value2) + * Compare two native floating-point values and return -1, 0, or 1 based + * on their relationship. If either value is "not a number", + * then 1 is returned. + * @end deftypefun + * + * @deftypefun jit_nfloat jit_nfloat_abs (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_min (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_max (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_int jit_nfloat_sign (jit_nfloat value1) + * Calculate the absolute value, minimum, maximum, or sign for + * native floating point values. + * @end deftypefun +@*/ +jit_nfloat jit_nfloat_add(jit_nfloat value1, jit_nfloat value2) +{ + return value1 + value2; +} + +jit_nfloat jit_nfloat_sub(jit_nfloat value1, jit_nfloat value2) +{ + return value1 - value2; +} + +jit_nfloat jit_nfloat_mul(jit_nfloat value1, jit_nfloat value2) +{ + return value1 * value2; +} + +jit_nfloat jit_nfloat_div(jit_nfloat value1, jit_nfloat value2) +{ + return value1 / value2; +} + +jit_nfloat jit_nfloat_rem(jit_nfloat value1, jit_nfloat value2) +{ +#if defined(HAVE_FMODL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return fmodl(value1, value2); +#elif defined(HAVE_FMOD) + return fmod(value1, value2); +#elif defined(HAVE_CEILL) && defined(HAVE_FLOORL) && \ + !defined(JIT_NFLOAT_IS_DOUBLE) + jit_nfloat temp = value1 / value2; + if(jit_nfloat_is_nan(temp)) + { + return temp; + } + if(temp < (jit_nfloat)0.0) + { + temp = ceill(temp); + } + else + { + temp = floorl(temp); + } + return value1 - temp * value2; +#elif defined(HAVE_CEIL) && defined(HAVE_FLOOR) + jit_nfloat temp = value1 / value2; + if(jit_nfloat_is_nan(temp)) + { + return temp; + } + if(temp < (jit_nfloat)0.0) + { + temp = ceil(temp); + } + else + { + temp = floor(temp); + } + return value1 - temp * value2; +#else + /* Don't know how to compute remainders on this platform */ + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_ieee_rem(jit_nfloat value1, jit_nfloat value2) +{ +#if defined(HAVE_REMAINDERL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return remainderl(value1, value2); +#elif defined(HAVE_REMAINDER) + return remainder(value1, value2); +#elif defined(HAVE_DREML) && !defined(JIT_NFLOAT_IS_DOUBLE) + return dreml(value1, value2); +#elif defined(HAVE_DREM) + return drem(value1, value2); +#elif defined(HAVE_CEILL) && defined(HAVE_FLOORL) && \ + !defined(JIT_NFLOAT_IS_DOUBLE) + jit_nfloat temp = value1 / value2; + jit_nfloat ceil_value, floor_value; + if(jit_nfloat_is_nan(temp)) + { + return temp; + } + ceil_value = ceill(temp); + floor_value = floorl(temp); + if((temp - floor_value) < (jit_nfloat)0.5) + { + temp = floor_value; + } + else if((temp - floor_value) > (jit_nfloat)0.5) + { + temp = ceil_value; + } + else if((floor(ceil_value / (jit_nfloat)2.0) * (jit_nfloat)2.0) + == ceil_value) + { + temp = ceil_value; + } + else + { + temp = floor_value; + } + return value1 - temp * value2; +#elif defined(HAVE_CEIL) && defined(HAVE_FLOOR) + jit_nfloat temp = value1 / value2; + jit_nfloat ceil_value, floor_value; + if(jit_nfloat_is_nan(temp)) + { + return temp; + } + ceil_value = ceil(temp); + floor_value = floor(temp); + if((temp - floor_value) < (jit_nfloat)0.5) + { + temp = floor_value; + } + else if((temp - floor_value) > (jit_nfloat)0.5) + { + temp = ceil_value; + } + else if((floor(ceil_value / (jit_nfloat)2.0) * (jit_nfloat)2.0) + == ceil_value) + { + temp = ceil_value; + } + else + { + temp = floor_value; + } + return value1 - temp * value2; +#else + /* Don't know how to compute remainders on this platform */ + return (jit_nfloat)(0.0 / 0.0); +#endif +} + +jit_nfloat jit_nfloat_neg(jit_nfloat value1) +{ + return -value1; +} + +jit_int jit_nfloat_eq(jit_nfloat value1, jit_nfloat value2) +{ + return (value1 == value2); +} + +jit_int jit_nfloat_ne(jit_nfloat value1, jit_nfloat value2) +{ + return (value1 != value2); +} + +jit_int jit_nfloat_lt(jit_nfloat value1, jit_nfloat value2) +{ + return (value1 < value2); +} + +jit_int jit_nfloat_le(jit_nfloat value1, jit_nfloat value2) +{ + return (value1 <= value2); +} + +jit_int jit_nfloat_gt(jit_nfloat value1, jit_nfloat value2) +{ + return (value1 > value2); +} + +jit_int jit_nfloat_ge(jit_nfloat value1, jit_nfloat value2) +{ + return (value1 >= value2); +} + +jit_int jit_nfloat_cmpl(jit_nfloat value1, jit_nfloat value2) +{ + if(jit_nfloat_is_nan(value1) || jit_nfloat_is_nan(value2)) + { + return -1; + } + else if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_int jit_nfloat_cmpg(jit_nfloat value1, jit_nfloat value2) +{ + if(jit_nfloat_is_nan(value1) || jit_nfloat_is_nan(value2)) + { + return 1; + } + else if(value1 < value2) + { + return -1; + } + else if(value1 > value2) + { + return 1; + } + else + { + return 0; + } +} + +jit_nfloat jit_nfloat_abs(jit_nfloat value1) +{ + if(jit_nfloat_is_nan(value1)) + { + return jit_nfloat_nan; + } + return ((value1 >= 0) ? value1 : -value1); +} + +jit_nfloat jit_nfloat_min(jit_nfloat value1, jit_nfloat value2) +{ + if(jit_nfloat_is_nan(value1) || jit_nfloat_is_nan(value2)) + { + return jit_nfloat_nan; + } + return ((value1 <= value2) ? value1 : value2); +} + +jit_nfloat jit_nfloat_max(jit_nfloat value1, jit_nfloat value2) +{ + if(jit_nfloat_is_nan(value1) || jit_nfloat_is_nan(value2)) + { + return jit_nfloat_nan; + } + return ((value1 >= value2) ? value1 : value2); +} + +jit_int jit_nfloat_sign(jit_nfloat value1) +{ + if(jit_nfloat_is_nan(value1)) + { + return 0; + } + else if(value1 < 0) + { + return -1; + } + else if(value1 > 0) + { + return 0; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_nfloat jit_nfloat_acos (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_asin (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_atan (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_atan2 (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_ceil (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_cos (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_cosh (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_exp (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_floor (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_log (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_log10 (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_pow (jit_nfloat value1, jit_nfloat value2) + * @deftypefunx jit_nfloat jit_nfloat_sin (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_sinh (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_sqrt (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_tan (jit_nfloat value1) + * @deftypefunx jit_nfloat jit_nfloat_tanh (jit_nfloat value1) + * Apply a mathematical function to one or two native floating-point values. + * @end deftypefun +@*/ +jit_nfloat jit_nfloat_acos(jit_nfloat value1) +{ +#if defined(HAVE_ACOSL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(acosl(value1)); +#elif defined(HAVE_ACOS) + return (jit_nfloat)(acos(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_asin(jit_nfloat value1) +{ +#if defined(HAVE_ASINL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(asinl(value1)); +#elif defined(HAVE_ASIN) + return (jit_nfloat)(asin(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_atan(jit_nfloat value1) +{ +#if defined(HAVE_ATANL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(atanl(value1)); +#elif defined(HAVE_ATAN) + return (jit_nfloat)(atan(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_atan2(jit_nfloat value1, jit_nfloat value2) +{ +#if defined(HAVE_ATAN2L) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(atan2l(value1, value2)); +#elif defined(HAVE_ATAN2) + return (jit_nfloat)(atan2(value1, value2)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_ceil(jit_nfloat value1) +{ +#if defined(HAVE_CEILL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(ceill(value1)); +#elif defined(HAVE_CEIL) + return (jit_nfloat)(ceil(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_cos(jit_nfloat value1) +{ +#if defined(HAVE_COSL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(cosl(value1)); +#elif defined(HAVE_COS) + return (jit_nfloat)(cos(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_cosh(jit_nfloat value1) +{ +#if defined(HAVE_COSHL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(coshl(value1)); +#elif defined(HAVE_COSH) + return (jit_nfloat)(cosh(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_exp(jit_nfloat value1) +{ +#if defined(HAVE_EXPL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(expl(value1)); +#elif defined(HAVE_EXP) + return (jit_nfloat)(exp(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_floor(jit_nfloat value1) +{ +#if defined(HAVE_FLOORL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(floorl(value1)); +#elif defined(HAVE_FLOOR) + return (jit_nfloat)(floor(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_log(jit_nfloat value1) +{ +#if defined(HAVE_LOGL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(logl(value1)); +#elif defined(HAVE_LOG) + return (jit_nfloat)(log(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_log10(jit_nfloat value1) +{ +#if defined(HAVE_LOG10L) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(log10l(value1)); +#elif defined(HAVE_LOG10) + return (jit_nfloat)(log10(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_pow(jit_nfloat value1, jit_nfloat value2) +{ +#if defined(HAVE_POWL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(powl(value1, value2)); +#elif defined(HAVE_POW) + return (jit_nfloat)(pow(value1, value2)); +#else + return jit_nfloat_nan; +#endif +} + +/*@ + * @deftypefun jit_nfloat jit_nfloat_rint (jit_nfloat value1) + * Round @code{value1} to the nearest integer. Half-way cases + * are rounded to an even number. + * @end deftypefun +@*/ +jit_nfloat jit_nfloat_rint(jit_nfloat value1) +{ + jit_nfloat above, below; + if(!jit_nfloat_is_finite(value1)) + { + return value1; + } + above = jit_nfloat_ceil(value1); + below = jit_nfloat_floor(value1); + if((above - value1) < (jit_nfloat)0.5) + { + return above; + } + else if((value1 - below) < (jit_nfloat)0.5) + { + return below; + } + else if(jit_nfloat_ieee_rem(above, (jit_nfloat)2.0) == (jit_nfloat)0.0) + { + return above; + } + else + { + return below; + } +} + +/*@ + * @deftypefun jit_nfloat jit_nfloat_round (jit_nfloat value1) + * Round @code{value1} to the nearest integer. Half-way cases + * are rounded away from zero. + * @end deftypefun +@*/ +jit_nfloat jit_nfloat_round(jit_nfloat value1) +{ + jit_nfloat above, below; + if(!jit_nfloat_is_finite(value1)) + { + return value1; + } + above = jit_nfloat_ceil(value1); + below = jit_nfloat_floor(value1); + if((above - value1) < (jit_nfloat)0.5) + { + return above; + } + else if((value1 - below) < (jit_nfloat)0.5) + { + return below; + } + else if(above >= (jit_nfloat)0.0) + { + return above; + } + else + { + return below; + } +} + +jit_nfloat jit_nfloat_sin(jit_nfloat value1) +{ +#if defined(HAVE_SINL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(sinl(value1)); +#elif defined(HAVE_SIN) + return (jit_nfloat)(sin(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_sinh(jit_nfloat value1) +{ +#if defined(HAVE_SINHL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(sinhl(value1)); +#elif defined(HAVE_SINH) + return (jit_nfloat)(sinh(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_sqrt(jit_nfloat value1) +{ +#if defined(HAVE_SQRTL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(sqrtl(value1)); +#elif defined(HAVE_SQRT) + return (jit_nfloat)(sqrt(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_tan(jit_nfloat value1) +{ +#if defined(HAVE_TANL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(tanl(value1)); +#elif defined(HAVE_TAN) + return (jit_nfloat)(tan(value1)); +#else + return jit_nfloat_nan; +#endif +} + +jit_nfloat jit_nfloat_tanh(jit_nfloat value1) +{ +#if defined(HAVE_TANHL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return (jit_nfloat)(tanhl(value1)); +#elif defined(HAVE_TANH) + return (jit_nfloat)(tanh(value1)); +#else + return jit_nfloat_nan; +#endif +} + +/*@ + * @deftypefun jit_int jit_nfloat_is_finite (jit_nfloat value) + * Determine if a native floating point value is finite, returning + * non-zero if it is, or zero if it is not. If the value is + * "not a number", this function returns zero. + * @end deftypefun +@*/ +jit_int jit_nfloat_is_finite(jit_nfloat value) +{ +#if defined(hpux) || defined(JIT_WIN32_NATIVE) + return isfinite(value); +#else /* !hpux */ +#if defined(HAVE_FINITEL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return finitel(value); +#elif defined(HAVE_FINITE) + return finite(value); +#else /* !HAVE_FINITE */ +#if defined(HAVE_ISNANL) && defined(HAVE_ISINFL) && \ + !defined(JIT_NFLOAT_IS_DOUBLE) + return (!isnanl(value) && isinfl(value) == 0); +#elif defined(HAVE_ISNAN) && defined(HAVE_ISINF) + return (!isnan(value) && isinf(value) == 0); +#else + #error "Don't know how to determine if floating point numbers are finite" + return 1; +#endif +#endif /* !HAVE_FINITE */ +#endif /* !hpux */ +} + +/*@ + * @deftypefun jit_int jit_nfloat_is_nan (jit_nfloat value) + * Determine if a native floating point value is "not a number", returning + * non-zero if it is, or zero if it is not. + * @end deftypefun +@*/ +jit_int jit_nfloat_is_nan(jit_nfloat value) +{ +#if defined(HAVE_ISNANL) && !defined(JIT_NFLOAT_IS_DOUBLE) + return isnanl(value); +#elif defined(HAVE_ISNAN) + return isnan(value); +#else + return (value != value); +#endif +} + +/*@ + * @deftypefun jit_int jit_nfloat_is_inf (jit_nfloat value) + * Determine if a native floating point value is infinite or not. + * Returns -1 for negative infinity, 1 for positive infinity, + * and 0 for everything else. + * + * Note: this function is preferable to the system @code{isinf} intrinsic + * because some systems have a broken @code{isinf} function that returns + * 1 for both positive and negative infinity. + * @end deftypefun +@*/ +jit_int jit_nfloat_is_inf(jit_nfloat value) +{ + /* The code below works around broken "isinf" implementations */ +#if defined(HAVE_ISINFL) && !defined(JIT_NFLOAT_IS_DOUBLE) + if(isinfl(value) == 0) + { + return 0; + } +#elif defined(HAVE_ISINF) + if(isinf(value) == 0) + { + return 0; + } +#else + if(jit_nfloat_is_nan(value) || jit_nfloat_is_finite(value)) + { + return 0; + } +#endif + if(value < (jit_nfloat)0.0) + { + return -1; + } + else + { + return 1; + } +} + +/*@ + * @deftypefun jit_int jit_int_to_sbyte (jit_int value) + * @deftypefunx jit_int jit_int_to_ubyte (jit_int value) + * @deftypefunx jit_int jit_int_to_short (jit_int value) + * @deftypefunx jit_int jit_int_to_ushort (jit_int value) + * @deftypefunx jit_int jit_int_to_int (jit_int value) + * @deftypefunx jit_uint jit_int_to_uint (jit_int value) + * @deftypefunx jit_long jit_int_to_long (jit_int value) + * @deftypefunx jit_ulong jit_int_to_ulong (jit_int value) + * @deftypefunx jit_int jit_uint_to_int (jit_uint value) + * @deftypefunx jit_uint jit_uint_to_uint (jit_uint value) + * @deftypefunx jit_long jit_uint_to_long (jit_uint value) + * @deftypefunx jit_ulong jit_uint_to_ulong (jit_uint value) + * @deftypefunx jit_int jit_long_to_int (jit_long value) + * @deftypefunx jit_uint jit_long_to_uint (jit_long value) + * @deftypefunx jit_long jit_long_to_long (jit_long value) + * @deftypefunx jit_ulong jit_long_to_ulong (jit_long value) + * @deftypefunx jit_int jit_ulong_to_int (jit_ulong value) + * @deftypefunx jit_uint jit_ulong_to_uint (jit_ulong value) + * @deftypefunx jit_long jit_ulong_to_long (jit_ulong value) + * @deftypefunx jit_ulong jit_ulong_to_ulong (jit_ulong value) + * Convert between integer types. + * @end deftypefun +@*/ +jit_int jit_int_to_sbyte(jit_int value) +{ + return (jit_int)(jit_sbyte)value; +} + +jit_int jit_int_to_ubyte(jit_int value) +{ + return (jit_int)(jit_ubyte)value; +} + +jit_int jit_int_to_short(jit_int value) +{ + return (jit_int)(jit_short)value; +} + +jit_int jit_int_to_ushort(jit_int value) +{ + return (jit_int)(jit_ushort)value; +} + +jit_int jit_int_to_int(jit_int value) +{ + return value; +} + +jit_uint jit_int_to_uint(jit_int value) +{ + return (jit_uint)value; +} + +jit_long jit_int_to_long(jit_int value) +{ + return (jit_long)value; +} + +jit_ulong jit_int_to_ulong(jit_int value) +{ + return (jit_ulong)(jit_long)value; +} + +jit_int jit_uint_to_int(jit_uint value) +{ + return (jit_int)value; +} + +jit_uint jit_uint_to_uint(jit_uint value) +{ + return value; +} + +jit_long jit_uint_to_long(jit_uint value) +{ + return (jit_long)value; +} + +jit_ulong jit_uint_to_ulong(jit_uint value) +{ + return (jit_long)value; +} + +jit_int jit_long_to_int(jit_long value) +{ + return (jit_int)value; +} + +jit_uint jit_long_to_uint(jit_long value) +{ + return (jit_uint)value; +} + +jit_long jit_long_to_long(jit_long value) +{ + return value; +} + +jit_ulong jit_long_to_ulong(jit_long value) +{ + return (jit_ulong)value; +} + +jit_int jit_ulong_to_int(jit_ulong value) +{ + return (jit_int)value; +} + +jit_uint jit_ulong_to_uint(jit_ulong value) +{ + return (jit_uint)value; +} + +jit_long jit_ulong_to_long(jit_ulong value) +{ + return (jit_long)value; +} + +jit_ulong jit_ulong_to_ulong(jit_ulong value) +{ + return value; +} + +/*@ + * @deftypefun jit_int jit_int_to_sbyte_ovf ({jit_int *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_ubyte_ovf ({jit_int *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_short_ovf ({jit_int *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_ushort_ovf ({jit_int *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_int_ovf ({jit_int *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_uint_ovf ({jit_uint *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_long_ovf ({jit_long *result}, jit_int value) + * @deftypefunx jit_int jit_int_to_ulong_ovf ({jit_ulong *result}, jit_int value) + * @deftypefunx jit_int jit_uint_to_int_ovf ({jit_int *result}, jit_uint value) + * @deftypefunx jit_int jit_uint_to_uint_ovf ({jit_uint *result}, jit_uint value) + * @deftypefunx jit_int jit_uint_to_long_ovf ({jit_long *result}, jit_uint value) + * @deftypefunx jit_int jit_uint_to_ulong_ovf ({jit_ulong *result}, jit_uint value) + * @deftypefunx jit_int jit_long_to_int_ovf ({jit_int *result}, jit_long value) + * @deftypefunx jit_int jit_long_to_uint_ovf ({jit_uint *result}, jit_long value) + * @deftypefunx jit_int jit_long_to_long_ovf ({jit_long *result}, jit_long value) + * @deftypefunx jit_int jit_long_to_ulong_ovf ({jit_ulong *result}, jit_long value) + * @deftypefunx jit_int jit_ulong_to_int_ovf ({jit_int *result}, jit_ulong value) + * @deftypefunx jit_int jit_ulong_to_uint_ovf ({jit_uint *result}, jit_ulong value) + * @deftypefunx jit_int jit_ulong_to_long_ovf ({jit_long *result}, jit_ulong value) + * @deftypefunx jit_int jit_ulong_to_ulong_ovf ({jit_ulong *result}, jit_ulong value) + * Convert between integer types with overflow detection. + * @end deftypefun +@*/ +jit_int jit_int_to_sbyte_ovf(jit_int *result, jit_int value) +{ + return ((*result = (jit_int)(jit_sbyte)value) == value); +} + +jit_int jit_int_to_ubyte_ovf(jit_int *result, jit_int value) +{ + return ((*result = (jit_int)(jit_ubyte)value) == value); +} + +jit_int jit_int_to_short_ovf(jit_int *result, jit_int value) +{ + return ((*result = (jit_int)(jit_short)value) == value); +} + +jit_int jit_int_to_ushort_ovf(jit_int *result, jit_int value) +{ + return ((*result = (jit_int)(jit_ushort)value) == value); +} + +jit_int jit_int_to_int_ovf(jit_int *result, jit_int value) +{ + *result = value; + return 1; +} + +jit_int jit_int_to_uint_ovf(jit_uint *result, jit_int value) +{ + *result = (jit_uint)value; + return (value >= 0); +} + +jit_int jit_int_to_long_ovf(jit_long *result, jit_int value) +{ + *result = (jit_long)value; + return 1; +} + +jit_int jit_int_to_ulong_ovf(jit_ulong *result, jit_int value) +{ + *result = (jit_ulong)(jit_long)value; + return (value >= 0); +} + +jit_int jit_uint_to_int_ovf(jit_int *result, jit_uint value) +{ + *result = (jit_int)value; + return (*result >= 0); +} + +jit_int jit_uint_to_uint_ovf(jit_uint *result, jit_uint value) +{ + *result = value; + return 1; +} + +jit_int jit_uint_to_long_ovf(jit_long *result, jit_uint value) +{ + *result = (jit_long)value; + return 1; +} + +jit_int jit_uint_to_ulong_ovf(jit_ulong *result, jit_uint value) +{ + *result = (jit_ulong)value; + return 1; +} + +jit_int jit_long_to_int_ovf(jit_int *result, jit_long value) +{ + *result = (jit_int)value; + return (((jit_long)(*result)) == value); +} + +jit_int jit_long_to_uint_ovf(jit_uint *result, jit_long value) +{ + *result = (jit_uint)value; + return (((jit_long)(*result)) == value); +} + +jit_int jit_long_to_long_ovf(jit_long *result, jit_long value) +{ + *result = value; + return 1; +} + +jit_int jit_long_to_ulong_ovf(jit_ulong *result, jit_long value) +{ + *result = (jit_ulong)value; + return (value >= 0); +} + +jit_int jit_ulong_to_int_ovf(jit_int *result, jit_ulong value) +{ + *result = (jit_int)value; + return (value <= (jit_ulong)(jit_long)jit_max_int); +} + +jit_int jit_ulong_to_uint_ovf(jit_uint *result, jit_ulong value) +{ + *result = (jit_uint)value; + return (value <= (jit_ulong)jit_max_uint); +} + +jit_int jit_ulong_to_long_ovf(jit_long *result, jit_ulong value) +{ + *result = (jit_long)value; + return (*result >= 0); +} + +jit_int jit_ulong_to_ulong_ovf(jit_ulong *result, jit_ulong value) +{ + *result = value; + return 1; +} + +/*@ + * @deftypefun jit_int jit_nfloat_to_int (jit_nfloat value) + * @deftypefunx jit_uint jit_nfloat_to_uint (jit_nfloat value) + * @deftypefunx jit_long jit_nfloat_to_long (jit_nfloat value) + * @deftypefunx jit_ulong jit_nfloat_to_ulong (jit_nfloat value) + * Convert a native floating-point value into an integer. + * @end deftypefun +@*/ +jit_int jit_nfloat_to_int(jit_nfloat value) +{ + return (jit_int)value; +} + +jit_uint jit_nfloat_to_uint(jit_nfloat value) +{ + return (jit_uint)value; +} + +jit_long jit_nfloat_to_long(jit_nfloat value) +{ + return (jit_long)value; +} + +jit_ulong jit_nfloat_to_ulong(jit_nfloat value) +{ + /* Some platforms cannot perform the conversion directly, + so we need to do it in stages */ + if(jit_nfloat_is_finite(value)) + { + if(value >= (jit_nfloat)0.0) + { + if(value < (jit_nfloat)9223372036854775808.0) + { + return (jit_ulong)(jit_long)value; + } + else if(value < (jit_nfloat)18446744073709551616.0) + { + jit_long temp = (jit_long)(value - 9223372036854775808.0); + return (jit_ulong)(temp - jit_min_long); + } + else + { + return jit_max_ulong; + } + } + else + { + return 0; + } + } + else if(jit_nfloat_is_nan(value)) + { + return 0; + } + else if(value < (jit_nfloat)0.0) + { + return 0; + } + else + { + return jit_max_ulong; + } +} + +/*@ + * @deftypefun jit_int jit_nfloat_to_int_ovf ({jit_int *} result, jit_nfloat value) + * @deftypefunx jit_uint jit_nfloat_to_uint_ovf ({jit_uint *} result, jit_nfloat value) + * @deftypefunx jit_long jit_nfloat_to_long_ovf ({jit_long *} result, jit_nfloat value) + * @deftypefunx jit_ulong jit_nfloat_to_ulong_ovf ({jit_ulong *} result, jit_nfloat value) + * Convert a native floating-point value into an integer, + * with overflow detection. Returns @code{JIT_RESULT_OK} if the conversion + * was successful or @code{JIT_RESULT_OVERFLOW} if an overflow occurred. + * @end deftypefun +@*/ +jit_int jit_nfloat_to_int_ovf(jit_int *result, jit_nfloat value) +{ + if(jit_nfloat_is_finite(value)) + { + if(value > (jit_nfloat)(-2147483649.0) && + value < (jit_nfloat)2147483648.0) + { + *result = jit_nfloat_to_int(value); + return 1; + } + } + return 0; +} + +jit_int jit_nfloat_to_uint_ovf(jit_uint *result, jit_nfloat value) +{ + if(jit_nfloat_is_finite(value)) + { + if(value >= (jit_nfloat)0.0 && + value < (jit_nfloat)4294967296.0) + { + *result = jit_nfloat_to_uint(value); + return 1; + } + } + return 0; +} + +jit_int jit_nfloat_to_long_ovf(jit_long *result, jit_nfloat value) +{ + if(jit_nfloat_is_finite(value)) + { + if(value >= (jit_nfloat)-9223372036854775808.0 && + value < (jit_nfloat)9223372036854775808.0) + { + *result = jit_nfloat_to_long(value); + return 1; + } + else if(value < (jit_nfloat)0.0) + { + /* Account for the range -9223372036854775809.0 to + -9223372036854775808.0, which may get rounded + off if we aren't careful */ + value += (jit_nfloat)9223372036854775808.0; + if(value > (jit_nfloat)(-1.0)) + { + *result = jit_min_long; + return 1; + } + } + } + return 0; +} + +jit_int jit_nfloat_to_ulong_ovf(jit_ulong *result, jit_nfloat value) +{ + if(jit_nfloat_is_finite(value)) + { + if(value >= (jit_nfloat)0.0) + { + if(value < (jit_nfloat)18446744073709551616.0) + { + *result = jit_nfloat_to_ulong(value); + return 1; + } + } + } + return 0; +} + +/*@ + * @deftypefun jit_nfloat jit_int_to_nfloat (jit_int value) + * @deftypefunx jit_nfloat jit_uint_to_nfloat (jit_uint value) + * @deftypefunx jit_nfloat jit_long_to_nfloat (jit_long value) + * @deftypefunx jit_nfloat jit_ulong_to_nfloat (jit_ulong value) + * Convert an integer into native floating-point value. + * @end deftypefun +@*/ +jit_nfloat jit_int_to_nfloat(jit_int value) +{ + return (jit_nfloat)value; +} + +jit_nfloat jit_uint_to_nfloat(jit_uint value) +{ + return (jit_nfloat)value; +} + +jit_nfloat jit_long_to_nfloat(jit_long value) +{ + return (jit_nfloat)value; +} + +jit_nfloat jit_ulong_to_nfloat(jit_ulong value) +{ + /* Some platforms cannot perform the conversion directly, + so we need to do it in stages */ + if(value < (((jit_ulong)1) << 63)) + { + return (jit_nfloat)(jit_long)value; + } + else + { + return ((jit_nfloat)(((jit_long)value) + jit_min_long)) + + (jit_nfloat)9223372036854775808.0; + } +} + +/*@ + * @deftypefun jit_nfloat jit_float32_to_nfloat (jit_float32 value) + * @deftypefunx jit_nfloat jit_float64_to_nfloat (jit_float64 value) + * @deftypefunx jit_float32 jit_nfloat_to_float32 (jit_nfloat value) + * @deftypefunx jit_float64 jit_nfloat_to_float64 (jit_nfloat value) + * Convert between floating-point types. + * @end deftypefun +@*/ +jit_nfloat jit_float32_to_nfloat(jit_float32 value) +{ + return (jit_nfloat)value; +} + +jit_nfloat jit_float64_to_nfloat(jit_float64 value) +{ + return (jit_nfloat)value; +} + +jit_float32 jit_nfloat_to_float32(jit_nfloat value) +{ + return (jit_float32)value; +} + +jit_float64 jit_nfloat_to_float64(jit_nfloat value) +{ + return (jit_float64)value; +} diff --git a/jit/jit-live.c b/jit/jit-live.c new file mode 100644 index 0000000..882a54b --- /dev/null +++ b/jit/jit-live.c @@ -0,0 +1,205 @@ +/* + * jit-live.c - Liveness analysis for function bodies. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" + +/* + * Compute liveness information for a basic block. + */ +static void compute_liveness_for_block(jit_block_t block) +{ + jit_insn_iter_t iter; + jit_insn_t insn; + jit_value_t dest; + jit_value_t value1; + jit_value_t value2; + int flags; + + /* Scan backwards to compute the liveness flags */ + jit_insn_iter_init_last(&iter, block); + while((insn = jit_insn_iter_previous(&iter)) != 0) + { + /* Skip NOP instructions, which may have arguments left + over from when the instruction was replaced, but which + are not relevant to our liveness analysis */ + if(insn->opcode == JIT_OP_NOP) + { + continue; + } + + /* Fetch the value parameters to this instruction */ + flags = insn->flags; + if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0) + { + dest = insn->dest; + if(dest && dest->is_constant) + { + dest = 0; + } + } + else + { + dest = 0; + } + if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) + { + value1 = insn->value1; + if(value1 && value1->is_constant) + { + value1 = 0; + } + } + else + { + value1 = 0; + } + if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) + { + value2 = insn->value2; + if(value2 && value2->is_constant) + { + value2 = 0; + } + } + else + { + value2 = 0; + } + + /* Record the liveness information in the instruction flags */ + flags &= ~JIT_INSN_LIVENESS_FLAGS; + if(dest) + { + if(dest->live) + { + flags |= JIT_INSN_DEST_LIVE; + } + if(dest->next_use) + { + flags |= JIT_INSN_DEST_NEXT_USE; + } + } + if(value1) + { + if(value1->live) + { + flags |= JIT_INSN_VALUE1_LIVE; + } + if(value1->next_use) + { + flags |= JIT_INSN_VALUE1_NEXT_USE; + } + } + if(value2) + { + if(value2->live) + { + flags |= JIT_INSN_VALUE2_LIVE; + } + if(value2->next_use) + { + flags |= JIT_INSN_VALUE2_NEXT_USE; + } + } + insn->flags = (short)flags; + + /* Set the destination to "not live, no next use" */ + if(dest) + { + if((flags & JIT_INSN_DEST_IS_VALUE) == 0) + { + if(!(dest->next_use) && !(dest->live)) + { + /* There is no next use of this value and it is not + live on exit from the block. So we can discard + the entire instruction as it will have no effect */ + insn->opcode = (short)JIT_OP_NOP; + continue; + } + dest->live = 0; + dest->next_use = 0; + } + else + { + /* The destination is actually a source value for this + instruction (e.g. JIT_OP_STORE_RELATIVE_*) */ + dest->live = 1; + dest->next_use = 1; + } + } + + /* Set value1 and value2 to "live, next use" */ + if(value1) + { + value1->live = 1; + value1->next_use = 1; + } + if(value2) + { + value2->live = 1; + value2->next_use = 1; + } + } + + /* Re-scan the block to reset the liveness flags on all non-temporaries + because we need them in the original state for the next block */ + jit_insn_iter_init_last(&iter, block); + while((insn = jit_insn_iter_previous(&iter)) != 0) + { + flags = insn->flags; + if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0) + { + dest = insn->dest; + if(dest && !(dest->is_constant) && !(dest->is_temporary)) + { + dest->live = 1; + dest->next_use = 0; + } + } + if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) + { + value1 = insn->value1; + if(value1 && !(value1->is_constant) && !(value1->is_temporary)) + { + value1->live = 1; + value1->next_use = 0; + } + } + if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) + { + value2 = insn->value2; + if(value2 && !(value2->is_constant) && !(value2->is_temporary)) + { + value2->live = 1; + value2->next_use = 0; + } + } + } +} + +void _jit_function_compute_liveness(jit_function_t func) +{ + jit_block_t block = func->builder->first_block; + while(block != 0) + { + compute_liveness_for_block(block); + block = block->next; + } +} diff --git a/jit/jit-memory.c b/jit/jit-memory.c new file mode 100644 index 0000000..06056ab --- /dev/null +++ b/jit/jit-memory.c @@ -0,0 +1,178 @@ +/* + * jit-memory.c - Memory copy/set/compare routines. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-memory.h" + +/* + * Undefine the macros in "jit-memory.h" so that we + * can define the real function forms. + */ +#undef jit_memset +#undef jit_memcpy +#undef jit_memmove +#undef jit_memcmp +#undef jit_memchr + +/*@ + * @section Memory set, copy, compare, etc + * @cindex Memory operations + * + * The following functions are provided to set, copy, compare, and search + * memory blocks. +@*/ + +/*@ + * @deftypefun {void *} jit_memset ({void *} dest, int ch, {unsigned int} len) + * Set the @code{len} bytes at @code{dest} to the value @code{ch}. + * Returns @code{dest}. + * @end deftypefun +@*/ +void *jit_memset(void *dest, int ch, unsigned int len) +{ +#ifdef HAVE_MEMSET + return memset(dest, ch, len); +#else + unsigned char *d = (unsigned char *)dest; + while(len > 0) + { + *d++ = (unsigned char)ch; + --len; + } + return dest; +#endif +} + +/*@ + * @deftypefun {void *} jit_memcpy ({void *} dest, {const void *} src, {unsigned int} len) + * Copy the @code{len} bytes at @code{src} to @code{dest}. Returns + * @code{dest}. The behavior is undefined if the blocks overlap + * (use @code{jit_memmove} instead for that case). + * @end deftypefun +@*/ +void *jit_memcpy(void *dest, const void *src, unsigned int len) +{ +#if defined(HAVE_MEMCPY) + return memcpy(dest, src, len); +#elif defined(HAVE_BCOPY) + bcopy(src, dest, len); + return dest; +#else + unsigned char *d = (unsigned char *)dest; + const unsigned char *s = (const unsigned char *)src; + while(len > 0) + { + *d++ = *s++; + --len; + } + return dest; +#endif +} + +/*@ + * @deftypefun {void *} jit_memmove ({void *} dest, {const void *} src, {unsigned int} len) + * Copy the @code{len} bytes at @code{src} to @code{dest} and handle + * overlapping blocks correctly. Returns @code{dest}. + * @end deftypefun +@*/ +void *jit_memmove(void *dest, const void *src, unsigned int len) +{ +#ifdef HAVE_MEMMOVE + return memmove(dest, src, len); +#else + unsigned char *d = (unsigned char *)dest; + const unsigned char *s = (const unsigned char *)src; + if(((const unsigned char *)d) < s) + { + while(len > 0) + { + *d++ = *s++; + --len; + } + } + else + { + d += len; + s += len; + while(len > 0) + { + *(--d) = *(--s); + --len; + } + } + return dest; +#endif +} + +/*@ + * @deftypefun int jit_memcmp ({const void *} s1, {const void *} s2, {unsigned int} len) + * Compare @code{len} bytes at @code{s1} and @code{s2}, returning a negative, + * zero, or positive result depending upon their relationship. It is + * system-specific as to whether this function uses signed or unsigned + * byte comparisons. + * @end deftypefun +@*/ +int jit_memcmp(const void *s1, const void *s2, unsigned int len) +{ +#if defined(HAVE_MEMCMP) + return memcmp(s1, s2, len); +#elif defined(HAVE_BCMP) + return bcmp(s1, s2, len); +#else + const unsigned char *str1 = (const unsigned char *)s1; + const unsigned char *str2 = (const unsigned char *)s2; + while(len > 0) + { + if(*str1 < *str2) + return -1; + else if(*str1 > *str2) + return 1; + ++str1; + ++str2; + --len; + } + return 0; +#endif +} + +/*@ + * @deftypefun {void *} jit_memchr ({void *} str, int ch, {unsigned int} len) + * Search the @code{len} bytes at @code{str} for the first instance of + * the value @code{ch}. Returns the location of @code{ch} if it was found, + * or NULL if it was not found. + * @end deftypefun +@*/ +void *jit_memchr(const void *str, int ch, unsigned int len) +{ +#ifdef HAVE_MEMCHR + return memchr(str, ch, len); +#else + const unsigned char *s = (const unsigned char *)str; + while(len > 0) + { + if(*s == (unsigned char)ch) + { + return (void *)s; + } + ++s; + --len; + } + return (void *)0; +#endif +} diff --git a/jit/jit-memory.h b/jit/jit-memory.h new file mode 100644 index 0000000..6a1800c --- /dev/null +++ b/jit/jit-memory.h @@ -0,0 +1,76 @@ +/* + * jit-memory.h - Memory copy/set/compare routines. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_MEMORY_H +#define _JIT_MEMORY_H + +#include +#ifdef HAVE_STRING_H + #include +#else + #ifdef HAVE_STRINGS_H + #include + #endif +#endif +#ifdef HAVE_MEMORY_H + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Macros that replace the routines in + * with direct calls on the underlying library functions. + */ +#ifdef HAVE_MEMSET + #define jit_memset(dest,ch,len) (memset((dest), (ch), (len))) + #define jit_memzero(dest,len) (memset((dest), 0, (len))) +#else + #ifdef HAVE_BZERO + #define jit_memzero(dest,len) (bzero((char *)(dest), (len))) + #else + #define jit_memzero(dest,len) (jit_memset((char *)(dest), 0, (len))) + #endif +#endif +#ifdef HAVE_MEMCPY + #define jit_memcpy(dest,src,len) (memcpy((dest), (src), (len))) +#endif +#ifdef HAVE_MEMMOVE + #define jit_memmove(dest,src,len) (memmove((dest), (src), (len))) +#endif +#ifdef HAVE_MEMCMP + #define jit_memcmp(s1,s2,len) (memcmp((s1), (s2), (len))) +#else + #ifdef HAVE_BCMP + #define jit_memcmp(s1,s2,len) \ + (bcmp((char *)(s1), (char *)(s2), (len))) + #endif +#endif +#ifdef HAVE_MEMCHR + #define jit_memchr(str,ch,len) (memchr((str), (ch), (len))) +#endif + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_MEMORY_H */ diff --git a/jit/jit-meta.c b/jit/jit-meta.c new file mode 100644 index 0000000..f8d856b --- /dev/null +++ b/jit/jit-meta.c @@ -0,0 +1,216 @@ +/* + * jit-meta.c - Functions for manipulating metadata lists. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" + +/*@ + +@section Metadata handling +@cindex Metadata handling +@cindex jit-meta.h + +Many of the structures in the @code{libjit} library can have user-supplied +metadata associated with them. Metadata may be used to store dependency +graphs, branch prediction information, or any other information that is +useful to optimizers or code generators. + +Metadata can also be used by higher level user code to store information +about the structures that is specific to the user's virtual machine or +language. + +The library structures have special-purpose metadata routines associated +with them (e.g. @code{jit_function_set_meta}, @code{jit_block_get_meta}). +However, sometimes you may wish to create your own metadata lists and +attach them to your own structures. The functions below enable you +to do this: + +@*/ + +/*@ + * @deftypefun int jit_meta_set ({jit_meta_t *}list, int type, {void *}data, jit_meta_free_func free_data, jit_function_t pool_owner) + * Set a metadata value on a list. If the @code{type} is already present + * in the list, then its previous value will be freed. The @code{free_func} + * is called when the metadata value is freed with @code{jit_meta_free} + * or @code{jit_meta_destroy}. Returns zero if out of memory. + * + * If @code{pool_owner} is not NULL, then the metadata value will persist + * until the specified function is finished building. Normally you would + * set this to NULL. + * + * Metadata type values of 10000 or greater are reserved for internal use. + * They should never be used by external user code. + * @end deftypefun +@*/ +int jit_meta_set(jit_meta_t *list, int type, void *data, + jit_meta_free_func free_data, jit_function_t pool_owner) +{ + jit_meta_t current; + + /* See if we already have this type in the list */ + current = *list; + while(current != 0) + { + if(current->type == type) + { + if(data == current->data) + { + /* The value is unchanged, so don't free the previous value */ + return 1; + } + if(current->free_data) + { + (*(current->free_data))(current->data); + } + current->data = data; + current->free_data = free_data; + return 1; + } + current = current->next; + } + + /* Create a new metadata block and add it to the list */ + if(pool_owner) + { + if((current = jit_memory_pool_alloc + (&(pool_owner->builder->meta_pool), struct _jit_meta)) == 0) + { + return 0; + } + } + else + { + if((current = jit_new(struct _jit_meta)) == 0) + { + return 0; + } + } + current->type = type; + current->data = data; + current->free_data = free_data; + current->next = *list; + current->pool_owner = pool_owner; + *list = current; + return 1; +} + +/*@ + * @deftypefun {void *} jit_meta_get (jit_meta_t list, int type) + * Get the value associated with @code{type} in the specified @code{list}. + * Returns NULL if @code{type} is not present. + * @end deftypefun +@*/ +void *jit_meta_get(jit_meta_t list, int type) +{ + while(list != 0) + { + if(list->type == type) + { + return list->data; + } + list = list->next; + } + return 0; +} + +/*@ + * @deftypefun void jit_meta_free ({jit_meta_t *} list, int type) + * Free the metadata value in the @code{list} that has the + * specified @code{type}. Does nothing if the @code{type} + * is not present. + * @end deftypefun +@*/ +void jit_meta_free(jit_meta_t *list, int type) +{ + jit_meta_t current = *list; + jit_meta_t prev = 0; + while(current != 0) + { + if(current->type == type) + { + if(current->free_data) + { + (*(current->free_data))(current->data); + current->free_data = 0; + } + if(prev) + { + prev->next = current->next; + } + else + { + *list = current->next; + } + if(current->pool_owner) + { + jit_memory_pool_dealloc + (&(current->pool_owner->builder->meta_pool), current); + } + else + { + jit_free(current); + } + return; + } + else + { + prev = current; + current = current->next; + } + } +} + +/*@ + * @deftypefun void jit_meta_destroy ({jit_meta_t *}list) + * Destroy all of the metadata values in the specified @code{list}. + * @end deftypefun +@*/ +void jit_meta_destroy(jit_meta_t *list) +{ + jit_meta_t current = *list; + jit_meta_t next; + while(current != 0) + { + next = current->next; + if(current->free_data) + { + (*(current->free_data))(current->data); + current->free_data = 0; + } + if(current->pool_owner) + { + jit_memory_pool_dealloc + (&(current->pool_owner->builder->meta_pool), current); + } + else + { + jit_free(current); + } + current = next; + } +} + +void _jit_meta_free_one(void *meta) +{ + jit_meta_t current = (jit_meta_t)meta; + if(current->free_data) + { + (*(current->free_data))(current->data); + } +} diff --git a/jit/jit-opcode.c b/jit/jit-opcode.c new file mode 100644 index 0000000..ce86446 --- /dev/null +++ b/jit/jit-opcode.c @@ -0,0 +1,584 @@ +/* + * jit-opcode.c - Information about all of the JIT opcodes. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" + +#define F_(dest,src1,src2) \ + (JIT_OPCODE_DEST_##dest | JIT_OPCODE_SRC1_##src1 | JIT_OPCODE_SRC2_##src2) +#define O_(dest,src1,src2,oper) \ + (JIT_OPCODE_DEST_##dest | JIT_OPCODE_SRC1_##src1 | \ + JIT_OPCODE_SRC2_##src2 | JIT_OPCODE_OPER_##oper) +#define B_(src1,src2) \ + (JIT_OPCODE_IS_BRANCH | JIT_OPCODE_SRC1_##src1 | JIT_OPCODE_SRC2_##src2) +#define A_(src1,src2,oper) \ + (JIT_OPCODE_IS_BRANCH | JIT_OPCODE_SRC1_##src1 | \ + JIT_OPCODE_SRC2_##src2 | JIT_OPCODE_OPER_##oper) +#if defined(JIT_BACKEND_INTERP) + #define NINT_ARG JIT_OPCODE_NINT_ARG + #define NINT_ARG_TWO JIT_OPCODE_NINT_ARG_TWO + #define INDIRECT_ARGS JIT_OPCODE_CALL_INDIRECT_ARGS +#else + #define NINT_ARG 0 + #define NINT_ARG_TWO 0 + #define INDIRECT_ARGS 0 +#endif + +jit_opcode_info_t const jit_opcodes[JIT_OP_NUM_OPCODES] = { + + /* + * Simple opcodes. + */ + {"nop", F_(EMPTY, EMPTY, EMPTY)}, + + /* + * Conversion opcodes. + */ + {"trunc_sbyte", F_(INT, INT, EMPTY)}, + {"trunc_ubyte", F_(INT, INT, EMPTY)}, + {"trunc_short", F_(INT, INT, EMPTY)}, + {"trunc_ushort", F_(INT, INT, EMPTY)}, + {"trunc_int", F_(INT, INT, EMPTY)}, + {"trunc_uint", F_(INT, INT, EMPTY)}, + {"check_sbyte", F_(INT, INT, EMPTY)}, + {"check_ubyte", F_(INT, INT, EMPTY)}, + {"check_short", F_(INT, INT, EMPTY)}, + {"check_ushort", F_(INT, INT, EMPTY)}, + {"check_int", F_(INT, INT, EMPTY)}, + {"check_uint", F_(INT, INT, EMPTY)}, + {"low_word", F_(INT, LONG, EMPTY)}, + {"expand_int", F_(LONG, INT, EMPTY)}, + {"expand_uint", F_(LONG, INT, EMPTY)}, + {"check_low_word", F_(INT, LONG, EMPTY)}, + {"check_signed_low_word", F_(INT, LONG, EMPTY)}, + {"check_long", F_(LONG, LONG, EMPTY)}, + {"check_ulong", F_(LONG, LONG, EMPTY)}, + {"nfloat_to_int", F_(INT, NFLOAT, EMPTY)}, + {"nfloat_to_uint", F_(INT, NFLOAT, EMPTY)}, + {"nfloat_to_long", F_(LONG, NFLOAT, EMPTY)}, + {"nfloat_to_ulong", F_(LONG, NFLOAT, EMPTY)}, + {"check_nfloat_to_int", F_(INT, NFLOAT, EMPTY)}, + {"check_nfloat_to_uint", F_(INT, NFLOAT, EMPTY)}, + {"check_nfloat_to_long", F_(LONG, NFLOAT, EMPTY)}, + {"check_nfloat_to_ulong", F_(LONG, NFLOAT, EMPTY)}, + {"int_to_nfloat", F_(NFLOAT, INT, EMPTY)}, + {"uint_to_nfloat", F_(NFLOAT, INT, EMPTY)}, + {"long_to_nfloat", F_(NFLOAT, LONG, EMPTY)}, + {"ulong_to_nfloat", F_(NFLOAT, LONG, EMPTY)}, + {"nfloat_to_float32", F_(FLOAT32, NFLOAT, EMPTY)}, + {"nfloat_to_float64", F_(FLOAT64, NFLOAT, EMPTY)}, + {"float32_to_nfloat", F_(NFLOAT, FLOAT32, EMPTY)}, + {"float64_to_nfloat", F_(NFLOAT, FLOAT64, EMPTY)}, + + /* + * Arithmetic opcodes. + */ + {"iadd", O_(INT, INT, INT, ADD)}, + {"iadd_ovf", F_(INT, INT, INT)}, + {"iadd_ovf_un", F_(INT, INT, INT)}, + {"isub", O_(INT, INT, INT, SUB)}, + {"isub_ovf", F_(INT, INT, INT)}, + {"isub_ovf_un", F_(INT, INT, INT)}, + {"imul", O_(INT, INT, INT, MUL)}, + {"imul_ovf", F_(INT, INT, INT)}, + {"imul_ovf_un", F_(INT, INT, INT)}, + {"idiv", O_(INT, INT, INT, DIV)}, + {"idiv_un", F_(INT, INT, INT)}, + {"irem", O_(INT, INT, INT, REM)}, + {"irem_un", F_(INT, INT, INT)}, + {"ineg", O_(INT, INT, EMPTY, NEG)}, + {"ladd", O_(LONG, LONG, LONG, ADD)}, + {"ladd_ovf", F_(LONG, LONG, LONG)}, + {"ladd_ovf_un", F_(LONG, LONG, LONG)}, + {"lsub", O_(LONG, LONG, LONG, SUB)}, + {"lsub_ovf", F_(LONG, LONG, LONG)}, + {"lsub_ovf_un", F_(LONG, LONG, LONG)}, + {"lmul", O_(LONG, LONG, LONG, MUL)}, + {"lmul_ovf", F_(LONG, LONG, LONG)}, + {"lmul_ovf_un", F_(LONG, LONG, LONG)}, + {"ldiv", O_(LONG, LONG, LONG, DIV)}, + {"ldiv_un", F_(LONG, LONG, LONG)}, + {"lrem", O_(LONG, LONG, LONG, REM)}, + {"lrem_un", F_(LONG, LONG, LONG)}, + {"lneg", O_(LONG, LONG, EMPTY, NEG)}, + {"fadd", O_(FLOAT32, FLOAT32, FLOAT32, ADD)}, + {"fsub", O_(FLOAT32, FLOAT32, FLOAT32, SUB)}, + {"fmul", O_(FLOAT32, FLOAT32, FLOAT32, MUL)}, + {"fdiv", O_(FLOAT32, FLOAT32, FLOAT32, DIV)}, + {"frem", O_(FLOAT32, FLOAT32, FLOAT32, REM)}, + {"frem_ieee", F_(FLOAT32, FLOAT32, FLOAT32)}, + {"fneg", O_(FLOAT32, FLOAT32, EMPTY, NEG)}, + {"dadd", O_(FLOAT64, FLOAT64, FLOAT64, ADD)}, + {"dsub", O_(FLOAT64, FLOAT64, FLOAT64, SUB)}, + {"dmul", O_(FLOAT64, FLOAT64, FLOAT64, MUL)}, + {"ddiv", O_(FLOAT64, FLOAT64, FLOAT64, DIV)}, + {"drem", O_(FLOAT64, FLOAT64, FLOAT64, REM)}, + {"drem_ieee", F_(FLOAT64, FLOAT64, FLOAT64)}, + {"dneg", O_(FLOAT64, FLOAT64, EMPTY, NEG)}, + {"nfadd", O_(NFLOAT, NFLOAT, NFLOAT, ADD)}, + {"nfsub", O_(NFLOAT, NFLOAT, NFLOAT, SUB)}, + {"nfmul", O_(NFLOAT, NFLOAT, NFLOAT, MUL)}, + {"nfdiv", O_(NFLOAT, NFLOAT, NFLOAT, DIV)}, + {"nfrem", O_(NFLOAT, NFLOAT, NFLOAT, REM)}, + {"nfrem_ieee", F_(NFLOAT, NFLOAT, NFLOAT)}, + {"nfneg", O_(NFLOAT, NFLOAT, EMPTY, NEG)}, + + /* + * Bitwise opcodes. + */ + {"iand", O_(INT, INT, INT, AND)}, + {"ior", O_(INT, INT, INT, OR)}, + {"ixor", O_(INT, INT, INT, XOR)}, + {"inot", O_(INT, INT, EMPTY, NOT)}, + {"ishl", O_(INT, INT, INT, SHL)}, + {"ishr", O_(INT, INT, INT, SHR)}, + {"ishr_un", O_(INT, INT, INT, SHR_UN)}, + {"land", O_(LONG, LONG, LONG, AND)}, + {"lor", O_(LONG, LONG, LONG, OR)}, + {"lxor", O_(LONG, LONG, LONG, XOR)}, + {"lnot", O_(LONG, LONG, EMPTY, NOT)}, + {"lshl", O_(LONG, LONG, INT, SHL)}, + {"lshr", O_(LONG, LONG, INT, SHR)}, + {"lshr_un", O_(LONG, LONG, INT, SHR_UN)}, + + /* + * Branch opcodes. + */ + {"br", B_(EMPTY, EMPTY)}, + {"br_ifalse", B_(INT, EMPTY)}, + {"br_itrue", B_(INT, EMPTY)}, + {"br_ieq", A_(INT, INT, EQ)}, + {"br_ine", A_(INT, INT, NE)}, + {"br_ilt", A_(INT, INT, LT)}, + {"br_ilt_un", B_(INT, INT)}, + {"br_ile", A_(INT, INT, LE)}, + {"br_ile_un", B_(INT, INT)}, + {"br_igt", A_(INT, INT, GT)}, + {"br_igt_un", B_(INT, INT)}, + {"br_ige", A_(INT, INT, GE)}, + {"br_ige_un", B_(INT, INT)}, + {"br_lfalse", B_(LONG, EMPTY)}, + {"br_ltrue", B_(LONG, EMPTY)}, + {"br_leq", A_(LONG, LONG, EQ)}, + {"br_lne", A_(LONG, LONG, NE)}, + {"br_llt", A_(LONG, LONG, LT)}, + {"br_llt_un", B_(LONG, LONG)}, + {"br_lle", A_(LONG, LONG, LE)}, + {"br_lle_un", B_(LONG, LONG)}, + {"br_lgt", A_(LONG, LONG, GT)}, + {"br_lgt_un", B_(LONG, LONG)}, + {"br_lge", A_(LONG, LONG, GE)}, + {"br_lge_un", B_(LONG, LONG)}, + {"br_feq", A_(FLOAT32, FLOAT32, EQ)}, + {"br_fne", A_(FLOAT32, FLOAT32, NE)}, + {"br_flt", A_(FLOAT32, FLOAT32, LT)}, + {"br_fle", A_(FLOAT32, FLOAT32, LE)}, + {"br_fgt", A_(FLOAT32, FLOAT32, GT)}, + {"br_fge", A_(FLOAT32, FLOAT32, GE)}, + {"br_feq_inv", B_(FLOAT32, FLOAT32)}, + {"br_fne_inv", B_(FLOAT32, FLOAT32)}, + {"br_flt_inv", B_(FLOAT32, FLOAT32)}, + {"br_fle_inv", B_(FLOAT32, FLOAT32)}, + {"br_fgt_inv", B_(FLOAT32, FLOAT32)}, + {"br_fge_inv", B_(FLOAT32, FLOAT32)}, + {"br_deq", A_(FLOAT64, FLOAT64, EQ)}, + {"br_dne", A_(FLOAT64, FLOAT64, NE)}, + {"br_dlt", A_(FLOAT64, FLOAT64, LT)}, + {"br_dle", A_(FLOAT64, FLOAT64, LE)}, + {"br_dgt", A_(FLOAT64, FLOAT64, GT)}, + {"br_dge", A_(FLOAT64, FLOAT64, GE)}, + {"br_deq_inv", B_(FLOAT64, FLOAT64)}, + {"br_dne_inv", B_(FLOAT64, FLOAT64)}, + {"br_dlt_inv", B_(FLOAT64, FLOAT64)}, + {"br_dle_inv", B_(FLOAT64, FLOAT64)}, + {"br_dgt_inv", B_(FLOAT64, FLOAT64)}, + {"br_dge_inv", B_(FLOAT64, FLOAT64)}, + {"br_nfeq", A_(NFLOAT, NFLOAT, EQ)}, + {"br_nfne", A_(NFLOAT, NFLOAT, NE)}, + {"br_nflt", A_(NFLOAT, NFLOAT, LT)}, + {"br_nfle", A_(NFLOAT, NFLOAT, LE)}, + {"br_nfgt", A_(NFLOAT, NFLOAT, GT)}, + {"br_nfge", A_(NFLOAT, NFLOAT, GE)}, + {"br_nfeq_inv", B_(NFLOAT, NFLOAT)}, + {"br_nfne_inv", B_(NFLOAT, NFLOAT)}, + {"br_nflt_inv", B_(NFLOAT, NFLOAT)}, + {"br_nfle_inv", B_(NFLOAT, NFLOAT)}, + {"br_nfgt_inv", B_(NFLOAT, NFLOAT)}, + {"br_nfge_inv", B_(NFLOAT, NFLOAT)}, + + /* + * Comparison opcodes. + */ + {"icmp", F_(INT, INT, INT)}, + {"icmp_un", F_(INT, INT, INT)}, + {"lcmp", F_(INT, LONG, LONG)}, + {"lcmp_un", F_(INT, LONG, LONG)}, + {"fcmpl", F_(INT, FLOAT32, FLOAT32)}, + {"fcmpg", F_(INT, FLOAT32, FLOAT32)}, + {"dcmpl", F_(INT, FLOAT64, FLOAT64)}, + {"dcmpg", F_(INT, FLOAT64, FLOAT64)}, + {"nfcmpl", F_(INT, NFLOAT, NFLOAT)}, + {"nfcmpg", F_(INT, NFLOAT, NFLOAT)}, + {"ieq", O_(INT, INT, INT, EQ)}, + {"ine", O_(INT, INT, INT, NE)}, + {"ilt", O_(INT, INT, INT, LT)}, + {"ilt_un", F_(INT, INT, INT)}, + {"ile", O_(INT, INT, INT, LE)}, + {"ile_un", F_(INT, INT, INT)}, + {"igt", O_(INT, INT, INT, GT)}, + {"igt_un", F_(INT, INT, INT)}, + {"ige", O_(INT, INT, INT, GE)}, + {"ige_un", F_(INT, INT, INT)}, + {"leq", O_(INT, LONG, LONG, EQ)}, + {"lne", O_(INT, LONG, LONG, NE)}, + {"llt", O_(INT, LONG, LONG, LT)}, + {"llt_un", F_(INT, LONG, LONG)}, + {"lle", O_(INT, LONG, LONG, LE)}, + {"lle_un", F_(INT, LONG, LONG)}, + {"lgt", O_(INT, LONG, LONG, GT)}, + {"lgt_un", F_(INT, LONG, LONG)}, + {"lge", O_(INT, LONG, LONG, GE)}, + {"lge_un", F_(INT, LONG, LONG)}, + {"feq", O_(INT, FLOAT32, FLOAT32, EQ)}, + {"fne", O_(INT, FLOAT32, FLOAT32, NE)}, + {"flt", O_(INT, FLOAT32, FLOAT32, LT)}, + {"fle", O_(INT, FLOAT32, FLOAT32, LE)}, + {"fgt", O_(INT, FLOAT32, FLOAT32, GT)}, + {"fge", O_(INT, FLOAT32, FLOAT32, GE)}, + {"feq_inv", F_(INT, FLOAT32, FLOAT32)}, + {"fne_inv", F_(INT, FLOAT32, FLOAT32)}, + {"flt_inv", F_(INT, FLOAT32, FLOAT32)}, + {"fle_inv", F_(INT, FLOAT32, FLOAT32)}, + {"fgt_inv", F_(INT, FLOAT32, FLOAT32)}, + {"fge_inv", F_(INT, FLOAT32, FLOAT32)}, + {"deq", O_(INT, FLOAT64, FLOAT64, EQ)}, + {"dne", O_(INT, FLOAT64, FLOAT64, NE)}, + {"dlt", O_(INT, FLOAT64, FLOAT64, LT)}, + {"dle", O_(INT, FLOAT64, FLOAT64, LE)}, + {"dgt", O_(INT, FLOAT64, FLOAT64, GT)}, + {"dge", O_(INT, FLOAT64, FLOAT64, GE)}, + {"deq_inv", F_(INT, FLOAT64, FLOAT64)}, + {"dne_inv", F_(INT, FLOAT64, FLOAT64)}, + {"dlt_inv", F_(INT, FLOAT64, FLOAT64)}, + {"dle_inv", F_(INT, FLOAT64, FLOAT64)}, + {"dgt_inv", F_(INT, FLOAT64, FLOAT64)}, + {"dge_inv", F_(INT, FLOAT64, FLOAT64)}, + {"nfeq", O_(INT, NFLOAT, NFLOAT, EQ)}, + {"nfne", O_(INT, NFLOAT, NFLOAT, NE)}, + {"nflt", O_(INT, NFLOAT, NFLOAT, LT)}, + {"nfle", O_(INT, NFLOAT, NFLOAT, LE)}, + {"nfgt", O_(INT, NFLOAT, NFLOAT, GT)}, + {"nfge", O_(INT, NFLOAT, NFLOAT, GE)}, + {"nfeq_inv", F_(INT, NFLOAT, NFLOAT)}, + {"nfne_inv", F_(INT, NFLOAT, NFLOAT)}, + {"nflt_inv", F_(INT, NFLOAT, NFLOAT)}, + {"nfle_inv", F_(INT, NFLOAT, NFLOAT)}, + {"nfgt_inv", F_(INT, NFLOAT, NFLOAT)}, + {"nfge_inv", F_(INT, NFLOAT, NFLOAT)}, + {"is_fnan", F_(INT, FLOAT32, EMPTY)}, + {"is_finf", F_(INT, FLOAT32, EMPTY)}, + {"is_ffinite", F_(INT, FLOAT32, EMPTY)}, + {"is_dnan", F_(INT, FLOAT64, EMPTY)}, + {"is_dinf", F_(INT, FLOAT64, EMPTY)}, + {"is_dfinite", F_(INT, FLOAT64, EMPTY)}, + {"is_nfnan", F_(INT, NFLOAT, EMPTY)}, + {"is_nfinf", F_(INT, NFLOAT, EMPTY)}, + {"is_nffinite", F_(INT, NFLOAT, EMPTY)}, + + /* + * Mathematical functions. + */ + {"facos", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fasin", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fatan", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fatan2", F_(FLOAT32, FLOAT32, FLOAT32)}, + {"fceil", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fcos", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fcosh", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fexp", F_(FLOAT32, FLOAT32, EMPTY)}, + {"ffloor", F_(FLOAT32, FLOAT32, EMPTY)}, + {"flog", F_(FLOAT32, FLOAT32, EMPTY)}, + {"flog10", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fpow", F_(FLOAT32, FLOAT32, FLOAT32)}, + {"frint", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fround", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fsin", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fsinh", F_(FLOAT32, FLOAT32, EMPTY)}, + {"fsqrt", F_(FLOAT32, FLOAT32, EMPTY)}, + {"ftan", F_(FLOAT32, FLOAT32, EMPTY)}, + {"ftanh", F_(FLOAT32, FLOAT32, EMPTY)}, + {"dacos", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dasin", F_(FLOAT64, FLOAT64, EMPTY)}, + {"datan", F_(FLOAT64, FLOAT64, EMPTY)}, + {"datan2", F_(FLOAT64, FLOAT64, FLOAT64)}, + {"dceil", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dcos", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dcosh", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dexp", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dfloor", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dlog", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dlog10", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dpow", F_(FLOAT64, FLOAT64, FLOAT64)}, + {"drint", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dround", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dsin", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dsinh", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dsqrt", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dtan", F_(FLOAT64, FLOAT64, EMPTY)}, + {"dtanh", F_(FLOAT64, FLOAT64, EMPTY)}, + {"nfacos", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfasin", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfatan", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfatan2", F_(NFLOAT, NFLOAT, NFLOAT)}, + {"nfceil", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfcos", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfcosh", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfexp", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nffloor", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nflog", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nflog10", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfpow", F_(NFLOAT, NFLOAT, NFLOAT)}, + {"nfrint", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfround", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfsin", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfsinh", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nfsqrt", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nftan", F_(NFLOAT, NFLOAT, EMPTY)}, + {"nftanh", F_(NFLOAT, NFLOAT, EMPTY)}, + + /* + * Absolute, minimum, maximum, and sign. + */ + {"iabs", F_(INT, INT, EMPTY)}, + {"labs", F_(LONG, LONG, EMPTY)}, + {"fabs", F_(FLOAT32, FLOAT32, EMPTY)}, + {"dabs", F_(FLOAT64, FLOAT64, EMPTY)}, + {"nfabs", F_(NFLOAT, NFLOAT, EMPTY)}, + {"imin", F_(INT, INT, INT)}, + {"imin_un", F_(INT, INT, INT)}, + {"lmin", F_(LONG, LONG, LONG)}, + {"lmin_un", F_(LONG, LONG, LONG)}, + {"fmin", F_(FLOAT32, FLOAT32, FLOAT32)}, + {"dmin", F_(FLOAT64, FLOAT64, FLOAT64)}, + {"nfmin", F_(NFLOAT, NFLOAT, NFLOAT)}, + {"imax", F_(INT, INT, INT)}, + {"imax_un", F_(INT, INT, INT)}, + {"lmax", F_(LONG, LONG, LONG)}, + {"lmax_un", F_(LONG, LONG, LONG)}, + {"fmax", F_(FLOAT32, FLOAT32, FLOAT32)}, + {"dmax", F_(FLOAT64, FLOAT64, FLOAT64)}, + {"nfmax", F_(NFLOAT, NFLOAT, NFLOAT)}, + {"isign", F_(INT, INT, EMPTY)}, + {"lsign", F_(INT, LONG, EMPTY)}, + {"fsign", F_(INT, FLOAT32, EMPTY)}, + {"dsign", F_(INT, FLOAT64, EMPTY)}, + {"nfsign", F_(INT, NFLOAT, EMPTY)}, + + /* + * Pointer check opcodes. + */ + {"check_null", F_(EMPTY, PTR, EMPTY)}, + + /* + * Function calls. + */ + {"call", JIT_OPCODE_IS_CALL}, + {"call_tail", JIT_OPCODE_IS_CALL}, + {"call_indirect", F_(EMPTY, PTR, EMPTY) | INDIRECT_ARGS}, + {"call_vtable_ptr", F_(EMPTY, PTR, EMPTY) | INDIRECT_ARGS}, + {"call_external", JIT_OPCODE_IS_CALL_EXTERNAL}, + {"return", F_(EMPTY, EMPTY, EMPTY)}, + {"return_int", F_(EMPTY, INT, EMPTY)}, + {"return_long", F_(EMPTY, LONG, EMPTY)}, + {"return_float32", F_(EMPTY, FLOAT32, EMPTY)}, + {"return_float64", F_(EMPTY, FLOAT64, EMPTY)}, + {"return_nfloat", F_(EMPTY, NFLOAT, EMPTY)}, + {"return_small_struct", F_(EMPTY, PTR, EMPTY) | NINT_ARG}, + {"setup_for_nested", F_(EMPTY, INT, EMPTY)}, + {"setup_for_sibling", F_(EMPTY, INT, INT) | NINT_ARG}, + {"import", F_(PTR, ANY, INT)}, + + /* + * Exception handling. + */ + {"throw", F_(EMPTY, PTR, EMPTY)}, + {"load_pc", F_(PTR, EMPTY, EMPTY)}, + {"enter_catch", F_(PTR, EMPTY, EMPTY)}, + {"enter_finally", F_(EMPTY, EMPTY, EMPTY)}, + {"leave_finally", F_(EMPTY, EMPTY, EMPTY)}, + {"enter_filter", F_(ANY, EMPTY, EMPTY)}, + {"leave_filter", F_(EMPTY, ANY, EMPTY)}, + {"call_filter", B_(ANY, EMPTY)}, + {"call_filter_return", F_(ANY, EMPTY, EMPTY)}, + {"prepare_for_leave", F_(EMPTY, EMPTY, EMPTY)}, + {"prepare_for_return", F_(EMPTY, EMPTY, EMPTY)}, + + /* + * Data manipulation. + */ + {"copy_load_sbyte", F_(INT, INT, EMPTY)}, + {"copy_load_ubyte", F_(INT, INT, EMPTY)}, + {"copy_load_short", F_(INT, INT, EMPTY)}, + {"copy_load_ushort", F_(INT, INT, EMPTY)}, + {"copy_int", O_(INT, INT, EMPTY, COPY)}, + {"copy_long", O_(LONG, LONG, EMPTY, COPY)}, + {"copy_float32", O_(FLOAT32, FLOAT32, EMPTY, COPY)}, + {"copy_float64", O_(FLOAT64, FLOAT64, EMPTY, COPY)}, + {"copy_nfloat", O_(NFLOAT, NFLOAT, EMPTY, COPY)}, + {"copy_struct", O_(PTR, PTR, EMPTY, COPY)}, + {"copy_store_byte", F_(INT, INT, EMPTY)}, + {"copy_store_short", F_(INT, INT, EMPTY)}, + {"address_of", O_(PTR, ANY, EMPTY, ADDRESS_OF)}, + + /* + * Incoming registers, outgoing registers, and stack pushes. + */ + {"incoming_reg", JIT_OPCODE_IS_REG}, + {"incoming_frame_posn", F_(EMPTY, ANY, INT)}, + {"outgoing_reg", JIT_OPCODE_IS_REG}, + {"return_reg", JIT_OPCODE_IS_REG}, + {"push_int", F_(EMPTY, INT, EMPTY)}, + {"push_long", F_(EMPTY, LONG, EMPTY)}, + {"push_float32", F_(EMPTY, FLOAT32, EMPTY)}, + {"push_float64", F_(EMPTY, FLOAT64, EMPTY)}, + {"push_nfloat", F_(EMPTY, NFLOAT, EMPTY)}, + {"push_struct", F_(EMPTY, ANY, EMPTY)}, + {"pop_stack", F_(EMPTY, INT, EMPTY) | NINT_ARG}, + {"flush_small_struct", F_(EMPTY, ANY, EMPTY)}, + + /* + * Pointer-relative loads and stores. + */ + {"load_relative_sbyte", F_(INT, PTR, INT) | NINT_ARG}, + {"load_relative_ubyte", F_(INT, PTR, INT) | NINT_ARG}, + {"load_relative_short", F_(INT, PTR, INT) | NINT_ARG}, + {"load_relative_ushort", F_(INT, PTR, INT) | NINT_ARG}, + {"load_relative_int", F_(INT, PTR, INT) | NINT_ARG}, + {"load_relative_long", F_(LONG, PTR, INT) | NINT_ARG}, + {"load_relative_float32", F_(FLOAT32, PTR, INT) | NINT_ARG}, + {"load_relative_float64", F_(FLOAT64, PTR, INT) | NINT_ARG}, + {"load_relative_nfloat", F_(NFLOAT, PTR, INT) | NINT_ARG}, + {"load_relative_struct", F_(ANY, PTR, INT) | NINT_ARG_TWO}, + {"store_relative_byte", F_(PTR, INT, INT) | NINT_ARG}, + {"store_relative_short", F_(PTR, INT, INT) | NINT_ARG}, + {"store_relative_int", F_(PTR, INT, INT) | NINT_ARG}, + {"store_relative_long", F_(PTR, LONG, INT) | NINT_ARG}, + {"store_relative_float32", F_(PTR, FLOAT32, INT) | NINT_ARG}, + {"store_relative_float64", F_(PTR, FLOAT64, INT) | NINT_ARG}, + {"store_relative_nfloat", F_(PTR, NFLOAT, INT) | NINT_ARG}, + {"store_relative_struct", F_(PTR, ANY, INT) | NINT_ARG_TWO}, + {"add_relative", F_(PTR, PTR, INT) | NINT_ARG}, +}; + +#if defined(JIT_BACKEND_INTERP) + +jit_opcode_info_t const _jit_interp_opcodes[JIT_OP_NUM_INTERP_OPCODES] = { + + /* + * Argument variable access opcodes. + */ + {"ldarg_sbyte", JIT_OPCODE_NINT_ARG}, + {"ldarg_ubyte", JIT_OPCODE_NINT_ARG}, + {"ldarg_short", JIT_OPCODE_NINT_ARG}, + {"ldarg_ushort", JIT_OPCODE_NINT_ARG}, + {"ldarg_int", JIT_OPCODE_NINT_ARG}, + {"ldarg_long", JIT_OPCODE_NINT_ARG}, + {"ldarg_float32", JIT_OPCODE_NINT_ARG}, + {"ldarg_float64", JIT_OPCODE_NINT_ARG}, + {"ldarg_nfloat", JIT_OPCODE_NINT_ARG}, + {"ldarg_struct", JIT_OPCODE_NINT_ARG_TWO}, + {"ldarga", JIT_OPCODE_NINT_ARG}, + {"starg_byte", JIT_OPCODE_NINT_ARG}, + {"starg_short", JIT_OPCODE_NINT_ARG}, + {"starg_int", JIT_OPCODE_NINT_ARG}, + {"starg_long", JIT_OPCODE_NINT_ARG}, + {"starg_float32", JIT_OPCODE_NINT_ARG}, + {"starg_float64", JIT_OPCODE_NINT_ARG}, + {"starg_nfloat", JIT_OPCODE_NINT_ARG}, + {"starg_struct", JIT_OPCODE_NINT_ARG_TWO}, + + /* + * Local variable frame access opcodes. + */ + {"ldloc_sbyte", JIT_OPCODE_NINT_ARG}, + {"ldloc_ubyte", JIT_OPCODE_NINT_ARG}, + {"ldloc_short", JIT_OPCODE_NINT_ARG}, + {"ldloc_ushort", JIT_OPCODE_NINT_ARG}, + {"ldloc_int", JIT_OPCODE_NINT_ARG}, + {"ldloc_long", JIT_OPCODE_NINT_ARG}, + {"ldloc_float32", JIT_OPCODE_NINT_ARG}, + {"ldloc_float64", JIT_OPCODE_NINT_ARG}, + {"ldloc_nfloat", JIT_OPCODE_NINT_ARG}, + {"ldloc_struct", JIT_OPCODE_NINT_ARG_TWO}, + {"ldloca", JIT_OPCODE_NINT_ARG}, + {"stloc_byte", JIT_OPCODE_NINT_ARG}, + {"stloc_short", JIT_OPCODE_NINT_ARG}, + {"stloc_int", JIT_OPCODE_NINT_ARG}, + {"stloc_long", JIT_OPCODE_NINT_ARG}, + {"stloc_float32", JIT_OPCODE_NINT_ARG}, + {"stloc_float64", JIT_OPCODE_NINT_ARG}, + {"stloc_nfloat", JIT_OPCODE_NINT_ARG}, + {"stloc_struct", JIT_OPCODE_NINT_ARG_TWO}, + + /* + * Pointer check opcodes (interpreter only). + */ + {"check_null_n", JIT_OPCODE_NINT_ARG}, + + /* + * Stack management. + */ + {"pop", 0}, + {"pop_2", 0}, + {"pop_3", 0}, + {"push_return_int", 0}, + {"push_return_long", 0}, + {"push_return_float32", 0}, + {"push_return_float64", 0}, + {"push_return_nfloat", 0}, + {"push_return_small_struct", JIT_OPCODE_NINT_ARG}, + + /* + * Nested function call handling. + */ + {"push_parent_locals", 0}, + {"push_parent_args", 0}, + + /* + * Push constant values onto the stack. + */ + {"push_const_int", JIT_OPCODE_NINT_ARG}, + {"push_const_long", JIT_OPCODE_CONST_LONG}, + {"push_const_float32", JIT_OPCODE_CONST_FLOAT32}, + {"push_const_float64", JIT_OPCODE_CONST_FLOAT64}, + {"push_const_nfloat", JIT_OPCODE_CONST_NFLOAT}, + + /* + * Exception handling (interpreter only). + */ + {"call_finally", B_(EMPTY, EMPTY)}, + + /* + * Marker opcode for the end of a function. + */ + {"end_marker", 0}, +}; + +#endif /* JIT_BACKEND_INTERP */ diff --git a/jit/jit-pool.c b/jit/jit-pool.c new file mode 100644 index 0000000..959b6be --- /dev/null +++ b/jit/jit-pool.c @@ -0,0 +1,87 @@ +/* + * jit-pool.c - Functions for managing memory pools. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-memory.h" + +void _jit_memory_pool_init(jit_memory_pool *pool, unsigned int elem_size) +{ + pool->elem_size = elem_size; + pool->elems_per_block = 4000 / elem_size; + pool->elems_in_last = pool->elems_per_block; + pool->blocks = 0; + pool->free_list = 0; +} + +void _jit_memory_pool_free(jit_memory_pool *pool, jit_meta_free_func func) +{ + jit_pool_block_t block; + while(pool->blocks != 0) + { + block = pool->blocks; + pool->blocks = block->next; + if(func) + { + while(pool->elems_in_last > 0) + { + --(pool->elems_in_last); + (*func)(block->data + pool->elems_in_last * pool->elem_size); + } + } + jit_free(block); + pool->elems_in_last = pool->elems_per_block; + } + pool->free_list = 0; +} + +void *_jit_memory_pool_alloc(jit_memory_pool *pool) +{ + void *data; + if(pool->free_list) + { + /* Reclaim an item that was previously deallocated */ + data = pool->free_list; + pool->free_list = *((void **)data); + jit_memzero(data, pool->elem_size); + return data; + } + if(pool->elems_in_last >= pool->elems_per_block) + { + data = (void *)jit_calloc(1, sizeof(struct jit_pool_block) + + pool->elem_size * pool->elems_per_block - 1); + if(!data) + { + return 0; + } + ((jit_pool_block_t)data)->next = pool->blocks; + pool->blocks = (jit_pool_block_t)data; + pool->elems_in_last = 0; + } + data = (void *)(pool->blocks->data + + pool->elems_in_last * pool->elem_size); + ++(pool->elems_in_last); + return data; +} + +void _jit_memory_pool_dealloc(jit_memory_pool *pool, void *item) +{ + *((void **)item) = pool->free_list; + pool->free_list = item; +} diff --git a/jit/jit-reg-alloc.c b/jit/jit-reg-alloc.c new file mode 100644 index 0000000..be3312f --- /dev/null +++ b/jit/jit-reg-alloc.c @@ -0,0 +1,1191 @@ +/* + * jit-reg-alloc.c - Register allocation routines for the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-reg-alloc.h" +#include +#include + +/*@ + +The @code{libjit} library provides a number of functions for +performing register allocation within basic blocks so that you +mostly don't have to worry about it: + +@*/ + +/*@ + * @deftypefun void _jit_regs_init_for_block (jit_gencode_t gen) + * Initialize the register allocation state for a new block. + * @end deftypefun +@*/ +void _jit_regs_init_for_block(jit_gencode_t gen) +{ + int reg; + gen->current_age = 1; + for(reg = 0; reg < JIT_NUM_REGS; ++reg) + { + /* Clear everything except permanent and fixed registers */ + if(!jit_reg_is_used(gen->permanent, reg) && + (_jit_reg_info[reg].flags & JIT_REG_FIXED) == 0) + { + gen->contents[reg].num_values = 0; + gen->contents[reg].is_long_start = 0; + gen->contents[reg].is_long_end = 0; + gen->contents[reg].age = 0; + gen->contents[reg].remap = -1; + gen->contents[reg].used_for_temp = 0; + } + gen->stack_map[reg] = -1; + } +} + +/*@ + * @deftypefun int _jit_regs_needs_long_pair (jit_type_t type) + * Determine if a type requires a long register pair. + * @end deftypefun +@*/ +int _jit_regs_needs_long_pair(jit_type_t type) +{ +#if defined(JIT_NATIVE_INT32) && !defined(JIT_BACKEND_INTERP) + type = jit_type_normalize(type); + if(type) + { + if(type->kind == JIT_TYPE_LONG || type->kind == JIT_TYPE_ULONG) + { + return 1; + } + } + return 0; +#else + /* We don't register pairs on 64-bit platforms or the interpreter */ + return 0; +#endif +} + +/*@ + * @deftypefun int _jit_regs_get_cpu (jit_gencode_t gen, int reg, int *other_reg) + * Get the CPU register that corresponds to a pseudo register. + * "other_reg" will be set to the other register in a pair, + * or -1 if the register is not part of a pair. + * @end deftypefun +@*/ +int _jit_regs_get_cpu(jit_gencode_t gen, int reg, int *other_reg) +{ + int cpu_reg, other; + cpu_reg = gen->contents[reg].remap; + if(cpu_reg == -1) + { + cpu_reg = _jit_reg_info[reg].cpu_reg; + } + else + { + cpu_reg = _jit_reg_info[cpu_reg].cpu_reg; + } + if(gen->contents[reg].is_long_start) + { + other = _jit_reg_info[reg].other_reg; + if(gen->contents[other].remap == -1) + { + other = _jit_reg_info[other].cpu_reg; + } + else + { + other = _jit_reg_info[gen->contents[other].remap].cpu_reg; + } + } + else + { + other = -1; + } + if(other_reg) + { + *other_reg = other; + } + return cpu_reg; +} + +/* + * Dump debug information about the register allocation state. + */ +/*#define JIT_REG_DEBUG 1*/ +#ifdef JIT_REG_DEBUG +static void dump_regs(jit_gencode_t gen, const char *name) +{ + int reg; + unsigned int index; + printf("%s:\n", name); + for(reg = 0; reg < JIT_NUM_REGS; ++reg) + { + if(gen->contents[reg].num_values == 0 && + !(gen->contents[reg].used_for_temp) && + gen->contents[reg].remap == -1) + { + continue; + } + printf("\t%s: ", _jit_reg_info[reg].name); + if(gen->contents[reg].num_values > 0) + { + for(index = 0; index < gen->contents[reg].num_values; ++index) + { + if(index) + fputs(", ", stdout); + jit_dump_value(stdout, jit_value_get_function + (gen->contents[reg].values[index]), + gen->contents[reg].values[index], 0); + } + if(gen->contents[reg].used_for_temp) + { + printf(", used_for_temp"); + } + } + else if(gen->contents[reg].used_for_temp) + { + printf("used_for_temp"); + } + else + { + printf("free"); + } + if(gen->contents[reg].remap != -1) + { + printf(", remap=%d", (int)(gen->contents[reg].remap)); + } + for(index = 0; index < JIT_NUM_REGS; ++index) + { + if(gen->stack_map[index] == reg) + { + printf(", reverse_remap=%d", (int)index); + } + } + putc('\n', stdout); + } +} +#endif + +/* + * Spill all registers between two end points. + */ +static void spill_all_between(jit_gencode_t gen, int first, int last) +{ + int reg, posn, other_reg, real_reg; + int first_stack_reg = 0; + jit_value_t value; + int value_used; + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "enter spill_all_between"); +#endif + + /* Handle the non-stack registers first, as they are easy to spill */ + for(reg = first; reg <= last; ++reg) + { + /* Skip this register if it is permanent or fixed */ + if(jit_reg_is_used(gen->permanent, reg) || + (_jit_reg_info[reg].flags & JIT_REG_FIXED) != 0) + { + continue; + } + + /* Remember this register if it is the start of a stack */ + if((_jit_reg_info[reg].flags & JIT_REG_START_STACK) != 0) + { + first_stack_reg = reg; + } + + /* Skip this register if there is nothing in it */ + if(gen->contents[reg].num_values == 0 && + !(gen->contents[reg].used_for_temp)) + { + continue; + } + + /* If this is a stack register, then we need to find the + register that contains the top-most stack position, + because we must spill stack registers from top down. + As we spill each one, something else will become the top */ + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0) + { + real_reg = gen->stack_map[first_stack_reg]; + } + else + { + real_reg = reg; + } + + /* Get the other register in a long pair, if there is one */ + if(gen->contents[real_reg].is_long_start) + { + other_reg = _jit_reg_info[real_reg].other_reg; + } + else + { + other_reg = -1; + } + + /* Spill all values that are associated with the register */ + value_used = 0; + if(gen->contents[real_reg].num_values > 0) + { + for(posn = gen->contents[real_reg].num_values - 1; + posn >= 0; --posn) + { + value = gen->contents[real_reg].values[posn]; + if(!(value->in_frame)) + { + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + _jit_gen_spill_reg(gen, real_reg, other_reg, value); + } + else + { + /* The back end needs to think that we are spilling + the first register in the stack, regardless of + what "real_reg" might happen to be */ + _jit_gen_spill_reg(gen, first_stack_reg, -1, value); + } + value->in_frame = 1; + value_used = 1; + } + value->in_register = 0; + value->reg = -1; + } + } + + /* Free the register */ + _jit_regs_free_reg(gen, real_reg, value_used); + } + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "leave spill_all_between"); +#endif +} + +/* + * Spill a specific register. If it is in a stack, then all registers + * above the specific register must also be spilled. + */ +static void spill_register(jit_gencode_t gen, int reg) +{ + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + return spill_all_between(gen, reg, reg); + } + else + { + int first_reg; + reg = gen->contents[reg].remap; + first_reg = reg; + while((_jit_reg_info[first_reg].flags & JIT_REG_START_STACK) == 0) + { + --first_reg; + } + spill_all_between(gen, first_reg, reg); + } +} + +/* + * Spill all stack registers of a specific type. + */ +static void spill_all_stack(jit_gencode_t gen, int reg) +{ + int first_reg; + while((_jit_reg_info[reg].flags & JIT_REG_START_STACK) == 0) + { + --reg; + } + first_reg = reg; + while((_jit_reg_info[reg].flags & JIT_REG_END_STACK) == 0) + { + ++reg; + } + spill_all_between(gen, first_reg, reg); +} + +/*@ + * @deftypefun void _jit_regs_spill_all (jit_gencode_t gen) + * Spill all of the temporary registers to memory locations. + * Normally used at the end of a block, but may also be used in + * situations where a value must be in a certain register and + * it is too hard to swap things around to put it there. + * @end deftypefun +@*/ +void _jit_regs_spill_all(jit_gencode_t gen) +{ + spill_all_between(gen, 0, JIT_NUM_REGS - 1); +} + +/* + * Free a register within a stack, and renumber the other stack registers + * to compensate for the change. + */ +static void free_stack_reg(jit_gencode_t gen, int reg) +{ + int remap; + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "enter free_stack_reg"); +#endif + + /* Shift everything after this register up by one position */ + remap = gen->contents[reg].remap; + if((_jit_reg_info[remap].flags & JIT_REG_END_STACK) == 0) + { + ++remap; + for(;;) + { + if(gen->stack_map[remap] == -1) + { + /* There are no more active values in this stack */ + gen->stack_map[remap - 1] = -1; + break; + } + else if((_jit_reg_info[remap].flags & JIT_REG_END_STACK) != 0) + { + /* This is the last register in the stack */ + --(gen->contents[gen->stack_map[remap]].remap); + gen->stack_map[remap - 1] = gen->stack_map[remap]; + gen->stack_map[remap] = -1; + break; + } + else + { + /* Shift this stack entry up by one */ + --(gen->contents[gen->stack_map[remap]].remap); + gen->stack_map[remap - 1] = gen->stack_map[remap]; + ++remap; + } + } + } + + /* Clear the remapping for the register */ + gen->contents[reg].remap = -1; + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "leave free_stack_reg"); +#endif +} + +/* + * Make space for a new stack register in a particular stack. + * Returns the pseudo register number of the newly allocated register. + */ +static int create_stack_reg(jit_gencode_t gen, int reg, int roll_down) +{ + int first_stack_reg; + int temp_reg, remap; + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "enter create_stack_reg"); +#endif + + /* Find the first pseudo register in the stack */ + while((_jit_reg_info[reg].flags & JIT_REG_START_STACK) == 0) + { + --reg; + } + first_stack_reg = reg; + + /* Find a free pseudo register in the stack */ + for(;;) + { + if(gen->contents[reg].num_values == 0 && + !(gen->contents[reg].used_for_temp)) + { + break; + } + if((_jit_reg_info[reg].flags & JIT_REG_END_STACK) != 0) + { + /* None of the registers are free, so we have to spill them all */ + spill_all_between(gen, first_stack_reg, reg); + reg = first_stack_reg; + break; + } + ++reg; + } + + /* Roll the stack remappings down to make room at the top */ + if(roll_down) + { + temp_reg = first_stack_reg - 1; + do + { + ++temp_reg; + remap = gen->contents[temp_reg].remap; + if(remap != -1) + { + /* Change the register's position in the stack */ + gen->contents[temp_reg].remap = remap + 1; + gen->stack_map[remap + 1] = temp_reg; + + /* Mark the rolled-down register position as touched */ + jit_reg_set_used(gen->touched, remap + 1); + } + } + while((_jit_reg_info[temp_reg].flags & JIT_REG_END_STACK) == 0); + gen->contents[reg].remap = first_stack_reg; + gen->stack_map[first_stack_reg] = reg; + } + + /* Mark the register as touched, in case it needs to be saved */ + jit_reg_set_used(gen->touched, reg); + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "leave create_stack_reg"); +#endif + + /* Return the free register to the caller */ + return reg; +} + +/* + * Free a register, and optionally spill its value. + */ +static void free_reg_and_spill + (jit_gencode_t gen, int reg, int value_used, int spill) +{ + int other_reg, posn; + jit_value_t value; + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "enter free_reg_and_spill"); +#endif + + /* Find the other register in a long pair */ + if(gen->contents[reg].is_long_start) + { + other_reg = _jit_reg_info[reg].other_reg; + gen->contents[reg].is_long_start = 0; + gen->contents[other_reg].is_long_end = 0; + } + else if(gen->contents[reg].is_long_end) + { + gen->contents[reg].is_long_end = 0; + other_reg = reg; + for(reg = 0; reg < JIT_NUM_REGS; ++reg) + { + if(other_reg == _jit_reg_info[reg].other_reg) + { + gen->contents[reg].is_long_start = 0; + break; + } + } + } + else + { + other_reg = -1; + } + + /* Spill the register's contents to the local variable frame */ + if(spill && gen->contents[reg].num_values > 0) + { + for(posn = gen->contents[reg].num_values - 1; posn >= 0; --posn) + { + value = gen->contents[reg].values[posn]; + if(!(value->in_frame)) + { + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + _jit_gen_spill_reg(gen, reg, other_reg, value); + } + else + { + _jit_gen_spill_reg + (gen, gen->contents[reg].remap, -1, value); + } + value->in_frame = 1; + value_used = 1; + } + value->in_register = 0; + value->reg = -1; + } + } + + /* The registers do not contain values any more */ + gen->contents[reg].num_values = 0; + gen->contents[reg].used_for_temp = 0; + if(other_reg != -1) + { + gen->contents[other_reg].num_values = 0; + gen->contents[other_reg].used_for_temp = 0; + } + + /* If the registers are members of a stack, then readjust the + stack mappings to compensate for the change */ + if(gen->contents[reg].remap != -1) + { + free_stack_reg(gen, reg); + } + if(other_reg != -1 && gen->contents[other_reg].remap != -1) + { + free_stack_reg(gen, other_reg); + } + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "leave free_reg_and_spill"); +#endif + + /* Free the register using CPU-specific code */ + _jit_gen_free_reg(gen, reg, other_reg, value_used); +} + +/*@ + * @deftypefun void _jit_regs_want_reg (jit_gencode_t gen, int reg) + * Tell the register allocator that we want a particular register + * for a specific purpose. The current contents of the register + * are spilled. If @code{reg} is part of a register pair, then the + * other register in the pair will also be spilled. If @code{reg} + * is a stack register, then it should be the first one. + * + * This is typically used for x86 instructions that require operands + * to be in certain registers (especially multiplication and division), + * and we want to make sure that the register is free before we clobber it. + * It is also used to make space in the x86 FPU for floating-point returns. + * + * This may return a different pseudo register number if @code{reg} + * was a member of a stack and some other register was made free. + * @end deftypefun +@*/ +int _jit_regs_want_reg(jit_gencode_t gen, int reg, int for_long) +{ + int other_reg; + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + /* Spill an ordinary register and its pair register */ + free_reg_and_spill(gen, reg, 0, 1); + if(for_long) + { + other_reg = _jit_reg_info[reg].other_reg; + if(other_reg != -1) + { + free_reg_and_spill(gen, other_reg, 0, 1); + } + } + else + { + other_reg = -1; + } + + /* Mark the register as touched and return it */ + jit_reg_set_used(gen->touched, reg); + if(other_reg != -1) + { + jit_reg_set_used(gen->touched, other_reg); + } + return reg; + } + else + { + /* If we want a stack register, all we have to do is roll + everything down to make room for the new value. If the + stack is full, then we spill the entire stack */ + return create_stack_reg(gen, reg, 0); + } +} + +/*@ + * @deftypefun void _jit_regs_free_reg (jit_gencode_t gen, int reg, int value_used) + * Free the contents of a pseudo register, without spilling. Used when + * the contents of a register becomes invalid. If @code{value_used} + * is non-zero, then it indicates that the value has already been + * used. On some systems, an explicit instruction is needed to free + * a register whose value hasn't been used yet (e.g. x86 floating point + * stack registers). + * @end deftypefun +@*/ +void _jit_regs_free_reg(jit_gencode_t gen, int reg, int value_used) +{ + free_reg_and_spill(gen, reg, value_used, 0); +} + +/*@ + * @deftypefun void _jit_regs_set_value (jit_gencode_t gen, int reg, jit_value_t value, int still_in_frame) + * Set pseudo register @code{reg} to record that it currently holds the + * contents of @code{value}. The value is assumed to already be in + * the register and no spill occurs. If @code{still_in_frame} is + * non-zero, then the value is still in the stack frame; otherwise the + * value is exclusively in the register. + * @end deftypefun +@*/ +void _jit_regs_set_value + (jit_gencode_t gen, int reg, jit_value_t value, int still_in_frame) +{ + int other_reg; + int first_stack_reg; + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "enter set_value"); +#endif + + /* Get the other register in a pair */ + if(_jit_regs_needs_long_pair(value->type)) + { + other_reg = _jit_reg_info[reg].other_reg; + } + else + { + other_reg = -1; + } + + /* Mark the register as touched */ + jit_reg_set_used(gen->touched, reg); + if(other_reg != -1) + { + jit_reg_set_used(gen->touched, other_reg); + } + + /* Adjust the allocation state to reflect that "reg" contains "value" */ + gen->contents[reg].values[0] = value; + gen->contents[reg].num_values = 1; + gen->contents[reg].age = gen->current_age; + if(other_reg == -1) + { + gen->contents[reg].is_long_start = 0; + gen->contents[reg].is_long_end = 0; + gen->contents[reg].used_for_temp = 0; + } + else + { + gen->contents[reg].is_long_start = 1; + gen->contents[reg].is_long_end = 0; + gen->contents[reg].used_for_temp = 0; + gen->contents[other_reg].num_values = 0; + gen->contents[other_reg].is_long_start = 0; + gen->contents[other_reg].is_long_end = 1; + gen->contents[other_reg].age = gen->current_age; + gen->contents[other_reg].used_for_temp = 0; + } + (gen->current_age)++; + + /* Set the stack mappings for this register */ + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0) + { + first_stack_reg = reg; + while((_jit_reg_info[first_stack_reg].flags & JIT_REG_START_STACK) == 0) + { + --first_stack_reg; + } + gen->contents[reg].remap = first_stack_reg; + gen->stack_map[first_stack_reg] = reg; + } + +#ifdef JIT_REG_DEBUG + dump_regs(gen, "leave set_value"); +#endif + + /* Adjust the value to reflect that it is in "reg", and maybe the frame */ + value->in_register = 1; + value->in_frame = still_in_frame; + value->reg = (short)reg; +} + +/*@ + * @deftypefun void _jit_regs_set_incoming (jit_gencode_t gen, int reg, jit_value_t value) + * Set pseudo register @code{reg} to record that it currently holds the + * contents of @code{value}. If the register was previously in use, + * then spill its value first. + * @end deftypefun +@*/ +void _jit_regs_set_incoming(jit_gencode_t gen, int reg, jit_value_t value) +{ + /* Eject any values that are currently in the register */ + reg = _jit_regs_want_reg(gen, reg, _jit_regs_needs_long_pair(value->type)); + + /* Record that the value is in "reg", but not in the frame */ + _jit_regs_set_value(gen, reg, value, 0); +} + +/*@ + * @deftypefun void _jit_regs_set_outgoing (jit_gencode_t gen, int reg, jit_value_t value) + * Load the contents of @code{value} into pseudo register @code{reg}, + * spilling out the current contents. This is used to set up outgoing + * parameters for a function call. + * @end deftypefun +@*/ +void _jit_regs_set_outgoing(jit_gencode_t gen, int reg, jit_value_t value) +{ + /* TODO */ +} + +/*@ + * @deftypefun int _jit_regs_is_top (jit_gencode_t gen, jit_value_t value) + * Determine if @code{value} is currently the in top-most position + * in the appropriate register stack. Always returns non-zero if + * @code{value} is in a register, but that register is not part of a + * register stack. This is used to check if an operand value is + * already in the right position for a unary operation. + * @end deftypefun +@*/ +int _jit_regs_is_top(jit_gencode_t gen, jit_value_t value) +{ + int reg, remap; + if(!(value->in_register)) + { + return 0; + } + reg = value->reg; + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + return 1; + } + remap = gen->contents[reg].remap; + if(remap != -1 && (_jit_reg_info[remap].flags & JIT_REG_START_STACK) != 0) + { + return 1; + } + return 0; +} + +/*@ + * @deftypefun int _jit_regs_is_top_two (jit_gencode_t gen, jit_value_t value1, jit_value_t value2) + * Determine if @code{value1} and @code{value2} are in the top two positions + * in the appropriate register stack, and @code{value2} is above + * @code{value1}. Always returns non-zero if @code{value} and + * @code{value2} are in registers, but those registers are not part + * of a register stack. This is used to check if the operand values + * for a binary operation are already in the right positions. + * @end deftypefun +@*/ +int _jit_regs_is_top_two + (jit_gencode_t gen, jit_value_t value1, jit_value_t value2) +{ + int reg, remap; + if(!(value1->in_register) || !(value2->in_register)) + { + return 0; + } + reg = value2->reg; + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + reg = value1->reg; + return ((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0); + } + remap = gen->contents[reg].remap; + if(remap == -1 || (_jit_reg_info[remap].flags & JIT_REG_START_STACK) == 0) + { + return 0; + } + reg = value1->reg; + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) == 0) + { + return 1; + } + return (gen->contents[reg].remap == (remap + 1)); +} + +/* + * Load a value into a register. + */ +static void load_value(jit_gencode_t gen, int reg, int other_reg, + jit_value_t value, int destroy) +{ + _jit_gen_load_value(gen, reg, other_reg, value); + if(destroy || value->is_constant) + { + /* Mark the register as containing a temporary value */ + gen->contents[reg].used_for_temp = 1; + jit_reg_set_used(gen->touched, reg); + if(other_reg != -1) + { + gen->contents[reg].is_long_start = 1; + gen->contents[other_reg].is_long_end = 1; + gen->contents[other_reg].used_for_temp = 1; + jit_reg_set_used(gen->touched, other_reg); + } + } + else + { + /* Mark the register as containing the value we have loaded */ + _jit_regs_set_value(gen, reg, value, value->in_frame); + } +} + +/*@ + * @deftypefun int _jit_regs_load_value (jit_gencode_t gen, jit_value_t value, int destroy, int used_again) + * Load a value into any register that is suitable and return that register. + * If the value needs a long pair, then this will return the first register + * in the pair. Returns -1 if the value will not fit into any register. + * + * If @code{destroy} is non-zero, then we are about to destroy the register, + * so the system must make sure that such destruction will not side-effect + * @code{value} or any of the other values currently in that register. + * + * If @code{used_again} is non-zero, then it indicates that the value is + * used again further down the block. + * @end deftypefun +@*/ +int _jit_regs_load_value + (jit_gencode_t gen, jit_value_t value, int destroy, int used_again) +{ + int reg, other_reg, type; + int suitable_reg, need_pair; + int suitable_age; + + /* Determine if we need a long pair for this value */ + need_pair = _jit_regs_needs_long_pair(value->type); + + /* If the value is already in a register, then try to use that register */ + if(value->in_register) + { + reg = value->reg; + if(destroy) + { + if(gen->contents[reg].num_values == 1 && + (value->in_frame || !used_again)) + { + /* We are the only value in this register, and the + value is duplicated in the frame, or will never + be used again in this block. In this case, + we can disassociate the register from the value + and just return the register as-is */ + value->in_register = 0; + gen->contents[reg].num_values = 0; + gen->contents[reg].used_for_temp = 1; + gen->contents[reg].age = gen->current_age; + if(need_pair) + { + other_reg = _jit_reg_info[reg].other_reg; + gen->contents[other_reg].used_for_temp = 1; + gen->contents[other_reg].age = gen->current_age; + } + ++(gen->current_age); + return reg; + } + else + { + /* We need to spill the register and then reload it */ + spill_register(gen, reg); + } + } + else + { + gen->contents[reg].age = gen->current_age; + if(need_pair) + { + other_reg = _jit_reg_info[reg].other_reg; + gen->contents[other_reg].age = gen->current_age; + } + ++(gen->current_age); + return reg; + } + } + + /* Determine the type of register that we need */ + switch(jit_type_normalize(value->type)->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + case JIT_TYPE_SIGNATURE: + case JIT_TYPE_PTR: + { + type = JIT_REG_WORD; + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + if(need_pair) + type = JIT_REG_LONG; + else + type = JIT_REG_WORD; + } + break; + + case JIT_TYPE_FLOAT32: + case JIT_TYPE_FLOAT64: + case JIT_TYPE_NFLOAT: + { + type = JIT_REG_FLOAT; + } + break; + + default: return -1; + } + + /* Search for a free register, ignoring permanent global allocations. + We also keep track of the oldest suitable register that is not free */ + suitable_reg = -1; + suitable_age = -1; + for(reg = 0; reg < JIT_NUM_REGS; ++reg) + { + if((_jit_reg_info[reg].flags & type) != 0 && + !jit_reg_is_used(gen->permanent, reg)) + { + if((_jit_reg_info[reg].flags & JIT_REG_IN_STACK) != 0) + { + /* We always load stack values to the top of the stack */ + reg = create_stack_reg(gen, reg, 1); + load_value(gen, reg, -1, value, destroy); + return reg; + } + else if(!need_pair) + { + if(gen->contents[reg].num_values == 0 && + !(gen->contents[reg].used_for_temp)) + { + load_value(gen, reg, -1, value, destroy); + return reg; + } + } + else + { + other_reg = _jit_reg_info[reg].other_reg; + if(gen->contents[reg].num_values == 0 && + !(gen->contents[reg].used_for_temp) && + gen->contents[other_reg].num_values == 0 && + !(gen->contents[other_reg].used_for_temp)) + { + load_value(gen, reg, other_reg, value, destroy); + return reg; + } + } + if(suitable_reg == -1 || gen->contents[reg].age < suitable_age) + { + /* This is the oldest suitable register of this type */ + suitable_reg = reg; + suitable_age = gen->contents[reg].age; + } + } + } + + /* If there were no suitable registers at all, then fail */ + if(suitable_reg == -1) + { + return -1; + } + + /* Eject the current contents of the register */ + reg = _jit_regs_want_reg(gen, reg, need_pair); + + /* Load the value into the register */ + if(!need_pair) + { + load_value(gen, reg, -1, value, destroy); + } + else + { + load_value(gen, reg, _jit_reg_info[reg].other_reg, value, destroy); + } + return reg; +} + +/* + * Determine if "num" stack registers are free in a specific stack. + */ +static int stack_regs_free(jit_gencode_t gen, int reg, int num) +{ + int first_reg; + + /* Find the extents of the stack */ + while((_jit_reg_info[reg].flags & JIT_REG_START_STACK) == 0) + { + --reg; + } + first_reg = reg; + while((_jit_reg_info[reg].flags & JIT_REG_END_STACK) == 0) + { + ++reg; + } + + /* Search for free registers */ + while(reg >= first_reg) + { + if(gen->contents[reg].num_values == 0 && + !(gen->contents[reg].used_for_temp)) + { + --num; + if(num <= 0) + { + return 1; + } + } + --reg; + } + return 0; +} + +/*@ + * @deftypefun int _jit_regs_load_to_top (jit_gencode_t gen, jit_value_t value, int used_again, int type_reg) + * Load the contents of @code{value} into a register that is guaranteed to + * be at the top of its stack. This is the preferred way to set up for a + * unary operation on a stack-based architecture. Returns the pseudo + * register that contains the value. + * + * When @code{value} is loaded, the "destroy" flag is set so that the + * unary operation will not affect the original contents of @code{value}. + * The @code{used_again} flag indicates if @code{value} is used again + * in the current basic block. + * + * The @code{type_reg} parameter should be set to the pseudo register + * number of a suitable register. This is used to determine which + * register stack to use for the allocation. + * @end deftypefun +@*/ +int _jit_regs_load_to_top(jit_gencode_t gen, jit_value_t value, int used_again, int type_reg) +{ + int reg; + + /* Determine if the value is already in the top-most register */ + if(value->in_register) + { + reg = value->reg; + if((_jit_reg_info[gen->contents[reg].remap].flags + & JIT_REG_START_STACK) != 0) + { + if(value->in_frame || !used_again) + { + /* Disassociate the value from the register and return */ + value->in_register = 0; + gen->contents[reg].num_values = 0; + gen->contents[reg].used_for_temp = 1; + gen->contents[reg].age = gen->current_age; + ++(gen->current_age); + return reg; + } + } + spill_all_stack(gen, type_reg); + } + + /* If there are free registers of this type, then load the value now */ + if(stack_regs_free(gen, type_reg, 1)) + { + return _jit_regs_load_value(gen, value, 1, used_again); + } + + /* Spill the entire stack contents, to get things into a known state */ + spill_all_stack(gen, type_reg); + + /* Reload the value and return */ + return _jit_regs_load_value(gen, value, 1, used_again); +} + +/*@ + * @deftypefun int _jit_regs_load_to_top_two (jit_gencode_t gen, jit_value_t value, jit_value_t value2, int used_again1, int used_again2, int type_reg) + * Load the contents of @code{value} and @code{value2} into registers that + * are guaranteed to be at the top of the relevant register stack. + * This is the preferred way to set up for a binary operation on a + * stack-based architecture. + * + * Returns the pseudo register that contains @code{value}. The pseudo + * register that contains @code{value2} is marked as free, because it is + * assumed that the binary operation will immediately consume its value. + * + * When @code{value} are @code{value2} are loaded, the "destroy" flag is + * set so that the binary operation will not affect their original contents. + * The @code{used_again1} and @code{used_again2} flags indicate if + * @code{value} and @code{value2} are used again in the current basic block. + * + * The @code{type_reg} parameter should be set to the pseudo register + * number of a suitable register. This is used to determine which + * register stack to use for the allocation. + * @end deftypefun +@*/ +int _jit_regs_load_to_top_two + (jit_gencode_t gen, jit_value_t value, jit_value_t value2, + int used_again1, int used_again2, int type_reg) +{ + int reg, reg2; + + /* Determine if the values are already in the top two registers */ + if(value->in_register && value2->in_register) + { + reg = value->reg; + reg2 = value->reg; + if((_jit_reg_info[gen->contents[reg2].remap].flags + & JIT_REG_START_STACK) != 0 && + gen->contents[reg].remap == (gen->contents[reg2].remap + 1)) + { + if((value->in_frame || !used_again1) && + (value2->in_frame || !used_again2)) + { + /* Disassociate the values from the registers and return */ + free_stack_reg(gen, reg2); + value->in_register = 0; + value2->in_register = 0; + gen->contents[reg].num_values = 0; + gen->contents[reg].used_for_temp = 1; + gen->contents[reg].age = gen->current_age; + gen->contents[reg2].num_values = 0; + gen->contents[reg2].used_for_temp = 0; + gen->contents[reg2].age = gen->current_age; + ++(gen->current_age); + return reg; + } + } + spill_all_stack(gen, type_reg); + } + + /* If there are free registers of this type, then load the values now */ + if(stack_regs_free(gen, type_reg, 2)) + { + reg = _jit_regs_load_value(gen, value, 1, used_again1); + reg2 = _jit_regs_load_value(gen, value2, 1, used_again2); + free_stack_reg(gen, reg2); + gen->contents[reg2].used_for_temp = 0; + return reg; + } + + /* Spill the entire stack contents, to get things into a known state */ + spill_all_stack(gen, type_reg); + + /* Reload the values and return */ + reg = _jit_regs_load_value(gen, value, 1, used_again1); + reg2 = _jit_regs_load_value(gen, value2, 1, used_again2); + free_stack_reg(gen, reg2); + gen->contents[reg2].used_for_temp = 0; + return reg; +} + +/*@ + * @deftypefun int _jit_regs_num_used (jit_gencode_t gen, int type_reg) + * Get the number of stack registers in use within the register stack + * indicated by @code{type_reg}. + * @end deftypefun +@*/ +int _jit_regs_num_used(jit_gencode_t gen, int type_reg) +{ + int count; + while((_jit_reg_info[type_reg].flags & JIT_REG_START_STACK) == 0) + { + --type_reg; + } + count = 0; + for(;;) + { + if(gen->contents[type_reg].num_values > 0 || + gen->contents[type_reg].used_for_temp) + { + ++count; + } + if((_jit_reg_info[type_reg].flags & JIT_REG_END_STACK) == 0) + { + break; + } + ++type_reg; + } + return count; +} diff --git a/jit/jit-reg-alloc.h b/jit/jit-reg-alloc.h new file mode 100644 index 0000000..f516e96 --- /dev/null +++ b/jit/jit-reg-alloc.h @@ -0,0 +1,56 @@ +/* + * jit-reg-alloc.h - Register allocation routines for the JIT. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_REG_ALLOC_H +#define _JIT_REG_ALLOC_H + +#include "jit-rules.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void _jit_regs_init_for_block(jit_gencode_t gen); +int _jit_regs_needs_long_pair(jit_type_t type); +int _jit_regs_get_cpu(jit_gencode_t gen, int reg, int *other_reg); +void _jit_regs_spill_all(jit_gencode_t gen); +int _jit_regs_want_reg(jit_gencode_t gen, int reg, int for_long); +void _jit_regs_free_reg(jit_gencode_t gen, int reg, int value_used); +void _jit_regs_set_value + (jit_gencode_t gen, int reg, jit_value_t value, int still_in_frame); +void _jit_regs_set_incoming(jit_gencode_t gen, int reg, jit_value_t value); +void _jit_regs_set_outgoing(jit_gencode_t gen, int reg, jit_value_t value); +int _jit_regs_is_top(jit_gencode_t gen, jit_value_t value); +int _jit_regs_is_top_two + (jit_gencode_t gen, jit_value_t value1, jit_value_t value2); +int _jit_regs_load_value + (jit_gencode_t gen, jit_value_t value, int destroy, int used_again); +int _jit_regs_load_to_top + (jit_gencode_t gen, jit_value_t value, int used_again, int type_reg); +int _jit_regs_load_to_top_two + (jit_gencode_t gen, jit_value_t value, jit_value_t value2, + int used_again1, int used_again2, int type_reg); +int _jit_regs_num_used(jit_gencode_t gen, int type_reg); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_REG_ALLOC_H */ diff --git a/jit/jit-rules-arm.c b/jit/jit-rules-arm.c new file mode 100644 index 0000000..e85101f --- /dev/null +++ b/jit/jit-rules-arm.c @@ -0,0 +1,669 @@ +/* + * jit-rules-arm.c - Rules that define the characteristics of the ARM. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" +#include "jit-apply-rules.h" + +#if defined(JIT_BACKEND_ARM) + +#include "jit-gen-arm.h" + +/* + * Determine if we actually have floating-point registers. + */ +#if JIT_APPLY_NUM_FLOAT_REGS != 0 || JIT_APPLY_RETURN_FLOATS_AFTER != 0 + #define JIT_ARM_HAS_FLOAT_REGS 1 +#endif + +/* + * Round a size up to a multiple of the stack word size. + */ +#define ROUND_STACK(size) \ + (((size) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) + +void _jit_init_backend(void) +{ +#ifndef JIT_ARM_HAS_FLOAT_REGS + /* Turn off floating-point registers, as this ARM core doesn't have them */ + int reg; + for(reg = 16; reg < JIT_NUM_REGS; ++reg) + { + _jit_reg_info[reg].flags = JIT_REG_FIXED; + } +#endif +} + +void _jit_gen_get_elf_info(jit_elf_info_t *info) +{ + info->machine = 40; /* EM_ARM */ + info->abi = 0; /* ELFOSABI_SYSV */ + info->abi_version = 0; +} + +/* + * Force values out of parameter registers that cannot be easily + * accessed in register form (i.e. long, float, and struct values). + */ +static int force_out_of_regs(jit_function_t func, jit_value_t param, + int next_reg, unsigned int size) +{ + jit_value_t address; + jit_value_t temp; + jit_nint offset = 0; + jit_nint frame_offset = sizeof(void *); + + /* Get the address of the parameter, to force it into the frame, + and to set up for the later "jit_insn_store_relative" calls */ + address = jit_insn_address_of(func, param); + if(!address) + { + return 0; + } + + /* Force the values out of the registers */ + while(next_reg < ARM_NUM_PARAM_REGS && size > 0) + { + temp = jit_value_create(func, jit_type_void_ptr); + if(!temp) + { + return 0; + } + if(!jit_insn_incoming_reg(func, temp, next_reg)) + { + return 0; + } + if(!jit_insn_store_relative(func, address, offset, temp)) + { + return 0; + } + offset += sizeof(void *); + size -= sizeof(void *); + ++next_reg; + } + + + /* Force the rest of the value out of the incoming stack frame */ + while(size > 0) + { + temp = jit_value_create(func, jit_type_void_ptr); + if(!temp) + { + return 0; + } + if(!jit_insn_incoming_frame_posn(func, temp, frame_offset)) + { + return 0; + } + if(!jit_insn_store_relative(func, address, offset, temp)) + { + return 0; + } + offset += sizeof(void *); + frame_offset += sizeof(void *); + size -= sizeof(void *); + } + return 1; +} + +int _jit_create_entry_insns(jit_function_t func) +{ + jit_type_t signature = func->signature; + jit_type_t type; + int next_reg; + jit_nint offset; + jit_value_t value; + unsigned int num_params; + unsigned int param; + unsigned int size; + + /* Reset the frame size for this function. We start by assuming + that lr, sp, fp, r8, r7, r6, r5, and r4 need to be saved in + the local frame, as that is the worst-case scenario */ + func->builder->frame_size = 8 * sizeof(void *); + + /* The next register to be allocated to parameters is r0 */ + next_reg = 0; + + /* The starting parameter offset (saved pc on stack) */ + offset = sizeof(void *); + + /* If the function is nested, then we need an extra parameter + to pass the pointer to the parent's local variable frame */ + if(func->nested_parent) + { + ++next_reg; + } + + /* Allocate the structure return pointer */ + value = jit_value_get_struct_pointer(func); + if(value) + { + if(!jit_insn_incoming_reg(func, value, next_reg)) + { + return 0; + } + ++next_reg; + } + + /* Allocate the parameter registers and offsets */ + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + value = jit_value_get_param(func, param); + if(!value) + { + continue; + } + type = jit_type_normalize(jit_value_get_type(value)); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + case JIT_TYPE_SIGNATURE: + case JIT_TYPE_PTR: + { + if(next_reg < ARM_NUM_PARAM_REGS) + { + if(!jit_insn_incoming_reg(func, value, next_reg)) + { + return 0; + } + ++next_reg; + } + else + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + offset += sizeof(void *); + } + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + case JIT_TYPE_FLOAT32: + case JIT_TYPE_FLOAT64: + case JIT_TYPE_NFLOAT: + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + /* Force these kinds of values out of the word registers. + While technically we could keep long and float values + in word registers on ARM, it simplifies the register + allocator if we force them out first */ + size = ROUND_STACK(jit_type_get_size(type)); + if(next_reg < ARM_NUM_PARAM_REGS) + { + if(!force_out_of_regs(func, value, next_reg, size)) + { + return 0; + } + while(size > 0 && next_reg < ARM_NUM_PARAM_REGS) + { + ++next_reg; + size -= sizeof(void *); + } + offset += size; + } + else + { + /* The value is completely on the stack */ + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + offset += size; + } + } + break; + } + } + return 1; +} + +int _jit_create_call_setup_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + int is_nested, int nesting_level, jit_value_t *struct_return) +{ + jit_type_t type = jit_type_get_return(signature); + jit_value_t value; + unsigned int size; + unsigned int index; + unsigned int num_stack_args; + unsigned int word_regs; + jit_value_t partial; + + /* Determine which values are going to end up in registers */ + word_regs = 0; + if(func->nested_parent) + { + ++word_regs; + } + if(jit_type_return_via_pointer(type)) + { + ++word_regs; + } + index = 0; + partial = 0; + while(index < num_args && word_regs < ARM_NUM_PARAM_REGS) + { + size = jit_type_get_size(jit_value_get_type(args[index])); + size = ROUND_STACK(size) / sizeof(void *); + if(size <= (ARM_NUM_PARAM_REGS - word_regs)) + { + /* This argument will fit entirely in registers */ + word_regs += size; + ++index; + } + else + { + /* Partly in registers and partly on the stack. + We first copy it into a buffer that we can address */ + partial = jit_value_create + (func, jit_value_get_type(args[index])); + if(!partial) + { + return 0; + } + jit_value_set_addressable(partial); + if(!jit_insn_store(func, partial, args[index])) + { + return 0; + } + ++index; + break; + } + } + num_stack_args = num_args - index; + + /* Push all of the stacked arguments in reverse order */ + while(num_stack_args > 0) + { + --num_stack_args; + --num_args; + if(!jit_insn_push(func, args[num_args])) + { + return 0; + } + } + + /* Handle a value that is partly on the stack and partly in registers */ + if(partial) + { + --num_args; + index = (ARM_NUM_PARAM_REGS - word_regs) * sizeof(void *); + size = ROUND_STACK(jit_type_get_size(jit_value_get_type(partial))); + while(size > index) + { + size -= sizeof(void *); + value = jit_value_create(func, jit_type_void_ptr); + if(!value) + { + return 0; + } + value = jit_insn_load_relative + (func, value, (jit_nint)size, jit_type_void_ptr); + if(!value) + { + return 0; + } + if(!jit_insn_push(func, value)) + { + return 0; + } + } + while(size > 0) + { + size -= sizeof(void *); + value = jit_value_create(func, jit_type_void_ptr); + if(!value) + { + return 0; + } + value = jit_insn_load_relative + (func, value, (jit_nint)size, jit_type_void_ptr); + if(!value) + { + return 0; + } + --word_regs; + if(!jit_insn_outgoing_reg(func, value, (int)word_regs)) + { + return 0; + } + } + } + + /* Push arguments that will end up entirely in registers */ + while(num_args > 0) + { + --num_args; + size = jit_type_get_size(jit_value_get_type(args[num_args])); + size = ROUND_STACK(size) / sizeof(void *); + word_regs -= size; + if(!jit_insn_outgoing_reg(func, args[num_args], (int)size)) + { + return 0; + } + } + + /* Do we need to add a structure return pointer argument? */ + if(jit_type_return_via_pointer(type)) + { + value = jit_value_create(func, type); + if(!value) + { + return 0; + } + *struct_return = value; + value = jit_insn_address_of(func, value); + if(!value) + { + return 0; + } + --word_regs; + if(!jit_insn_outgoing_reg(func, value, (int)word_regs)) + { + return 0; + } + } + else + { + *struct_return = 0; + } + + /* Do we need to add nested function scope information? */ + if(is_nested) + { + --word_regs; + if(!jit_insn_setup_for_nested(func, nesting_level, (int)word_regs)) + { + return 0; + } + } + + /* The call is ready to proceed */ + return 1; +} + +int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value) +{ + return jit_insn_outgoing_reg(func, value, ARM_WORK); +} + +int _jit_create_call_return_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + jit_value_t return_value, int is_nested) +{ + jit_nint pop_bytes; + unsigned int size; + jit_type_t return_type; + int ptr_return; + + /* Calculate the number of bytes that we need to pop */ + return_type = jit_type_normalize(jit_type_get_return(signature)); + ptr_return = jit_type_return_via_pointer(return_type); + pop_bytes = 0; + while(num_args > 0) + { + --num_args; + size = jit_type_get_size(jit_value_get_type(args[num_args])); + pop_bytes += ROUND_STACK(size); + } + if(ptr_return) + { + pop_bytes += sizeof(void *); + } + if(is_nested) + { + pop_bytes += sizeof(void *); + } + if(pop_bytes > (ARM_NUM_PARAM_REGS * sizeof(void *))) + { + pop_bytes -= (ARM_NUM_PARAM_REGS * sizeof(void *)); + } + else + { + pop_bytes = 0; + } + + /* Pop the bytes from the system stack */ + if(pop_bytes > 0) + { + if(!jit_insn_pop_stack(func, pop_bytes)) + { + return 0; + } + } + + /* Bail out now if we don't need to worry about return values */ + if(!return_value || ptr_return) + { + return 0; + } + + /* Structure values must be flushed into the frame, and + everything else ends up in a register */ + if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) + { + if(!jit_insn_flush_struct(func, return_value)) + { + return 0; + } + } + else + { + if(!jit_insn_return_reg(func, return_value, ARM_R0)) + { + return 0; + } + } + + /* Everything is back where it needs to be */ + return 1; +} + +void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf) +{ + unsigned int prolog[JIT_PROLOG_SIZE / sizeof(int)]; + arm_inst_ptr inst = prolog; + int reg, regset; + unsigned int saved; + unsigned int frame_size; + + /* Determine which registers need to be preserved */ + regset = 0; + saved = 0; + for(reg = 0; reg <= 15; ++reg) + { + if(jit_reg_is_used(gen->touched, reg) && + (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) + { + regset |= (1 << reg); + saved += sizeof(void *); + } + } + + /* Setup the frame, pushing all the callee-save registers */ + arm_setup_frame(inst, regset); + + /* Allocate space for the local variable frame. Subtract off + the space for the registers that we just saved. The pc, lr, + and fp registers are always saved, so account for them too */ + frame_size = func->builder->frame_size - (saved + 3); + if(frame_size > 0) + { + arm_alu_reg_imm(inst, ARM_SUB, ARM_SP, ARM_SP, frame_size); + } + + /* Copy the prolog into place and return the adjusted entry position */ + reg = (int)((inst - prolog) * sizeof(unsigned int)); + jit_memcpy(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg, prolog, reg); + return (void *)(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg); +} + +void _jit_gen_epilog(jit_gencode_t gen, jit_function_t func) +{ + int reg, regset; + arm_inst_ptr inst; + + /* Bail out if there is insufficient space for the epilog */ + if(!jit_cache_check_for_n(&(gen->posn), 4)) + { + jit_cache_mark_full(&(gen->posn)); + return; + } + + /* Determine which registers need to be restored when we return */ + regset = 0; + for(reg = 0; reg <= 15; ++reg) + { + if(jit_reg_is_used(gen->touched, reg) && + (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) + { + regset |= (1 << reg); + } + } + + /* Pop the local stack frame and return */ + inst = (arm_inst_ptr)(gen->posn.ptr); + arm_pop_frame(inst, regset); + gen->posn.ptr = (unsigned char *)inst; +} + +void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func) +{ + void *ptr, *entry; + arm_inst_ptr inst; + if(!jit_cache_check_for_n(&(gen->posn), 12)) + { + jit_cache_mark_full(&(gen->posn)); + return 0; + } + ptr = (void *)&(func->entry_point); + entry = gen->posn.ptr; + inst = (arm_inst_ptr)(gen->posn.ptr); + arm_load_membase(inst, ARM_WORK, ARM_PC, 0); + arm_load_membase(inst, ARM_PC, ARM_WORK, 0); + *inst++ = (unsigned int)ptr; + gen->posn.ptr = (unsigned char *)inst; + return entry; +} + +/* + * Setup or teardown the ARM code output process. + */ +#define jit_cache_setup_output(needed) \ + arm_inst_ptr inst = (arm_inst_ptr)(gen->posn.ptr); \ + if(!jit_cache_check_for_n(&(gen->posn), (needed))) \ + { \ + jit_cache_mark_full(&(gen->posn)); \ + return; \ + } +#define jit_cache_end_output() \ + gen->posn.ptr = (unsigned char *)inst + +void _jit_gen_spill_reg(jit_gencode_t gen, int reg, + int other_reg, jit_value_t value) +{ + int offset; + + /* Make sure that we have sufficient space */ + jit_cache_setup_output(20); + + /* Fix the value in place within the local variable frame */ + _jit_gen_fix_value(value); + + /* Output an appropriate instruction to spill the value */ + offset = (int)(value->frame_offset); + arm_store_membase(inst, ARM_FP, offset, reg); + if(other_reg != -1) + { + /* Spill the other word register in a pair */ + offset += sizeof(void *); + arm_store_membase(inst, ARM_FP, offset, reg); + } + + /* End the code output process */ + jit_cache_end_output(); +} + +void _jit_gen_free_reg(jit_gencode_t gen, int reg, + int other_reg, int value_used) +{ + /* We don't have to do anything to free ARM registers */ +} + +void _jit_gen_load_value + (jit_gencode_t gen, int reg, int other_reg, jit_value_t value) +{ + /* TODO */ +} + +void _jit_gen_fix_value(jit_value_t value) +{ + if(!(value->has_frame_offset) && !(value->is_constant)) + { + jit_nint size = (jit_nint)(ROUND_STACK(jit_type_get_size(value->type))); + value->block->func->builder->frame_size += size; + value->frame_offset = -(value->block->func->builder->frame_size); + value->has_frame_offset = 1; + } +} + +void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, + jit_block_t block, jit_insn_t insn) +{ + /* TODO */ +} + +void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block) +{ + /* TODO: label fixups */ +} + +void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) +{ + /* Nothing to do here for ARM */ +} + +void _jit_gen_call_finally + (jit_gencode_t gen, jit_function_t func, jit_label_t label) +{ + /* TODO */ +} + +void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object) +{ + /* TODO */ +} + +#endif /* JIT_BACKEND_ARM */ diff --git a/jit/jit-rules-arm.h b/jit/jit-rules-arm.h new file mode 100644 index 0000000..4ec5f60 --- /dev/null +++ b/jit/jit-rules-arm.h @@ -0,0 +1,89 @@ +/* + * jit-rules-arm.h - Rules that define the characteristics of the ARM. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_RULES_ARM_H +#define _JIT_RULES_ARM_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Information about all of the registers, in allocation order. + * We use r0-r5 for general-purpose values and r6-r8 for globals. + * + * Of the floating-point registers, we only use f0-f3 at present, + * so that we don't have to worry about saving the values of f4-f7. + * The floating-point registers are only present on some ARM cores. + * "_jit_init_backend" will disable the FP registers if they don't exist. + */ +#define JIT_REG_INFO \ + {"r0", 0, 1, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"r1", 1, -1, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"r2", 2, 3, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"r3", 3, -1, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"r4", 4, -1, JIT_REG_WORD}, \ + {"r5", 5, -1, JIT_REG_WORD}, \ + {"r6", 6, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ + {"r7", 7, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ + {"r8", 8, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ + {"r9", 9, -1, JIT_REG_FIXED}, /* pic reg */ \ + {"r10", 10, -1, JIT_REG_FIXED}, /* stack limit */ \ + {"fp", 11, -1, JIT_REG_FIXED | JIT_REG_FRAME}, \ + {"r12", 12, -1, JIT_REG_FIXED | JIT_REG_CALL_USED}, /* work reg */ \ + {"sp", 13, -1, JIT_REG_FIXED | JIT_REG_STACK_PTR}, \ + {"lr", 14, -1, JIT_REG_FIXED}, \ + {"pc", 15, -1, JIT_REG_FIXED}, \ + {"f0", 0, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED}, \ + {"f1", 1, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED}, \ + {"f2", 2, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED}, \ + {"f3", 3, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED}, +#define JIT_NUM_REGS 20 + +/* + * Define to 1 if we should always load values into registers + * before operating on them. i.e. the CPU does not have reg-mem + * and mem-reg addressing modes. + */ +#define JIT_ALWAYS_REG_REG 1 + +/* + * The maximum number of bytes to allocate for the prolog. + * This may be shortened once we know the true prolog size. + */ +#define JIT_PROLOG_SIZE 48 + +/* + * Preferred alignment for the start of functions. + */ +#define JIT_FUNCTION_ALIGNMENT 4 + +/* + * Define this to 1 if the platform allows reads and writes on + * any byte boundary. Define to 0 if only properly-aligned + * memory accesses are allowed. + */ +#define JIT_ALIGN_OVERRIDES 0 + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_RULES_ARM_H */ diff --git a/jit/jit-rules-interp.c b/jit/jit-rules-interp.c new file mode 100644 index 0000000..3ac69b1 --- /dev/null +++ b/jit/jit-rules-interp.c @@ -0,0 +1,1351 @@ +/* + * jit-rules-interp.c - Rules that define the interpreter characteristics. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" +#include "jit-reg-alloc.h" + +#if defined(JIT_BACKEND_INTERP) + +#include "jit-interp.h" + +/*@ + +The architecture definition rules for a CPU are placed into the files +@code{jit-rules-ARCH.h} and @code{jit-rules-ARCH.c}. You should add +both of these files to @code{Makefile.am} in @code{libjit/jit}. + +You will also need to edit @code{jit-rules.h} in two places. First, +place detection logic at the top of the file to detect your platform +and define @code{JIT_BACKEND_ARCH} to 1. Further down the file, +you should add the following two lines to the include file logic: + +@example +#elif defined(JIT_BACKEND_ARCH) +#include "jit-rules-ARCH.h" +@end example + +@subsection Defining the registers + +Every rule header file needs to define the macro @code{JIT_REG_INFO} to +an array of values that represents the properties of the CPU's +registers. The @code{_jit_reg_info} array is populated with +these values. @code{JIT_NUM_REGS} defines the number of +elements in the array. Each element in the array has the +following members: + +@table @code +@item name +The name of the register. This is used for debugging purposes. + +@item cpu_reg +The raw CPU register number. Registers in @code{libjit} are +referred to by their pseudo register numbers, corresponding to +their index within @code{JIT_REG_INFO}. However, these pseudo +register numbers may not necessarily correspond to the register +numbers used by the actual CPU. This field provides a mapping. + +@item other_reg +The second pseudo register in a 64-bit register pair, or -1 if +the current register cannot be used as the first pseudo register +in a 64-bit register pair. This field only has meaning on 32-bit +platforms, and should always be set to -1 on 64-bit platforms. + +@item flags +Flag bits that describe the pseudo register's properties. +@end table + +@noindent +The following flags may be present: + +@table @code +@item JIT_REG_WORD +This register can hold an integer word value. + +@item JIT_REG_LONG +This register can hold a 64-bit long value without needing a +second register. Normally only used on 64-bit platforms. + +@item JIT_REG_FLOAT +This register can hold a native floating-point value. + +@item JIT_REG_FRAME +This register holds the frame pointer. You will almost always supply +@code{JIT_REG_FIXED} for this register. + +@item JIT_REG_STACK_PTR +This register holds the stack pointer. You will almost always supply +@code{JIT_REG_FIXED} for this register. + +@item JIT_REG_FIXED +This register has a fixed meaning and cannot be used for general allocation. + +@item JIT_REG_CALL_USED +This register will be destroyed by a function call. + +@item JIT_REG_START_STACK +This register is the start of a range of registers that are used in a +stack-like arrangement. Operations can typically only occur at the +top of the stack, and may automatically pop values as a side-effect +of the operation. The stack continues until the next register that is +marked with @code{JIT_REG_END_STACK}. The starting register must +also have the @code{JIT_REG_IN_STACK} flag set. + +@item JIT_REG_END_STACK +This register is the end of a range of registers that are used in a +stack-like arrangement. The ending register must also have the +@code{JIT_REG_IN_STACK} flag set. + +@item JIT_REG_IN_STACK +This register is in a stack-like arrangement. If neither +@code{JIT_REG_START_STACK} or @code{JIT_REG_END_STACK} is present, +then the register is in the "middle" of the stack. + +@item JIT_REG_GLOBAL +This register is a candidate for global register allocation. +@end table + +@subsection Other architecture macros + +@noindent +The rule file may also have definitions of the following macros: + +@table @code +@item JIT_ALWAYS_REG_REG +Define this to 1 if arithmetic operations must always be performed +on registers. Define this to 0 if register/memory and memory/register +operations are possible. + +@item JIT_PROLOG_SIZE +If defined, this indicates the maximum size of the function prolog. + +@item JIT_FUNCTION_ALIGNMENT +This value indicates the alignment required for the start of a function. +e.g. define this to 32 if functions should be aligned on a 32-byte +boundary. + +@item JIT_ALIGN_OVERRIDES +Define this to 1 if the platform allows reads and writes on +any byte boundary. Define to 0 if only properly-aligned +memory accesses are allowed. Normally only defined to 1 under x86. + +@item jit_extra_gen_state +@itemx jit_extra_gen_init +@itemx jit_extra_gen_cleanup +The @code{jit_extra_gen_state} macro can be supplied to add extra fields +to the @code{struct jit_gencode} type in @code{jit-rules.h}, for +extra CPU-specific code generation state information. + +The @code{jit_extra_gen_init} macro initializes this extra information, +and the @code{jit_extra_gen_cleanup} macro cleans it up when code +generation is complete. +@end table + +@subsection Architecture-dependent functions + +@*/ + +/* + * Write an interpreter opcode to the cache. + */ +#define jit_cache_opcode(posn,opcode) \ + jit_cache_native((posn), (jit_nint)(opcode)) + +/* + * Write "n" bytes to the cache, rounded up to a multiple of "void *". + */ +#define jit_cache_add_n(posn,buf,size) \ + do { \ + unsigned int __size = \ + ((size) + sizeof(void *) - 1) & ~(sizeof(void *) - 1); \ + if(jit_cache_check_for_n((posn), __size)) \ + { \ + jit_memcpy((posn)->ptr, (buf), (size)); \ + (posn)->ptr += __size; \ + } \ + else \ + { \ + jit_cache_mark_full((posn)); \ + } \ + } while (0) + +/* + * Adjust the height of the working area. + */ +#define adjust_working(gen,adjust) \ + do { \ + (gen)->working_area += (adjust); \ + if((gen)->working_area > (gen)->max_working_area) \ + { \ + (gen)->max_working_area = (gen)->working_area; \ + } \ + } while (0) + +/*@ + * @deftypefun void _jit_init_backend (void) + * Initialize the backend. This is normally used to configure registers + * that may not appear on all CPU's in a given family. For example, only + * some ARM cores have floating-point registers. + * @end deftypefun +@*/ +void _jit_init_backend(void) +{ + /* Nothing to do here for the interpreter */ +} + +/*@ + * @deftypefun void _jit_gen_get_elf_info ({jit_elf_info_t *} info) + * Get the ELF machine and ABI type information for this platform. + * The @code{machine} field should be set to one of the @code{EM_*} + * values in @code{jit-elf-defs.h}. The @code{abi} field should + * be set to one of the @code{ELFOSABI_*} values in @code{jit-elf-defs.h} + * (@code{ELFOSABI_SYSV} will normally suffice if you are unsure). + * The @code{abi_version} field should be set to the ABI version, + * which is usually zero. + * @end deftypefun +@*/ +void _jit_gen_get_elf_info(jit_elf_info_t *info) +{ + /* The interpreter's ELF machine type is defined to be "Lj", + which hopefully won't clash with any standard types */ + info->machine = 0x4C6A; + info->abi = 0; + info->abi_version = JIT_OPCODE_VERSION; +} + +/*@ + * @deftypefun int _jit_create_entry_insns (jit_function_t func) + * Create instructions in the entry block to initialize the + * registers and frame offsets that contain the parameters. + * Returns zero if out of memory. + * + * This function is called when a builder is initialized. It should + * scan the signature and decide which register or frame position + * contains each of the parameters and then call either + * @code{jit_insn_incoming_reg} or @code{jit_insn_incoming_frame_posn} + * to notify @code{libjit} of the location. + * @end deftypefun +@*/ +int _jit_create_entry_insns(jit_function_t func) +{ + jit_type_t signature = func->signature; + jit_type_t type; + jit_nint offset; + jit_value_t value; + unsigned int num_params; + unsigned int param; + + /* Reset the frame size for this function */ + func->builder->frame_size = 0; + + /* The starting parameter offset. We use negative offsets to indicate + an offset into the "args" block, and positive offsets to indicate + an offset into the "frame" block. The negative values will be + flipped when we output the argument opcodes for interpretation */ + offset = -1; + + /* If the function is nested, then we need two extra parameters + to pass the pointer to the parent's local variables and arguments */ + if(func->nested_parent) + { + offset -= 2; + } + + /* Allocate the structure return pointer */ + value = jit_value_get_struct_pointer(func); + if(value) + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + --offset; + } + + /* Allocate the parameter offsets */ + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + value = jit_value_get_param(func, param); + if(!value) + { + continue; + } + type = jit_type_normalize(jit_value_get_type(value)); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + { + if(!jit_insn_incoming_frame_posn + (func, value, offset - _jit_int_lowest_byte())) + { + return 0; + } + --offset; + } + break; + + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + { + if(!jit_insn_incoming_frame_posn + (func, value, offset - _jit_int_lowest_short())) + { + return 0; + } + --offset; + } + break; + + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + case JIT_TYPE_SIGNATURE: + case JIT_TYPE_PTR: + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + case JIT_TYPE_FLOAT32: + case JIT_TYPE_FLOAT64: + case JIT_TYPE_NFLOAT: + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + --offset; + } + break; + + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + offset -= JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type)); + } + break; + } + } + return 1; +} + +/*@ + * @deftypefun int _jit_create_call_setup_insns (jit_function_t func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int is_nested, int nested_level, jit_value_t *struct_return) + * Create instructions within @code{func} necessary to set up for a + * function call to a function with the specified @code{signature}. + * Use @code{jit_insn_push} to push values onto the system stack, + * or @code{jit_insn_outgoing_reg} to copy values into call registers. + * + * If @code{is_nested} is non-zero, then it indicates that we are calling a + * nested function within the current function's nested relationship tree. + * The @code{nested_level} value will be -1 to call a child, zero to call a + * sibling of @code{func}, 1 to call a sibling of the parent, 2 to call + * a sibling of the grandparent, etc. The @code{jit_insn_setup_for_nested} + * instruction should be used to create the nested function setup code. + * + * If the function returns a structure by pointer, then @code{struct_return} + * must be set to a new local variable that will contain the returned + * structure. Otherwise it should be set to NULL. + * @end deftypefun +@*/ +int _jit_create_call_setup_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + int is_nested, int nested_level, jit_value_t *struct_return) +{ + jit_type_t type; + jit_value_t value; + + /* Push all of the arguments in reverse order */ + while(num_args > 0) + { + --num_args; + if(!jit_insn_push(func, args[num_args])) + { + return 0; + } + } + + /* Do we need to add a structure return pointer argument? */ + type = jit_type_get_return(signature); + if(jit_type_return_via_pointer(type)) + { + value = jit_value_create(func, type); + if(!value) + { + return 0; + } + *struct_return = value; + value = jit_insn_address_of(func, value); + if(!value) + { + return 0; + } + if(!jit_insn_push(func, value)) + { + return 0; + } + } + else + { + *struct_return = 0; + } + + /* Do we need to add nested function scope information? */ + if(is_nested) + { + if(!jit_insn_setup_for_nested(func, nested_level, -1)) + { + return 0; + } + } + + /* The call is ready to proceed */ + return 1; +} + +/*@ + * @deftypefun int _jit_setup_indirect_pointer (jit_function_t func, jit_value_t value) + * Place the indirect function pointer @code{value} into a suitable register + * or stack location for a subsequent indirect call. + * @end deftypefun +@*/ +int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value) +{ + return jit_insn_push(func, value); +} + +/*@ + * @deftypefun int _jit_create_call_return_insns (jit_function_t func, jit_type_t signature, jit_value_t *args, unsigned int num_args, jit_value_t return_value, int is_nested) + * Create instructions within @code{func} to clean up after a function call + * and to place the function's result into @code{return_value}. + * This should use @code{jit_insn_pop_stack} to pop values off the system + * stack and @code{jit_insn_return_reg} to tell @code{libjit} which + * register contains the return value. In the case of a @code{void} + * function, @code{return_value} will be NULL. + * + * Note: the argument values are passed again because it may not be possible + * to determine how many bytes to pop from the stack from the @code{signature} + * alone; especially if the called function is vararg. + * @end deftypefun +@*/ +int _jit_create_call_return_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + jit_value_t return_value, int is_nested) +{ + jit_nint pop_items; + unsigned int size; + jit_type_t return_type; + int ptr_return; + + /* Calculate the number of items that we need to pop */ + pop_items = 0; + while(num_args > 0) + { + --num_args; + size = jit_type_get_size(jit_value_get_type(args[num_args])); + pop_items += JIT_NUM_ITEMS_IN_STRUCT(size); + } + return_type = jit_type_normalize(jit_type_get_return(signature)); + ptr_return = jit_type_return_via_pointer(return_type); + if(ptr_return) + { + ++pop_items; + } + if(is_nested) + { + /* The interpreter needs two arguments for the parent frame info */ + pop_items += 2; + } + + /* Pop the items from the system stack */ + if(pop_items > 0) + { + if(!jit_insn_pop_stack(func, pop_items)) + { + return 0; + } + } + + /* Bail out now if we don't need to worry about return values */ + if(!return_value || ptr_return) + { + return 0; + } + + /* Structure values must be flushed into the frame, and + everything else ends up in the top-most stack register */ + if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) + { + if(!jit_insn_flush_struct(func, return_value)) + { + return 0; + } + } + else + { + if(!jit_insn_return_reg(func, return_value, 0)) + { + return 0; + } + } + + /* Everything is back where it needs to be */ + return 1; +} + +/*@ + * @deftypefun int _jit_opcode_is_supported (int opcode) + * Not all CPU's support all arithmetic, conversion, bitwise, or + * comparison operators natively. For example, most ARM platforms + * need to call out to helper functions to perform floating-point. + * + * If this function returns zero, then @code{jit-insn.c} will output a + * call to an intrinsic function that is equivalent to the desired opcode. + * This is how you tell @code{libjit} that you cannot handle the + * opcode natively. + * + * This function can also help you develop your back end incrementally. + * Initially, you can report that only integer operations are supported, + * and then once you have them working you can move on to the floating point + * operations. + * @end deftypefun +@*/ +int _jit_opcode_is_supported(int opcode) +{ + /* We support all opcodes in the interpreter */ + return 1; +} + +/* + * Calculate the size of the argument area for an interpreted function. + */ +unsigned int _jit_interp_calculate_arg_size + (jit_function_t func, jit_type_t signature) +{ + unsigned int size = 0; + jit_type_t type; + unsigned int num_params; + unsigned int param; + + /* Determine if we need nested parameter information */ + if(func->nested_parent) + { + size += 2 * sizeof(jit_item); + } + + /* Determine if we need a structure pointer argument */ + type = jit_type_get_return(signature); + if(jit_type_return_via_pointer(type)) + { + size += sizeof(jit_item); + } + + /* Calculate the total size of the regular arguments */ + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + type = jit_type_normalize(jit_type_get_param(signature, param)); + if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) + { + size += JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(type)) * + sizeof(jit_item); + } + else + { + size += sizeof(jit_item); + } + } + + /* Return the final size to the caller */ + return size; +} + +/*@ + * @deftypefun {void *} _jit_gen_prolog (jit_gencode_t gen, jit_function_t func, {void *} buf) + * Generate the prolog for a function into a previously-prepared + * buffer area of @code{JIT_PROLOG_SIZE} bytes in size. Returns + * the start of the prolog, which may be different than @code{buf}. + * + * This function is called at the end of the code generation process, + * not the beginning. At this point, it is known which callee save + * registers must be preserved, allowing the back end to output the + * most compact prolog possible. + * @end deftypefun +@*/ +void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf) +{ + /* Output the jit_function_interp structure at the beginning */ + jit_function_interp_t interp = (jit_function_interp_t)buf; + interp->func = func; + interp->args_size = _jit_interp_calculate_arg_size(func, func->signature); + interp->frame_size = + (func->builder->frame_size + gen->max_working_area) * sizeof(jit_item); + interp->working_area = gen->max_working_area; + return buf; +} + +/*@ + * @deftypefun void _jit_gen_epilog (jit_gencode_t gen, jit_function_t func) + * Generate a function epilog, restoring the registers that + * were saved on entry to the function, and then returning. + * + * Only one epilog is generated per function. Functions with multiple + * @code{jit_insn_return} instructions will all jump to the common epilog. + * This is needed because the code generator may not know which callee + * save registers need to be restored by the epilog until the full function + * has been processed. + * @end deftypefun +@*/ +void _jit_gen_epilog(jit_gencode_t gen, jit_function_t func) +{ + jit_cache_opcode(&(gen->posn), JIT_OP_END_MARKER); +} + +/*@ + * @deftypefun {void *} _jit_gen_redirector (jit_gencode_t gen, jit_function_t func) + * Generate code for a redirector, which makes an indirect jump + * to the contents of @code{func->entry_point}. Redirectors + * are used on recompilable functions in place of the regular + * entry point. This allows @code{libjit} to redirect existing + * calls to the new version after recompilation. + * @end deftypefun +@*/ +void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func) +{ + /* The interpreter doesn't need redirectors */ + return 0; +} + +/*@ + * @deftypefun void _jit_gen_spill_reg (jit_gencode_t gen, int reg, int other_reg, jit_value_t value) + * Generate instructions to spill a pseudo register to the local + * variable frame. If @code{other_reg} is not -1, then it indicates + * the second register in a 64-bit register pair. + * + * This function will typically call @code{_jit_gen_fix_value} to + * fix the value's frame position, and will then generate the + * appropriate spill instructions. + * @end deftypefun +@*/ +void _jit_gen_spill_reg(jit_gencode_t gen, int reg, + int other_reg, jit_value_t value) +{ + int opcode; + jit_nint offset; + + /* Fix the value in place within the local variable frame */ + _jit_gen_fix_value(value); + + /* Output an appropriate instruction to spill the value */ + offset = value->frame_offset; + if(offset >= 0) + { + opcode = _jit_store_opcode(JIT_OP_STLOC_BYTE, 0, value->type); + } + else + { + opcode = _jit_store_opcode(JIT_OP_STARG_BYTE, 0, value->type); + offset = -(offset + 1); + } + jit_cache_opcode(&(gen->posn), opcode); + jit_cache_native(&(gen->posn), offset); + + /* Adjust the working area to account for the popped value */ + adjust_working(gen, -1); +} + +/*@ + * @deftypefun void _jit_gen_free_reg (jit_gencode_t gen, int reg, int other_reg, int value_used) + * Generate instructions to free a register without spilling its value. + * This is called when a register's contents become invalid, or its + * value is no longer required. If @code{value_used} is set to a non-zero + * value, then it indicates that the register's value was just used. + * Otherwise, there is a value in the register but it was never used. + * + * On most platforms, this function won't need to do anything to free + * the register. But some do need to take explicit action. For example, + * x86 needs an explicit instruction to remove a floating-point value + * from the FPU's stack if its value has not been used yet. + * @end deftypefun +@*/ +void _jit_gen_free_reg(jit_gencode_t gen, int reg, + int other_reg, int value_used) +{ + /* If the value wasn't used, then pop it from the stack. + Registers are always freed from the top down */ + if(!value_used) + { + jit_cache_opcode(&(gen->posn), JIT_OP_POP); + adjust_working(gen, -1); + } +} + +/*@ + * @deftypefun void _jit_gen_load_value (jit_gencode_t gen, int reg, int other_reg, jit_value_t value) + * Generate instructions to load a value into a register. The value will + * either be a constant or a slot in the frame. You should fix frame slots + * with @code{_jit_gen_fix_value}. + * @end deftypefun +@*/ +void _jit_gen_load_value + (jit_gencode_t gen, int reg, int other_reg, jit_value_t value) +{ + int opcode; + if(value->is_constant) + { + /* Determine the type of constant to be loaded */ + switch(jit_type_normalize(value->type)->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_CONST_INT); + jit_cache_native(&(gen->posn), (jit_nint)(value->address)); + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + jit_long long_value; + long_value = jit_value_get_long_constant(value); + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_CONST_LONG); + #ifdef JIT_NATIVE_INT64 + jit_cache_native(&(gen->posn), long_value); + #else + jit_cache_add_n(&(gen->posn), &long_value, sizeof(long_value)); + #endif + } + break; + + case JIT_TYPE_FLOAT32: + { + jit_float32 float32_value; + float32_value = jit_value_get_float32_constant(value); + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_CONST_FLOAT32); + jit_cache_add_n + (&(gen->posn), &float32_value, sizeof(float32_value)); + } + break; + + case JIT_TYPE_FLOAT64: + { + jit_float64 float64_value; + float64_value = jit_value_get_float64_constant(value); + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_CONST_FLOAT64); + jit_cache_add_n + (&(gen->posn), &float64_value, sizeof(float64_value)); + } + break; + + case JIT_TYPE_NFLOAT: + { + jit_nfloat nfloat_value; + nfloat_value = jit_value_get_nfloat_constant(value); + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_CONST_NFLOAT); + jit_cache_add_n + (&(gen->posn), &nfloat_value, sizeof(nfloat_value)); + } + break; + } + } + else + { + /* Fix the position of the value in the stack frame */ + _jit_gen_fix_value(value); + + /* Generate a local or argument access opcode, as appropriate */ + if(value->frame_offset >= 0) + { + /* Load a local variable value onto the stack */ + opcode = _jit_load_opcode + (JIT_OP_LDLOC_SBYTE, value->type, value, 0); + jit_cache_opcode(&(gen->posn), opcode); + jit_cache_native(&(gen->posn), value->frame_offset); + } + else + { + /* Load an argument value onto the stack */ + opcode = _jit_load_opcode + (JIT_OP_LDARG_SBYTE, value->type, value, 0); + jit_cache_opcode(&(gen->posn), opcode); + jit_cache_native(&(gen->posn), -(value->frame_offset + 1)); + } + } + + /* We have one more value on the stack */ + adjust_working(gen, 1); +} + +/*@ + * @deftypefun void _jit_gen_fix_value (jit_value_t value) + * Fix the position of a value within the local variable frame. + * If it doesn't already have a position, then assign one for it. + * @end deftypefun +@*/ +void _jit_gen_fix_value(jit_value_t value) +{ + if(!(value->has_frame_offset) && !(value->is_constant)) + { + jit_nint size = (jit_nint) + (JIT_NUM_ITEMS_IN_STRUCT(jit_type_get_size(value->type))); + value->frame_offset = value->block->func->builder->frame_size; + value->block->func->builder->frame_size += size; + value->has_frame_offset = 1; + } +} + +/* + * Get the destination of a branch instruction, and thread through + * unconditional branches that this one points to. Returns + * "jit_label_undefined" if we are branching to the next block + * (i.e. the branch instruction can be quietly eliminated). + */ +static jit_label_t get_branch_dest(jit_block_t block, jit_insn_t insn) +{ + jit_label_t label; + jit_block_t new_block; + int max_thread; + jit_insn_iter_t iter; + + /* Get the starting label */ + label = (jit_label_t)(insn->dest); + + /* Bail out now if we are within an exception block, because we + don't want to thread to jumps outside the "finally" context */ + if(block->block_eh && insn->opcode == JIT_OP_BR) + { + return label; + } + + /* Thread unconditional jumps at the destination */ + max_thread = 20; + while(max_thread > 0 && + (new_block = jit_block_from_label(block->func, label)) != 0) + { + jit_insn_iter_init(&iter, new_block); + insn = jit_insn_iter_next(&iter); + if(!insn || insn->opcode != JIT_OP_BR) + { + break; + } + label = (jit_label_t)(insn->dest); + --max_thread; + } + + /* Determine if we are branching to the next block */ + if(block->next && block->next->label == label) + { + return jit_label_undefined; + } + + /* Return the destination label to the caller */ + return label; +} + +/*@ + * @deftypefun void _jit_gen_insn (jit_gencode_t gen, jit_function_t func, jit_block_t block, jit_insn_t insn) + * Generate native code for the specified @code{insn}. This function should + * call the appropriate register allocation routines, output the instruction, + * and then arrange for the result to be placed in an appropriate register + * or memory destination. + * @end deftypefun +@*/ +void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, + jit_block_t block, jit_insn_t insn) +{ + int reg; + jit_label_t label; + void **pc; + + switch(insn->opcode) + { + /* TODO */ + + case JIT_OP_BR: + { + /* Unconditional branch */ + _jit_regs_spill_all(gen); + label = get_branch_dest(block, insn); + if(label != jit_label_undefined) + { + branch: + pc = (void **)(gen->posn.ptr); + jit_cache_opcode(&(gen->posn), insn->opcode); + block = jit_block_from_label(func, label); + if(!block) + { + break; + } + if(block->address) + { + /* We already know the address of the block */ + jit_cache_native + (&(gen->posn), ((void **)(block->address)) - pc); + } + else + { + /* Record this position on the block's fixup list */ + jit_cache_native(&(gen->posn), block->fixup_list); + block->fixup_list = (void *)pc; + } + } + } + break; + + case JIT_OP_BR_IFALSE: + case JIT_OP_BR_ITRUE: + case JIT_OP_BR_LFALSE: + case JIT_OP_BR_LTRUE: + { + /* Unary branch */ + label = get_branch_dest(block, insn); + if(label == jit_label_undefined) + { + /* We are falling through, no matter what the test + says, so optimize the entire instruction away */ + _jit_regs_spill_all(gen); + break; + } + if(!_jit_regs_is_top(gen, insn->value1) || + _jit_regs_num_used(gen, 0) != 1) + { + _jit_regs_spill_all(gen); + } + reg = _jit_regs_load_to_top + (gen, insn->value1, (insn->flags & JIT_INSN_VALUE1_LIVE), 0); + _jit_regs_free_reg(gen, reg, 1); + goto branch; + } + /* Not reached */ + + case JIT_OP_BR_IEQ: + case JIT_OP_BR_INE: + case JIT_OP_BR_ILT: + case JIT_OP_BR_ILT_UN: + case JIT_OP_BR_ILE: + case JIT_OP_BR_ILE_UN: + case JIT_OP_BR_IGT: + case JIT_OP_BR_IGT_UN: + case JIT_OP_BR_IGE: + case JIT_OP_BR_IGE_UN: + case JIT_OP_BR_LEQ: + case JIT_OP_BR_LNE: + case JIT_OP_BR_LLT: + case JIT_OP_BR_LLT_UN: + case JIT_OP_BR_LLE: + case JIT_OP_BR_LLE_UN: + case JIT_OP_BR_LGT: + case JIT_OP_BR_LGT_UN: + case JIT_OP_BR_LGE: + case JIT_OP_BR_LGE_UN: + case JIT_OP_BR_FEQ: + case JIT_OP_BR_FNE: + case JIT_OP_BR_FLT: + case JIT_OP_BR_FLE: + case JIT_OP_BR_FGT: + case JIT_OP_BR_FGE: + case JIT_OP_BR_FEQ_INV: + case JIT_OP_BR_FNE_INV: + case JIT_OP_BR_FLT_INV: + case JIT_OP_BR_FLE_INV: + case JIT_OP_BR_FGT_INV: + case JIT_OP_BR_FGE_INV: + case JIT_OP_BR_DEQ: + case JIT_OP_BR_DNE: + case JIT_OP_BR_DLT: + case JIT_OP_BR_DLE: + case JIT_OP_BR_DGT: + case JIT_OP_BR_DGE: + case JIT_OP_BR_DEQ_INV: + case JIT_OP_BR_DNE_INV: + case JIT_OP_BR_DLT_INV: + case JIT_OP_BR_DLE_INV: + case JIT_OP_BR_DGT_INV: + case JIT_OP_BR_DGE_INV: + case JIT_OP_BR_NFEQ: + case JIT_OP_BR_NFNE: + case JIT_OP_BR_NFLT: + case JIT_OP_BR_NFLE: + case JIT_OP_BR_NFGT: + case JIT_OP_BR_NFGE: + case JIT_OP_BR_NFEQ_INV: + case JIT_OP_BR_NFNE_INV: + case JIT_OP_BR_NFLT_INV: + case JIT_OP_BR_NFLE_INV: + case JIT_OP_BR_NFGT_INV: + case JIT_OP_BR_NFGE_INV: + { + /* Binary branch */ + label = get_branch_dest(block, insn); + if(label == jit_label_undefined) + { + /* We are falling through, no matter what the test + says, so optimize the entire instruction away */ + _jit_regs_spill_all(gen); + break; + } + if(!_jit_regs_is_top_two(gen, insn->value1, insn->value2) || + _jit_regs_num_used(gen, 0) != 2) + { + _jit_regs_spill_all(gen); + } + reg = _jit_regs_load_to_top_two + (gen, insn->value1, insn->value2, + (insn->flags & JIT_INSN_VALUE1_LIVE), + (insn->flags & JIT_INSN_VALUE2_LIVE), 0); + _jit_regs_free_reg(gen, reg, 1); + goto branch; + } + /* Not reached */ + + case JIT_OP_CALL: + case JIT_OP_CALL_EXTERNAL: + { + /* Call a function, whose pointer is supplied explicitly */ + jit_cache_opcode(&(gen->posn), insn->opcode); + jit_cache_native(&(gen->posn), (jit_nint)(insn->dest)); + } + break; + + case JIT_OP_CALL_INDIRECT: + case JIT_OP_CALL_VTABLE_PTR: + { + /* Call a function, whose pointer is supplied on the stack */ + _jit_regs_load_to_top(gen, insn->value1, 0, 0); + jit_cache_opcode(&(gen->posn), insn->opcode); + adjust_working(gen, -1); + } + break; + + case JIT_OP_RETURN: + { + /* Return from the current function with no result */ + _jit_regs_spill_all(gen); + jit_cache_opcode(&(gen->posn), JIT_OP_RETURN); + } + break; + + case JIT_OP_RETURN_INT: + case JIT_OP_RETURN_LONG: + case JIT_OP_RETURN_FLOAT32: + case JIT_OP_RETURN_FLOAT64: + case JIT_OP_RETURN_NFLOAT: + { + /* Return from the current function with a specific result */ + if(!_jit_regs_is_top(gen, insn->value1) || + _jit_regs_num_used(gen, 0) != 1) + { + _jit_regs_spill_all(gen); + } + reg = _jit_regs_load_to_top(gen, insn->value1, 0, 0); + jit_cache_opcode(&(gen->posn), insn->opcode); + _jit_regs_free_reg(gen, reg, 1); + } + break; + + case JIT_OP_RETURN_REG: + { + /* Push a function return value back onto the stack */ + switch(jit_type_normalize(insn->value1->type)->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_INT); + adjust_working(gen, 1); + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_LONG); + adjust_working(gen, 1); + } + break; + + case JIT_TYPE_FLOAT32: + { + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_FLOAT32); + adjust_working(gen, 1); + } + break; + + case JIT_TYPE_FLOAT64: + { + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_FLOAT64); + adjust_working(gen, 1); + } + break; + + case JIT_TYPE_NFLOAT: + { + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_NFLOAT); + adjust_working(gen, 1); + } + break; + } + } + break; + + case JIT_OP_PUSH_INT: + case JIT_OP_PUSH_LONG: + case JIT_OP_PUSH_FLOAT32: + case JIT_OP_PUSH_FLOAT64: + case JIT_OP_PUSH_NFLOAT: + { + /* Push an item onto the stack, ready for a function call */ + if(!_jit_regs_is_top(gen, insn->value1) || + _jit_regs_num_used(gen, 0) != 1) + { + _jit_regs_spill_all(gen); + } + reg = _jit_regs_load_to_top + (gen, insn->value1, + (insn->flags & (JIT_INSN_VALUE1_NEXT_USE | + JIT_INSN_VALUE1_LIVE)), 0); + _jit_regs_free_reg(gen, reg, 1); + } + break; + + case JIT_OP_PUSH_STRUCT: + { + /* TODO */ + } + break; + + case JIT_OP_POP_STACK: + { + /* Pop parameter values from the stack after a function returns */ + jit_nint size = jit_value_get_nint_constant(insn->value1); + if(size == 1) + { + jit_cache_opcode(&(gen->posn), JIT_OP_POP); + } + else if(size == 2) + { + jit_cache_opcode(&(gen->posn), JIT_OP_POP_2); + } + else if(size == 3) + { + jit_cache_opcode(&(gen->posn), JIT_OP_POP_3); + } + else if(size != 0) + { + jit_cache_opcode(&(gen->posn), JIT_OP_POP_STACK); + jit_cache_native(&(gen->posn), size); + } + } + break; + + case JIT_OP_FLUSH_SMALL_STRUCT: + { + /* Flush a small structure return value back into the frame */ + _jit_gen_fix_value(insn->value1); + if(insn->value1->frame_offset >= 0) + { + jit_cache_opcode(&(gen->posn), JIT_OP_LDLOCA); + jit_cache_native(&(gen->posn), insn->value1->frame_offset); + } + else + { + jit_cache_opcode(&(gen->posn), JIT_OP_LDARGA); + jit_cache_native + (&(gen->posn), -(insn->value1->frame_offset + 1)); + } + jit_cache_opcode(&(gen->posn), JIT_OP_PUSH_RETURN_SMALL_STRUCT); + jit_cache_native + (&(gen->posn), jit_type_get_size(insn->value1->type)); + adjust_working(gen, 2); + jit_cache_opcode(&(gen->posn), JIT_OP_STORE_RELATIVE_STRUCT); + jit_cache_native(&(gen->posn), 0); + jit_cache_native + (&(gen->posn), jit_type_get_size(insn->value1->type)); + adjust_working(gen, -2); + } + break; + + default: + { + /* Whatever opcodes are left are binary or unary operators, + and the interpreter's opcode is identical to the JIT's */ + if(insn->value2) + { + /* Generate code for a binary operator */ + reg = _jit_regs_load_to_top_two + (gen, insn->value1, insn->value2, + (insn->flags & (JIT_INSN_VALUE1_NEXT_USE | + JIT_INSN_VALUE1_LIVE)), + (insn->flags & (JIT_INSN_VALUE2_NEXT_USE | + JIT_INSN_VALUE2_LIVE)), 0); + jit_cache_opcode(&(gen->posn), insn->opcode); + adjust_working(gen, -1); + if(insn->dest) + { + if((insn->flags & JIT_INSN_DEST_NEXT_USE) != 0) + { + /* Record that the destination is in "reg" */ + _jit_regs_set_value(gen, reg, insn->dest, 0); + } + else + { + /* No next use, so store to the destination */ + _jit_gen_spill_reg(gen, reg, -1, insn->dest); + insn->dest->in_frame = 1; + _jit_regs_free_reg(gen, reg, 1); + } + } + else + { + /* This is a note, with the result left on the stack */ + _jit_regs_free_reg(gen, reg, 1); + } + } + else + { + /* Generate code for a unary operator */ + reg = _jit_regs_load_to_top + (gen, insn->value1, + (insn->flags & (JIT_INSN_VALUE1_NEXT_USE | + JIT_INSN_VALUE1_LIVE)), 0); + jit_cache_opcode(&(gen->posn), insn->opcode); + if(insn->dest) + { + if((insn->flags & JIT_INSN_DEST_NEXT_USE) != 0) + { + /* Record that the destination is in "reg" */ + _jit_regs_set_value(gen, reg, insn->dest, 0); + } + else + { + /* No next use, so store to the destination */ + _jit_gen_spill_reg(gen, reg, -1, insn->dest); + insn->dest->in_frame = 1; + _jit_regs_free_reg(gen, reg, 1); + } + } + else + { + /* This is a note, with the result left on the stack */ + _jit_regs_free_reg(gen, reg, 1); + } + } + } + break; + } +} + +/*@ + * @deftypefun void _jit_gen_start_block (jit_gencode_t gen, jit_block_t block) + * Called to notify the back end that the start of a basic block + * has been reached. + * @end deftypefun +@*/ +void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block) +{ + void **fixup; + void **next; + + /* Set the address of this block */ + block->address = (void *)(gen->posn.ptr); + + /* If this block has pending fixups, then apply them now */ + fixup = (void **)(block->fixup_list); + while(fixup != 0) + { + next = (void **)(fixup[1]); + fixup[1] = (void *)(jit_nint)(((void **)(block->address)) - fixup); + fixup = next; + } + block->fixup_list = 0; +} + +/*@ + * @deftypefun void _jit_gen_end_block (jit_gencode_t gen) + * Called to notify the back end that the end of a basic block + * has been reached. + * @end deftypefun +@*/ +void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) +{ + /* Reset the working area size to zero for the next block */ + gen->working_area = 0; +} + +/*@ + * @deftypefun void _jit_gen_call_finally (jit_gencode_t gen, jit_function_t func, jit_label_t label) + * Call a @code{finally} clause at @code{label}. + * @end deftypefun +@*/ +void _jit_gen_call_finally + (jit_gencode_t gen, jit_function_t func, jit_label_t label) +{ + jit_block_t block; + void **pc; + _jit_regs_spill_all(gen); + pc = (void **)(gen->posn.ptr); + jit_cache_opcode(&(gen->posn), JIT_OP_CALL_FINALLY); + block = jit_block_from_label(func, label); + if(!block) + { + return; + } + if(block->address) + { + /* We already know the address of the block */ + jit_cache_native + (&(gen->posn), ((void **)(block->address)) - pc); + } + else + { + /* Record this position on the block's fixup list */ + jit_cache_native(&(gen->posn), block->fixup_list); + block->fixup_list = (void *)pc; + } +} + +/*@ + * @deftypefun void _jit_gen_unwind_stack ({void *} stacktop, {void *} catch_pc, {void *} object) + * Unwind the stack back to @code{stacktop}, restore the frame, and + * jump to @code{catch_pc}. The registers are set up to arrange for + * @code{object} to be in the right place for a @code{catch} clause. + * @end deftypefun +@*/ +void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object) +{ + /* Not used by the interpreted back end */ +} + +#endif /* JIT_BACKEND_INTERP */ diff --git a/jit/jit-rules-interp.h b/jit/jit-rules-interp.h new file mode 100644 index 0000000..68112b2 --- /dev/null +++ b/jit/jit-rules-interp.h @@ -0,0 +1,110 @@ +/* + * jit-rules-interp.h - Rules that define the interpreter characteristics. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_RULES_INTERP_H +#define _JIT_RULES_INTERP_H + +#include "jit-interp.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Information about all of the registers, in allocation order. + */ +#define JIT_REG_INFO \ + {"r0", 0, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_START_STACK | JIT_REG_IN_STACK}, \ + {"r1", 1, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r2", 2, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r3", 3, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r4", 4, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r5", 5, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r6", 6, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r7", 7, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r8", 8, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r9", 9, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r10", 10, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r11", 11, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r12", 12, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r13", 13, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r14", 14, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"r15", 15, -1, JIT_REG_WORD | JIT_REG_LONG | JIT_REG_FLOAT | \ + JIT_REG_CALL_USED | JIT_REG_END_STACK | JIT_REG_IN_STACK}, +#define JIT_NUM_REGS 16 + +/* + * Define to 1 if we should always load values into registers + * before operating on them. i.e. the CPU does not have reg-mem + * and mem-reg addressing modes. + */ +#define JIT_ALWAYS_REG_REG 1 + +/* + * The maximum number of bytes to allocate for the prolog. + * This may be shortened once we know the true prolog size. + */ +#define JIT_PROLOG_SIZE jit_function_interp_size + +/* + * Preferred alignment for the start of functions. + */ +#define JIT_FUNCTION_ALIGNMENT (sizeof(void *)) + +/* + * Define this to 1 if the platform allows reads and writes on + * any byte boundary. Define to 0 if only properly-aligned + * memory accesses are allowed. + */ +#define JIT_ALIGN_OVERRIDES 0 + +/* + * Extra state information that is added to the "jit_gencode" structure. + */ +#define jit_extra_gen_state \ + int working_area; \ + int max_working_area +#define jit_extra_gen_init(gen) \ + do { \ + (gen)->working_area = 0; \ + (gen)->max_working_area = 0; \ + } while (0) +#define jit_extra_gen_cleanup(gen) do { ; } while (0) + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_RULES_INTERP_H */ diff --git a/jit/jit-rules-x86.c b/jit/jit-rules-x86.c new file mode 100644 index 0000000..92bc703 --- /dev/null +++ b/jit/jit-rules-x86.c @@ -0,0 +1,1042 @@ +/* + * jit-rules-x86.c - Rules that define the characteristics of the x86. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" +#include "jit-apply-rules.h" + +#if defined(JIT_BACKEND_X86) + +#include "jit-gen-x86.h" + +/* + * Pseudo register numbers for the x86 registers. These are not the + * same as the CPU instruction register numbers. The order of these + * values must match the order in "JIT_REG_INFO". + */ +#define X86_REG_EAX 0 +#define X86_REG_ECX 1 +#define X86_REG_EDX 2 +#define X86_REG_EBX 3 +#define X86_REG_ESI 4 +#define X86_REG_EDI 5 +#define X86_REG_EBP 6 +#define X86_REG_ESP 7 +#define X86_REG_ST0 8 +#define X86_REG_ST1 9 +#define X85_REG_ST2 10 +#define X86_REG_ST3 11 +#define X86_REG_ST4 12 +#define X86_REG_ST5 13 +#define X86_REG_ST6 14 +#define X86_REG_ST7 15 + +/* + * Round a size up to a multiple of the stack word size. + */ +#define ROUND_STACK(size) \ + (((size) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) + +void _jit_init_backend(void) +{ + /* Nothing to do here for the x86 */ +} + +void _jit_gen_get_elf_info(jit_elf_info_t *info) +{ +#ifdef JIT_NATIVE_INT32 + info->machine = 3; /* EM_386 */ +#else + info->machine = 62; /* EM_X86_64 */ +#endif +#if JIT_APPLY_X86_FASTCALL == 0 + info->abi = 0; /* ELFOSABI_SYSV */ +#else + info->abi = 186; /* Private code, indicating STDCALL/FASTCALL support */ +#endif + info->abi_version = 0; +} + +/* + * Force values out of fastcall registers that cannot be easily + * accessed in register form (i.e. long, float, and struct values). + */ +static int force_out_of_regs(jit_function_t func, jit_value_t param, + int num_regs, unsigned int num_stack_words) +{ + jit_value_t address; + jit_value_t temp; + jit_nint offset = 0; + jit_nint frame_offset = 2 * sizeof(void *); + + /* Get the address of the parameter, to force it into the frame, + and to set up for the later "jit_insn_store_relative" calls */ + address = jit_insn_address_of(func, param); + if(!address) + { + return 0; + } + + /* Force the values out of the registers */ + while(num_regs > 0) + { + temp = jit_value_create(func, jit_type_void_ptr); + if(!temp) + { + return 0; + } + if(num_regs == 2) + { + if(!jit_insn_incoming_reg(func, temp, X86_REG_ECX)) + { + return 0; + } + } + else + { + if(!jit_insn_incoming_reg(func, temp, X86_REG_EDX)) + { + return 0; + } + } + if(!jit_insn_store_relative(func, address, offset, temp)) + { + return 0; + } + offset += sizeof(void *); + --num_regs; + } + + + /* Force the rest of the value out of the incoming stack frame */ + while(num_stack_words > 0) + { + temp = jit_value_create(func, jit_type_void_ptr); + if(!temp) + { + return 0; + } + if(!jit_insn_incoming_frame_posn(func, temp, frame_offset)) + { + return 0; + } + if(!jit_insn_store_relative(func, address, offset, temp)) + { + return 0; + } + offset += sizeof(void *); + frame_offset += sizeof(void *); + --num_stack_words; + } + return 1; +} + +int _jit_create_entry_insns(jit_function_t func) +{ + jit_type_t signature = func->signature; + jit_type_t type; + int num_regs; + jit_nint offset; + jit_value_t value; + unsigned int num_params; + unsigned int param; + unsigned int size; + unsigned int num_stack_words; + + /* Reset the frame size for this function. We start by assuming + that ESI, EDI, and EBX need to be saved in the local frame */ + func->builder->frame_size = 3 * sizeof(void *); + + /* Determine the number of registers to allocate to parameters */ +#if JIT_APPLY_X86_FASTCALL == 1 + if(jit_type_get_abi(signature) == jit_abi_fastcall) + { + num_regs = 2; + } + else +#endif + { + num_regs = 0; + } + + /* The starting parameter offset (saved ebp and return address on stack) */ + offset = 2 * sizeof(void *); + + /* If the function is nested, then we need an extra parameter + to pass the pointer to the parent's local variable frame */ + if(func->nested_parent) + { + if(num_regs > 0) + { + --num_regs; + } + else + { + offset += sizeof(void *); + } + } + + /* Allocate the structure return pointer */ + value = jit_value_get_struct_pointer(func); + if(value) + { + if(num_regs == 2) + { + if(!jit_insn_incoming_reg(func, value, X86_REG_ECX)) + { + return 0; + } + --num_regs; + } + else if(num_regs == 1) + { + if(!jit_insn_incoming_reg(func, value, X86_REG_EDX)) + { + return 0; + } + --num_regs; + } + else + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + offset += sizeof(void *); + } + } + + /* Allocate the parameter offsets */ + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + value = jit_value_get_param(func, param); + if(!value) + { + continue; + } + type = jit_type_normalize(jit_value_get_type(value)); + switch(type->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + case JIT_TYPE_SIGNATURE: + case JIT_TYPE_PTR: + { + if(num_regs == 2) + { + if(!jit_insn_incoming_reg(func, value, X86_REG_ECX)) + { + return 0; + } + --num_regs; + } + else if(num_regs == 1) + { + if(!jit_insn_incoming_reg(func, value, X86_REG_EDX)) + { + return 0; + } + --num_regs; + } + else + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + offset += sizeof(void *); + } + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + case JIT_TYPE_FLOAT32: + case JIT_TYPE_FLOAT64: + case JIT_TYPE_NFLOAT: + case JIT_TYPE_STRUCT: + case JIT_TYPE_UNION: + { + /* Deal with the possibility that the value may be split + between registers and the stack */ + size = ROUND_STACK(jit_type_get_size(type)); + if(num_regs == 2) + { + if(size <= sizeof(void *)) + { + if(!force_out_of_regs(func, value, 1, 0)) + { + return 0; + } + --num_regs; + } + else + { + num_stack_words = (size / sizeof(void *)) - 2; + if(!force_out_of_regs(func, value, 2, num_stack_words)) + { + return 0; + } + num_regs = 0; + offset += num_stack_words * sizeof(void *); + } + } + else if(num_regs == 1) + { + num_stack_words = (size / sizeof(void *)) - 1; + if(!force_out_of_regs(func, value, 1, num_stack_words)) + { + return 0; + } + num_regs = 0; + offset += num_stack_words * sizeof(void *); + } + else + { + if(!jit_insn_incoming_frame_posn(func, value, offset)) + { + return 0; + } + offset += size; + } + } + break; + } + } + return 1; +} + +int _jit_create_call_setup_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + int is_nested, int nesting_level, jit_value_t *struct_return) +{ + jit_type_t type = jit_type_get_return(signature); + jit_value_t value; + unsigned int size; + unsigned int index; + unsigned int num_stack_args; + unsigned int word_regs; + jit_value_t partial; + + /* Determine which values are going to end up in fastcall registers */ +#if JIT_APPLY_X86_FASTCALL == 1 + if(jit_type_get_abi(signature) == jit_abi_fastcall) + { + word_regs = 0; + if(func->nested_parent) + { + ++word_regs; + } + if(jit_type_return_via_pointer(type)) + { + ++word_regs; + } + index = 0; + partial = 0; + while(index < num_args && word_regs < 2) + { + size = jit_type_get_size(jit_value_get_type(args[index])); + size = ROUND_STACK(size) / sizeof(void *); + if(size <= (2 - word_regs)) + { + /* This argument will fit entirely in registers */ + word_regs += size; + ++index; + } + else + { + /* Partly in registers and partly on the stack. + We first copy it into a buffer that we can address */ + partial = jit_value_create + (func, jit_value_get_type(args[index])); + if(!partial) + { + return 0; + } + jit_value_set_addressable(partial); + if(!jit_insn_store(func, partial, args[index])) + { + return 0; + } + ++index; + break; + } + } + num_stack_args = num_args - index; + } + else +#endif + { + word_regs = 0; + partial = 0; + num_stack_args = num_args; + } + + /* Push all of the stacked arguments in reverse order */ + while(num_stack_args > 0) + { + --num_stack_args; + --num_args; + if(!jit_insn_push(func, args[num_args])) + { + return 0; + } + } + + /* Handle a value that is partly on the stack and partly in registers */ + if(partial) + { + --num_args; + index = (2 - word_regs) * sizeof(void *); + size = ROUND_STACK(jit_type_get_size(jit_value_get_type(partial))); + while(size > index) + { + size -= sizeof(void *); + value = jit_value_create(func, jit_type_void_ptr); + if(!value) + { + return 0; + } + value = jit_insn_load_relative + (func, value, (jit_nint)size, jit_type_void_ptr); + if(!value) + { + return 0; + } + if(!jit_insn_push(func, value)) + { + return 0; + } + } + while(size > 0) + { + size -= sizeof(void *); + value = jit_value_create(func, jit_type_void_ptr); + if(!value) + { + return 0; + } + value = jit_insn_load_relative + (func, value, (jit_nint)size, jit_type_void_ptr); + if(!value) + { + return 0; + } + if(word_regs == 2) + { + if(!jit_insn_outgoing_reg(func, value, X86_REG_EDX)) + { + return 0; + } + --word_regs; + } + else + { + if(!jit_insn_outgoing_reg(func, value, X86_REG_ECX)) + { + return 0; + } + --word_regs; + } + } + } + + /* Push arguments that will end up entirely in registers */ + while(num_args > 0) + { + --num_args; + size = jit_type_get_size(jit_value_get_type(args[num_args])); + size = ROUND_STACK(size) / sizeof(void *); + if(size == 2) + { + if(!jit_insn_outgoing_reg(func, args[num_args], X86_REG_ECX)) + { + return 0; + } + word_regs = 0; + } + else if(word_regs == 2) + { + if(!jit_insn_outgoing_reg(func, args[num_args], X86_REG_EDX)) + { + return 0; + } + --word_regs; + } + else + { + if(!jit_insn_outgoing_reg(func, args[num_args], X86_REG_ECX)) + { + return 0; + } + --word_regs; + } + } + + /* Do we need to add a structure return pointer argument? */ + if(jit_type_return_via_pointer(type)) + { + value = jit_value_create(func, type); + if(!value) + { + return 0; + } + *struct_return = value; + value = jit_insn_address_of(func, value); + if(!value) + { + return 0; + } + if(word_regs == 2) + { + if(!jit_insn_outgoing_reg(func, value, X86_REG_EDX)) + { + return 0; + } + --word_regs; + } + else if(word_regs == 1) + { + if(!jit_insn_outgoing_reg(func, value, X86_REG_ECX)) + { + return 0; + } + --word_regs; + } + else + { + if(!jit_insn_push(func, value)) + { + return 0; + } + } + } + else + { + *struct_return = 0; + } + + /* Do we need to add nested function scope information? */ + if(is_nested) + { + if(word_regs > 0) + { + if(!jit_insn_setup_for_nested(func, nesting_level, X86_REG_ECX)) + { + return 0; + } + } + else + { + if(!jit_insn_setup_for_nested(func, nesting_level, -1)) + { + return 0; + } + } + } + + /* The call is ready to proceed */ + return 1; +} + +int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value) +{ + return jit_insn_outgoing_reg(func, value, X86_REG_EAX); +} + +int _jit_create_call_return_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + jit_value_t return_value, int is_nested) +{ + jit_nint pop_bytes; + unsigned int size; + jit_type_t return_type; + int ptr_return; + + /* Calculate the number of bytes that we need to pop */ + return_type = jit_type_normalize(jit_type_get_return(signature)); + ptr_return = jit_type_return_via_pointer(return_type); +#if JIT_APPLY_X86_FASTCALL == 1 + if(jit_type_get_abi(signature) == jit_abi_stdcall || + jit_type_get_abi(signature) == jit_abi_fastcall) + { + /* STDCALL and FASTCALL functions pop their own arguments */ + pop_bytes = 0; + } + else +#endif + { + pop_bytes = 0; + while(num_args > 0) + { + --num_args; + size = jit_type_get_size(jit_value_get_type(args[num_args])); + pop_bytes += ROUND_STACK(size); + } +#if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 + if(ptr_return && is_nested) + { + /* Note: we only need this for nested functions, because + regular functions will pop the structure return for us */ + pop_bytes += sizeof(void *); + } +#else + if(ptr_return) + { + pop_bytes += sizeof(void *); + } +#endif + if(is_nested) + { + pop_bytes += sizeof(void *); + } + } + + /* Pop the bytes from the system stack */ + if(pop_bytes > 0) + { + if(!jit_insn_pop_stack(func, pop_bytes)) + { + return 0; + } + } + + /* Bail out now if we don't need to worry about return values */ + if(!return_value || ptr_return) + { + return 0; + } + + /* Structure values must be flushed into the frame, and + everything else ends up in a register */ + if(jit_type_is_struct(return_type) || jit_type_is_union(return_type)) + { + if(!jit_insn_flush_struct(func, return_value)) + { + return 0; + } + } + else if(return_type == jit_type_float32 || + return_type == jit_type_float64 || + return_type == jit_type_nfloat) + { + if(!jit_insn_return_reg(func, return_value, X86_REG_ST0)) + { + return 0; + } + } + else + { + if(!jit_insn_return_reg(func, return_value, X86_REG_EAX)) + { + return 0; + } + } + + /* Everything is back where it needs to be */ + return 1; +} + +void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf) +{ + unsigned char prolog[JIT_PROLOG_SIZE]; + unsigned char *inst = prolog; + int reg; + unsigned int saved; + + /* Push ebp onto the stack */ + x86_push_reg(inst, X86_EBP); + + /* Initialize EBP for the current frame */ + x86_mov_reg_reg(inst, X86_EBP, X86_ESP, sizeof(void *)); + + /* Save registers that we need to preserve */ + saved = 0; + for(reg = 0; reg <= 7; ++reg) + { + if(jit_reg_is_used(gen->touched, reg) && + (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) + { + x86_push_reg(inst, _jit_reg_info[reg].cpu_reg); + saved += sizeof(void *); + } + } + + /* Allocate space for the local variable frame. Subtract off + the space for the registers that we just saved */ + if((func->builder->frame_size - saved) > 0) + { + x86_alu_reg_imm(inst, X86_SUB, X86_ESP, + (int)(func->builder->frame_size - saved)); + } + + /* Copy the prolog into place and return the adjusted entry position */ + reg = (int)(inst - prolog); + jit_memcpy(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg, prolog, reg); + return (void *)(((unsigned char *)buf) + JIT_PROLOG_SIZE - reg); +} + +void _jit_gen_epilog(jit_gencode_t gen, jit_function_t func) +{ + jit_nint pop_bytes = 0; + int num_regs, reg; + unsigned char *inst; + int struct_return_offset = 0; + + /* Bail out if there is insufficient space for the epilog */ + if(!jit_cache_check_for_n(&(gen->posn), 32)) + { + jit_cache_mark_full(&(gen->posn)); + return; + } + +#if JIT_APPLY_X86_FASTCALL == 1 + /* Determine the number of parameter bytes to pop when we return */ + { + jit_type_t signature; + unsigned int num_params; + unsigned int param; + signature = func->signature; + if(jit_type_abi(signature) == jit_type_stdcall || + jit_type_abi(signature) == jit_type_fastcall) + { + if(func->nested_parent) + { + pop_bytes += sizeof(void *); + } + if(jit_type_return_via_pointer(jit_type_get_return(signature))) + { + struct_return_offset = 2 * sizeof(void *) + pop_bytes; + pop_bytes += sizeof(void *); + } + num_params = jit_type_num_params(signature); + for(param = 0; param < num_params; ++param) + { + pop_bytes += ROUND_STACK + (jit_type_get_size + (jit_type_get_param(signature, param))); + } + if(jit_type_abi(signature) == jit_type_fastcall) + { + /* The first two words are in fastcall registers */ + if(pop_bytes > (2 * sizeof(void *))) + { + pop_bytes -= 2 * sizeof(void *); + } + else + { + pop_bytes = 0; + } + struct_return_offset = 0; + } + } + else if(!(func->nested_parent) && + jit_type_return_via_pointer(jit_type_get_return(signature))) + { +#if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 + pop_bytes += sizeof(void *); +#endif + struct_return_offset = 2 * sizeof(void *); + } + } +#else + { + /* We only need to pop structure pointers in non-nested functions */ + jit_type_t signature; + signature = func->signature; + if(!(func->nested_parent) && + jit_type_return_via_pointer(jit_type_get_return(signature))) + { +#if JIT_APPLY_X86_POP_STRUCT_RETURN == 1 + pop_bytes += sizeof(void *); +#endif + struct_return_offset = 2 * sizeof(void *); + } + } +#endif + + /* If we are returning a structure via a pointer, then copy + the pointer value into EAX when we return */ + if(struct_return_offset != 0) + { + x86_mov_reg_membase(inst, X86_EAX, X86_EBP, struct_return_offset, 4); + } + + /* Determine the number of callee save registers on the stack */ + num_regs = 0; + for(reg = 7; reg >= 0; --reg) + { + if(jit_reg_is_used(gen->touched, reg) && + (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) + { + ++num_regs; + } + } + + /* Pop the local stack frame, and get back to the callee save regs */ + inst = gen->posn.ptr; + if(num_regs == 0) + { + x86_mov_reg_reg(inst, X86_ESP, X86_EBP, sizeof(void *)); + } + else + { + x86_lea_membase(inst, X86_ESP, X86_EBP, -(sizeof(void *) * num_regs)); + } + + /* Pop the callee save registers that we used */ + for(reg = 7; reg >= 0; --reg) + { + if(jit_reg_is_used(gen->touched, reg) && + (_jit_reg_info[reg].flags & JIT_REG_CALL_USED) == 0) + { + x86_pop_reg(inst, _jit_reg_info[reg].cpu_reg); + } + } + + /* Pop the saved copy of ebp */ + x86_pop_reg(inst, X86_EBP); + + /* Return from the current function */ + if(pop_bytes > 0) + { + x86_ret_imm(inst, pop_bytes); + } + else + { + x86_ret(inst); + } + gen->posn.ptr = inst; +} + +void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func) +{ + void *ptr, *entry; + if(!jit_cache_check_for_n(&(gen->posn), 8)) + { + jit_cache_mark_full(&(gen->posn)); + return 0; + } + ptr = (void *)&(func->entry_point); + entry = gen->posn.ptr; + x86_jump_mem(gen->posn.ptr, ptr); + return entry; +} + +/* + * Setup or teardown the x86 code output process. + */ +#define jit_cache_setup_output(needed) \ + unsigned char *inst = gen->posn.ptr; \ + if(!jit_cache_check_for_n(&(gen->posn), (needed))) \ + { \ + jit_cache_mark_full(&(gen->posn)); \ + return; \ + } +#define jit_cache_end_output() \ + gen->posn.ptr = inst + +void _jit_gen_spill_reg(jit_gencode_t gen, int reg, + int other_reg, jit_value_t value) +{ + int offset; + + /* Make sure that we have sufficient space */ + jit_cache_setup_output(16); + + /* Fix the value in place within the local variable frame */ + _jit_gen_fix_value(value); + + /* Output an appropriate instruction to spill the value */ + offset = (int)(value->frame_offset); + if(reg < X86_REG_ST0) + { + /* Spill a word register */ + reg = _jit_reg_info[reg].cpu_reg; + x86_mov_membase_reg(inst, X86_EBP, offset, reg, 4); + if(other_reg != -1) + { + /* Spill the other word register in a pair */ + reg = _jit_reg_info[other_reg].cpu_reg; + offset += sizeof(void *); + x86_mov_membase_reg(inst, X86_EBP, offset, reg, 4); + } + } + else + { + /* Spill the top of the floating-point register stack */ + switch(jit_type_normalize(value->type)->kind) + { + case JIT_TYPE_FLOAT32: + { + x86_fst_membase(inst, X86_EBP, offset, 0, 1); + } + break; + + case JIT_TYPE_FLOAT64: + { + x86_fst_membase(inst, X86_EBP, offset, 1, 1); + } + break; + + case JIT_TYPE_NFLOAT: + { + x86_fst80_membase(inst, X86_EBP, offset); + } + break; + } + } + + /* End the code output process */ + jit_cache_end_output(); +} + +void _jit_gen_free_reg(jit_gencode_t gen, int reg, + int other_reg, int value_used) +{ + /* We only need to take explicit action if we are freeing a + floating-point register whose value hasn't been used yet */ + if(!value_used && reg >= X86_REG_ST0 && reg <= X86_REG_ST7) + { + if(jit_cache_check_for_n(&(gen->posn), 2)) + { + x86_fstp(gen->posn.ptr, reg - X86_REG_ST0); + } + else + { + jit_cache_mark_full(&(gen->posn)); + } + } +} + +void _jit_gen_load_value + (jit_gencode_t gen, int reg, int other_reg, jit_value_t value) +{ + /* TODO */ +} + +void _jit_gen_fix_value(jit_value_t value) +{ + if(!(value->has_frame_offset) && !(value->is_constant)) + { + jit_nint size = (jit_nint)(ROUND_STACK(jit_type_get_size(value->type))); + value->block->func->builder->frame_size += size; + value->frame_offset = -(value->block->func->builder->frame_size); + value->has_frame_offset = 1; + } +} + +void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, + jit_block_t block, jit_insn_t insn) +{ + /* TODO */ +} + +void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block) +{ + /* TODO: label fixups */ +} + +void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block) +{ + /* Nothing to do here for x86 */ +} + +void _jit_gen_call_finally + (jit_gencode_t gen, jit_function_t func, jit_label_t label) +{ + /* TODO */ +} + +void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object) +{ + void *frame; + + /* Fetch the proper EBP value from the stack, just before + where we need to unwind back to */ + frame = ((void **)stacktop)[-2]; + + /* Unwind the stack and jump to "catch_pc" */ +#if defined(__GNUC__) + __asm__ ( + "movl %0, %%edx\n\t" + "movl %1, %%eax\n\t" + "movl %2, %%ecx\n\t" + "movl %3, %%ebx\n\t" + "movl %%ebx, %%ebp\n\t" + "movl %%edx, %%esp\n\t" + "jmp *(%%eax)\n\t" + : : "m"(stacktop), "m"(catch_pc), "m"(object), "m"(frame) + : "eax", "ebx", "ecx", "edx" + ); +#elif defined(_MSC_VER) + __asm { + mov edx, dword ptr stacktop + mov eax, dword ptr catch_pc + mov ecx, dword ptr object + mov ebx, dword ptr frame + mov ebp, ebx + mov esp, edx + jmp [eax] + } +#else + #error "Don't know how to unwind the stack under x86" +#endif +} + +void _jit_unwind_stack(void *frame, void *pc, void *object) +{ +#if defined(__GNUC__) + __asm__ ( + "movl %0, %%edx\n\t" + "movl %1, %%eax\n\t" + "movl %2, %%ecx\n\t" + "movl %%edx, %%ebp\n\t" + "popl %%ebp\n\t" + "popl %%edx\n\t" + "jmp *%%eax\n\t" + : : "m"(frame), "m"(pc), "m"(object) + : "eax", "ecx", "edx" + ); +#elif defined(_MSC_VER) + __asm { + mov edx, dword ptr frame + mov eax, dword ptr pc + mov ecx, dword ptr object + mov ebp, edx + pop ebp + pop edx + jmp eax + } +#else + #error "Don't know how to unwind the stack on x86 platforms" +#endif +} + +#endif /* JIT_BACKEND_X86 */ diff --git a/jit/jit-rules-x86.h b/jit/jit-rules-x86.h new file mode 100644 index 0000000..c0feb39 --- /dev/null +++ b/jit/jit-rules-x86.h @@ -0,0 +1,81 @@ +/* + * jit-rules-x86.h - Rules that define the characteristics of the x86. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_RULES_X86_H +#define _JIT_RULES_X86_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Information about all of the registers, in allocation order. + */ +#define JIT_REG_INFO \ + {"eax", 0, 2, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"ecx", 1, 3, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"edx", 2, -1, JIT_REG_WORD | JIT_REG_CALL_USED}, \ + {"ebx", 3, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ + {"esi", 6, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ + {"edi", 7, -1, JIT_REG_WORD | JIT_REG_GLOBAL}, \ + {"ebp", 4, -1, JIT_REG_FRAME | JIT_REG_FIXED}, \ + {"esp", 5, -1, JIT_REG_STACK_PTR | JIT_REG_FIXED | JIT_REG_CALL_USED}, \ + {"st", 0, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_START_STACK | \ + JIT_REG_IN_STACK}, \ + {"st1", 1, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"st2", 2, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"st3", 3, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"st4", 4, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"st5", 5, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"st6", 6, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_IN_STACK}, \ + {"st7", 7, -1, JIT_REG_FLOAT | JIT_REG_CALL_USED | JIT_REG_END_STACK | \ + JIT_REG_IN_STACK}, +#define JIT_NUM_REGS 16 + +/* + * Define to 1 if we should always load values into registers + * before operating on them. i.e. the CPU does not have reg-mem + * and mem-reg addressing modes. + */ +#define JIT_ALWAYS_REG_REG 0 + +/* + * The maximum number of bytes to allocate for the prolog. + * This may be shortened once we know the true prolog size. + */ +#define JIT_PROLOG_SIZE 32 + +/* + * Preferred alignment for the start of functions. + */ +#define JIT_FUNCTION_ALIGNMENT 32 + +/* + * Define this to 1 if the platform allows reads and writes on + * any byte boundary. Define to 0 if only properly-aligned + * memory accesses are allowed. + */ +#define JIT_ALIGN_OVERRIDES 1 + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_RULES_X86_H */ diff --git a/jit/jit-rules.c b/jit/jit-rules.c new file mode 100644 index 0000000..35ed926 --- /dev/null +++ b/jit/jit-rules.c @@ -0,0 +1,124 @@ +/* + * jit-rules.c - Rules that define the characteristics of the back-end. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-rules.h" + +/* + * The information blocks for all registers in the system. + */ +jit_reginfo_t const _jit_reg_info[JIT_NUM_REGS] = {JIT_REG_INFO}; + +int _jit_int_lowest_byte(void) +{ + union + { + unsigned char bytes[4]; + jit_int value; + } volatile un; + int posn; + un.value = (jit_int)0x01020304; + posn = 0; + while(un.bytes[posn] != 0x04) + { + ++posn; + } + return posn; +} + +int _jit_int_lowest_short(void) +{ + union + { + unsigned char bytes[4]; + jit_int value; + } volatile un; + int posn; + un.value = (jit_int)0x01020304; + posn = 0; + while(un.bytes[posn] != 0x03 && un.bytes[posn] != 0x04) + { + ++posn; + } + return posn; +} + +int _jit_nint_lowest_byte(void) +{ +#ifdef JIT_NATIVE_INT32 + return _jit_int_lowest_byte(); +#else + union + { + unsigned char bytes[8]; + jit_long value; + } volatile un; + int posn; + un.value = (jit_long)0x0102030405060708; + posn = 0; + while(un.bytes[posn] != 0x08) + { + ++posn; + } + return posn; +#endif +} + +int _jit_nint_lowest_short(void) +{ +#ifdef JIT_NATIVE_INT32 + return _jit_int_lowest_short(); +#else + union + { + unsigned char bytes[8]; + jit_long value; + } volatile un; + int posn; + un.value = (jit_long)0x0102030405060708; + posn = 0; + while(un.bytes[posn] != 0x07 && un.bytes[posn] != 0x08) + { + ++posn; + } + return posn; +#endif +} + +int _jit_nint_lowest_int(void) +{ +#ifdef JIT_NATIVE_INT32 + return 0; +#else + union + { + unsigned char bytes[8]; + jit_long value; + } volatile un; + int posn; + un.value = (jit_long)0x0102030405060708; + posn = 0; + while(un.bytes[posn] <= 0x04) + { + ++posn; + } + return posn; +#endif +} diff --git a/jit/jit-rules.h b/jit/jit-rules.h new file mode 100644 index 0000000..0337f71 --- /dev/null +++ b/jit/jit-rules.h @@ -0,0 +1,222 @@ +/* + * jit-rules.h - Rules that define the characteristics of the back-end. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_RULES_H +#define _JIT_RULES_H + +#include "jit-cache.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Determine which backend to use. + */ +#define JIT_BACKEND_INTERP 1 +/*#define JIT_BACKEND_X86 1*/ +/*#define JIT_BACKEND_ARM 1*/ + +/* + * Information about a register. + */ +typedef struct +{ + const char *name; /* Name of the register, for debugging */ + short cpu_reg; /* CPU register number */ + short other_reg; /* Other register for a "long" pair, or -1 */ + int flags; /* Flags that define the register type */ + +} jit_reginfo_t; + +/* + * Register information flags. + */ +#define JIT_REG_WORD (1 << 0) /* Can be used for word values */ +#define JIT_REG_LONG (1 << 1) /* Can be used for long values */ +#define JIT_REG_FLOAT (1 << 2) /* Can be used for float values */ +#define JIT_REG_FRAME (1 << 3) /* Contains frame pointer */ +#define JIT_REG_STACK_PTR (1 << 4) /* Contains CPU stack pointer */ +#define JIT_REG_FIXED (1 << 5) /* Fixed use; not for allocation */ +#define JIT_REG_CALL_USED (1 << 6) /* Destroyed by a call */ +#define JIT_REG_START_STACK (1 << 7) /* Stack of stack-like allocation */ +#define JIT_REG_END_STACK (1 << 8) /* End of stack-like allocation */ +#define JIT_REG_IN_STACK (1 << 9) /* Middle of stack-like allocation */ +#define JIT_REG_GLOBAL (1 << 10) /* Candidate for global allocation */ + +/* + * Include definitions that are specific to the backend. + */ +#if defined(JIT_BACKEND_INTERP) + #include "jit-rules-interp.h" +#elif defined(JIT_BACKEND_X86) + #include "jit-rules-x86.h" +#elif defined(JIT_BACKEND_ARM) + #include "jit-rules-arm.h" +#else + #error "unknown jit backend type" +#endif + +/* + * The information blocks for all registers in the system. + */ +extern jit_reginfo_t const _jit_reg_info[JIT_NUM_REGS]; + +/* + * Manipulate register usage masks. The backend may override these + * definitions if it has more registers than can fit in a "jit_uint". + */ +#if !defined(jit_regused_init) +typedef jit_uint jit_regused_t; +#define jit_regused_init (0) +#define jit_reg_is_used(mask,reg) \ + (((mask) & (((jit_uint)1) << (reg))) != 0) +#define jit_reg_set_used(mask,reg) ((mask) |= (((jit_uint)1) << (reg))) +#define jit_reg_clear_used(mask,reg) ((mask) &= ~(((jit_uint)1) << (reg))) +#endif + +/* + * Information about a register's contents. + */ +#define JIT_MAX_REG_VALUES 8 +typedef struct jit_regcontents jit_regcontents_t; +struct jit_regcontents +{ + /* List of values that are currently stored in this register */ + jit_value_t values[JIT_MAX_REG_VALUES]; + short num_values; + + /* Flag that indicates if this register is holding the first + word of a double-word long value (32-bit platforms only) */ + char is_long_start; + + /* Flag that indicates if this register is holding the second + word of a double-word long value (32-bit platforms only) */ + char is_long_end; + + /* Current age of this register. Older registers are reclaimed first */ + int age; + + /* Remapped version of this register, when used in a stack */ + short remap; + + /* Flag that indicates if the register holds a valid value, + but there are no actual "jit_value_t" objects associated */ + short used_for_temp; +}; + +/* + * Code generation information. + */ +typedef struct jit_gencode *jit_gencode_t; +struct jit_gencode +{ + jit_regused_t permanent; /* Permanently allocated global regs */ + jit_regused_t touched; /* All registers that were touched */ + jit_cache_posn posn; /* Current cache output position */ + jit_regcontents_t contents[JIT_NUM_REGS]; /* Contents of each register */ + int current_age;/* Current age value for registers */ + int stack_map[JIT_NUM_REGS]; /* Reverse stack mappings */ +#ifdef jit_extra_gen_state + jit_extra_gen_state; /* CPU-specific extra information */ +#endif +}; + +/* + * ELF machine type and ABI information. + */ +typedef struct jit_elf_info jit_elf_info_t; +struct jit_elf_info +{ + int machine; + int abi; + int abi_version; + +}; + +/* + * External function defintions. + */ +void _jit_init_backend(void); +void _jit_gen_get_elf_info(jit_elf_info_t *info); +int _jit_create_entry_insns(jit_function_t func); +int _jit_create_call_setup_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + int is_nested, int nesting_level, jit_value_t *struct_return); +int _jit_setup_indirect_pointer(jit_function_t func, jit_value_t value); +int _jit_create_call_return_insns + (jit_function_t func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, + jit_value_t return_value, int is_nested); +int _jit_opcode_is_supported(int opcode); +void *_jit_gen_prolog(jit_gencode_t gen, jit_function_t func, void *buf); +void _jit_gen_epilog(jit_gencode_t gen, jit_function_t func); +void *_jit_gen_redirector(jit_gencode_t gen, jit_function_t func); +void _jit_gen_spill_reg(jit_gencode_t gen, int reg, + int other_reg, jit_value_t value); +void _jit_gen_free_reg(jit_gencode_t gen, int reg, + int other_reg, int value_used); +void _jit_gen_load_value + (jit_gencode_t gen, int reg, int other_reg, jit_value_t value); +void _jit_gen_fix_value(jit_value_t value); +void _jit_gen_insn(jit_gencode_t gen, jit_function_t func, + jit_block_t block, jit_insn_t insn); +void _jit_gen_start_block(jit_gencode_t gen, jit_block_t block); +void _jit_gen_end_block(jit_gencode_t gen, jit_block_t block); +void _jit_gen_call_finally + (jit_gencode_t gen, jit_function_t func, jit_label_t label); +void _jit_gen_unwind_stack(void *stacktop, void *catch_pc, void *object); + +/* + * Determine the byte number within a "jit_int" where the low + * order byte can be found. + */ +int _jit_int_lowest_byte(void); + +/* + * Determine the byte number within a "jit_int" where the low + * order short can be found. + */ +int _jit_int_lowest_short(void); + +/* + * Determine the byte number within a "jit_nint" where the low + * order byte can be found. + */ +int _jit_nint_lowest_byte(void); + +/* + * Determine the byte number within a "jit_nint" where the low + * order short can be found. + */ +int _jit_nint_lowest_short(void); + +/* + * Determine the byte number within a "jit_nint" where the low + * order int can be found. + */ +int _jit_nint_lowest_int(void); + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_RULES_H */ diff --git a/jit/jit-string.c b/jit/jit-string.c new file mode 100644 index 0000000..bdc18af --- /dev/null +++ b/jit/jit-string.c @@ -0,0 +1,486 @@ +/* + * jit-string.c - String handling routines. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include +#ifdef HAVE_STRING_H + #include +#elif defined(HAVE_STRINGS_H) + #include +#endif +#include +#ifdef HAVE_STDARG_H + #include +#elif HAVE_VARARGS_H + #include +#endif + +/*@ + * @section String operations + * @cindex String operations + * + * The following functions are provided to manipulate NULL-terminated + * strings. It is highly recommended that you use these functions in + * preference to system functions, because the corresponding system + * functions are extremely non-portable. +@*/ + +/*@ + * @deftypefun {unsigned int} jit_strlen ({const char *} str) + * Returns the length of @code{str}. + * @end deftypefun +@*/ +unsigned int jit_strlen(const char *str) +{ +#ifdef HAVE_STRLEN + return (unsigned int)(strlen(str)); +#else + unsigned int len = 0; + while(*str++ != '\0') + { + ++len; + } + return len; +#endif +} + +/*@ + * @deftypefun {char *} jit_strcpy ({char *} dest, {const char *} src) + * Copy the string at @code{src} to @code{dest}. Returns @code{dest}. + * @end deftypefun +@*/ +char *jit_strcpy(char *dest, const char *src) +{ +#ifdef HAVE_STRCPY + return strcpy(dest, src); +#else + char ch; + char *d = dest; + while((ch = *src++) != '\0') + { + *d++ = ch; + } + *d = '\0'; + return dest; +#endif +} + +/*@ + * @deftypefun {char *} jit_strcat ({char *} dest, {const char *} src) + * Copy the string at @code{src} to the end of the string at @code{dest}. + * Returns @code{dest}. + * @end deftypefun +@*/ +char *jit_strcat(char *dest, const char *src) +{ +#ifdef HAVE_STRCAT + return strcat(dest, src); +#else + char ch; + char *d = dest + jit_strlen(dest); + while((ch = *src++) != '\0') + { + *d++ = ch; + } + *d = '\0'; + return dest; +#endif +} + +/*@ + * @deftypefun {char *} jit_strncpy ({char *} dest, {const char *} src, {unsigned int} len) + * Copy at most @code{len} characters from the string at @code{src} to + * @code{dest}. Returns @code{dest}. + * @end deftypefun +@*/ +char *jit_strncpy(char *dest, const char *src, unsigned int len) +{ +#ifdef HAVE_STRNCPY + return strncpy(dest, src, len); +#else + char ch; + char *d = dest; + while(len > 0 && (ch = *src++) != '\0') + { + *d++ = ch; + --len; + } + while(len > 0) + { + *d++ = '\0'; + --len; + } + return dest; +#endif +} + +/*@ + * @deftypefun {char *} jit_strdup ({const char *} str) + * Allocate a block of memory using @code{jit_malloc} and copy + * @code{str} into it. Returns NULL if @code{str} is NULL or there + * is insufficient memory to perform the @code{jit_malloc} operation. + * @end deftypefun +@*/ +char *jit_strdup(const char *str) +{ + char *new_str; + if(!str) + { + return 0; + } + new_str = jit_malloc(strlen(str) + 1); + if(!new_str) + { + return 0; + } + strcpy(new_str, str); + return new_str; +} + +/*@ + * @deftypefun {char *} jit_strndup ({const char *} str, unsigned int len) + * Allocate a block of memory using @code{jit_malloc} and copy at most + * @code{len} characters of @code{str} into it. The copied string is then + * NULL-terminated. Returns NULL if @code{str} is NULL or there + * is insufficient memory to perform the @code{jit_malloc} operation. + * @end deftypefun +@*/ +char *jit_strndup(const char *str, unsigned int len) +{ + char *new_str; + if(!str) + { + return 0; + } + new_str = jit_malloc(len + 1); + if(!new_str) + { + return 0; + } + jit_memcpy(new_str, str, len); + new_str[len] = '\0'; + return new_str; +} + +/*@ + * @deftypefun int jit_strcmp ({const char *} str1, {const char *} str2) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * @end deftypefun +@*/ +int jit_strcmp(const char *str1, const char *str2) +{ +#ifdef HAVE_STRCMP + return strcmp(str1, str2); +#else + int ch1, ch2; + for(;;) + { + ch1 = *str1++; + ch2 = *str2++; + if(ch1 != ch2 || !ch1 || !ch2) + { + break; + } + } + return (ch1 - ch2); +#endif +} + +/*@ + * @deftypefun int jit_strncmp ({const char *} str1, {const char *} str2, {unsigned int} len) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * At most @code{len} characters are compared. + * @end deftypefun +@*/ +int jit_strncmp(const char *str1, const char *str2, unsigned int len) +{ +#ifdef HAVE_STRNCMP + return strncmp(str1, str2, len); +#else + int ch1, ch2; + while(len > 0) + { + ch1 = *str1++; + ch2 = *str2++; + if(ch1 != ch2 || !ch1 || !ch2) + { + return (ch1 - ch2); + } + --len; + } + return 0; +#endif +} + +/*@ + * @deftypefun int jit_stricmp ({const char *} str1, {const char *} str2) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * Instances of the English letters A to Z are converted into their + * lower case counterparts before comparison. + * + * Note: this function is guaranteed to use English case comparison rules, + * no matter what the current locale is set to. Use @code{jit_stricoll} for + * locale-sensitive string comparison. + * @end deftypefun +@*/ +int jit_stricmp(const char *str1, const char *str2) +{ + int ch1, ch2; + for(;;) + { + ch1 = *str1++; + ch2 = *str2++; + if(ch1 >= 'A' && ch1 <= 'Z') + { + ch1 = ch1 - 'A' + 'a'; + } + if(ch2 >= 'A' && ch2 <= 'Z') + { + ch2 = ch2 - 'A' + 'a'; + } + if(ch1 != ch2 || !ch1 || !ch2) + { + break; + } + } + return (ch1 - ch2); +} + +/*@ + * @deftypefun int jit_strnicmp ({const char *} str1, {const char *} str2, {unsigned int} len) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * At most @code{len} characters are compared. Instances of the English + * letters A to Z are converted into their lower case counterparts + * before comparison. + * + * Note: this function is guaranteed to use English case comparison rules, + * no matter what the current locale is set to. Use @code{jit_strnicoll} for + * locale-sensitive string comparison. + * @end deftypefun +@*/ +int jit_strnicmp(const char *str1, const char *str2, unsigned int len) +{ + int ch1, ch2; + while(len > 0) + { + ch1 = *str1++; + ch2 = *str2++; + if(ch1 >= 'A' && ch1 <= 'Z') + { + ch1 = ch1 - 'A' + 'a'; + } + if(ch2 >= 'A' && ch2 <= 'Z') + { + ch2 = ch2 - 'A' + 'a'; + } + if(ch1 != ch2 || !ch1 || !ch2) + { + return (ch1 - ch2); + } + --len; + } + return 0; +} + +/*@ + * @deftypefun int jit_strcoll ({const char *} str1, {const char *} str2) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * This function uses locale-sensitive comparison rules, but case is + * still considered significant. If the system does not have locale + * sensitive comparisons, this function will be identical to + * @code{jit_strcmp}. + * @end deftypefun +@*/ +int jit_strcoll(const char *str1, const char *str2) +{ +#if defined(HAVE_STRCOLL) + return strcoll(str1, str2); +#elif defined(HAVE__STRCOLL) + return _strcoll(str1, str2); +#elif defined(HAVE_STRCMP) + return strcmp(str1, str2); +#else + return jit_strcmp(str1, str2); +#endif +} + +/*@ + * @deftypefun int jit_stricoll ({const char *} str1, {const char *} str2) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * This function uses locale-sensitive comparison rules, while ignoring + * case. If the system does not have locale sensitive comparisons, this + * function will be identical to @code{jit_stricmp}. + * @end deftypefun +@*/ +int jit_stricoll(const char *str1, const char *str2) +{ +#if defined(HAVE_STRICOLL) + return stricoll(str1, str2); +#elif defined(HAVE__STRICOLL) + return _stricoll(str1, str2); +#elif defined(HAVE_STRCASECMP) + return strcasecmp(str1, str2); +#else + return jit_stricmp(str1, str2); +#endif +} + +/*@ + * @deftypefun int jit_strncoll ({const char *} str1, {const char *} str2, {unsigned int} len) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * At most @code{len} characters are compared, but it is otherwise + * the same as @code{jit_strcoll}. + * @end deftypefun +@*/ +int jit_strncoll(const char *str1, const char *str2, unsigned int len) +{ +#if defined(HAVE_STRNCOLL) + return strncoll(str1, str2, len); +#elif defined(HAVE__STRNCOLL) + return _strncoll(str1, str2, len); +#elif defined(HAVE_STRNCMP) + return strncmp(str1, str2, len); +#else + return jit_strncmp(str1, str2, len); +#endif +} + +/*@ + * @deftypefun int jit_strnicoll ({const char *} str1, {const char *} str2, {unsigned int} len) + * Compare the two strings @code{str1} and @code{str2}, returning + * a negative, zero, or positive value depending upon their relationship. + * At most @code{len} characters are compared, but it is otherwise + * the same as @code{jit_stricoll}. + * @end deftypefun +@*/ +int jit_strnicoll(const char *str1, const char *str2, unsigned int len) +{ +#if defined(HAVE_STRNICOLL) + return strincoll(str1, str2, len); +#elif defined(HAVE__STRNICOLL) + return _strnicoll(str1, str2, len); +#elif defined(HAVE_STRNCASECMP) + return strncasecmp(str1, str2, len); +#else + return jit_strnicmp(str1, str2, len); +#endif +} + +/*@ + * @deftypefun {char *} jit_strchr ({const char *} str, int ch) + * Search @code{str} for the first occurrence of @code{ch}. Returns + * the address where @code{ch} was found, or NULL if not found. + * @end deftypefun +@*/ +char *jit_strchr(const char *str, int ch) +{ +#ifdef HAVE_STRCHR + return strchr(str, ch); +#else + char *s = (char *)str; + for(;;) + { + if(*s == (char)ch) + { + return s; + } + else if(*s == '\0') + { + break; + } + ++s; + } + return 0; +#endif +} + +/*@ + * @deftypefun {char *} jit_strrchr ({const char *} str, int ch) + * Search @code{str} for the first occurrence of @code{ch}, starting + * at the end of the string. Returns the address where @code{ch} + * was found, or NULL if not found. + * @end deftypefun +@*/ +char *jit_strrchr(const char *str, int ch) +{ +#ifdef HAVE_STRRCHR + return strrchr(str, ch); +#else + unsigned int len = jit_strlen(str); + char *s = (char *)(str + len); + while(len > 0) + { + --s; + if(*s == (char)ch) + { + return s; + } + --len; + } + return 0; +#endif +} + +int jit_sprintf(char *str, const char *format, ...) +{ + va_list va; + int result; +#ifdef HAVE_STDARG_H + va_start(va, format); +#else + va_start(va); +#endif +#ifdef VSPRINTF + result = vsprintf(str, format, va); +#else + *str = '\0'; + result = 0; +#endif + va_end(va); + return result; +} + +int jit_snprintf(char *str, unsigned int len, const char *format, ...) +{ + va_list va; + int result; +#ifdef HAVE_STDARG_H + va_start(va, format); +#else + va_start(va); +#endif +#if defined(HAVE_VSNPRINTF) + result = vsnprintf(str, len, format, va); +#elif defined(HAVE__VSNPRINTF) + result = _vsnprintf(str, len, format, va); +#else + *str = '\0'; + result = 0; +#endif + va_end(va); + return result; +} diff --git a/jit/jit-thread.c b/jit/jit-thread.c new file mode 100644 index 0000000..aceb660 --- /dev/null +++ b/jit/jit-thread.c @@ -0,0 +1,128 @@ +/* + * jit-thread.c - Internal thread management routines for libjit. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" + +#if defined(JIT_THREADS_PTHREAD) + +/* + * The thread-specific key to use to fetch the control object. + */ +static pthread_key_t control_key; + +/* + * Initialize the pthread support routines. Only called once. + */ +static void init_pthread(void) +{ + /* Allocate a thread-specific variable for the JIT's thread + control object, and arrange for it to be freed when the + thread exits or is otherwise terminated */ + pthread_key_create(&control_key, jit_free); +} + +#elif defined(JIT_THREADS_WIN32) + +/* + * The thread-specific key to use to fetch the control object. + */ +static DWORD control_key; + +/* + * Initialize the Win32 thread support routines. Only called once. + */ +static void init_win32_thread(void) +{ + control_key = TlsAlloc(); +} + +#else /* No thread package */ + +/* + * The control object for the only thread in the system. + */ +static void *control_object = 0; + +#endif /* No thread package */ + +void _jit_thread_init(void) +{ +#if defined(JIT_THREADS_PTHREAD) + static pthread_once_t once_control = PTHREAD_ONCE_INIT; + pthread_once(&once_control, init_pthread); +#elif defined(JIT_THREADS_WIN32) + static LONG volatile once_control = 0; + if(!InterlockedExchange((PLONG)&once_control, 1)) + { + init_win32_thread(); + } +#endif +} + +static void *get_raw_control(void) +{ + _jit_thread_init(); +#if defined(JIT_THREADS_PTHREAD) + return pthread_getspecific(control_key); +#elif defined(JIT_THREADS_WIN32) + return (void *)(TlsGetValue(control_key)); +#else + return control_object; +#endif +} + +static void set_raw_control(void *obj) +{ + _jit_thread_init(); +#if defined(JIT_THREADS_PTHREAD) + pthread_setspecific(control_key, obj); +#elif defined(JIT_THREADS_WIN32) + TlsSetValue(control_key, obj); +#else + control_object = obj; +#endif +} + +jit_thread_control_t _jit_thread_get_control(void) +{ + jit_thread_control_t control; + control = (jit_thread_control_t)get_raw_control(); + if(!control) + { + control = jit_cnew(struct jit_thread_control); + if(control) + { + set_raw_control(control); + } + } + return control; +} + +jit_thread_id_t _jit_thread_current_id(void) +{ +#if defined(JIT_THREADS_PTHREAD) + return pthread_self(); +#elif defined(JIT_THREADS_WIN32) + return GetCurrentThread(); +#else + /* There is only one thread, so lets give it an identifier of 1 */ + return 1; +#endif +} diff --git a/jit/jit-thread.h b/jit/jit-thread.h new file mode 100644 index 0000000..3737cd8 --- /dev/null +++ b/jit/jit-thread.h @@ -0,0 +1,115 @@ +/* + * jit-thread.h - Internal thread management routines for libjit. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#ifndef _JIT_THREAD_H +#define _JIT_THREAD_H + +#include +#if defined(HAVE_PTHREAD_H) && defined(HAVE_LIBPTHREAD) + #include +#elif defined(JIT_WIN32_PLATFORM) + #include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Determine the type of threading library that we are using. + */ +#if defined(HAVE_PTHREAD_H) && defined(HAVE_LIBPTHREAD) + #define JIT_THREADS_SUPPORTED 1 + #define JIT_THREADS_PTHREAD 1 +#elif defined(JIT_WIN32_PLATFORM) + #define JIT_THREADS_SUPPORTED 1 + #define JIT_THREADS_WIN32 1 +#else + #define JIT_THREADS_SUPPORTED 0 +#endif + +/* + * Type that describes a thread's identifier, and the id comparison function. + */ +#if defined(JIT_THREADS_PTHREAD) +typedef pthread_t jit_thread_id_t; +#define jit_thread_id_equal(x,y) (pthread_equal((x), (y))) +#elif defined(JIT_THREADS_WIN32) +typedef HANDLE jit_thread_id_t; +#define jit_thread_id_equal(x,y) ((x) == (y)) +#else +typedef int jit_thread_id_t; +#define jit_thread_id_equal(x,y) ((x) == (y)) +#endif + +/* + * Control information that is associated with a thread. + */ +typedef struct jit_thread_control *jit_thread_control_t; + +/* + * Initialize the thread routines. Ignored if called multiple times. + */ +void _jit_thread_init(void); + +/* + * Get the JIT control object for the current thread. + */ +jit_thread_control_t _jit_thread_get_control(void); + +/* + * Get the identifier for the current thread. + */ +jit_thread_id_t _jit_thread_current_id(void); + +/* + * Define the primitive mutex operations. + */ +#if defined(JIT_THREADS_PTHREAD) + +typedef pthread_mutex_t jit_mutex_t; +#define jit_mutex_create(mutex) (pthread_mutex_init((mutex), 0)) +#define jit_mutex_destroy(mutex) (pthread_mutex_destroy((mutex))) +#define jit_mutex_lock(mutex) (pthread_mutex_lock((mutex))) +#define jit_mutex_unlock(mutex) (pthread_mutex_unlock((mutex))) + +#elif defined(JIT_THREADS_WIN32) + +typedef CRITICAL_SECTION jit_mutex_t; +#define jit_mutex_create(mutex) (InitializeCriticalSection((mutex))) +#define jit_mutex_destroy(mutex) (DeleteCriticalSection((mutex))) +#define jit_mutex_lock(mutex) (EnterCriticalSection((mutex))) +#define jit_mutex_unlock(mutex) (LeaveCriticalSection((mutex))) + +#else + +typedef int jit_mutex_t; +#define jit_mutex_create(mutex) do { ; } while (0) +#define jit_mutex_destroy(mutex) do { ; } while (0) +#define jit_mutex_lock(mutex) do { ; } while (0) +#define jit_mutex_unlock(mutex) do { ; } while (0) + +#endif + +#ifdef __cplusplus +}; +#endif + +#endif /* _JIT_THREAD_H */ diff --git a/jit/jit-type.c b/jit/jit-type.c new file mode 100644 index 0000000..9a5554d --- /dev/null +++ b/jit/jit-type.c @@ -0,0 +1,1323 @@ +/* + * jit-type.c - Functions for manipulating type descriptors. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-apply-rules.h" +#include "jit-rules.h" +#include + +/*@ + +@cindex jit-type.h +@tindex jit_type_t + +The functions that are defined in @code{} allow +the library user to create and manipulate objects that represent +native system types. For example, @code{jit_type_int} represents +the signed 32-bit integer type. + +Each @code{jit_type_t} object represents a basic system type, +be it a primitive, a struct, a union, a pointer, or a function signature. +The library uses this information to lay out values in memory. + +The following pre-defined types are available: + +@table @code +@vindex jit_type_void +@item jit_type_void +Represents the @code{void} type. + +@vindex jit_type_sbyte +@item jit_type_sbyte +Represents a signed 8-bit integer type. + +@vindex jit_type_ubyte +@item jit_type_ubyte +Represents an unsigned 8-bit integer type. + +@vindex jit_type_short +@item jit_type_short +Represents a signed 16-bit integer type. + +@vindex jit_type_ushort +@item jit_type_ushort +Represents an unsigned 16-bit integer type. + +@vindex jit_type_int +@item jit_type_int +Represents a signed 32-bit integer type. + +@vindex jit_type_uint +@item jit_type_uint +Represents an unsigned 32-bit integer type. + +@vindex jit_type_nint +@item jit_type_nint +Represents a signed integer type that has the same size and +alignment as a native pointer. + +@vindex jit_type_nuint +@item jit_type_nuint +Represents an unsigned integer type that has the same size and +alignment as a native pointer. + +@vindex jit_type_long +@item jit_type_long +Represents a signed 64-bit integer type. + +@vindex jit_type_ulong +@item jit_type_ulong +Represents an unsigned 64-bit integer type. + +@vindex jit_type_float32 +@item jit_type_float32 +Represents a 32-bit floating point type. + +@vindex jit_type_float64 +@item jit_type_float64 +Represents a 64-bit floating point type. + +@vindex jit_type_nfloat +@item jit_type_nfloat +Represents a floating point type that represents the greatest +precision supported on the native platform. + +@vindex jit_type_void_ptr +@item jit_type_void_ptr +Represents the system's @code{void *} type. This can be used wherever +a native pointer type is required. +@end table + +Type descriptors are reference counted. You can make a copy of a type +descriptor using the @code{jit_type_copy} function, and free the copy with +@code{jit_type_free}. + +Some languages have special versions of the primitive numeric types +(e.g. boolean types, 16-bit Unicode character types, enumerations, etc). +If it is important to distinguish these special versions from the +numeric types, then you should use the @code{jit_type_create_tagged} +function below. + +@*/ + +/* + * Pre-defined type descriptors. + */ +struct _jit_type const _jit_type_void_def = + {1, JIT_TYPE_VOID, 0, 1, 0, 1, 1}; +jit_type_t const jit_type_void = (jit_type_t)&_jit_type_void_def; +struct _jit_type const _jit_type_sbyte_def = + {1, JIT_TYPE_SBYTE, 0, 1, 0, sizeof(jit_sbyte), JIT_ALIGN_SBYTE}; +jit_type_t const jit_type_sbyte = (jit_type_t)&_jit_type_sbyte_def; +struct _jit_type const _jit_type_ubyte_def = + {1, JIT_TYPE_UBYTE, 0, 1, 0, sizeof(jit_ubyte), JIT_ALIGN_UBYTE}; +jit_type_t const jit_type_ubyte = (jit_type_t)&_jit_type_ubyte_def; +struct _jit_type const _jit_type_short_def = + {1, JIT_TYPE_SHORT, 0, 1, 0, sizeof(jit_short), JIT_ALIGN_SHORT}; +jit_type_t const jit_type_short = (jit_type_t)&_jit_type_short_def; +struct _jit_type const _jit_type_ushort_def = + {1, JIT_TYPE_USHORT, 0, 1, 0, sizeof(jit_ushort), JIT_ALIGN_USHORT}; +jit_type_t const jit_type_ushort = (jit_type_t)&_jit_type_ushort_def; +struct _jit_type const _jit_type_int_def = + {1, JIT_TYPE_INT, 0, 1, 0, sizeof(jit_int), JIT_ALIGN_INT}; +jit_type_t const jit_type_int = (jit_type_t)&_jit_type_int_def; +struct _jit_type const _jit_type_uint_def = + {1, JIT_TYPE_UINT, 0, 1, 0, sizeof(jit_uint), JIT_ALIGN_UINT}; +jit_type_t const jit_type_uint = (jit_type_t)&_jit_type_uint_def; +struct _jit_type const _jit_type_nint_def = + {1, JIT_TYPE_NINT, 0, 1, 0, sizeof(jit_nint), JIT_ALIGN_NINT}; +jit_type_t const jit_type_nint = (jit_type_t)&_jit_type_nint_def; +struct _jit_type const _jit_type_nuint_def = + {1, JIT_TYPE_NUINT, 0, 1, 0, sizeof(jit_nuint), JIT_ALIGN_NUINT}; +jit_type_t const jit_type_nuint = (jit_type_t)&_jit_type_nuint_def; +struct _jit_type const _jit_type_long_def = + {1, JIT_TYPE_LONG, 0, 1, 0, sizeof(jit_long), JIT_ALIGN_LONG}; +jit_type_t const jit_type_long = (jit_type_t)&_jit_type_long_def; +struct _jit_type const _jit_type_ulong_def = + {1, JIT_TYPE_ULONG, 0, 1, 0, sizeof(jit_ulong), JIT_ALIGN_ULONG}; +jit_type_t const jit_type_ulong = (jit_type_t)&_jit_type_ulong_def; +struct _jit_type const _jit_type_float32_def = + {1, JIT_TYPE_FLOAT32, 0, 1, 0, sizeof(jit_float32), JIT_ALIGN_FLOAT32}; +jit_type_t const jit_type_float32 = (jit_type_t)&_jit_type_float32_def; +struct _jit_type const _jit_type_float64_def = + {1, JIT_TYPE_FLOAT64, 0, 1, 0, sizeof(jit_float64), JIT_ALIGN_FLOAT64}; +jit_type_t const jit_type_float64 = (jit_type_t)&_jit_type_float64_def; +struct _jit_type const _jit_type_nfloat_def = + {1, JIT_TYPE_NFLOAT, 0, 1, 0, sizeof(jit_nfloat), JIT_ALIGN_NFLOAT}; +jit_type_t const jit_type_nfloat = (jit_type_t)&_jit_type_nfloat_def; +struct _jit_type const _jit_type_void_ptr_def = + {1, JIT_TYPE_PTR, 0, 1, 0, sizeof(void *), JIT_ALIGN_PTR, + (jit_type_t)&_jit_type_void_def}; +jit_type_t const jit_type_void_ptr = (jit_type_t)&_jit_type_void_ptr_def; + +/* + * Type descriptors for the system "char", "int", "long", etc types. + * These are defined to one of the above values. + */ +#ifdef __CHAR_UNSIGNED__ +jit_type_t const jit_type_sys_char = (jit_type_t)&_jit_type_ubyte_def; +#else +jit_type_t const jit_type_sys_char = (jit_type_t)&_jit_type_sbyte_def; +#endif +jit_type_t const jit_type_sys_schar = (jit_type_t)&_jit_type_sbyte_def; +jit_type_t const jit_type_sys_uchar = (jit_type_t)&_jit_type_ubyte_def; +#if SIZEOF_SHORT == 4 +jit_type_t const jit_type_sys_short = (jit_type_t)&_jit_type_int_def; +jit_type_t const jit_type_sys_ushort = (jit_type_t)&_jit_type_uint_def; +#elif SIZEOF_SHORT == 8 +jit_type_t const jit_type_sys_short = (jit_type_t)&_jit_type_long_def; +jit_type_t const jit_type_sys_ushort = (jit_type_t)&_jit_type_ulong_def; +#else +jit_type_t const jit_type_sys_short = (jit_type_t)&_jit_type_short_def; +jit_type_t const jit_type_sys_ushort = (jit_type_t)&_jit_type_ushort_def; +#endif +#if SIZEOF_INT == 8 +jit_type_t const jit_type_sys_int = (jit_type_t)&_jit_type_long_def; +jit_type_t const jit_type_sys_uint = (jit_type_t)&_jit_type_ulong_def; +#elif SIZEOF_INT == 2 +jit_type_t const jit_type_sys_int = (jit_type_t)&_jit_type_short_def; +jit_type_t const jit_type_sys_uint = (jit_type_t)&_jit_type_ushort_def; +#else +jit_type_t const jit_type_sys_int = (jit_type_t)&_jit_type_int_def; +jit_type_t const jit_type_sys_uint = (jit_type_t)&_jit_type_uint_def; +#endif +#if SIZEOF_LONG == 8 +jit_type_t const jit_type_sys_long = (jit_type_t)&_jit_type_long_def; +jit_type_t const jit_type_sys_ulong = (jit_type_t)&_jit_type_ulong_def; +#elif SIZEOF_LONG == 2 +jit_type_t const jit_type_sys_long = (jit_type_t)&_jit_type_short_def; +jit_type_t const jit_type_sys_ulong = (jit_type_t)&_jit_type_ushort_def; +#else +jit_type_t const jit_type_sys_long = (jit_type_t)&_jit_type_int_def; +jit_type_t const jit_type_sys_ulong = (jit_type_t)&_jit_type_uint_def; +#endif +#if SIZEOF_LONG_LONG == 8 || SIZEOF___INT64 == 8 +jit_type_t const jit_type_sys_longlong = (jit_type_t)&_jit_type_long_def; +jit_type_t const jit_type_sys_ulonglong = (jit_type_t)&_jit_type_ulong_def; +#elif SIZEOF_LONG_LONG == 4 +jit_type_t const jit_type_sys_longlong = (jit_type_t)&_jit_type_int_def; +jit_type_t const jit_type_sys_ulonglong = (jit_type_t)&_jit_type_uint_def; +#elif SIZEOF_LONG_LONG == 2 +jit_type_t const jit_type_sys_longlong = (jit_type_t)&_jit_type_short_def; +jit_type_t const jit_type_sys_ulonglong = (jit_type_t)&_jit_type_ushort_def; +#else +jit_type_t const jit_type_sys_longlong = (jit_type_t)&_jit_type_long_def; +jit_type_t const jit_type_sys_ulonglong = (jit_type_t)&_jit_type_ulong_def; +#endif +jit_type_t const jit_type_sys_float = (jit_type_t)&_jit_type_float32_def; +jit_type_t const jit_type_sys_double = (jit_type_t)&_jit_type_float64_def; +jit_type_t const jit_type_sys_long_double = (jit_type_t)&_jit_type_nfloat_def; + +/* + * Special offset flags. + */ +#define JIT_OFFSET_IS_INTERNAL (((jit_nuint)1) << (sizeof(jit_nint) * 8 - 1)) +#define JIT_OFFSET_NOT_SET (~((jit_nuint)0)) + +/* + * Layout flags. + */ +#define JIT_LAYOUT_NEEDED 1 +#define JIT_LAYOUT_EXPLICIT_SIZE 2 +#define JIT_LAYOUT_EXPLICIT_ALIGN 4 + +/* + * Perform layout on a structure or union type. + */ +static void perform_layout(jit_type_t type) +{ + jit_nuint size = 0; + jit_nuint maxSize = 0; + jit_nuint maxAlign = 1; + jit_nuint alignLimit; + jit_nuint fieldSize; + jit_nuint fieldAlign; + unsigned int index; + + /* Determine the alignment limit, if there is an override */ +#ifdef JIT_ALIGN_OVERRIDES + if((type->layout_flags & JIT_LAYOUT_EXPLICIT_ALIGN) != 0) + { + alignLimit = type->alignment; + } + else +#endif + { + alignLimit = 0; + } + + /* Lay out all of the fields in this structure */ + for(index = 0; index < type->num_components; ++index) + { + /* Get the size and alignment of the field */ + fieldSize = jit_type_get_size(type->components[index].type); + fieldAlign = jit_type_get_alignment(type->components[index].type); + + /* Clamp the alignment if we have a limit */ + if(alignLimit != 0 && fieldAlign > alignLimit) + { + fieldAlign = alignLimit; + } + + /* Update the size and alignment values */ + if(type->kind == JIT_TYPE_STRUCT) + { + /* Perform layout for a struct type */ + if((type->components[index].offset & JIT_OFFSET_IS_INTERNAL) != 0) + { + /* Calculate the offset for the field automatically */ + if((size % fieldAlign) != 0) + { + size += fieldAlign - (size % fieldAlign); + } + type->components[index].offset = JIT_OFFSET_IS_INTERNAL | size; + size += fieldSize; + } + else + { + /* Use the explicitly-supplied offset for the field */ + size = type->components[index].offset + fieldSize; + } + if(size > maxSize) + { + maxSize = size; + } + } + else + { + /* Perform layout for a union type (offset is always zero) */ + type->components[index].offset = JIT_OFFSET_IS_INTERNAL | 0; + if((fieldSize % fieldAlign) != 0) + { + fieldSize += fieldAlign - (fieldSize % fieldAlign); + } + if(fieldSize > maxSize) + { + maxSize = fieldSize; + } + } + if(fieldAlign > maxAlign) + { + maxAlign = fieldAlign; + } + } + + /* Align the full structure */ + if((maxSize % maxAlign) != 0) + { + maxSize += maxAlign - (maxSize % maxAlign); + } + + /* Record the final size and alignment values */ + if((type->layout_flags & JIT_LAYOUT_EXPLICIT_SIZE) != 0) + { + if(maxSize > type->size) + { + type->size = maxSize; + } + } + else + { + type->size = maxSize; + } + type->alignment = maxAlign; +} + +/*@ + * @deftypefun jit_type_t jit_type_copy (jit_type_t type) + * Make a copy of the type descriptor @code{type} by increasing + * its reference count. + * @end deftypefun +@*/ +jit_type_t jit_type_copy(jit_type_t type) +{ + if(!type || type->is_fixed) + { + return type; + } + ++(type->ref_count); + return type; +} + +/*@ + * @deftypefun void jit_type_free (jit_type_t type) + * Free a type descriptor by decreasing its reference count. + * This function is safe to use on pre-defined types, which are + * never actually freed. + * @end deftypefun +@*/ +void jit_type_free(jit_type_t type) +{ + unsigned int index; + if(!type || type->is_fixed) + { + return; + } + if(--(type->ref_count) != 0) + { + return; + } + jit_type_free(type->sub_type); + for(index = 0; index < type->num_components; ++type) + { + jit_type_free(type->components[index].type); + if(type->components[index].name) + { + jit_free(type->components[index].name); + } + } + if(type->kind >= JIT_TYPE_FIRST_TAGGED) + { + struct jit_tagged_type *tagged = (struct jit_tagged_type *)type; + if(tagged->free_func) + { + (*(tagged->free_func))(tagged->data); + } + } + jit_free(type); +} + +static jit_type_t create_complex(int kind, jit_type_t *types, + unsigned int num, int incref) +{ + jit_type_t type; + unsigned int index; + if(num <= 1) + { + type = jit_cnew(struct _jit_type); + } + else + { + type = (jit_type_t)jit_calloc + (1, sizeof(struct _jit_type) + + (num - 1) * sizeof(struct jit_component)); + } + if(!type) + { + return 0; + } + type->ref_count = 1; + type->kind = kind; + type->layout_flags = JIT_LAYOUT_NEEDED; + type->num_components = num; + for(index = 0; index < num; ++index) + { + if(incref) + { + type->components[index].type = jit_type_copy(types[index]); + } + else + { + type->components[index].type = types[index]; + } + type->components[index].offset = JIT_OFFSET_NOT_SET; + type->components[index].name = 0; + } + return type; +} + +/*@ + * @deftypefun jit_type_t jit_type_create_struct ({jit_type_t *} fields, {unsigned int} num_fields, int incref) + * Create a type descriptor for a structure. Returns NULL if out of memory. + * If there are no fields, then the size of the structure will be zero. + * It is necessary to add a padding field if the language does not allow + * zero-sized structures. The reference counts on the field types are + * incremented if @code{incref} is non-zero. + * + * The @code{libjit} library does not provide any special support for + * implementing structure inheritance, where one structure extends the + * definition of another. The effect of inheritance can be achieved + * by always allocating the first field of a structure to be an instance + * of the inherited structure. Multiple inheritance can be supported + * by allocating several special fields at the front of an inheriting + * structure. + * + * Similarly, no special support is provided for vtables. The program + * is responsible for allocating an appropriate slot in a structure to + * contain the vtable pointer, and dereferencing it wherever necessary. + * The vtable will itself be a structure, containing signature types + * for each of the method slots. + * + * The choice not to provide special support for inheritance and vtables + * in @code{libjit} was deliberate. The layout of objects and vtables + * is highly specific to the language and virtual machine being emulated, + * and no single scheme can hope to capture all possibilities. + * @end deftypefun +@*/ +jit_type_t jit_type_create_struct(jit_type_t *fields, unsigned int num_fields, + int incref) +{ + return create_complex(JIT_TYPE_STRUCT, fields, num_fields, incref); +} + +/*@ + * @deftypefun jit_type_t jit_type_create_union ({jit_type_t *} fields, {unsigned int} num_fields, int incref) + * Create a type descriptor for a union. Returns NULL if out of memory. + * If there are no fields, then the size of the union will be zero. + * It is necessary to add a padding field if the language does not allow + * zero-sized unions. The reference counts on the field types are + * incremented if @code{incref} is non-zero. + * @end deftypefun +@*/ +jit_type_t jit_type_create_union(jit_type_t *fields, unsigned int num_fields, + int incref) +{ + return create_complex(JIT_TYPE_UNION, fields, num_fields, incref); +} + +/*@ + * @deftypefun jit_type_t jit_type_create_signature (jit_abi_t abi, jit_type_t return_type, {jit_type_t *} params, {unsigned int} num_params, int incref) + * Create a type descriptor for a function signature. Returns NULL if out + * of memory. The reference counts on the component types are incremented + * if @code{incref} is non-zero. + * + * When used as a structure or union field, function signatures are laid + * out like pointers. That is, they represent a pointer to a function + * that has the specified parameters and return type. + * + * @tindex jit_abi_t + * The @code{abi} parameter specifies the Application Binary Interface (ABI) + * that the function uses. It may be one of the following values: + * + * @table @code + * @vindex jit_abi_cdecl + * @item jit_abi_cdecl + * Use the native C ABI definitions of the underlying platform. + * + * @vindex jit_abi_vararg + * @item jit_abi_vararg + * Use the native C ABI definitions of the underlying platform, + * and allow for an optional list of variable argument parameters. + * + * @vindex jit_abi_stdcall + * @item jit_abi_stdcall + * Use the Win32 STDCALL ABI definitions, whereby the callee pops + * its arguments rather than the caller. If the platform does + * not support this type of ABI, then @code{jit_abi_stdcall} will be + * identical to @code{jit_abi_cdecl}. + * + * @vindex jit_abi_fastcall + * @item jit_abi_fastcall + * Use the Win32 FASTCALL ABI definitions, whereby the callee pops + * its arguments rather than the caller, and the first two word + * arguments are passed in ECX and EDX. If the platform does + * not support this type of ABI, then @code{jit_abi_fastcall} will be + * identical to @code{jit_abi_cdecl}. + * @end table + * @end deftypefun +@*/ +jit_type_t jit_type_create_signature(jit_abi_t abi, jit_type_t return_type, + jit_type_t *params, + unsigned int num_params, int incref) +{ + jit_type_t type; + type = create_complex(JIT_TYPE_SIGNATURE, params, num_params, incref); + if(type) + { + type->abi = (int)abi; + type->layout_flags = 0; + type->size = 0; + type->alignment = JIT_ALIGN_PTR; + if(incref) + { + type->sub_type = jit_type_copy(return_type); + } + else + { + type->sub_type = return_type; + } + } + return type; +} + +/*@ + * @deftypefun jit_type_t jit_type_create_pointer (jit_type_t type, int incref) + * Create a type descriptor for a pointer to another type. Returns NULL + * if out of memory. The reference count on @code{type} is incremented if + * @code{incref} is non-zero. + * @end deftypefun +@*/ +jit_type_t jit_type_create_pointer(jit_type_t type, int incref) +{ + jit_type_t ntype; + if(type == jit_type_void) + { + return jit_type_void_ptr; + } + if((ntype = jit_cnew(struct _jit_type)) == 0) + { + return 0; + } + ntype->ref_count = 1; + ntype->kind = JIT_TYPE_PTR; + ntype->size = sizeof(void *); + ntype->alignment = JIT_ALIGN_PTR; + if(incref) + { + ntype->sub_type = jit_type_copy(type); + } + else + { + ntype->sub_type = type; + } + return ntype; +} + +/*@ + * @deftypefun jit_type_t jit_type_create_tagged (jit_type_t type, int kind, {void *} data, jit_meta_free_func free_func, int incref) + * Tag a type with some additional user data. Tagging is typically used by + * higher-level programs to embed extra information about a type that + * @code{libjit} itself does not support. + * + * As an example, a language might have a 16-bit Unicode character type + * and a 16-bit unsigned integer type that are distinct types, even though + * they share the same fundamental representation (@code{jit_ushort}). + * Tagging allows the program to distinguish these two types, when + * it is necessary to do so, without affecting @code{libjit}'s ability + * to compile the code efficiently. + * + * The @code{kind} is a small positive integer value that the program + * can use to distinguish multiple tag types. The @code{data} pointer is + * the actual data that you wish to store. And @code{free_func} is a + * function that is used to free @code{data} when the type is freed + * with @code{jit_type_free}. + * + * If you need to store more than one piece of information, you can + * tag a type multiple times. The order in which multiple tags are + * applied is irrelevant to @code{libjit}, although it may be relevant + * to the higher-level program. + * @end deftypefun +@*/ +jit_type_t jit_type_create_tagged(jit_type_t type, int kind, void *data, + jit_meta_free_func free_func, int incref) +{ + struct jit_tagged_type *ntype; + if((ntype = jit_cnew(struct jit_tagged_type)) == 0) + { + return 0; + } + ntype->type.ref_count = 1; + ntype->type.kind = JIT_TYPE_FIRST_TAGGED + kind; + ntype->type.size = 0; + ntype->type.alignment = 1; + if(incref) + { + ntype->type.sub_type = jit_type_copy(type); + } + else + { + ntype->type.sub_type = type; + } + ntype->data = data; + ntype->free_func = free_func; + return &(ntype->type); +} + +/*@ + * @deftypefun int jit_type_set_names (jit_type_t type, {char **} names, {unsigned int} num_names) + * Set the field or parameter names for @code{type}. Returns zero + * if there is insufficient memory to set the names. + * + * Normally fields are accessed via their index. Field names are a + * convenience for front ends that prefer to use names to indices. + * @end deftypefun +@*/ +int jit_type_set_names(jit_type_t type, char **names, unsigned int num_names) +{ + char *temp; + if(!type || type->is_fixed || !names) + { + return 1; + } + if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION || + type->kind == JIT_TYPE_SIGNATURE) + { + if(num_names > type->num_components) + { + num_names = type->num_components; + } + while(num_names > 0) + { + --num_names; + if(type->components[num_names].name) + { + jit_free(type->components[num_names].name); + type->components[num_names].name = 0; + } + if(names[num_names]) + { + temp = jit_strdup(names[num_names]); + if(!temp) + { + return 0; + } + type->components[num_names].name = temp; + } + } + } + return 1; +} + +/*@ + * @deftypefun void jit_type_set_size_and_alignment (jit_type_t type, jit_nint size, jit_nint alignment) + * Set the size and alignment information for a structure or union + * type. Use this for performing explicit type layout. Normally + * the size is computed automatically. Ignored if not a + * structure or union type. Setting either value to -1 will cause + * that value to be computed automatically. + * @end deftypefun +@*/ +void jit_type_set_size_and_alignment(jit_type_t type, jit_nint size, + jit_nint alignment) +{ + if(!type) + { + return; + } + if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) + { + type->size = (jit_nuint)size; + type->alignment = (jit_nuint)alignment; + if(size != -1) + { + type->layout_flags |= JIT_LAYOUT_EXPLICIT_SIZE; + } + if(alignment != -1) + { + type->layout_flags |= JIT_LAYOUT_EXPLICIT_ALIGN; + } + type->layout_flags |= JIT_LAYOUT_NEEDED; + } +} + +/*@ + * @deftypefun void jit_type_set_offset (jit_type_t type, {unsigned int} field_index, jit_nuint offset) + * Set the offset of a specific structure field. Use this for + * performing explicit type layout. Normally the offset is + * computed automatically. Ignored if not a structure type, + * or the field index is out of range. + * @end deftypefun +@*/ +void jit_type_set_offset(jit_type_t type, unsigned int field_index, + jit_nuint offset) +{ + if(!type || field_index >= type->num_components) + { + return; + } + if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) + { + type->components[field_index].offset = offset; + type->layout_flags |= JIT_LAYOUT_NEEDED; + } +} + +/*@ + * @deftypefun jit_nuint jit_type_get_size (jit_type_t type) + * Get the size of a type in bytes. + * @end deftypefun +@*/ +jit_nuint jit_type_get_size(jit_type_t type) +{ + if(!type) + { + return 0; + } + if(type->kind == JIT_TYPE_SIGNATURE) + { + /* The "size" field is used for argument size, not type size, + so we ignore it and return the real size here */ + return sizeof(void *); + } + else if(type->kind >= JIT_TYPE_FIRST_TAGGED) + { + return jit_type_get_size(type->sub_type); + } + if((type->layout_flags & JIT_LAYOUT_NEEDED) != 0) + { + perform_layout(type); + } + return type->size; +} + +/*@ + * @deftypefun jit_nuint jit_type_get_alignment (jit_type_t type) + * Get the alignment of a type. An alignment value of 2 indicates + * that the type should be aligned on a two-byte boundary, for example. + * @end deftypefun +@*/ +jit_nuint jit_type_get_alignment(jit_type_t type) +{ + if(!type) + { + return 0; + } + if(type->kind >= JIT_TYPE_FIRST_TAGGED) + { + return jit_type_get_alignment(type->sub_type); + } + if((type->layout_flags & JIT_LAYOUT_NEEDED) != 0) + { + perform_layout(type); + } + return type->alignment; +} + +/*@ + * @deftypefun {unsigned int} jit_type_num_fields (jit_type_t type) + * Get the number of fields in a structure or union type. + * @end deftypefun +@*/ +unsigned int jit_type_num_fields(jit_type_t type) +{ + if(!type || + (type->kind != JIT_TYPE_STRUCT && type->kind != JIT_TYPE_UNION)) + { + return 0; + } + else + { + return type->num_components; + } +} + +/*@ + * @deftypefun jit_type_t jit_type_get_field (jit_type_t type, {unsigned int} field_index) + * Get the type of a specific field within a structure or union. + * Returns NULL if not a structure or union, or the index is out of range. + * @end deftypefun +@*/ +jit_type_t jit_type_get_field(jit_type_t type, unsigned int field_index) +{ + if(!type || field_index >= type->num_components) + { + return 0; + } + if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION) + { + return type->components[field_index].type; + } + return 0; +} + +/*@ + * @deftypefun jit_nuint jit_type_get_offset (jit_type_t type, {unsigned int} field_index) + * Get the offset of a specific field within a structure. + * Returns zero if not a structure, or the index is out of range, + * so this is safe to use on non-structure types. + * @end deftypefun +@*/ +jit_nuint jit_type_get_offset(jit_type_t type, unsigned int field_index) +{ + if(!type || field_index >= type->num_components) + { + return 0; + } + if(type->kind != JIT_TYPE_STRUCT && type->kind != JIT_TYPE_UNION) + { + return 0; + } + if((type->layout_flags & JIT_LAYOUT_NEEDED) != 0) + { + perform_layout(type); + } + return type->components[field_index].offset & ~JIT_OFFSET_IS_INTERNAL; +} + +/*@ + * @deftypefun {const char *} jit_type_get_name (jit_type_t type, {unsigned int} index) + * Get the name of a structure, union, or signature field/parameter. + * Returns NULL if not a structure, union, or signature, the index + * is out of range, or there is no name associated with the component. + * @end deftypefun +@*/ +const char *jit_type_get_name(jit_type_t type, unsigned int index) +{ + if(!type || index >= type->num_components) + { + return 0; + } + else + { + return type->components[index].name; + } +} + +/*@ + * @deftypefun {unsigned int} jit_type_find_name (jit_type_t type, {const char *} name) + * Find the field/parameter index for a particular name. Returns + * @code{JIT_INVALID_NAME} if the name was not present. + * @end deftypefun +@*/ +unsigned int jit_type_find_name(jit_type_t type, const char *name) +{ + unsigned int index; + if(!type || !name) + { + return JIT_INVALID_NAME; + } + if(type->kind == JIT_TYPE_STRUCT || type->kind == JIT_TYPE_UNION || + type->kind == JIT_TYPE_SIGNATURE) + { + for(index = 0; index < type->num_components; ++index) + { + if(type->components[index].name && + !jit_strcmp(type->components[index].name, name)) + { + return index; + } + } + } + return JIT_INVALID_NAME; +} + +/*@ + * @deftypefun {unsigned int} jit_type_num_params (jit_type_t type) + * Get the number of parameters in a signature type. + * @end deftypefun +@*/ +unsigned int jit_type_num_params(jit_type_t type) +{ + if(!type || type->kind != JIT_TYPE_SIGNATURE) + { + return 0; + } + else + { + return type->num_components; + } +} + +/*@ + * @deftypefun jit_type_t jit_type_get_return (jit_type_t type) + * Get the return type from a signature type. Returns NULL if + * not a signature type. + * @end deftypefun +@*/ +jit_type_t jit_type_get_return(jit_type_t type) +{ + if(type) + { + if(type->kind == JIT_TYPE_SIGNATURE) + { + return type->sub_type; + } + } + return 0; +} + +/*@ + * @deftypefun jit_type_t jit_type_get_param (jit_type_t type, {unsigned int} param_index) + * Get a specific parameter from a signature type. Returns NULL + * if not a signature type or the index is out of range. + * @end deftypefun +@*/ +jit_type_t jit_type_get_param(jit_type_t type, unsigned int param_index) +{ + if(!type || param_index >= type->num_components) + { + return 0; + } + if(type->kind == JIT_TYPE_SIGNATURE) + { + return type->components[param_index].type; + } + return 0; +} + +/*@ + * @deftypefun jit_abi_t jit_type_get_abi (jit_type_t type) + * Get the ABI code from a signature type. Returns @code{jit_abi_cdecl} + * if not a signature type. + * @end deftypefun +@*/ +jit_abi_t jit_type_get_abi(jit_type_t type) +{ + if(type) + { + return (jit_abi_t)(type->abi); + } + else + { + return jit_abi_cdecl; + } +} + +/*@ + * @deftypefun jit_type_t jit_type_get_ref (jit_type_t type) + * Get the type that is referred to by a pointer type. Returns NULL + * if not a pointer type. + * @end deftypefun +@*/ +jit_type_t jit_type_get_ref(jit_type_t type) +{ + if(type) + { + if(type->kind == JIT_TYPE_PTR) + { + return type->sub_type; + } + } + return 0; +} + +/*@ + * @deftypefun jit_type_t jit_type_get_tagged_type (jit_type_t type) + * Get the type that underlies a tagged type. Returns NULL + * if not a tagged type. + * @end deftypefun +@*/ +jit_type_t jit_type_get_tagged_type(jit_type_t type) +{ + if(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + return type->sub_type; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun void jit_type_set_tagged_type (jit_type_t type, jit_type_t underlying) + * Set the type that underlies a tagged type. Ignored if @code{type} + * is not a tagged type. If @code{type} already has an underlying + * type, then the original is freed. + * + * This function is typically used to flesh out the body of a + * forward-declared type. The tag is used as a placeholder + * until the definition can be located. + * @end deftypefun +@*/ +void jit_type_set_tagged_type(jit_type_t type, jit_type_t underlying, + int incref) +{ + if(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + if(type->sub_type != underlying) + { + jit_type_free(type->sub_type); + if(incref) + { + type->sub_type = jit_type_copy(underlying); + } + else + { + type->sub_type = underlying; + } + } + } +} + +/*@ + * @deftypefun int jit_type_get_tagged_type (jit_type_t type) + * Get the kind of tag that is applied to a tagged type. Returns -1 + * if not a tagged type. + * @end deftypefun +@*/ +int jit_type_get_tagged_kind(jit_type_t type) +{ + if(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + return type->kind - JIT_TYPE_FIRST_TAGGED; + } + else + { + return -1; + } +} + +/*@ + * @deftypefun {void *} jit_type_get_tagged_data (jit_type_t type) + * Get the user data is associated with a tagged type. Returns NULL + * if not a tagged type. + * @end deftypefun +@*/ +void *jit_type_get_tagged_data(jit_type_t type) +{ + if(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + return ((struct jit_tagged_type *)type)->data; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun void jit_type_set_tagged_data (jit_type_t type, {void *} data, jit_meta_free_func free_fun) + * Set the user data is associated with a tagged type. The original data, + * if any, is freed. + * @end deftypefun +@*/ +void jit_type_set_tagged_data(jit_type_t type, void *data, + jit_meta_free_func free_func) +{ + if(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + struct jit_tagged_type *tagged = (struct jit_tagged_type *)type; + if(tagged->data != data) + { + if(tagged->free_func) + { + (*(tagged->free_func))(tagged->data); + } + tagged->data = data; + tagged->free_func = free_func; + } + } +} + +/*@ + * @deftypefun int jit_type_is_primitive (jit_type_t type) + * Determine if a type is primitive. + * @end deftypefun +@*/ +int jit_type_is_primitive(jit_type_t type) +{ + if(type) + { + return (type->kind <= JIT_TYPE_MAX_PRIMITIVE); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_type_is_struct (jit_type_t type) + * Determine if a type is a structure. + * @end deftypefun +@*/ +int jit_type_is_struct(jit_type_t type) +{ + if(type) + { + return (type->kind == JIT_TYPE_STRUCT); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_type_is_union (jit_type_t type) + * Determine if a type is a union. + * @end deftypefun +@*/ +int jit_type_is_union(jit_type_t type) +{ + if(type) + { + return (type->kind == JIT_TYPE_UNION); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_type_is_signature (jit_type_t type) + * Determine if a type is a function signature. + * @end deftypefun +@*/ +int jit_type_is_signature(jit_type_t type) +{ + if(type) + { + return (type->kind == JIT_TYPE_SIGNATURE); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_type_is_pointer (jit_type_t type) + * Determine if a type is a pointer. + * @end deftypefun +@*/ +int jit_type_is_pointer(jit_type_t type) +{ + if(type) + { + return (type->kind == JIT_TYPE_PTR); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_type_is_tagged (jit_type_t type) + * Determine if a type is a tagged type. + * @end deftypefun +@*/ +int jit_type_is_tagged(jit_type_t type) +{ + if(type) + { + return (type->kind >= JIT_TYPE_FIRST_TAGGED); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_nuint jit_type_best_alignment (void) + * Get the best alignment value for this platform. + * @end deftypefun +@*/ +jit_nuint jit_type_best_alignment(void) +{ + return JIT_BEST_ALIGNMENT; +} + +/*@ + * @deftypefun jit_type_t jit_type_normalize (jit_type_t type) + * Normalize a type to its basic numeric form. e.g. "jit_type_nint" is + * turned into "jit_type_int" or "jit_type_long", depending upon + * the underlying platform. Pointers are normalized like "jit_type_nint". + * If the type does not have a normalized form, it is left unchanged. + * + * Normalization is typically used prior to applying a binary numeric + * instruction, to make it easier to determine the common type. + * It will also remove tags from the specified type. + * @end deftypefun +@*/ +jit_type_t jit_type_normalize(jit_type_t type) +{ + while(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + /* Remove any tags that are attached to the type */ + type = type->sub_type; + } + if(!type) + { + return type; + } + if(type == jit_type_nint || type->kind == JIT_TYPE_PTR || + type->kind == JIT_TYPE_SIGNATURE) + { + #ifdef JIT_NATIVE_INT32 + return jit_type_int; + #else + return jit_type_long; + #endif + } + else if(type == jit_type_nuint) + { + #ifdef JIT_NATIVE_INT32 + return jit_type_uint; + #else + return jit_type_ulong; + #endif + } + else if(type == jit_type_nfloat) + { + if(sizeof(jit_nfloat) == sizeof(jit_float64)) + { + return jit_type_float64; + } + else if(sizeof(jit_nfloat) == sizeof(jit_float32)) + { + return jit_type_float32; + } + } + return type; +} + +/*@ + * @deftypefun jit_type_t jit_type_remove_tags (jit_type_t type) + * Remove tags from a type, and return the underlying type. + * This is different from normalization, which will also collapses + * native types to their basic numeric counterparts. + * @end deftypefun +@*/ +jit_type_t jit_type_remove_tags(jit_type_t type) +{ + while(type && type->kind >= JIT_TYPE_FIRST_TAGGED) + { + type = type->sub_type; + } + return type; +} + +/*@ + * @deftypefun jit_type_t jit_type_promote_int (jit_type_t type) + * If @code{type} is @code{jit_type_sbyte}, @code{jit_type_ubyte}, + * @code{jit_type_short}, or @code{jit_type_ushort}, then return + * @code{jit_type_int}. Otherwise return @code{type} as-is. + * @end deftypefun +@*/ +jit_type_t jit_type_promote_int(jit_type_t type) +{ + if(type == jit_type_sbyte || type == jit_type_ubyte || + type == jit_type_short || type == jit_type_ushort) + { + return jit_type_int; + } + else + { + return type; + } +} + +/*@ + * @deftypefun int jit_type_return_via_pointer (jit_type_t type) + * Determine if a type should be returned via a pointer if it appears + * as the return type in a signature. + * @end deftypefun +@*/ +int jit_type_return_via_pointer(jit_type_t type) +{ + extern unsigned char const _jit_apply_return_in_reg[]; + unsigned int size; + + /* Normalize the type first, just in case the structure is tagged */ + type = jit_type_normalize(type); + + /* Only structure and union types require special handling */ + if(!jit_type_is_struct(type) && !jit_type_is_union(type)) + { + return 0; + } + + /* Determine if the structure can be returned in a register */ + size = jit_type_get_size(type); + if(size >= 1 && size <= 64) + { + if((_jit_apply_return_in_reg[(size - 1) / 8] & + (1 << ((size - 1) % 8))) != 0) + { + return 0; + } + } + return 1; +} diff --git a/jit/jit-value.c b/jit/jit-value.c new file mode 100644 index 0000000..8bb7268 --- /dev/null +++ b/jit/jit-value.c @@ -0,0 +1,2380 @@ +/* + * jit-value.c - Functions for manipulating temporary values. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" + +/*@ + +@cindex jit-value.h + +Values form the backbone of the storage system in @code{libjit}. +Every value in the system, be it a constant, a local variable, +or a temporary result, is represented by an object of type +@code{jit_value_t}. The JIT then allocates registers or memory +locations to the values as appropriate. + +We will demonstrate how to use values with a simple example of +adding two local variables together and putting the result into a +third local variable. First, we allocate storage for the +three local variables: + +@example +value1 = jit_value_create(func, jit_type_int); +value2 = jit_value_create(func, jit_type_int); +value3 = jit_value_create(func, jit_type_int); +@end example + +Here, @code{func} is the function that we are building. To add +@code{value1} and @code{value2} and put the result into @code{value3}, +we use the following code: + +@example +temp = jit_insn_add(func, value1, value2); +jit_insn_store(func, value3, temp); +@end example + +The @code{jit_insn_add} function allocates a temporary value +(@code{temp}) and places the result of the addition into it. +The @code{jit_insn_store} function then stores the temporary +result into @code{value3}. + +You might be tempted to think that the above code is inefficient. +Why do we copy the result into a temporary variable first? +Why not put the result directly to @code{value3}? + +Behind the scenes, the JIT will typically optimize @code{temp} away, +resulting in the final code that you expect (i.e. @code{value3 = value1 + +value2}). It is simply easier to use @code{libjit} if all results +end up in temporary variables first, so that's what we do. + +Using temporary values, it is very easy to convert stack machine +bytecodes into JIT instructions. Consider the following Java +Virtual Machine bytecode (representing @code{value4 = value1 * value2 + +value3}): + +@example +iload 1 +iload 2 +imul +iload 3 +iadd +istore 4 +@end example + +Let us demonstrate how this code would be translated, instruction +by instruction. We assume that we have a @code{stack} available, +which keeps track of the temporary values in the system. We also +assume that @code{jit_value_t} objects representing the local variables +are already stored in an array called @code{locals}. + +@noindent +First, we load local variable 1 onto the stack: + +@example +stack[size++] = jit_insn_load(func, locals[1]); +@end example + +@noindent +We repeat this for local variable 2: + +@example +stack[size++] = jit_insn_load(func, locals[2]); +@end example + +@noindent +Now we pop these two values and push their multiplication: + +@example +stack[size - 2] = jit_insn_mul(func, stack[size - 2], stack[size - 1]); +--size; +@end example + +@noindent +Next, we need to push the value of local variable 3 and add it +to the product that we just computed: + +@example +stack[size++] = jit_insn_load(func, locals[3]); +stack[size - 2] = jit_insn_add(func, stack[size - 2], stack[size - 1]); +--size; +@end example + +@noindent +Finally, we store the result into local variable 4: + +@example +jit_insn_store(func, locals[4], stack[--size]); +@end example + +@noindent +Collecting up all of the above code, we get the following: + +@example +stack[size++] = jit_insn_load(func, locals[1]); +stack[size++] = jit_insn_load(func, locals[2]); +stack[size - 2] = jit_insn_mul(func, stack[size - 2], stack[size - 1]); +--size; +stack[size++] = jit_insn_load(func, locals[3]); +stack[size - 2] = jit_insn_add(func, stack[size - 2], stack[size - 1]); +--size; +jit_insn_store(func, locals[4], stack[--size]); +@end example + +The JIT will optimize away most of these temporary results, leaving +the final machine code that you expect. + +If the virtual machine was register-based, then a slightly different +translation strategy would be used. Consider the following code, +which computes @code{reg4 = reg1 * reg2 + reg3}, with the intermediate +result stored temporarily in @code{reg5}: + +@example +mul reg5, reg1, reg2 +add reg4, reg5, reg3 +@end example + +You would start by allocating value objects for all of the registers +in your system (with @code{jit_value_create}): + +@example +reg[1] = jit_value_create(func, jit_type_int); +reg[2] = jit_value_create(func, jit_type_int); +reg[3] = jit_value_create(func, jit_type_int); +reg[4] = jit_value_create(func, jit_type_int); +reg[5] = jit_value_create(func, jit_type_int); +@end example + +@noindent +Then, the virtual register machine code is translated as follows: + +@example +temp1 = jit_insn_mul(func, reg[1], reg[2]); +jit_insn_store(reg[5], temp1); +temp2 = jit_insn_add(func, reg[5], reg[3]); +jit_insn_store(reg[4], temp2); +@end example + +Each virtual register machine instruction turns into two @code{libjit} +function calls. The JIT will normally optimize away the temporary +results. If the value in @code{reg5} is not used further down the code, +then the JIT may also be able to optimize @code{reg5} away. + +The rest of this section describes the functions that are available +to create and manipulate values. + +@*/ + +/* + * Allocate a new value from a function's memory pool. + */ +static jit_value_t alloc_value(jit_function_t func, jit_type_t type) +{ + jit_value_t value; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + value = jit_memory_pool_alloc(&(func->builder->value_pool), + struct _jit_value); + if(!value) + { + return 0; + } + value->block = func->builder->current_block; + value->type = jit_type_copy(type); + value->reg = -1; + value->frame_offset = JIT_INVALID_FRAME_OFFSET; + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create (jit_function_t func, jit_type_t type) + * Create a new value in the context of a function's current block. + * The value initially starts off as a block-specific temporary. + * It will be converted into a function-wide local variable if + * it is ever referenced from a different block. Returns NULL + * if out of memory. + * + * Note: It isn't possible to refer to global variables directly using + * values. If you need to access a global variable, then load its + * address into a temporary and use @code{jit_insn_load_relative} + * or @code{jit_insn_store_relative} to manipulate it. It simplifies + * the JIT if it can assume that all values are local. + * @end deftypefun +@*/ +jit_value_t jit_value_create(jit_function_t func, jit_type_t type) +{ + jit_value_t value = alloc_value(func, type); + if(!value) + { + return 0; + } + value->is_temporary = 1; + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create_nint_constant (jit_function_t func, jit_type_t type, jit_nint const_value) + * Create a new native integer constant in the specified function. + * Returns NULL if out of memory. + * + * The @code{type} parameter indicates the actual type of the constant, + * if it happens to be something other than @code{jit_type_nint}. + * For example, the following will create an unsigned byte constant: + * + * @example + * value = jit_value_create_nint_constant(context, jit_type_ubyte, 128); + * @end example + * + * This function can be used to create constants of type @code{jit_type_sbyte}, + * @code{jit_type_ubyte}, @code{jit_type_short}, @code{jit_type_ushort}, + * @code{jit_type_int}, @code{jit_type_uint}, @code{jit_type_nint}, + * @code{jit_type_nuint}, and all pointer types. + * @end deftypefun +@*/ +jit_value_t jit_value_create_nint_constant + (jit_function_t func, jit_type_t type, jit_nint const_value) +{ + jit_value_t value; + jit_type_t stripped; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + if(!const_value) + { + /* Special cases: see if this is the NULL or zero constant */ + stripped = jit_type_remove_tags(type); + if(jit_type_is_pointer(stripped) || stripped == jit_type_nint) + { + if(func->builder->null_constant) + { + return func->builder->null_constant; + } + } + else if(stripped == jit_type_int) + { + if(func->builder->zero_constant) + { + return func->builder->zero_constant; + } + } + } + value = alloc_value(func, type); + if(!value) + { + return 0; + } + value->is_constant = 1; + value->is_nint_constant = 1; + value->address = const_value; + if(!const_value) + { + /* Special cases: see if we need to cache this constant for later */ + stripped = jit_type_remove_tags(type); + if(jit_type_is_pointer(stripped) || stripped == jit_type_nint) + { + func->builder->null_constant = value; + } + else if(stripped == jit_type_int) + { + func->builder->zero_constant = value; + } + } + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create_long_constant (jit_function_t func, jit_type_t type, jit_long const_value) + * Create a new 64-bit integer constant in the specified + * function. This can also be used to create constants of + * type @code{jit_type_ulong}. Returns NULL if out of memory. + * @end deftypefun +@*/ +jit_value_t jit_value_create_long_constant + (jit_function_t func, jit_type_t type, jit_long const_value) +{ + jit_value_t value = alloc_value(func, type); + if(!value) + { + return 0; + } + value->is_constant = 1; +#ifdef JIT_NATIVE_INT64 + value->address = (jit_nint)const_value; +#else + value->address = (jit_nint)jit_malloc(sizeof(jit_long)); + if(!(value->address)) + { + return 0; + } + *((jit_long *)(value->address)) = const_value; + value->free_address = 1; +#endif + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create_float32_constant (jit_function_t func, jit_type_t type, jit_float32 const_value) + * Create a new 32-bit floating-point constant in the specified + * function. Returns NULL if out of memory. + * @end deftypefun +@*/ +jit_value_t jit_value_create_float32_constant + (jit_function_t func, jit_type_t type, jit_float32 const_value) +{ + jit_value_t value = alloc_value(func, type); + if(!value) + { + return 0; + } + value->is_constant = 1; + value->address = (jit_nint)jit_malloc(sizeof(jit_float32)); + if(!(value->address)) + { + return 0; + } + *((jit_float32 *)(value->address)) = const_value; + value->free_address = 1; + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create_float64_constant (jit_function_t func, jit_type_t type, jit_float64 const_value) + * Create a new 64-bit floating-point constant in the specified + * function. Returns NULL if out of memory. + * @end deftypefun +@*/ +jit_value_t jit_value_create_float64_constant + (jit_function_t func, jit_type_t type, jit_float64 const_value) +{ + jit_value_t value = alloc_value(func, type); + if(!value) + { + return 0; + } + value->is_constant = 1; + value->address = (jit_nint)jit_malloc(sizeof(jit_float64)); + if(!(value->address)) + { + return 0; + } + *((jit_float64 *)(value->address)) = const_value; + value->free_address = 1; + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create_nfloat_constant (jit_function_t func, jit_type_t type, jit_nfloat const_value) + * Create a new native floating-point constant in the specified + * function. Returns NULL if out of memory. + * @end deftypefun +@*/ +jit_value_t jit_value_create_nfloat_constant + (jit_function_t func, jit_type_t type, jit_nfloat const_value) +{ + jit_value_t value = alloc_value(func, type); + if(!value) + { + return 0; + } + value->is_constant = 1; + value->address = (jit_nint)jit_malloc(sizeof(jit_nfloat)); + if(!(value->address)) + { + return 0; + } + *((jit_nfloat *)(value->address)) = const_value; + value->free_address = 1; + return value; +} + +/*@ + * @deftypefun jit_value_t jit_value_create_constant (jit_function_t func, const jit_constant *const_value) + * Create a new constant from a generic constant structure in the specified + * function. Returns NULL if out of memory or if the type in + * @code{const_value} is not suitable for a constant. + * @end deftypefun +@*/ +jit_value_t jit_value_create_constant + (jit_function_t func, const jit_constant_t *const_value) +{ + jit_type_t stripped = jit_type_normalize(const_value->type); + if(!stripped) + { + return 0; + } + switch(stripped->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + return jit_value_create_nint_constant + (func, const_value->type, const_value->un.int_value); + + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + return jit_value_create_nint_constant + (func, const_value->type, const_value->un.nint_value); + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + return jit_value_create_long_constant + (func, const_value->type, const_value->un.long_value); + + case JIT_TYPE_FLOAT32: + return jit_value_create_float32_constant + (func, const_value->type, const_value->un.float32_value); + + case JIT_TYPE_FLOAT64: + return jit_value_create_float64_constant + (func, const_value->type, const_value->un.float64_value); + + case JIT_TYPE_NFLOAT: + return jit_value_create_nfloat_constant + (func, const_value->type, const_value->un.nfloat_value); + } + return 0; +} + +/*@ + * @deftypefun jit_value_t jit_value_get_param (jit_function_t func, {unsigned int} param) + * Get the value that corresponds to a specified function parameter. + * Returns NULL if out of memory. + * @end deftypefun +@*/ +jit_value_t jit_value_get_param(jit_function_t func, unsigned int param) +{ + jit_type_t signature; + unsigned int num_params, current; + jit_value_t *values; + + /* Ensure that we have a builder for this function */ + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + + /* If we have already created the values, then exit immediately */ + values = func->builder->param_values; + if(values) + { + return values[param]; + } + + /* Create the values for the first time */ + signature = func->signature; + num_params = jit_type_num_params(signature); + values = (jit_value_t *)jit_calloc(num_params, sizeof(jit_value_t)); + if(!values) + { + return 0; + } + func->builder->param_values = values; + for(current = 0; current < num_params; ++current) + { + values[current] = jit_value_create + (func, jit_type_get_param(signature, current)); + if(values[current]) + { + /* The value belongs to the entry block, no matter + where it happens to be created */ + values[current]->block = func->builder->entry; + } + } + + /* Return the value block for the desired parameter */ + return values[param]; +} + +/*@ + * @deftypefun jit_value_t jit_value_get_struct_pointer (jit_function_t func) + * Get the value that contains the structure return pointer for + * a function. If the function does not have a structure return pointer + * (i.e. structures are returned in registers), then this returns NULL. + * @end deftypefun +@*/ +jit_value_t jit_value_get_struct_pointer(jit_function_t func) +{ + jit_type_t type; + jit_value_t value; + if(!_jit_function_ensure_builder(func)) + { + return 0; + } + type = jit_type_normalize(jit_type_get_return(func->signature)); + if(jit_type_is_struct(type) || jit_type_is_union(type)) + { + if(jit_type_return_via_pointer(type)) + { + if(!(func->builder->struct_return)) + { + type = jit_type_create_pointer(type, 1); + if(!type) + { + return 0; + } + value = jit_value_create(func, type); + func->builder->struct_return = value; + if(value) + { + /* The value belongs to the entry block, no matter + where it happens to be created */ + value->block = func->builder->entry; + } + jit_type_free(type); + } + return func->builder->struct_return; + } + } + return 0; +} + +/*@ + * @deftypefun int jit_value_is_temporary (jit_value_t value) + * Determine if a value is temporary. i.e. its scope extends + * over a single block within its function. + * @end deftypefun +@*/ +int jit_value_is_temporary(jit_value_t value) +{ + return value->is_temporary; +} + +/*@ + * @deftypefun int jit_value_is_local (jit_value_t value) + * Determine if a value is local. i.e. its scope extends + * over multiple blocks within its function. + * @end deftypefun +@*/ +int jit_value_is_local(jit_value_t value) +{ + return value->is_local; +} + +/*@ + * @deftypefun int jit_value_is_constant (jit_value_t value) + * Determine if a value is a constant. + * @end deftypefun +@*/ +int jit_value_is_constant(jit_value_t value) +{ + return value->is_constant; +} + +/*@ + * @deftypefun void jit_value_ref (jit_function_t func, jit_value_t value) + * Create a reference to the specified @code{value} from the current + * block in @code{func}. This will convert a temporary value into + * a local value if @code{value} is being referenced from a different + * block than its original. + * + * It is not necessary that @code{func} be the same function as the + * one where the value was originally created. It may be a nested + * function, referring to a local variable in its parent function. + * @end deftypefun +@*/ +void jit_value_ref(jit_function_t func, jit_value_t value) +{ + if(!_jit_function_ensure_builder(func)) + { + return; + } + if(value->is_temporary) + { + if(value->block->func != func) + { + /* Reference from a different function: local and addressable */ + value->is_temporary = 0; + value->is_local = 1; + value->is_addressable = 1; + value->live = 1; + + /* Mark the two functions as not leaves because we will need + them to set up proper frame pointers to allow us to access + the local variable across the nested function boundary */ + value->block->func->builder->non_leaf = 1; + func->builder->non_leaf = 1; + } + else if(value->block != func->builder->current_block) + { + /* Reference from another block in same function: local */ + value->is_temporary = 0; + value->is_local = 1; + value->live = 1; + } + } + else if(value->is_local && value->block->func != func) + { + /* Convert a previously local value into an addressable one */ + value->is_addressable = 1; + value->block->func->builder->non_leaf = 1; + func->builder->non_leaf = 1; + } +} + +/*@ + * @deftypefun void jit_value_set_volatile (jit_value_t value) + * Set a flag on a value to indicate that it is volatile. The contents + * of the value must always be reloaded from memory, never from a + * cached register copy. + * @end deftypefun +@*/ +void jit_value_set_volatile(jit_value_t value) +{ + value->is_volatile = 1; +} + +/*@ + * @deftypefun int jit_value_is_volatile (jit_value_t value) + * Determine if a value is volatile. + * @end deftypefun +@*/ +int jit_value_is_volatile(jit_value_t value) +{ + return value->is_volatile; +} + +/*@ + * @deftypefun void jit_value_set_addressable (jit_value_t value) + * Set a flag on a value to indicate that it is addressable. + * This should be used when you want to take the address of a + * value (e.g. @code{&variable} in C). The value is guaranteed + * to not be stored in a register across a function call. + * If you refer to a value from a nested function (@code{jit_value_ref}), + * then the value will be automatically marked as addressable. + * @end deftypefun +@*/ +void jit_value_set_addressable(jit_value_t value) +{ + value->is_addressable = 1; +} + +/*@ + * @deftypefun int jit_value_is_addressable (jit_value_t value) + * Determine if a value is addressable. + * @end deftypefun +@*/ +int jit_value_is_addressable(jit_value_t value) +{ + return value->is_addressable; +} + +/*@ + * @deftypefun jit_type_t jit_value_get_type (jit_value_t value) + * Get the type that is associated with a value. + * @end deftypefun +@*/ +jit_type_t jit_value_get_type(jit_value_t value) +{ + if(value) + { + return value->type; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_function_t jit_value_get_function (jit_value_t value) + * Get the function which owns a particular @code{value}. + * @end deftypefun +@*/ +jit_function_t jit_value_get_function(jit_value_t value) +{ + if(value) + { + return value->block->func; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_block_t jit_value_get_block (jit_value_t value) + * Get the block which owns a particular @code{value}. + * @end deftypefun +@*/ +jit_block_t jit_value_get_block(jit_value_t value) +{ + if(value) + { + return value->block; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_context_t jit_value_get_context (jit_value_t value) + * Get the context which owns a particular @code{value}. + * @end deftypefun +@*/ +jit_context_t jit_value_get_context(jit_value_t value) +{ + if(value) + { + return value->block->func->context; + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_constant_t jit_value_get_constant (jit_value_t value) + * Get the constant value within a particular @code{value}. The returned + * structure's @code{type} field will be @code{jit_type_void} if + * @code{value} is not a constant. + * @end deftypefun +@*/ +jit_constant_t jit_value_get_constant(jit_value_t value) +{ + jit_constant_t result; + if(!value || !(value->is_constant)) + { + result.type = jit_type_void; + return result; + } + result.type = value->type; + switch(jit_type_normalize(value->type)->kind) + { + case JIT_TYPE_SBYTE: + case JIT_TYPE_UBYTE: + case JIT_TYPE_SHORT: + case JIT_TYPE_USHORT: + case JIT_TYPE_INT: + case JIT_TYPE_UINT: + { + result.un.int_value = (jit_int)(value->address); + } + break; + + case JIT_TYPE_NINT: + case JIT_TYPE_NUINT: + { + result.un.nint_value = value->address; + } + break; + + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + #ifdef JIT_NATIVE_INT64 + result.un.long_value = (jit_long)(value->address); + #else + result.un.long_value = *((jit_long *)(value->address)); + #endif + } + break; + + case JIT_TYPE_FLOAT32: + { + result.un.float32_value = *((jit_float32 *)(value->address)); + } + break; + + case JIT_TYPE_FLOAT64: + { + result.un.float64_value = *((jit_float64 *)(value->address)); + } + break; + + case JIT_TYPE_NFLOAT: + { + result.un.nfloat_value = *((jit_nfloat *)(value->address)); + } + break; + + default: + { + result.type = jit_type_void; + } + break; + } + return result; +} + +/*@ + * @deftypefun jit_nint jit_value_get_nint_constant (jit_value_t value) + * Get the constant value within a particular @code{value}, assuming + * that its type is compatible with @code{jit_type_nint}. + * @end deftypefun +@*/ +jit_nint jit_value_get_nint_constant(jit_value_t value) +{ + if(value->is_nint_constant) + { + return (jit_nint)(value->address); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun jit_nint jit_value_get_nint_constant (jit_value_t value) + * Get the constant value within a particular @code{value}, assuming + * that its type is compatible with @code{jit_type_nint}. + * @end deftypefun +@*/ +jit_long jit_value_get_long_constant(jit_value_t value) +{ + if(!(value->is_constant)) + { + return 0; + } + switch(jit_type_normalize(value->type)->kind) + { + case JIT_TYPE_LONG: + case JIT_TYPE_ULONG: + { + #ifdef JIT_NATIVE_INT64 + return (jit_long)(value->address); + #else + return *((jit_long *)(value->address)); + #endif + } + /* Not reached */ + } + return 0; +} + +/*@ + * @deftypefun jit_float32 jit_value_get_float32_constant (jit_value_t value) + * Get the constant value within a particular @code{value}, assuming + * that its type is compatible with @code{jit_type_float32}. + * @end deftypefun +@*/ +jit_float32 jit_value_get_float32_constant(jit_value_t value) +{ + if(!(value->is_constant)) + { + return (jit_float32)0.0; + } + if(jit_type_normalize(value->type)->kind == JIT_TYPE_FLOAT32) + { + return *((jit_float32 *)(value->address)); + } + return (jit_float32)0.0; +} + +/*@ + * @deftypefun jit_float64 jit_value_get_float64_constant (jit_value_t value) + * Get the constant value within a particular @code{value}, assuming + * that its type is compatible with @code{jit_type_float64}. + * @end deftypefun +@*/ +jit_float64 jit_value_get_float64_constant(jit_value_t value) +{ + if(!(value->is_constant)) + { + return (jit_float64)0.0; + } + if(jit_type_normalize(value->type)->kind == JIT_TYPE_FLOAT64) + { + return *((jit_float64 *)(value->address)); + } + return (jit_float64)0.0; +} + +/*@ + * @deftypefun jit_nfloat jit_value_get_nfloat_constant (jit_value_t value) + * Get the constant value within a particular @code{value}, assuming + * that its type is compatible with @code{jit_type_nfloat}. + * @end deftypefun +@*/ +jit_nfloat jit_value_get_nfloat_constant(jit_value_t value) +{ + if(!(value->is_constant)) + { + return (jit_nfloat)0.0; + } + if(jit_type_normalize(value->type)->kind == JIT_TYPE_FLOAT64) + { + return *((jit_nfloat *)(value->address)); + } + return (jit_nfloat)0.0; +} + +/*@ + * @deftypefun int jit_constant_convert ({jit_constant_t *} result, {const jit_constant_t *} value, jit_type_t type, int overflow_check) + * Convert a the constant @code{value} into a new @code{type}, and + * return its value in @code{result}. Returns zero if the conversion + * is not possible, usually due to overflow. + * @end deftypefun +@*/ +int jit_constant_convert + (jit_constant_t *result, const jit_constant_t *value, + jit_type_t type, int overflow_check) +{ + jit_type_t srctype; + jit_type_t desttype; + + /* Normalize the source and destination types. The source type + is also promoted, to reduce the number of cases in the + inner switch statements below */ + if(!result || !value) + { + return 0; + } + srctype = jit_type_promote_int(jit_type_normalize(value->type)); + if(!srctype) + { + return 0; + } + desttype = jit_type_normalize(type); + if(!desttype) + { + return 0; + } + + /* Determine what kind of conversion to perform */ + result->type = type; + switch(desttype->kind) + { + case JIT_TYPE_SBYTE: + { + /* Convert to a signed 8-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + if(overflow_check) + { + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + value->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte(value->un.int_value); + } + } + break; + + case JIT_TYPE_UINT: + { + if(overflow_check) + { + if(!jit_uint_to_int_ovf + (&(result->un.int_value), + value->un.uint_value)) + { + return 0; + } + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte(value->un.int_value); + } + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_int_ovf + (&(result->un.int_value), + value->un.long_value)) + { + return 0; + } + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte + (jit_long_to_int(value->un.long_value)); + } + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_int_ovf + (&(result->un.int_value), + value->un.ulong_value)) + { + return 0; + } + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte + (jit_ulong_to_int(value->un.ulong_value)); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float32_value)) + { + return 0; + } + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte + (jit_nfloat_to_int(value->un.float32_value)); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float64_value)) + { + return 0; + } + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte + (jit_nfloat_to_int(value->un.float64_value)); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.nfloat_value)) + { + return 0; + } + if(!jit_int_to_sbyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_sbyte + (jit_nfloat_to_int(value->un.nfloat_value)); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_UBYTE: + { + /* Convert to an unsigned 8-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + if(overflow_check) + { + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + value->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte(value->un.int_value); + } + } + break; + + case JIT_TYPE_UINT: + { + if(overflow_check) + { + if(!jit_uint_to_int_ovf + (&(result->un.int_value), + value->un.uint_value)) + { + return 0; + } + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte(value->un.int_value); + } + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_int_ovf + (&(result->un.int_value), + value->un.long_value)) + { + return 0; + } + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte + (jit_long_to_int(value->un.long_value)); + } + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_int_ovf + (&(result->un.int_value), + value->un.ulong_value)) + { + return 0; + } + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte + (jit_ulong_to_int(value->un.ulong_value)); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float32_value)) + { + return 0; + } + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte + (jit_nfloat_to_int(value->un.float32_value)); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float64_value)) + { + return 0; + } + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte + (jit_nfloat_to_int(value->un.float64_value)); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.nfloat_value)) + { + return 0; + } + if(!jit_int_to_ubyte_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ubyte + (jit_nfloat_to_int(value->un.nfloat_value)); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_SHORT: + { + /* Convert to a signed 16-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + if(overflow_check) + { + if(!jit_int_to_short_ovf + (&(result->un.int_value), + value->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short(value->un.int_value); + } + } + break; + + case JIT_TYPE_UINT: + { + if(overflow_check) + { + if(!jit_uint_to_int_ovf + (&(result->un.int_value), + value->un.uint_value)) + { + return 0; + } + if(!jit_int_to_short_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short(value->un.int_value); + } + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_int_ovf + (&(result->un.int_value), + value->un.long_value)) + { + return 0; + } + if(!jit_int_to_short_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short + (jit_long_to_int(value->un.long_value)); + } + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_int_ovf + (&(result->un.int_value), + value->un.ulong_value)) + { + return 0; + } + if(!jit_int_to_short_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short + (jit_ulong_to_int(value->un.ulong_value)); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float32_value)) + { + return 0; + } + if(!jit_int_to_short_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short + (jit_nfloat_to_int(value->un.float32_value)); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float64_value)) + { + return 0; + } + if(!jit_int_to_short_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short + (jit_nfloat_to_int(value->un.float64_value)); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.nfloat_value)) + { + return 0; + } + if(!jit_int_to_short_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_short + (jit_nfloat_to_int(value->un.nfloat_value)); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_USHORT: + { + /* Convert to an unsigned 16-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + if(overflow_check) + { + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + value->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort(value->un.int_value); + } + } + break; + + case JIT_TYPE_UINT: + { + if(overflow_check) + { + if(!jit_uint_to_int_ovf + (&(result->un.int_value), + value->un.uint_value)) + { + return 0; + } + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort(value->un.int_value); + } + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_int_ovf + (&(result->un.int_value), + value->un.long_value)) + { + return 0; + } + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort + (jit_long_to_int(value->un.long_value)); + } + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_int_ovf + (&(result->un.int_value), + value->un.ulong_value)) + { + return 0; + } + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort + (jit_ulong_to_int(value->un.ulong_value)); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float32_value)) + { + return 0; + } + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort + (jit_nfloat_to_int(value->un.float32_value)); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float64_value)) + { + return 0; + } + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort + (jit_nfloat_to_int(value->un.float64_value)); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.nfloat_value)) + { + return 0; + } + if(!jit_int_to_ushort_ovf + (&(result->un.int_value), + result->un.int_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_int_to_ushort + (jit_nfloat_to_int(value->un.nfloat_value)); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_INT: + { + /* Convert to a signed 32-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + result->un.int_value = value->un.int_value; + } + break; + + case JIT_TYPE_UINT: + { + if(overflow_check) + { + if(!jit_uint_to_int_ovf + (&(result->un.int_value), + value->un.uint_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_uint_to_int(value->un.uint_value); + } + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_int_ovf + (&(result->un.int_value), + value->un.long_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_long_to_int(value->un.long_value); + } + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_int_ovf + (&(result->un.int_value), + value->un.ulong_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_ulong_to_int(value->un.ulong_value); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float32_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_nfloat_to_int(value->un.float32_value); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.float64_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_nfloat_to_int(value->un.float64_value); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_int_ovf + (&(result->un.int_value), + value->un.nfloat_value)) + { + return 0; + } + } + else + { + result->un.int_value = + jit_nfloat_to_int(value->un.nfloat_value); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_UINT: + { + /* Convert to an unsigned 32-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + if(overflow_check) + { + if(!jit_int_to_uint_ovf + (&(result->un.uint_value), + value->un.uint_value)) + { + return 0; + } + } + else + { + result->un.uint_value = + jit_int_to_uint(value->un.int_value); + } + } + break; + + case JIT_TYPE_UINT: + { + result->un.uint_value = value->un.uint_value; + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_uint_ovf + (&(result->un.uint_value), + value->un.long_value)) + { + return 0; + } + } + else + { + result->un.uint_value = + jit_long_to_uint(value->un.long_value); + } + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_uint_ovf + (&(result->un.uint_value), + value->un.ulong_value)) + { + return 0; + } + } + else + { + result->un.uint_value = + jit_ulong_to_uint(value->un.ulong_value); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_uint_ovf + (&(result->un.uint_value), + value->un.float32_value)) + { + return 0; + } + } + else + { + result->un.uint_value = + jit_nfloat_to_uint(value->un.float32_value); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_uint_ovf + (&(result->un.uint_value), + value->un.float64_value)) + { + return 0; + } + } + else + { + result->un.uint_value = + jit_nfloat_to_uint(value->un.float64_value); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_uint_ovf + (&(result->un.uint_value), + value->un.nfloat_value)) + { + return 0; + } + } + else + { + result->un.uint_value = + jit_nfloat_to_uint(value->un.nfloat_value); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_LONG: + { + /* Convert to a signed 64-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + result->un.long_value = + jit_int_to_long(value->un.int_value); + } + break; + + case JIT_TYPE_UINT: + { + result->un.long_value = + jit_uint_to_long(value->un.int_value); + } + break; + + case JIT_TYPE_LONG: + { + result->un.long_value = value->un.long_value; + } + break; + + case JIT_TYPE_ULONG: + { + if(overflow_check) + { + if(!jit_ulong_to_long_ovf + (&(result->un.long_value), + value->un.ulong_value)) + { + return 0; + } + } + else + { + result->un.long_value = + jit_ulong_to_long(value->un.ulong_value); + } + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_long_ovf + (&(result->un.long_value), + value->un.float32_value)) + { + return 0; + } + } + else + { + result->un.long_value = + jit_nfloat_to_long(value->un.float32_value); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_long_ovf + (&(result->un.long_value), + value->un.float64_value)) + { + return 0; + } + } + else + { + result->un.long_value = + jit_nfloat_to_long(value->un.float64_value); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_long_ovf + (&(result->un.long_value), + value->un.nfloat_value)) + { + return 0; + } + } + else + { + result->un.long_value = + jit_nfloat_to_long(value->un.nfloat_value); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_ULONG: + { + /* Convert to an unsigned 64-bit integer */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + if(overflow_check) + { + if(!jit_int_to_ulong_ovf + (&(result->un.ulong_value), + value->un.int_value)) + { + return 0; + } + } + else + { + result->un.ulong_value = + jit_int_to_ulong(value->un.int_value); + } + } + break; + + case JIT_TYPE_UINT: + { + result->un.ulong_value = + jit_uint_to_ulong(value->un.uint_value); + } + break; + + case JIT_TYPE_LONG: + { + if(overflow_check) + { + if(!jit_long_to_ulong_ovf + (&(result->un.ulong_value), + value->un.long_value)) + { + return 0; + } + } + else + { + result->un.ulong_value = + jit_long_to_ulong(value->un.long_value); + } + } + break; + + case JIT_TYPE_ULONG: + { + result->un.ulong_value = value->un.ulong_value; + } + break; + + case JIT_TYPE_FLOAT32: + { + if(overflow_check) + { + if(!jit_nfloat_to_ulong_ovf + (&(result->un.ulong_value), + value->un.float32_value)) + { + return 0; + } + } + else + { + result->un.ulong_value = + jit_nfloat_to_ulong(value->un.float32_value); + } + } + break; + + case JIT_TYPE_FLOAT64: + { + if(overflow_check) + { + if(!jit_nfloat_to_ulong_ovf + (&(result->un.ulong_value), + value->un.float64_value)) + { + return 0; + } + } + else + { + result->un.ulong_value = + jit_nfloat_to_ulong(value->un.float64_value); + } + } + break; + + case JIT_TYPE_NFLOAT: + { + if(overflow_check) + { + if(!jit_nfloat_to_ulong_ovf + (&(result->un.ulong_value), + value->un.nfloat_value)) + { + return 0; + } + } + else + { + result->un.ulong_value = + jit_nfloat_to_ulong(value->un.nfloat_value); + } + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_FLOAT32: + { + /* Convert to a 32-bit float */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + result->un.float32_value = + jit_nfloat_to_float32 + (jit_int_to_nfloat(value->un.int_value)); + } + break; + + case JIT_TYPE_UINT: + { + result->un.float32_value = + jit_nfloat_to_float32 + (jit_uint_to_nfloat(value->un.uint_value)); + } + break; + + case JIT_TYPE_LONG: + { + result->un.float32_value = + jit_nfloat_to_float32 + (jit_long_to_nfloat(value->un.long_value)); + } + break; + + case JIT_TYPE_ULONG: + { + result->un.float32_value = + jit_nfloat_to_float32 + (jit_ulong_to_nfloat(value->un.ulong_value)); + } + break; + + case JIT_TYPE_FLOAT32: + { + result->un.float32_value = value->un.float32_value; + } + break; + + case JIT_TYPE_FLOAT64: + { + result->un.float32_value = + jit_nfloat_to_float32(value->un.float64_value); + } + break; + + case JIT_TYPE_NFLOAT: + { + result->un.float32_value = + jit_nfloat_to_float32(value->un.nfloat_value); + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_FLOAT64: + { + /* Convert to a 64-bit float */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + result->un.float64_value = + jit_nfloat_to_float64 + (jit_int_to_nfloat(value->un.int_value)); + } + break; + + case JIT_TYPE_UINT: + { + result->un.float64_value = + jit_nfloat_to_float64 + (jit_uint_to_nfloat(value->un.uint_value)); + } + break; + + case JIT_TYPE_LONG: + { + result->un.float64_value = + jit_nfloat_to_float64 + (jit_long_to_nfloat(value->un.long_value)); + } + break; + + case JIT_TYPE_ULONG: + { + result->un.float64_value = + jit_nfloat_to_float64 + (jit_ulong_to_nfloat(value->un.ulong_value)); + } + break; + + case JIT_TYPE_FLOAT32: + { + result->un.float64_value = + jit_nfloat_to_float64(value->un.float32_value); + } + break; + + case JIT_TYPE_FLOAT64: + { + result->un.float64_value = value->un.float64_value; + } + break; + + case JIT_TYPE_NFLOAT: + { + result->un.float64_value = + jit_nfloat_to_float64(value->un.nfloat_value); + } + break; + + default: return 0; + } + } + break; + + case JIT_TYPE_NFLOAT: + { + /* Convert to a native float */ + switch(srctype->kind) + { + case JIT_TYPE_INT: + { + result->un.nfloat_value = + jit_int_to_nfloat(value->un.int_value); + } + break; + + case JIT_TYPE_UINT: + { + result->un.nfloat_value = + jit_uint_to_nfloat(value->un.uint_value); + } + break; + + case JIT_TYPE_LONG: + { + result->un.nfloat_value = + jit_long_to_nfloat(value->un.long_value); + } + break; + + case JIT_TYPE_ULONG: + { + result->un.nfloat_value = + jit_ulong_to_nfloat(value->un.ulong_value); + } + break; + + case JIT_TYPE_FLOAT32: + { + result->un.nfloat_value = + jit_float32_to_nfloat(value->un.float32_value); + } + break; + + case JIT_TYPE_FLOAT64: + { + result->un.nfloat_value = + jit_float64_to_nfloat(value->un.float64_value); + } + break; + + case JIT_TYPE_NFLOAT: + { + result->un.nfloat_value = value->un.nfloat_value; + } + break; + + default: return 0; + } + } + break; + + default: return 0; + } + return 1; +} + +void _jit_value_free(void *_value) +{ + jit_value_t value = (jit_value_t)_value; + jit_type_free(value->type); + if(value->free_address && value->address) + { + /* We need to free the memory for a large constant */ + jit_free((void *)(value->address)); + } +} diff --git a/jit/jit-walk.c b/jit/jit-walk.c new file mode 100644 index 0000000..a55aacb --- /dev/null +++ b/jit/jit-walk.c @@ -0,0 +1,229 @@ +/* + * jit-walk.c - Routines for performing native stack walking. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include "jit-internal.h" +#include "jit-apply-rules.h" + +/* + * The routines here are system specific to a large extent, + * but we can avoid a lot of the nastiness using gcc builtins. + * It is highly recommended that you use gcc to build libjit. + * + * The following macros may need to be tweaked on some platforms. + */ + +/* + * Some platforms store the return address in an altered form + * (e.g. an offset rather than a pointer). We use this macro to + * fix such address values. + */ +#if defined(__GNUC__) +#define jit_fix_return_address(x) (__builtin_extract_return_addr((x))) +#else +#define jit_fix_return_address(x) (x) +#endif + +#if JIT_APPLY_BROKEN_FRAME_BUILTINS == 0 + +/* + * Extract the next frame pointer in the chain. + */ +#define jit_next_frame_pointer(x) \ + (*((void **)(((unsigned char *)(x)) + JIT_APPLY_PARENT_FRAME_OFFSET))) + +/* + * Extract the return address from a particular frame. + */ +#define jit_extract_return_address(x) \ + (*((void **)(((unsigned char *)(x)) + JIT_APPLY_RETURN_ADDRESS_OFFSET))) + +#else /* JIT_APPLY_BROKEN_FRAME_BUILTINS */ + +/* + * Extract the next frame pointer in the chain. + */ +#define jit_next_frame_pointer(x) 0 + +/* + * Extract the return address from a particular frame. + */ +#define jit_extract_return_address(x) 0 + +#endif /* JIT_APPLY_BROKEN_FRAME_BUILTINS */ + +/* + * Fetch the starting frame address if the caller did not supply it + * (probably because the caller wasn't compiled with gcc). The address + * that we want is actually one frame out from where we are at the moment. + */ +#if defined(__GNUC__) +#define jit_get_starting_frame() \ + do { \ + start = __builtin_frame_address(0); \ + if(start) \ + { \ + start = jit_next_frame_pointer(start); \ + } \ + } while (0) +#elif defined(_MSC_VER) && defined(_M_IX86) +#define jit_get_starting_frame() \ + __asm \ + { \ + __asm mov eax, [ebp] \ + __asm mov dword ptr start, eax \ + } +#else +#define jit_get_starting_frame() do { ; } while (0) +#endif + +/*@ + +@section Stack walking +@cindex Stack walking +@cindex jit-walk.h + +The functions in @code{} allow the caller to walk +up the native execution stack, inspecting frames and return addresses. + +@*/ + +/*@ + * @deftypefun {void *} jit_get_frame_address (unsigned int n) + * Get the frame address for the call frame @code{n} levels up + * the stack. Setting @code{n} to zero will retrieve the frame + * address for the current function. Returns NULL if it isn't + * possible to retrieve the address of the specified frame. + * @end deftypefun + * + * @deftypefun {void *} jit_get_current_frame (void) + * Get the frame address for the current function. This may be more + * efficient on some platforms than using @code{jit_get_frame_address(0)}. + * Returns NULL if it isn't possible to retrieve the address of + * the current frame. + * @end deftypefun +@*/ +void *_jit_get_frame_address(void *start, unsigned int n) +{ + /* Fetch the starting frame address if the caller did not supply it */ + if(!start) + { + jit_get_starting_frame(); + } + + /* Scan up the stack until we find the frame we want */ + while(start != 0 && n > 0) + { + start = jit_next_frame_pointer(start); + --n; + } + return start; +} + +/*@ + * @deftypefun {void *} jit_get_next_frame_address ({void *} frame) + * Get the address of the next frame up the stack from @code{frame}. + * Returns NULL if it isn't possible to retrieve the address of + * the next frame up the stack. + * @end deftypefun +@*/ +void *jit_get_next_frame_address(void *frame) +{ + if(frame) + { + return jit_next_frame_pointer(frame); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun {void *} jit_get_return_address ({void *} frame) + * Get the return address from a specified frame. The address + * represents the place where execution returns to when the + * specified frame exits. Returns NULL if it isn't possible + * to retrieve the return address of the specified frame. + * @end deftypefun + * + * @deftypefun {void *} jit_get_current_return (void) + * Get the return address for the current function. This may be more + * efficient on some platforms than using @code{jit_get_return_address(0)}. + * Returns NULL if it isn't possible to retrieve the return address of + * the current frame. + * @end deftypefun +@*/ +void *_jit_get_return_address(void *frame, void *frame0, void *return0) +{ + /* If the caller was compiled with gcc, it may have already figured + out the return address for us using builtin gcc facilities */ + if(frame && frame == frame0) + { + return jit_fix_return_address(return0); + } + else if(frame) + { + return jit_fix_return_address(jit_extract_return_address(frame)); + } + else + { + return 0; + } +} + +/*@ + * @deftypefun int jit_frame_contains_crawl_mark ({void *}frame, {jit_crawl_mark_t *} mark) + * Determine if the stack frame that resides just above @code{frame} + * contains a local variable whose address is @code{mark}. The @code{mark} + * parameter should be the address of a local variable that is declared with + * @code{jit_declare_crawl_mark(name)}. + * + * Crawl marks are used internally by libjit to determine where control + * passes between JIT'ed and ordinary code during an exception throw. + * They can also be used to mark frames that have special security + * conditions associated with them. + * @end deftypefun +@*/ +int jit_frame_contains_crawl_mark(void *frame, jit_crawl_mark_t *mark) +{ + void *markptr = (void *)mark; + void *next; + if(!frame) + { + /* We don't have a frame to check against */ + return 0; + } + next = jit_next_frame_pointer(frame); + if(!next) + { + /* We are at the top of the stack crawl */ + return 0; + } + if(frame <= next) + { + /* The stack grows downwards in memory */ + return (markptr >= frame && markptr < next); + } + else + { + /* The stack grows upwards in memory */ + return (markptr >= next && markptr < frame); + } +} diff --git a/jitplus/.cvsignore b/jitplus/.cvsignore new file mode 100644 index 0000000..051d1bd --- /dev/null +++ b/jitplus/.cvsignore @@ -0,0 +1,3 @@ +Makefile +Makefile.in +.deps diff --git a/jitplus/Makefile.am b/jitplus/Makefile.am new file mode 100644 index 0000000..6df7893 --- /dev/null +++ b/jitplus/Makefile.am @@ -0,0 +1,9 @@ + +lib_LIBRARIES = libjitplus.a + +libjitplus_a_SOURCES = \ + jit-plus-context.cpp \ + jit-plus-function.cpp \ + jit-plus-value.cpp + +AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir) diff --git a/jitplus/jit-plus-context.cpp b/jitplus/jit-plus-context.cpp new file mode 100644 index 0000000..a7a6ca0 --- /dev/null +++ b/jitplus/jit-plus-context.cpp @@ -0,0 +1,79 @@ +/* + * jit-plus-context.cpp - C++ wrapper for JIT contexts. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include + +/*@ + +The @code{jit_context} class provides a C++ counterpart to the +C @code{jit_context_t} type. @xref{Initialization}, for more +information on creating and managing contexts. + +@*/ + +/*@ + * @defop Constructor jit_context jit_context () + * Construct a new JIT context. This is equivalent to calling + * @code{jit_context_create} in the C API. The raw C context is + * destroyed when the @code{jit_context} object is destructed. + * @end defop +@*/ +jit_context::jit_context() +{ + jit_init(); + context = jit_context_create(); + copied = 0; +} + +/*@ + * @defop Constructor jit_context jit_context (jit_context_t context) + * Construct a new JIT context by wrapping up an existing raw C context. + * This is useful for importing a context from third party C code + * into a program that prefers to use C++. + * + * When you this form of construction, @code{jit_context_destroy} + * will not be called on the context when the @code{jit_context} + * object is destructed. You will need to arrange for that manually. + * @end defop +@*/ +jit_context::jit_context(jit_context_t context) +{ + this->context = context; + this->copied = 1; +} + +/*@ + * @defop Destructor jit_context ~jit_context () + * Destruct a JIT context. + * @end defop +@*/ +jit_context::~jit_context() +{ + if(!copied) + { + jit_context_destroy(context); + } +} + +/*@ + * @deftypemethod jit_context jit_context_t raw () const + * Get the raw C context pointer that underlies this object. + * @end deftypemethod +@*/ diff --git a/jitplus/jit-plus-function.cpp b/jitplus/jit-plus-function.cpp new file mode 100644 index 0000000..cf858d4 --- /dev/null +++ b/jitplus/jit-plus-function.cpp @@ -0,0 +1,1159 @@ +/* + * jit-plus-function.cpp - C++ wrapper for JIT functions. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include +#include +#ifdef HAVE_STDARG_H + #include +#elif HAVE_VARARGS_H + #include +#endif + +/*@ + +The @code{jit_function} class provides a C++ counterpart to the +C @code{jit_function_t} type. @xref{Functions}, for more information +on creating and managing functions. + +The @code{jit_function} class also provides a large number of methods +for creating the instructions within a function body. @xref{Instructions}, +for more information on creating and managing instructions. + +@*/ + +#define JITPP_MAPPING 20000 + +class jit_build_exception +{ +public: + jit_build_exception(int result) { this->result = result; } + ~jit_build_exception() {} + + int result; +}; + +jit_type_t const jit_function::end_params = (jit_type_t)0; + +/*@ + * @defop Constructor jit_function jit_function ({jit_context&} context, jit_type_t signature) + * Constructs a new function handler with the specified @code{signature} in + * the given @code{context}. It then calls @code{create(signature)} to + * create the actual function. + * @end defop +@*/ +jit_function::jit_function(jit_context& context, jit_type_t signature) +{ + // Save the context for the "create" method. + this->context = context.raw(); + this->func = 0; + + // Create the function. + create(signature); +} + +/*@ + * @defop Constructor jit_function jit_function ({jit_context&} context) + * Constructs a new function handler in the specified @code{context}. + * The actual function is not created until you call @code{create()}. + * @end defop +@*/ +jit_function::jit_function(jit_context& context) +{ + // Save the context, but don't create the function yet. + this->context = context.raw(); + this->func = 0; +} + +/*@ + * @defop Constructor jit_function jit_function (jit_function_t func) + * Constructs a new function handler and wraps it around the specified + * raw C @code{jit_function_t} object. This can be useful for layering + * the C++ on-demand building facility on top of an existing C function. + * @end defop +@*/ +jit_function::jit_function(jit_function_t func) +{ + this->context = jit_function_get_context(func); + this->func = func; + if(func) + { + jit_context_build_start(context); + jit_function_set_meta(func, JITPP_MAPPING, (void *)this, 0, 0); + register_on_demand(); + jit_context_build_end(context); + } +} + +/*@ + * @defop Destructor jit_function ~jit_function () + * Destroy this function handler. The raw function will persist + * until the context is destroyed. + * @end defop +@*/ +jit_function::~jit_function() +{ + if(func) + { + jit_context_build_start(context); + jit_function_free_meta(func, JITPP_MAPPING); + jit_context_build_end(context); + } +} + +/*@ + * @deftypemethod jit_function jit_function_t raw () const + * Get the raw C @code{jit_function_t} value that underlies this object. + * @end deftypemethod + * + * @deftypemethod jit_function int is_valid () const + * Determine if the raw C @code{jit_function_t} value that + * underlies this object is valid. + * @end deftypemethod +@*/ + +/*@ + * @deftypemethod jit_function {static jit_function *} from_raw (jit_function_t func) + * Find the C++ @code{jit_function} object that is associated with a + * raw C @code{jit_function_t} pointer. Returns NULL if there is + * no such object. + * @end deftypemethod +@*/ +jit_function *jit_function::from_raw(jit_function_t func) +{ + return (jit_function *)jit_function_get_meta(func, JITPP_MAPPING); +} + +/*@ + * @deftypemethod jit_function jit_type_t signature () const + * Get the signature type for this function. + * @end deftypemethod +@*/ + +/*@ + * @deftypemethod jit_function void create (jit_type_t signature) + * Create this function if it doesn't already exist. + * @end deftypemethod +@*/ +void jit_function::create(jit_type_t signature) +{ + // Bail out if the function is already created. + if(func) + { + return; + } + + // Lock down the context. + jit_context_build_start(context); + + // Create the new function. + func = jit_function_create(context, signature); + if(!func) + { + jit_context_build_end(context); + return; + } + + // Store this object's pointer on the raw function so that we can + // map the raw function back to this object later. + jit_function_set_meta(func, JITPP_MAPPING, (void *)this, 0, 0); + + // Register us as the on-demand compiler. + register_on_demand(); + + // Unlock the context. + jit_context_build_end(context); +} + +/*@ + * @deftypemethod jit_function void create () + * Create this function if it doesn't already exist. This version will + * call the virtual @code{create_signature()} method to obtain the + * signature from the subclass. + * @end deftypemethod +@*/ +void jit_function::create() +{ + if(!func) + { + jit_type_t signature = create_signature(); + create(signature); + if(!func) + { + jit_type_free(signature); + } + } +} + +/*@ + * @deftypemethod jit_function int compile () + * Compile this function explicity. You normally don't need to use this + * method because the function will be compiled on-demand. If you do + * choose to build the function manually, then the correct sequence of + * operations is as follows: + * + * @enumerate + * @item + * Invoke the @code{build_start} method to lock down the function builder. + * + * @item + * Build the function by calling the value-related and instruction-related + * methods within @code{jit_function}. + * + * @item + * Compile the function with the @code{compile} method. + * + * @item + * Unlock the function builder by invoking @code{build_end}. + * @end enumerate + * @end deftypemethod + * + * @deftypemethod jit_function int is_compiled () const + * Determine if this function has already been compiled. + * @end deftypemethod +@*/ +int jit_function::compile() +{ + if(!func) + { + return 0; + } + else + { + return jit_function_compile(func); + } +} + +/*@ + * @deftypemethod jit_function int recompile () + * Force a function to be recompiled. + * @end deftypemethod + * + * @deftypemethod jit_function int is_recompilable () const + * Determine if this function is recompilable. + * @end deftypemethod + * + * @deftypemethod jit_function void set_recompilable () + * @deftypemethodx jit_function void clear_recompilable () + * @deftypemethodx jit_function void set_recompilable (int flag) + * Modify the "recompilable" flag on this function. + * @end deftypemethod +@*/ +int jit_function::recompile() +{ + if(!func) + { + return JIT_RESULT_COMPILE_ERROR; + } + else + { + return jit_function_recompile(func); + } +} + +/*@ + * @deftypemethod jit_function void set_optimization_level ({unsigned int} level) + * @deftypemethodx jit_function {unsigned int} optimization_level () const + * Set or get the optimization level for this function. + * @end deftypemethod + * + * @deftypemethod jit_function {static unsigned int} max_optimization_level () + * Get the maximum optimization level for @code{libjit}. + * @end deftypemethod + * + * @deftypemethod jit_function {void *} closure () const + * @deftypemethodx jit_function {void *} vtable_pointer () const + * Get the closure or vtable pointer form of this function. + * @end deftypemethod + * + * @deftypemethod jit_function int apply ({void **} args, {void *} result) + * @deftypemethodx jit_function int apply (jit_type_t signature, {void **} args, {void *} return_area) + * Call this function, applying the specified arguments. + * @end deftypemethod +@*/ + +/*@ + * @deftypemethod jit_function {static jit_type_t} signature_helper (jit_type_t return_type, ...) + * You can call this method from @code{create_signature()} to help build the + * correct signature for your function. The first parameter is the return + * type, following by zero or more types for the parameters. The parameter + * list is terminated with the special value @code{jit_function::end_params}. + * + * A maximum of 32 parameter types can be supplied, and the signature + * ABI is always set to @code{jit_abi_cdecl}. + * @end deftypemethod +@*/ +jit_type_t jit_function::signature_helper(jit_type_t return_type, ...) +{ + va_list va; + jit_type_t params[32]; + unsigned int num_params = 0; + jit_type_t type; +#ifdef HAVE_STDARG_H + va_start(va, return_type); +#else + va_start(va); +#endif + while(num_params < 32 && (type = va_arg(va, jit_type_t)) != 0) + { + params[num_params++] = type; + } + va_end(va); + return jit_type_create_signature + (jit_abi_cdecl, return_type, params, num_params, 1); +} + +/*@ + * @deftypemethod jit_function void build () + * This method is called when the function has to be build on-demand, + * or in response to an explicit @code{recompile} request. You build the + * function by calling the value-related and instruction-related + * methods within @code{jit_function} that are described below. + * + * The default implementation of @code{build} will fail, so you must + * override it if you didn't build the function manually and call + * @code{compile}. + * @end deftypemethod +@*/ +void jit_function::build() +{ + // Normally overridden by subclasses. + fail(); +} + +/*@ + * @deftypemethod jit_function jit_type_t create_signature () + * This method is called by @code{create()} to create the function's + * signature. The default implementation creates a signature that + * returns @code{void} and has no parameters. + * @end deftypemethod +@*/ +jit_type_t jit_function::create_signature() +{ + // Normally overridden by subclasses. + return signature_helper(jit_type_void, end_params); +} + +/*@ + * @deftypemethod jit_function void fail () + * This method can be called by @code{build} to fail the on-demand + * compilation process. It throws an exception to unwind the build. + * @end deftypemethod +@*/ +void jit_function::fail() +{ + throw jit_build_exception(JIT_RESULT_COMPILE_ERROR); +} + +/*@ + * @deftypemethod jit_function void out_of_memory () + * This method can be called by @code{build} to indicate that the on-demand + * compilation process ran out of memory. It throws an exception to + * unwind the build. + * @end deftypemethod +@*/ +void jit_function::out_of_memory() +{ + throw jit_build_exception(JIT_RESULT_OUT_OF_MEMORY); +} + +/*@ + * @deftypemethod jit_function void build_start () + * Start an explicit build process. Not needed if you will be using + * on-demand compilation. + * @end deftypemethod + * + * @deftypemethod jit_function void build_end () + * End an explicit build process. + * @end deftypemethod +@*/ + +#define value_wrap(x) \ + jit_value val((x)); \ + if(!(val.raw())) \ + out_of_memory(); \ + return val + +/*@ + * @deftypemethod jit_function jit_value new_value (jit_type_t type) + * Create a new temporary value. This is the C++ counterpart to + * @code{jit_value_create}. + * @end deftypemethod +@*/ +jit_value jit_function::new_value(jit_type_t type) +{ + value_wrap(jit_value_create(func, type)); +} + +/*@ + * @deftypemethod jit_function jit_value new_constant (jit_sbyte value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_ubyte value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_short value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_ushort value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_int value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_uint value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_long value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_ulong value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_float32 value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_float64 value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant (jit_nfloat value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant ({void *} value, jit_type_t type) + * @deftypemethodx jit_function jit_value new_constant ({const jit_constant_t&} value) + * Create constant values of various kinds. @xref{Values}, for more + * information on creating and managing constants. + * @end deftypemethod +@*/ +jit_value jit_function::new_constant(jit_sbyte value, jit_type_t type) +{ + if(!type) + { + type = jit_type_sbyte; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(jit_ubyte value, jit_type_t type) +{ + if(!type) + { + type = jit_type_ubyte; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(jit_short value, jit_type_t type) +{ + if(!type) + { + type = jit_type_short; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(jit_ushort value, jit_type_t type) +{ + if(!type) + { + type = jit_type_ushort; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(jit_int value, jit_type_t type) +{ + if(!type) + { + type = jit_type_int; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(jit_uint value, jit_type_t type) +{ + if(!type) + { + type = jit_type_uint; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(jit_long value, jit_type_t type) +{ + if(!type) + { + type = jit_type_long; + } + value_wrap(jit_value_create_long_constant(func, type, value)); +} + +jit_value jit_function::new_constant(jit_ulong value, jit_type_t type) +{ + if(!type) + { + type = jit_type_ulong; + } + value_wrap(jit_value_create_long_constant(func, type, (jit_long)value)); +} + +jit_value jit_function::new_constant(jit_float32 value, jit_type_t type) +{ + if(!type) + { + type = jit_type_float32; + } + value_wrap(jit_value_create_float32_constant(func, type, value)); +} + +jit_value jit_function::new_constant(jit_float64 value, jit_type_t type) +{ + if(!type) + { + type = jit_type_float64; + } + value_wrap(jit_value_create_float64_constant(func, type, value)); +} + +#ifndef JIT_NFLOAT_IS_DOUBLE + +jit_value jit_function::new_constant(jit_nfloat value, jit_type_t type) +{ + if(!type) + { + type = jit_type_nfloat; + } + value_wrap(jit_value_create_nfloat_constant(func, type, value)); +} + +#endif + +jit_value jit_function::new_constant(void *value, jit_type_t type) +{ + if(!type) + { + type = jit_type_void_ptr; + } + value_wrap(jit_value_create_nint_constant(func, type, (jit_nint)value)); +} + +jit_value jit_function::new_constant(const jit_constant_t& value) +{ + value_wrap(jit_value_create_constant(func, &value)); +} + +/*@ + * @deftypemethod jit_function jit_value get_param ({unsigned int} param) + * Get the value that corresponds to parameter @code{param}. + * @end deftypemethod +@*/ +jit_value jit_function::get_param(unsigned int param) +{ + value_wrap(jit_value_get_param(func, param)); +} + +/*@ + * @deftypemethod jit_function jit_value get_struct_pointer () + * Get the value that corresponds to the structure pointer parameter, + * if this function has one. Returns an empty value if it does not. + * @end deftypemethod +@*/ +jit_value jit_function::get_struct_pointer() +{ + value_wrap(jit_value_get_struct_pointer(func)); +} + +/*@ + * @deftypemethod jit_function void insn_label ({jit_label&} label) + * @deftypemethodx jit_function jit_value insn_load ({const jit_value&} value) + * @deftypemethodx jit_function jit_value insn_dup ({const jit_value&} value) + * @deftypemethodx jit_function jit_value insn_load_small ({const jit_value&} value) + * @deftypemethodx jit_function void store ({const jit_value&} dest, {const jit_value&} value) + * @deftypemethodx jit_function jit_value insn_load_relative ({const jit_value&} value, jit_nint offset, jit_type_t type) + * @deftypemethodx jit_function void insn_store_relative ({const jit_value&} dest, jit_nint offset, {const jit_value&} value) + * @deftypemethodx jit_function jit_value insn_add_relative ({const jit_value&} value, jit_nint offset) + * @deftypemethodx jit_function void insn_check_null ({const jit_value&} value) + * @deftypemethodx jit_function jit_value insn_add ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_add_ovf ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_sub ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_sub_ovf ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_mul ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_mul_ovf ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_div ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_rem ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_rem_ieee ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_neg ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_and ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_or ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_xor ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_not ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_shl ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_shr ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_ushr ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_sshr ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_eq ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_ne ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_lt ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_le ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_gt ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_ge ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_cmpl ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_cmpg ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_to_bool ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_to_not_bool ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_acos ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_asin ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_atan ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_atan2 ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_ceil ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_cos ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_cosh ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_exp ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_floor ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_log ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_log10 ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_pow ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_rint ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_round ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_sin ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_sinh ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_sqrt ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_tan ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_tanh ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_is_nan ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_is_finite ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_is_inf ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_abs ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_min ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_max ({const jit_value&} value1, {const jit_value&} value2) + * @deftypemethodx jit_function jit_value insn_sign ({const jit_value&} value1) + * @deftypemethodx jit_function void insn_branch ({jit_label&} label) + * @deftypemethodx jit_function void insn_branch_if ({const jit_value&} value, {jit_label&} label) + * @deftypemethodx jit_function void insn_branch_if_not ({const jit_value&} value, {jit_label&} label) + * @deftypemethodx jit_function jit_value insn_address_of ({const jit_value&} value1) + * @deftypemethodx jit_function jit_value insn_convert ({const jit_value&} value, jit_type_t type, int overflow_check) + * @deftypemethodx jit_function jit_value insn_call ({const char *} name, jit_function_t jit_func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * @deftypemethodx jit_function jit_value insn_call_indirect ({const jit_value&} value, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * @deftypemethodx jit_function jit_value insn_call_indirect_vtable ({const jit_value&} value, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * @deftypemethodx jit_function jit_value insn_call_native ({const char *} name, {void *} native_func, jit_type_t signature, {jit_value_t *} args, {unsigned int} num_args, int flags) + * @deftypemethodx jit_function jit_value insn_call_intrinsic ({const char *} name, {void *} intrinsic_func, {const jit_intrinsic_descr_t&} descriptor, {const jit_value&} arg1, {const jit_value&} arg2) + * @deftypemethodx jit_function jit_value insn_import (jit_value value) + * @deftypemethodx jit_function void insn_return ({const jit_value&} value) + * @deftypemethodx jit_function void insn_return () + * @deftypemethodx jit_function void insn_default_return () + * @deftypemethodx jit_function void insn_throw ({const jit_value&} value) + * @deftypemethodx jit_function jit_value insn_get_call_stack () + * Create instructions of various kinds. @xref{Instructions}, for more + * information on the individual instructions and their arguments. + * @end deftypemethod +@*/ + +void jit_function::insn_label(jit_label& label) +{ + if(!jit_insn_label(func, label.rawp())) + { + out_of_memory(); + } +} + +jit_value jit_function::insn_load(const jit_value& value) +{ + value_wrap(jit_insn_load(func, value.raw())); +} + +jit_value jit_function::insn_dup(const jit_value& value) +{ + value_wrap(jit_insn_dup(func, value.raw())); +} + +jit_value jit_function::insn_load_small(const jit_value& value) +{ + value_wrap(jit_insn_load_small(func, value.raw())); +} + +void jit_function::store(const jit_value& dest, const jit_value& value) +{ + if(!jit_insn_store(func, dest.raw(), value.raw())) + { + out_of_memory(); + } +} + +jit_value jit_function::insn_load_relative + (const jit_value& value, jit_nint offset, jit_type_t type) +{ + value_wrap(jit_insn_load_relative(func, value.raw(), offset, type)); +} + +void jit_function::insn_store_relative + (const jit_value& dest, jit_nint offset, const jit_value& value) +{ + if(!jit_insn_store_relative(func, dest.raw(), offset, value.raw())) + { + out_of_memory(); + } +} + +jit_value jit_function::insn_add_relative + (const jit_value& value, jit_nint offset) +{ + value_wrap(jit_insn_add_relative(func, value.raw(), offset)); +} + +void jit_function::insn_check_null(const jit_value& value) +{ + if(!jit_insn_check_null(func, value.raw())) + { + out_of_memory(); + } +} + +jit_value jit_function::insn_add + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_add(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_add_ovf + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_add_ovf(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_sub + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_sub(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_sub_ovf + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_sub_ovf(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_mul + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_mul(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_mul_ovf + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_mul_ovf(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_div + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_div(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_rem + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_rem(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_rem_ieee + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_rem_ieee(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_neg(const jit_value& value1) +{ + value_wrap(jit_insn_neg(func, value1.raw())); +} + +jit_value jit_function::insn_and + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_and(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_or + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_or(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_xor + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_xor(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_not(const jit_value& value1) +{ + value_wrap(jit_insn_not(func, value1.raw())); +} + +jit_value jit_function::insn_shl + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_shl(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_shr + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_shr(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_ushr + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_ushr(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_sshr + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_sshr(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_eq + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_eq(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_ne + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_ne(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_lt + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_lt(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_le + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_le(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_gt + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_gt(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_ge + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_ge(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_cmpl + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_cmpl(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_cmpg + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_cmpg(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_to_bool(const jit_value& value1) +{ + value_wrap(jit_insn_to_bool(func, value1.raw())); +} + +jit_value jit_function::insn_to_not_bool(const jit_value& value1) +{ + value_wrap(jit_insn_to_not_bool(func, value1.raw())); +} + +jit_value jit_function::insn_acos(const jit_value& value1) +{ + value_wrap(jit_insn_acos(func, value1.raw())); +} + +jit_value jit_function::insn_asin(const jit_value& value1) +{ + value_wrap(jit_insn_asin(func, value1.raw())); +} + +jit_value jit_function::insn_atan(const jit_value& value1) +{ + value_wrap(jit_insn_atan(func, value1.raw())); +} + +jit_value jit_function::insn_atan2 + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_atan2(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_ceil(const jit_value& value1) +{ + value_wrap(jit_insn_ceil(func, value1.raw())); +} + +jit_value jit_function::insn_cos(const jit_value& value1) +{ + value_wrap(jit_insn_cos(func, value1.raw())); +} + +jit_value jit_function::insn_cosh(const jit_value& value1) +{ + value_wrap(jit_insn_cosh(func, value1.raw())); +} + +jit_value jit_function::insn_exp(const jit_value& value1) +{ + value_wrap(jit_insn_exp(func, value1.raw())); +} + +jit_value jit_function::insn_floor(const jit_value& value1) +{ + value_wrap(jit_insn_floor(func, value1.raw())); +} + +jit_value jit_function::insn_log(const jit_value& value1) +{ + value_wrap(jit_insn_log(func, value1.raw())); +} + +jit_value jit_function::insn_log10(const jit_value& value1) +{ + value_wrap(jit_insn_log10(func, value1.raw())); +} + +jit_value jit_function::insn_pow + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_pow(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_rint(const jit_value& value1) +{ + value_wrap(jit_insn_rint(func, value1.raw())); +} + +jit_value jit_function::insn_round(const jit_value& value1) +{ + value_wrap(jit_insn_round(func, value1.raw())); +} + +jit_value jit_function::insn_sin(const jit_value& value1) +{ + value_wrap(jit_insn_sin(func, value1.raw())); +} + +jit_value jit_function::insn_sinh(const jit_value& value1) +{ + value_wrap(jit_insn_sinh(func, value1.raw())); +} + +jit_value jit_function::insn_sqrt(const jit_value& value1) +{ + value_wrap(jit_insn_sqrt(func, value1.raw())); +} + +jit_value jit_function::insn_tan(const jit_value& value1) +{ + value_wrap(jit_insn_tan(func, value1.raw())); +} + +jit_value jit_function::insn_tanh(const jit_value& value1) +{ + value_wrap(jit_insn_tanh(func, value1.raw())); +} + +jit_value jit_function::insn_is_nan(const jit_value& value1) +{ + value_wrap(jit_insn_is_nan(func, value1.raw())); +} + +jit_value jit_function::insn_is_finite(const jit_value& value1) +{ + value_wrap(jit_insn_is_finite(func, value1.raw())); +} + +jit_value jit_function::insn_is_inf(const jit_value& value1) +{ + value_wrap(jit_insn_is_inf(func, value1.raw())); +} + +jit_value jit_function::insn_abs(const jit_value& value1) +{ + value_wrap(jit_insn_abs(func, value1.raw())); +} + +jit_value jit_function::insn_min + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_min(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_max + (const jit_value& value1, const jit_value& value2) +{ + value_wrap(jit_insn_max(func, value1.raw(), value2.raw())); +} + +jit_value jit_function::insn_sign(const jit_value& value1) +{ + value_wrap(jit_insn_sign(func, value1.raw())); +} + +void jit_function::insn_branch(jit_label& label) +{ + if(!jit_insn_branch(func, label.rawp())) + { + out_of_memory(); + } +} + +void jit_function::insn_branch_if(const jit_value& value, jit_label& label) +{ + if(!jit_insn_branch_if(func, value.raw(), label.rawp())) + { + out_of_memory(); + } +} + +void jit_function::insn_branch_if_not(const jit_value& value, jit_label& label) +{ + if(!jit_insn_branch_if_not(func, value.raw(), label.rawp())) + { + out_of_memory(); + } +} + +jit_value jit_function::insn_address_of(const jit_value& value1) +{ + value_wrap(jit_insn_address_of(func, value1.raw())); +} + +jit_value jit_function::insn_convert + (const jit_value& value, jit_type_t type, int overflow_check) +{ + value_wrap(jit_insn_convert(func, value.raw(), type, overflow_check)); +} + +jit_value jit_function::insn_call + (const char *name, jit_function_t jit_func, + jit_type_t signature, jit_value_t *args, unsigned int num_args, + int flags) +{ + value_wrap(jit_insn_call + (func, name, jit_func, signature, args, num_args, flags)); +} + +jit_value jit_function::insn_call_indirect + (const jit_value& value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) +{ + value_wrap(jit_insn_call_indirect + (func, value.raw(), signature, args, num_args, flags)); +} + +jit_value jit_function::insn_call_indirect_vtable + (const jit_value& value, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) +{ + value_wrap(jit_insn_call_indirect_vtable + (func, value.raw(), signature, args, num_args, flags)); +} + +jit_value jit_function::insn_call_native + (const char *name, void *native_func, jit_type_t signature, + jit_value_t *args, unsigned int num_args, int flags) +{ + value_wrap(jit_insn_call_native + (func, name, native_func, signature, args, num_args, flags)); +} + +jit_value jit_function::insn_call_intrinsic + (const char *name, void *intrinsic_func, + const jit_intrinsic_descr_t& descriptor, + const jit_value& arg1, const jit_value& arg2) +{ + value_wrap(jit_insn_call_intrinsic + (func, name, intrinsic_func, &descriptor, arg1.raw(), arg2.raw())); +} + +jit_value jit_function::insn_import(jit_value value) +{ + value_wrap(jit_insn_import(func, value.raw())); +} + +void jit_function::insn_return(const jit_value& value) +{ + if(!jit_insn_return(func, value.raw())) + { + out_of_memory(); + } +} + +void jit_function::insn_return() +{ + if(!jit_insn_return(func, 0)) + { + out_of_memory(); + } +} + +void jit_function::insn_default_return() +{ + if(!jit_insn_default_return(func)) + { + out_of_memory(); + } +} + +void jit_function::insn_throw(const jit_value& value) +{ + if(!jit_insn_throw(func, value.raw())) + { + out_of_memory(); + } +} + +jit_value jit_function::insn_get_call_stack() +{ + value_wrap(jit_insn_get_call_stack(func)); +} + +void jit_function::register_on_demand() +{ + jit_function_set_on_demand_compiler(func, on_demand_compiler); +} + +int jit_function::on_demand_compiler(jit_function_t func) +{ + jit_function *func_object; + + // Get the object that corresponds to the raw function. + func_object = from_raw(func); + if(!func_object) + { + return JIT_RESULT_COMPILE_ERROR; + } + + // Attempt to build the function's contents. + try + { + func_object->build(); + if(!jit_insn_default_return(func)) + { + func_object->out_of_memory(); + } + return JIT_RESULT_OK; + } + catch(jit_build_exception e) + { + return e.result; + } +} + +void jit_function::free_mapping(void *data) +{ + // If we were called during the context's shutdown, + // then the raw function pointer is no longer valid. + ((jit_function *)data)->func = 0; +} diff --git a/jitplus/jit-plus-value.cpp b/jitplus/jit-plus-value.cpp new file mode 100644 index 0000000..4032a8a --- /dev/null +++ b/jitplus/jit-plus-value.cpp @@ -0,0 +1,245 @@ +/* + * jit-plus-value.cpp - C++ wrapper for JIT values. + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include + +/*@ + +The @code{jit_value} class provides a C++ counterpart to the +@code{jit_value_t} type. Values normally result by calling methods +on the @code{jit_function} class during the function building process. +@xref{Values}, for more information on creating and managing values. + +@defop Constructor jit_value jit_value () +Construct an empty value. +@end defop + +@defop Constructor jit_value jit_value (jit_value_t value) +Construct a value by wrapping up a raw C @code{jit_value_t} object. +@end defop + +@defop Constructor jit_value jit_value ({const jit_value&} value) +Create a copy of @code{value}. +@end defop + +@defop Destructor jit_value ~jit_value () +Destroy the C++ value wrapper, but leave the underlying raw C +value alone. +@end defop + +@defop Operator jit_value {jit_value& operator=} ({const jit_value&} value) +Copy @code{jit_value} objects. +@end defop + +@deftypemethod jit_value jit_value_t raw () const +Get the raw C @code{jit_value_t} value that underlies this object. +@end deftypemethod + +@deftypemethod jit_value int is_valid () const +Determine if this @code{jit_value} object contains a valid raw +C @code{jit_value_t} value. +@end deftypemethod + +@deftypemethod jit_value int is_temporary () const +@deftypemethodx jit_value int is_local () const +@deftypemethodx jit_value int is_constant () const +Determine if this @code{jit_value} is temporary, local, or constant. +@end deftypemethod + +@deftypemethod jit_value void set_volatile () +@deftypemethodx jit_value int is_volatile () const +Set or check the "volatile" state on this value. +@end deftypemethod + +@deftypemethod jit_value void set_addressable () +@deftypemethodx jit_value int is_addressable () const +Set or check the "addressable" state on this value. +@end deftypemethod + +@deftypemethod jit_value jit_type_t type () const +Get the type of this value. +@end deftypemethod + +@deftypemethod jit_value jit_function_t function () const +@deftypemethodx jit_value jit_block_t block () const +@deftypemethodx jit_value jit_context_t context () const +Get the owning function, block, or context for this value. +@end deftypemethod + +@deftypemethod jit_value jit_constant_t constant () const +@deftypemethodx jit_value jit_nint nint_constant () const +@deftypemethodx jit_value jit_long long_constant () const +@deftypemethodx jit_value jit_float32 float32_constant () const +@deftypemethodx jit_value jit_float64 float64_constant () const +@deftypemethodx jit_value jit_nfloat nfloat_constant () const +Extract the constant stored in this value. +@end deftypemethod + +@defop Operator jit_value {jit_value operator+} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator-} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator*} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator/} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator%} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator-} ({const jit_value&} value1) +@defopx Operator jit_value {jit_value operator&} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator|} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator^} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator~} ({const jit_value&} value1) +@defopx Operator jit_value {jit_value operator<<} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator>>} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator==} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator!=} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator<} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator<=} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator>} ({const jit_value&} value1, {const jit_value&} value2) +@defopx Operator jit_value {jit_value operator>=} ({const jit_value&} value1, {const jit_value&} value2) +Generate an arithmetic, bitwise, or comparison instruction based on +one or two @code{jit_value} objects. These operators are shortcuts +for calling @code{insn_add}, @code{insn_sub}, etc on the +@code{jit_function} object. +@end defop + +@*/ + +// Get the function that owns a pair of values. It will choose +// the function for the first value, unless it is NULL (e.g. for +// global values). In that case, it will choose the function +// for the second value. +static inline jit_function_t value_owner + (const jit_value& value1, const jit_value& value2) +{ + jit_function_t func = jit_value_get_function(value1.raw()); + if(func) + { + return func; + } + else + { + return jit_value_get_function(value2.raw()); + } +} + +jit_value operator+(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_add(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator-(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_sub(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator*(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_mul(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator/(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_div(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator%(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_rem(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator-(const jit_value& value1) +{ + return jit_value(jit_insn_neg(jit_value_get_function(value1.raw()), + value1.raw())); +} + +jit_value operator&(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_and(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator|(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_or(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator^(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_xor(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator~(const jit_value& value1) +{ + return jit_value(jit_insn_not(jit_value_get_function(value1.raw()), + value1.raw())); +} + +jit_value operator<<(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_shl(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator>>(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_shr(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator==(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_eq(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator!=(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_ne(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator<(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_lt(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator<=(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_le(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator>(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_gt(value_owner(value1, value2), + value1.raw(), value2.raw())); +} + +jit_value operator>=(const jit_value& value1, const jit_value& value2) +{ + return jit_value(jit_insn_ge(value_owner(value1, value2), + value1.raw(), value2.raw())); +} diff --git a/missing b/missing new file mode 100755 index 0000000..6a37006 --- /dev/null +++ b/missing @@ -0,0 +1,336 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997, 1999, 2000, 2002 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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, 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. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.4 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. + You can get \`$1Help2man' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 0000000..d3d085b --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.1 2004/04/30 23:29:35 rweather Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/tools/.cvsignore b/tools/.cvsignore new file mode 100644 index 0000000..8a86675 --- /dev/null +++ b/tools/.cvsignore @@ -0,0 +1,5 @@ +Makefile +Makefile.in +.deps +gen-apply +gen-apply.exe diff --git a/tools/Makefile.am b/tools/Makefile.am new file mode 100644 index 0000000..a48f253 --- /dev/null +++ b/tools/Makefile.am @@ -0,0 +1,14 @@ + +noinst_PROGRAMS = gen-apply + +gen_apply_SOURCES = gen-apply.c + +all-local: $(top_builddir)/jit/jit-apply-rules.h + +$(top_builddir)/jit/jit-apply-rules.h: gen-apply$(EXEEXT) + ./gen-apply >$@ + +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include \ + -I$(top_srcdir)/jit -I$(top_builddir)/jit + +CLEANFILES = $(top_builddir)/jit/jit-apply-rules.h diff --git a/tools/gen-apply.c b/tools/gen-apply.c new file mode 100644 index 0000000..072dfe6 --- /dev/null +++ b/tools/gen-apply.c @@ -0,0 +1,2211 @@ +/* + * gen-apply.c - Generate the rules that are needed to use "__builtin_apply". + * + * Copyright (C) 2004 Southern Storm Software, Pty Ltd. + * + * 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 + */ + +#include +#include "jit-apply-func.h" +#include +#include +#if HAVE_ALLOCA_H + #include +#endif + +/* + +This program tries to automatically discover the register assignment rules +that are used by gcc's "__builtin_apply" operator. It is used to generate +the "jit-apply-rules.h" file. + +*/ + +#if defined(__GNUC__) + #define PLATFORM_IS_GCC 1 +#endif +#if defined(__i386) || defined(__i386__) || defined(_M_IX86) + #define PLATFORM_IS_X86 1 +#if defined(__CYGWIN__) || defined(__CYGWIN32__) || \ + defined(_WIN32) || defined(WIN32) + #define PLATFORM_IS_WIN32 1 + #include + #ifndef alloca + #define alloca _alloca + #endif +#endif +#endif + +#if defined(PLATFORM_IS_GCC) || defined(PLATFORM_IS_WIN32) + +/* + * Values that are detected. + */ +int num_word_regs = 0; +int num_float_regs = 0; +int pass_stack_float_as_double = 0; +int pass_stack_float_as_nfloat = 0; +int pass_stack_double_as_nfloat = 0; +int pass_stack_nfloat_as_double = 0; +int pass_reg_float_as_double = 0; +int pass_reg_float_as_nfloat = 0; +int pass_reg_double_as_nfloat = 0; +int pass_reg_nfloat_as_double = 0; +int return_float_as_double = 0; +int return_float_as_nfloat = 0; +int return_double_as_nfloat = 0; +int return_nfloat_as_double = 0; +int floats_in_word_regs = 0; +int return_floats_after = 0; +int varargs_on_stack = 0; +int struct_return_special_reg = 0; +int struct_reg_overlaps_word_reg = 0; +int struct_return_in_reg[64]; +int align_long_regs = 0; +int align_long_stack = 0; +int max_apply_size = 0; +int x86_fastcall = 0; +int parent_frame_offset = 0; +int return_address_offset = 0; +int broken_frame_builtins = 0; +int max_struct_in_reg = 0; +int x86_pop_struct_return = 0; + +void *mem_copy(void *dest, const void *src, unsigned int len) +{ + unsigned char *d = (unsigned char *)dest; + const unsigned char *s = (const unsigned char *)src; + while(len > 0) + { + *d++ = *s++; + --len; + } + return dest; +} +#define jit_memcpy mem_copy + +void mem_set(void *dest, int value, unsigned int len) +{ + unsigned char *d = (unsigned char *)dest; + while(len > 0) + { + *d++ = (unsigned char)value; + --len; + } +} + +int mem_cmp(const void *s1, const void *s2, unsigned int len) +{ + const unsigned char *str1 = (const unsigned char *)s1; + const unsigned char *str2 = (const unsigned char *)s2; + while(len > 0) + { + if(*str1 < *str2) + return -1; + else if(*str1 > *str2) + return 1; + ++str1; + ++str2; + --len; + } + return 0; +} + +/* + * Detect the number of word registers that are used in function calls. + * We assume that the platform uses less than 32 registers in outgoing calls. + */ +void detect_word_regs(jit_nint arg1, jit_nint arg2, jit_nint arg3, + jit_nint arg4, jit_nint arg5, jit_nint arg6, + jit_nint arg7, jit_nint arg8, jit_nint arg9, + jit_nint arg10, jit_nint arg11, jit_nint arg12, + jit_nint arg13, jit_nint arg14, jit_nint arg15, + jit_nint arg16, jit_nint arg17, jit_nint arg18, + jit_nint arg19, jit_nint arg20, jit_nint arg21, + jit_nint arg22, jit_nint arg23, jit_nint arg24, + jit_nint arg25, jit_nint arg26, jit_nint arg27, + jit_nint arg28, jit_nint arg29, jit_nint arg30, + jit_nint arg31, jit_nint arg32) +{ + /* We fetch the number in the first stack argument, which will + correspond to the number of word registers that are present */ + jit_nint *args, *stack_args; + jit_builtin_apply_args(jit_nint *, args); + stack_args = (jit_nint *)(args[0]); + num_word_regs = (int)(stack_args[0]); + + /* Detect the presence of a structure return register by checking + to see if "arg1" is in the second word position after "stack_args" */ + if(num_word_regs > 1 && args[2] == 0) + { + struct_return_special_reg = 1; + } +} + +/* + * Detect the presence of a structure return register if there are 0 or 1 + * word registers as detected by "detect_word_regs". The structure + * below must be big enough to avoid being returned in a register. + */ +struct detect_struct_reg +{ + void *field1; + void *field2; + void *field3; + void *field4; + void *field5; + void *field6; + void *field7; + void *field8; +}; +static struct detect_struct_reg detect_struct_buf; +struct detect_struct_reg detect_struct_return(jit_nint arg1, jit_nint arg2) +{ + struct detect_struct_reg ret; + jit_nint *args, *stack_args; + jit_builtin_apply_args(jit_nint *, args); + stack_args = (jit_nint *)(args[0]); + + /* Set the return value */ + ret.field1 = 0; + ret.field2 = 0; + ret.field3 = 0; + ret.field4 = 0; + ret.field5 = 0; + ret.field6 = 0; + ret.field7 = 0; + ret.field8 = 0; + + /* If the stack starts with something other than 1 or 2, + then the structure return pointer is passed on the stack */ + if(stack_args[0] != 1 && stack_args[0] != 2) + { + return ret; + } + + /* Check the slots to determine where the pointer resides */ + if(num_word_regs == 0) + { + /* If there are no word registers and the stack top + does not look like a return pointer, then the + structure return must be in a special register + that is separate from the normal word regsiters */ + struct_return_special_reg = 1; + } + else + { + if(stack_args[0] == 2) + { + /* The first word argument is still in a register, + so there must be a special structure register. + If the first word argument was on the stack, then + the structure return is in an ordinary register */ + struct_return_special_reg = 1; + } + } + + /* Done */ + return ret; +} + +/* + * Determine if a special structure return register overlaps + * with the first word register. + */ +struct detect_struct_reg detect_struct_overlap(jit_nint arg1, jit_nint arg2) +{ + struct detect_struct_reg ret; + jit_nint *args; + jit_builtin_apply_args(jit_nint *, args); + + /* Set the return value */ + ret.field1 = 0; + ret.field2 = 0; + ret.field3 = 0; + ret.field4 = 0; + ret.field5 = 0; + ret.field6 = 0; + ret.field7 = 0; + ret.field8 = 0; + + /* Check for overlap */ + if(struct_return_special_reg && num_word_regs > 0) + { + if(args[1] == args[2]) + { + struct_reg_overlaps_word_reg = 1; + } + } + + /* Done */ + return ret; +} + +/* + * Detect the number of floating-point registers. + */ +void detect_float_regs(double arg1, double arg2, double arg3, + double arg4, double arg5, double arg6, + double arg7, double arg8, double arg9, + double arg10, double arg11, double arg12, + double arg13, double arg14, double arg15, + double arg16, double arg17, double arg18, + double arg19, double arg20, double arg21, + double arg22, double arg23, double arg24, + double arg25, double arg26, double arg27, + double arg28, double arg29, double arg30, + double arg31, double arg32) +{ + jit_nint *args; + double *stack_args; + jit_builtin_apply_args(jit_nint *, args); + stack_args = (double *)(args[0]); + + /* The first stack argument indicates the number of floating-point + registers. At the moment we don't know if they overlap with + the word registers or not */ + num_float_regs = (int)(stack_args[0]); +} + +#ifdef JIT_NATIVE_INT32 + +/* + * Detect if a "float" value will use a word register. + */ +void detect_float_overlap(float x, jit_nint y) +{ + /* We have an overlap if "y" is on the stack */ + jit_nint *args; + jit_builtin_apply_args(jit_nint *, args); + if(args[struct_return_special_reg + 1] != 1) + { + floats_in_word_regs = 1; + num_float_regs = 0; + } +} + +#endif /* JIT_NATIVE_INT32 */ + +/* + * Detect if a "double" value will use a word register. + */ +void detect_double_overlap(double x, jit_nint y, jit_nint z) +{ + /* We have an overlap if "x" is in the first word register slot */ + double temp; + jit_nint *args; + jit_builtin_apply_args(jit_nint *, args); + mem_copy(&temp, args + struct_return_special_reg + 1, sizeof(temp)); + if(!mem_cmp(&temp, &x, sizeof(double))) + { + floats_in_word_regs = 1; + num_float_regs = 0; + } +} + +/* + * Detect if floating-point registers are "double" or "long double" in size. + */ +void detect_float_reg_size_regs(double x, double y) +{ + double temp; + jit_nint *args; + jit_builtin_apply_args(jit_nint *, args); + mem_copy(&temp, args + 1 + struct_return_special_reg + num_word_regs, + sizeof(temp)); + if(!mem_cmp(&temp, &x, sizeof(double))) + { + pass_reg_nfloat_as_double = 1; + } + else + { + pass_reg_double_as_nfloat = 1; + } +} +void detect_float_reg_size_stack(jit_nfloat x, jit_nfloat y) +{ + double temp; + double dx; + jit_nint *args; + jit_builtin_apply_args(jit_nint *, args); + mem_copy(&temp, (void *)(args[0]), sizeof(temp)); + dx = (double)x; + if(!mem_cmp(&temp, &dx, sizeof(double))) + { + pass_stack_nfloat_as_double = 1; + } +} + +/* + * Detect the promotion rules for "float" values. + */ +void detect_float_promotion(float arg1, float arg2, float arg3, + float arg4, float arg5, float arg6, + float arg7, float arg8, float arg9, + float arg10, float arg11, float arg12, + float arg13, float arg14, float arg15, + float arg16, float arg17, float arg18, + float arg19, float arg20, float arg21, + float arg22, float arg23, float arg24, + float arg25, float arg26, float arg27, + float arg28, float arg29, float arg30, + float arg31, float arg32) +{ + jit_nint *args, *stack_args; + float value, test; + double dvalue; + int reg_promote; + int stack_promote; + int index; + + /* Extract the arguments */ + jit_builtin_apply_args(jit_nint *, args); + stack_args = (jit_nint *)(args[0]); + reg_promote = 0; + stack_promote = 0; + + /* Handle the easy promotion cases first */ + if(floats_in_word_regs) + { + /* The value will either be in 1 or 2 word registers */ + mem_copy(&value, args + 1 + struct_return_special_reg, sizeof(value)); + if(value != arg1) + { + reg_promote = 1; + } + } + else if(num_float_regs > 0) + { + /* The value is in a float register, which is always promoted */ + reg_promote = 1; + } + + /* Skip the arguments that will be stored in registers */ + index = 1; + if(floats_in_word_regs) + { + if(reg_promote && sizeof(jit_nint) == sizeof(jit_int)) + { + index += num_word_regs / 2; + } + else + { + index += num_word_regs; + } + } + else if(num_float_regs > 0) + { + index += num_float_regs; + } + + /* Get the value corresponding to argument "index" */ + switch(index) + { + case 1: test = arg1; break; + case 2: test = arg2; break; + case 3: test = arg3; break; + case 4: test = arg4; break; + case 5: test = arg5; break; + case 6: test = arg6; break; + case 7: test = arg7; break; + case 8: test = arg8; break; + case 9: test = arg9; break; + case 10: test = arg10; break; + case 11: test = arg11; break; + case 12: test = arg12; break; + case 13: test = arg13; break; + case 14: test = arg14; break; + case 15: test = arg15; break; + case 16: test = arg16; break; + case 17: test = arg17; break; + case 18: test = arg18; break; + case 19: test = arg19; break; + case 20: test = arg20; break; + case 30: test = arg30; break; + case 31: test = arg31; break; + case 32: test = arg32; break; + default: test = (float)(-1.0); break; + } + + /* Determine if stacked values are promoted */ + mem_copy(&value, stack_args, sizeof(value)); + if(value != test) + { + stack_promote = 1; + mem_copy(&dvalue, stack_args, sizeof(dvalue)); + if(dvalue != (double)test) + { + stack_promote = 2; + } + } + + /* Set the appropriate promotion rules */ + if(reg_promote) + { + /* Promoting "float" to "nfloat" in registers */ + if(pass_reg_nfloat_as_double) + { + pass_reg_float_as_double = 1; + } + else + { + pass_reg_float_as_nfloat = 1; + } + } + if(stack_promote == 2) + { + /* Promoting "float" to "nfloat" on the stack */ + if(pass_stack_nfloat_as_double) + { + pass_stack_float_as_double = 1; + } + else + { + pass_stack_float_as_nfloat = 1; + } + } + else if(stack_promote) + { + /* Promoting "float" to "double" on the stack */ + pass_stack_float_as_double = 1; + } +} + +/* + * Detect the stack promotion rules for "double" values. + */ +void detect_double_promotion(double arg1, double arg2, double arg3, + double arg4, double arg5, double arg6, + double arg7, double arg8, double arg9, + double arg10, double arg11, double arg12, + double arg13, double arg14, double arg15, + double arg16, double arg17, double arg18, + double arg19, double arg20, double arg21, + double arg22, double arg23, double arg24, + double arg25, double arg26, double arg27, + double arg28, double arg29, double arg30, + double arg31, double arg32) +{ + jit_nint *args, *stack_args; + double value, test; + int stack_promote; + int index; + + /* Extract the arguments */ + jit_builtin_apply_args(jit_nint *, args); + stack_args = (jit_nint *)(args[0]); + stack_promote = 0; + + /* Skip the arguments that will be stored in registers */ + index = 1; + if(floats_in_word_regs) + { + if(sizeof(jit_nint) == sizeof(jit_int)) + { + index += num_word_regs / 2; + } + else + { + index += num_word_regs; + } + } + else if(num_float_regs > 0) + { + index += num_float_regs; + } + + /* Get the value corresponding to argument "index" */ + switch(index) + { + case 1: test = arg1; break; + case 2: test = arg2; break; + case 3: test = arg3; break; + case 4: test = arg4; break; + case 5: test = arg5; break; + case 6: test = arg6; break; + case 7: test = arg7; break; + case 8: test = arg8; break; + case 9: test = arg9; break; + case 10: test = arg10; break; + case 11: test = arg11; break; + case 12: test = arg12; break; + case 13: test = arg13; break; + case 14: test = arg14; break; + case 15: test = arg15; break; + case 16: test = arg16; break; + case 17: test = arg17; break; + case 18: test = arg18; break; + case 19: test = arg19; break; + case 20: test = arg20; break; + case 30: test = arg30; break; + case 31: test = arg31; break; + case 32: test = arg32; break; + default: test = (double)(-1.0); break; + } + + /* Determine if stacked values are promoted to "nfloat" */ + mem_copy(&value, stack_args, sizeof(value)); + if(value != test) + { + stack_promote = 1; + } + + /* Set the appropriate promotion rules */ + if(stack_promote) + { + /* Promoting "double" to "nfloat" on the stack */ + pass_stack_double_as_nfloat = 1; + } +} + +/* + * Determine if variable arguments are always passed on the stack, + * even if the values would otherwise fit into a register. + */ +void detect_varargs_on_stack(jit_nint start, ...) +{ + jit_nint *args, *stack_args; + jit_builtin_apply_args(jit_nint *, args); + stack_args = (jit_nint *)(args[0]); + if(num_word_regs == 0) + { + varargs_on_stack = 1; + } + else if(stack_args[0] == 1) + { + varargs_on_stack = 1; + } +} + +/* + * Dummy functions for helping detect the size and position of "float", + * "double", and "long double" return values. + */ +float return_float(void) +{ + return (float)123.0; +} +double return_double(void) +{ + return (double)456.7; +} +jit_nfloat return_nfloat(void) +{ + return (jit_nfloat)8901.2; +} + +/* + * Detect the behaviour of floating-point values in return blocks. + */ +void detect_float_return(void) +{ + jit_nint *args; + int offset; + unsigned char *return_value; + float float_value; + double double_value; + jit_nfloat nfloat_value; + int float_size; + int double_size; + int nfloat_size; + + /* Allocate space for the outgoing arguments */ + jit_builtin_apply_args(jit_nint *, args); + + /* Call "return_float" and get its return structure */ + jit_builtin_apply(return_float, args, 0, 1, return_value); + + /* Find the location of the return value */ + offset = 0; + while(offset < 64) + { + mem_copy(&float_value, return_value + offset, sizeof(float)); + if(float_value != (float)123.0) + { + mem_copy(&double_value, return_value + offset, sizeof(double)); + if(double_value != (double)123.0) + { + mem_copy(&nfloat_value, return_value + offset, + sizeof(jit_nfloat)); + if(nfloat_value == (jit_nfloat)123.0) + { + break; + } + } + else + { + break; + } + } + else + { + break; + } + offset += sizeof(void *); + } + + /* Determine the size of the "float" return value */ + mem_copy(&float_value, return_value + offset, sizeof(float)); + if(float_value == (float)123.0) + { + float_size = 1; + } + else + { + mem_copy(&double_value, return_value + offset, sizeof(double)); + if(double_value == (double)123.0) + { + float_size = 2; + } + else + { + float_size = 3; + } + } + + /* Call "return_double" and get its return structure */ + jit_builtin_apply(return_double, args, 0, 1, return_value); + + /* Determine the size of the "double" return value */ + mem_copy(&double_value, return_value + offset, sizeof(double)); + if(double_value == (double)456.7) + { + double_size = 2; + } + else + { + double_size = 3; + } + + /* Call "return_nfloat" and get its return structure */ + jit_builtin_apply(return_nfloat, args, 0, 1, return_value); + + /* Determine the size of the "nfloat" return value */ + mem_copy(&double_value, return_value + offset, sizeof(double)); + if(double_value == (double)8901.2) + { + nfloat_size = 2; + } + else + { + nfloat_size = 3; + } + + /* Use the offset and size information to set the final parameters */ + return_floats_after = offset; + if(float_size == 2) + { + return_float_as_double = 1; + } + else if(float_size == 3) + { + return_float_as_nfloat = 1; + } + if(double_size == 3) + { + return_double_as_nfloat = 1; + } + if(nfloat_size == 2) + { + return_nfloat_as_double = 1; + } +} + +/* + * Detect whether small struct values are returned in registers. + */ +#define declare_struct_test(n) \ + struct detect_##n \ + { \ + jit_sbyte value[(n)]; \ + }; \ + struct detect_##n detect_struct_##n(void) \ + { \ + struct detect_##n d; \ + mem_set(&d, 0xFF, sizeof(d)); \ + return d; \ + } +#define call_struct_test(n) \ + mem_set(buffer, 0, sizeof(buffer)); \ + jit_builtin_apply(detect_struct_##n, args, \ + sizeof(jit_nint), 0, apply_return); \ + if(((struct detect_##n *)buffer)->value[0] == 0x00) \ + { \ + /* The buffer doesn't contain the value, so it must be in registers */ \ + struct_return_in_reg[(n) - 1] = 1; \ + } +declare_struct_test(1); +declare_struct_test(2); +declare_struct_test(3); +declare_struct_test(4); +declare_struct_test(5); +declare_struct_test(6); +declare_struct_test(7); +declare_struct_test(8); +declare_struct_test(9); +declare_struct_test(10); +declare_struct_test(11); +declare_struct_test(12); +declare_struct_test(13); +declare_struct_test(14); +declare_struct_test(15); +declare_struct_test(16); +declare_struct_test(17); +declare_struct_test(18); +declare_struct_test(19); +declare_struct_test(20); +declare_struct_test(21); +declare_struct_test(22); +declare_struct_test(23); +declare_struct_test(24); +declare_struct_test(25); +declare_struct_test(26); +declare_struct_test(27); +declare_struct_test(28); +declare_struct_test(29); +declare_struct_test(30); +declare_struct_test(31); +declare_struct_test(32); +declare_struct_test(33); +declare_struct_test(34); +declare_struct_test(35); +declare_struct_test(36); +declare_struct_test(37); +declare_struct_test(38); +declare_struct_test(39); +declare_struct_test(40); +declare_struct_test(41); +declare_struct_test(42); +declare_struct_test(43); +declare_struct_test(44); +declare_struct_test(45); +declare_struct_test(46); +declare_struct_test(47); +declare_struct_test(48); +declare_struct_test(49); +declare_struct_test(50); +declare_struct_test(51); +declare_struct_test(52); +declare_struct_test(53); +declare_struct_test(54); +declare_struct_test(55); +declare_struct_test(56); +declare_struct_test(57); +declare_struct_test(58); +declare_struct_test(59); +declare_struct_test(60); +declare_struct_test(61); +declare_struct_test(62); +declare_struct_test(63); +declare_struct_test(64); +void detect_struct_conventions(void) +{ + jit_nint *args; + jit_nint stack[1]; + jit_nint buffer[64 / sizeof(jit_nint)]; + void *apply_return; + + /* Initialize the arguments as though we'll be using a struct pointer */ + jit_builtin_apply_args(jit_nint *, args); + args[0] = (jit_nint)stack; + stack[0] = (jit_nint)buffer; + if(struct_return_special_reg || num_word_regs > 0) + { + args[1] = (jit_nint)buffer; + } + + /* Apply the structure return tests for all sizes from 1 to 64 */ + call_struct_test(1); + call_struct_test(2); + call_struct_test(3); + call_struct_test(4); + call_struct_test(5); + call_struct_test(6); + call_struct_test(7); + call_struct_test(8); + call_struct_test(9); + call_struct_test(10); + call_struct_test(11); + call_struct_test(12); + call_struct_test(13); + call_struct_test(14); + call_struct_test(15); + call_struct_test(16); + call_struct_test(17); + call_struct_test(18); + call_struct_test(19); + call_struct_test(20); + call_struct_test(21); + call_struct_test(22); + call_struct_test(23); + call_struct_test(24); + call_struct_test(25); + call_struct_test(26); + call_struct_test(27); + call_struct_test(28); + call_struct_test(29); + call_struct_test(30); + call_struct_test(31); + call_struct_test(32); + call_struct_test(33); + call_struct_test(34); + call_struct_test(35); + call_struct_test(36); + call_struct_test(37); + call_struct_test(38); + call_struct_test(39); + call_struct_test(40); + call_struct_test(41); + call_struct_test(42); + call_struct_test(43); + call_struct_test(44); + call_struct_test(45); + call_struct_test(46); + call_struct_test(47); + call_struct_test(48); + call_struct_test(49); + call_struct_test(50); + call_struct_test(51); + call_struct_test(52); + call_struct_test(53); + call_struct_test(54); + call_struct_test(55); + call_struct_test(56); + call_struct_test(57); + call_struct_test(58); + call_struct_test(59); + call_struct_test(60); + call_struct_test(61); + call_struct_test(62); + call_struct_test(63); + call_struct_test(64); +} + +/* + * Determine the maximum size for the apply structure. + */ +void detect_max_sizes(void) +{ + max_apply_size = (struct_return_special_reg + num_word_regs + 1) + * sizeof(jit_nint); + if(pass_reg_nfloat_as_double) + { + max_apply_size += num_float_regs * sizeof(double); + } + else + { + max_apply_size += num_float_regs * sizeof(jit_nfloat); + } + if(x86_fastcall && max_apply_size < 12) + { + max_apply_size = 12; + } +} + +/* + * Detect the offsets of parent frame and return address pointers + * in the values returned by "__builtin_frame_address". We have to + * do this carefully, to deal with architectures that don't create + * a real frame for leaf functions. + */ +#if defined(PLATFORM_IS_GCC) +void find_frame_offset_inner(void *looking_for, void **frame) +{ + int offset; + for(offset = 0; offset >= -8; --offset) + { + if(frame[offset] == looking_for) + { + parent_frame_offset = offset * sizeof(void *); + return; + } + } + for(offset = 1; offset <= 8; ++offset) + { + if(frame[offset] == looking_for) + { + parent_frame_offset = offset * sizeof(void *); + return; + } + } +} +void find_frame_offset_outer(void *looking_for) +{ + find_frame_offset_inner(looking_for, (void **)__builtin_frame_address(0)); +} +void find_return_offset(void *looking_for, void **frame) +{ + int offset; + for(offset = 1; offset <= 8; ++offset) + { + if(frame[offset] == looking_for) + { + return_address_offset = offset * sizeof(void *); + return; + } + } + for(offset = 0; offset >= -8; --offset) + { + if(frame[offset] == looking_for) + { + return_address_offset = offset * sizeof(void *); + return; + } + } +} +void detect_frame_offsets(void) +{ + void *frame_address = __builtin_frame_address(0); + void *return_address = __builtin_return_address(0); + find_frame_offset_outer(frame_address); + find_return_offset(return_address, frame_address); + if(parent_frame_offset == 0 && return_address_offset == 0) + { + /* Can happen on platforms like ia64 where there are so + many registers that the frame is almost never concrete */ + broken_frame_builtins = 1; + } +} +#else +void detect_frame_offsets(void) +{ + /* We don't know how to detect the offsets, so assume some defaults */ + parent_frame_offset = 0; + return_address_offset = sizeof(void *); +} +#endif + +/* + * Dump the definition of the "jit_apply_return" union, which defines + * the layout of the return value from "__builtin_apply". + */ +void dump_return_union(void) +{ + const char *float_type; + const char *double_type; + const char *nfloat_type; + + /* Dump the definition of "jit_apply_float" */ + printf("typedef union\n{\n"); + if(return_float_as_nfloat) + { + float_type = "jit_nfloat"; + } + else if(return_float_as_double) + { + float_type = "double"; + } + else + { + float_type = "float"; + } + if(return_double_as_nfloat) + { + double_type = "jit_nfloat"; + } + else + { + double_type = "double"; + } + if(return_nfloat_as_double) + { + nfloat_type = "double"; + } + else + { + nfloat_type = "jit_nfloat"; + } + printf("\t%s float_value;\n", float_type); + printf("\t%s double_value;\n", double_type); + printf("\t%s nfloat_value;\n", nfloat_type); + printf("\n} jit_apply_float;\n"); + + /* Dump the definition of "jit_apply_return" */ + printf("typedef union\n{\n"); + printf("\tjit_nint int_value;\n"); + printf("\tjit_nuint uint_value;\n"); + printf("\tjit_long long_value;\n"); + printf("\tjit_ulong ulong_value;\n"); + if(return_floats_after) + { + printf("\tstruct { jit_ubyte pad[%d]; jit_apply_float inner_value; } f_value;\n", + return_floats_after); + } + else + { + printf("\tstruct { jit_apply_float inner_value; } f_value;\n"); + } + if(max_struct_in_reg > 0) + { + printf("\tjit_ubyte small_struct_value[%d];\n", max_struct_in_reg); + } + printf("\n} jit_apply_return;\n\n"); + + /* Output access macros for manipulating the contents */ + printf("#define jit_apply_return_get_sbyte(result)\t\\\n"); + printf("\t((jit_sbyte)((result)->int_value))\n"); + printf("#define jit_apply_return_get_ubyte(result)\t\\\n"); + printf("\t((jit_ubyte)((result)->int_value))\n"); + printf("#define jit_apply_return_get_short(result)\t\\\n"); + printf("\t((jit_short)((result)->int_value))\n"); + printf("#define jit_apply_return_get_ushort(result)\t\\\n"); + printf("\t((jit_ushort)((result)->int_value))\n"); + printf("#define jit_apply_return_get_int(result)\t\\\n"); + printf("\t((jit_int)((result)->int_value))\n"); + printf("#define jit_apply_return_get_uint(result)\t\\\n"); + printf("\t((jit_uint)((result)->uint_value))\n"); + printf("#define jit_apply_return_get_nint(result)\t\\\n"); + printf("\t((jit_nint)((result)->int_value))\n"); + printf("#define jit_apply_return_get_nuint(result)\t\\\n"); + printf("\t((jit_nuint)((result)->uint_value))\n"); + printf("#define jit_apply_return_get_long(result)\t\\\n"); + printf("\t((jit_long)((result)->long_value))\n"); + printf("#define jit_apply_return_get_ulong(result)\t\\\n"); + printf("\t((jit_ulong)((result)->ulong_value))\n"); + printf("#define jit_apply_return_get_float32(result)\t\\\n"); + printf("\t((jit_float32)((result)->f_value.inner_value.float_value))\n"); + printf("#define jit_apply_return_get_float64(result)\t\\\n"); + printf("\t((jit_float64)((result)->f_value.inner_value.double_value))\n"); + printf("#define jit_apply_return_get_nfloat(result)\t\\\n"); + printf("\t((jit_nfloat)((result)->f_value.inner_value.nfloat_value))\n"); + printf("\n"); + printf("#define jit_apply_return_set_sbyte(result,value)\t\\\n"); + printf("\t(((result)->int_value) = ((jit_nint)(value)))\n"); + printf("#define jit_apply_return_set_ubyte(result,value)\t\\\n"); + printf("\t(((result)->int_value) = ((jit_nint)(value)))\n"); + printf("#define jit_apply_return_set_short(result,value)\t\\\n"); + printf("\t(((result)->int_value) = ((jit_nint)(value)))\n"); + printf("#define jit_apply_return_set_ushort(result,value)\t\\\n"); + printf("\t(((result)->int_value) = ((jit_nint)(value)))\n"); + printf("#define jit_apply_return_set_int(result,value)\t\\\n"); + printf("\t(((result)->int_value) = ((jit_nint)(value)))\n"); + printf("#define jit_apply_return_set_uint(result,value)\t\\\n"); + printf("\t(((result)->uint_value) = ((jit_nuint)(value)))\n"); + printf("#define jit_apply_return_set_nint(result,value)\t\\\n"); + printf("\t(((result)->int_value) = ((jit_nint)(value)))\n"); + printf("#define jit_apply_return_set_nuint(result,value)\t\\\n"); + printf("\t(((result)->uint_value) = ((jit_nuint)(value)))\n"); + printf("#define jit_apply_return_set_long(result,value)\t\\\n"); + printf("\t(((result)->long_value) = ((jit_long)(value)))\n"); + printf("#define jit_apply_return_set_ulong(result,value)\t\\\n"); + printf("\t(((result)->ulong_value) = ((jit_ulong)(value)))\n"); + printf("#define jit_apply_return_set_float32(result,value)\t\\\n"); + printf("\t(((result)->f_value.inner_value.float_value) = ((%s)(value)))\n", + float_type); + printf("#define jit_apply_return_set_float64(result,value)\t\\\n"); + printf("\t(((result)->f_value.inner_value.double_value) = ((%s)(value)))\n", + double_type); + printf("#define jit_apply_return_set_nfloat(result,value)\t\\\n"); + printf("\t(((result)->f_value.inner_value.nfloat_value) = ((%s)(value)))\n", + nfloat_type); + printf("\n"); +} + +/* + * Dump macro definitions that are used to build the apply parameter block. + */ +void dump_apply_macros(void) +{ + int apply_size; + int reg_offset; + const char *name; + + /* Declare the "jit_apply_builder" structure */ + printf("typedef struct\n{\n"); + printf("\tunsigned char *apply_args;\n"); + printf("\tunsigned char *stack_args;\n"); + printf("\tunsigned int stack_used;\n"); + if(num_word_regs > 0 || x86_fastcall) + { + printf("\tunsigned char *word_regs;\n"); + printf("\tunsigned int word_left;\n"); + if(align_long_regs) + { + printf("\tunsigned int word_max;\n"); + } + } + if(num_float_regs > 0) + { + printf("\tunsigned char *float_regs;\n"); + printf("\tunsigned int float_left;\n"); + } + printf("\tvoid *struct_return;\n"); + printf("\n} jit_apply_builder;\n\n"); + + /* Macro to initialize the apply builder */ + printf("#define jit_apply_builder_init(builder,type)\t\\\n"); + printf("\tdo { \\\n"); + apply_size = max_apply_size; + printf("\t\t(builder)->apply_args = (unsigned char *)alloca(%d); \\\n", apply_size); + if(apply_size > sizeof(void *)) + { + printf("\t\tjit_memset((builder)->apply_args, 0, %d); \\\n", apply_size); + } + printf("\t\t(builder)->stack_args = (unsigned char *)alloca(jit_type_get_max_arg_size((type))); \\\n"); + printf("\t\t((void **)((builder)->apply_args))[0] = (builder)->stack_args; \\\n"); + printf("\t\t(builder)->stack_used = 0; \\\n"); + reg_offset = sizeof(void *); + if(struct_return_special_reg) + { + reg_offset += sizeof(void *); + } + if(x86_fastcall) + { + printf("\t\t(builder)->word_regs = (builder)->apply_args + %d; \\\n", + reg_offset); + printf("\t\tif(jit_type_get_abi((type)) == jit_abi_fastcall) \\\n"); + printf("\t\t\t(builder)->word_left = 2; \\\n"); + printf("\t\telse; \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + reg_offset += 2 * sizeof(void *); + } + else if(num_word_regs > 0) + { + printf("\t\t(builder)->word_regs = (builder)->apply_args + %d; \\\n", + reg_offset); + printf("\t\t(builder)->word_left = %d; \\\n", num_word_regs); + reg_offset += num_word_regs * sizeof(void *); + } + if(align_long_regs) + { + printf("\t\t(builder)->word_max = (builder)->word_left; \\\n"); + } + if(num_float_regs > 0) + { + printf("\t\t(builder)->float_regs = (builder)->apply_args + %d; \\\n", + reg_offset); + printf("\t\t(builder)->float_left = %d; \\\n", num_float_regs); + } + printf("\t\t(builder)->struct_return = 0; \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to initialize the apply builder in closure parse mode. + The "args" parameter is the result of calling "__builtin_apply_args" */ + printf("#define jit_apply_parser_init(builder,type,args)\t\\\n"); + printf("\tdo { \\\n"); + printf("\t\t(builder)->apply_args = (unsigned char *)(args); \\\n"); + printf("\t\t(builder)->stack_args = (unsigned char *)(((void **)((builder)->apply_args))[0]); \\\n"); + printf("\t\t(builder)->stack_used = 0; \\\n"); + reg_offset = sizeof(void *); + if(struct_return_special_reg) + { + reg_offset += sizeof(void *); + } + if(x86_fastcall) + { + printf("\t\t(builder)->word_regs = (builder)->apply_args + %d; \\\n", + reg_offset); + printf("\t\tif(jit_type_get_abi((type)) == jit_abi_fastcall) \\\n"); + printf("\t\t\t(builder)->word_left = 2; \\\n"); + printf("\t\telse; \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + reg_offset += 2 * sizeof(void *); + } + else if(num_word_regs > 0) + { + printf("\t\t(builder)->word_regs = (builder)->apply_args + %d; \\\n", + reg_offset); + printf("\t\t(builder)->word_left = %d; \\\n", num_word_regs); + reg_offset += num_word_regs * sizeof(void *); + } + if(align_long_regs) + { + printf("\t\t(builder)->word_max = (builder)->word_left; \\\n"); + } + if(num_float_regs > 0) + { + printf("\t\t(builder)->float_regs = (builder)->apply_args + %d; \\\n", + reg_offset); + printf("\t\t(builder)->float_left = %d; \\\n", num_float_regs); + } + printf("\t\t(builder)->struct_return = 0; \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to add a word argument to the apply parameters */ + printf("#define jit_apply_builder_add_word(builder,value) \\\n"); + printf("\tdo { \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + printf("\t\tif((builder)->word_left > 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t*((jit_nint *)((builder)->word_regs)) = (jit_nint)(value); \\\n"); + printf("\t\t\t(builder)->word_regs += sizeof(jit_nint); \\\n"); + if(struct_reg_overlaps_word_reg) + { + /* We need to set the struct register slot as well */ + printf("\t\t\tif((builder)->word_left == %d) \\\n", num_word_regs); + printf("\t\t\t{ \\\n"); + printf("\t\t\t\t((jit_nint *)((builder)->apply_args))[1] = (jit_nint)(value); \\\n"); + printf("\t\t\t} \\\n"); + } + printf("\t\t\t--((builder)->word_left); \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t*((jit_nint*)((builder)->stack_args + (builder)->stack_used)) = (jit_nint)(value); \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_nint); \\\n"); + printf("\t\t} \\\n"); + } + else + { + printf("\t\t*((jit_nint*)((builder)->stack_args + (builder)->stack_used)) = (jit_nint)(value); \\\n"); + printf("\t\t(builder)->stack_used += sizeof(jit_nint); \\\n"); + } + printf("\t} while (0)\n\n"); + + /* Macro to get a word argument from the apply parameters */ + printf("#define jit_apply_parser_get_word(builder,type,value) \\\n"); + printf("\tdo { \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + printf("\t\tif((builder)->word_left > 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(value) = (type)(*((jit_nint *)((builder)->word_regs))); \\\n"); + printf("\t\t\t(builder)->word_regs += sizeof(jit_nint); \\\n"); + printf("\t\t\t--((builder)->word_left); \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(value) = (type)(*((jit_nint*)((builder)->stack_args + (builder)->stack_used))); \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_nint); \\\n"); + printf("\t\t} \\\n"); + } + else + { + printf("\t\t(value) = (type)(*((jit_nint*)((builder)->stack_args + (builder)->stack_used))); \\\n"); + printf("\t\t(builder)->stack_used += sizeof(jit_nint); \\\n"); + } + printf("\t} while (0)\n\n"); + + /* Macro to add a large (e.g. dword) argument to the apply parameters */ + printf("#define jit_apply_builder_add_large(builder,type,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\ttype __temp = (type)(value); \\\n"); + printf("\t\tunsigned int __num_words = (sizeof(__temp) + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + if(align_long_regs) + { + printf("\t\tif((builder)->word_left > 0 && \\\n"); + printf("\t\t (((builder)->word_max - (builder)->word_left) %% 2) == 1) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->word_regs += sizeof(jit_nint); \\\n"); + printf("\t\t\t--((builder)->word_left); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tif((builder)->word_left >= __num_words) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((builder)->word_regs, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->word_regs += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left -= __num_words; \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + if(align_long_stack) + { + printf("\t\t\tif(((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t\t{ \\\n"); + printf("\t\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t\t} \\\n"); + } + printf("\t\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + printf("\t\t} \\\n"); + } + else + { + if(align_long_stack) + { + printf("\t\tif(((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + } + printf("\t} while (0)\n\n"); + + /* Macro to get a large (e.g. dword) argument from the apply parameters */ + printf("#define jit_apply_parser_get_large(builder,type,finaltype,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\ttype __temp; \\\n"); + printf("\t\tunsigned int __num_words = (sizeof(__temp) + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + if(align_long_regs) + { + printf("\t\tif((builder)->word_left > 0 && \\\n"); + printf("\t\t (((builder)->word_max - (builder)->word_left) %% 2) == 1) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->word_regs += sizeof(jit_nint); \\\n"); + printf("\t\t\t--((builder)->word_left); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tif((builder)->word_left >= __num_words) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy(&__temp, (builder)->word_regs, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->word_regs += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left -= __num_words; \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + if(align_long_stack) + { + printf("\t\t\tif(((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t\t{ \\\n"); + printf("\t\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t\t} \\\n"); + } + printf("\t\t\tjit_memcpy(&__temp, (builder)->stack_args + (builder)->stack_used, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + printf("\t\t} \\\n"); + } + else + { + if(align_long_stack) + { + printf("\t\tif(((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tjit_memcpy(&__temp, (builder)->stack_args + (builder)->stack_used, sizeof(__temp)); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + } + printf("\t\t(value) = (finaltype)(__temp); \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to add a large (e.g. dword) argument to the apply parameters + on the stack, ignoring word registers */ + printf("#define jit_apply_builder_add_large_stack(builder,type,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\ttype __temp = (type)(value); \\\n"); + printf("\t\tunsigned int __num_words = (sizeof(__temp) + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(align_long_stack) + { + printf("\t\tif(((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to get a large (e.g. dword) argument from the apply parameters + on the stack, ignoring word registers */ + printf("#define jit_apply_parser_get_large_stack(builder,type,finaltype,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\ttype __temp; \\\n"); + printf("\t\tunsigned int __num_words = (sizeof(__temp) + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(align_long_stack) + { + printf("\t\tif(((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tjit_memcpy(&__temp, (builder)->stack_args + (builder)->stack_used, sizeof(__temp)); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t(value) = (finaltype)(__temp); \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to add a large argument to the apply parameters with no alignment */ + printf("#define jit_apply_builder_add_large_noalign(builder,type,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\ttype __temp = (type)(value); \\\n"); + printf("\t\tunsigned int __num_words = (sizeof(__temp) + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + printf("\t\tif((builder)->word_left >= __num_words) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((builder)->word_regs, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->word_regs += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left -= __num_words; \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + printf("\t\t} \\\n"); + } + else + { + printf("\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + } + printf("\t} while (0)\n\n"); + + /* Macro to set the structure return area */ + printf("#define jit_apply_builder_add_struct_return(builder,size,return_buf) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tunsigned int __struct_size = (unsigned int)(size); \\\n"); + printf("\t\tif(__struct_size >= 1 && __struct_size <= 64 && \\\n"); + printf("\t\t (_jit_apply_return_in_reg[(__struct_size - 1) / 8] \\\n"); + printf("\t\t & (1 << ((__struct_size - 1) %% 8))) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->struct_return = 0; \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tif((return_buf) != 0) \\\n"); + printf("\t\t\t\t(builder)->struct_return = (void *)(return_buf); \\\n"); + printf("\t\t\telse \\\n"); + printf("\t\t\t\t(builder)->struct_return = alloca(__struct_size); \\\n"); + if(struct_return_special_reg && !struct_reg_overlaps_word_reg) + { + printf("\t\t\t((void **)((builder)->apply_args))[1] = (builder)->struct_return; \\\n"); + } + else + { + printf("\t\t\tjit_apply_builder_add_word((builder), (builder)->struct_return); \\\n"); + } + printf("\t\t} \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to extract the structure return value, if it is in registers */ + printf("#define jit_apply_builder_get_struct_return(builder,size,return_buf,apply_return) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif(!((builder)->struct_return)) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((return_buf), (apply_return), (size)); \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse if((builder)->struct_return != (void *)(return_buf)) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((return_buf), (builder)->struct_return, (size)); \\\n"); + printf("\t\t} \\\n"); + printf("\t} while (0)\n\n"); + + /* Macro to start the vararg area */ + printf("#define jit_apply_builder_start_varargs(builder) \\\n"); + printf("\tdo { \\\n"); + if(varargs_on_stack) + { + if(num_word_regs > 0 || x86_fastcall) + { + printf("\t\t(builder)->word_left = 0; \\\n"); + } + if(num_float_regs > 0) + { + printf("\t\t(builder)->float_left = 0; \\\n"); + } + } + printf("\t} while (0)\n\n"); + + /* Macro to start the vararg area when parsing a closure */ + printf("#define jit_apply_parser_start_varargs(builder) \\\n"); + printf("\tdo { \\\n"); + if(varargs_on_stack) + { + if(num_word_regs > 0 || x86_fastcall) + { + printf("\t\t(builder)->word_left = 0; \\\n"); + } + if(num_float_regs > 0) + { + printf("\t\t(builder)->float_left = 0; \\\n"); + } + } + printf("\t} while (0)\n\n"); + + /* Add parameter values of various types */ + printf("#define jit_apply_builder_add_sbyte(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_ubyte(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_short(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_ushort(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_int(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_uint(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (jit_nuint)(value));\n"); + printf("#define jit_apply_builder_add_nint(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_nuint(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (jit_nuint)(value));\n"); +#ifdef JIT_NATIVE_INT32 + printf("#define jit_apply_builder_add_long(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large((builder), jit_long, (value));\n"); + printf("#define jit_apply_builder_add_ulong(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large((builder), jit_ulong, (value));\n"); +#else + printf("#define jit_apply_builder_add_long(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (value));\n"); + printf("#define jit_apply_builder_add_ulong(builder,value) \\\n"); + printf("\tjit_apply_builder_add_word((builder), (jit_nuint)(value));\n"); +#endif + if(num_float_regs > 0) + { + /* Pass floating point values in registers, if possible */ + printf("#define jit_apply_builder_add_float32(builder,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif((builder)->float_left > 0) \\\n"); + printf("\t\t{ \\\n"); + if(pass_reg_float_as_double) + name = "jit_float64"; + else if(pass_reg_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("\t\t\t*((%s *)((builder)->float_regs)) = (%s)(value); \\\n", + name, name); + printf("\t\t\t(builder)->float_regs += sizeof(%s); \\\n", name); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + if(pass_stack_float_as_double) + name = "jit_float64"; + else if(pass_stack_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("\t\t\t%s __temp = (%s)(value); \\\n", name, name); + printf("\t\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += (sizeof(%s) + sizeof(jit_nint) - 1) & ~(sizeof(jit_nint) - 1); \\\n", name); + printf("\t\t} \\\n"); + printf("\t} while (0)\n"); + + printf("#define jit_apply_builder_add_float64(builder,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif((builder)->float_left > 0) \\\n"); + printf("\t\t{ \\\n"); + if(pass_reg_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("\t\t\t*((%s *)((builder)->float_regs)) = (%s)(value); \\\n", + name, name); + printf("\t\t\t(builder)->float_regs += sizeof(%s); \\\n", name); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + if(pass_stack_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("\t\t{ \\\n"); + printf("\t\t\t%s __temp = (%s)(value); \\\n", name, name); + printf("\t\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += (sizeof(%s) + sizeof(jit_nint) - 1) & ~(sizeof(jit_nint) - 1); \\\n", name); + printf("\t\t} \\\n"); + printf("\t} while (0)\n"); + + printf("#define jit_apply_builder_add_nfloat(builder,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif((builder)->float_left > 0) \\\n"); + printf("\t\t{ \\\n"); + if(pass_reg_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("\t\t\t*((%s *)((builder)->float_regs)) = (%s)(value); \\\n", + name, name); + printf("\t\t\t(builder)->float_regs += sizeof(%s); \\\n", name); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + if(pass_stack_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("\t\t{ \\\n"); + printf("\t\t\t%s __temp = (%s)(value); \\\n", name, name); + printf("\t\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, &__temp, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += (sizeof(%s) + sizeof(jit_nint) - 1) & ~(sizeof(jit_nint) - 1); \\\n", name); + printf("\t\t} \\\n"); + printf("\t} while (0)\n"); + } + else if(floats_in_word_regs) + { + /* Pass floating point values in word registers */ + if(pass_reg_float_as_double) + name = "jit_float64"; + else if(pass_reg_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("#define jit_apply_builder_add_float32(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large((builder), %s, (value));\n", name); + + if(pass_reg_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("#define jit_apply_builder_add_float64(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large((builder), %s, (value));\n", name); + + if(pass_reg_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("#define jit_apply_builder_add_nfloat(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large((builder), %s, (value));\n", name); + } + else + { + /* Pass floating point values on the stack */ + if(pass_stack_float_as_double) + name = "jit_float64"; + else if(pass_stack_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("#define jit_apply_builder_add_float32(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large_stack((builder), %s, (value));\n", name); + + if(pass_stack_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("#define jit_apply_builder_add_float64(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large_stack((builder), %s, (value));\n", name); + + if(pass_stack_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("#define jit_apply_builder_add_nfloat(builder,value) \\\n"); + printf("\tjit_apply_builder_add_large_stack((builder), %s, (value));\n", name); + } + printf("#define jit_apply_builder_add_struct(builder,value,size,align) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tunsigned int __size = (size); \\\n"); + if(align_long_regs || align_long_stack) + { + printf("\t\tunsigned int __align = (align); \\\n"); + } + printf("\t\tunsigned int __num_words = (__size + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + if(align_long_regs) + { + printf("\t\tif(__align >= sizeof(jit_long) && \\\n"); + printf("\t\t (builder)->word_left > 0 && \\\n"); + printf("\t\t (((builder)->word_max - (builder)->word_left) %% 2) == 1) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->word_regs += sizeof(jit_nint); \\\n"); + printf("\t\t\t--((builder)->word_left); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tif((builder)->word_left >= __num_words) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((builder)->word_regs, (value), __size); \\\n"); + printf("\t\t\t(builder)->word_regs += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left -= __num_words; \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + if(align_long_stack) + { + printf("\t\t\tif(__align >= sizeof(jit_long) && \\\n"); + printf("\t\t\t ((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t\t{ \\\n"); + printf("\t\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t\t} \\\n"); + } + printf("\t\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, (value), __size); \\\n"); + printf("\t\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + printf("\t\t} \\\n"); + } + else + { + if(align_long_stack) + { + printf("\t\tif(__align >= sizeof(jit_long) && \\\n"); + printf("\t\t ((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tjit_memcpy((builder)->stack_args + (builder)->stack_used, (value), __size); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + } + printf("\t} while (0)\n\n"); + + printf("\n"); + + /* Get parameter values of various types from a closure's arguments */ + printf("#define jit_apply_parser_get_sbyte(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_sbyte, (value));\n"); + printf("#define jit_apply_parser_get_ubyte(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_ubyte, (value));\n"); + printf("#define jit_apply_parser_get_short(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_short, (value));\n"); + printf("#define jit_apply_parser_get_ushort(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_ushort, (value));\n"); + printf("#define jit_apply_parser_get_int(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_int, (value));\n"); + printf("#define jit_apply_parser_get_uint(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_uint, (value));\n"); + printf("#define jit_apply_parser_get_nint(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_nint, (value));\n"); + printf("#define jit_apply_parser_get_nuint(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_nuint, (value));\n"); +#ifdef JIT_NATIVE_INT32 + printf("#define jit_apply_parser_get_long(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large((builder), jit_long, jit_long, (value));\n"); + printf("#define jit_apply_parser_get_ulong(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large((builder), jit_ulong, jit_ulong, (value));\n"); +#else + printf("#define jit_apply_parser_get_long(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_long, (value));\n"); + printf("#define jit_apply_parser_get_ulong(builder,value) \\\n"); + printf("\tjit_apply_parser_get_word((builder), jit_ulong, (value));\n"); +#endif + if(num_float_regs > 0) + { + /* Pass floating point values in registers, if possible */ + printf("#define jit_apply_parser_get_float32(builder,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif((builder)->float_left > 0) \\\n"); + printf("\t\t{ \\\n"); + if(pass_reg_float_as_double) + name = "jit_float64"; + else if(pass_reg_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("\t\t\t(value) = (jit_float32)(*((%s *)((builder)->float_regs))); \\\n", + name); + printf("\t\t\t(builder)->float_regs += sizeof(%s); \\\n", name); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + if(pass_stack_float_as_double) + name = "jit_float64"; + else if(pass_stack_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("\t\t\t%s __temp; \\\n", name); + printf("\t\t\tjit_memcpy(&__temp, (builder)->stack_args + (builder)->stack_used, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += (sizeof(%s) + sizeof(jit_nint) - 1) & ~(sizeof(jit_nint) - 1); \\\n", name); + printf("\t\t\t(value) = (jit_float32)__temp; \\\n"); + printf("\t\t} \\\n"); + printf("\t} while (0)\n"); + + printf("#define jit_apply_parser_get_float64(builder,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif((builder)->float_left > 0) \\\n"); + printf("\t\t{ \\\n"); + if(pass_reg_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("\t\t\t(value) = (jit_float64)(*((%s *)((builder)->float_regs))); \\\n", + name); + printf("\t\t\t(builder)->float_regs += sizeof(%s); \\\n", name); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + if(pass_stack_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("\t\t{ \\\n"); + printf("\t\t\t%s __temp; \\\n", name); + printf("\t\t\tjit_memcpy(&__temp, (builder)->stack_args + (builder)->stack_used, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += (sizeof(%s) + sizeof(jit_nint) - 1) & ~(sizeof(jit_nint) - 1); \\\n", name); + printf("\t\t\t(value) = (jit_float64)__temp; \\\n"); + printf("\t\t} \\\n"); + printf("\t} while (0)\n"); + + printf("#define jit_apply_parser_get_nfloat(builder,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tif((builder)->float_left > 0) \\\n"); + printf("\t\t{ \\\n"); + if(pass_reg_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("\t\t\t(value) = (jit_nfloat)(*((%s *)((builder)->float_regs))); \\\n", + name); + printf("\t\t\t(builder)->float_regs += sizeof(%s); \\\n", name); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + if(pass_stack_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("\t\t{ \\\n"); + printf("\t\t\t%s __temp; \\\n", name); + printf("\t\t\tjit_memcpy(&__temp, (builder)->stack_args + (builder)->stack_used, sizeof(__temp)); \\\n"); + printf("\t\t\t(builder)->stack_used += (sizeof(%s) + sizeof(jit_nint) - 1) & ~(sizeof(jit_nint) - 1); \\\n", name); + printf("\t\t\t(value) = (jit_nfloat)__temp; \\\n"); + printf("\t\t} \\\n"); + printf("\t} while (0)\n"); + } + else if(floats_in_word_regs) + { + /* Pass floating point values in word registers */ + if(pass_reg_float_as_double) + name = "jit_float64"; + else if(pass_reg_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("#define jit_apply_parser_get_float32(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large((builder), %s, jit_float32, (value));\n", name); + + if(pass_reg_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("#define jit_apply_parser_get_float64(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large((builder), %s, jit_float64, (value));\n", name); + + if(pass_reg_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("#define jit_apply_parser_get_nfloat(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large((builder), %s, jit_nfloat, (value));\n", name); + } + else + { + /* Pass floating point values on the stack */ + if(pass_stack_float_as_double) + name = "jit_float64"; + else if(pass_stack_float_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float32"; + printf("#define jit_apply_parser_get_float32(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large_stack((builder), %s, jit_float32, (value));\n", name); + + if(pass_stack_double_as_nfloat) + name = "jit_nfloat"; + else + name = "jit_float64"; + printf("#define jit_apply_parser_get_float64(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large_stack((builder), %s, jit_float64, (value));\n", name); + + if(pass_stack_nfloat_as_double) + name = "jit_float64"; + else + name = "jit_nfloat"; + printf("#define jit_apply_parser_get_nfloat(builder,value) \\\n"); + printf("\tjit_apply_parser_get_large_stack((builder), %s, jit_nfloat, (value));\n", name); + } + printf("#define jit_apply_parser_get_struct_return(builder,value) \\\n"); + if(struct_return_special_reg && !struct_reg_overlaps_word_reg) + { + printf("\tdo { \\\n"); + printf("\t\t(value) = ((void **)((builder)->apply_args))[1]; \\\n"); + printf("\t} while (0)\n"); + } + else + { + printf("\tjit_apply_parser_get_word((builder), void *, (value));\n"); + } + printf("#define jit_apply_parser_get_struct(builder,size,align,value) \\\n"); + printf("\tdo { \\\n"); + printf("\t\tunsigned int __size = (size); \\\n"); + if(align_long_regs || align_long_stack) + { + printf("\t\tunsigned int __align = (align); \\\n"); + } + printf("\t\tunsigned int __num_words = (__size + sizeof(jit_nint) - 1) / sizeof(jit_nint); \\\n"); + if(num_word_regs > 0 || x86_fastcall) + { + if(align_long_regs) + { + printf("\t\tif(__align >= sizeof(jit_long) && \\\n"); + printf("\t\t (builder)->word_left > 0 && \\\n"); + printf("\t\t (((builder)->word_max - (builder)->word_left) %% 2) == 1) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->word_regs += sizeof(jit_nint); \\\n"); + printf("\t\t\t--((builder)->word_left); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tif((builder)->word_left >= __num_words) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\tjit_memcpy((value), (builder)->word_regs, __size); \\\n"); + printf("\t\t\t(builder)->word_regs += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left -= __num_words; \\\n"); + printf("\t\t} \\\n"); + printf("\t\telse \\\n"); + printf("\t\t{ \\\n"); + if(align_long_stack) + { + printf("\t\t\tif(__align >= sizeof(jit_long) && \\\n"); + printf("\t\t\t ((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t\t{ \\\n"); + printf("\t\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t\t} \\\n"); + } + printf("\t\t\tjit_memcpy((value), (builder)->stack_args + (builder)->stack_used, __size); \\\n"); + printf("\t\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + printf("\t\t\t(builder)->word_left = 0; \\\n"); + printf("\t\t} \\\n"); + } + else + { + if(align_long_stack) + { + printf("\t\tif(__align >= sizeof(jit_long) && \\\n"); + printf("\t\t ((builder)->stack_used %% sizeof(jit_long)) != 0) \\\n"); + printf("\t\t{ \\\n"); + printf("\t\t\t(builder)->stack_used += sizeof(jit_long) - ((builder)->stack_used %% sizeof(jit_long)); \\\n"); + printf("\t\t} \\\n"); + } + printf("\t\tjit_memcpy((value), (builder)->stack_args + (builder)->stack_used, __size); \\\n"); + printf("\t\t(builder)->stack_used += __num_words * sizeof(jit_nint); \\\n"); + } + printf("\t} while (0)\n"); + printf("\n"); +} + +int main(int argc, char *argv[]) +{ + int size; + int flags; + + /* Detect the number of word registers */ + detect_word_regs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31); + + /* Detect the presence of a structure return register if + "detect_word_regs" was unable to do so */ + if(num_word_regs <= 1) + { + detect_struct_buf = detect_struct_return(1, 2); + } + + /* Determine if the special structure register overlaps a word register */ + detect_struct_buf = detect_struct_overlap(1, 2); + + /* Detect the number of floating-point registers */ + detect_float_regs(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, + 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0); + + /* Determine if floating-point values are passed in word registers */ + if(num_float_regs > 0 && num_word_regs > 0) + { + #ifdef JIT_NATIVE_INT32 + if(num_word_regs == 1) + { + detect_float_overlap((float)(123.78), 1); + } + else + #endif + { + detect_double_overlap(123.78, 1, 2); + } + } + + /* Determine if "long double" values should be demoted to "double" */ + if(floats_in_word_regs) + { + pass_reg_nfloat_as_double = 1; + } + else if(num_float_regs > 0) + { + detect_float_reg_size_regs(48.67, 182.36); + } + else + { + detect_float_reg_size_stack(48.67, 182.36); + } + if(sizeof(jit_float64) == sizeof(jit_nfloat)) + { + pass_stack_nfloat_as_double = 1; + pass_reg_nfloat_as_double = 1; + } + + /* Determine if "float" should be promoted to "double" */ + detect_float_promotion(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, + 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0); + + /* Determine if "double" should be promoted to "nfloat" */ + detect_double_promotion(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, + 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, + 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, + 25.0, 26.0, 27.0, 28.0, 29.0, 30.0, 31.0); + + /* Detect the alignment of "long" values in registers and on the stack */ +#ifdef JIT_NATIVE_INT32 + if(num_word_regs > 1) + { + /* TODO */ + } + else + { + /* TODO */ + } +#endif + + /* Determine if variable arguments are always passed on the stack */ + detect_varargs_on_stack(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31); + + /* Detect the size and positioning of floating-point return values */ + detect_float_return(); + + /* Detect the calling conventions for structures */ + detect_struct_conventions(); + + /* Detect support for x86 FASTCALL handling code (Win32 only) */ +#if defined(PLATFORM_IS_WIN32) + x86_fastcall = 1; +#endif + + /* Detect whether x86 platforms pop the structure return pointer */ +#if defined(PLATFORM_IS_X86) + x86_pop_struct_return = 1; + /* TODO */ +#endif + + /* Determine the maximum sizes */ + detect_max_sizes(); + + /* Detect the location of parent frames and return addresses + in the value returned by "__builtin_frame_address" */ + detect_frame_offsets(); + + /* Print the results */ + printf("/%c This file was auto-generated by \"gen-apply\" - DO NOT EDIT %c/\n\n", '*', '*'); + printf("#ifndef _JIT_APPLY_RULES_H\n"); + printf("#define _JIT_APPLY_RULES_H\n\n"); + printf("#define JIT_APPLY_NUM_WORD_REGS %d\n", num_word_regs); + printf("#define JIT_APPLY_NUM_FLOAT_REGS %d\n", num_float_regs); + printf("#define JIT_APPLY_PASS_STACK_FLOAT_AS_DOUBLE %d\n", + pass_stack_float_as_double); + printf("#define JIT_APPLY_PASS_STACK_FLOAT_AS_NFLOAT %d\n", + pass_stack_float_as_nfloat); + printf("#define JIT_APPLY_PASS_STACK_DOUBLE_AS_NFLOAT %d\n", + pass_stack_double_as_nfloat); + printf("#define JIT_APPLY_PASS_STACK_NFLOAT_AS_DOUBLE %d\n", + pass_stack_nfloat_as_double); + printf("#define JIT_APPLY_PASS_REG_FLOAT_AS_DOUBLE %d\n", + pass_reg_float_as_double); + printf("#define JIT_APPLY_PASS_REG_FLOAT_AS_NFLOAT %d\n", + pass_reg_float_as_nfloat); + printf("#define JIT_APPLY_PASS_REG_DOUBLE_AS_NFLOAT %d\n", + pass_reg_double_as_nfloat); + printf("#define JIT_APPLY_PASS_REG_NFLOAT_AS_DOUBLE %d\n", + pass_reg_nfloat_as_double); + printf("#define JIT_APPLY_RETURN_FLOAT_AS_DOUBLE %d\n", return_float_as_double); + printf("#define JIT_APPLY_RETURN_FLOAT_AS_NFLOAT %d\n", return_float_as_nfloat); + printf("#define JIT_APPLY_RETURN_DOUBLE_AS_NFLOAT %d\n", return_double_as_nfloat); + printf("#define JIT_APPLY_RETURN_NFLOAT_AS_DOUBLE %d\n", return_nfloat_as_double); + printf("#define JIT_APPLY_FLOATS_IN_WORD_REGS %d\n", floats_in_word_regs); + printf("#define JIT_APPLY_RETURN_FLOATS_AFTER %d\n", return_floats_after); + printf("#define JIT_APPLY_VARARGS_ON_STACK %d\n", varargs_on_stack); + printf("#define JIT_APPLY_STRUCT_RETURN_SPECIAL_REG %d\n", struct_return_special_reg); + printf("#define JIT_APPLY_STRUCT_REG_OVERLAPS_WORD_REG %d\n", + struct_reg_overlaps_word_reg); + printf("#define JIT_APPLY_ALIGN_LONG_REGS %d\n", align_long_regs); + printf("#define JIT_APPLY_ALIGN_LONG_STACK %d\n", align_long_stack); + printf("#define JIT_APPLY_STRUCT_RETURN_IN_REG_INIT \\\n\t{"); + max_struct_in_reg = 0; + for(size = 0; size < 64; size += 8) + { + flags = 0; + if(struct_return_in_reg[size]) + { + flags |= 0x01; + max_struct_in_reg = size + 1; + } + if(struct_return_in_reg[size + 1]) + { + flags |= 0x02; + max_struct_in_reg = size + 2; + } + if(struct_return_in_reg[size + 2]) + { + flags |= 0x04; + max_struct_in_reg = size + 3; + } + if(struct_return_in_reg[size + 3]) + { + flags |= 0x08; + max_struct_in_reg = size + 4; + } + if(struct_return_in_reg[size + 4]) + { + flags |= 0x10; + max_struct_in_reg = size + 5; + } + if(struct_return_in_reg[size + 5]) + { + flags |= 0x20; + max_struct_in_reg = size + 6; + } + if(struct_return_in_reg[size + 6]) + { + flags |= 0x40; + max_struct_in_reg = size + 7; + } + if(struct_return_in_reg[size + 7]) + { + flags |= 0x80; + max_struct_in_reg = size + 8; + } + if(size != 0) + { + printf(", 0x%02X", flags); + } + else + { + printf("0x%02X", flags); + } + } + printf("}\n"); + printf("#define JIT_APPLY_MAX_STRUCT_IN_REG %d\n", max_struct_in_reg); + printf("#define JIT_APPLY_MAX_APPLY_SIZE %d\n", max_apply_size); + printf("#define JIT_APPLY_X86_FASTCALL %d\n", x86_fastcall); + printf("#define JIT_APPLY_PARENT_FRAME_OFFSET %d\n", parent_frame_offset); + printf("#define JIT_APPLY_RETURN_ADDRESS_OFFSET %d\n", + return_address_offset); + printf("#define JIT_APPLY_BROKEN_FRAME_BUILTINS %d\n", + broken_frame_builtins); + printf("#define JIT_APPLY_X86_POP_STRUCT_RETURN %d\n", + x86_pop_struct_return); + printf("\n"); + + /* Dump the definition of the "jit_apply_return" union */ + dump_return_union(); + + /* Dump the macros that are used to perform function application */ + dump_apply_macros(); + + /* Print the footer on the output */ + printf("#endif /%c _JIT_APPLY_RULES_H %c/\n", '*', '*'); + + /* Done */ + return 0; +} + +#else /* !(PLATFORM_IS_GCC || PLATFORM_IS_WIN32) */ + +int main(int argc, char *argv[]) +{ + printf("#error \"gcc is required to detect the apply rules\"\n"); + return 0; +} + +#endif /* !(PLATFORM_IS_GCC || PLATFORM_IS_WIN32) */ diff --git a/tutorial/.cvsignore b/tutorial/.cvsignore new file mode 100644 index 0000000..9b064a5 --- /dev/null +++ b/tutorial/.cvsignore @@ -0,0 +1,8 @@ +Makefile +Makefile.in +.deps +*.exe +t1 +t2 +t3 +t4 diff --git a/tutorial/Makefile.am b/tutorial/Makefile.am new file mode 100644 index 0000000..89186a7 --- /dev/null +++ b/tutorial/Makefile.am @@ -0,0 +1,24 @@ + +noinst_PROGRAMS = t1 t2 t3 t4 + +CCLD = $(CXX) + +t1_SOURCES = t1.c +t1_LDADD = $(top_builddir)/jit/libjit.a +t1_DEPENDENCIES = $(top_builddir)/jit/libjit.a + +t2_SOURCES = t2.c +t2_LDADD = $(top_builddir)/jit/libjit.a +t2_DEPENDENCIES = $(top_builddir)/jit/libjit.a + +t3_SOURCES = t3.c +t3_LDADD = $(top_builddir)/jit/libjit.a +t3_DEPENDENCIES = $(top_builddir)/jit/libjit.a + +t4_SOURCES = t4.cpp +t4_LDADD = $(top_builddir)/jitplus/libjitplus.a $(top_builddir)/jit/libjit.a +t4_DEPENDENCIES = $(top_builddir)/jitplus/libjitplus.a \ + $(top_builddir)/jit/libjit.a + +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir) +AM_CXXFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I. -I$(srcdir) diff --git a/tutorial/README b/tutorial/README new file mode 100644 index 0000000..9e30466 --- /dev/null +++ b/tutorial/README @@ -0,0 +1,11 @@ + +This directory contains the source code for the tutorial programs. +The tutorials themselves can be found in "libjit/doc/libjit.texi". + +The source code for these tutorials is hereby placed into the public domain. +You can do whatever you wish with the source, including cutting and pasting +bits and pieces into your own program. + +However, libjit itself remains under the terms of the GNU General Public +License, so you must still obey the terms of the GPL when you link your +program against libjit. diff --git a/tutorial/t1.c b/tutorial/t1.c new file mode 100644 index 0000000..99a1019 --- /dev/null +++ b/tutorial/t1.c @@ -0,0 +1,74 @@ +/* + +Tutorial 1 - mul_add + +Builds and compiles the following function: + +int mul_add(int x, int y, int z) +{ + return x * y + z; +} + +*/ + +#include +#include + +int main(int argc, char **argv) +{ + jit_context_t context; + jit_type_t params[3]; + jit_type_t signature; + jit_function_t function; + jit_value_t x, y, z; + jit_value_t temp1, temp2; + jit_int arg1, arg2, arg3; + void *args[3]; + jit_int result; + + /* Create a context to hold the JIT's primary state */ + context = jit_context_create(); + + /* Lock the context while we build and compile the function */ + jit_context_build_start(context); + + /* Build the function signature */ + params[0] = jit_type_int; + params[1] = jit_type_int; + params[2] = jit_type_int; + signature = jit_type_create_signature + (jit_abi_cdecl, jit_type_int, params, 3, 1); + + /* Create the function object */ + function = jit_function_create(context, signature); + + /* Construct the function body */ + x = jit_value_get_param(function, 0); + y = jit_value_get_param(function, 1); + z = jit_value_get_param(function, 2); + temp1 = jit_insn_mul(function, x, y); + temp2 = jit_insn_add(function, temp1, z); + jit_insn_return(function, temp2); + + /* Compile the function */ + jit_function_compile(function); + + /* Unlock the context */ + jit_context_build_end(context); + + /* Execute the function and print the result */ + arg1 = 3; + arg2 = 5; + arg3 = 2; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + jit_function_apply(function, args, &result); + printf("mul_add(3, 5, 2) = %d\n", (int)result); + + /* Clean up */ + jit_context_destroy(context); + + /* Finished */ + return 0; +} diff --git a/tutorial/t2.c b/tutorial/t2.c new file mode 100644 index 0000000..1160e2b --- /dev/null +++ b/tutorial/t2.c @@ -0,0 +1,111 @@ +/* + +Tutorial 2 - gcd + +Builds and compiles the following function: + +unsigned int gcd(unsigned int x, unsigned int y) +{ + if(x == y) + { + return x; + } + else if(x < y) + { + return gcd(x, y - x); + } + else + { + return gcd(x - y, y); + } +} + +*/ + +#include +#include + +int main(int argc, char **argv) +{ + jit_context_t context; + jit_type_t params[2]; + jit_type_t signature; + jit_function_t function; + jit_value_t x, y; + jit_value_t temp1, temp2; + jit_value_t temp3, temp4; + jit_value_t temp_args[2]; + jit_label_t label1 = jit_label_undefined; + jit_label_t label2 = jit_label_undefined; + jit_uint arg1, arg2; + void *args[2]; + jit_uint result; + + /* Create a context to hold the JIT's primary state */ + context = jit_context_create(); + + /* Lock the context while we build and compile the function */ + jit_context_build_start(context); + + /* Build the function signature */ + params[0] = jit_type_uint; + params[1] = jit_type_uint; + signature = jit_type_create_signature + (jit_abi_cdecl, jit_type_uint, params, 2, 1); + + /* Create the function object */ + function = jit_function_create(context, signature); + + /* Check the condition "if(x == y)" */ + x = jit_value_get_param(function, 0); + y = jit_value_get_param(function, 1); + temp1 = jit_insn_eq(function, x, y); + jit_insn_branch_if_not(function, temp1, &label1); + + /* Implement "return x" */ + jit_insn_return(function, x); + + /* Set "label1" at this position */ + jit_insn_label(function, &label1); + + /* Check the condition "if(x < y)" */ + temp2 = jit_insn_lt(function, x, y); + jit_insn_branch_if_not(function, temp2, &label2); + + /* Implement "return gcd(x, y - x)" */ + temp_args[0] = x; + temp_args[1] = jit_insn_sub(function, y, x); + temp3 = jit_insn_call + (function, "gcd", function, 0, temp_args, 2, 0); + jit_insn_return(function, temp3); + + /* Set "label2" at this position */ + jit_insn_label(function, &label2); + + /* Implement "return gcd(x - y, y)" */ + temp_args[0] = jit_insn_sub(function, x, y); + temp_args[1] = y; + temp4 = jit_insn_call + (function, "gcd", function, 0, temp_args, 2, 0); + jit_insn_return(function, temp4); + + /* Compile the function */ + jit_function_compile(function); + + /* Unlock the context */ + jit_context_build_end(context); + + /* Execute the function and print the result */ + arg1 = 27; + arg2 = 14; + args[0] = &arg1; + args[1] = &arg2; + jit_function_apply(function, args, &result); + printf("gcd(27, 14) = %u\n", (unsigned int)result); + + /* Clean up */ + jit_context_destroy(context); + + /* Finished */ + return 0; +} diff --git a/tutorial/t3.c b/tutorial/t3.c new file mode 100644 index 0000000..d40f19d --- /dev/null +++ b/tutorial/t3.c @@ -0,0 +1,115 @@ +/* + +Tutorial 3 - compiling on-demand + +Builds and compiles the following function: + +int mul_add(int x, int y, int z) +{ + return x * y + z; +} + +Differs from Tutorial 1 in that this version only builds the function +when it is called, not at startup time. + +*/ + +#include +#include + +int compile_mul_add(jit_function_t function) +{ + jit_value_t x, y, z; + jit_value_t temp1, temp2; + + printf("Compiling mul_add on demand\n"); + + x = jit_value_get_param(function, 0); + y = jit_value_get_param(function, 1); + z = jit_value_get_param(function, 2); + + temp1 = jit_insn_mul(function, x, y); + temp2 = jit_insn_add(function, temp1, z); + + jit_insn_return(function, temp2); + return 1; +} + +int main(int argc, char **argv) +{ + jit_context_t context; + jit_type_t params[3]; + jit_type_t signature; + jit_function_t function; + jit_int arg1, arg2, arg3; + void *args[3]; + jit_int result; + + /* Create a context to hold the JIT's primary state */ + context = jit_context_create(); + + /* Lock the context while we construct the function */ + jit_context_build_start(context); + + /* Build the function signature */ + params[0] = jit_type_int; + params[1] = jit_type_int; + params[2] = jit_type_int; + signature = jit_type_create_signature + (jit_abi_cdecl, jit_type_int, params, 3, 1); + + /* Create the function object */ + function = jit_function_create(context, signature); + + /* Make the function recompilable */ + jit_function_set_recompilable(function); + + /* Set the on-demand compiler for "mul_add" */ + jit_function_set_on_demand_compiler(function, compile_mul_add); + + /* Unlock the context. It will be automatically locked for + us when the on-demand compiler is called */ + jit_context_build_end(context); + + /* Execute the function and print the result. This will arrange + to call the on-demand compiler to build the function's body */ + arg1 = 3; + arg2 = 5; + arg3 = 2; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + jit_function_apply(function, args, &result); + printf("mul_add(3, 5, 2) = %d\n", (int)result); + + /* Execute the function again, to demonstrate that the + on-demand compiler is not invoked a second time */ + arg1 = 13; + arg2 = 5; + arg3 = 7; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + jit_function_apply(function, args, &result); + printf("mul_add(13, 5, 7) = %d\n", (int)result); + + /* Force the function to be recompiled. Normally we'd use another + on-demand compiler with greater optimization capabilities */ + jit_function_recompile(function); + + /* Execute the function a third time, after it is recompiled */ + arg1 = 2; + arg2 = 18; + arg3 = -3; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + jit_function_apply(function, args, &result); + printf("mul_add(2, 18, -3) = %d\n", (int)result); + + /* Clean up */ + jit_context_destroy(context); + + /* Finished */ + return 0; +} diff --git a/tutorial/t4.cpp b/tutorial/t4.cpp new file mode 100644 index 0000000..6354a58 --- /dev/null +++ b/tutorial/t4.cpp @@ -0,0 +1,100 @@ +/* + +Tutorial 4 - mul_add, C++ version + +Builds and compiles the following function: + +int mul_add(int x, int y, int z) +{ + return x * y + z; +} + +Differs from Tutorial 3 in that this version is written in C++. + +*/ + +#include +#include + +class mul_add_function : public jit_function +{ +public: + mul_add_function(jit_context& context) : jit_function(context) + { + create(); + set_recompilable(); + } + +protected: + virtual jit_type_t create_signature(); + virtual void build(); +}; + +jit_type_t mul_add_function::create_signature() +{ + // Return type, followed by three parameters, terminated with "end_params". + return signature_helper + (jit_type_int, jit_type_int, jit_type_int, jit_type_int, end_params); +} + +void mul_add_function::build() +{ + printf("Compiling mul_add on demand\n"); + + jit_value x = get_param(0); + jit_value y = get_param(1); + jit_value z = get_param(2); + + insn_return(x * y + z); +} + +int main(int argc, char **argv) +{ + jit_int arg1, arg2, arg3; + void *args[3]; + jit_int result; + + // Create a context to hold the JIT's primary state. + jit_context context; + + // Create the function object. + mul_add_function mul_add(context); + + // Execute the function and print the result. This will arrange + // to call "mul_add_function::build" to build the function's body. + arg1 = 3; + arg2 = 5; + arg3 = 2; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + mul_add.apply(args, &result); + printf("mul_add(3, 5, 2) = %d\n", (int)result); + + // Execute the function again, to demonstrate that the + // on-demand compiler is not invoked a second time. + arg1 = 13; + arg2 = 5; + arg3 = 7; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + mul_add.apply(args, &result); + printf("mul_add(13, 5, 7) = %d\n", (int)result); + + // Force the function to be recompiled. + mul_add.recompile(); + + // Execute the function a third time, after it is recompiled. + arg1 = 2; + arg2 = 18; + arg3 = -3; + args[0] = &arg1; + args[1] = &arg2; + args[2] = &arg3; + mul_add.apply(args, &result); + printf("mul_add(2, 18, -3) = %d\n", (int)result); + + /* Finished */ + return 0; +} -- 2.47.3