From: Rhys Weatherley Date: Fri, 30 Apr 2004 23:29:28 +0000 (+0000) Subject: Initial revision X-Git-Tag: Rhys1~1 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=690437872483a93cc1ffde4f2ec4f10a1ec3adc1;p=francis%2Flibjit.git Initial revision --- 690437872483a93cc1ffde4f2ec4f10a1ec3adc1 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; +}