anonymous
3 months ago
commit
08d35251f5
258 changed files with 151319 additions and 0 deletions
@ -0,0 +1,18 @@ |
|||
# This file is for zig-specific build artifacts. |
|||
# If you have OS-specific or editor-specific files to ignore, |
|||
# such as *.swp or .DS_Store, put those in your global |
|||
# ~/.gitignore and put this in your ~/.gitconfig: |
|||
# |
|||
# [core] |
|||
# excludesfile = ~/.gitignore |
|||
# |
|||
# Cheers! |
|||
# -andrewrk |
|||
|
|||
zig-cache/ |
|||
zig-out/ |
|||
/release/ |
|||
/debug/ |
|||
/build/ |
|||
/build-*/ |
|||
/docgen_tmp/ |
@ -0,0 +1,232 @@ |
|||
GNU GENERAL PUBLIC LICENSE |
|||
Version 3, 29 June 2007 |
|||
|
|||
Copyright © 2007 Free Software Foundation, Inc. <http://fsf.org/> |
|||
|
|||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. |
|||
|
|||
Preamble |
|||
|
|||
The GNU General Public License is a free, copyleft license for software and other kinds of works. |
|||
|
|||
The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. 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 them 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 prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. |
|||
|
|||
For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. 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. |
|||
|
|||
Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. |
|||
|
|||
For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. |
|||
|
|||
Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. |
|||
|
|||
Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. |
|||
|
|||
The precise terms and conditions for copying, distribution and modification follow. |
|||
|
|||
TERMS AND CONDITIONS |
|||
|
|||
0. Definitions. |
|||
|
|||
“This License” refers to version 3 of the GNU General Public License. |
|||
|
|||
“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. |
|||
|
|||
“The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. |
|||
|
|||
To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. |
|||
|
|||
A “covered work” means either the unmodified Program or a work based on the Program. |
|||
|
|||
To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. |
|||
|
|||
To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. |
|||
|
|||
An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. |
|||
|
|||
1. Source Code. |
|||
The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. |
|||
|
|||
A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. |
|||
|
|||
The “System Libraries” of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. |
|||
|
|||
The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. |
|||
|
|||
The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. |
|||
|
|||
The Corresponding Source for a work in source code form is that same work. |
|||
|
|||
2. Basic Permissions. |
|||
All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. |
|||
|
|||
You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. |
|||
|
|||
Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. |
|||
|
|||
3. Protecting Users' Legal Rights From Anti-Circumvention Law. |
|||
No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. |
|||
|
|||
When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. |
|||
|
|||
4. Conveying Verbatim Copies. |
|||
You may convey 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; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. |
|||
|
|||
You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. |
|||
|
|||
5. Conveying Modified Source Versions. |
|||
You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: |
|||
|
|||
a) The work must carry prominent notices stating that you modified it, and giving a relevant date. |
|||
|
|||
b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. |
|||
|
|||
c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. |
|||
|
|||
d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. |
|||
|
|||
A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. |
|||
|
|||
6. Conveying Non-Source Forms. |
|||
You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: |
|||
|
|||
a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. |
|||
|
|||
b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. |
|||
|
|||
c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. |
|||
|
|||
d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. |
|||
|
|||
e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. |
|||
|
|||
A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. |
|||
|
|||
A “User Product” is either (1) a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. |
|||
|
|||
“Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. |
|||
|
|||
If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). |
|||
|
|||
The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. |
|||
|
|||
Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. |
|||
|
|||
7. Additional Terms. |
|||
“Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. |
|||
|
|||
When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. |
|||
|
|||
Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: |
|||
|
|||
a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or |
|||
|
|||
b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or |
|||
|
|||
c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or |
|||
|
|||
d) Limiting the use for publicity purposes of names of licensors or authors of the material; or |
|||
|
|||
e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or |
|||
|
|||
f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. |
|||
|
|||
All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. |
|||
|
|||
If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. |
|||
|
|||
Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. |
|||
|
|||
8. Termination. |
|||
You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). |
|||
|
|||
However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. |
|||
|
|||
Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. |
|||
|
|||
Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. |
|||
|
|||
9. Acceptance Not Required for Having Copies. |
|||
You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. |
|||
|
|||
10. Automatic Licensing of Downstream Recipients. |
|||
Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. |
|||
|
|||
An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. |
|||
|
|||
You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. |
|||
|
|||
11. Patents. |
|||
A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. |
|||
|
|||
A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. |
|||
|
|||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. |
|||
|
|||
In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. |
|||
|
|||
If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. |
|||
|
|||
If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. |
|||
|
|||
A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. |
|||
|
|||
Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. |
|||
|
|||
12. No Surrender of Others' Freedom. |
|||
If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. |
|||
|
|||
13. Use with the GNU Affero General Public License. |
|||
Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. |
|||
|
|||
14. Revised Versions of this License. |
|||
The Free Software Foundation may publish revised and/or new versions of the GNU 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 that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. |
|||
|
|||
If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. |
|||
|
|||
Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. |
|||
|
|||
15. Disclaimer of Warranty. |
|||
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. |
|||
|
|||
16. Limitation of Liability. |
|||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 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. |
|||
|
|||
17. Interpretation of Sections 15 and 16. |
|||
If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. |
|||
|
|||
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 state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. |
|||
|
|||
<one line to give the program's name and a brief idea of what it does.> |
|||
Copyright (C) <year> <name of author> |
|||
|
|||
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 3 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, see <http://www.gnu.org/licenses/>. |
|||
|
|||
Also add information on how to contact you by electronic and paper mail. |
|||
|
|||
If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: |
|||
|
|||
<program> Copyright (C) <year> <name of author> |
|||
This program 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, your program's commands might be different; for a GUI interface, you would use an “about box”. |
|||
|
|||
You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <http://www.gnu.org/licenses/>. |
|||
|
|||
The GNU 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 Lesser General Public License instead of this License. But first, please read <http://www.gnu.org/philosophy/why-not-lgpl.html>. |
@ -0,0 +1,28 @@ |
|||
econ |
|||
==== |
|||
A small, gamified input-output planning demo, As of now the only algorithm implemented (mess.lua) is an adaptation of the harmony function from TANS. |
|||
|
|||
Dependencies |
|||
------------ |
|||
- zig-0.11.0 |
|||
|
|||
Build |
|||
----- |
|||
zig build |
|||
|
|||
Run |
|||
--- |
|||
zig-out/bin/econ [#iterations] |
|||
|
|||
You can change most of the game parameters in the config.json file. |
|||
Planning scripts must contain the follwing functions: |
|||
```js |
|||
start() |
|||
assignResources() |
|||
assignWages() |
|||
assignPrices() |
|||
printInventory() |
|||
printConsumption() |
|||
printProduction() |
|||
``` |
|||
`start()` is only called once, at the start of the game and doesn't return anything. The three assign functions are called in that order, every iteration, and should return an array with the assignations (check mess.lua). The print functions don't return anything and you can leave them empty if you want. |
@ -0,0 +1,41 @@ |
|||
const std = @import("std"); |
|||
const lua = @import("lib/luajit/build.zig"); |
|||
|
|||
pub fn build(b: *std.build.Builder) !void { |
|||
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
|||
defer std.debug.assert(gpa.deinit() != .leak); |
|||
|
|||
const allocator = gpa.allocator(); |
|||
|
|||
var arena = std.heap.ArenaAllocator.init(allocator); |
|||
defer arena.deinit(); |
|||
|
|||
const arena_allocator = arena.allocator(); |
|||
|
|||
const target = b.standardTargetOptions(.{}); |
|||
const mode = b.standardOptimizeOption(.{}); |
|||
|
|||
const exe = b.addExecutable(.{ |
|||
.name = "econ", |
|||
.root_source_file = .{ .path = "src/main.zig" }, |
|||
.target = target, |
|||
.optimize = mode, |
|||
}); |
|||
|
|||
exe.addAnonymousModule( |
|||
"ringbuffer", |
|||
.{ .source_file = .{ .path = "lib/ringbuffer/src/lib.zig" } }, |
|||
); |
|||
try lua.addLuajit(arena_allocator, exe); |
|||
|
|||
b.installArtifact(exe); |
|||
|
|||
const run_cmd = b.addRunArtifact(exe); |
|||
run_cmd.step.dependOn(b.getInstallStep()); |
|||
if (b.args) |args| { |
|||
run_cmd.addArgs(args); |
|||
} |
|||
|
|||
const run_step = b.step("run", "Run the app"); |
|||
run_step.dependOn(&run_cmd.step); |
|||
} |
@ -0,0 +1,109 @@ |
|||
{ |
|||
"start": { |
|||
"seed": 123, |
|||
"average_agent_labor": 40, |
|||
"initial_stock_days": 3, |
|||
"task": {}, |
|||
"agent": { |
|||
"number": { |
|||
"min": 250, |
|||
"max": 500 |
|||
}, |
|||
"funds": { |
|||
"min": 100, |
|||
"max": 200 |
|||
}, |
|||
"age": { |
|||
"min": 6570, |
|||
"max": 23725 |
|||
}, |
|||
"health": { |
|||
"min": 0.8, |
|||
"max": 1 |
|||
}, |
|||
"happiness": { |
|||
"min": 0.4, |
|||
"max": 1 |
|||
}, |
|||
"consumption": { |
|||
"min": 10, |
|||
"max": 20 |
|||
}, |
|||
"prefs": { |
|||
"min": 10, |
|||
"max": 20 |
|||
}, |
|||
"pref_threshold": 0.7 |
|||
}, |
|||
"primary": { |
|||
"number": { |
|||
"min": 1, |
|||
"max": 5 |
|||
}, |
|||
"cost": { |
|||
"labor": { |
|||
"min": 0.1, |
|||
"max": 2 |
|||
} |
|||
}, |
|||
"necessity": { |
|||
"min": 0, |
|||
"max": 0.5 |
|||
} |
|||
}, |
|||
"secondary": { |
|||
"number": { |
|||
"min": 10, |
|||
"max": 20 |
|||
}, |
|||
"cost": { |
|||
"labor": { |
|||
"min": 0.1, |
|||
"max": 2 |
|||
}, |
|||
"number": { |
|||
"min": 1, |
|||
"max": 10 |
|||
}, |
|||
"quantity": { |
|||
"min": 0.001, |
|||
"max": 0.1 |
|||
} |
|||
}, |
|||
"necessity": { |
|||
"min": 0, |
|||
"max": 0.75 |
|||
} |
|||
}, |
|||
"tertiary": { |
|||
"number": { |
|||
"min": 10, |
|||
"max": 20 |
|||
}, |
|||
"cost": { |
|||
"labor": { |
|||
"min": 0.1, |
|||
"max": 2 |
|||
}, |
|||
"number": { |
|||
"min": 1, |
|||
"max": 10 |
|||
}, |
|||
"quantity": { |
|||
"min": 0.001, |
|||
"max": 0.1 |
|||
} |
|||
}, |
|||
"necessity": { |
|||
"min": 0, |
|||
"max": 1 |
|||
} |
|||
}, |
|||
"script": "mess.lua" |
|||
}, |
|||
"run": { |
|||
"task": { |
|||
"unspent": "remove" |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,2 @@ |
|||
zig-cache |
|||
zig-out |
@ -0,0 +1,23 @@ |
|||
Boost Software License - Version 1.0 - August 17th, 2003 |
|||
|
|||
Permission is hereby granted, free of charge, to any person or organization |
|||
obtaining a copy of the software and accompanying documentation covered by |
|||
this license (the "Software") to use, reproduce, display, distribute, |
|||
execute, and transmit the Software, and to prepare derivative works of the |
|||
Software, and to permit third-parties to whom the Software is furnished to |
|||
do so, all subject to the following: |
|||
|
|||
The copyright notices in the Software and this entire statement, including |
|||
the above license grant, this restriction and the following disclaimer, |
|||
must be included in all copies of the Software, in whole or in part, and |
|||
all derivative works of the Software, unless such copies or derivative |
|||
works are solely in the form of machine-executable object code generated by |
|||
a source language processor. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
|||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
|||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
|||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|||
DEALINGS IN THE SOFTWARE. |
@ -0,0 +1,25 @@ |
|||
zig-luajit |
|||
========== |
|||
|
|||
Zig bindings for luajit. |
|||
|
|||
The luajit build process has been ported to zig build system to try and support |
|||
cross compilation for all targets supported by both projects. |
|||
|
|||
Currently, this has only been tested on OSX and Linux. But bug reports and pull |
|||
requests about the other platforms are welcome. |
|||
|
|||
The bindings will stay as is (i.e., only leveraging ``@cImport``) as existing |
|||
projects already provide a good abstraction layer over the plain C functions. |
|||
|
|||
I recommend to use this project together with `zoltan`_ if a more zig friendly API |
|||
is desired. |
|||
|
|||
.. _zoltan: https://github.com/ranciere/zoltan |
|||
|
|||
Licensing |
|||
--------- |
|||
|
|||
While the work in this repository is under Boost Source License unless otherwise |
|||
stated (such as in the ``myscript.lua`` file), the luajit's license need to be |
|||
properly reproduced as well. |
@ -0,0 +1,532 @@ |
|||
const std = @import("std"); |
|||
|
|||
//fn thisDir(comptime suffix: []const u8) []const u8 { |
|||
// @setEvalBranchQuota(10000); |
|||
// return comptime blk: { |
|||
// const root_dir = std.fs.path.dirname(@src().file) orelse "."; |
|||
// break :blk root_dir ++ "/" ++ suffix; |
|||
// }; |
|||
//} |
|||
|
|||
pub fn module(b: *std.build.Builder) *std.build.Module { |
|||
return b.createModule(.{ |
|||
.source_file = .{ .path = (comptime thisDir()) ++ "/src/main.zig" }, |
|||
}); |
|||
} |
|||
|
|||
pub const luajit_src_path = thisDir() ++ "/third_party/luajit/src/"; |
|||
const ljlib_c: []const []const u8 = &.{ |
|||
"lib_base.c", "lib_math.c", "lib_bit.c", "lib_string.c", |
|||
"lib_table.c", "lib_io.c", "lib_os.c", "lib_package.c", |
|||
"lib_debug.c", "lib_jit.c", "lib_ffi.c", "lib_buffer.c", |
|||
}; |
|||
|
|||
// NOTE: as opposed to the Makefile, does not include ljlib_c |
|||
const ljcore_c: []const []const u8 = &.{ |
|||
"lj_assert.c", "lj_gc.c", "lj_err.c", "lj_char.c", |
|||
"lj_bc.c", "lj_obj.c", "lj_buf.c", "lj_str.c", |
|||
"lj_tab.c", "lj_func.c", "lj_udata.c", "lj_meta.c", |
|||
"lj_debug.c", "lj_prng.c", "lj_state.c", "lj_dispatch.c", |
|||
"lj_vmevent.c", "lj_vmmath.c", "lj_strscan.c", "lj_strfmt.c", |
|||
"lj_strfmt_num.c", "lj_serialize.c", "lj_api.c", "lj_profile.c", |
|||
"lj_lex.c", "lj_parse.c", "lj_bcread.c", "lj_bcwrite.c", |
|||
"lj_load.c", "lj_ir.c", "lj_opt_mem.c", "lj_opt_fold.c", |
|||
"lj_opt_narrow.c", "lj_opt_dce.c", "lj_opt_loop.c", "lj_opt_split.c", |
|||
"lj_opt_sink.c", "lj_mcode.c", "lj_snap.c", "lj_record.c", |
|||
"lj_crecord.c", "lj_ffrecord.c", "lj_asm.c", "lj_trace.c", |
|||
"lj_gdbjit.c", "lj_ctype.c", "lj_cdata.c", "lj_cconv.c", |
|||
"lj_ccall.c", "lj_ccallback.c", "lj_carith.c", "lj_clib.c", |
|||
"lj_cparse.c", "lj_lib.c", "lj_alloc.c", "lib_aux.c", |
|||
"lib_init.c", |
|||
}; |
|||
|
|||
fn thisDir() []const u8 { |
|||
return std.fs.path.dirname(@src().file) orelse "."; |
|||
} |
|||
|
|||
pub fn build(b: *std.build.Builder) void { |
|||
const target = b.standardTargetOptions(.{}); |
|||
const mode = b.standardOptimizeOption(.{}); |
|||
|
|||
const lib = b.addStaticLibrary(.{ |
|||
.name = "zig-luajit", |
|||
.root_source_file = .{ .path = "src/main.zig" }, |
|||
.target = target, |
|||
.optimize = mode, |
|||
}); |
|||
addLuajit(lib) catch unreachable; |
|||
lib.addIncludePath(luajit_src_path); |
|||
lib.install(); |
|||
|
|||
const main_tests = b.addTest("src/main.zig"); |
|||
main_tests.setTarget(target); |
|||
main_tests.setBuildMode(mode); |
|||
addLuajit(main_tests) catch unreachable; |
|||
|
|||
const test_step = b.step("test", "Run library tests"); |
|||
test_step.dependOn(&main_tests.step); |
|||
|
|||
buildExample(b, target, mode); |
|||
} |
|||
|
|||
pub fn buildExample( |
|||
b: *std.build.Builder, |
|||
target: std.zig.CrossTarget, |
|||
mode: std.builtin.Mode, |
|||
) void { |
|||
const exe = b.addExecutable(.{ |
|||
.name = "my-lua-proj", |
|||
.root_source_file = .{ .path = "example/main.zig" }, |
|||
.target = target, |
|||
.optimize = mode, |
|||
}); |
|||
// earlier in your build script; you would have |
|||
// const addLuajit = @import("path/to/this/build.zig").addLuajit; |
|||
addLuajit(exe) catch unreachable; |
|||
|
|||
// run step |
|||
const run_exe = exe.run(); |
|||
run_exe.cwd = "./example/"; |
|||
const run_step = b.step("run-example", "Run luajit example"); |
|||
run_step.dependOn(&run_exe.step); |
|||
|
|||
// add some tests! |
|||
} |
|||
|
|||
/// returns an owned slice |
|||
fn generateLjArchOutput( |
|||
zig_exe: []const u8, |
|||
allocator: std.mem.Allocator, |
|||
) ![]u8 { |
|||
var zig_cmd = std.ArrayList([]const u8).init(allocator); |
|||
defer zig_cmd.deinit(); |
|||
|
|||
try zig_cmd.append(zig_exe); |
|||
try zig_cmd.append("cc"); |
|||
try zig_cmd.append("-E"); |
|||
try zig_cmd.append(luajit_src_path ++ "lj_arch.h"); |
|||
try zig_cmd.append("-dM"); |
|||
|
|||
var child_proc = std.ChildProcess.init(zig_cmd.items, allocator); |
|||
|
|||
child_proc.stdin_behavior = .Close; |
|||
child_proc.stdout_behavior = .Pipe; |
|||
child_proc.stderr_behavior = .Inherit; |
|||
|
|||
child_proc.spawn() catch |err| { |
|||
std.log.err( |
|||
"Error spawning {s}: {s}\n", |
|||
.{ zig_cmd.items[0], @errorName(err) }, |
|||
); |
|||
return err; |
|||
}; |
|||
|
|||
return try child_proc.stdout.?.reader().readAllAlloc( |
|||
allocator, |
|||
2 * 1024 * 1024, |
|||
); |
|||
} |
|||
|
|||
fn createBuildvmExeStep( |
|||
builder: *std.build.Builder, |
|||
host_cflags: []const []const u8, |
|||
) !*std.build.LibExeObjStep { |
|||
const buildvm_c: []const []const u8 = &.{ |
|||
luajit_src_path ++ "/host/buildvm.c", |
|||
luajit_src_path ++ "/host/buildvm_asm.c", |
|||
luajit_src_path ++ "/host/buildvm_peobj.c", |
|||
luajit_src_path ++ "/host/buildvm_lib.c", |
|||
luajit_src_path ++ "/host/buildvm_fold.c", |
|||
}; |
|||
|
|||
var buildvm_exe = builder.addExecutable(.{ |
|||
.name = "buildvm", |
|||
.target = std.zig.CrossTarget.fromTarget(builder.host.target), |
|||
.optimize = .ReleaseSmall, |
|||
}); |
|||
buildvm_exe.addIncludePath(.{ .path = luajit_src_path }); |
|||
buildvm_exe.addCSourceFiles(buildvm_c, host_cflags); |
|||
buildvm_exe.linkSystemLibrary("m"); |
|||
buildvm_exe.linkLibC(); |
|||
return buildvm_exe; |
|||
} |
|||
|
|||
fn createBuildvmGenStep( |
|||
builder: *std.build.Builder, |
|||
buildvm_exe: *std.build.LibExeObjStep, |
|||
target_os_tag: std.Target.Os.Tag, |
|||
) !*std.build.Step { |
|||
var buildvm_gen_step = try builder.allocator.create(std.build.Step); |
|||
buildvm_gen_step.* = std.build.Step.init(.{ |
|||
.id = .custom, |
|||
.name = "buildvm", |
|||
.owner = builder, |
|||
}); |
|||
|
|||
// those targets all build the same |
|||
const simple_targets: []const []const u8 = &.{ |
|||
"bcdef", "ffdef", "libdef", "recdef", |
|||
}; |
|||
|
|||
inline for (simple_targets) |t| { |
|||
const target = "lj_" ++ t ++ ".h"; |
|||
const buildvm_gen_simple = builder.addRunArtifact(buildvm_exe); |
|||
buildvm_gen_simple.cwd = luajit_src_path; |
|||
buildvm_gen_simple.addArgs(&.{ |
|||
"-m", t, "-o", target, |
|||
}); |
|||
buildvm_gen_simple.addArgs(ljlib_c); |
|||
buildvm_gen_step.dependOn(&buildvm_gen_simple.step); |
|||
} |
|||
|
|||
// remains 3 target that require custom handling: |
|||
// * folddef |
|||
// * vmdef |
|||
// * a platform dependent one |
|||
|
|||
{ // folddef |
|||
var buildvm_gen_folddef = builder.addRunArtifact(buildvm_exe); |
|||
buildvm_gen_folddef.cwd = luajit_src_path; |
|||
buildvm_gen_folddef.addArgs(&.{ |
|||
"-m", "folddef", "-o", "lj_folddef.h", "lj_opt_fold.c", |
|||
}); |
|||
buildvm_gen_step.dependOn(&buildvm_gen_folddef.step); |
|||
} |
|||
|
|||
{ // vmdef |
|||
var buildvm_gen_vmdef = builder.addRunArtifact(buildvm_exe); |
|||
buildvm_gen_vmdef.cwd = luajit_src_path; |
|||
buildvm_gen_vmdef.addArgs(&.{ |
|||
"-m", "vmdef", "-o", "jit/vmdef.lua", |
|||
}); |
|||
buildvm_gen_vmdef.addArgs(ljlib_c); |
|||
buildvm_gen_step.dependOn(&buildvm_gen_vmdef.step); |
|||
} |
|||
|
|||
{ // platform dependent |
|||
const mode = blk: { |
|||
switch (target_os_tag) { |
|||
.macos, .ios => break :blk "machasm", |
|||
.windows => break :blk "peobj", |
|||
else => break :blk "elfasm", |
|||
} |
|||
}; |
|||
const target = blk: { |
|||
switch (target_os_tag) { |
|||
.windows => break :blk "lj_vm.o", |
|||
else => break :blk "lj_vm.S", |
|||
} |
|||
}; |
|||
var buildvm_gen_ljvm = builder.addRunArtifact(buildvm_exe); |
|||
buildvm_gen_ljvm.cwd = luajit_src_path; |
|||
buildvm_gen_ljvm.addArgs(&.{ |
|||
"-m", mode, "-o", target, |
|||
}); |
|||
buildvm_gen_step.dependOn(&buildvm_gen_ljvm.step); |
|||
} |
|||
return buildvm_gen_step; |
|||
} |
|||
|
|||
pub fn addLuajit(allocator: std.mem.Allocator, exe: *std.build.LibExeObjStep) !void { |
|||
const target_os_tag = exe.target.os_tag orelse |
|||
exe.step.owner.host.target.os.tag; |
|||
const target_arch = exe.target.cpu_arch orelse |
|||
exe.step.owner.host.target.cpu.arch; |
|||
|
|||
var stdout = try generateLjArchOutput( |
|||
exe.step.owner.zig_exe, |
|||
allocator, |
|||
); |
|||
|
|||
var minilua_flags = std.ArrayList([]const u8).init(allocator); |
|||
defer minilua_flags.deinit(); |
|||
|
|||
// disable UB-san as minilua relies way too much on them |
|||
// NOTE: we could replace minilua with other interpreter |
|||
try minilua_flags.append("-fno-sanitize=undefined"); |
|||
|
|||
// TARGET_ARCH from target cpu arch |
|||
// NOTE: TARGET_ARCH ends up containing many things but we can discriminate |
|||
// them in three categories: |
|||
// - target cpu arch |
|||
// - target os related (though there is only things for PS3 here, that we |
|||
// do not support) |
|||
// - features related to the target platform |
|||
switch (target_arch) { |
|||
.aarch64_be => { |
|||
try minilua_flags.append("-D__AARCH64EB__=1"); |
|||
}, |
|||
.powerpc => { |
|||
try minilua_flags.append("-DLJ_ARCH_ENDIAN=LUAJIT_BE"); |
|||
}, |
|||
.powerpcle => { |
|||
try minilua_flags.append("-DLJ_ARCH_ENDIAN=LUAJIT_LE"); |
|||
}, |
|||
.mipsel, .mips64el => { |
|||
try minilua_flags.append("-D__MIPSEL__=1"); |
|||
}, |
|||
else => {}, |
|||
} |
|||
|
|||
// TARGET_LJARCH |
|||
const luajit_arch_name = blk: { |
|||
switch (target_arch) { |
|||
.arm, .mips, .mips64, .mipsel, .mips64el => { |
|||
break :blk @tagName(target_arch); |
|||
}, |
|||
// other arch have different names for luajit |
|||
.aarch64, .aarch64_be => { |
|||
break :blk "arm64"; |
|||
}, |
|||
.x86 => { |
|||
break :blk "x86"; |
|||
}, |
|||
.x86_64 => { |
|||
break :blk "x64"; |
|||
}, |
|||
.powerpc, .powerpcle => { |
|||
break :blk "ppc"; |
|||
}, |
|||
// every other arch are not supported |
|||
else => { |
|||
return error.UnsupportedCpuArchitecture; |
|||
}, |
|||
} |
|||
}; |
|||
var minilua_luajit_target = try std.fmt.allocPrint( |
|||
allocator, |
|||
"-DLUAJIT_TARGET=LUAJIT_ARCH_{s}", |
|||
.{luajit_arch_name}, |
|||
); |
|||
try minilua_flags.append(minilua_luajit_target); |
|||
|
|||
// CCOPT_$arch |
|||
switch (target_arch) { |
|||
.x86 => { |
|||
try minilua_flags.append("-march=i686"); |
|||
try minilua_flags.append("-msse"); |
|||
try minilua_flags.append("-msse2"); |
|||
try minilua_flags.append("-mfpmath=sse"); |
|||
}, |
|||
// No specific flags for other platforms by default |
|||
else => {}, |
|||
} |
|||
|
|||
var dasm_aflags = std.ArrayList([]const u8).init(allocator); |
|||
defer dasm_aflags.deinit(); |
|||
|
|||
if (std.mem.indexOf(u8, stdout, "LJ_LE 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("ENDIAN_LE"); |
|||
} else { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("ENDIAN_BE"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_BITS 64")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("P64"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_HASJIT 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("JIT"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_HASFFI 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("FFI"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_DUALNUM 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("DUALNUM"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_HASFPU 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("FPU"); |
|||
try minilua_flags.append("-DLJ_ARCH_HASFPU=1"); |
|||
} else { |
|||
try minilua_flags.append("-DLJ_ARCH_HASFPU=0"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_ABI_SOFTFP 1")) |_| { |
|||
try minilua_flags.append("-DLJ_ABI_SOFTFP=1"); |
|||
} else { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("HFABI"); |
|||
try minilua_flags.append("-DLJ_ABI_SOFTFP=0"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_NO_UNWIND 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("NO_UNWIND"); |
|||
try minilua_flags.append("-DLUAJIT_NO_UNWIND"); |
|||
} |
|||
|
|||
const version_define = "LJ_ARCH_VERSION "; |
|||
const version = blk: { |
|||
var vs: []const u8 = ""; |
|||
if (std.mem.indexOf(u8, stdout, version_define)) |i| { |
|||
const version_start = i + version_define.len; |
|||
if (std.mem.indexOfPosLinear( |
|||
u8, |
|||
stdout, |
|||
version_start, |
|||
"\n", |
|||
)) |version_end| { |
|||
vs = stdout[version_start..version_end]; |
|||
} |
|||
} |
|||
break :blk try std.fmt.allocPrint(allocator, "VER={s}", .{vs}); |
|||
}; |
|||
|
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append(version); |
|||
|
|||
if (target_os_tag == .windows) { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("WIN"); |
|||
} |
|||
|
|||
var dasm_arch = luajit_arch_name; |
|||
if (target_arch == .aarch64) { |
|||
// need to rename aarch64 to arm64 to match the vm_<arch>.dasc filename |
|||
dasm_arch = "arm64"; |
|||
} |
|||
if (target_arch == .x86_64) { |
|||
if (std.mem.indexOf(u8, stdout, "LJ_FR2 1")) |_| {} else { |
|||
dasm_arch = "x86"; |
|||
} |
|||
} else if (target_arch == .arm) { |
|||
if (target_os_tag == .ios) { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("IOS"); |
|||
} |
|||
} else { |
|||
if (std.mem.indexOf(u8, stdout, "LJ_TARGET_MIPSR6 ")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("MIPSR6"); |
|||
} |
|||
if (target_arch == .powerpc) { |
|||
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_SQRT 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("SQRT"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_ROUND 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("ROUND"); |
|||
} |
|||
if (std.mem.indexOf(u8, stdout, "LJ_ARCH_PPC32ON64 1")) |_| { |
|||
try dasm_aflags.append("-D"); |
|||
try dasm_aflags.append("GPR64"); |
|||
} |
|||
// .ps3 not available (has .ps4 though) |
|||
//if (target_os_tag == .ps3) { |
|||
// try dasm_aflags.append("-D"); |
|||
// try dasm_aflags.append("PPE"); |
|||
// try dasm_aflags.append("-D"); |
|||
// try dasm_aflags.append("TOC"); |
|||
//} |
|||
} |
|||
} |
|||
|
|||
try dasm_aflags.append("-o"); |
|||
try dasm_aflags.append("host/buildvm_arch.h"); |
|||
|
|||
var dasm_dasc = try std.fmt.allocPrint( |
|||
allocator, |
|||
"vm_{s}.dasc", |
|||
.{dasm_arch}, |
|||
); |
|||
|
|||
try dasm_aflags.append(dasm_dasc); |
|||
|
|||
// compile minilua for the host machine, will be needed to generate various |
|||
// files before the compilation of luajit |
|||
var minilua_exe = exe.step.owner.addExecutable(.{ |
|||
.name = "minilua", |
|||
.target = std.zig.CrossTarget.fromTarget(exe.step.owner.host.target), |
|||
.optimize = .ReleaseSmall, |
|||
}); |
|||
minilua_exe.addCSourceFile(.{ |
|||
.file = .{ .path = luajit_src_path ++ "/host/minilua.c" }, |
|||
.flags = minilua_flags.items, |
|||
}); |
|||
minilua_exe.linkSystemLibrary("m"); |
|||
minilua_exe.linkLibC(); |
|||
const minilua_run = exe.step.owner.addRunArtifact(minilua_exe); |
|||
|
|||
minilua_run.cwd = luajit_src_path; |
|||
minilua_run.addArg("../dynasm/dynasm.lua"); |
|||
minilua_run.addArgs(dasm_aflags.items); |
|||
|
|||
var buildvm_exe = try createBuildvmExeStep( |
|||
exe.step.owner, |
|||
minilua_flags.items, |
|||
); |
|||
buildvm_exe.step.dependOn(&minilua_run.step); |
|||
|
|||
var buildvm_gen_step = try createBuildvmGenStep( |
|||
exe.step.owner, |
|||
buildvm_exe, |
|||
target_os_tag, |
|||
); |
|||
|
|||
var luajit_sys_libs = |
|||
try std.ArrayList([]const u8).initCapacity(allocator, 4); |
|||
defer luajit_sys_libs.deinit(); |
|||
var luajit_flags = |
|||
try std.ArrayList([]const u8).initCapacity(allocator, 4); |
|||
defer luajit_flags.deinit(); |
|||
|
|||
try luajit_flags.append("-fno-sanitize=undefined"); |
|||
try luajit_flags.append("-O2"); |
|||
try luajit_flags.append("-fomit-frame-pointer"); |
|||
|
|||
try luajit_sys_libs.append("m"); |
|||
|
|||
switch (target_os_tag) { |
|||
.freebsd => try luajit_sys_libs.append("dl"), |
|||
.linux => { |
|||
try luajit_sys_libs.append("dl"); |
|||
try luajit_sys_libs.append("unwind"); |
|||
try luajit_flags.append("-funwind-tables"); |
|||
try luajit_flags.append("-DLUAJIT_UNWIND_EXTERNAL"); |
|||
}, |
|||
// NOTE: PS3 would require pthread but it's not supported by zig |
|||
else => {}, |
|||
} |
|||
|
|||
var luajit_src = try std.ArrayList([]const u8).initCapacity( |
|||
allocator, |
|||
ljlib_c.len + ljcore_c.len, |
|||
); |
|||
inline for (ljlib_c) |c| { |
|||
try luajit_src.append(luajit_src_path ++ c); |
|||
} |
|||
inline for (ljcore_c) |c| { |
|||
try luajit_src.append(luajit_src_path ++ c); |
|||
} |
|||
|
|||
var luajit_lib = exe.step.owner.addStaticLibrary(.{ |
|||
.name = "luajit", |
|||
.target = exe.target, |
|||
.optimize = exe.optimize, |
|||
}); |
|||
luajit_lib.step.dependOn(buildvm_gen_step); |
|||
luajit_lib.linkLibC(); |
|||
luajit_lib.addCSourceFiles(luajit_src.items, luajit_flags.items); |
|||
if (target_os_tag == .windows) { |
|||
luajit_lib.addObjectFile(.{ .path = luajit_src_path ++ "/lj_vm.o" }); |
|||
} else { |
|||
luajit_lib.addAssemblyFile(.{ .path = luajit_src_path ++ "/lj_vm.S" }); |
|||
} |
|||
|
|||
exe.linkLibrary(luajit_lib); |
|||
for (luajit_sys_libs.items) |l| { |
|||
exe.linkSystemLibrary(l); |
|||
} |
|||
|
|||
exe.addIncludePath(.{ .path = luajit_src_path }); |
|||
|
|||
exe.addModule("luajit", module(exe.step.owner)); |
|||
} |
|||
|
|||
pub fn includeLuajit(exe: *std.build.LibExeObjStep) void { |
|||
exe.addIncludePath(luajit_src_path); |
|||
} |
@ -0,0 +1,16 @@ |
|||
const lj = @import("luajit"); |
|||
|
|||
const Error = error{ |
|||
LuaError, |
|||
}; |
|||
|
|||
pub fn main() !void { |
|||
const L = lj.c.luaL_newstate(); |
|||
lj.c.luaL_openlibs(L); |
|||
|
|||
if (lj.c.luaL_loadfile(L, "myscript.lua") != 0) return error.LuaError; |
|||
if (lj.c.lua_pcall(L, 0, lj.c.LUA_MULTRET, 0) != 0) return error.LuaError; |
|||
|
|||
// That's all floks! |
|||
lj.c.lua_close(L); |
|||
} |
@ -0,0 +1,424 @@ |
|||
------------------------------------------------------------------------------ |
|||
-- Lua SciMark (2010-12-20). |
|||
-- |
|||
-- A literal translation of SciMark 2.0a, written in Java and C. |
|||
-- Credits go to the original authors Roldan Pozo and Bruce Miller. |
|||
-- See: http://math.nist.gov/scimark2/ |
|||
------------------------------------------------------------------------------ |
|||
-- Copyright (C) 2006-2010 Mike Pall. All rights reserved. |
|||
-- |
|||
-- Permission is hereby granted, free of charge, to any person obtaining |
|||
-- a copy of this software and associated documentation files (the |
|||
-- "Software"), to deal in the Software without restriction, including |
|||
-- without limitation the rights to use, copy, modify, merge, publish, |
|||
-- distribute, sublicense, and/or sell copies of the Software, and to |
|||
-- permit persons to whom the Software is furnished to do so, subject to |
|||
-- the following conditions: |
|||
-- |
|||
-- The above copyright notice and this permission notice shall be |
|||
-- included in all copies or substantial portions of the Software. |
|||
-- |
|||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
-- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|||
-- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
|||
-- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
|||
-- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
|||
-- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
|||
-- |
|||
-- [ MIT license: http://www.opensource.org/licenses/mit-license.php ] |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local SCIMARK_VERSION = "2010-12-10" |
|||
local SCIMARK_COPYRIGHT = "Copyright (C) 2006-2010 Mike Pall" |
|||
|
|||
local MIN_TIME = 2.0 |
|||
local RANDOM_SEED = 101009 -- Must be odd. |
|||
local SIZE_SELECT = "small" |
|||
|
|||
local benchmarks = { |
|||
"FFT", "SOR", "MC", "SPARSE", "LU", |
|||
small = { |
|||
FFT = { 1024 }, |
|||
SOR = { 100 }, |
|||
MC = { }, |
|||
SPARSE = { 1000, 5000 }, |
|||
LU = { 100 }, |
|||
}, |
|||
large = { |
|||
FFT = { 1048576 }, |
|||
SOR = { 1000 }, |
|||
MC = { }, |
|||
SPARSE = { 100000, 1000000 }, |
|||
LU = { 1000 }, |
|||
}, |
|||
} |
|||
|
|||
local abs, log, sin, floor = math.abs, math.log, math.sin, math.floor |
|||
local pi, clock = math.pi, os.clock |
|||
local format = string.format |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Select array type: Lua tables or native (FFI) arrays |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local darray, iarray |
|||
|
|||
local function array_init() |
|||
if jit and jit.status and jit.status() then |
|||
local ok, ffi = pcall(require, "ffi") |
|||
if ok then |
|||
darray = ffi.typeof("double[?]") |
|||
iarray = ffi.typeof("int[?]") |
|||
return |
|||
end |
|||
end |
|||
function darray(n) return {} end |
|||
iarray = darray |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- This is a Lagged Fibonacci Pseudo-random Number Generator with |
|||
-- j, k, M = 5, 17, 31. Pretty weak, but same as C/Java SciMark. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local rand, rand_init |
|||
|
|||
if jit and jit.status and jit.status() then |
|||
-- LJ2 has bit operations and zero-based arrays (internally). |
|||
local bit = require("bit") |
|||
local band, sar = bit.band, bit.arshift |
|||
function rand_init(seed) |
|||
local Rm, Rj, Ri = iarray(17), 16, 11 |
|||
for i=0,16 do Rm[i] = 0 end |
|||
for i=16,0,-1 do |
|||
seed = band(seed*9069, 0x7fffffff) |
|||
Rm[i] = seed |
|||
end |
|||
function rand() |
|||
local i = band(Ri+1, sar(Ri-16, 31)) |
|||
local j = band(Rj+1, sar(Rj-16, 31)) |
|||
Ri, Rj = i, j |
|||
local k = band(Rm[i] - Rm[j], 0x7fffffff) |
|||
Rm[j] = k |
|||
return k * (1.0/2147483647.0) |
|||
end |
|||
end |
|||
else |
|||
-- Better for standard Lua with one-based arrays and without bit operations. |
|||
function rand_init(seed) |
|||
local Rm, Rj = {}, 1 |
|||
for i=1,17 do Rm[i] = 0 end |
|||
for i=17,1,-1 do |
|||
seed = (seed*9069) % (2^31) |
|||
Rm[i] = seed |
|||
end |
|||
function rand() |
|||
local j, m = Rj, Rm |
|||
local h = j - 5 |
|||
if h < 1 then h = h + 17 end |
|||
local k = m[h] - m[j] |
|||
if k < 0 then k = k + 2147483647 end |
|||
m[j] = k |
|||
if j < 17 then Rj = j + 1 else Rj = 1 end |
|||
return k * (1.0/2147483647.0) |
|||
end |
|||
end |
|||
end |
|||
|
|||
local function random_vector(n) |
|||
local v = darray(n+1) |
|||
for x=1,n do v[x] = rand() end |
|||
return v |
|||
end |
|||
|
|||
local function random_matrix(m, n) |
|||
local a = {} |
|||
for y=1,m do |
|||
local v = darray(n+1) |
|||
a[y] = v |
|||
for x=1,n do v[x] = rand() end |
|||
end |
|||
return a |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- FFT: Fast Fourier Transform. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function fft_bitreverse(v, n) |
|||
local j = 0 |
|||
for i=0,2*n-4,2 do |
|||
if i < j then |
|||
v[i+1], v[i+2], v[j+1], v[j+2] = v[j+1], v[j+2], v[i+1], v[i+2] |
|||
end |
|||
local k = n |
|||
while k <= j do j = j - k; k = k / 2 end |
|||
j = j + k |
|||
end |
|||
end |
|||
|
|||
local function fft_transform(v, n, dir) |
|||
if n <= 1 then return end |
|||
fft_bitreverse(v, n) |
|||
local dual = 1 |
|||
repeat |
|||
local dual2 = 2*dual |
|||
for i=1,2*n-1,2*dual2 do |
|||
local j = i+dual2 |
|||
local ir, ii = v[i], v[i+1] |
|||
local jr, ji = v[j], v[j+1] |
|||
v[j], v[j+1] = ir - jr, ii - ji |
|||
v[i], v[i+1] = ir + jr, ii + ji |
|||
end |
|||
local theta = dir * pi / dual |
|||
local s, s2 = sin(theta), 2.0 * sin(theta * 0.5)^2 |
|||
local wr, wi = 1.0, 0.0 |
|||
for a=3,dual2-1,2 do |
|||
wr, wi = wr - s*wi - s2*wr, wi + s*wr - s2*wi |
|||
for i=a,a+2*(n-dual2),2*dual2 do |
|||
local j = i+dual2 |
|||
local jr, ji = v[j], v[j+1] |
|||
local dr, di = wr*jr - wi*ji, wr*ji + wi*jr |
|||
local ir, ii = v[i], v[i+1] |
|||
v[j], v[j+1] = ir - dr, ii - di |
|||
v[i], v[i+1] = ir + dr, ii + di |
|||
end |
|||
end |
|||
dual = dual2 |
|||
until dual >= n |
|||
end |
|||
|
|||
function benchmarks.FFT(n) |
|||
local l2n = log(n)/log(2) |
|||
if l2n % 1 ~= 0 then |
|||
io.stderr:write("Error: FFT data length is not a power of 2\n") |
|||
os.exit(1) |
|||
end |
|||
local v = random_vector(n*2) |
|||
return function(cycles) |
|||
local norm = 1.0 / n |
|||
for p=1,cycles do |
|||
fft_transform(v, n, -1) |
|||
fft_transform(v, n, 1) |
|||
for i=1,n*2 do v[i] = v[i] * norm end |
|||
end |
|||
return ((5*n-2)*l2n + 2*(n+1)) * cycles |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- SOR: Jacobi Successive Over-Relaxation. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function sor_run(mat, m, n, cycles, omega) |
|||
local om4, om1 = omega*0.25, 1.0-omega |
|||
m = m - 1 |
|||
n = n - 1 |
|||
for i=1,cycles do |
|||
for y=2,m do |
|||
local v, vp, vn = mat[y], mat[y-1], mat[y+1] |
|||
for x=2,n do |
|||
v[x] = om4*((vp[x]+vn[x])+(v[x-1]+v[x+1])) + om1*v[x] |
|||
end |
|||
end |
|||
end |
|||
end |
|||
|
|||
function benchmarks.SOR(n) |
|||
local mat = random_matrix(n, n) |
|||
return function(cycles) |
|||
sor_run(mat, n, n, cycles, 1.25) |
|||
return (n-1)*(n-1)*cycles*6 |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- MC: Monte Carlo Integration. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function mc_integrate(cycles) |
|||
local under_curve = 0 |
|||
local rand = rand |
|||
for i=1,cycles do |
|||
local x = rand() |
|||
local y = rand() |
|||
if x*x + y*y <= 1.0 then under_curve = under_curve + 1 end |
|||
end |
|||
return (under_curve/cycles) * 4 |
|||
end |
|||
|
|||
function benchmarks.MC() |
|||
return function(cycles) |
|||
local res = mc_integrate(cycles) |
|||
assert(math.sqrt(cycles)*math.abs(res-math.pi) < 5.0, "bad MC result") |
|||
return cycles * 4 -- Way off, but same as SciMark in C/Java. |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Sparse Matrix Multiplication. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function sparse_mult(n, cycles, vy, val, row, col, vx) |
|||
for p=1,cycles do |
|||
for r=1,n do |
|||
local sum = 0 |
|||
for i=row[r],row[r+1]-1 do sum = sum + vx[col[i]] * val[i] end |
|||
vy[r] = sum |
|||
end |
|||
end |
|||
end |
|||
|
|||
function benchmarks.SPARSE(n, nz) |
|||
local nr = floor(nz/n) |
|||
local anz = nr*n |
|||
local vx = random_vector(n) |
|||
local val = random_vector(anz) |
|||
local vy, col, row = darray(n+1), iarray(nz+1), iarray(n+2) |
|||
row[1] = 1 |
|||
for r=1,n do |
|||
local step = floor(r/nr) |
|||
if step < 1 then step = 1 end |
|||
local rr = row[r] |
|||
row[r+1] = rr+nr |
|||
for i=0,nr-1 do col[rr+i] = 1+i*step end |
|||
end |
|||
return function(cycles) |
|||
sparse_mult(n, cycles, vy, val, row, col, vx) |
|||
return anz*cycles*2 |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- LU: Dense Matrix Factorization. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function lu_factor(a, pivot, m, n) |
|||
local min_m_n = m < n and m or n |
|||
for j=1,min_m_n do |
|||
local jp, t = j, abs(a[j][j]) |
|||
for i=j+1,m do |
|||
local ab = abs(a[i][j]) |
|||
if ab > t then |
|||
jp = i |
|||
t = ab |
|||
end |
|||
end |
|||
pivot[j] = jp |
|||
if a[jp][j] == 0 then error("zero pivot") end |
|||
if jp ~= j then a[j], a[jp] = a[jp], a[j] end |
|||
if j < m then |
|||
local recp = 1.0 / a[j][j] |
|||
for k=j+1,m do |
|||
local v = a[k] |
|||
v[j] = v[j] * recp |
|||
end |
|||
end |
|||
if j < min_m_n then |
|||
for i=j+1,m do |
|||
local vi, vj = a[i], a[j] |
|||
local eij = vi[j] |
|||
for k=j+1,n do vi[k] = vi[k] - eij * vj[k] end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
|
|||
local function matrix_alloc(m, n) |
|||
local a = {} |
|||
for y=1,m do a[y] = darray(n+1) end |
|||
return a |
|||
end |
|||
|
|||
local function matrix_copy(dst, src, m, n) |
|||
for y=1,m do |
|||
local vd, vs = dst[y], src[y] |
|||
for x=1,n do vd[x] = vs[x] end |
|||
end |
|||
end |
|||
|
|||
function benchmarks.LU(n) |
|||
local mat = random_matrix(n, n) |
|||
local tmp = matrix_alloc(n, n) |
|||
local pivot = iarray(n+1) |
|||
return function(cycles) |
|||
for i=1,cycles do |
|||
matrix_copy(tmp, mat, n, n) |
|||
lu_factor(tmp, pivot, n, n) |
|||
end |
|||
return 2.0/3.0*n*n*n*cycles |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Main program. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function printf(...) |
|||
io.write(format(...)) |
|||
end |
|||
|
|||
local function fmtparams(p1, p2) |
|||
if p2 then return format("[%d, %d]", p1, p2) |
|||
elseif p1 then return format("[%d]", p1) end |
|||
return "" |
|||
end |
|||
|
|||
local function measure(min_time, name, ...) |
|||
array_init() |
|||
rand_init(RANDOM_SEED) |
|||
local run = benchmarks[name](...) |
|||
local cycles = 1 |
|||
repeat |
|||
local tm = clock() |
|||
local flops = run(cycles, ...) |
|||
tm = clock() - tm |
|||
if tm >= min_time then |
|||
local res = flops / tm * 1.0e-6 |
|||
local p1, p2 = ... |
|||
printf("%-7s %8.2f %s\n", name, res, fmtparams(...)) |
|||
return res |
|||
end |
|||
cycles = cycles * 2 |
|||
until false |
|||
end |
|||
|
|||
local unpack = table.unpack or unpack |
|||
|
|||
printf("Lua SciMark %s based on SciMark 2.0a. %s.\n\n", |
|||
SCIMARK_VERSION, SCIMARK_COPYRIGHT) |
|||
|
|||
while arg and arg[1] do |
|||
local a = table.remove(arg, 1) |
|||
if a == "-noffi" then |
|||
package.preload.ffi = nil |
|||
elseif a == "-small" then |
|||
SIZE_SELECT = "small" |
|||
elseif a == "-large" then |
|||
SIZE_SELECT = "large" |
|||
elseif benchmarks[a] then |
|||
local p = benchmarks[SIZE_SELECT][a] |
|||
measure(MIN_TIME, a, tonumber(arg[1]) or p[1], tonumber(arg[2]) or p[2]) |
|||
return |
|||
else |
|||
printf("Usage: scimark [-noffi] [-small|-large] [BENCH params...]\n\n") |
|||
printf("BENCH -small -large\n") |
|||
printf("---------------------------------------\n") |
|||
for _,name in ipairs(benchmarks) do |
|||
printf("%-7s %-13s %s\n", name, |
|||
fmtparams(unpack(benchmarks.small[name])), |
|||
fmtparams(unpack(benchmarks.large[name]))) |
|||
end |
|||
printf("\n") |
|||
os.exit(1) |
|||
end |
|||
end |
|||
|
|||
local params = benchmarks[SIZE_SELECT] |
|||
local sum = 0 |
|||
for _,name in ipairs(benchmarks) do |
|||
sum = sum + measure(MIN_TIME, name, unpack(params[name])) |
|||
end |
|||
printf("\nSciMark %8.2f [%s problem sizes]\n", sum / #benchmarks, SIZE_SELECT) |
|||
io.flush() |
@ -0,0 +1,27 @@ |
|||
const std = @import("std"); |
|||
const testing = std.testing; |
|||
|
|||
pub const c = @cImport({ |
|||
@cInclude("lua.h"); |
|||
@cInclude("lualib.h"); |
|||
@cInclude("lauxlib.h"); |
|||
@cInclude("luajit.h"); |
|||
}); |
|||
|
|||
export fn add(a: i32, b: i32) i32 { |
|||
return a + b; |
|||
} |
|||
|
|||
test "basic functionality" { |
|||
const script = |
|||
\\print("hello from luajit :)\n") |
|||
\\io.flush() |
|||
; |
|||
const L = c.luaL_newstate(); |
|||
c.luaL_openlibs(L); |
|||
_ = c.luaL_loadstring(L, @as([*c]const u8, @ptrCast(script))); |
|||
_ = c.lua_pcall(L, 0, c.LUA_MULTRET, 0); |
|||
c.lua_close(L); |
|||
|
|||
std.log.warn("hi", .{}); |
|||
} |
@ -0,0 +1,11 @@ |
|||
*.[oa] |
|||
*.so |
|||
*.obj |
|||
*.lib |
|||
*.exp |
|||
*.dll |
|||
*.exe |
|||
*.manifest |
|||
*.dmp |
|||
*.swp |
|||
.tags |
@ -0,0 +1,56 @@ |
|||
=============================================================================== |
|||
LuaJIT -- a Just-In-Time Compiler for Lua. https://luajit.org/ |
|||
|
|||
Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
|||
|
|||
[ MIT license: https://www.opensource.org/licenses/mit-license.php ] |
|||
|
|||
=============================================================================== |
|||
[ LuaJIT includes code from Lua 5.1/5.2, which has this license statement: ] |
|||
|
|||
Copyright (C) 1994-2012 Lua.org, PUC-Rio. |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in |
|||
all copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|||
THE SOFTWARE. |
|||
|
|||
=============================================================================== |
|||
[ LuaJIT includes code from dlmalloc, which has this license statement: ] |
|||
|
|||
This is a version (aka dlmalloc) of malloc/free/realloc written by |
|||
Doug Lea and released to the public domain, as explained at |
|||
https://creativecommons.org/licenses/publicdomain |
|||
|
|||
=============================================================================== |
@ -0,0 +1,166 @@ |
|||
##############################################################################
|
|||
# LuaJIT top level Makefile for installation. Requires GNU Make.
|
|||
#
|
|||
# Please read doc/install.html before changing any variables!
|
|||
#
|
|||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
|||
# Note: src/Makefile has many more configurable options.
|
|||
#
|
|||
# ##### This Makefile is NOT useful for Windows! #####
|
|||
# For MSVC, please follow the instructions given in src/msvcbuild.bat.
|
|||
# For MinGW and Cygwin, cd to src and run make with the Makefile there.
|
|||
#
|
|||
# Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h
|
|||
##############################################################################
|
|||
|
|||
MAJVER= 2 |
|||
MINVER= 1 |
|||
RELVER= 0 |
|||
PREREL= -beta3 |
|||
VERSION= $(MAJVER).$(MINVER).$(RELVER)$(PREREL) |
|||
ABIVER= 5.1 |
|||
|
|||
##############################################################################
|
|||
#
|
|||
# Change the installation path as needed. This automatically adjusts
|
|||
# the paths in src/luaconf.h, too. Note: PREFIX must be an absolute path!
|
|||
#
|
|||
export PREFIX= /usr/local |
|||
export MULTILIB= lib |
|||
##############################################################################
|
|||
|
|||
DPREFIX= $(DESTDIR)$(PREFIX) |
|||
INSTALL_BIN= $(DPREFIX)/bin |
|||
INSTALL_LIB= $(DPREFIX)/$(MULTILIB) |
|||
INSTALL_SHARE= $(DPREFIX)/share |
|||
INSTALL_INC= $(DPREFIX)/include/luajit-$(MAJVER).$(MINVER) |
|||
|
|||
INSTALL_LJLIBD= $(INSTALL_SHARE)/luajit-$(VERSION) |
|||
INSTALL_JITLIB= $(INSTALL_LJLIBD)/jit |
|||
INSTALL_LMODD= $(INSTALL_SHARE)/lua |
|||
INSTALL_LMOD= $(INSTALL_LMODD)/$(ABIVER) |
|||
INSTALL_CMODD= $(INSTALL_LIB)/lua |
|||
INSTALL_CMOD= $(INSTALL_CMODD)/$(ABIVER) |
|||
INSTALL_MAN= $(INSTALL_SHARE)/man/man1 |
|||
INSTALL_PKGCONFIG= $(INSTALL_LIB)/pkgconfig |
|||
|
|||
INSTALL_TNAME= luajit-$(VERSION) |
|||
INSTALL_TSYMNAME= luajit |
|||
INSTALL_ANAME= libluajit-$(ABIVER).a |
|||
INSTALL_SOSHORT1= libluajit-$(ABIVER).so |
|||
INSTALL_SOSHORT2= libluajit-$(ABIVER).so.$(MAJVER) |
|||
INSTALL_SONAME= $(INSTALL_SOSHORT2).$(MINVER).$(RELVER) |
|||
INSTALL_DYLIBSHORT1= libluajit-$(ABIVER).dylib |
|||
INSTALL_DYLIBSHORT2= libluajit-$(ABIVER).$(MAJVER).dylib |
|||
INSTALL_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).$(MINVER).$(RELVER).dylib |
|||
INSTALL_PCNAME= luajit.pc |
|||
|
|||
INSTALL_STATIC= $(INSTALL_LIB)/$(INSTALL_ANAME) |
|||
INSTALL_DYN= $(INSTALL_LIB)/$(INSTALL_SONAME) |
|||
INSTALL_SHORT1= $(INSTALL_LIB)/$(INSTALL_SOSHORT1) |
|||
INSTALL_SHORT2= $(INSTALL_LIB)/$(INSTALL_SOSHORT2) |
|||
INSTALL_T= $(INSTALL_BIN)/$(INSTALL_TNAME) |
|||
INSTALL_TSYM= $(INSTALL_BIN)/$(INSTALL_TSYMNAME) |
|||
INSTALL_PC= $(INSTALL_PKGCONFIG)/$(INSTALL_PCNAME) |
|||
|
|||
INSTALL_DIRS= $(INSTALL_BIN) $(INSTALL_LIB) $(INSTALL_INC) $(INSTALL_MAN) \
|
|||
$(INSTALL_PKGCONFIG) $(INSTALL_JITLIB) $(INSTALL_LMOD) $(INSTALL_CMOD) |
|||
UNINSTALL_DIRS= $(INSTALL_JITLIB) $(INSTALL_LJLIBD) $(INSTALL_INC) \
|
|||
$(INSTALL_LMOD) $(INSTALL_LMODD) $(INSTALL_CMOD) $(INSTALL_CMODD) |
|||
|
|||
RM= rm -f |
|||
MKDIR= mkdir -p |
|||
RMDIR= rmdir 2>/dev/null |
|||
SYMLINK= ln -sf |
|||
INSTALL_X= install -m 0755 |
|||
INSTALL_F= install -m 0644 |
|||
UNINSTALL= $(RM) |
|||
LDCONFIG= ldconfig -n 2>/dev/null |
|||
SED_PC= sed -e "s|^prefix=.*|prefix=$(PREFIX)|" \
|
|||
-e "s|^multilib=.*|multilib=$(MULTILIB)|" |
|||
|
|||
FILE_T= luajit |
|||
FILE_A= libluajit.a |
|||
FILE_SO= libluajit.so |
|||
FILE_MAN= luajit.1 |
|||
FILE_PC= luajit.pc |
|||
FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h |
|||
FILES_JITLIB= bc.lua bcsave.lua dump.lua p.lua v.lua zone.lua \
|
|||
dis_x86.lua dis_x64.lua dis_arm.lua dis_arm64.lua \
|
|||
dis_arm64be.lua dis_ppc.lua dis_mips.lua dis_mipsel.lua \
|
|||
dis_mips64.lua dis_mips64el.lua vmdef.lua |
|||
|
|||
ifeq (,$(findstring Windows,$(OS))) |
|||
HOST_SYS:= $(shell uname -s) |
|||
else |
|||
HOST_SYS= Windows |
|||
endif |
|||
TARGET_SYS?= $(HOST_SYS) |
|||
|
|||
ifeq (Darwin,$(TARGET_SYS)) |
|||
INSTALL_SONAME= $(INSTALL_DYLIBNAME) |
|||
INSTALL_SOSHORT1= $(INSTALL_DYLIBSHORT1) |
|||
INSTALL_SOSHORT2= $(INSTALL_DYLIBSHORT2) |
|||
LDCONFIG= : |
|||
endif |
|||
|
|||
##############################################################################
|
|||
|
|||
INSTALL_DEP= src/luajit |
|||
|
|||
default all $(INSTALL_DEP): |
|||
@echo "==== Building LuaJIT $(VERSION) ====" |
|||
$(MAKE) -C src |
|||
@echo "==== Successfully built LuaJIT $(VERSION) ====" |
|||
|
|||
install: $(INSTALL_DEP) |
|||
@echo "==== Installing LuaJIT $(VERSION) to $(PREFIX) ====" |
|||
$(MKDIR) $(INSTALL_DIRS) |
|||
cd src && $(INSTALL_X) $(FILE_T) $(INSTALL_T) |
|||
cd src && test -f $(FILE_A) && $(INSTALL_F) $(FILE_A) $(INSTALL_STATIC) || : |
|||
$(RM) $(INSTALL_DYN) $(INSTALL_SHORT1) $(INSTALL_SHORT2) |
|||
cd src && test -f $(FILE_SO) && \
|
|||
$(INSTALL_X) $(FILE_SO) $(INSTALL_DYN) && \
|
|||
( $(LDCONFIG) $(INSTALL_LIB) || : ) && \
|
|||
$(SYMLINK) $(INSTALL_SONAME) $(INSTALL_SHORT1) && \
|
|||
$(SYMLINK) $(INSTALL_SONAME) $(INSTALL_SHORT2) || : |
|||
cd etc && $(INSTALL_F) $(FILE_MAN) $(INSTALL_MAN) |
|||
cd etc && $(SED_PC) $(FILE_PC) > $(FILE_PC).tmp && \
|
|||
$(INSTALL_F) $(FILE_PC).tmp $(INSTALL_PC) && \
|
|||
$(RM) $(FILE_PC).tmp |
|||
cd src && $(INSTALL_F) $(FILES_INC) $(INSTALL_INC) |
|||
cd src/jit && $(INSTALL_F) $(FILES_JITLIB) $(INSTALL_JITLIB) |
|||
@echo "==== Successfully installed LuaJIT $(VERSION) to $(PREFIX) ====" |
|||
@echo "" |
|||
@echo "Note: the development releases deliberately do NOT install a symlink for luajit" |
|||
@echo "You can do this now by running this command (with sudo):" |
|||
@echo "" |
|||
@echo " $(SYMLINK) $(INSTALL_TNAME) $(INSTALL_TSYM)" |
|||
@echo "" |
|||
|
|||
|
|||
uninstall: |
|||
@echo "==== Uninstalling LuaJIT $(VERSION) from $(PREFIX) ====" |
|||
$(UNINSTALL) $(INSTALL_T) $(INSTALL_STATIC) $(INSTALL_DYN) $(INSTALL_SHORT1) $(INSTALL_SHORT2) $(INSTALL_MAN)/$(FILE_MAN) $(INSTALL_PC) |
|||
for file in $(FILES_JITLIB); do \
|
|||
$(UNINSTALL) $(INSTALL_JITLIB)/$$file; \
|
|||
done |
|||
for file in $(FILES_INC); do \
|
|||
$(UNINSTALL) $(INSTALL_INC)/$$file; \
|
|||
done |
|||
$(LDCONFIG) $(INSTALL_LIB) |
|||
$(RMDIR) $(UNINSTALL_DIRS) || : |
|||
@echo "==== Successfully uninstalled LuaJIT $(VERSION) from $(PREFIX) ====" |
|||
|
|||
##############################################################################
|
|||
|
|||
amalg: |
|||
@echo "Building LuaJIT $(VERSION)" |
|||
$(MAKE) -C src amalg |
|||
|
|||
clean: |
|||
$(MAKE) -C src clean |
|||
|
|||
.PHONY: all install amalg clean |
|||
|
|||
##############################################################################
|
@ -0,0 +1,16 @@ |
|||
README for LuaJIT 2.1.0-beta3 |
|||
----------------------------- |
|||
|
|||
LuaJIT is a Just-In-Time (JIT) compiler for the Lua programming language. |
|||
|
|||
Project Homepage: https://luajit.org/ |
|||
|
|||
LuaJIT is Copyright (C) 2005-2022 Mike Pall. |
|||
LuaJIT is free software, released under the MIT license. |
|||
See full Copyright Notice in the COPYRIGHT file or in luajit.h. |
|||
|
|||
Documentation for LuaJIT is available in HTML format. |
|||
Please point your favorite browser to: |
|||
|
|||
doc/luajit.html |
|||
|
@ -0,0 +1,166 @@ |
|||
/* Copyright (C) 2004-2022 Mike Pall. |
|||
* |
|||
* You are welcome to use the general ideas of this design for your own sites. |
|||
* But please do not steal the stylesheet, the layout or the color scheme. |
|||
*/ |
|||
body { |
|||
font-family: serif; |
|||
font-size: 11pt; |
|||
margin: 0 3em; |
|||
padding: 0; |
|||
border: none; |
|||
} |
|||
a:link, a:visited, a:hover, a:active { |
|||
text-decoration: none; |
|||
background: transparent; |
|||
color: #0000ff; |
|||
} |
|||
h1, h2, h3 { |
|||
font-family: sans-serif; |
|||
font-weight: bold; |
|||
text-align: left; |
|||
margin: 0.5em 0; |
|||
padding: 0; |
|||
} |
|||
h1 { |
|||
font-size: 200%; |
|||
} |
|||
h2 { |
|||
font-size: 150%; |
|||
} |
|||
h3 { |
|||
font-size: 125%; |
|||
} |
|||
p { |
|||
margin: 0 0 0.5em 0; |
|||
padding: 0; |
|||
} |
|||
ul, ol { |
|||
margin: 0.5em 0; |
|||
padding: 0 0 0 2em; |
|||
} |
|||
ul { |
|||
list-style: outside square; |
|||
} |
|||
ol { |
|||
list-style: outside decimal; |
|||
} |
|||
li { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
dl { |
|||
margin: 1em 0; |
|||
padding: 1em; |
|||
border: 1px solid black; |
|||
} |
|||
dt { |
|||
font-weight: bold; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
dt sup { |
|||
float: right; |
|||
margin-left: 1em; |
|||
} |
|||
dd { |
|||
margin: 0.5em 0 0 2em; |
|||
padding: 0; |
|||
} |
|||
table { |
|||
table-layout: fixed; |
|||
width: 100%; |
|||
margin: 1em 0; |
|||
padding: 0; |
|||
border: 1px solid black; |
|||
border-spacing: 0; |
|||
border-collapse: collapse; |
|||
} |
|||
tr { |
|||
margin: 0; |
|||
padding: 0; |
|||
border: none; |
|||
} |
|||
td { |
|||
text-align: left; |
|||
margin: 0; |
|||
padding: 0.2em 0.5em; |
|||
border-top: 1px solid black; |
|||
border-bottom: 1px solid black; |
|||
} |
|||
tr.separate td { |
|||
border-top: double; |
|||
} |
|||
tt, pre, code, kbd, samp { |
|||
font-family: monospace; |
|||
font-size: 75%; |
|||
} |
|||
kbd { |
|||
font-weight: bolder; |
|||
} |
|||
blockquote, pre { |
|||
margin: 1em 2em; |
|||
padding: 0; |
|||
} |
|||
img { |
|||
border: none; |
|||
vertical-align: baseline; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
img.left { |
|||
float: left; |
|||
margin: 0.5em 1em 0.5em 0; |
|||
} |
|||
img.right { |
|||
float: right; |
|||
margin: 0.5em 0 0.5em 1em; |
|||
} |
|||
.flush { |
|||
clear: both; |
|||
visibility: hidden; |
|||
} |
|||
.hide, .noprint, #nav { |
|||
display: none !important; |
|||
} |
|||
.pagebreak { |
|||
page-break-before: always; |
|||
} |
|||
#site { |
|||
text-align: right; |
|||
font-family: sans-serif; |
|||
font-weight: bold; |
|||
margin: 0 1em; |
|||
border-bottom: 1pt solid black; |
|||
} |
|||
#site a { |
|||
font-size: 1.2em; |
|||
} |
|||
#site a:link, #site a:visited { |
|||
text-decoration: none; |
|||
font-weight: bold; |
|||
background: transparent; |
|||
color: #ffffff; |
|||
} |
|||
#logo { |
|||
color: #ff8000; |
|||
} |
|||
#head { |
|||
clear: both; |
|||
margin: 0 1em; |
|||
} |
|||
#main { |
|||
line-height: 1.3; |
|||
text-align: justify; |
|||
margin: 1em; |
|||
} |
|||
#foot { |
|||
clear: both; |
|||
font-size: 80%; |
|||
text-align: center; |
|||
margin: 0 1.25em; |
|||
padding: 0.5em 0 0 0; |
|||
border-top: 1pt solid black; |
|||
page-break-before: avoid; |
|||
page-break-after: avoid; |
|||
} |
@ -0,0 +1,325 @@ |
|||
/* Copyright (C) 2004-2022 Mike Pall. |
|||
* |
|||
* You are welcome to use the general ideas of this design for your own sites. |
|||
* But please do not steal the stylesheet, the layout or the color scheme. |
|||
*/ |
|||
/* colorscheme: |
|||
* |
|||
* site | head #4162bf/white | #6078bf/#e6ecff |
|||
* ------+------ ----------------+------------------- |
|||
* nav | main #bfcfff | #e6ecff/black |
|||
* |
|||
* nav: hiback loback #c5d5ff #b9c9f9 |
|||
* hiborder loborder #e6ecff #97a7d7 |
|||
* link hover #2142bf #ff0000 |
|||
* |
|||
* link: link visited hover #2142bf #8122bf #ff0000 |
|||
* |
|||
* main: boxback boxborder #f0f4ff #bfcfff |
|||
*/ |
|||
body { |
|||
font-family: Verdana, Arial, Helvetica, sans-serif; |
|||
font-size: 10pt; |
|||
margin: 0; |
|||
padding: 0; |
|||
border: none; |
|||
background: #e0e0e0; |
|||
color: #000000; |
|||
} |
|||
a:link { |
|||
text-decoration: none; |
|||
background: transparent; |
|||
color: #2142bf; |
|||
} |
|||
a:visited { |
|||
text-decoration: none; |
|||
background: transparent; |
|||
color: #8122bf; |
|||
} |
|||
a:hover, a:active { |
|||
text-decoration: underline; |
|||
background: transparent; |
|||
color: #ff0000; |
|||
} |
|||
h1, h2, h3 { |
|||
font-weight: bold; |
|||
text-align: left; |
|||
margin: 0.5em 0; |
|||
padding: 0; |
|||
background: transparent; |
|||
} |
|||
h1 { |
|||
font-size: 200%; |
|||
line-height: 3em; /* really 6em relative to body, match #site span */ |
|||
margin: 0; |
|||
} |
|||
h2 { |
|||
font-size: 150%; |
|||
color: #606060; |
|||
} |
|||
h3 { |
|||
font-size: 125%; |
|||
color: #404040; |
|||
} |
|||
p { |
|||
max-width: 600px; |
|||
margin: 0 0 0.5em 0; |
|||
padding: 0; |
|||
} |
|||
b { |
|||
color: #404040; |
|||
} |
|||
ul, ol { |
|||
max-width: 600px; |
|||
margin: 0.5em 0; |
|||
padding: 0 0 0 2em; |
|||
} |
|||
ul { |
|||
list-style: outside square; |
|||
} |
|||
ol { |
|||
list-style: outside decimal; |
|||
} |
|||
li { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
dl { |
|||
max-width: 600px; |
|||
margin: 1em 0; |
|||
padding: 1em; |
|||
border: 1px solid #bfcfff; |
|||
background: #f0f4ff; |
|||
} |
|||
dt { |
|||
font-weight: bold; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
dt sup { |
|||
float: right; |
|||
margin-left: 1em; |
|||
color: #808080; |
|||
} |
|||
dt a:visited { |
|||
text-decoration: none; |
|||
color: #2142bf; |
|||
} |
|||
dt a:hover, dt a:active { |
|||
text-decoration: none; |
|||
color: #ff0000; |
|||
} |
|||
dd { |
|||
margin: 0.5em 0 0 2em; |
|||
padding: 0; |
|||
} |
|||
div.tablewrap { /* for IE *sigh* */ |
|||
max-width: 600px; |
|||
} |
|||
table { |
|||
table-layout: fixed; |
|||
border-spacing: 0; |
|||
border-collapse: collapse; |
|||
max-width: 600px; |
|||
width: 100%; |
|||
margin: 1em 0; |
|||
padding: 0; |
|||
border: 1px solid #bfcfff; |
|||
} |
|||
tr { |
|||
margin: 0; |
|||
padding: 0; |
|||
border: none; |
|||
} |
|||
tr.odd { |
|||
background: #f0f4ff; |
|||
} |
|||
tr.separate td { |
|||
border-top: 1px solid #bfcfff; |
|||
} |
|||
td { |
|||
text-align: left; |
|||
margin: 0; |
|||
padding: 0.2em 0.5em; |
|||
border: none; |
|||
} |
|||
tt, code, kbd, samp { |
|||
font-family: Courier New, Courier, monospace; |
|||
line-height: 1.2; |
|||
font-size: 110%; |
|||
} |
|||
kbd { |
|||
font-weight: bolder; |
|||
} |
|||
blockquote, pre { |
|||
max-width: 600px; |
|||
margin: 1em 2em; |
|||
padding: 0; |
|||
} |
|||
pre { |
|||
line-height: 1.1; |
|||
} |
|||
pre.code { |
|||
line-height: 1.4; |
|||
margin: 0.5em 0 1em 0.5em; |
|||
padding: 0.5em 1em; |
|||
border: 1px solid #bfcfff; |
|||
background: #f0f4ff; |
|||
} |
|||
pre.mark { |
|||
padding-left: 2em; |
|||
} |
|||
span.codemark { |
|||
position:absolute; |
|||
left: 16em; |
|||
color: #4040c0; |
|||
} |
|||
span.mark { |
|||
color: #4040c0; |
|||
font-family: Courier New, Courier, monospace; |
|||
line-height: 1.1; |
|||
} |
|||
img { |
|||
border: none; |
|||
vertical-align: baseline; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
img.left { |
|||
float: left; |
|||
margin: 0.5em 1em 0.5em 0; |
|||
} |
|||
img.right { |
|||
float: right; |
|||
margin: 0.5em 0 0.5em 1em; |
|||
} |
|||
.indent { |
|||
padding-left: 1em; |
|||
} |
|||
.flush { |
|||
clear: both; |
|||
visibility: hidden; |
|||
} |
|||
.hide, .noscreen { |
|||
display: none !important; |
|||
} |
|||
.ext { |
|||
color: #ff8000; |
|||
} |
|||
.new { |
|||
font-size: 6pt; |
|||
vertical-align: middle; |
|||
background: #ff8000; |
|||
color: #ffffff; |
|||
} |
|||
#site { |
|||
clear: both; |
|||
float: left; |
|||
width: 13em; |
|||
text-align: center; |
|||
font-weight: bold; |
|||
margin: 0; |
|||
padding: 0; |
|||
background: transparent; |
|||
color: #ffffff; |
|||
} |
|||
#site a { |
|||
font-size: 200%; |
|||
} |
|||
#site a:link, #site a:visited { |
|||
text-decoration: none; |
|||
font-weight: bold; |
|||
background: transparent; |
|||
color: #ffffff; |
|||
} |
|||
#site span { |
|||
line-height: 3em; /* really 6em relative to body, match h1 */ |
|||
} |
|||
#logo { |
|||
color: #ffb380; |
|||
} |
|||
#head { |
|||
margin: 0; |
|||
padding: 0 0 0 2em; |
|||
border-left: solid 13em #4162bf; |
|||
border-right: solid 3em #6078bf; |
|||
background: #6078bf; |
|||
color: #e6ecff; |
|||
} |
|||
#nav { |
|||
clear: both; |
|||
float: left; |
|||
overflow: hidden; |
|||
text-align: left; |
|||
line-height: 1.5; |
|||
width: 13em; |
|||
padding-top: 1em; |
|||
background: transparent; |
|||
} |
|||
#nav ul { |
|||
list-style: none outside; |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
#nav li { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
#nav a { |
|||
display: block; |
|||
text-decoration: none; |
|||
font-weight: bold; |
|||
margin: 0; |
|||
padding: 2px 1em; |
|||
border-top: 1px solid transparent; |
|||
border-bottom: 1px solid transparent; |
|||
background: transparent; |
|||
color: #2142bf; |
|||
} |
|||
#nav a:hover, #nav a:active { |
|||
text-decoration: none; |
|||
border-top: 1px solid #97a7d7; |
|||
border-bottom: 1px solid #e6ecff; |
|||
background: #b9c9f9; |
|||
color: #ff0000; |
|||
} |
|||
#nav a.current, #nav a.current:hover, #nav a.current:active { |
|||
border-top: 1px solid #e6ecff; |
|||
border-bottom: 1px solid #97a7d7; |
|||
background: #c5d5ff; |
|||
color: #2142bf; |
|||
} |
|||
#nav ul ul a { |
|||
padding: 0 1em 0 1.7em; |
|||
} |
|||
#nav ul ul ul a { |
|||
padding: 0 0.5em 0 2.4em; |
|||
} |
|||
#main { |
|||
line-height: 1.5; |
|||
text-align: left; |
|||
margin: 0; |
|||
padding: 1em 2em; |
|||
border-left: solid 13em #bfcfff; |
|||
border-right: solid 3em #e6ecff; |
|||
background: #e6ecff; |
|||
} |
|||
#foot { |
|||
clear: both; |
|||
font-size: 80%; |
|||
text-align: center; |
|||
margin: 0; |
|||
padding: 0.5em; |
|||
background: #6078bf; |
|||
color: #ffffff; |
|||
} |
|||
#foot a:link, #foot a:visited { |
|||
text-decoration: underline; |
|||
background: transparent; |
|||
color: #ffffff; |
|||
} |
|||
#foot a:hover, #foot a:active { |
|||
text-decoration: underline; |
|||
background: transparent; |
|||
color: #bfcfff; |
|||
} |
@ -0,0 +1,107 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Contact</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Contact</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
If you want to report bugs, propose fixes or suggest enhancements, |
|||
please use the |
|||
<a href="https://github.com/LuaJIT/LuaJIT/issues"><span class="ext">»</span> GitHub issue tracker</a>. |
|||
</p> |
|||
<p> |
|||
Please send general questions to the |
|||
<a href="https://luajit.org/list.html"><span class="ext">»</span> LuaJIT mailing list</a>. |
|||
</p> |
|||
<p> |
|||
You can also send any questions you have directly to me: |
|||
</p> |
|||
|
|||
<script type="text/javascript"> |
|||
<!-- |
|||
var xS="@-:\" .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<abc>defghijklmnopqrstuvwxyz";function xD(s) |
|||
{var len=s.length;var r="";for(var i=0;i<len;i++) |
|||
{var c=s.charAt(i);var n=xS.indexOf(c);if(n!=-1)c=xS.charAt(69-n);r+=c;} |
|||
document.write("<"+"p>"+r+"<"+"/p>\n");} |
|||
//--> |
|||
</script> |
|||
<script type="text/javascript"> |
|||
<!-- |
|||
xD("fyZKB8xv\"FJytmz8.KAB0u52D") |
|||
//--></script> |
|||
<noscript> |
|||
<p><img src="img/contact.png" alt="Contact info in image" width="170" height="13"> |
|||
</p> |
|||
</noscript> |
|||
|
|||
<h2>Copyright</h2> |
|||
<p> |
|||
All documentation is |
|||
Copyright © 2005-2022 Mike Pall. |
|||
</p> |
|||
|
|||
|
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,697 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>String Buffer Library</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
.lib { |
|||
vertical-align: middle; |
|||
margin-left: 5px; |
|||
padding: 0 5px; |
|||
font-size: 60%; |
|||
border-radius: 5px; |
|||
background: #c5d5ff; |
|||
color: #000; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>String Buffer Library</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a class="current" href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
The string buffer library allows <b>high-performance manipulation of |
|||
string-like data</b>. |
|||
</p> |
|||
<p> |
|||
Unlike Lua strings, which are constants, string buffers are |
|||
<b>mutable</b> sequences of 8-bit (binary-transparent) characters. Data |
|||
can be stored, formatted and encoded into a string buffer and later |
|||
converted, extracted or decoded. |
|||
</p> |
|||
<p> |
|||
The convenient string buffer API simplifies common string manipulation |
|||
tasks, that would otherwise require creating many intermediate strings. |
|||
String buffers improve performance by eliminating redundant memory |
|||
copies, object creation, string interning and garbage collection |
|||
overhead. In conjunction with the FFI library, they allow zero-copy |
|||
operations. |
|||
</p> |
|||
<p> |
|||
The string buffer libary also includes a high-performance |
|||
<a href="serialize">serializer</a> for Lua objects. |
|||
</p> |
|||
|
|||
<h2 id="wip" style="color:#ff0000">Work in Progress</h2> |
|||
<p> |
|||
<b style="color:#ff0000">This library is a work in progress. More |
|||
functionality will be added soon.</b> |
|||
</p> |
|||
|
|||
<h2 id="use">Using the String Buffer Library</h2> |
|||
<p> |
|||
The string buffer library is built into LuaJIT by default, but it's not |
|||
loaded by default. Add this to the start of every Lua file that needs |
|||
one of its functions: |
|||
</p> |
|||
<pre class="code"> |
|||
local buffer = require("string.buffer") |
|||
</pre> |
|||
<p> |
|||
The convention for the syntax shown on this page is that <tt>buffer</tt> |
|||
refers to the buffer library and <tt>buf</tt> refers to an individual |
|||
buffer object. |
|||
</p> |
|||
<p> |
|||
Please note the difference between a Lua function call, e.g. |
|||
<tt>buffer.new()</tt> (with a dot) and a Lua method call, e.g. |
|||
<tt>buf:reset()</tt> (with a colon). |
|||
</p> |
|||
|
|||
<h3 id="buffer_object">Buffer Objects</h3> |
|||
<p> |
|||
A buffer object is a garbage-collected Lua object. After creation with |
|||
<tt>buffer.new()</tt>, it can (and should) be reused for many operations. |
|||
When the last reference to a buffer object is gone, it will eventually |
|||
be freed by the garbage collector, along with the allocated buffer |
|||
space. |
|||
</p> |
|||
<p> |
|||
Buffers operate like a FIFO (first-in first-out) data structure. Data |
|||
can be appended (written) to the end of the buffer and consumed (read) |
|||
from the front of the buffer. These operations may be freely mixed. |
|||
</p> |
|||
<p> |
|||
The buffer space that holds the characters is managed automatically |
|||
— it grows as needed and already consumed space is recycled. Use |
|||
<tt>buffer.new(size)</tt> and <tt>buf:free()</tt>, if you need more |
|||
control. |
|||
</p> |
|||
<p> |
|||
The maximum size of a single buffer is the same as the maximum size of a |
|||
Lua string, which is slightly below two gigabytes. For huge data sizes, |
|||
neither strings nor buffers are the right data structure — use the |
|||
FFI library to directly map memory or files up to the virtual memory |
|||
limit of your OS. |
|||
</p> |
|||
|
|||
<h3 id="buffer_overview">Buffer Method Overview</h3> |
|||
<ul> |
|||
<li> |
|||
The <tt>buf:put*()</tt>-like methods append (write) characters to the |
|||
end of the buffer. |
|||
</li> |
|||
<li> |
|||
The <tt>buf:get*()</tt>-like methods consume (read) characters from the |
|||
front of the buffer. |
|||
</li> |
|||
<li> |
|||
Other methods, like <tt>buf:tostring()</tt> only read the buffer |
|||
contents, but don't change the buffer. |
|||
</li> |
|||
<li> |
|||
The <tt>buf:set()</tt> method allows zero-copy consumption of a string |
|||
or an FFI cdata object as a buffer. |
|||
</li> |
|||
<li> |
|||
The FFI-specific methods allow zero-copy read/write-style operations or |
|||
modifying the buffer contents in-place. Please check the |
|||
<a href="#ffi_caveats">FFI caveats</a> below, too. |
|||
</li> |
|||
<li> |
|||
Methods that don't need to return anything specific, return the buffer |
|||
object itself as a convenience. This allows method chaining, e.g.: |
|||
<tt>buf:reset():encode(obj)</tt> or <tt>buf:skip(len):get()</tt> |
|||
</li> |
|||
</ul> |
|||
|
|||
<h2 id="create">Buffer Creation and Management</h2> |
|||
|
|||
<h3 id="buffer_new"><tt>local buf = buffer.new([size [,options]])<br> |
|||
local buf = buffer.new([options])</tt></h3> |
|||
<p> |
|||
Creates a new buffer object. |
|||
</p> |
|||
<p> |
|||
The optional <tt>size</tt> argument ensures a minimum initial buffer |
|||
size. This is strictly an optimization when the required buffer size is |
|||
known beforehand. The buffer space will grow as needed, in any case. |
|||
</p> |
|||
<p> |
|||
The optional table <tt>options</tt> sets various |
|||
<a href="#serialize_options">serialization options</a>. |
|||
</p> |
|||
|
|||
<h3 id="buffer_reset"><tt>buf = buf:reset()</tt></h3> |
|||
<p> |
|||
Reset (empty) the buffer. The allocated buffer space is not freed and |
|||
may be reused. |
|||
</p> |
|||
|
|||
<h3 id="buffer_free"><tt>buf = buf:free()</tt></h3> |
|||
<p> |
|||
The buffer space of the buffer object is freed. The object itself |
|||
remains intact, empty and may be reused. |
|||
</p> |
|||
<p> |
|||
Note: you normally don't need to use this method. The garbage collector |
|||
automatically frees the buffer space, when the buffer object is |
|||
collected. Use this method, if you need to free the associated memory |
|||
immediately. |
|||
</p> |
|||
|
|||
<h2 id="write">Buffer Writers</h2> |
|||
|
|||
<h3 id="buffer_put"><tt>buf = buf:put([str|num|obj] [,…])</tt></h3> |
|||
<p> |
|||
Appends a string <tt>str</tt>, a number <tt>num</tt> or any object |
|||
<tt>obj</tt> with a <tt>__tostring</tt> metamethod to the buffer. |
|||
Multiple arguments are appended in the given order. |
|||
</p> |
|||
<p> |
|||
Appending a buffer to a buffer is possible and short-circuited |
|||
internally. But it still involves a copy. Better combine the buffer |
|||
writes to use a single buffer. |
|||
</p> |
|||
|
|||
<h3 id="buffer_putf"><tt>buf = buf:putf(format, …)</tt></h3> |
|||
<p> |
|||
Appends the formatted arguments to the buffer. The <tt>format</tt> |
|||
string supports the same options as <tt>string.format()</tt>. |
|||
</p> |
|||
|
|||
<h3 id="buffer_putcdata"><tt>buf = buf:putcdata(cdata, len)</tt><span class="lib">FFI</span></h3> |
|||
<p> |
|||
Appends the given <tt>len</tt> number of bytes from the memory pointed |
|||
to by the FFI <tt>cdata</tt> object to the buffer. The object needs to |
|||
be convertible to a (constant) pointer. |
|||
</p> |
|||
|
|||
<h3 id="buffer_set"><tt>buf = buf:set(str)<br> |
|||
buf = buf:set(cdata, len)</tt><span class="lib">FFI</span></h3> |
|||
<p> |
|||
This method allows zero-copy consumption of a string or an FFI cdata |
|||
object as a buffer. It stores a reference to the passed string |
|||
<tt>str</tt> or the FFI <tt>cdata</tt> object in the buffer. Any buffer |
|||
space originally allocated is freed. This is <i>not</i> an append |
|||
operation, unlike the <tt>buf:put*()</tt> methods. |
|||
</p> |
|||
<p> |
|||
After calling this method, the buffer behaves as if |
|||
<tt>buf:free():put(str)</tt> or <tt>buf:free():put(cdata, len)</tt> |
|||
had been called. However, the data is only referenced and not copied, as |
|||
long as the buffer is only consumed. |
|||
</p> |
|||
<p> |
|||
In case the buffer is written to later on, the referenced data is copied |
|||
and the object reference is removed (copy-on-write semantics). |
|||
</p> |
|||
<p> |
|||
The stored reference is an anchor for the garbage collector and keeps the |
|||
originally passed string or FFI cdata object alive. |
|||
</p> |
|||
|
|||
<h3 id="buffer_reserve"><tt>ptr, len = buf:reserve(size)</tt><span class="lib">FFI</span><br> |
|||
<tt>buf = buf:commit(used)</tt><span class="lib">FFI</span></h3> |
|||
<p> |
|||
The <tt>reserve</tt> method reserves at least <tt>size</tt> bytes of |
|||
write space in the buffer. It returns an <tt>uint8_t *</tt> FFI |
|||
cdata pointer <tt>ptr</tt> that points to this space. |
|||
</p> |
|||
<p> |
|||
The available length in bytes is returned in <tt>len</tt>. This is at |
|||
least <tt>size</tt> bytes, but may be more to facilitate efficient |
|||
buffer growth. You can either make use of the additional space or ignore |
|||
<tt>len</tt> and only use <tt>size</tt> bytes. |
|||
</p> |
|||
<p> |
|||
The <tt>commit</tt> method appends the <tt>used</tt> bytes of the |
|||
previously returned write space to the buffer data. |
|||
</p> |
|||
<p> |
|||
This pair of methods allows zero-copy use of C read-style APIs: |
|||
</p> |
|||
<pre class="code"> |
|||
local MIN_SIZE = 65536 |
|||
repeat |
|||
local ptr, len = buf:reserve(MIN_SIZE) |
|||
local n = C.read(fd, ptr, len) |
|||
if n == 0 then break end -- EOF. |
|||
if n < 0 then error("read error") end |
|||
buf:commit(n) |
|||
until false |
|||
</pre> |
|||
<p> |
|||
The reserved write space is <i>not</i> initialized. At least the |
|||
<tt>used</tt> bytes <b>must</b> be written to before calling the |
|||
<tt>commit</tt> method. There's no need to call the <tt>commit</tt> |
|||
method, if nothing is added to the buffer (e.g. on error). |
|||
</p> |
|||
|
|||
<h2 id="read">Buffer Readers</h2> |
|||
|
|||
<h3 id="buffer_length"><tt>len = #buf</tt></h3> |
|||
<p> |
|||
Returns the current length of the buffer data in bytes. |
|||
</p> |
|||
|
|||
<h3 id="buffer_concat"><tt>res = str|num|buf .. str|num|buf […]</tt></h3> |
|||
<p> |
|||
The Lua concatenation operator <tt>..</tt> also accepts buffers, just |
|||
like strings or numbers. It always returns a string and not a buffer. |
|||
</p> |
|||
<p> |
|||
Note that although this is supported for convenience, this thwarts one |
|||
of the main reasons to use buffers, which is to avoid string |
|||
allocations. Rewrite it with <tt>buf:put()</tt> and <tt>buf:get()</tt>. |
|||
</p> |
|||
<p> |
|||
Mixing this with unrelated objects that have a <tt>__concat</tt> |
|||
metamethod may not work, since these probably only expect strings. |
|||
</p> |
|||
|
|||
<h3 id="buffer_skip"><tt>buf = buf:skip(len)</tt></h3> |
|||
<p> |
|||
Skips (consumes) <tt>len</tt> bytes from the buffer up to the current |
|||
length of the buffer data. |
|||
</p> |
|||
|
|||
<h3 id="buffer_get"><tt>str, … = buf:get([len|nil] [,…])</tt></h3> |
|||
<p> |
|||
Consumes the buffer data and returns one or more strings. If called |
|||
without arguments, the whole buffer data is consumed. If called with a |
|||
number, up to <tt>len</tt> bytes are consumed. A <tt>nil</tt> argument |
|||
consumes the remaining buffer space (this only makes sense as the last |
|||
argument). Multiple arguments consume the buffer data in the given |
|||
order. |
|||
</p> |
|||
<p> |
|||
Note: a zero length or no remaining buffer data returns an empty string |
|||
and not <tt>nil</tt>. |
|||
</p> |
|||
|
|||
<h3 id="buffer_tostring"><tt>str = buf:tostring()<br> |
|||
str = tostring(buf)</tt></h3> |
|||
<p> |
|||
Creates a string from the buffer data, but doesn't consume it. The |
|||
buffer remains unchanged. |
|||
</p> |
|||
<p> |
|||
Buffer objects also define a <tt>__tostring</tt> metamethod. This means |
|||
buffers can be passed to the global <tt>tostring()</tt> function and |
|||
many other functions that accept this in place of strings. The important |
|||
internal uses in functions like <tt>io.write()</tt> are short-circuited |
|||
to avoid the creation of an intermediate string object. |
|||
</p> |
|||
|
|||
<h3 id="buffer_ref"><tt>ptr, len = buf:ref()</tt><span class="lib">FFI</span></h3> |
|||
<p> |
|||
Returns an <tt>uint8_t *</tt> FFI cdata pointer <tt>ptr</tt> that |
|||
points to the buffer data. The length of the buffer data in bytes is |
|||
returned in <tt>len</tt>. |
|||
</p> |
|||
<p> |
|||
The returned pointer can be directly passed to C functions that expect a |
|||
buffer and a length. You can also do bytewise reads |
|||
(<tt>local x = ptr[i]</tt>) or writes |
|||
(<tt>ptr[i] = 0x40</tt>) of the buffer data. |
|||
</p> |
|||
<p> |
|||
In conjunction with the <tt>skip</tt> method, this allows zero-copy use |
|||
of C write-style APIs: |
|||
</p> |
|||
<pre class="code"> |
|||
repeat |
|||
local ptr, len = buf:ref() |
|||
if len == 0 then break end |
|||
local n = C.write(fd, ptr, len) |
|||
if n < 0 then error("write error") end |
|||
buf:skip(n) |
|||
until n >= len |
|||
</pre> |
|||
<p> |
|||
Unlike Lua strings, buffer data is <i>not</i> implicitly |
|||
zero-terminated. It's not safe to pass <tt>ptr</tt> to C functions that |
|||
expect zero-terminated strings. If you're not using <tt>len</tt>, then |
|||
you're doing something wrong. |
|||
</p> |
|||
|
|||
<h2 id="serialize">Serialization of Lua Objects</h2> |
|||
<p> |
|||
The following functions and methods allow <b>high-speed serialization</b> |
|||
(encoding) of a Lua object into a string and decoding it back to a Lua |
|||
object. This allows convenient storage and transport of <b>structured |
|||
data</b>. |
|||
</p> |
|||
<p> |
|||
The encoded data is in an <a href="#serialize_format">internal binary |
|||
format</a>. The data can be stored in files, binary-transparent |
|||
databases or transmitted to other LuaJIT instances across threads, |
|||
processes or networks. |
|||
</p> |
|||
<p> |
|||
Encoding speed can reach up to 1 Gigabyte/second on a modern desktop- or |
|||
server-class system, even when serializing many small objects. Decoding |
|||
speed is mostly constrained by object creation cost. |
|||
</p> |
|||
<p> |
|||
The serializer handles most Lua types, common FFI number types and |
|||
nested structures. Functions, thread objects, other FFI cdata and full |
|||
userdata cannot be serialized (yet). |
|||
</p> |
|||
<p> |
|||
The encoder serializes nested structures as trees. Multiple references |
|||
to a single object will be stored separately and create distinct objects |
|||
after decoding. Circular references cause an error. |
|||
</p> |
|||
|
|||
<h3 id="serialize_methods">Serialization Functions and Methods</h3> |
|||
|
|||
<h3 id="buffer_encode"><tt>str = buffer.encode(obj)<br> |
|||
buf = buf:encode(obj)</tt></h3> |
|||
<p> |
|||
Serializes (encodes) the Lua object <tt>obj</tt>. The stand-alone |
|||
function returns a string <tt>str</tt>. The buffer method appends the |
|||
encoding to the buffer. |
|||
</p> |
|||
<p> |
|||
<tt>obj</tt> can be any of the supported Lua types — it doesn't |
|||
need to be a Lua table. |
|||
</p> |
|||
<p> |
|||
This function may throw an error when attempting to serialize |
|||
unsupported object types, circular references or deeply nested tables. |
|||
</p> |
|||
|
|||
<h3 id="buffer_decode"><tt>obj = buffer.decode(str)<br> |
|||
obj = buf:decode()</tt></h3> |
|||
<p> |
|||
The stand-alone function de-serializes (decodes) the string |
|||
<tt>str</tt>, the buffer method de-serializes one object from the |
|||
buffer. Both return a Lua object <tt>obj</tt>. |
|||
</p> |
|||
<p> |
|||
The returned object may be any of the supported Lua types — |
|||
even <tt>nil</tt>. |
|||
</p> |
|||
<p> |
|||
This function may throw an error when fed with malformed or incomplete |
|||
encoded data. The stand-alone function throws when there's left-over |
|||
data after decoding a single top-level object. The buffer method leaves |
|||
any left-over data in the buffer. |
|||
</p> |
|||
<p> |
|||
Attempting to de-serialize an FFI type will throw an error, if the FFI |
|||
library is not built-in or has not been loaded, yet. |
|||
</p> |
|||
|
|||
<h3 id="serialize_options">Serialization Options</h3> |
|||
<p> |
|||
The <tt>options</tt> table passed to <tt>buffer.new()</tt> may contain |
|||
the following members (all optional): |
|||
</p> |
|||
<ul> |
|||
<li> |
|||
<tt>dict</tt> is a Lua table holding a <b>dictionary of strings</b> that |
|||
commonly occur as table keys of objects you are serializing. These keys |
|||
are compactly encoded as indexes during serialization. A well chosen |
|||
dictionary saves space and improves serialization performance. |
|||
</li> |
|||
<li> |
|||
<tt>metatable</tt> is a Lua table holding a <b>dictionary of metatables</b> |
|||
for the table objects you are serializing. |
|||
</li> |
|||
</ul> |
|||
<p> |
|||
<tt>dict</tt> needs to be an array of strings and <tt>metatable</tt> needs |
|||
to be an array of tables. Both starting at index 1 and without holes (no |
|||
<tt>nil</tt> inbetween). The tables are anchored in the buffer object and |
|||
internally modified into a two-way index (don't do this yourself, just pass |
|||
a plain array). The tables must not be modified after they have been passed |
|||
to <tt>buffer.new()</tt>. |
|||
</p> |
|||
<p> |
|||
The <tt>dict</tt> and <tt>metatable</tt> tables used by the encoder and |
|||
decoder must be the same. Put the most common entries at the front. Extend |
|||
at the end to ensure backwards-compatibility — older encodings can |
|||
then still be read. You may also set some indexes to <tt>false</tt> to |
|||
explicitly drop backwards-compatibility. Old encodings that use these |
|||
indexes will throw an error when decoded. |
|||
</p> |
|||
<p> |
|||
Metatables that are not found in the <tt>metatable</tt> dictionary are |
|||
ignored when encoding. Decoding returns a table with a <tt>nil</tt> |
|||
metatable. |
|||
</p> |
|||
<p> |
|||
Note: parsing and preparation of the options table is somewhat |
|||
expensive. Create a buffer object only once and recycle it for multiple |
|||
uses. Avoid mixing encoder and decoder buffers, since the |
|||
<tt>buf:set()</tt> method frees the already allocated buffer space: |
|||
</p> |
|||
<pre class="code"> |
|||
local options = { |
|||
dict = { "commonly", "used", "string", "keys" }, |
|||
} |
|||
local buf_enc = buffer.new(options) |
|||
local buf_dec = buffer.new(options) |
|||
|
|||
local function encode(obj) |
|||
return buf_enc:reset():encode(obj):get() |
|||
end |
|||
|
|||
local function decode(str) |
|||
return buf_dec:set(str):decode() |
|||
end |
|||
</pre> |
|||
|
|||
<h3 id="serialize_stream">Streaming Serialization</h3> |
|||
<p> |
|||
In some contexts, it's desirable to do piecewise serialization of large |
|||
datasets, also known as <i>streaming</i>. |
|||
</p> |
|||
<p> |
|||
This serialization format can be safely concatenated and supports streaming. |
|||
Multiple encodings can simply be appended to a buffer and later decoded |
|||
individually: |
|||
</p> |
|||
<pre class="code"> |
|||
local buf = buffer.new() |
|||
buf:encode(obj1) |
|||
buf:encode(obj2) |
|||
local copy1 = buf:decode() |
|||
local copy2 = buf:decode() |
|||
</pre> |
|||
<p> |
|||
Here's how to iterate over a stream: |
|||
</p> |
|||
<pre class="code"> |
|||
while #buf ~= 0 do |
|||
local obj = buf:decode() |
|||
-- Do something with obj. |
|||
end |
|||
</pre> |
|||
<p> |
|||
Since the serialization format doesn't prepend a length to its encoding, |
|||
network applications may need to transmit the length, too. |
|||
</p> |
|||
|
|||
<h3 id="serialize_format">Serialization Format Specification</h3> |
|||
<p> |
|||
This serialization format is designed for <b>internal use</b> by LuaJIT |
|||
applications. Serialized data is upwards-compatible and portable across |
|||
all supported LuaJIT platforms. |
|||
</p> |
|||
<p> |
|||
It's an <b>8-bit binary format</b> and not human-readable. It uses e.g. |
|||
embedded zeroes and stores embedded Lua string objects unmodified, which |
|||
are 8-bit-clean, too. Encoded data can be safely concatenated for |
|||
streaming and later decoded one top-level object at a time. |
|||
</p> |
|||
<p> |
|||
The encoding is reasonably compact, but tuned for maximum performance, |
|||
not for minimum space usage. It compresses well with any of the common |
|||
byte-oriented data compression algorithms. |
|||
</p> |
|||
<p> |
|||
Although documented here for reference, this format is explicitly |
|||
<b>not</b> intended to be a 'public standard' for structured data |
|||
interchange across computer languages (like JSON or MessagePack). Please |
|||
do not use it as such. |
|||
</p> |
|||
<p> |
|||
The specification is given below as a context-free grammar with a |
|||
top-level <tt>object</tt> as the starting point. Alternatives are |
|||
separated by the <tt>|</tt> symbol and <tt>*</tt> indicates repeats. |
|||
Grouping is implicit or indicated by <tt>{…}</tt>. Terminals are |
|||
either plain hex numbers, encoded as bytes, or have a <tt>.format</tt> |
|||
suffix. |
|||
</p> |
|||
<pre> |
|||
object → nil | false | true |
|||
| null | lightud32 | lightud64 |
|||
| int | num | tab | tab_mt |
|||
| int64 | uint64 | complex |
|||
| string |
|||
|
|||
nil → 0x00 |
|||
false → 0x01 |
|||
true → 0x02 |
|||
|
|||
null → 0x03 // NULL lightuserdata |
|||
lightud32 → 0x04 data.I // 32 bit lightuserdata |
|||
lightud64 → 0x05 data.L // 64 bit lightuserdata |
|||
|
|||
int → 0x06 int.I // int32_t |
|||
num → 0x07 double.L |
|||
|
|||
tab → 0x08 // Empty table |
|||
| 0x09 h.U h*{object object} // Key/value hash |
|||
| 0x0a a.U a*object // 0-based array |
|||
| 0x0b a.U a*object h.U h*{object object} // Mixed |
|||
| 0x0c a.U (a-1)*object // 1-based array |
|||
| 0x0d a.U (a-1)*object h.U h*{object object} // Mixed |
|||
tab_mt → 0x0e (index-1).U tab // Metatable dict entry |
|||
|
|||
int64 → 0x10 int.L // FFI int64_t |
|||
uint64 → 0x11 uint.L // FFI uint64_t |
|||
complex → 0x12 re.L im.L // FFI complex |
|||
|
|||
string → (0x20+len).U len*char.B |
|||
| 0x0f (index-1).U // String dict entry |
|||
|
|||
.B = 8 bit |
|||
.I = 32 bit little-endian |
|||
.L = 64 bit little-endian |
|||
.U = prefix-encoded 32 bit unsigned number n: |
|||
0x00..0xdf → n.B |
|||
0xe0..0x1fdf → (0xe0|(((n-0xe0)>>8)&0x1f)).B ((n-0xe0)&0xff).B |
|||
0x1fe0.. → 0xff n.I |
|||
</pre> |
|||
|
|||
<h2 id="error">Error handling</h2> |
|||
<p> |
|||
Many of the buffer methods can throw an error. Out-of-memory or usage |
|||
errors are best caught with an outer wrapper for larger parts of code. |
|||
There's not much one can do after that, anyway. |
|||
</p> |
|||
<p> |
|||
OTOH you may want to catch some errors individually. Buffer methods need |
|||
to receive the buffer object as the first argument. The Lua colon-syntax |
|||
<tt>obj:method()</tt> does that implicitly. But to wrap a method with |
|||
<tt>pcall()</tt>, the arguments need to be passed like this: |
|||
</p> |
|||
<pre class="code"> |
|||
local ok, err = pcall(buf.encode, buf, obj) |
|||
if not ok then |
|||
-- Handle error in err. |
|||
end |
|||
</pre> |
|||
|
|||
<h2 id="ffi_caveats">FFI caveats</h2> |
|||
<p> |
|||
The string buffer library has been designed to work well together with |
|||
the FFI library. But due to the low-level nature of the FFI library, |
|||
some care needs to be taken: |
|||
</p> |
|||
<p> |
|||
First, please remember that FFI pointers are zero-indexed. The space |
|||
returned by <tt>buf:reserve()</tt> and <tt>buf:ref()</tt> starts at the |
|||
returned pointer and ends before <tt>len</tt> bytes after that. |
|||
</p> |
|||
<p> |
|||
I.e. the first valid index is <tt>ptr[0]</tt> and the last valid index |
|||
is <tt>ptr[len-1]</tt>. If the returned length is zero, there's no valid |
|||
index at all. The returned pointer may even be <tt>NULL</tt>. |
|||
</p> |
|||
<p> |
|||
The space pointed to by the returned pointer is only valid as long as |
|||
the buffer is not modified in any way (neither append, nor consume, nor |
|||
reset, etc.). The pointer is also not a GC anchor for the buffer object |
|||
itself. |
|||
</p> |
|||
<p> |
|||
Buffer data is only guaranteed to be byte-aligned. Casting the returned |
|||
pointer to a data type with higher alignment may cause unaligned |
|||
accesses. It depends on the CPU architecture whether this is allowed or |
|||
not (it's always OK on x86/x64 and mostly OK on other modern |
|||
architectures). |
|||
</p> |
|||
<p> |
|||
FFI pointers or references do not count as GC anchors for an underlying |
|||
object. E.g. an <tt>array</tt> allocated with <tt>ffi.new()</tt> is |
|||
anchored by <tt>buf:set(array, len)</tt>, but not by |
|||
<tt>buf:set(array+offset, len)</tt>. The addition of the offset |
|||
creates a new pointer, even when the offset is zero. In this case, you |
|||
need to make sure there's still a reference to the original array as |
|||
long as its contents are in use by the buffer. |
|||
</p> |
|||
<p> |
|||
Even though each LuaJIT VM instance is single-threaded (but you can |
|||
create multiple VMs), FFI data structures can be accessed concurrently. |
|||
Be careful when reading/writing FFI cdata from/to buffers to avoid |
|||
concurrent accesses or modifications. In particular, the memory |
|||
referenced by <tt>buf:set(cdata, len)</tt> must not be modified |
|||
while buffer readers are working on it. Shared, but read-only memory |
|||
mappings of files are OK, but only if the file does not change. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,185 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Lua/C API Extensions</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Lua/C API Extensions</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a class="current" href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
LuaJIT adds some extensions to the standard Lua/C API. The LuaJIT include |
|||
directory must be in the compiler search path (<tt>-I<i>path</i></tt>) |
|||
to be able to include the required header for C code: |
|||
</p> |
|||
<pre class="code"> |
|||
#include "luajit.h" |
|||
</pre> |
|||
<p> |
|||
Or for C++ code: |
|||
</p> |
|||
<pre class="code"> |
|||
#include "lua.hpp" |
|||
</pre> |
|||
|
|||
<h2 id="luaJIT_setmode"><tt>luaJIT_setmode(L, idx, mode)</tt> |
|||
— Control VM</h2> |
|||
<p> |
|||
This is a C API extension to allow control of the VM from C code. The |
|||
full prototype of <tt>LuaJIT_setmode</tt> is: |
|||
</p> |
|||
<pre class="code"> |
|||
LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); |
|||
</pre> |
|||
<p> |
|||
The returned status is either success (<tt>1</tt>) or failure (<tt>0</tt>). |
|||
The second argument is either <tt>0</tt> or a stack index (similar to the |
|||
other Lua/C API functions). |
|||
</p> |
|||
<p> |
|||
The third argument specifies the mode, which is 'or'ed with a flag. |
|||
The flag can be <tt>LUAJIT_MODE_OFF</tt> to turn a feature off, |
|||
<tt>LUAJIT_MODE_ON</tt> to turn a feature on, or |
|||
<tt>LUAJIT_MODE_FLUSH</tt> to flush cached code. |
|||
</p> |
|||
<p> |
|||
The following modes are defined: |
|||
</p> |
|||
|
|||
<h3 id="mode_engine"><tt>luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|flag)</tt></h3> |
|||
<p> |
|||
Turn the whole JIT compiler on or off or flush the whole cache of compiled code. |
|||
</p> |
|||
|
|||
<h3 id="mode_func"><tt>luaJIT_setmode(L, idx, LUAJIT_MODE_FUNC|flag)</tt><br> |
|||
<tt>luaJIT_setmode(L, idx, LUAJIT_MODE_ALLFUNC|flag)</tt><br> |
|||
<tt>luaJIT_setmode(L, idx, LUAJIT_MODE_ALLSUBFUNC|flag)</tt></h3> |
|||
<p> |
|||
This sets the mode for the function at the stack index <tt>idx</tt> or |
|||
the parent of the calling function (<tt>idx = 0</tt>). It either |
|||
enables JIT compilation for a function, disables it and flushes any |
|||
already compiled code or only flushes already compiled code. This |
|||
applies recursively to all sub-functions of the function with |
|||
<tt>LUAJIT_MODE_ALLFUNC</tt> or only to the sub-functions with |
|||
<tt>LUAJIT_MODE_ALLSUBFUNC</tt>. |
|||
</p> |
|||
|
|||
<h3 id="mode_trace"><tt>luaJIT_setmode(L, trace,<br> |
|||
LUAJIT_MODE_TRACE|LUAJIT_MODE_FLUSH)</tt></h3> |
|||
<p> |
|||
Flushes the specified root trace and all of its side traces from the cache. |
|||
The code for the trace will be retained as long as there are any other |
|||
traces which link to it. |
|||
</p> |
|||
|
|||
<h3 id="mode_wrapcfunc"><tt>luaJIT_setmode(L, idx, LUAJIT_MODE_WRAPCFUNC|flag)</tt></h3> |
|||
<p> |
|||
This mode defines a wrapper function for calls to C functions. If |
|||
called with <tt>LUAJIT_MODE_ON</tt>, the stack index at <tt>idx</tt> |
|||
must be a <tt>lightuserdata</tt> object holding a pointer to the wrapper |
|||
function. From now on all C functions are called through the wrapper |
|||
function. If called with <tt>LUAJIT_MODE_OFF</tt> this mode is turned |
|||
off and all C functions are directly called. |
|||
</p> |
|||
<p> |
|||
The wrapper function can be used for debugging purposes or to catch |
|||
and convert foreign exceptions. But please read the section on |
|||
<a href="extensions.html#exceptions">C++ exception interoperability</a> |
|||
first. Recommended usage can be seen in this C++ code excerpt: |
|||
</p> |
|||
<pre class="code"> |
|||
#include <exception> |
|||
#include "lua.hpp" |
|||
|
|||
// Catch C++ exceptions and convert them to Lua error messages. |
|||
// Customize as needed for your own exception classes. |
|||
static int wrap_exceptions(lua_State *L, lua_CFunction f) |
|||
{ |
|||
try { |
|||
return f(L); // Call wrapped function and return result. |
|||
} catch (const char *s) { // Catch and convert exceptions. |
|||
lua_pushstring(L, s); |
|||
} catch (std::exception& e) { |
|||
lua_pushstring(L, e.what()); |
|||
} catch (...) { |
|||
lua_pushliteral(L, "caught (...)"); |
|||
} |
|||
return lua_error(L); // Rethrow as a Lua error. |
|||
} |
|||
|
|||
static int myinit(lua_State *L) |
|||
{ |
|||
... |
|||
// Define wrapper function and enable it. |
|||
lua_pushlightuserdata(L, (void *)wrap_exceptions); |
|||
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_ON); |
|||
lua_pop(L, 1); |
|||
... |
|||
} |
|||
</pre> |
|||
<p> |
|||
Note that you can only define <b>a single global wrapper function</b>, |
|||
so be careful when using this mechanism from multiple C++ modules. |
|||
Also note that this mechanism is not without overhead. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,328 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>FFI Library</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>FFI Library</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a class="current" href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
|
|||
The FFI library allows <b>calling external C functions</b> and |
|||
<b>using C data structures</b> from pure Lua code. |
|||
|
|||
</p> |
|||
<p> |
|||
|
|||
The FFI library largely obviates the need to write tedious manual |
|||
Lua/C bindings in C. No need to learn a separate binding language |
|||
— <b>it parses plain C declarations!</b> These can be |
|||
cut-n-pasted from C header files or reference manuals. It's up to |
|||
the task of binding large libraries without the need for dealing with |
|||
fragile binding generators. |
|||
|
|||
</p> |
|||
<p> |
|||
The FFI library is tightly integrated into LuaJIT (it's not available |
|||
as a separate module). The code generated by the JIT-compiler for |
|||
accesses to C data structures from Lua code is on par with the |
|||
code a C compiler would generate. Calls to C functions can |
|||
be inlined in JIT-compiled code, unlike calls to functions bound via |
|||
the classic Lua/C API. |
|||
</p> |
|||
<p> |
|||
This page gives a short introduction to the usage of the FFI library. |
|||
<em>Please use the FFI sub-topics in the navigation bar to learn more.</em> |
|||
</p> |
|||
|
|||
<h2 id="call">Motivating Example: Calling External C Functions</h2> |
|||
<p> |
|||
It's really easy to call an external C library function: |
|||
</p> |
|||
<pre class="code mark"> |
|||
<span class="codemark">① |
|||
② |
|||
|
|||
|
|||
③</span>local ffi = require("ffi") |
|||
ffi.cdef[[ |
|||
<span style="color:#00a000;">int printf(const char *fmt, ...);</span> |
|||
]] |
|||
ffi.C.printf("Hello %s!", "world") |
|||
</pre> |
|||
<p> |
|||
So, let's pick that apart: |
|||
</p> |
|||
<p> |
|||
<span class="mark">①</span> Load the FFI library. |
|||
</p> |
|||
<p> |
|||
<span class="mark">②</span> Add a C declaration |
|||
for the function. The part inside the double-brackets (in green) is |
|||
just standard C syntax. |
|||
</p> |
|||
<p> |
|||
<span class="mark">③</span> Call the named |
|||
C function — Yes, it's that simple! |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Actually, what goes on behind the scenes is far from simple: <span |
|||
style="color:#4040c0;">③</span> makes use of the standard |
|||
C library namespace <tt>ffi.C</tt>. Indexing this namespace with |
|||
a symbol name (<tt>"printf"</tt>) automatically binds it to the |
|||
standard C library. The result is a special kind of object which, |
|||
when called, runs the <tt>printf</tt> function. The arguments passed |
|||
to this function are automatically converted from Lua objects to the |
|||
corresponding C types. |
|||
</p> |
|||
<p> |
|||
Ok, so maybe the use of <tt>printf()</tt> wasn't such a spectacular |
|||
example. You could have done that with <tt>io.write()</tt> and |
|||
<tt>string.format()</tt>, too. But you get the idea ... |
|||
</p> |
|||
<p> |
|||
So here's something to pop up a message box on Windows: |
|||
</p> |
|||
<pre class="code"> |
|||
local ffi = require("ffi") |
|||
ffi.cdef[[ |
|||
<span style="color:#00a000;">int MessageBoxA(void *w, const char *txt, const char *cap, int type);</span> |
|||
]] |
|||
ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0) |
|||
</pre> |
|||
<p> |
|||
Bing! Again, that was far too easy, no? |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Compare this with the effort required to bind that function using the |
|||
classic Lua/C API: create an extra C file, add a C function |
|||
that retrieves and checks the argument types passed from Lua and calls |
|||
the actual C function, add a list of module functions and their |
|||
names, add a <tt>luaopen_*</tt> function and register all module |
|||
functions, compile and link it into a shared library (DLL), move it to |
|||
the proper path, add Lua code that loads the module aaaand ... finally |
|||
call the binding function. Phew! |
|||
</p> |
|||
|
|||
<h2 id="cdata">Motivating Example: Using C Data Structures</h2> |
|||
<p> |
|||
The FFI library allows you to create and access C data |
|||
structures. Of course the main use for this is for interfacing with |
|||
C functions. But they can be used stand-alone, too. |
|||
</p> |
|||
<p> |
|||
Lua is built upon high-level data types. They are flexible, extensible |
|||
and dynamic. That's why we all love Lua so much. Alas, this can be |
|||
inefficient for certain tasks, where you'd really want a low-level |
|||
data type. E.g. a large array of a fixed structure needs to be |
|||
implemented with a big table holding lots of tiny tables. This imposes |
|||
both a substantial memory overhead as well as a performance overhead. |
|||
</p> |
|||
<p> |
|||
Here's a sketch of a library that operates on color images plus a |
|||
simple benchmark. First, the plain Lua version: |
|||
</p> |
|||
<pre class="code"> |
|||
local floor = math.floor |
|||
|
|||
local function image_ramp_green(n) |
|||
local img = {} |
|||
local f = 255/(n-1) |
|||
for i=1,n do |
|||
img[i] = { red = 0, green = floor((i-1)*f), blue = 0, alpha = 255 } |
|||
end |
|||
return img |
|||
end |
|||
|
|||
local function image_to_grey(img, n) |
|||
for i=1,n do |
|||
local y = floor(0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue) |
|||
img[i].red = y; img[i].green = y; img[i].blue = y |
|||
end |
|||
end |
|||
|
|||
local N = 400*400 |
|||
local img = image_ramp_green(N) |
|||
for i=1,1000 do |
|||
image_to_grey(img, N) |
|||
end |
|||
</pre> |
|||
<p> |
|||
This creates a table with 160.000 pixels, each of which is a table |
|||
holding four number values in the range of 0-255. First an image with |
|||
a green ramp is created (1D for simplicity), then the image is |
|||
converted to greyscale 1000 times. Yes, that's silly, but I was in |
|||
need of a simple example ... |
|||
</p> |
|||
<p> |
|||
And here's the FFI version. The modified parts have been marked in |
|||
bold: |
|||
</p> |
|||
<pre class="code mark"> |
|||
<span class="codemark">① |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
② |
|||
|
|||
③ |
|||
④ |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
③ |
|||
⑤</span><b>local ffi = require("ffi") |
|||
ffi.cdef[[ |
|||
</b><span style="color:#00a000;">typedef struct { uint8_t red, green, blue, alpha; } rgba_pixel;</span><b> |
|||
]]</b> |
|||
|
|||
local function image_ramp_green(n) |
|||
<b>local img = ffi.new("rgba_pixel[?]", n)</b> |
|||
local f = 255/(n-1) |
|||
for i=<b>0,n-1</b> do |
|||
<b>img[i].green = i*f</b> |
|||
<b>img[i].alpha = 255</b> |
|||
end |
|||
return img |
|||
end |
|||
|
|||
local function image_to_grey(img, n) |
|||
for i=<b>0,n-1</b> do |
|||
local y = <b>0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue</b> |
|||
img[i].red = y; img[i].green = y; img[i].blue = y |
|||
end |
|||
end |
|||
|
|||
local N = 400*400 |
|||
local img = image_ramp_green(N) |
|||
for i=1,1000 do |
|||
image_to_grey(img, N) |
|||
end |
|||
</pre> |
|||
<p> |
|||
Ok, so that wasn't too difficult: |
|||
</p> |
|||
<p> |
|||
<span class="mark">①</span> First, load the FFI |
|||
library and declare the low-level data type. Here we choose a |
|||
<tt>struct</tt> which holds four byte fields, one for each component |
|||
of a 4x8 bit RGBA pixel. |
|||
</p> |
|||
<p> |
|||
<span class="mark">②</span> Creating the data |
|||
structure with <tt>ffi.new()</tt> is straightforward — the |
|||
<tt>'?'</tt> is a placeholder for the number of elements of a |
|||
variable-length array. |
|||
</p> |
|||
<p> |
|||
<span class="mark">③</span> C arrays are |
|||
zero-based, so the indexes have to run from <tt>0</tt> to |
|||
<tt>n-1</tt>. One might want to allocate one more element instead to |
|||
simplify converting legacy code. |
|||
</p> |
|||
<p> |
|||
<span class="mark">④</span> Since <tt>ffi.new()</tt> |
|||
zero-fills the array by default, we only need to set the green and the |
|||
alpha fields. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑤</span> The calls to |
|||
<tt>math.floor()</tt> can be omitted here, because floating-point |
|||
numbers are already truncated towards zero when converting them to an |
|||
integer. This happens implicitly when the number is stored in the |
|||
fields of each pixel. |
|||
</p> |
|||
<p> |
|||
Now let's have a look at the impact of the changes: first, memory |
|||
consumption for the image is down from 22 Megabytes to |
|||
640 Kilobytes (400*400*4 bytes). That's a factor of 35x less! So, |
|||
yes, tables do have a noticeable overhead. BTW: The original program |
|||
would consume 40 Megabytes in plain Lua (on x64). |
|||
</p> |
|||
<p> |
|||
Next, performance: the pure Lua version runs in 9.57 seconds (52.9 |
|||
seconds with the Lua interpreter) and the FFI version runs in 0.48 |
|||
seconds on my machine (YMMV). That's a factor of 20x faster (110x |
|||
faster than the Lua interpreter). |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
The avid reader may notice that converting the pure Lua version over |
|||
to use array indexes for the colors (<tt>[1]</tt> instead of |
|||
<tt>.red</tt>, <tt>[2]</tt> instead of <tt>.green</tt> etc.) ought to |
|||
be more compact and faster. This is certainly true (by a factor of |
|||
~1.7x). Switching to a struct-of-arrays would help, too. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
However the resulting code would be less idiomatic and rather |
|||
error-prone. And it still doesn't get even close to the performance of |
|||
the FFI version of the code. Also, high-level data structures cannot |
|||
be easily passed to other C functions, especially I/O functions, |
|||
without undue conversion penalties. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,568 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>ffi.* API Functions</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
table.abitable { width: 30em; line-height: 1.2; } |
|||
tr.abihead td { font-weight: bold; } |
|||
td.abiparam { font-weight: bold; width: 6em; } |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1><tt>ffi.*</tt> API Functions</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a class="current" href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
This page describes the API functions provided by the FFI library in |
|||
detail. It's recommended to read through the |
|||
<a href="ext_ffi.html">introduction</a> and the |
|||
<a href="ext_ffi_tutorial.html">FFI tutorial</a> first. |
|||
</p> |
|||
|
|||
<h2 id="glossary">Glossary</h2> |
|||
<ul> |
|||
<li><b>cdecl</b> — An abstract C type declaration (a Lua |
|||
string).</li> |
|||
<li><b>ctype</b> — A C type object. This is a special kind of |
|||
<b>cdata</b> returned by <tt>ffi.typeof()</tt>. It serves as a |
|||
<b>cdata</b> <a href="#ffi_new">constructor</a> when called.</li> |
|||
<li><b>cdata</b> — A C data object. It holds a value of the |
|||
corresponding <b>ctype</b>.</li> |
|||
<li><b>ct</b> — A C type specification which can be used for |
|||
most of the API functions. Either a <b>cdecl</b>, a <b>ctype</b> or a |
|||
<b>cdata</b> serving as a template type.</li> |
|||
<li><b>cb</b> — A callback object. This is a C data object |
|||
holding a special function pointer. Calling this function from |
|||
C code runs an associated Lua function.</li> |
|||
<li><b>VLA</b> — A variable-length array is declared with a |
|||
<tt>?</tt> instead of the number of elements, e.g. <tt>"int[?]"</tt>. |
|||
The number of elements (<tt>nelem</tt>) must be given when it's |
|||
<a href="#ffi_new">created</a>.</li> |
|||
<li><b>VLS</b> — A variable-length struct is a <tt>struct</tt> C |
|||
type where the last element is a <b>VLA</b>. The same rules for |
|||
declaration and creation apply.</li> |
|||
</ul> |
|||
|
|||
<h2 id="decl">Declaring and Accessing External Symbols</h2> |
|||
<p> |
|||
External symbols must be declared first and can then be accessed by |
|||
indexing a <a href="ext_ffi_semantics.html#clib">C library |
|||
namespace</a>, which automatically binds the symbol to a specific |
|||
library. |
|||
</p> |
|||
|
|||
<h3 id="ffi_cdef"><tt>ffi.cdef(def)</tt></h3> |
|||
<p> |
|||
Adds multiple C declarations for types or external symbols (named |
|||
variables or functions). <tt>def</tt> must be a Lua string. It's |
|||
recommended to use the syntactic sugar for string arguments as |
|||
follows: |
|||
</p> |
|||
<pre class="code"> |
|||
ffi.cdef[[ |
|||
<span style="color:#00a000;">typedef struct foo { int a, b; } foo_t; // Declare a struct and typedef. |
|||
int dofoo(foo_t *f, int n); /* Declare an external C function. */</span> |
|||
]] |
|||
</pre> |
|||
<p> |
|||
The contents of the string (the part in green above) must be a |
|||
sequence of |
|||
<a href="ext_ffi_semantics.html#clang">C declarations</a>, |
|||
separated by semicolons. The trailing semicolon for a single |
|||
declaration may be omitted. |
|||
</p> |
|||
<p> |
|||
Please note that external symbols are only <em>declared</em>, but they |
|||
are <em>not bound</em> to any specific address, yet. Binding is |
|||
achieved with C library namespaces (see below). |
|||
</p> |
|||
<p style="color: #c00000;"> |
|||
C declarations are not passed through a C pre-processor, |
|||
yet. No pre-processor tokens are allowed, except for |
|||
<tt>#pragma pack</tt>. Replace <tt>#define</tt> in existing |
|||
C header files with <tt>enum</tt>, <tt>static const</tt> |
|||
or <tt>typedef</tt> and/or pass the files through an external |
|||
C pre-processor (once). Be careful not to include unneeded or |
|||
redundant declarations from unrelated header files. |
|||
</p> |
|||
|
|||
<h3 id="ffi_C"><tt>ffi.C</tt></h3> |
|||
<p> |
|||
This is the default C library namespace — note the |
|||
uppercase <tt>'C'</tt>. It binds to the default set of symbols or |
|||
libraries on the target system. These are more or less the same as a |
|||
C compiler would offer by default, without specifying extra link |
|||
libraries. |
|||
</p> |
|||
<p> |
|||
On POSIX systems, this binds to symbols in the default or global |
|||
namespace. This includes all exported symbols from the executable and |
|||
any libraries loaded into the global namespace. This includes at least |
|||
<tt>libc</tt>, <tt>libm</tt>, <tt>libdl</tt> (on Linux), |
|||
<tt>libgcc</tt> (if compiled with GCC), as well as any exported |
|||
symbols from the Lua/C API provided by LuaJIT itself. |
|||
</p> |
|||
<p> |
|||
On Windows systems, this binds to symbols exported from the |
|||
<tt>*.exe</tt>, the <tt>lua51.dll</tt> (i.e. the Lua/C API |
|||
provided by LuaJIT itself), the C runtime library LuaJIT was linked |
|||
with (<tt>msvcrt*.dll</tt>), <tt>kernel32.dll</tt>, |
|||
<tt>user32.dll</tt> and <tt>gdi32.dll</tt>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_load"><tt>clib = ffi.load(name [,global])</tt></h3> |
|||
<p> |
|||
This loads the dynamic library given by <tt>name</tt> and returns |
|||
a new C library namespace which binds to its symbols. On POSIX |
|||
systems, if <tt>global</tt> is <tt>true</tt>, the library symbols are |
|||
loaded into the global namespace, too. |
|||
</p> |
|||
<p> |
|||
If <tt>name</tt> is a path, the library is loaded from this path. |
|||
Otherwise <tt>name</tt> is canonicalized in a system-dependent way and |
|||
searched in the default search path for dynamic libraries: |
|||
</p> |
|||
<p> |
|||
On POSIX systems, if the name contains no dot, the extension |
|||
<tt>.so</tt> is appended. Also, the <tt>lib</tt> prefix is prepended |
|||
if necessary. So <tt>ffi.load("z")</tt> looks for <tt>"libz.so"</tt> |
|||
in the default shared library search path. |
|||
</p> |
|||
<p> |
|||
On Windows systems, if the name contains no dot, the extension |
|||
<tt>.dll</tt> is appended. So <tt>ffi.load("ws2_32")</tt> looks for |
|||
<tt>"ws2_32.dll"</tt> in the default DLL search path. |
|||
</p> |
|||
|
|||
<h2 id="create">Creating cdata Objects</h2> |
|||
<p> |
|||
The following API functions create cdata objects (<tt>type()</tt> |
|||
returns <tt>"cdata"</tt>). All created cdata objects are |
|||
<a href="ext_ffi_semantics.html#gc">garbage collected</a>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_new"><tt>cdata = ffi.new(ct [,nelem] [,init...])<br> |
|||
cdata = <em>ctype</em>([nelem,] [init...])</tt></h3> |
|||
<p> |
|||
Creates a cdata object for the given <tt>ct</tt>. VLA/VLS types |
|||
require the <tt>nelem</tt> argument. The second syntax uses a ctype as |
|||
a constructor and is otherwise fully equivalent. |
|||
</p> |
|||
<p> |
|||
The cdata object is initialized according to the |
|||
<a href="ext_ffi_semantics.html#init">rules for initializers</a>, |
|||
using the optional <tt>init</tt> arguments. Excess initializers cause |
|||
an error. |
|||
</p> |
|||
<p> |
|||
Performance notice: if you want to create many objects of one kind, |
|||
parse the cdecl only once and get its ctype with |
|||
<tt>ffi.typeof()</tt>. Then use the ctype as a constructor repeatedly. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Please note that an anonymous <tt>struct</tt> declaration implicitly |
|||
creates a new and distinguished ctype every time you use it for |
|||
<tt>ffi.new()</tt>. This is probably <b>not</b> what you want, |
|||
especially if you create more than one cdata object. Different anonymous |
|||
<tt>structs</tt> are not considered assignment-compatible by the |
|||
C standard, even though they may have the same fields! Also, they |
|||
are considered different types by the JIT-compiler, which may cause an |
|||
excessive number of traces. It's strongly suggested to either declare |
|||
a named <tt>struct</tt> or <tt>typedef</tt> with <tt>ffi.cdef()</tt> |
|||
or to create a single ctype object for an anonymous <tt>struct</tt> |
|||
with <tt>ffi.typeof()</tt>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_typeof"><tt>ctype = ffi.typeof(ct)</tt></h3> |
|||
<p> |
|||
Creates a ctype object for the given <tt>ct</tt>. |
|||
</p> |
|||
<p> |
|||
This function is especially useful to parse a cdecl only once and then |
|||
use the resulting ctype object as a <a href="#ffi_new">constructor</a>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_cast"><tt>cdata = ffi.cast(ct, init)</tt></h3> |
|||
<p> |
|||
Creates a scalar cdata object for the given <tt>ct</tt>. The cdata |
|||
object is initialized with <tt>init</tt> using the "cast" variant of |
|||
the <a href="ext_ffi_semantics.html#convert">C type conversion |
|||
rules</a>. |
|||
</p> |
|||
<p> |
|||
This functions is mainly useful to override the pointer compatibility |
|||
checks or to convert pointers to addresses or vice versa. |
|||
</p> |
|||
|
|||
<h3 id="ffi_metatype"><tt>ctype = ffi.metatype(ct, metatable)</tt></h3> |
|||
<p> |
|||
Creates a ctype object for the given <tt>ct</tt> and associates it with |
|||
a metatable. Only <tt>struct</tt>/<tt>union</tt> types, complex numbers |
|||
and vectors are allowed. Other types may be wrapped in a |
|||
<tt>struct</tt>, if needed. |
|||
</p> |
|||
<p> |
|||
The association with a metatable is permanent and cannot be changed |
|||
afterwards. Neither the contents of the <tt>metatable</tt> nor the |
|||
contents of an <tt>__index</tt> table (if any) may be modified |
|||
afterwards. The associated metatable automatically applies to all uses |
|||
of this type, no matter how the objects are created or where they |
|||
originate from. Note that pre-defined operations on types have |
|||
precedence (e.g. declared field names cannot be overridden). |
|||
</p> |
|||
<p> |
|||
All standard Lua metamethods are implemented. These are called directly, |
|||
without shortcuts and on any mix of types. For binary operations, the |
|||
left operand is checked first for a valid ctype metamethod. The |
|||
<tt>__gc</tt> metamethod only applies to <tt>struct</tt>/<tt>union</tt> |
|||
types and performs an implicit <a href="#ffi_gc"><tt>ffi.gc()</tt></a> |
|||
call during creation of an instance. |
|||
</p> |
|||
|
|||
<h3 id="ffi_gc"><tt>cdata = ffi.gc(cdata, finalizer)</tt></h3> |
|||
<p> |
|||
Associates a finalizer with a pointer or aggregate cdata object. The |
|||
cdata object is returned unchanged. |
|||
</p> |
|||
<p> |
|||
This function allows safe integration of unmanaged resources into the |
|||
automatic memory management of the LuaJIT garbage collector. Typical |
|||
usage: |
|||
</p> |
|||
<pre class="code"> |
|||
local p = ffi.gc(ffi.C.malloc(n), ffi.C.free) |
|||
... |
|||
p = nil -- Last reference to p is gone. |
|||
-- GC will eventually run finalizer: ffi.C.free(p) |
|||
</pre> |
|||
<p> |
|||
A cdata finalizer works like the <tt>__gc</tt> metamethod for userdata |
|||
objects: when the last reference to a cdata object is gone, the |
|||
associated finalizer is called with the cdata object as an argument. The |
|||
finalizer can be a Lua function or a cdata function or cdata function |
|||
pointer. An existing finalizer can be removed by setting a <tt>nil</tt> |
|||
finalizer, e.g. right before explicitly deleting a resource: |
|||
</p> |
|||
<pre class="code"> |
|||
ffi.C.free(ffi.gc(p, nil)) -- Manually free the memory. |
|||
</pre> |
|||
|
|||
<h2 id="info">C Type Information</h2> |
|||
<p> |
|||
The following API functions return information about C types. |
|||
They are most useful for inspecting cdata objects. |
|||
</p> |
|||
|
|||
<h3 id="ffi_sizeof"><tt>size = ffi.sizeof(ct [,nelem])</tt></h3> |
|||
<p> |
|||
Returns the size of <tt>ct</tt> in bytes. Returns <tt>nil</tt> if |
|||
the size is not known (e.g. for <tt>"void"</tt> or function types). |
|||
Requires <tt>nelem</tt> for VLA/VLS types, except for cdata objects. |
|||
</p> |
|||
|
|||
<h3 id="ffi_alignof"><tt>align = ffi.alignof(ct)</tt></h3> |
|||
<p> |
|||
Returns the minimum required alignment for <tt>ct</tt> in bytes. |
|||
</p> |
|||
|
|||
<h3 id="ffi_offsetof"><tt>ofs [,bpos,bsize] = ffi.offsetof(ct, field)</tt></h3> |
|||
<p> |
|||
Returns the offset (in bytes) of <tt>field</tt> relative to the start |
|||
of <tt>ct</tt>, which must be a <tt>struct</tt>. Additionally returns |
|||
the position and the field size (in bits) for bit fields. |
|||
</p> |
|||
|
|||
<h3 id="ffi_istype"><tt>status = ffi.istype(ct, obj)</tt></h3> |
|||
<p> |
|||
Returns <tt>true</tt> if <tt>obj</tt> has the C type given by |
|||
<tt>ct</tt>. Returns <tt>false</tt> otherwise. |
|||
</p> |
|||
<p> |
|||
C type qualifiers (<tt>const</tt> etc.) are ignored. Pointers are |
|||
checked with the standard pointer compatibility rules, but without any |
|||
special treatment for <tt>void *</tt>. If <tt>ct</tt> specifies a |
|||
<tt>struct</tt>/<tt>union</tt>, then a pointer to this type is accepted, |
|||
too. Otherwise the types must match exactly. |
|||
</p> |
|||
<p> |
|||
Note: this function accepts all kinds of Lua objects for the |
|||
<tt>obj</tt> argument, but always returns <tt>false</tt> for non-cdata |
|||
objects. |
|||
</p> |
|||
|
|||
<h2 id="util">Utility Functions</h2> |
|||
|
|||
<h3 id="ffi_errno"><tt>err = ffi.errno([newerr])</tt></h3> |
|||
<p> |
|||
Returns the error number set by the last C function call which |
|||
indicated an error condition. If the optional <tt>newerr</tt> argument |
|||
is present, the error number is set to the new value and the previous |
|||
value is returned. |
|||
</p> |
|||
<p> |
|||
This function offers a portable and OS-independent way to get and set the |
|||
error number. Note that only <em>some</em> C functions set the error |
|||
number. And it's only significant if the function actually indicated an |
|||
error condition (e.g. with a return value of <tt>-1</tt> or |
|||
<tt>NULL</tt>). Otherwise, it may or may not contain any previously set |
|||
value. |
|||
</p> |
|||
<p> |
|||
You're advised to call this function only when needed and as close as |
|||
possible after the return of the related C function. The |
|||
<tt>errno</tt> value is preserved across hooks, memory allocations, |
|||
invocations of the JIT compiler and other internal VM activity. The same |
|||
applies to the value returned by <tt>GetLastError()</tt> on Windows, but |
|||
you need to declare and call it yourself. |
|||
</p> |
|||
|
|||
<h3 id="ffi_string"><tt>str = ffi.string(ptr [,len])</tt></h3> |
|||
<p> |
|||
Creates an interned Lua string from the data pointed to by |
|||
<tt>ptr</tt>. |
|||
</p> |
|||
<p> |
|||
If the optional argument <tt>len</tt> is missing, <tt>ptr</tt> is |
|||
converted to a <tt>"char *"</tt> and the data is assumed to be |
|||
zero-terminated. The length of the string is computed with |
|||
<tt>strlen()</tt>. |
|||
</p> |
|||
<p> |
|||
Otherwise <tt>ptr</tt> is converted to a <tt>"void *"</tt> and |
|||
<tt>len</tt> gives the length of the data. The data may contain |
|||
embedded zeros and need not be byte-oriented (though this may cause |
|||
endianess issues). |
|||
</p> |
|||
<p> |
|||
This function is mainly useful to convert (temporary) |
|||
<tt>"const char *"</tt> pointers returned by |
|||
C functions to Lua strings and store them or pass them to other |
|||
functions expecting a Lua string. The Lua string is an (interned) copy |
|||
of the data and bears no relation to the original data area anymore. |
|||
Lua strings are 8 bit clean and may be used to hold arbitrary, |
|||
non-character data. |
|||
</p> |
|||
<p> |
|||
Performance notice: it's faster to pass the length of the string, if |
|||
it's known. E.g. when the length is returned by a C call like |
|||
<tt>sprintf()</tt>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_copy"><tt>ffi.copy(dst, src, len)<br> |
|||
ffi.copy(dst, str)</tt></h3> |
|||
<p> |
|||
Copies the data pointed to by <tt>src</tt> to <tt>dst</tt>. |
|||
<tt>dst</tt> is converted to a <tt>"void *"</tt> and <tt>src</tt> |
|||
is converted to a <tt>"const void *"</tt>. |
|||
</p> |
|||
<p> |
|||
In the first syntax, <tt>len</tt> gives the number of bytes to copy. |
|||
Caveat: if <tt>src</tt> is a Lua string, then <tt>len</tt> must not |
|||
exceed <tt>#src+1</tt>. |
|||
</p> |
|||
<p> |
|||
In the second syntax, the source of the copy must be a Lua string. All |
|||
bytes of the string <em>plus a zero-terminator</em> are copied to |
|||
<tt>dst</tt> (i.e. <tt>#src+1</tt> bytes). |
|||
</p> |
|||
<p> |
|||
Performance notice: <tt>ffi.copy()</tt> may be used as a faster |
|||
(inlinable) replacement for the C library functions |
|||
<tt>memcpy()</tt>, <tt>strcpy()</tt> and <tt>strncpy()</tt>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_fill"><tt>ffi.fill(dst, len [,c])</tt></h3> |
|||
<p> |
|||
Fills the data pointed to by <tt>dst</tt> with <tt>len</tt> constant |
|||
bytes, given by <tt>c</tt>. If <tt>c</tt> is omitted, the data is |
|||
zero-filled. |
|||
</p> |
|||
<p> |
|||
Performance notice: <tt>ffi.fill()</tt> may be used as a faster |
|||
(inlinable) replacement for the C library function |
|||
<tt>memset(dst, c, len)</tt>. Please note the different |
|||
order of arguments! |
|||
</p> |
|||
|
|||
<h2 id="target">Target-specific Information</h2> |
|||
|
|||
<h3 id="ffi_abi"><tt>status = ffi.abi(param)</tt></h3> |
|||
<p> |
|||
Returns <tt>true</tt> if <tt>param</tt> (a Lua string) applies for the |
|||
target ABI (Application Binary Interface). Returns <tt>false</tt> |
|||
otherwise. The following parameters are currently defined: |
|||
</p> |
|||
<table class="abitable"> |
|||
<tr class="abihead"> |
|||
<td class="abiparam">Parameter</td> |
|||
<td class="abidesc">Description</td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="abiparam">32bit</td><td class="abidesc">32 bit architecture</td></tr> |
|||
<tr class="even"> |
|||
<td class="abiparam">64bit</td><td class="abidesc">64 bit architecture</td></tr> |
|||
<tr class="odd separate"> |
|||
<td class="abiparam">le</td><td class="abidesc">Little-endian architecture</td></tr> |
|||
<tr class="even"> |
|||
<td class="abiparam">be</td><td class="abidesc">Big-endian architecture</td></tr> |
|||
<tr class="odd separate"> |
|||
<td class="abiparam">fpu</td><td class="abidesc">Target has a hardware FPU</td></tr> |
|||
<tr class="even"> |
|||
<td class="abiparam">softfp</td><td class="abidesc">softfp calling conventions</td></tr> |
|||
<tr class="odd"> |
|||
<td class="abiparam">hardfp</td><td class="abidesc">hardfp calling conventions</td></tr> |
|||
<tr class="even separate"> |
|||
<td class="abiparam">eabi</td><td class="abidesc">EABI variant of the standard ABI</td></tr> |
|||
<tr class="odd"> |
|||
<td class="abiparam">win</td><td class="abidesc">Windows variant of the standard ABI</td></tr> |
|||
<tr class="even"> |
|||
<td class="abiparam">uwp</td><td class="abidesc">Universal Windows Platform</td></tr> |
|||
<tr class="odd"> |
|||
<td class="abiparam">gc64</td><td class="abidesc">64 bit GC references</td></tr> |
|||
</table> |
|||
|
|||
<h3 id="ffi_os"><tt>ffi.os</tt></h3> |
|||
<p> |
|||
Contains the target OS name. Same contents as |
|||
<a href="ext_jit.html#jit_os"><tt>jit.os</tt></a>. |
|||
</p> |
|||
|
|||
<h3 id="ffi_arch"><tt>ffi.arch</tt></h3> |
|||
<p> |
|||
Contains the target architecture name. Same contents as |
|||
<a href="ext_jit.html#jit_arch"><tt>jit.arch</tt></a>. |
|||
</p> |
|||
|
|||
<h2 id="callback">Methods for Callbacks</h2> |
|||
<p> |
|||
The C types for <a href="ext_ffi_semantics.html#callback">callbacks</a> |
|||
have some extra methods: |
|||
</p> |
|||
|
|||
<h3 id="callback_free"><tt>cb:free()</tt></h3> |
|||
<p> |
|||
Free the resources associated with a callback. The associated Lua |
|||
function is unanchored and may be garbage collected. The callback |
|||
function pointer is no longer valid and must not be called anymore |
|||
(it may be reused by a subsequently created callback). |
|||
</p> |
|||
|
|||
<h3 id="callback_set"><tt>cb:set(func)</tt></h3> |
|||
<p> |
|||
Associate a new Lua function with a callback. The C type of the |
|||
callback and the callback function pointer are unchanged. |
|||
</p> |
|||
<p> |
|||
This method is useful to dynamically switch the receiver of callbacks |
|||
without creating a new callback each time and registering it again (e.g. |
|||
with a GUI library). |
|||
</p> |
|||
|
|||
<h2 id="extended">Extended Standard Library Functions</h2> |
|||
<p> |
|||
The following standard library functions have been extended to work |
|||
with cdata objects: |
|||
</p> |
|||
|
|||
<h3 id="tonumber"><tt>n = tonumber(cdata)</tt></h3> |
|||
<p> |
|||
Converts a number cdata object to a <tt>double</tt> and returns it as |
|||
a Lua number. This is particularly useful for boxed 64 bit |
|||
integer values. Caveat: this conversion may incur a precision loss. |
|||
</p> |
|||
|
|||
<h3 id="tostring"><tt>s = tostring(cdata)</tt></h3> |
|||
<p> |
|||
Returns a string representation of the value of 64 bit integers |
|||
(<tt><b>"</b>nnn<b>LL"</b></tt> or <tt><b>"</b>nnn<b>ULL"</b></tt>) or |
|||
complex numbers (<tt><b>"</b>re±im<b>i"</b></tt>). Otherwise |
|||
returns a string representation of the C type of a ctype object |
|||
(<tt><b>"ctype<</b>type<b>>"</b></tt>) or a cdata object |
|||
(<tt><b>"cdata<</b>type<b>>: </b>address"</tt>), unless you |
|||
override it with a <tt>__tostring</tt> metamethod (see |
|||
<a href="#ffi_metatype"><tt>ffi.metatype()</tt></a>). |
|||
</p> |
|||
|
|||
<h3 id="pairs"><tt>iter, obj, start = pairs(cdata)<br> |
|||
iter, obj, start = ipairs(cdata)<br></tt></h3> |
|||
<p> |
|||
Calls the <tt>__pairs</tt> or <tt>__ipairs</tt> metamethod of the |
|||
corresponding ctype. |
|||
</p> |
|||
|
|||
<h2 id="literals">Extensions to the Lua Parser</h2> |
|||
<p> |
|||
The parser for Lua source code treats numeric literals with the |
|||
suffixes <tt>LL</tt> or <tt>ULL</tt> as signed or unsigned 64 bit |
|||
integers. Case doesn't matter, but uppercase is recommended for |
|||
readability. It handles decimal (<tt>42LL</tt>), hexadecimal |
|||
(<tt>0x2aLL</tt>) and binary (<tt>0b101010LL</tt>) literals. |
|||
</p> |
|||
<p> |
|||
The imaginary part of complex numbers can be specified by suffixing |
|||
number literals with <tt>i</tt> or <tt>I</tt>, e.g. <tt>12.5i</tt>. |
|||
Caveat: you'll need to use <tt>1i</tt> to get an imaginary part with |
|||
the value one, since <tt>i</tt> itself still refers to a variable |
|||
named <tt>i</tt>. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
File diff suppressed because it is too large
@ -0,0 +1,599 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>FFI Tutorial</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
table.idiomtable { font-size: 90%; line-height: 1.2; } |
|||
table.idiomtable tt { font-size: 100%; } |
|||
table.idiomtable td { vertical-align: top; } |
|||
tr.idiomhead td { font-weight: bold; } |
|||
td.idiomlua b { font-weight: normal; color: #2142bf; } |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>FFI Tutorial</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a class="current" href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
This page is intended to give you an overview of the features of the FFI |
|||
library by presenting a few use cases and guidelines. |
|||
</p> |
|||
<p> |
|||
This page makes no attempt to explain all of the FFI library, though. |
|||
You'll want to have a look at the <a href="ext_ffi_api.html">ffi.* API |
|||
function reference</a> and the <a href="ext_ffi_semantics.html">FFI |
|||
semantics</a> to learn more. |
|||
</p> |
|||
|
|||
<h2 id="load">Loading the FFI Library</h2> |
|||
<p> |
|||
The FFI library is built into LuaJIT by default, but it's not loaded |
|||
and initialized by default. The suggested way to use the FFI library |
|||
is to add the following to the start of every Lua file that needs one |
|||
of its functions: |
|||
</p> |
|||
<pre class="code"> |
|||
local ffi = require("ffi") |
|||
</pre> |
|||
<p> |
|||
Please note this doesn't define an <tt>ffi</tt> variable in the table |
|||
of globals — you really need to use the local variable. The |
|||
<tt>require</tt> function ensures the library is only loaded once. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Note: If you want to experiment with the FFI from the interactive prompt |
|||
of the command line executable, omit the <tt>local</tt>, as it doesn't |
|||
preserve local variables across lines. |
|||
</p> |
|||
|
|||
<h2 id="sleep">Accessing Standard System Functions</h2> |
|||
<p> |
|||
The following code explains how to access standard system functions. |
|||
We slowly print two lines of dots by sleeping for 10 milliseconds |
|||
after each dot: |
|||
</p> |
|||
<pre class="code mark"> |
|||
<span class="codemark"> |
|||
① |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
② |
|||
③ |
|||
④ |
|||
|
|||
|
|||
|
|||
⑤ |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
⑥</span>local ffi = require("ffi") |
|||
ffi.cdef[[ |
|||
<span style="color:#00a000;">void Sleep(int ms); |
|||
int poll(struct pollfd *fds, unsigned long nfds, int timeout);</span> |
|||
]] |
|||
|
|||
local sleep |
|||
if ffi.os == "Windows" then |
|||
function sleep(s) |
|||
ffi.C.Sleep(s*1000) |
|||
end |
|||
else |
|||
function sleep(s) |
|||
ffi.C.poll(nil, 0, s*1000) |
|||
end |
|||
end |
|||
|
|||
for i=1,160 do |
|||
io.write("."); io.flush() |
|||
sleep(0.01) |
|||
end |
|||
io.write("\n") |
|||
</pre> |
|||
<p> |
|||
Here's the step-by-step explanation: |
|||
</p> |
|||
<p> |
|||
<span class="mark">①</span> This defines the |
|||
C library functions we're going to use. The part inside the |
|||
double-brackets (in green) is just standard C syntax. You can |
|||
usually get this info from the C header files or the |
|||
documentation provided by each C library or C compiler. |
|||
</p> |
|||
<p> |
|||
<span class="mark">②</span> The difficulty we're |
|||
facing here, is that there are different standards to choose from. |
|||
Windows has a simple <tt>Sleep()</tt> function. On other systems there |
|||
are a variety of functions available to achieve sub-second sleeps, but |
|||
with no clear consensus. Thankfully <tt>poll()</tt> can be used for |
|||
this task, too, and it's present on most non-Windows systems. The |
|||
check for <tt>ffi.os</tt> makes sure we use the Windows-specific |
|||
function only on Windows systems. |
|||
</p> |
|||
<p> |
|||
<span class="mark">③</span> Here we're wrapping the |
|||
call to the C function in a Lua function. This isn't strictly |
|||
necessary, but it's helpful to deal with system-specific issues only |
|||
in one part of the code. The way we're wrapping it ensures the check |
|||
for the OS is only done during initialization and not for every call. |
|||
</p> |
|||
<p> |
|||
<span class="mark">④</span> A more subtle point is |
|||
that we defined our <tt>sleep()</tt> function (for the sake of this |
|||
example) as taking the number of seconds, but accepting fractional |
|||
seconds. Multiplying this by 1000 gets us milliseconds, but that still |
|||
leaves it a Lua number, which is a floating-point value. Alas, the |
|||
<tt>Sleep()</tt> function only accepts an integer value. Luckily for |
|||
us, the FFI library automatically performs the conversion when calling |
|||
the function (truncating the FP value towards zero, like in C). |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Some readers will notice that <tt>Sleep()</tt> is part of |
|||
<tt>KERNEL32.DLL</tt> and is also a <tt>stdcall</tt> function. So how |
|||
can this possibly work? The FFI library provides the <tt>ffi.C</tt> |
|||
default C library namespace, which allows calling functions from |
|||
the default set of libraries, like a C compiler would. Also, the |
|||
FFI library automatically detects <tt>stdcall</tt> functions, so you |
|||
don't need to declare them as such. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑤</span> The <tt>poll()</tt> |
|||
function takes a couple more arguments we're not going to use. You can |
|||
simply use <tt>nil</tt> to pass a <tt>NULL</tt> pointer and <tt>0</tt> |
|||
for the <tt>nfds</tt> parameter. Please note that the |
|||
number <tt>0</tt> <em>does not convert to a pointer value</em>, |
|||
unlike in C++. You really have to pass pointers to pointer arguments |
|||
and numbers to number arguments. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
The page on <a href="ext_ffi_semantics.html">FFI semantics</a> has all |
|||
of the gory details about |
|||
<a href="ext_ffi_semantics.html#convert">conversions between Lua |
|||
objects and C types</a>. For the most part you don't have to deal |
|||
with this, as it's performed automatically and it's carefully designed |
|||
to bridge the semantic differences between Lua and C. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑥</span> Now that we have defined |
|||
our own <tt>sleep()</tt> function, we can just call it from plain Lua |
|||
code. That wasn't so bad, huh? Turning these boring animated dots into |
|||
a fascinating best-selling game is left as an exercise for the reader. |
|||
:-) |
|||
</p> |
|||
|
|||
<h2 id="zlib">Accessing the zlib Compression Library</h2> |
|||
<p> |
|||
The following code shows how to access the <a |
|||
href="https://zlib.net/"><span class="ext">»</span> zlib</a> compression library from Lua code. |
|||
We'll define two convenience wrapper functions that take a string and |
|||
compress or uncompress it to another string: |
|||
</p> |
|||
<pre class="code mark"> |
|||
<span class="codemark"> |
|||
① |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
② |
|||
|
|||
|
|||
③ |
|||
|
|||
④ |
|||
|
|||
|
|||
⑤ |
|||
|
|||
|
|||
⑥ |
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
|
|||
⑦</span>local ffi = require("ffi") |
|||
ffi.cdef[[ |
|||
<span style="color:#00a000;">unsigned long compressBound(unsigned long sourceLen); |
|||
int compress2(uint8_t *dest, unsigned long *destLen, |
|||
const uint8_t *source, unsigned long sourceLen, int level); |
|||
int uncompress(uint8_t *dest, unsigned long *destLen, |
|||
const uint8_t *source, unsigned long sourceLen);</span> |
|||
]] |
|||
local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z") |
|||
|
|||
local function compress(txt) |
|||
local n = zlib.compressBound(#txt) |
|||
local buf = ffi.new("uint8_t[?]", n) |
|||
local buflen = ffi.new("unsigned long[1]", n) |
|||
local res = zlib.compress2(buf, buflen, txt, #txt, 9) |
|||
assert(res == 0) |
|||
return ffi.string(buf, buflen[0]) |
|||
end |
|||
|
|||
local function uncompress(comp, n) |
|||
local buf = ffi.new("uint8_t[?]", n) |
|||
local buflen = ffi.new("unsigned long[1]", n) |
|||
local res = zlib.uncompress(buf, buflen, comp, #comp) |
|||
assert(res == 0) |
|||
return ffi.string(buf, buflen[0]) |
|||
end |
|||
|
|||
-- Simple test code. |
|||
local txt = string.rep("abcd", 1000) |
|||
print("Uncompressed size: ", #txt) |
|||
local c = compress(txt) |
|||
print("Compressed size: ", #c) |
|||
local txt2 = uncompress(c, #txt) |
|||
assert(txt2 == txt) |
|||
</pre> |
|||
<p> |
|||
Here's the step-by-step explanation: |
|||
</p> |
|||
<p> |
|||
<span class="mark">①</span> This defines some of the |
|||
C functions provided by zlib. For the sake of this example, some |
|||
type indirections have been reduced and it uses the pre-defined |
|||
fixed-size integer types, while still adhering to the zlib API/ABI. |
|||
</p> |
|||
<p> |
|||
<span class="mark">②</span> This loads the zlib shared |
|||
library. On POSIX systems it's named <tt>libz.so</tt> and usually |
|||
comes pre-installed. Since <tt>ffi.load()</tt> automatically adds any |
|||
missing standard prefixes/suffixes, we can simply load the |
|||
<tt>"z"</tt> library. On Windows it's named <tt>zlib1.dll</tt> and |
|||
you'll have to download it first from the |
|||
<a href="https://zlib.net/"><span class="ext">»</span> zlib site</a>. The check for |
|||
<tt>ffi.os</tt> makes sure we pass the right name to |
|||
<tt>ffi.load()</tt>. |
|||
</p> |
|||
<p> |
|||
<span class="mark">③</span> First, the maximum size of |
|||
the compression buffer is obtained by calling the |
|||
<tt>zlib.compressBound</tt> function with the length of the |
|||
uncompressed string. The next line allocates a byte buffer of this |
|||
size. The <tt>[?]</tt> in the type specification indicates a |
|||
variable-length array (VLA). The actual number of elements of this |
|||
array is given as the 2nd argument to <tt>ffi.new()</tt>. |
|||
</p> |
|||
<p> |
|||
<span class="mark">④</span> This may look strange at |
|||
first, but have a look at the declaration of the <tt>compress2</tt> |
|||
function from zlib: the destination length is defined as a pointer! |
|||
This is because you pass in the maximum buffer size and get back the |
|||
actual length that was used. |
|||
</p> |
|||
<p> |
|||
In C you'd pass in the address of a local variable |
|||
(<tt>&buflen</tt>). But since there's no address-of operator in |
|||
Lua, we'll just pass in a one-element array. Conveniently it can be |
|||
initialized with the maximum buffer size in one step. Calling the |
|||
actual <tt>zlib.compress2</tt> function is then straightforward. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑤</span> We want to return the |
|||
compressed data as a Lua string, so we'll use <tt>ffi.string()</tt>. |
|||
It needs a pointer to the start of the data and the actual length. The |
|||
length has been returned in the <tt>buflen</tt> array, so we'll just |
|||
get it from there. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Note that since the function returns now, the <tt>buf</tt> and |
|||
<tt>buflen</tt> variables will eventually be garbage collected. This |
|||
is fine, because <tt>ffi.string()</tt> has copied the contents to a |
|||
newly created (interned) Lua string. If you plan to call this function |
|||
lots of times, consider reusing the buffers and/or handing back the |
|||
results in buffers instead of strings. This will reduce the overhead |
|||
for garbage collection and string interning. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑥</span> The <tt>uncompress</tt> |
|||
functions does the exact opposite of the <tt>compress</tt> function. |
|||
The compressed data doesn't include the size of the original string, |
|||
so this needs to be passed in. Otherwise no surprises here. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑦</span> The code, that makes use |
|||
of the functions we just defined, is just plain Lua code. It doesn't |
|||
need to know anything about the LuaJIT FFI — the convenience |
|||
wrapper functions completely hide it. |
|||
</p> |
|||
<p> |
|||
One major advantage of the LuaJIT FFI is that you are now able to |
|||
write those wrappers <em>in Lua</em>. And at a fraction of the time it |
|||
would cost you to create an extra C module using the Lua/C API. |
|||
Many of the simpler C functions can probably be used directly |
|||
from your Lua code, without any wrappers. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Side note: the zlib API uses the <tt>long</tt> type for passing |
|||
lengths and sizes around. But all those zlib functions actually only |
|||
deal with 32 bit values. This is an unfortunate choice for a |
|||
public API, but may be explained by zlib's history — we'll just |
|||
have to deal with it. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
First, you should know that a <tt>long</tt> is a 64 bit type e.g. |
|||
on POSIX/x64 systems, but a 32 bit type on Windows/x64 and on |
|||
32 bit systems. Thus a <tt>long</tt> result can be either a plain |
|||
Lua number or a boxed 64 bit integer cdata object, depending on |
|||
the target system. |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Ok, so the <tt>ffi.*</tt> functions generally accept cdata objects |
|||
wherever you'd want to use a number. That's why we get a away with |
|||
passing <tt>n</tt> to <tt>ffi.string()</tt> above. But other Lua |
|||
library functions or modules don't know how to deal with this. So for |
|||
maximum portability one needs to use <tt>tonumber()</tt> on returned |
|||
<tt>long</tt> results before passing them on. Otherwise the |
|||
application might work on some systems, but would fail in a POSIX/x64 |
|||
environment. |
|||
</p> |
|||
|
|||
<h2 id="metatype">Defining Metamethods for a C Type</h2> |
|||
<p> |
|||
The following code explains how to define metamethods for a C type. |
|||
We define a simple point type and add some operations to it: |
|||
</p> |
|||
<pre class="code mark"> |
|||
<span class="codemark"> |
|||
① |
|||
|
|||
|
|||
|
|||
② |
|||
|
|||
③ |
|||
|
|||
④ |
|||
|
|||
|
|||
|
|||
⑤ |
|||
|
|||
⑥</span>local ffi = require("ffi") |
|||
ffi.cdef[[ |
|||
<span style="color:#00a000;">typedef struct { double x, y; } point_t;</span> |
|||
]] |
|||
|
|||
local point |
|||
local mt = { |
|||
__add = function(a, b) return point(a.x+b.x, a.y+b.y) end, |
|||
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end, |
|||
__index = { |
|||
area = function(a) return a.x*a.x + a.y*a.y end, |
|||
}, |
|||
} |
|||
point = ffi.metatype("point_t", mt) |
|||
|
|||
local a = point(3, 4) |
|||
print(a.x, a.y) --> 3 4 |
|||
print(#a) --> 5 |
|||
print(a:area()) --> 25 |
|||
local b = a + point(0.5, 8) |
|||
print(#b) --> 12.5 |
|||
</pre> |
|||
<p> |
|||
Here's the step-by-step explanation: |
|||
</p> |
|||
<p> |
|||
<span class="mark">①</span> This defines the C type for a |
|||
two-dimensional point object. |
|||
</p> |
|||
<p> |
|||
<span class="mark">②</span> We have to declare the variable |
|||
holding the point constructor first, because it's used inside of a |
|||
metamethod. |
|||
</p> |
|||
<p> |
|||
<span class="mark">③</span> Let's define an <tt>__add</tt> |
|||
metamethod which adds the coordinates of two points and creates a new |
|||
point object. For simplicity, this function assumes that both arguments |
|||
are points. But it could be any mix of objects, if at least one operand |
|||
is of the required type (e.g. adding a point plus a number or vice |
|||
versa). Our <tt>__len</tt> metamethod returns the distance of a point to |
|||
the origin. |
|||
</p> |
|||
<p> |
|||
<span class="mark">④</span> If we run out of operators, we can |
|||
define named methods, too. Here the <tt>__index</tt> table defines an |
|||
<tt>area</tt> function. For custom indexing needs, one might want to |
|||
define <tt>__index</tt> and <tt>__newindex</tt> <em>functions</em> instead. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑤</span> This associates the metamethods with |
|||
our C type. This only needs to be done once. For convenience, a |
|||
constructor is returned by |
|||
<a href="ext_ffi_api.html#ffi_metatype"><tt>ffi.metatype()</tt></a>. |
|||
We're not required to use it, though. The original C type can still |
|||
be used e.g. to create an array of points. The metamethods automatically |
|||
apply to any and all uses of this type. |
|||
</p> |
|||
<p> |
|||
Please note that the association with a metatable is permanent and |
|||
<b>the metatable must not be modified afterwards!</b> Ditto for the |
|||
<tt>__index</tt> table. |
|||
</p> |
|||
<p> |
|||
<span class="mark">⑥</span> Here are some simple usage examples |
|||
for the point type and their expected results. The pre-defined |
|||
operations (such as <tt>a.x</tt>) can be freely mixed with the newly |
|||
defined metamethods. Note that <tt>area</tt> is a method and must be |
|||
called with the Lua syntax for methods: <tt>a:area()</tt>, not |
|||
<tt>a.area()</tt>. |
|||
</p> |
|||
<p> |
|||
The C type metamethod mechanism is most useful when used in |
|||
conjunction with C libraries that are written in an object-oriented |
|||
style. Creators return a pointer to a new instance and methods take an |
|||
instance pointer as the first argument. Sometimes you can just point |
|||
<tt>__index</tt> to the library namespace and <tt>__gc</tt> to the |
|||
destructor and you're done. But often enough you'll want to add |
|||
convenience wrappers, e.g. to return actual Lua strings or when |
|||
returning multiple values. |
|||
</p> |
|||
<p> |
|||
Some C libraries only declare instance pointers as an opaque |
|||
<tt>void *</tt> type. In this case you can use a fake type for all |
|||
declarations, e.g. a pointer to a named (incomplete) struct will do: |
|||
<tt>typedef struct foo_type *foo_handle</tt>. The C side doesn't |
|||
know what you declare with the LuaJIT FFI, but as long as the underlying |
|||
types are compatible, everything still works. |
|||
</p> |
|||
|
|||
<h2 id="idioms">Translating C Idioms</h2> |
|||
<p> |
|||
Here's a list of common C idioms and their translation to the |
|||
LuaJIT FFI: |
|||
</p> |
|||
<table class="idiomtable"> |
|||
<tr class="idiomhead"> |
|||
<td class="idiomdesc">Idiom</td> |
|||
<td class="idiomc">C code</td> |
|||
<td class="idiomlua">Lua code</td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="idiomdesc">Pointer dereference<br><tt>int *p;</tt></td><td class="idiomc"><tt>x = *p;<br>*p = y;</tt></td><td class="idiomlua"><tt>x = <b>p[0]</b><br><b>p[0]</b> = y</tt></td></tr> |
|||
<tr class="even"> |
|||
<td class="idiomdesc">Pointer indexing<br><tt>int i, *p;</tt></td><td class="idiomc"><tt>x = p[i];<br>p[i+1] = y;</tt></td><td class="idiomlua"><tt>x = p[i]<br>p[i+1] = y</tt></td></tr> |
|||
<tr class="odd"> |
|||
<td class="idiomdesc">Array indexing<br><tt>int i, a[];</tt></td><td class="idiomc"><tt>x = a[i];<br>a[i+1] = y;</tt></td><td class="idiomlua"><tt>x = a[i]<br>a[i+1] = y</tt></td></tr> |
|||
<tr class="even separate"> |
|||
<td class="idiomdesc"><tt>struct</tt>/<tt>union</tt> dereference<br><tt>struct foo s;</tt></td><td class="idiomc"><tt>x = s.field;<br>s.field = y;</tt></td><td class="idiomlua"><tt>x = s.field<br>s.field = y</tt></td></tr> |
|||
<tr class="odd"> |
|||
<td class="idiomdesc"><tt>struct</tt>/<tt>union</tt> pointer deref.<br><tt>struct foo *sp;</tt></td><td class="idiomc"><tt>x = sp->field;<br>sp->field = y;</tt></td><td class="idiomlua"><tt>x = <b>s.field</b><br><b>s.field</b> = y</tt></td></tr> |
|||
<tr class="even separate"> |
|||
<td class="idiomdesc">Pointer arithmetic<br><tt>int i, *p;</tt></td><td class="idiomc"><tt>x = p + i;<br>y = p - i;</tt></td><td class="idiomlua"><tt>x = p + i<br>y = p - i</tt></td></tr> |
|||
<tr class="odd"> |
|||
<td class="idiomdesc">Pointer difference<br><tt>int *p1, *p2;</tt></td><td class="idiomc"><tt>x = p1 - p2;</tt></td><td class="idiomlua"><tt>x = p1 - p2</tt></td></tr> |
|||
<tr class="even"> |
|||
<td class="idiomdesc">Array element pointer<br><tt>int i, a[];</tt></td><td class="idiomc"><tt>x = &a[i];</tt></td><td class="idiomlua"><tt>x = <b>a+i</b></tt></td></tr> |
|||
<tr class="odd"> |
|||
<td class="idiomdesc">Cast pointer to address<br><tt>int *p;</tt></td><td class="idiomc"><tt>x = (intptr_t)p;</tt></td><td class="idiomlua"><tt>x = <b>tonumber(<br> ffi.cast("intptr_t",<br> p))</b></tt></td></tr> |
|||
<tr class="even separate"> |
|||
<td class="idiomdesc">Functions with outargs<br><tt>void foo(int *inoutlen);</tt></td><td class="idiomc"><tt>int len = x;<br>foo(&len);<br>y = len;</tt></td><td class="idiomlua"><tt><b>local len =<br> ffi.new("int[1]", x)<br>foo(len)<br>y = len[0]</b></tt></td></tr> |
|||
<tr class="odd"> |
|||
<td class="idiomdesc"><a href="ext_ffi_semantics.html#convert_vararg">Vararg conversions</a><br><tt>int printf(char *fmt, ...);</tt></td><td class="idiomc"><tt>printf("%g", 1.0);<br>printf("%d", 1);<br> </tt></td><td class="idiomlua"><tt>printf("%g", 1);<br>printf("%d",<br> <b>ffi.new("int", 1)</b>)</tt></td></tr> |
|||
</table> |
|||
|
|||
<h2 id="cache">To Cache or Not to Cache</h2> |
|||
<p> |
|||
It's a common Lua idiom to cache library functions in local variables |
|||
or upvalues, e.g.: |
|||
</p> |
|||
<pre class="code"> |
|||
local byte, char = string.byte, string.char |
|||
local function foo(x) |
|||
return char(byte(x)+1) |
|||
end |
|||
</pre> |
|||
<p> |
|||
This replaces several hash-table lookups with a (faster) direct use of |
|||
a local or an upvalue. This is less important with LuaJIT, since the |
|||
JIT compiler optimizes hash-table lookups a lot and is even able to |
|||
hoist most of them out of the inner loops. It can't eliminate |
|||
<em>all</em> of them, though, and it saves some typing for often-used |
|||
functions. So there's still a place for this, even with LuaJIT. |
|||
</p> |
|||
<p> |
|||
The situation is a bit different with C function calls via the |
|||
FFI library. The JIT compiler has special logic to eliminate <em>all |
|||
of the lookup overhead</em> for functions resolved from a |
|||
<a href="ext_ffi_semantics.html#clib">C library namespace</a>! |
|||
Thus it's not helpful and actually counter-productive to cache |
|||
individual C functions like this: |
|||
</p> |
|||
<pre class="code"> |
|||
local <b>funca</b>, <b>funcb</b> = ffi.C.funca, ffi.C.funcb -- <span style="color:#c00000;">Not helpful!</span> |
|||
local function foo(x, n) |
|||
for i=1,n do <b>funcb</b>(<b>funca</b>(x, i), 1) end |
|||
end |
|||
</pre> |
|||
<p> |
|||
This turns them into indirect calls and generates bigger and slower |
|||
machine code. Instead you'll want to cache the namespace itself and |
|||
rely on the JIT compiler to eliminate the lookups: |
|||
</p> |
|||
<pre class="code"> |
|||
local <b>C</b> = ffi.C -- <span style="color:#00a000;">Instead use this!</span> |
|||
local function foo(x, n) |
|||
for i=1,n do <b>C.funcb</b>(<b>C.funca</b>(x, i), 1) end |
|||
end |
|||
</pre> |
|||
<p> |
|||
This generates both shorter and faster code. So <b>don't cache |
|||
C functions</b>, but <b>do</b> cache namespaces! Most often the |
|||
namespace is already in a local variable at an outer scope, e.g. from |
|||
<tt>local lib = ffi.load(...)</tt>. Note that copying |
|||
it to a local variable in the function scope is unnecessary. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,197 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>jit.* Library</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1><tt>jit.*</tt> Library</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a class="current" href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
The functions in this built-in module control the behavior of the JIT |
|||
compiler engine. Note that JIT-compilation is fully automatic — |
|||
you probably won't need to use any of the following functions unless |
|||
you have special needs. |
|||
</p> |
|||
|
|||
<h3 id="jit_onoff"><tt>jit.on()<br> |
|||
jit.off()</tt></h3> |
|||
<p> |
|||
Turns the whole JIT compiler on (default) or off. |
|||
</p> |
|||
<p> |
|||
These functions are typically used with the command line options |
|||
<tt>-j on</tt> or <tt>-j off</tt>. |
|||
</p> |
|||
|
|||
<h3 id="jit_flush"><tt>jit.flush()</tt></h3> |
|||
<p> |
|||
Flushes the whole cache of compiled code. |
|||
</p> |
|||
|
|||
<h3 id="jit_onoff_func"><tt>jit.on(func|true [,true|false])<br> |
|||
jit.off(func|true [,true|false])<br> |
|||
jit.flush(func|true [,true|false])</tt></h3> |
|||
<p> |
|||
<tt>jit.on</tt> enables JIT compilation for a Lua function (this is |
|||
the default). |
|||
</p> |
|||
<p> |
|||
<tt>jit.off</tt> disables JIT compilation for a Lua function and |
|||
flushes any already compiled code from the code cache. |
|||
</p> |
|||
<p> |
|||
<tt>jit.flush</tt> flushes the code, but doesn't affect the |
|||
enable/disable status. |
|||
</p> |
|||
<p> |
|||
The current function, i.e. the Lua function calling this library |
|||
function, can also be specified by passing <tt>true</tt> as the first |
|||
argument. |
|||
</p> |
|||
<p> |
|||
If the second argument is <tt>true</tt>, JIT compilation is also |
|||
enabled, disabled or flushed recursively for all sub-functions of a |
|||
function. With <tt>false</tt> only the sub-functions are affected. |
|||
</p> |
|||
<p> |
|||
The <tt>jit.on</tt> and <tt>jit.off</tt> functions only set a flag |
|||
which is checked when the function is about to be compiled. They do |
|||
not trigger immediate compilation. |
|||
</p> |
|||
<p> |
|||
Typical usage is <tt>jit.off(true, true)</tt> in the main chunk |
|||
of a module to turn off JIT compilation for the whole module for |
|||
debugging purposes. |
|||
</p> |
|||
|
|||
<h3 id="jit_flush_tr"><tt>jit.flush(tr)</tt></h3> |
|||
<p> |
|||
Flushes the root trace, specified by its number, and all of its side |
|||
traces from the cache. The code for the trace will be retained as long |
|||
as there are any other traces which link to it. |
|||
</p> |
|||
|
|||
<h3 id="jit_status"><tt>status, ... = jit.status()</tt></h3> |
|||
<p> |
|||
Returns the current status of the JIT compiler. The first result is |
|||
either <tt>true</tt> or <tt>false</tt> if the JIT compiler is turned |
|||
on or off. The remaining results are strings for CPU-specific features |
|||
and enabled optimizations. |
|||
</p> |
|||
|
|||
<h3 id="jit_version"><tt>jit.version</tt></h3> |
|||
<p> |
|||
Contains the LuaJIT version string. |
|||
</p> |
|||
|
|||
<h3 id="jit_version_num"><tt>jit.version_num</tt></h3> |
|||
<p> |
|||
Contains the version number of the LuaJIT core. Version xx.yy.zz |
|||
is represented by the decimal number xxyyzz. |
|||
</p> |
|||
|
|||
<h3 id="jit_os"><tt>jit.os</tt></h3> |
|||
<p> |
|||
Contains the target OS name: |
|||
"Windows", "Linux", "OSX", "BSD", "POSIX" or "Other". |
|||
</p> |
|||
|
|||
<h3 id="jit_arch"><tt>jit.arch</tt></h3> |
|||
<p> |
|||
Contains the target architecture name: |
|||
"x86", "x64", "arm", "arm64", "arm64be", "ppc", "mips", "mipsel", "mips64", "mips64el", "mips64r6", "mips64r6el". |
|||
</p> |
|||
|
|||
<h2 id="jit_opt"><tt>jit.opt.*</tt> — JIT compiler optimization control</h2> |
|||
<p> |
|||
This sub-module provides the backend for the <tt>-O</tt> command line |
|||
option. |
|||
</p> |
|||
<p> |
|||
You can also use it programmatically, e.g.: |
|||
</p> |
|||
<pre class="code"> |
|||
jit.opt.start(2) -- same as -O2 |
|||
jit.opt.start("-dce") |
|||
jit.opt.start("hotloop=10", "hotexit=2") |
|||
</pre> |
|||
<p> |
|||
Unlike in LuaJIT 1.x, the module is built-in and |
|||
<b>optimization is turned on by default!</b> |
|||
It's no longer necessary to run <tt>require("jit.opt").start()</tt>, |
|||
which was one of the ways to enable optimization. |
|||
</p> |
|||
|
|||
<h2 id="jit_util"><tt>jit.util.*</tt> — JIT compiler introspection</h2> |
|||
<p> |
|||
This sub-module holds functions to introspect the bytecode, generated |
|||
traces, the IR and the generated machine code. The functionality |
|||
provided by this module is still in flux and therefore undocumented. |
|||
</p> |
|||
<p> |
|||
The debug modules <tt>-jbc</tt>, <tt>-jv</tt> and <tt>-jdump</tt> make |
|||
extensive use of these functions. Please check out their source code, |
|||
if you want to know more. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,361 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Profiler</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Profiler</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a class="current" href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
LuaJIT has an integrated statistical profiler with very low overhead. It |
|||
allows sampling the currently executing stack and other parameters in |
|||
regular intervals. |
|||
</p> |
|||
<p> |
|||
The integrated profiler can be accessed from three levels: |
|||
</p> |
|||
<ul> |
|||
<li>The <a href="#hl_profiler">bundled high-level profiler</a>, invoked by the |
|||
<a href="#j_p"><tt>-jp</tt></a> command line option.</li> |
|||
<li>A <a href="#ll_lua_api">low-level Lua API</a> to control the profiler.</li> |
|||
<li>A <a href="#ll_c_api">low-level C API</a> to control the profiler.</li> |
|||
</ul> |
|||
|
|||
<h2 id="hl_profiler">High-Level Profiler</h2> |
|||
<p> |
|||
The bundled high-level profiler offers basic profiling functionality. It |
|||
generates simple textual summaries or source code annotations. It can be |
|||
accessed with the <a href="#j_p"><tt>-jp</tt></a> command line option |
|||
or from Lua code by loading the underlying <tt>jit.p</tt> module. |
|||
</p> |
|||
<p> |
|||
To cut to the chase — run this to get a CPU usage profile by |
|||
function name: |
|||
</p> |
|||
<pre class="code"> |
|||
luajit -jp myapp.lua |
|||
</pre> |
|||
<p> |
|||
It's <em>not</em> a stated goal of the bundled profiler to add every |
|||
possible option or to cater for special profiling needs. The low-level |
|||
profiler APIs are documented below. They may be used by third-party |
|||
authors to implement advanced functionality, e.g. IDE integration or |
|||
graphical profilers. |
|||
</p> |
|||
<p> |
|||
Note: Sampling works for both interpreted and JIT-compiled code. The |
|||
results for JIT-compiled code may sometimes be surprising. LuaJIT |
|||
heavily optimizes and inlines Lua code — there's no simple |
|||
one-to-one correspondence between source code lines and the sampled |
|||
machine code. |
|||
</p> |
|||
|
|||
<h3 id="j_p"><tt>-jp=[options[,output]]</tt></h3> |
|||
<p> |
|||
The <tt>-jp</tt> command line option starts the high-level profiler. |
|||
When the application run by the command line terminates, the profiler |
|||
stops and writes the results to <tt>stdout</tt> or to the specified |
|||
<tt>output</tt> file. |
|||
</p> |
|||
<p> |
|||
The <tt>options</tt> argument specifies how the profiling is to be |
|||
performed: |
|||
</p> |
|||
<ul> |
|||
<li><tt>f</tt> — Stack dump: function name, otherwise module:line. |
|||
This is the default mode.</li> |
|||
<li><tt>F</tt> — Stack dump: ditto, but dump module:name.</li> |
|||
<li><tt>l</tt> — Stack dump: module:line.</li> |
|||
<li><tt><number></tt> — stack dump depth (callee ← |
|||
caller). Default: 1.</li> |
|||
<li><tt>-<number></tt> — Inverse stack dump depth (caller |
|||
→ callee).</li> |
|||
<li><tt>s</tt> — Split stack dump after first stack level. Implies |
|||
depth ≥ 2 or depth ≤ -2.</li> |
|||
<li><tt>p</tt> — Show full path for module names.</li> |
|||
<li><tt>v</tt> — Show VM states.</li> |
|||
<li><tt>z</tt> — Show <a href="#jit_zone">zones</a>.</li> |
|||
<li><tt>r</tt> — Show raw sample counts. Default: show percentages.</li> |
|||
<li><tt>a</tt> — Annotate excerpts from source code files.</li> |
|||
<li><tt>A</tt> — Annotate complete source code files.</li> |
|||
<li><tt>G</tt> — Produce raw output suitable for graphical tools.</li> |
|||
<li><tt>m<number></tt> — Minimum sample percentage to be shown. |
|||
Default: 3%.</li> |
|||
<li><tt>i<number></tt> — Sampling interval in milliseconds. |
|||
Default: 10ms.<br> |
|||
Note: The actual sampling precision is OS-dependent.</li> |
|||
</ul> |
|||
<p> |
|||
The default output for <tt>-jp</tt> is a list of the most CPU consuming |
|||
spots in the application. Increasing the stack dump depth with (say) |
|||
<tt>-jp=2</tt> may help to point out the main callers or callees of |
|||
hotspots. But sample aggregation is still flat per unique stack dump. |
|||
</p> |
|||
<p> |
|||
To get a two-level view (split view) of callers/callees, use |
|||
<tt>-jp=s</tt> or <tt>-jp=-s</tt>. The percentages shown for the second |
|||
level are relative to the first level. |
|||
</p> |
|||
<p> |
|||
To see how much time is spent in each line relative to a function, use |
|||
<tt>-jp=fl</tt>. |
|||
</p> |
|||
<p> |
|||
To see how much time is spent in different VM states or |
|||
<a href="#jit_zone">zones</a>, use <tt>-jp=v</tt> or <tt>-jp=z</tt>. |
|||
</p> |
|||
<p> |
|||
Combinations of <tt>v/z</tt> with <tt>f/F/l</tt> produce two-level |
|||
views, e.g. <tt>-jp=vf</tt> or <tt>-jp=fv</tt>. This shows the time |
|||
spent in a VM state or zone vs. hotspots. This can be used to answer |
|||
questions like "Which time consuming functions are only interpreted?" or |
|||
"What's the garbage collector overhead for a specific function?". |
|||
</p> |
|||
<p> |
|||
Multiple options can be combined — but not all combinations make |
|||
sense, see above. E.g. <tt>-jp=3si4m1</tt> samples three stack levels |
|||
deep in 4ms intervals and shows a split view of the CPU consuming |
|||
functions and their callers with a 1% threshold. |
|||
</p> |
|||
<p> |
|||
Source code annotations produced by <tt>-jp=a</tt> or <tt>-jp=A</tt> are |
|||
always flat and at the line level. Obviously, the source code files need |
|||
to be readable by the profiler script. |
|||
</p> |
|||
<p> |
|||
The high-level profiler can also be started and stopped from Lua code with: |
|||
</p> |
|||
<pre class="code"> |
|||
require("jit.p").start(options, output) |
|||
... |
|||
require("jit.p").stop() |
|||
</pre> |
|||
|
|||
<h3 id="jit_zone"><tt>jit.zone</tt> — Zones</h3> |
|||
<p> |
|||
Zones can be used to provide information about different parts of an |
|||
application to the high-level profiler. E.g. a game could make use of an |
|||
<tt>"AI"</tt> zone, a <tt>"PHYS"</tt> zone, etc. Zones are hierarchical, |
|||
organized as a stack. |
|||
</p> |
|||
<p> |
|||
The <tt>jit.zone</tt> module needs to be loaded explicitly: |
|||
</p> |
|||
<pre class="code"> |
|||
local zone = require("jit.zone") |
|||
</pre> |
|||
<ul> |
|||
<li><tt>zone("name")</tt> pushes a named zone to the zone stack.</li> |
|||
<li><tt>zone()</tt> pops the current zone from the zone stack and |
|||
returns its name.</li> |
|||
<li><tt>zone:get()</tt> returns the current zone name or <tt>nil</tt>.</li> |
|||
<li><tt>zone:flush()</tt> flushes the zone stack.</li> |
|||
</ul> |
|||
<p> |
|||
To show the time spent in each zone use <tt>-jp=z</tt>. To show the time |
|||
spent relative to hotspots use e.g. <tt>-jp=zf</tt> or <tt>-jp=fz</tt>. |
|||
</p> |
|||
|
|||
<h2 id="ll_lua_api">Low-level Lua API</h2> |
|||
<p> |
|||
The <tt>jit.profile</tt> module gives access to the low-level API of the |
|||
profiler from Lua code. This module needs to be loaded explicitly: |
|||
<pre class="code"> |
|||
local profile = require("jit.profile") |
|||
</pre> |
|||
<p> |
|||
This module can be used to implement your own higher-level profiler. |
|||
A typical profiling run starts the profiler, captures stack dumps in |
|||
the profiler callback, adds them to a hash table to aggregate the number |
|||
of samples, stops the profiler and then analyzes all of the captured |
|||
stack dumps. Other parameters can be sampled in the profiler callback, |
|||
too. But it's important not to spend too much time in the callback, |
|||
since this may skew the statistics. |
|||
</p> |
|||
|
|||
<h3 id="profile_start"><tt>profile.start(mode, cb)</tt> |
|||
— Start profiler</h3> |
|||
<p> |
|||
This function starts the profiler. The <tt>mode</tt> argument is a |
|||
string holding options: |
|||
</p> |
|||
<ul> |
|||
<li><tt>f</tt> — Profile with precision down to the function level.</li> |
|||
<li><tt>l</tt> — Profile with precision down to the line level.</li> |
|||
<li><tt>i<number></tt> — Sampling interval in milliseconds (default |
|||
10ms).</br> |
|||
Note: The actual sampling precision is OS-dependent. |
|||
</li> |
|||
</ul> |
|||
<p> |
|||
The <tt>cb</tt> argument is a callback function which is called with |
|||
three arguments: <tt>(thread, samples, vmstate)</tt>. The callback is |
|||
called on a separate coroutine, the <tt>thread</tt> argument is the |
|||
state that holds the stack to sample for profiling. Note: do |
|||
<em>not</em> modify the stack of that state or call functions on it. |
|||
</p> |
|||
<p> |
|||
<tt>samples</tt> gives the number of accumulated samples since the last |
|||
callback (usually 1). |
|||
</p> |
|||
<p> |
|||
<tt>vmstate</tt> holds the VM state at the time the profiling timer |
|||
triggered. This may or may not correspond to the state of the VM when |
|||
the profiling callback is called. The state is either <tt>'N'</tt> |
|||
native (compiled) code, <tt>'I'</tt> interpreted code, <tt>'C'</tt> |
|||
C code, <tt>'G'</tt> the garbage collector, or <tt>'J'</tt> the JIT |
|||
compiler. |
|||
</p> |
|||
|
|||
<h3 id="profile_stop"><tt>profile.stop()</tt> |
|||
— Stop profiler</h3> |
|||
<p> |
|||
This function stops the profiler. |
|||
</p> |
|||
|
|||
<h3 id="profile_dump"><tt>dump = profile.dumpstack([thread,] fmt, depth)</tt> |
|||
— Dump stack </h3> |
|||
<p> |
|||
This function allows taking stack dumps in an efficient manner. It |
|||
returns a string with a stack dump for the <tt>thread</tt> (coroutine), |
|||
formatted according to the <tt>fmt</tt> argument: |
|||
</p> |
|||
<ul> |
|||
<li><tt>p</tt> — Preserve the full path for module names. Otherwise |
|||
only the file name is used.</li> |
|||
<li><tt>f</tt> — Dump the function name if it can be derived. Otherwise |
|||
use module:line.</li> |
|||
<li><tt>F</tt> — Ditto, but dump module:name.</li> |
|||
<li><tt>l</tt> — Dump module:line.</li> |
|||
<li><tt>Z</tt> — Zap the following characters for the last dumped |
|||
frame.</li> |
|||
<li>All other characters are added verbatim to the output string.</li> |
|||
</ul> |
|||
<p> |
|||
The <tt>depth</tt> argument gives the number of frames to dump, starting |
|||
at the topmost frame of the thread. A negative number dumps the frames in |
|||
inverse order. |
|||
</p> |
|||
<p> |
|||
The first example prints a list of the current module names and line |
|||
numbers of up to 10 frames in separate lines. The second example prints |
|||
semicolon-separated function names for all frames (up to 100) in inverse |
|||
order: |
|||
</p> |
|||
<pre class="code"> |
|||
print(profile.dumpstack(thread, "l\n", 10)) |
|||
print(profile.dumpstack(thread, "lZ;", -100)) |
|||
</pre> |
|||
|
|||
<h2 id="ll_c_api">Low-level C API</h2> |
|||
<p> |
|||
The profiler can be controlled directly from C code, e.g. for |
|||
use by IDEs. The declarations are in <tt>"luajit.h"</tt> (see |
|||
<a href="ext_c_api.html">Lua/C API</a> extensions). |
|||
</p> |
|||
|
|||
<h3 id="luaJIT_profile_start"><tt>luaJIT_profile_start(L, mode, cb, data)</tt> |
|||
— Start profiler</h3> |
|||
<p> |
|||
This function starts the profiler. <a href="#profile_start">See |
|||
above</a> for a description of the <tt>mode</tt> argument. |
|||
</p> |
|||
<p> |
|||
The <tt>cb</tt> argument is a callback function with the following |
|||
declaration: |
|||
</p> |
|||
<pre class="code"> |
|||
typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, |
|||
int samples, int vmstate); |
|||
</pre> |
|||
<p> |
|||
<tt>data</tt> is available for use by the callback. <tt>L</tt> is the |
|||
state that holds the stack to sample for profiling. Note: do |
|||
<em>not</em> modify this stack or call functions on this stack — |
|||
use a separate coroutine for this purpose. <a href="#profile_start">See |
|||
above</a> for a description of <tt>samples</tt> and <tt>vmstate</tt>. |
|||
</p> |
|||
|
|||
<h3 id="luaJIT_profile_stop"><tt>luaJIT_profile_stop(L)</tt> |
|||
— Stop profiler</h3> |
|||
<p> |
|||
This function stops the profiler. |
|||
</p> |
|||
|
|||
<h3 id="luaJIT_profile_dumpstack"><tt>p = luaJIT_profile_dumpstack(L, fmt, depth, len)</tt> |
|||
— Dump stack </h3> |
|||
<p> |
|||
This function allows taking stack dumps in an efficient manner. |
|||
<a href="#profile_dump">See above</a> for a description of <tt>fmt</tt> |
|||
and <tt>depth</tt>. |
|||
</p> |
|||
<p> |
|||
This function returns a <tt>const char *</tt> pointing to a |
|||
private string buffer of the profiler. The <tt>int *len</tt> |
|||
argument returns the length of the output string. The buffer is |
|||
overwritten on the next call and deallocated when the profiler stops. |
|||
You either need to consume the content immediately or copy it for later |
|||
use. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,474 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Extensions</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
table.exc { |
|||
line-height: 1.2; |
|||
} |
|||
tr.exchead td { |
|||
font-weight: bold; |
|||
} |
|||
td.excplatform { |
|||
width: 48%; |
|||
} |
|||
td.exccompiler { |
|||
width: 29%; |
|||
} |
|||
td.excinterop { |
|||
width: 23%; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Extensions</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a class="current" href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
LuaJIT is fully upwards-compatible with Lua 5.1. It supports all |
|||
<a href="https://www.lua.org/manual/5.1/manual.html#5"><span class="ext">»</span> standard Lua |
|||
library functions</a> and the full set of |
|||
<a href="https://www.lua.org/manual/5.1/manual.html#3"><span class="ext">»</span> Lua/C API |
|||
functions</a>. |
|||
</p> |
|||
<p> |
|||
LuaJIT is also fully ABI-compatible to Lua 5.1 at the linker/dynamic |
|||
loader level. This means you can compile a C module against the |
|||
standard Lua headers and load the same shared library from either Lua |
|||
or LuaJIT. |
|||
</p> |
|||
<p> |
|||
LuaJIT extends the standard Lua VM with new functionality and adds |
|||
several extension modules. Please note this page is only about |
|||
<em>functional</em> enhancements and not about performance enhancements, |
|||
such as the optimized VM, the faster interpreter or the JIT compiler. |
|||
</p> |
|||
|
|||
<h2 id="modules">Extensions Modules</h2> |
|||
<p> |
|||
LuaJIT comes with several built-in extension modules: |
|||
</p> |
|||
|
|||
<h3 id="bit"><tt>bit.*</tt> — Bitwise operations</h3> |
|||
<p> |
|||
LuaJIT supports all bitwise operations as defined by |
|||
<a href="https://bitop.luajit.org"><span class="ext">»</span> Lua BitOp</a>: |
|||
</p> |
|||
<pre class="code"> |
|||
bit.tobit bit.tohex bit.bnot bit.band bit.bor bit.bxor |
|||
bit.lshift bit.rshift bit.arshift bit.rol bit.ror bit.bswap |
|||
</pre> |
|||
<p> |
|||
This module is a LuaJIT built-in — you don't need to download or |
|||
install Lua BitOp. The Lua BitOp site has full documentation for all |
|||
<a href="https://bitop.luajit.org/api.html"><span class="ext">»</span> Lua BitOp API functions</a>. |
|||
The FFI adds support for |
|||
<a href="ext_ffi_semantics.html#cdata_arith">64 bit bitwise operations</a>, |
|||
using the same API functions. |
|||
</p> |
|||
<p> |
|||
Please make sure to <tt>require</tt> the module before using any of |
|||
its functions: |
|||
</p> |
|||
<pre class="code"> |
|||
local bit = require("bit") |
|||
</pre> |
|||
<p> |
|||
An already installed Lua BitOp module is ignored by LuaJIT. |
|||
This way you can use bit operations from both Lua and LuaJIT on a |
|||
shared installation. |
|||
</p> |
|||
|
|||
<h3 id="ffi"><tt>ffi.*</tt> — FFI library</h3> |
|||
<p> |
|||
The <a href="ext_ffi.html">FFI library</a> allows calling external |
|||
C functions and the use of C data structures from pure Lua |
|||
code. |
|||
</p> |
|||
|
|||
<h3 id="jit"><tt>jit.*</tt> — JIT compiler control</h3> |
|||
<p> |
|||
The functions in this module |
|||
<a href="ext_jit.html">control the behavior of the JIT compiler engine</a>. |
|||
</p> |
|||
|
|||
<h3 id="c_api">C API extensions</h3> |
|||
<p> |
|||
LuaJIT adds some |
|||
<a href="ext_c_api.html">extra functions to the Lua/C API</a>. |
|||
</p> |
|||
|
|||
<h3 id="profiler">Profiler</h3> |
|||
<p> |
|||
LuaJIT has an <a href="ext_profiler.html">integrated profiler</a>. |
|||
</p> |
|||
|
|||
<h2 id="library">Enhanced Standard Library Functions</h2> |
|||
|
|||
<h3 id="xpcall"><tt>xpcall(f, err [,args...])</tt> passes arguments</h3> |
|||
<p> |
|||
Unlike the standard implementation in Lua 5.1, <tt>xpcall()</tt> |
|||
passes any arguments after the error function to the function |
|||
which is called in a protected context. |
|||
</p> |
|||
|
|||
<h3 id="load"><tt>loadfile()</tt> etc. handle UTF-8 source code</h3> |
|||
<p> |
|||
Non-ASCII characters are handled transparently by the Lua source code parser. |
|||
This allows the use of UTF-8 characters in identifiers and strings. |
|||
A UTF-8 BOM is skipped at the start of the source code. |
|||
</p> |
|||
|
|||
<h3 id="tostring"><tt>tostring()</tt> etc. canonicalize NaN and ±Inf</h3> |
|||
<p> |
|||
All number-to-string conversions consistently convert non-finite numbers |
|||
to the same strings on all platforms. NaN results in <tt>"nan"</tt>, |
|||
positive infinity results in <tt>"inf"</tt> and negative infinity results |
|||
in <tt>"-inf"</tt>. |
|||
</p> |
|||
|
|||
<h3 id="tonumber"><tt>tonumber()</tt> etc. use builtin string to number conversion</h3> |
|||
<p> |
|||
All string-to-number conversions consistently convert integer and |
|||
floating-point inputs in decimal, hexadecimal and binary on all platforms. |
|||
<tt>strtod()</tt> is <em>not</em> used anymore, which avoids numerous |
|||
problems with poor C library implementations. The builtin conversion |
|||
function provides full precision according to the IEEE-754 standard, it |
|||
works independently of the current locale and it supports hex floating-point |
|||
numbers (e.g. <tt>0x1.5p-3</tt>). |
|||
</p> |
|||
|
|||
<h3 id="string_dump"><tt>string.dump(f [,strip])</tt> generates portable bytecode</h3> |
|||
<p> |
|||
An extra argument has been added to <tt>string.dump()</tt>. If set to |
|||
<tt>true</tt>, 'stripped' bytecode without debug information is |
|||
generated. This speeds up later bytecode loading and reduces memory |
|||
usage. See also the |
|||
<a href="running.html#opt_b"><tt>-b</tt> command line option</a>. |
|||
</p> |
|||
<p> |
|||
The generated bytecode is portable and can be loaded on any architecture |
|||
that LuaJIT supports, independent of word size or endianess. However the |
|||
bytecode compatibility versions must match. Bytecode stays compatible |
|||
for dot releases (x.y.0 → x.y.1), but may change with major or |
|||
minor releases (2.0 → 2.1) or between any beta release. Foreign |
|||
bytecode (e.g. from Lua 5.1) is incompatible and cannot be loaded. |
|||
</p> |
|||
<p> |
|||
Note: <tt>LJ_GC64</tt> mode requires a different frame layout, which implies |
|||
a different, incompatible bytecode format for all 64 bit ports. This may be |
|||
rectified in the future. |
|||
</p> |
|||
|
|||
<h3 id="table_new"><tt>table.new(narray, nhash)</tt> allocates a pre-sized table</h3> |
|||
<p> |
|||
An extra library function <tt>table.new()</tt> can be made available via |
|||
<tt>require("table.new")</tt>. This creates a pre-sized table, just like |
|||
the C API equivalent <tt>lua_createtable()</tt>. This is useful for big |
|||
tables if the final table size is known and automatic table resizing is |
|||
too expensive. |
|||
</p> |
|||
|
|||
<h3 id="table_clear"><tt>table.clear(tab)</tt> clears a table</h3> |
|||
<p> |
|||
An extra library function <tt>table.clear()</tt> can be made available |
|||
via <tt>require("table.clear")</tt>. This clears all keys and values |
|||
from a table, but preserves the allocated array/hash sizes. This is |
|||
useful when a table, which is linked from multiple places, needs to be |
|||
cleared and/or when recycling a table for use by the same context. This |
|||
avoids managing backlinks, saves an allocation and the overhead of |
|||
incremental array/hash part growth. |
|||
</p> |
|||
<p> |
|||
Please note this function is meant for very specific situations. In most |
|||
cases it's better to replace the (usually single) link with a new table |
|||
and let the GC do its work. |
|||
</p> |
|||
|
|||
<h3 id="math_random">Enhanced PRNG for <tt>math.random()</tt></h3> |
|||
<p> |
|||
LuaJIT uses a Tausworthe PRNG with period 2^223 to implement |
|||
<tt>math.random()</tt> and <tt>math.randomseed()</tt>. The quality of |
|||
the PRNG results is much superior compared to the standard Lua |
|||
implementation which uses the platform-specific ANSI rand(). |
|||
</p> |
|||
<p> |
|||
The PRNG generates the same sequences from the same seeds on all |
|||
platforms and makes use of all bits in the seed argument. |
|||
<tt>math.random()</tt> without arguments generates 52 pseudo-random bits |
|||
for every call. The result is uniformly distributed between 0.0 and 1.0. |
|||
It's correctly scaled up and rounded for <tt>math.random(n [,m])</tt> to |
|||
preserve uniformity. |
|||
</p> |
|||
<p> |
|||
Important: Neither this nor any other PRNG based on the simplistic |
|||
<tt>math.random()</tt> API is suitable for cryptographic use. |
|||
</p> |
|||
|
|||
<h3 id="io"><tt>io.*</tt> functions handle 64 bit file offsets</h3> |
|||
<p> |
|||
The file I/O functions in the standard <tt>io.*</tt> library handle |
|||
64 bit file offsets. In particular this means it's possible |
|||
to open files larger than 2 Gigabytes and to reposition or obtain |
|||
the current file position for offsets beyond 2 GB |
|||
(<tt>fp:seek()</tt> method). |
|||
</p> |
|||
|
|||
<h3 id="debug_meta"><tt>debug.*</tt> functions identify metamethods</h3> |
|||
<p> |
|||
<tt>debug.getinfo()</tt> and <tt>lua_getinfo()</tt> also return information |
|||
about invoked metamethods. The <tt>namewhat</tt> field is set to |
|||
<tt>"metamethod"</tt> and the <tt>name</tt> field has the name of |
|||
the corresponding metamethod (e.g. <tt>"__index"</tt>). |
|||
</p> |
|||
|
|||
<h2 id="resumable">Fully Resumable VM</h2> |
|||
<p> |
|||
The LuaJIT VM is fully resumable. This means you can yield from a |
|||
coroutine even across contexts, where this would not possible with |
|||
the standard Lua 5.1 VM: e.g. you can yield across <tt>pcall()</tt> |
|||
and <tt>xpcall()</tt>, across iterators and across metamethods. |
|||
</p> |
|||
|
|||
<h2 id="lua52">Extensions from Lua 5.2</h2> |
|||
<p> |
|||
LuaJIT supports some language and library extensions from Lua 5.2. |
|||
Features that are unlikely to break existing code are unconditionally |
|||
enabled: |
|||
</p> |
|||
<ul> |
|||
<li><tt>goto</tt> and <tt>::labels::</tt>.</li> |
|||
<li>Hex escapes <tt>'\x3F'</tt> and <tt>'\*'</tt> escape in strings.</li> |
|||
<li><tt>load(string|reader [, chunkname [,mode [,env]]])</tt>.</li> |
|||
<li><tt>loadstring()</tt> is an alias for <tt>load()</tt>.</li> |
|||
<li><tt>loadfile(filename [,mode [,env]])</tt>.</li> |
|||
<li><tt>math.log(x [,base])</tt>.</li> |
|||
<li><tt>string.rep(s, n [,sep])</tt>.</li> |
|||
<li><tt>string.format()</tt>: <tt>%q</tt> reversible. |
|||
<tt>%s</tt> checks <tt>__tostring</tt>. |
|||
<tt>%a</tt> and <tt>"%A</tt> added.</li> |
|||
<li>String matching pattern <tt>%g</tt> added.</li> |
|||
<li><tt>io.read("*L")</tt>.</li> |
|||
<li><tt>io.lines()</tt> and <tt>file:lines()</tt> process |
|||
<tt>io.read()</tt> options.</li> |
|||
<li><tt>os.exit(status|true|false [,close])</tt>.</li> |
|||
<li><tt>package.searchpath(name, path [, sep [, rep]])</tt>.</li> |
|||
<li><tt>package.loadlib(name, "*")</tt>.</li> |
|||
<li><tt>debug.getinfo()</tt> returns <tt>nparams</tt> and <tt>isvararg</tt> |
|||
for option <tt>"u"</tt>.</li> |
|||
<li><tt>debug.getlocal()</tt> accepts function instead of level.</li> |
|||
<li><tt>debug.getlocal()</tt> and <tt>debug.setlocal()</tt> accept negative |
|||
indexes for varargs.</li> |
|||
<li><tt>debug.getupvalue()</tt> and <tt>debug.setupvalue()</tt> handle |
|||
C functions.</li> |
|||
<li><tt>debug.upvalueid()</tt> and <tt>debug.upvaluejoin()</tt>.</li> |
|||
<li>Lua/C API extensions: |
|||
<tt>lua_version()</tt> |
|||
<tt>lua_upvalueid()</tt> |
|||
<tt>lua_upvaluejoin()</tt> |
|||
<tt>lua_loadx()</tt> |
|||
<tt>lua_copy()</tt> |
|||
<tt>lua_tonumberx()</tt> |
|||
<tt>lua_tointegerx()</tt> |
|||
<tt>luaL_fileresult()</tt> |
|||
<tt>luaL_execresult()</tt> |
|||
<tt>luaL_loadfilex()</tt> |
|||
<tt>luaL_loadbufferx()</tt> |
|||
<tt>luaL_traceback()</tt> |
|||
<tt>luaL_setfuncs()</tt> |
|||
<tt>luaL_pushmodule()</tt> |
|||
<tt>luaL_newlibtable()</tt> |
|||
<tt>luaL_newlib()</tt> |
|||
<tt>luaL_testudata()</tt> |
|||
<tt>luaL_setmetatable()</tt> |
|||
</li> |
|||
<li>Command line option <tt>-E</tt>.</li> |
|||
<li>Command line checks <tt>__tostring</tt> for errors.</li> |
|||
</ul> |
|||
<p> |
|||
Other features are only enabled, if LuaJIT is built with |
|||
<tt>-DLUAJIT_ENABLE_LUA52COMPAT</tt>: |
|||
</p> |
|||
<ul> |
|||
<li><tt>goto</tt> is a keyword and not a valid variable name anymore.</li> |
|||
<li><tt>break</tt> can be placed anywhere. Empty statements (<tt>;;</tt>) |
|||
are allowed.</li> |
|||
<li><tt>__lt</tt>, <tt>__le</tt> are invoked for mixed types.</li> |
|||
<li><tt>__len</tt> for tables. <tt>rawlen()</tt> library function.</li> |
|||
<li><tt>pairs()</tt> and <tt>ipairs()</tt> check for <tt>__pairs</tt> and |
|||
<tt>__ipairs</tt>.</li> |
|||
<li><tt>coroutine.running()</tt> returns two results.</li> |
|||
<li><tt>table.pack()</tt> and <tt>table.unpack()</tt> |
|||
(same as <tt>unpack()</tt>).</li> |
|||
<li><tt>io.write()</tt> and <tt>file:write()</tt> return file handle |
|||
instead of <tt>true</tt>.</li> |
|||
<li><tt>os.execute()</tt> and <tt>pipe:close()</tt> return detailed |
|||
exit status.</li> |
|||
<li><tt>debug.setmetatable()</tt> returns object.</li> |
|||
<li><tt>debug.getuservalue()</tt> and <tt>debug.setuservalue()</tt>.</li> |
|||
<li>Remove <tt>math.mod()</tt>, <tt>string.gfind()</tt>.</li> |
|||
<li><tt>package.searchers</tt>.</li> |
|||
<li><tt>module()</tt> returns the module table.</li> |
|||
</ul> |
|||
<p> |
|||
Note: this provides only partial compatibility with Lua 5.2 at the |
|||
language and Lua library level. LuaJIT is API+ABI-compatible with |
|||
Lua 5.1, which prevents implementing features that would otherwise |
|||
break the Lua/C API and ABI (e.g. <tt>_ENV</tt>). |
|||
</p> |
|||
|
|||
<h2 id="lua53">Extensions from Lua 5.3</h2> |
|||
<p> |
|||
LuaJIT supports some extensions from Lua 5.3: |
|||
<ul> |
|||
<li>Unicode escape <tt>'\u{XX...}'</tt> embeds the UTF-8 encoding in string literals.</li> |
|||
<li>The argument table <tt>arg</tt> can be read (and modified) by <tt>LUA_INIT</tt> and <tt>-e</tt> chunks.</li> |
|||
<li><tt>io.read()</tt> and <tt>file:read()</tt> accept formats with or without a leading <tt>*</tt>.</li> |
|||
<li><tt>assert()</tt> accepts any type of error object.</li> |
|||
<li><tt>table.move(a1, f, e, t [,a2])</tt>.</li> |
|||
<li><tt>coroutine.isyieldable()</tt>.</li> |
|||
<li>Lua/C API extensions: |
|||
<tt>lua_isyieldable()</tt> |
|||
</li> |
|||
</ul> |
|||
|
|||
<h2 id="exceptions">C++ Exception Interoperability</h2> |
|||
<p> |
|||
LuaJIT has built-in support for interoperating with C++ exceptions. |
|||
The available range of features depends on the target platform and |
|||
the toolchain used to compile LuaJIT: |
|||
</p> |
|||
<table class="exc"> |
|||
<tr class="exchead"> |
|||
<td class="excplatform">Platform</td> |
|||
<td class="exccompiler">Compiler</td> |
|||
<td class="excinterop">Interoperability</td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="excplatform">External frame unwinding</td> |
|||
<td class="exccompiler">GCC, Clang, MSVC</td> |
|||
<td class="excinterop"><b style="color: #00a000;">Full</b></td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="excplatform">Internal frame unwinding + DWARF2</td> |
|||
<td class="exccompiler">GCC, Clang</td> |
|||
<td class="excinterop"><b style="color: #c06000;">Limited</b></td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="excplatform">Windows 64 bit</td> |
|||
<td class="exccompiler">non-MSVC</td> |
|||
<td class="excinterop"><b style="color: #c06000;">Limited</b></td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="excplatform">Other platforms</td> |
|||
<td class="exccompiler">Other compilers</td> |
|||
<td class="excinterop"><b style="color: #a00000;">No</b></td> |
|||
</tr> |
|||
</table> |
|||
<p> |
|||
<b style="color: #00a000;">Full interoperability</b> means: |
|||
</p> |
|||
<ul> |
|||
<li>C++ exceptions can be caught on the Lua side with <tt>pcall()</tt>, |
|||
<tt>lua_pcall()</tt> etc.</li> |
|||
<li>C++ exceptions will be converted to the generic Lua error |
|||
<tt>"C++ exception"</tt>, unless you use the |
|||
<a href="ext_c_api.html#mode_wrapcfunc">C call wrapper</a> feature.</li> |
|||
<li>It's safe to throw C++ exceptions across non-protected Lua frames |
|||
on the C stack. The contents of the C++ exception object |
|||
pass through unmodified.</li> |
|||
<li>Lua errors can be caught on the C++ side with <tt>catch(...)</tt>. |
|||
The corresponding Lua error message can be retrieved from the Lua stack.<br> |
|||
For MSVC for Windows 64 bit this requires compilation of your C++ code |
|||
with <tt>/EHa</tt>.</li> |
|||
<li>Throwing Lua errors across C++ frames is safe. C++ destructors |
|||
will be called.</li> |
|||
</ul> |
|||
<p> |
|||
<b style="color: #c06000;">Limited interoperability</b> means: |
|||
</p> |
|||
<ul> |
|||
<li>C++ exceptions can be caught on the Lua side with <tt>pcall()</tt>, |
|||
<tt>lua_pcall()</tt> etc.</li> |
|||
<li>C++ exceptions will be converted to the generic Lua error |
|||
<tt>"C++ exception"</tt>, unless you use the |
|||
<a href="ext_c_api.html#mode_wrapcfunc">C call wrapper</a> feature.</li> |
|||
<li>C++ exceptions will be caught by non-protected Lua frames and |
|||
are rethrown as a generic Lua error. The C++ exception object will |
|||
be destroyed.</li> |
|||
<li>Lua errors <b>cannot</b> be caught on the C++ side.</li> |
|||
<li>Throwing Lua errors across C++ frames will <b>not</b> call |
|||
C++ destructors.</li> |
|||
</ul> |
|||
|
|||
<p> |
|||
<b style="color: #a00000;">No interoperability</b> means: |
|||
</p> |
|||
<ul> |
|||
<li>It's <b>not</b> safe to throw C++ exceptions across Lua frames.</li> |
|||
<li>C++ exceptions <b>cannot</b> be caught on the Lua side.</li> |
|||
<li>Lua errors <b>cannot</b> be caught on the C++ side.</li> |
|||
<li>Throwing Lua errors across C++ frames will <b>not</b> call |
|||
C++ destructors.</li> |
|||
</ul> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,199 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Frequently Asked Questions (FAQ)</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
dd { margin-left: 1.5em; } |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Frequently Asked Questions (FAQ)</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a class="current" href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<dl id="info"> |
|||
<dt>Q: Where can I learn more about LuaJIT and Lua?</dt> |
|||
<dd> |
|||
<ul style="padding: 0;"> |
|||
<li>The <a href="https://luajit.org/list.html"><span class="ext">»</span> LuaJIT mailing list</a> focuses on topics |
|||
related to LuaJIT.</li> |
|||
<li>The <a href="http://wiki.luajit.org/"><span class="ext">»</span> LuaJIT wiki</a> gathers community |
|||
resources about LuaJIT.</li> |
|||
<li>News about Lua itself can be found at the |
|||
<a href="https://www.lua.org/lua-l.html"><span class="ext">»</span> Lua mailing list</a>. |
|||
The mailing list archives are worth checking out for older postings |
|||
about LuaJIT.</li> |
|||
<li>The <a href="https://lua.org"><span class="ext">»</span> main Lua.org site</a> has complete |
|||
<a href="https://www.lua.org/docs.html"><span class="ext">»</span> documentation</a> of the language |
|||
and links to books and papers about Lua.</li> |
|||
<li>The community-managed <a href="http://lua-users.org/wiki/"><span class="ext">»</span> Lua Wiki</a> |
|||
has information about diverse topics.</li> |
|||
</ul></dd> |
|||
</dl> |
|||
|
|||
<dl id="tech"> |
|||
<dt>Q: Where can I learn more about the compiler technology used by LuaJIT?</dt> |
|||
<dd> |
|||
Please use the following Google Scholar searches to find relevant papers:<br> |
|||
Search for: <a href="https://scholar.google.com/scholar?q=Trace+Compiler"><span class="ext">»</span> Trace Compiler</a><br> |
|||
Search for: <a href="https://scholar.google.com/scholar?q=JIT+Compiler"><span class="ext">»</span> JIT Compiler</a><br> |
|||
Search for: <a href="https://scholar.google.com/scholar?q=Dynamic+Language+Optimizations"><span class="ext">»</span> Dynamic Language Optimizations</a><br> |
|||
Search for: <a href="https://scholar.google.com/scholar?q=SSA+Form"><span class="ext">»</span> SSA Form</a><br> |
|||
Search for: <a href="https://scholar.google.com/scholar?q=Linear+Scan+Register+Allocation"><span class="ext">»</span> Linear Scan Register Allocation</a><br> |
|||
Here is a list of the <a href="http://lua-users.org/lists/lua-l/2009-11/msg00089.html"><span class="ext">»</span> innovative features in LuaJIT</a>.<br> |
|||
And, you know, reading the source is of course the only way to enlightenment. |
|||
</dd> |
|||
</dl> |
|||
|
|||
<dl id="arg"> |
|||
<dt>Q: Why do I get this error: "attempt to index global 'arg' (a nil value)"?<br> |
|||
Q: My vararg functions fail after switching to LuaJIT!</dt> |
|||
<dd>LuaJIT is compatible to the Lua 5.1 language standard. It doesn't |
|||
support the implicit <tt>arg</tt> parameter for old-style vararg |
|||
functions from Lua 5.0.<br>Please convert your code to the |
|||
<a href="https://www.lua.org/manual/5.1/manual.html#2.5.9"><span class="ext">»</span> Lua 5.1 |
|||
vararg syntax</a>.</dd> |
|||
</dl> |
|||
|
|||
<dl id="x87"> |
|||
<dt>Q: Why do I get this error: "bad FPU precision"?<br> |
|||
<dt>Q: I get weird behavior after initializing Direct3D.<br> |
|||
<dt>Q: Some FPU operations crash after I load a Delphi DLL.<br> |
|||
</dt> |
|||
<dd> |
|||
|
|||
DirectX/Direct3D (up to version 9) sets the x87 FPU to single-precision |
|||
mode by default. This violates the Windows ABI and interferes with the |
|||
operation of many programs — LuaJIT is affected, too. Please make |
|||
sure you always use the <tt>D3DCREATE_FPU_PRESERVE</tt> flag when |
|||
initializing Direct3D.<br> |
|||
|
|||
Direct3D version 10 or higher do not show this behavior anymore. |
|||
Consider testing your application with older versions, too.<br> |
|||
|
|||
Similarly, the Borland/Delphi runtime modifies the FPU control word and |
|||
enables FP exceptions. Of course this violates the Windows ABI, too. |
|||
Please check the Delphi docs for the Set8087CW method.</dd> |
|||
</dl> |
|||
|
|||
<dl id="ctrlc"> |
|||
<dt>Q: Sometimes Ctrl-C fails to stop my Lua program. Why?</dt> |
|||
<dd>The interrupt signal handler sets a Lua debug hook. But this is |
|||
ignored by compiled code. If your program is running in a tight loop |
|||
and never falls back to the interpreter, the debug hook never runs and |
|||
can't throw the "interrupted!" error.<br> |
|||
You have to press Ctrl-C twice to get stop your program. That's similar |
|||
to when it's stuck running inside a C function under the Lua interpreter.</dd> |
|||
</dl> |
|||
|
|||
<dl id="order"> |
|||
<dt>Q: Table iteration with <tt>pairs()</tt> does not result in the same order?</dt> |
|||
<dd>The order of table iteration is explicitly <b>undefined</b> by |
|||
the Lua language standard.<br> |
|||
Different Lua implementations or versions may use different orders for |
|||
otherwise identical tables. Different ways of constructing a table may |
|||
result in different orders, too.<br> |
|||
Due to improved VM security, LuaJIT 2.1 may even use a different order |
|||
on separate VM invocations or when string keys are newly interned.<br><br> |
|||
If your program relies on a deterministic order, it has a bug. Rewrite it, |
|||
so it doesn't rely on the key order. Or sort the table keys, if you must.</dd> |
|||
</dl> |
|||
|
|||
<dl id="sandbox"> |
|||
<dt>Q: Can Lua code be safely sandboxed?</dt> |
|||
<dd> |
|||
Maybe for an extremly restricted subset of Lua and if you relentlessly |
|||
scrutinize every single interface function you offer to the untrusted code.<br> |
|||
|
|||
Although Lua provides some sandboxing functionality (<tt>setfenv()</tt>, hooks), |
|||
it's very hard to get this right even for the Lua core libraries. Of course, |
|||
you'll need to inspect any extension library, too. And there are libraries |
|||
that are inherently unsafe, e.g. the <a href="ext_ffi.html">FFI library</a>.<br> |
|||
|
|||
More reading material at the <a href="http://lua-users.org/wiki/SandBoxes"><span class="ext">»</span> Lua Wiki</a> and <a href="https://en.wikipedia.org/wiki/Sandbox_(computer_security)"><span class="ext">»</span> Wikipedia</a>.<br><br> |
|||
|
|||
Relatedly, <b>loading untrusted bytecode is not safe!</b><br> |
|||
|
|||
It's trivial to crash the Lua or LuaJIT VM with maliciously crafted bytecode. |
|||
This is well known and there's no bytecode verification on purpose, so please |
|||
don't report a bug about it. Check the <tt>mode</tt> parameter for the |
|||
<tt>load*()</tt> functions to disable loading of bytecode.<br><br> |
|||
|
|||
<b>In general, the only promising approach is to sandbox Lua code at the |
|||
process level and not the VM level.</b> |
|||
</dd> |
|||
</dl> |
|||
|
|||
<dl id="arch"> |
|||
<dt>Q: Lua runs everywhere. Why doesn't LuaJIT support my CPU?</dt> |
|||
<dd>Because it's a compiler — it needs to generate native |
|||
machine code. This means the code generator must be ported to each |
|||
architecture. And the fast interpreter is written in assembler and |
|||
must be ported, too. This is quite an undertaking.<br> |
|||
The <a href="install.html">install documentation</a> shows the supported |
|||
architectures.<br> |
|||
Other architectures may follow based on sufficient user demand and |
|||
market-relevance of the architecture. Sponsoring is required to develop |
|||
the port itself, to integrate it and to continuously maintain it in the |
|||
actively developed branches.</dd> |
|||
</dl> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
After Width: | Height: | Size: 1.3 KiB |
@ -0,0 +1,770 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Installation</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
table.compat { |
|||
line-height: 1.2; |
|||
font-size: 80%; |
|||
} |
|||
table.compat td { |
|||
border: 1px solid #bfcfff; |
|||
height: 1.5em; |
|||
} |
|||
table.compat tr.compathead td { |
|||
font-weight: bold; |
|||
border-bottom: 2px solid #bfcfff; |
|||
} |
|||
td.compatname, td.compatver { |
|||
width: 10%; |
|||
} |
|||
td.compatbits { |
|||
width: 5%; |
|||
} |
|||
td.compatx { |
|||
width: 21%; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Installation</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a class="current" href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
LuaJIT is only distributed as a source package. This page explains |
|||
how to build and install LuaJIT with different operating systems |
|||
and C compilers. |
|||
</p> |
|||
<p> |
|||
For the impatient (on POSIX systems): |
|||
</p> |
|||
<pre class="code"> |
|||
make && sudo make install |
|||
</pre> |
|||
|
|||
<h2 id="req">Requirements</h2> |
|||
<h3 id="systems">Systems</h3> |
|||
<p> |
|||
LuaJIT currently builds out-of-the box on most systems: |
|||
</p> |
|||
<table class="compat"> |
|||
<tr class="compathead"> |
|||
<td class="compatname">OS</td> |
|||
<td class="compatver">Min. Version</td> |
|||
<td class="compatx">Requirements</td> |
|||
<td class="compatx">LuaJIT Versions</td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="compatname"><a href="#windows">Windows</a></td> |
|||
<td class="compatver">7</td> |
|||
<td class="compatx">x86 or x64, ARM64: TBA</td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname"><a href="#posix">Linux</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname"><a href="#posix">*BSD</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname"><a href="#posix">macOS (OSX)</a></td> |
|||
<td class="compatver">10.4</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">v2.1 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname"><a href="#posix">POSIX</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">mmap, dlopen</td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even separate"> |
|||
<td class="compatname"><a href="#android">Android</a></td> |
|||
<td class="compatver">4.0</td> |
|||
<td class="compatx">Recent Android NDK</td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname"><a href="#ios">iOS</a></td> |
|||
<td class="compatver">3.0</td> |
|||
<td class="compatx">Xcode iOS SDK</td> |
|||
<td class="compatx">v2.1 –</td> |
|||
</tr> |
|||
<tr class="even separate"> |
|||
<td class="compatname"><a href="#consoles">PS3</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">PS3 SDK</td> |
|||
<td class="compatx">v2.0 – v2.1 EOL</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname"><a href="#consoles">PS4</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">PS4 SDK (ORBIS)</td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname"><a href="#consoles">PS5</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">PS5 SDK (PROSPERO)</td> |
|||
<td class="compatx">v2.1 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname"><a href="#consoles">PS Vita</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">PS Vita SDK (PSP2)</td> |
|||
<td class="compatx">v2.0 – v2.1 EOL</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname"><a href="#consoles">Xbox 360</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">Xbox 360 SDK (XEDK)</td> |
|||
<td class="compatx">v2.0 – v2.1 EOL</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname"><a href="#consoles">Xbox One</a></td> |
|||
<td class="compatver"> </td> |
|||
<td class="compatx">Xbox One SDK (DURANGO)</td> |
|||
<td class="compatx">v2.1 –</td> |
|||
</tr> |
|||
</table> |
|||
<p> |
|||
The codebase has compatibility defines for some more systems, but |
|||
without official support. |
|||
</p> |
|||
<h3 id="toolchains">Toolchains</h3> |
|||
<p> |
|||
Building LuaJIT requires a recent toolchain based on GCC, Clang/LLVM or |
|||
MSVC++. |
|||
</p> |
|||
<p> |
|||
The Makefile-based build system requires GNU Make and supports |
|||
cross-builds. Batch files are provided for MSVC++ builds and console |
|||
cross-builds. |
|||
</p> |
|||
<h3 id="architectures">CPU Architectures</h3> |
|||
<table class="compat"> |
|||
<tr class="compathead"> |
|||
<td class="compatname">CPU</td> |
|||
<td class="compatbits">Bits</td> |
|||
<td class="compatx">Requirements</td> |
|||
<td class="compatx">Variants</td> |
|||
<td class="compatx">LuaJIT Versions</td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="compatname">x86</td> |
|||
<td class="compatbits">32</td> |
|||
<td class="compatx">v2.1+: SSE2</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname">x64</td> |
|||
<td class="compatbits">64</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname">ARM</td> |
|||
<td class="compatbits">32</td> |
|||
<td class="compatx">ARMv5+, ARM9E+</td> |
|||
<td class="compatx">hard-fp + soft-fp</td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname">ARM64</td> |
|||
<td class="compatbits">64</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">ARM64le + ARM64be</td> |
|||
<td class="compatx">v2.1 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname">PPC32</td> |
|||
<td class="compatbits">32</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">hard-fp + soft-fp</td> |
|||
<td class="compatx">v2.0 – v2.1 EOL</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname">PPC/e500</td> |
|||
<td class="compatbits">32</td> |
|||
<td class="compatx">e500v2</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">v2.0 EOL</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname">MIPS32</td> |
|||
<td class="compatbits">32</td> |
|||
<td class="compatx">MIPS32r1 – r5</td> |
|||
<td class="compatx">hard-fp + soft-fp</td> |
|||
<td class="compatx">v2.0 –</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname">MIPS64</td> |
|||
<td class="compatbits">64</td> |
|||
<td class="compatx">MIPS64r1 – r5</td> |
|||
<td class="compatx">hard-fp + soft-fp</td> |
|||
<td class="compatx">v2.1 –</td> |
|||
</tr> |
|||
<tr class="odd"> |
|||
<td class="compatname">MIPS64</td> |
|||
<td class="compatbits">64</td> |
|||
<td class="compatx">MIPS64r6</td> |
|||
<td class="compatx">hard-fp + soft-fp</td> |
|||
<td class="compatx">v2.1 EOL</td> |
|||
</tr> |
|||
<tr class="even"> |
|||
<td class="compatname">RISC-V</td> |
|||
<td class="compatbits">64</td> |
|||
<td class="compatx">RVA22+</td> |
|||
<td class="compatx"> </td> |
|||
<td class="compatx">TBA</td> |
|||
</tr> |
|||
</table> |
|||
<p> |
|||
There are no plans to add historic architectures or to continue support |
|||
for end-of-life (EOL) architectures, for which no new CPUs are commonly |
|||
available anymore. Likewise, there are no plans to support marginal |
|||
and/or de-facto-dead architectures. |
|||
</p> |
|||
|
|||
<h2>Configuring LuaJIT</h2> |
|||
<p> |
|||
The standard configuration should work fine for most installations. |
|||
Usually there is no need to tweak the settings. The following files |
|||
hold all user-configurable settings: |
|||
</p> |
|||
<ul> |
|||
<li><tt>src/luaconf.h</tt> sets some configuration variables.</li> |
|||
<li><tt>Makefile</tt> has settings for <b>installing</b> LuaJIT (POSIX |
|||
only).</li> |
|||
<li><tt>src/Makefile</tt> has settings for <b>compiling</b> LuaJIT |
|||
under POSIX, MinGW or Cygwin.</li> |
|||
<li><tt>src/msvcbuild.bat</tt> has settings for compiling LuaJIT with |
|||
MSVC (Visual Studio).</li> |
|||
</ul> |
|||
<p> |
|||
Please read the instructions given in these files, before changing |
|||
any settings. |
|||
</p> |
|||
<p> |
|||
All LuaJIT 64 bit ports use 64 bit GC objects by default (<tt>LJ_GC64</tt>). |
|||
For x64, you can select the old 32-on-64 bit mode by adding |
|||
<tt>XCFLAGS=-DLUAJIT_DISABLE_GC64</tt> to the make command. |
|||
Please check the note about the |
|||
<a href="extensions.html#string_dump">bytecode format</a> differences, too. |
|||
</p> |
|||
|
|||
<h2 id="posix">POSIX Systems (Linux, macOS, *BSD etc.)</h2> |
|||
<h3>Prerequisites</h3> |
|||
<p> |
|||
Depending on your distribution, you may need to install a package for |
|||
GCC, the development headers and/or a complete SDK. E.g. on a current |
|||
Debian/Ubuntu, install <tt>libc6-dev</tt> with the package manager. |
|||
</p> |
|||
<p> |
|||
The recommended way to fetch the latest version is to do a pull from |
|||
the git repository. |
|||
</p> |
|||
<p> |
|||
Alternatively download the latest source package of LuaJIT (pick the .tar.gz). |
|||
Move it to a directory of your choice, open a terminal window and change |
|||
to this directory. Now unpack the archive and change to the newly created |
|||
directory (replace XX.YY.ZZ with the version you downloaded): |
|||
</p> |
|||
<pre class="code"> |
|||
tar zxf LuaJIT-XX.YY.ZZ.tar.gz |
|||
cd LuaJIT-XX.YY.ZZ |
|||
</pre> |
|||
<h3>Building LuaJIT</h3> |
|||
<p> |
|||
The supplied Makefiles try to auto-detect the settings needed for your |
|||
operating system and your compiler. They need to be run with GNU Make, |
|||
which is probably the default on your system, anyway. Simply run: |
|||
</p> |
|||
<pre class="code"> |
|||
make |
|||
</pre> |
|||
<p> |
|||
This always builds a native binary, depending on the host OS |
|||
you're running this command on. Check the section on |
|||
<a href="#cross">cross-compilation</a> for more options. |
|||
</p> |
|||
<p> |
|||
By default, modules are only searched under the prefix <tt>/usr/local</tt>. |
|||
You can add an extra prefix to the search paths by appending the |
|||
<tt>PREFIX</tt> option, e.g.: |
|||
</p> |
|||
<pre class="code"> |
|||
make PREFIX=/home/myself/lj2 |
|||
</pre> |
|||
<p> |
|||
Note for macOS: you <b>must</b> set the <tt>MACOSX_DEPLOYMENT_TARGET</tt> |
|||
environment variable to a value supported by your toolchain: |
|||
</p> |
|||
<pre class="code"> |
|||
MACOSX_DEPLOYMENT_TARGET=XX.YY make |
|||
</pre> |
|||
<h3>Installing LuaJIT</h3> |
|||
<p> |
|||
The top-level Makefile installs LuaJIT by default under |
|||
<tt>/usr/local</tt>, i.e. the executable ends up in |
|||
<tt>/usr/local/bin</tt> and so on. You need root privileges |
|||
to write to this path. So, assuming sudo is installed on your system, |
|||
run the following command and enter your sudo password: |
|||
</p> |
|||
<pre class="code"> |
|||
sudo make install |
|||
</pre> |
|||
<p> |
|||
Otherwise specify the directory prefix as an absolute path, e.g.: |
|||
</p> |
|||
<pre class="code"> |
|||
make install PREFIX=/home/myself/lj2 |
|||
</pre> |
|||
<p> |
|||
Obviously the prefixes given during build and installation need to be the same. |
|||
</p> |
|||
|
|||
<h2 id="windows">Windows Systems</h2> |
|||
<h3>Prerequisites</h3> |
|||
<p> |
|||
Either install one of the open source SDKs |
|||
(<a href="http://mingw.org/"><span class="ext">»</span> MinGW</a> or |
|||
<a href="https://www.cygwin.com/"><span class="ext">»</span> Cygwin</a>), which come with a modified |
|||
GCC plus the required development headers. |
|||
Or install Microsoft's Visual Studio (MSVC). |
|||
</p> |
|||
<p> |
|||
Next, pull from the git repository or download the source package and |
|||
unpack it using an archive manager (e.g. the Windows Explorer) to |
|||
a directory of your choice. |
|||
</p> |
|||
<h3>Building with MSVC</h3> |
|||
<p> |
|||
Open a "Visual Studio Command Prompt" (either x86 or x64), <tt>cd</tt> to the |
|||
directory where you've unpacked the sources and run these commands: |
|||
</p> |
|||
<pre class="code"> |
|||
cd src |
|||
msvcbuild |
|||
</pre> |
|||
<p> |
|||
Check the <tt>msvcbuild.bat</tt> file for more options. |
|||
Then follow the installation instructions below. |
|||
</p> |
|||
<h3>Building with MinGW or Cygwin</h3> |
|||
<p> |
|||
Open a command prompt window and make sure the MinGW or Cygwin programs |
|||
are in your path. Then <tt>cd</tt> to the directory of the git repository |
|||
or where you've unpacked the sources. Then run this command for MinGW: |
|||
</p> |
|||
<pre class="code"> |
|||
mingw32-make |
|||
</pre> |
|||
<p> |
|||
Or this command for Cygwin: |
|||
</p> |
|||
<pre class="code"> |
|||
make |
|||
</pre> |
|||
<p> |
|||
Then follow the installation instructions below. |
|||
</p> |
|||
<h3>Installing LuaJIT</h3> |
|||
<p> |
|||
Copy <tt>luajit.exe</tt> and <tt>lua51.dll</tt> (built in the <tt>src</tt> |
|||
directory) to a newly created directory (any location is ok). |
|||
Add <tt>lua</tt> and <tt>lua\jit</tt> directories below it and copy |
|||
all Lua files from the <tt>src\jit</tt> directory of the distribution |
|||
to the latter directory. |
|||
</p> |
|||
<p> |
|||
There are no hardcoded |
|||
absolute path names — all modules are loaded relative to the |
|||
directory where <tt>luajit.exe</tt> is installed |
|||
(see <tt>src/luaconf.h</tt>). |
|||
</p> |
|||
|
|||
<h2 id="cross">Cross-compiling LuaJIT</h2> |
|||
<p> |
|||
First, let's clear up some terminology: |
|||
</p> |
|||
<ul> |
|||
<li>Host: This is your development system, usually based on a x64 or x86 CPU.</li> |
|||
<li>Target: This is the target system you want LuaJIT to run on, e.g. Android/ARM.</li> |
|||
<li>Toolchain: This comprises a C compiler, linker, assembler and a matching C library.</li> |
|||
<li>Host (or system) toolchain: This is the toolchain used to build native binaries for your host system.</li> |
|||
<li>Cross-compile toolchain: This is the toolchain used to build binaries for the target system. They can only be run on the target system.</li> |
|||
</ul> |
|||
<p> |
|||
The GNU Makefile-based build system allows cross-compiling on any host |
|||
for any supported target: |
|||
</p> |
|||
<ul> |
|||
<li>Yes, you need a toolchain for both your host <em>and</em> your target!</li> |
|||
<li>Both host and target architectures must have the same pointer size.</li> |
|||
<li>E.g. if you want to cross-compile to a 32 bit target on a 64 bit host, you need to install the multilib development package (e.g. <tt>libc6-dev-i386</tt> on Debian/Ubuntu) and build a 32 bit host part (<tt>HOST_CC="gcc -m32"</tt>).</li> |
|||
<li>64 bit targets always require compilation on a 64 bit host.</li> |
|||
</ul> |
|||
<p> |
|||
You need to specify <tt>TARGET_SYS</tt> whenever the host OS and the |
|||
target OS differ, or you'll get assembler or linker errors: |
|||
</p> |
|||
<ul> |
|||
<li>E.g. if you're compiling on a Windows or macOS host for embedded Linux or Android, you need to add <tt>TARGET_SYS=Linux</tt> to the examples below.</li> |
|||
<li>For a minimal target OS, you may need to disable the built-in allocator in <tt>src/Makefile</tt> and use <tt>TARGET_SYS=Other</tt>.</li> |
|||
<li>Don't forget to specify the same <tt>TARGET_SYS</tt> for the install step, too.</li> |
|||
</ul> |
|||
<p> |
|||
Here are some examples where host and target have the same CPU: |
|||
</p> |
|||
<pre class="code"> |
|||
# Cross-compile to a 32 bit binary on a multilib x64 OS |
|||
make CC="gcc -m32" |
|||
|
|||
# Cross-compile on Debian/Ubuntu for Windows (mingw32 package) |
|||
make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows |
|||
</pre> |
|||
<p id="cross2"> |
|||
The <tt>CROSS</tt> prefix allows specifying a standard GNU cross-compile |
|||
toolchain (Binutils, GCC and a matching libc). The prefix may vary |
|||
depending on the <tt>--target</tt> the toolchain was built for (note the |
|||
<tt>CROSS</tt> prefix has a trailing <tt>"-"</tt>). The examples below |
|||
use the canonical toolchain triplets for Linux. |
|||
</p> |
|||
<p> |
|||
Since there's often no easy way to detect CPU features at runtime, it's |
|||
important to compile with the proper CPU or architecture settings: |
|||
</o> |
|||
<ul> |
|||
<li>The best way to get consistent results is to specify the correct settings when building the toolchain yourself.</li> |
|||
<li>For a pre-built, generic toolchain add <tt>-mcpu=...</tt> or <tt>-march=...</tt> and other necessary flags to <tt>TARGET_CFLAGS</tt>.</li> |
|||
<li>For ARM it's important to have the correct <tt>-mfloat-abi=...</tt> setting, too. Otherwise LuaJIT may not run at the full performance of your target CPU.</li> |
|||
<li>For MIPS it's important to select a supported ABI (o32 on MIPS32, n64 on MIPS64) and consistently compile your project either with hard-float or soft-float compiler settings.</li> |
|||
</ul> |
|||
<p> |
|||
Here are some examples for targets with a different CPU than the host: |
|||
</p> |
|||
<pre class="code"> |
|||
# ARM soft-float |
|||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabi- \ |
|||
TARGET_CFLAGS="-mfloat-abi=soft" |
|||
|
|||
# ARM soft-float ABI with VFP (example for Cortex-A9) |
|||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabi- \ |
|||
TARGET_CFLAGS="-mcpu=cortex-a9 -mfloat-abi=softfp" |
|||
|
|||
# ARM hard-float ABI with VFP (armhf, most modern toolchains) |
|||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabihf- |
|||
|
|||
# ARM64 |
|||
make CROSS=aarch64-linux- |
|||
|
|||
# PPC |
|||
make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu- |
|||
|
|||
# MIPS32 big-endian |
|||
make HOST_CC="gcc -m32" CROSS=mips-linux- |
|||
# MIPS32 little-endian |
|||
make HOST_CC="gcc -m32" CROSS=mipsel-linux- |
|||
|
|||
# MIPS64 big-endian |
|||
make CROSS=mips-linux- TARGET_CFLAGS="-mips64r2 -mabi=64" |
|||
# MIPS64 little-endian |
|||
make CROSS=mipsel-linux- TARGET_CFLAGS="-mips64r2 -mabi=64" |
|||
</pre> |
|||
<p> |
|||
You can cross-compile for <b id="android">Android</b> using the <a href="https://developer.android.com/ndk/"><span class="ext">»</span> Android NDK</a>. |
|||
Please adapt the environment variables to match the install locations and the |
|||
desired target platform. E.g. Android 4.1 corresponds to ABI level 16. |
|||
</p> |
|||
<pre class="code"> |
|||
# Android/ARM64, aarch64, Android 5.0+ (L) |
|||
NDKDIR=/opt/android/ndk |
|||
NDKBIN=$NDKDIR/toolchains/llvm/prebuilt/linux-x86_64/bin |
|||
NDKCROSS=$NDKBIN/aarch64-linux-android- |
|||
NDKCC=$NDKBIN/aarch64-linux-android21-clang |
|||
make CROSS=$NDKCROSS \ |
|||
STATIC_CC=$NDKCC DYNAMIC_CC="$NDKCC -fPIC" \ |
|||
TARGET_LD=$NDKCC TARGET_AR="$NDKBIN/llvm-ar rcus" \ |
|||
TARGET_STRIP=$NDKBIN/llvm-strip |
|||
|
|||
# Android/ARM, armeabi-v7a (ARMv7 VFP), Android 4.1+ (JB) |
|||
NDKDIR=/opt/android/ndk |
|||
NDKBIN=$NDKDIR/toolchains/llvm/prebuilt/linux-x86_64/bin |
|||
NDKCROSS=$NDKBIN/arm-linux-androideabi- |
|||
NDKCC=$NDKBIN/armv7a-linux-androideabi16-clang |
|||
make HOST_CC="gcc -m32" CROSS=$NDKCROSS \ |
|||
STATIC_CC=$NDKCC DYNAMIC_CC="$NDKCC -fPIC" \ |
|||
TARGET_LD=$NDKCC TARGET_AR="$NDKBIN/llvm-ar rcus" \ |
|||
TARGET_STRIP=$NDKBIN/llvm-strip |
|||
</pre> |
|||
<p> |
|||
You can cross-compile for <b id="ios">iOS 3.0+</b> (iPhone/iPad) using the <a href="https://developer.apple.com/ios/"><span class="ext">»</span> iOS SDK</a>: |
|||
</p> |
|||
<p style="font-size: 8pt;"> |
|||
Note: <b>the JIT compiler is disabled for iOS</b>, because regular iOS Apps |
|||
are not allowed to generate code at runtime. You'll only get the performance |
|||
of the LuaJIT interpreter on iOS. This is still faster than plain Lua, but |
|||
much slower than the JIT compiler. Please complain to Apple, not me. |
|||
Or use Android. :-p |
|||
</p> |
|||
<pre class="code"> |
|||
# iOS/ARM64 |
|||
ISDKP=$(xcrun --sdk iphoneos --show-sdk-path) |
|||
ICC=$(xcrun --sdk iphoneos --find clang) |
|||
ISDKF="-arch arm64 -isysroot $ISDKP" |
|||
make DEFAULT_CC=clang CROSS="$(dirname $ICC)/" \ |
|||
TARGET_FLAGS="$ISDKF" TARGET_SYS=iOS |
|||
</pre> |
|||
|
|||
<h3 id="consoles">Cross-compiling for consoles</h3> |
|||
<p> |
|||
Building LuaJIT for consoles requires both a supported host compiler |
|||
(x86 or x64) and a cross-compiler (to PPC or ARM) from the official |
|||
console SDK. |
|||
</p> |
|||
<p> |
|||
Due to restrictions on consoles, the JIT compiler is disabled and only |
|||
the fast interpreter is built. This is still faster than plain Lua, |
|||
but much slower than the JIT compiler. The FFI is disabled, too, since |
|||
it's not very useful in such an environment. |
|||
</p> |
|||
<p> |
|||
The following commands build a static library <tt>libluajit.a</tt>, |
|||
which can be linked against your game, just like the Lua library. |
|||
</p> |
|||
<p> |
|||
To cross-compile for <b id="ps3">PS3</b> from a Linux host (requires |
|||
32 bit GCC, i.e. multilib Linux/x64) or a Windows host (requires |
|||
32 bit MinGW), run this command: |
|||
</p> |
|||
<pre class="code"> |
|||
make HOST_CC="gcc -m32" CROSS=ppu-lv2- |
|||
</pre> |
|||
<p> |
|||
To cross-compile for <b id="ps4">PS4</b> from a Windows host, |
|||
open a "Visual Studio .NET Command Prompt" (64 bit host compiler), |
|||
<tt>cd</tt> to the directory where you've unpacked the sources and |
|||
run the following commands: |
|||
</p> |
|||
<pre class="code"> |
|||
cd src |
|||
ps4build |
|||
</pre> |
|||
<p> |
|||
To cross-compile for <b id="ps5">PS5</b> from a Windows host, |
|||
open a "Visual Studio .NET Command Prompt" (64 bit host compiler), |
|||
<tt>cd</tt> to the directory where you've unpacked the sources and |
|||
run the following commands: |
|||
</p> |
|||
<pre class="code"> |
|||
cd src |
|||
ps5build |
|||
</pre> |
|||
<p> |
|||
To cross-compile for <b id="psvita">PS Vita</b> from a Windows host, |
|||
open a "Visual Studio .NET Command Prompt" (32 bit host compiler), |
|||
<tt>cd</tt> to the directory where you've unpacked the sources and |
|||
run the following commands: |
|||
</p> |
|||
<pre class="code"> |
|||
cd src |
|||
psvitabuild |
|||
</pre> |
|||
<p> |
|||
To cross-compile for <b id="xbox360">Xbox 360</b> from a Windows host, |
|||
open a "Visual Studio .NET Command Prompt" (32 bit host compiler), |
|||
<tt>cd</tt> to the directory where you've unpacked the sources and run |
|||
the following commands: |
|||
</p> |
|||
<pre class="code"> |
|||
cd src |
|||
xedkbuild |
|||
</pre> |
|||
<p> |
|||
To cross-compile for <b id="xboxone">Xbox One</b> from a Windows host, |
|||
open a "Visual Studio .NET Command Prompt" (64 bit host compiler), |
|||
<tt>cd</tt> to the directory where you've unpacked the sources and run |
|||
the following commands: |
|||
</p> |
|||
<pre class="code"> |
|||
cd src |
|||
xb1build |
|||
</pre> |
|||
|
|||
<h2 id="embed">Embedding LuaJIT</h2> |
|||
<p> |
|||
LuaJIT is API-compatible with Lua 5.1. If you've already embedded Lua |
|||
into your application, you probably don't need to do anything to switch |
|||
to LuaJIT, except link with a different library: |
|||
</p> |
|||
<ul> |
|||
<li>It's strongly suggested to build LuaJIT separately using the supplied |
|||
build system. Please do <em>not</em> attempt to integrate the individual |
|||
source files into your build tree. You'll most likely get the internal build |
|||
dependencies wrong or mess up the compiler flags. Treat LuaJIT like any |
|||
other external library and link your application with either the dynamic |
|||
or static library, depending on your needs.</li> |
|||
<li>If you want to load C modules compiled for plain Lua |
|||
with <tt>require()</tt>, you need to make sure the public symbols |
|||
(e.g. <tt>lua_pushnumber</tt>) are exported, too: |
|||
<ul><li>On POSIX systems you can either link to the shared library |
|||
or link the static library into your application. In the latter case |
|||
you'll need to export all public symbols from your main executable |
|||
(e.g. <tt>-Wl,-E</tt> on Linux) and add the external dependencies |
|||
(e.g. <tt>-lm -ldl</tt> on Linux).</li> |
|||
<li>Since Windows symbols are bound to a specific DLL name, you need to |
|||
link to the <tt>lua51.dll</tt> created by the LuaJIT build (do not rename |
|||
the DLL). You may link LuaJIT statically on Windows only if you don't |
|||
intend to load Lua/C modules at runtime. |
|||
</li></ul> |
|||
</li> |
|||
</ul> |
|||
<p>Additional hints for initializing LuaJIT using the C API functions:</p> |
|||
<ul> |
|||
<li>Here's a |
|||
<a href="http://lua-users.org/wiki/SimpleLuaApiExample"><span class="ext">»</span> simple example</a> |
|||
for embedding Lua or LuaJIT into your application.</li> |
|||
<li>Make sure you use <tt>luaL_newstate</tt>. Avoid using |
|||
<tt>lua_newstate</tt>, since this uses the (slower) default memory |
|||
allocator from your system (no support for this on 64 bit architectures).</li> |
|||
<li>Make sure you use <tt>luaL_openlibs</tt> and not the old Lua 5.0 style |
|||
of calling <tt>luaopen_base</tt> etc. directly.</li> |
|||
<li>To change or extend the list of standard libraries to load, copy |
|||
<tt>src/lib_init.c</tt> to your project and modify it accordingly. |
|||
Make sure the <tt>jit</tt> library is loaded or the JIT compiler |
|||
will not be activated.</li> |
|||
<li>The <tt>bit.*</tt> module for bitwise operations |
|||
is already built-in. There's no need to statically link |
|||
<a href="https://bitop.luajit.org/"><span class="ext">»</span> Lua BitOp</a> to your application.</li> |
|||
</ul> |
|||
|
|||
<h2 id="distro">Hints for Distribution Maintainers</h2> |
|||
<p> |
|||
The LuaJIT build system has extra provisions for the needs of most |
|||
POSIX-based distributions. If you're a package maintainer for |
|||
a distribution, <em>please</em> make use of these features and |
|||
avoid patching, subverting, autotoolizing or messing up the build system |
|||
in unspeakable ways. |
|||
</p> |
|||
<p> |
|||
There should be absolutely no need to patch <tt>luaconf.h</tt> or any |
|||
of the Makefiles. And please do not hand-pick files for your packages — |
|||
simply use whatever <tt>make install</tt> creates. There's a reason |
|||
for all of the files <em>and</em> directories it creates. |
|||
</p> |
|||
<p> |
|||
The build system uses GNU make and auto-detects most settings based on |
|||
the host you're building it on. This should work fine for native builds, |
|||
even when sandboxed. You may need to pass some of the following flags to |
|||
<em>both</em> the <tt>make</tt> and the <tt>make install</tt> command lines |
|||
for a regular distribution build: |
|||
</p> |
|||
<ul> |
|||
<li><tt>PREFIX</tt> overrides the installation path and should usually |
|||
be set to <tt>/usr</tt>. Setting this also changes the module paths and |
|||
the paths needed to locate the shared library.</li> |
|||
<li><tt>DESTDIR</tt> is an absolute path which allows you to install |
|||
to a shadow tree instead of the root tree of the build system.</li> |
|||
<li><tt>MULTILIB</tt> sets the architecture-specific library path component |
|||
for multilib systems. The default is <tt>lib</tt>.</li> |
|||
<li>Have a look at the top-level <tt>Makefile</tt> and <tt>src/Makefile</tt> |
|||
for additional variables to tweak. The following variables <em>may</em> be |
|||
overridden, but it's <em>not</em> recommended, except for special needs |
|||
like cross-builds: |
|||
<tt>BUILDMODE, CC, HOST_CC, STATIC_CC, DYNAMIC_CC, CFLAGS, HOST_CFLAGS, |
|||
TARGET_CFLAGS, LDFLAGS, HOST_LDFLAGS, TARGET_LDFLAGS, TARGET_SHLDFLAGS, |
|||
TARGET_FLAGS, LIBS, HOST_LIBS, TARGET_LIBS, CROSS, HOST_SYS, TARGET_SYS |
|||
</tt></li> |
|||
</ul> |
|||
<p> |
|||
The build system has a special target for an amalgamated build, i.e. |
|||
<tt>make amalg</tt>. This compiles the LuaJIT core as one huge C file |
|||
and allows GCC to generate faster and shorter code. Alas, this requires |
|||
lots of memory during the build. This may be a problem for some users, |
|||
that's why it's not enabled by default. But it shouldn't be a problem for |
|||
most build farms. It's recommended that binary distributions use this |
|||
target for their LuaJIT builds. |
|||
</p> |
|||
<p> |
|||
The tl;dr version of the above: |
|||
</p> |
|||
<pre class="code"> |
|||
make amalg PREFIX=/usr && \ |
|||
make install PREFIX=/usr DESTDIR=/tmp/buildroot |
|||
</pre> |
|||
<p> |
|||
Finally, if you encounter any difficulties, please |
|||
<a href="contact.html">contact me</a> first, instead of releasing a broken |
|||
package onto unsuspecting users. Because they'll usually gonna complain |
|||
to me (the upstream) and not you (the package maintainer), anyway. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,232 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>LuaJIT</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<meta name="description" content="LuaJIT is a Just-In-Time (JIT) compiler for the Lua language."> |
|||
<style type="text/css"> |
|||
table.feature { |
|||
width: inherit; |
|||
line-height: 1.2; |
|||
margin: 0; |
|||
} |
|||
table.feature td { |
|||
width: 80px; |
|||
height: 40px; |
|||
vertical-align: middle; |
|||
text-align: center; |
|||
font-weight: bold; |
|||
border: 4px solid #e6ecff; |
|||
border-radius: 12px; |
|||
} |
|||
table.os td { |
|||
background: #7080d0; |
|||
background-image: linear-gradient(#4060c0 10%, #b0b0ff 95%); |
|||
background-image: -moz-linear-gradient(#4060c0 10%, #b0b0ff 95%); |
|||
background-image: -webkit-linear-gradient(#4060c0 10%, #b0b0ff 95%); |
|||
background-image: -o-linear-gradient(#4060c0 10%, #b0b0ff 95%); |
|||
background-image: -ms-linear-gradient(#4060c0 10%, #b0b0ff 95%); |
|||
} |
|||
table.os1 td { |
|||
color: #ffff80; |
|||
} |
|||
table.os2 td { |
|||
color: #ffa040; |
|||
} |
|||
table.os3 td { |
|||
color: #40ffff; |
|||
} |
|||
table.compiler td { |
|||
color: #2080ff; |
|||
background: #62bf41; |
|||
background-image: linear-gradient(#62bf41 10%, #b0ffb0 95%); |
|||
background-image: -moz-linear-gradient(#62bf41 10%, #b0ffb0 95%); |
|||
background-image: -webkit-linear-gradient(#62bf41 10%, #b0ffb0 95%); |
|||
background-image: -o-linear-gradient(#62bf41 10%, #b0ffb0 95%); |
|||
background-image: -ms-linear-gradient(#62bf41 10%, #b0ffb0 95%); |
|||
} |
|||
table.cpu td { |
|||
color: #ffff00; |
|||
background: #cf7251; |
|||
background-image: linear-gradient(#bf6241 10%, #ffb0b0 95%); |
|||
background-image: -moz-linear-gradient(#bf6241 10%, #ffb0b0 95%); |
|||
background-image: -webkit-linear-gradient(#bf6241 10%, #ffb0b0 95%); |
|||
background-image: -o-linear-gradient(#bf6241 10%, #ffb0b0 95%); |
|||
background-image: -ms-linear-gradient(#bf6241 10%, #ffb0b0 95%); |
|||
} |
|||
table.fcompat td { |
|||
color: #2060e0; |
|||
background: #61cfcf; |
|||
background-image: linear-gradient(#41bfbf 10%, #b0ffff 95%); |
|||
background-image: -moz-linear-gradient(#41bfbf 10%, #b0ffff 95%); |
|||
background-image: -webkit-linear-gradient(#41bfbf 10%, #b0ffff 95%); |
|||
background-image: -o-linear-gradient(#41bfbf 10%, #b0ffff 95%); |
|||
background-image: -ms-linear-gradient(#41bfbf 10%, #b0ffff 95%); |
|||
} |
|||
table.stats td { |
|||
color: #ffffff; |
|||
background: #a0a0a0; |
|||
background-image: linear-gradient(#808080 10%, #d0d0d0 95%); |
|||
background-image: -moz-linear-gradient(#808080 10%, #d0d0d0 95%); |
|||
background-image: -webkit-linear-gradient(#808080 10%, #d0d0d0 95%); |
|||
background-image: -o-linear-gradient(#808080 10%, #d0d0d0 95%); |
|||
background-image: -ms-linear-gradient(#808080 10%, #d0d0d0 95%); |
|||
} |
|||
table.stats td.speed { |
|||
color: #ff4020; |
|||
} |
|||
table.stats td.kb { |
|||
color: #ffff80; |
|||
background: #808080; |
|||
background-image: linear-gradient(#606060 10%, #c0c0c0 95%); |
|||
background-image: -moz-linear-gradient(#606060 10%, #c0c0c0 95%); |
|||
background-image: -webkit-linear-gradient(#606060 10%, #c0c0c0 95%); |
|||
background-image: -o-linear-gradient(#606060 10%, #c0c0c0 95%); |
|||
background-image: -ms-linear-gradient(#606060 10%, #c0c0c0 95%); |
|||
} |
|||
table.feature small { |
|||
font-size: 50%; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>LuaJIT</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a class="current" href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
LuaJIT is a <b>Just-In-Time Compiler</b> (JIT) for the |
|||
<a href="https://www.lua.org/"><span class="ext">»</span> Lua</a> programming language. |
|||
Lua is a powerful, dynamic and light-weight programming language. |
|||
It may be embedded or used as a general-purpose, stand-alone language. |
|||
</p> |
|||
<p> |
|||
LuaJIT is Copyright © 2005-2022 Mike Pall, released under the |
|||
<a href="https://www.opensource.org/licenses/mit-license.php"><span class="ext">»</span> MIT open source license</a>. |
|||
</p> |
|||
<p> |
|||
</p> |
|||
|
|||
<h2>Compatibility</h2> |
|||
<table class="feature os os1"> |
|||
<tr><td>Windows</td><td>Linux</td><td>BSD</td><td>macOS</td><td>POSIX</td></tr> |
|||
</table> |
|||
<table class="feature os os2"> |
|||
<tr><td><span style="font-size:90%;">Embedded</span></td><td>Android</td><td>iOS</td></tr> |
|||
</table> |
|||
<table class="feature os os3"> |
|||
<tr><td>PS3</td><td>PS4</td><td>PS5</td><td>PS Vita</td><td>Xbox 360</td><td>Xbox One</td></tr> |
|||
</table> |
|||
<table class="feature compiler"> |
|||
<tr><td>GCC</td><td>Clang<br>LLVM</td><td>MSVC</td></tr> |
|||
</table> |
|||
<table class="feature cpu"> |
|||
<tr><td>x86<br>x64</td><td>ARM<br>ARM64</td><td>PPC</td><td>MIPS32<br>MIPS64</td></tr> |
|||
</table> |
|||
<table class="feature fcompat"> |
|||
<tr><td>Lua 5.1<br>API+ABI</td><td>+ JIT</td><td>+ BitOp</td><td>+ FFI</td><td>Drop-in<br>DLL/.so</td></tr> |
|||
</table> |
|||
|
|||
<h2>Overview</h2> |
|||
<table class="feature stats"> |
|||
<tr> |
|||
<td class="speed">3x<br>- 100x</td> |
|||
<td class="kb">115 <small>KB</small><br>VM</td> |
|||
<td class="kb">90 <small>KB</small><br>JIT</td> |
|||
<td class="kloc">63 <small>KLOC</small><br>C</td> |
|||
<td class="kloc">24 <small>KLOC</small><br>ASM</td> |
|||
<td class="kloc">11 <small>KLOC</small><br>Lua</td> |
|||
</tr> |
|||
</table> |
|||
<p style="margin-top: 1em;"> |
|||
LuaJIT has been successfully used as a <b>scripting middleware</b> in |
|||
games, appliances, network and graphics apps, numerical simulations, |
|||
trading platforms and many other specialty applications. It scales from |
|||
embedded devices, smartphones, desktops up to server farms. It combines |
|||
high flexibility with high performance |
|||
and an unmatched <b>low memory footprint</b>. |
|||
</p> |
|||
<p> |
|||
LuaJIT has been in continuous development since 2005. It's widely |
|||
considered to be <b>one of the fastest dynamic language |
|||
implementations</b>. It has outperformed other dynamic languages on many |
|||
cross-language benchmarks since its first release — often by a |
|||
substantial margin. |
|||
</p> |
|||
<p> |
|||
For <b>LuaJIT 2.0</b>, the whole VM has been rewritten from the ground up |
|||
and relentlessly optimized for performance. It combines a <b>high-speed |
|||
interpreter</b>, written in assembler, with a <b>state-of-the-art JIT |
|||
compiler</b>. |
|||
</p> |
|||
<p> |
|||
An innovative <b>trace compiler</b> is integrated with advanced, |
|||
SSA-based optimizations and highly tuned code generation backends. |
|||
A substantial reduction of the overhead associated with dynamic languages |
|||
allows it to break into the performance range traditionally reserved for |
|||
offline, static language compilers. |
|||
</p> |
|||
|
|||
<h2>More ...</h2> |
|||
<p> |
|||
Please select a sub-topic in the navigation bar to learn more about LuaJIT. |
|||
</p> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,305 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Running LuaJIT</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
table.opt { |
|||
line-height: 1.2; |
|||
} |
|||
tr.opthead td { |
|||
font-weight: bold; |
|||
} |
|||
td.flag_name { |
|||
width: 4em; |
|||
} |
|||
td.flag_level { |
|||
width: 2em; |
|||
text-align: center; |
|||
} |
|||
td.param_name { |
|||
width: 6em; |
|||
} |
|||
td.param_default { |
|||
width: 4em; |
|||
text-align: right; |
|||
} |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Running LuaJIT</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a class="current" href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
LuaJIT has only a single stand-alone executable, called <tt>luajit</tt> on |
|||
POSIX systems or <tt>luajit.exe</tt> on Windows. It can be used to run simple |
|||
Lua statements or whole Lua applications from the command line. It has an |
|||
interactive mode, too. |
|||
</p> |
|||
|
|||
<h2 id="options">Command Line Options</h2> |
|||
<p> |
|||
The <tt>luajit</tt> stand-alone executable is just a slightly modified |
|||
version of the regular <tt>lua</tt> stand-alone executable. |
|||
It supports the same basic options, too. <tt>luajit -h</tt> |
|||
prints a short list of the available options. Please have a look at the |
|||
<a href="https://www.lua.org/manual/5.1/manual.html#6"><span class="ext">»</span> Lua manual</a> |
|||
for details. |
|||
</p> |
|||
<p> |
|||
LuaJIT has some additional options: |
|||
</p> |
|||
|
|||
<h3 id="opt_b"><tt>-b[options] input output</tt></h3> |
|||
<p> |
|||
This option saves or lists bytecode. The following additional options |
|||
are accepted: |
|||
</p> |
|||
<ul> |
|||
<li><tt>-l</tt> — Only list bytecode.</li> |
|||
<li><tt>-s</tt> — Strip debug info (this is the default).</li> |
|||
<li><tt>-g</tt> — Keep debug info.</li> |
|||
<li><tt>-n name</tt> — Set module name (default: auto-detect from input name)</li> |
|||
<li><tt>-t type</tt> — Set output file type (default: auto-detect from output name).</li> |
|||
<li><tt>-a arch</tt> — Override architecture for object files (default: native).</li> |
|||
<li><tt>-o os</tt> — Override OS for object files (default: native).</li> |
|||
<li><tt>-e chunk</tt> — Use chunk string as input.</li> |
|||
<li><tt>-</tt> (a single minus sign) — Use stdin as input and/or stdout as output.</li> |
|||
</ul> |
|||
<p> |
|||
The output file type is auto-detected from the extension of the output |
|||
file name: |
|||
</p> |
|||
<ul> |
|||
<li><tt>c</tt> — C source file, exported bytecode data.</li> |
|||
<li><tt>h</tt> — C header file, static bytecode data.</li> |
|||
<li><tt>obj</tt> or <tt>o</tt> — Object file, exported bytecode data |
|||
(OS- and architecture-specific).</li> |
|||
<li><tt>raw</tt> or any other extension — Raw bytecode file (portable). |
|||
</ul> |
|||
<p> |
|||
Notes: |
|||
</p> |
|||
<ul> |
|||
<li>See also <a href="extensions.html#string_dump">string.dump()</a> |
|||
for information on bytecode portability and compatibility.</li> |
|||
<li>A file in raw bytecode format is auto-detected and can be loaded like |
|||
any Lua source file. E.g. directly from the command line or with |
|||
<tt>loadfile()</tt>, <tt>dofile()</tt> etc.</li> |
|||
<li>To statically embed the bytecode of a module in your application, |
|||
generate an object file and just link it with your application.</li> |
|||
<li>On most ELF-based systems (e.g. Linux) you need to explicitly export the |
|||
global symbols when linking your application, e.g. with: <tt>-Wl,-E</tt></li> |
|||
<li><tt>require()</tt> tries to load embedded bytecode data from exported |
|||
symbols (in <tt>*.exe</tt> or <tt>lua51.dll</tt> on Windows) and from |
|||
shared libraries in <tt>package.cpath</tt>.</li> |
|||
</ul> |
|||
<p> |
|||
Typical usage examples: |
|||
</p> |
|||
<pre class="code"> |
|||
luajit -b test.lua test.out # Save bytecode to test.out |
|||
luajit -bg test.lua test.out # Keep debug info |
|||
luajit -be "print('hello world')" test.out # Save cmdline script |
|||
|
|||
luajit -bl test.lua # List to stdout |
|||
luajit -bl test.lua test.txt # List to test.txt |
|||
luajit -ble "print('hello world')" # List cmdline script |
|||
|
|||
luajit -b test.lua test.obj # Generate object file |
|||
# Link test.obj with your application and load it with require("test") |
|||
</pre> |
|||
|
|||
<h3 id="opt_j"><tt>-j cmd[=arg[,arg...]]</tt></h3> |
|||
<p> |
|||
This option performs a LuaJIT control command or activates one of the |
|||
loadable extension modules. The command is first looked up in the |
|||
<tt>jit.*</tt> library. If no matching function is found, a module |
|||
named <tt>jit.<cmd></tt> is loaded and the <tt>start()</tt> |
|||
function of the module is called with the specified arguments (if |
|||
any). The space between <tt>-j</tt> and <tt>cmd</tt> is optional. |
|||
</p> |
|||
<p> |
|||
Here are the available LuaJIT control commands: |
|||
</p> |
|||
<ul> |
|||
<li id="j_on"><tt>-jon</tt> — Turns the JIT compiler on (default).</li> |
|||
<li id="j_off"><tt>-joff</tt> — Turns the JIT compiler off (only use the interpreter).</li> |
|||
<li id="j_flush"><tt>-jflush</tt> — Flushes the whole cache of compiled code.</li> |
|||
<li id="j_v"><tt>-jv</tt> — Shows verbose information about the progress of the JIT compiler.</li> |
|||
<li id="j_dump"><tt>-jdump</tt> — Dumps the code and structures used in various compiler stages.</li> |
|||
<li id="j_p"><tt>-jp</tt> — Start the <a href="ext_profiler.html">integrated profiler</a>.</li> |
|||
</ul> |
|||
<p> |
|||
The <tt>-jv</tt> and <tt>-jdump</tt> commands are extension modules |
|||
written in Lua. They are mainly used for debugging the JIT compiler |
|||
itself. For a description of their options and output format, please |
|||
read the comment block at the start of their source. |
|||
They can be found in the <tt>lib</tt> directory of the source |
|||
distribution or installed under the <tt>jit</tt> directory. By default |
|||
this is <tt>/usr/local/share/luajit-XX.YY.ZZ>/jit</tt> on POSIX |
|||
systems (replace XX.YY.ZZ by the installed version). |
|||
</p> |
|||
|
|||
<h3 id="opt_O"><tt>-O[level]</tt><br> |
|||
<tt>-O[+]flag</tt> <tt>-O-flag</tt><br> |
|||
<tt>-Oparam=value</tt></h3> |
|||
<p> |
|||
This options allows fine-tuned control of the optimizations used by |
|||
the JIT compiler. This is mainly intended for debugging LuaJIT itself. |
|||
Please note that the JIT compiler is extremely fast (we are talking |
|||
about the microsecond to millisecond range). Disabling optimizations |
|||
doesn't have any visible impact on its overhead, but usually generates |
|||
code that runs slower. |
|||
</p> |
|||
<p> |
|||
The first form sets an optimization level — this enables a |
|||
specific mix of optimization flags. <tt>-O0</tt> turns off all |
|||
optimizations and higher numbers enable more optimizations. Omitting |
|||
the level (i.e. just <tt>-O</tt>) sets the default optimization level, |
|||
which is <tt>-O3</tt> in the current version. |
|||
</p> |
|||
<p> |
|||
The second form adds or removes individual optimization flags. |
|||
The third form sets a parameter for the VM or the JIT compiler |
|||
to a specific value. |
|||
</p> |
|||
<p> |
|||
You can either use this option multiple times (like <tt>-Ocse |
|||
-O-dce -Ohotloop=10</tt>) or separate several settings with a comma |
|||
(like <tt>-O+cse,-dce,hotloop=10</tt>). The settings are applied from |
|||
left to right and later settings override earlier ones. You can freely |
|||
mix the three forms, but note that setting an optimization level |
|||
overrides all earlier flags. |
|||
</p> |
|||
<p> |
|||
Here are the available flags and at what optimization levels they |
|||
are enabled: |
|||
</p> |
|||
<table class="opt"> |
|||
<tr class="opthead"> |
|||
<td class="flag_name">Flag</td> |
|||
<td class="flag_level">-O1</td> |
|||
<td class="flag_level">-O2</td> |
|||
<td class="flag_level">-O3</td> |
|||
<td class="flag_desc"> </td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="flag_name">fold</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Constant Folding, Simplifications and Reassociation</td></tr> |
|||
<tr class="even"> |
|||
<td class="flag_name">cse</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Common-Subexpression Elimination</td></tr> |
|||
<tr class="odd"> |
|||
<td class="flag_name">dce</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Dead-Code Elimination</td></tr> |
|||
<tr class="even"> |
|||
<td class="flag_name">narrow</td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Narrowing of numbers to integers</td></tr> |
|||
<tr class="odd"> |
|||
<td class="flag_name">loop</td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Loop Optimizations (code hoisting)</td></tr> |
|||
<tr class="even"> |
|||
<td class="flag_name">fwd</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Load Forwarding (L2L) and Store Forwarding (S2L)</td></tr> |
|||
<tr class="odd"> |
|||
<td class="flag_name">dse</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Dead-Store Elimination</td></tr> |
|||
<tr class="even"> |
|||
<td class="flag_name">abc</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Array Bounds Check Elimination</td></tr> |
|||
<tr class="odd"> |
|||
<td class="flag_name">sink</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Allocation/Store Sinking</td></tr> |
|||
<tr class="even"> |
|||
<td class="flag_name">fuse</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Fusion of operands into instructions</td></tr> |
|||
</table> |
|||
<p> |
|||
Here are the parameters and their default settings: |
|||
</p> |
|||
<table class="opt"> |
|||
<tr class="opthead"> |
|||
<td class="param_name">Parameter</td> |
|||
<td class="param_default">Default</td> |
|||
<td class="param_desc"> </td> |
|||
</tr> |
|||
<tr class="odd separate"> |
|||
<td class="param_name">maxtrace</td><td class="param_default">1000</td><td class="param_desc">Max. number of traces in the cache</td></tr> |
|||
<tr class="even"> |
|||
<td class="param_name">maxrecord</td><td class="param_default">4000</td><td class="param_desc">Max. number of recorded IR instructions</td></tr> |
|||
<tr class="odd"> |
|||
<td class="param_name">maxirconst</td><td class="param_default">500</td><td class="param_desc">Max. number of IR constants of a trace</td></tr> |
|||
<tr class="even"> |
|||
<td class="param_name">maxside</td><td class="param_default">100</td><td class="param_desc">Max. number of side traces of a root trace</td></tr> |
|||
<tr class="odd"> |
|||
<td class="param_name">maxsnap</td><td class="param_default">500</td><td class="param_desc">Max. number of snapshots for a trace</td></tr> |
|||
<tr class="even separate"> |
|||
<td class="param_name">hotloop</td><td class="param_default">56</td><td class="param_desc">Number of iterations to detect a hot loop or hot call</td></tr> |
|||
<tr class="odd"> |
|||
<td class="param_name">hotexit</td><td class="param_default">10</td><td class="param_desc">Number of taken exits to start a side trace</td></tr> |
|||
<tr class="even"> |
|||
<td class="param_name">tryside</td><td class="param_default">4</td><td class="param_desc">Number of attempts to compile a side trace</td></tr> |
|||
<tr class="odd separate"> |
|||
<td class="param_name">instunroll</td><td class="param_default">4</td><td class="param_desc">Max. unroll factor for instable loops</td></tr> |
|||
<tr class="even"> |
|||
<td class="param_name">loopunroll</td><td class="param_default">15</td><td class="param_desc">Max. unroll factor for loop ops in side traces</td></tr> |
|||
<tr class="odd"> |
|||
<td class="param_name">callunroll</td><td class="param_default">3</td><td class="param_desc">Max. unroll factor for pseudo-recursive calls</td></tr> |
|||
<tr class="even"> |
|||
<td class="param_name">recunroll</td><td class="param_default">2</td><td class="param_desc">Min. unroll factor for true recursion</td></tr> |
|||
<tr class="odd separate"> |
|||
<td class="param_name">sizemcode</td><td class="param_default">32</td><td class="param_desc">Size of each machine code area in KBytes (Windows: 64K)</td></tr> |
|||
<tr class="even"> |
|||
<td class="param_name">maxmcode</td><td class="param_default">512</td><td class="param_desc">Max. total size of all machine code areas in KBytes</td></tr> |
|||
</table> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,107 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<title>Status</title> |
|||
<meta charset="utf-8"> |
|||
<meta name="Copyright" content="Copyright (C) 2005-2022"> |
|||
<meta name="Language" content="en"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen"> |
|||
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print"> |
|||
<style type="text/css"> |
|||
ul li { padding-bottom: 0.3em; } |
|||
</style> |
|||
</head> |
|||
<body> |
|||
<div id="site"> |
|||
<a href="https://luajit.org"><span>Lua<span id="logo">JIT</span></span></a> |
|||
</div> |
|||
<div id="head"> |
|||
<h1>Status</h1> |
|||
</div> |
|||
<div id="nav"> |
|||
<ul><li> |
|||
<a href="luajit.html">LuaJIT</a> |
|||
<ul><li> |
|||
<a href="https://luajit.org/download.html">Download <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="install.html">Installation</a> |
|||
</li><li> |
|||
<a href="running.html">Running</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="extensions.html">Extensions</a> |
|||
<ul><li> |
|||
<a href="ext_ffi.html">FFI Library</a> |
|||
<ul><li> |
|||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a> |
|||
</li><li> |
|||
<a href="ext_ffi_api.html">ffi.* API</a> |
|||
</li><li> |
|||
<a href="ext_ffi_semantics.html">FFI Semantics</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a href="ext_buffer.html">String Buffers</a> |
|||
</li><li> |
|||
<a href="ext_jit.html">jit.* Library</a> |
|||
</li><li> |
|||
<a href="ext_c_api.html">Lua/C API</a> |
|||
</li><li> |
|||
<a href="ext_profiler.html">Profiler</a> |
|||
</li></ul> |
|||
</li><li> |
|||
<a class="current" href="status.html">Status</a> |
|||
</li><li> |
|||
<a href="faq.html">FAQ</a> |
|||
</li><li> |
|||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a> |
|||
</li><li> |
|||
<a href="https://luajit.org/list.html">Mailing List <span class="ext">»</span></a> |
|||
</li></ul> |
|||
</div> |
|||
<div id="main"> |
|||
<p> |
|||
This documentation is for LuaJIT 2.1.0-beta3. Please check the <tt>doc</tt> |
|||
directory in each git branch for the version-specific documentation. |
|||
</p> |
|||
<p> |
|||
The currently developed branches are LuaJIT 2.1 and LuaJIT 2.0. |
|||
</p> |
|||
<p> |
|||
LuaJIT 2.0 is in feature-freeze — new features will only |
|||
be added to LuaJIT 2.1. |
|||
</p> |
|||
|
|||
<h2>Current Status</h2> |
|||
<p> |
|||
LuaJIT ought to run all Lua 5.1-compatible source code just fine. |
|||
It's considered a serious bug if the VM crashes or produces unexpected |
|||
results — please report this. |
|||
</p> |
|||
<p> |
|||
Known incompatibilities and issues in LuaJIT 2.0: |
|||
</p> |
|||
<ul> |
|||
<li> |
|||
There are some differences in <b>implementation-defined</b> behavior. |
|||
These either have a good reason, are arbitrary design choices |
|||
or are due to quirks in the VM. The latter cases may get fixed if a |
|||
demonstrable need is shown. |
|||
</li> |
|||
<li> |
|||
The Lua <b>debug API</b> is missing a couple of features (return |
|||
hooks for non-Lua functions) and shows slightly different behavior |
|||
in LuaJIT (no per-coroutine hooks, no tail call counting). |
|||
</li> |
|||
</ul> |
|||
<br class="flush"> |
|||
</div> |
|||
<div id="foot"> |
|||
<hr class="hide"> |
|||
Copyright © 2005-2022 |
|||
<span class="noprint"> |
|||
· |
|||
<a href="contact.html">Contact</a> |
|||
</span> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -0,0 +1,461 @@ |
|||
/*
|
|||
** DynASM ARM encoding engine. |
|||
** Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
** Released under the MIT license. See dynasm.lua for full copyright notice. |
|||
*/ |
|||
|
|||
#include <stddef.h> |
|||
#include <stdarg.h> |
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#define DASM_ARCH "arm" |
|||
|
|||
#ifndef DASM_EXTERN |
|||
#define DASM_EXTERN(a,b,c,d) 0 |
|||
#endif |
|||
|
|||
/* Action definitions. */ |
|||
enum { |
|||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT, |
|||
/* The following actions need a buffer position. */ |
|||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG, |
|||
/* The following actions also have an argument. */ |
|||
DASM_REL_PC, DASM_LABEL_PC, |
|||
DASM_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12, DASM_IMMV8, |
|||
DASM__MAX |
|||
}; |
|||
|
|||
/* Maximum number of section buffer positions for a single dasm_put() call. */ |
|||
#define DASM_MAXSECPOS 25 |
|||
|
|||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */ |
|||
#define DASM_S_OK 0x00000000 |
|||
#define DASM_S_NOMEM 0x01000000 |
|||
#define DASM_S_PHASE 0x02000000 |
|||
#define DASM_S_MATCH_SEC 0x03000000 |
|||
#define DASM_S_RANGE_I 0x11000000 |
|||
#define DASM_S_RANGE_SEC 0x12000000 |
|||
#define DASM_S_RANGE_LG 0x13000000 |
|||
#define DASM_S_RANGE_PC 0x14000000 |
|||
#define DASM_S_RANGE_REL 0x15000000 |
|||
#define DASM_S_UNDEF_LG 0x21000000 |
|||
#define DASM_S_UNDEF_PC 0x22000000 |
|||
|
|||
/* Macros to convert positions (8 bit section + 24 bit index). */ |
|||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff) |
|||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000) |
|||
#define DASM_SEC2POS(sec) ((sec)<<24) |
|||
#define DASM_POS2SEC(pos) ((pos)>>24) |
|||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) |
|||
|
|||
/* Action list type. */ |
|||
typedef const unsigned int *dasm_ActList; |
|||
|
|||
/* Per-section structure. */ |
|||
typedef struct dasm_Section { |
|||
int *rbuf; /* Biased buffer pointer (negative section bias). */ |
|||
int *buf; /* True buffer pointer. */ |
|||
size_t bsize; /* Buffer size in bytes. */ |
|||
int pos; /* Biased buffer position. */ |
|||
int epos; /* End of biased buffer position - max single put. */ |
|||
int ofs; /* Byte offset into section. */ |
|||
} dasm_Section; |
|||
|
|||
/* Core structure holding the DynASM encoding state. */ |
|||
struct dasm_State { |
|||
size_t psize; /* Allocated size of this structure. */ |
|||
dasm_ActList actionlist; /* Current actionlist pointer. */ |
|||
int *lglabels; /* Local/global chain/pos ptrs. */ |
|||
size_t lgsize; |
|||
int *pclabels; /* PC label chains/pos ptrs. */ |
|||
size_t pcsize; |
|||
void **globals; /* Array of globals (bias -10). */ |
|||
dasm_Section *section; /* Pointer to active section. */ |
|||
size_t codesize; /* Total size of all code sections. */ |
|||
int maxsection; /* 0 <= sectionidx < maxsection. */ |
|||
int status; /* Status code. */ |
|||
dasm_Section sections[1]; /* All sections. Alloc-extended. */ |
|||
}; |
|||
|
|||
/* The size of the core structure depends on the max. number of sections. */ |
|||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) |
|||
|
|||
|
|||
/* Initialize DynASM state. */ |
|||
void dasm_init(Dst_DECL, int maxsection) |
|||
{ |
|||
dasm_State *D; |
|||
size_t psz = 0; |
|||
int i; |
|||
Dst_REF = NULL; |
|||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); |
|||
D = Dst_REF; |
|||
D->psize = psz; |
|||
D->lglabels = NULL; |
|||
D->lgsize = 0; |
|||
D->pclabels = NULL; |
|||
D->pcsize = 0; |
|||
D->globals = NULL; |
|||
D->maxsection = maxsection; |
|||
for (i = 0; i < maxsection; i++) { |
|||
D->sections[i].buf = NULL; /* Need this for pass3. */ |
|||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); |
|||
D->sections[i].bsize = 0; |
|||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ |
|||
} |
|||
} |
|||
|
|||
/* Free DynASM state. */ |
|||
void dasm_free(Dst_DECL) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
for (i = 0; i < D->maxsection; i++) |
|||
if (D->sections[i].buf) |
|||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); |
|||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize); |
|||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize); |
|||
DASM_M_FREE(Dst, D, D->psize); |
|||
} |
|||
|
|||
/* Setup global label array. Must be called before dasm_setup(). */ |
|||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
D->globals = gl - 10; /* Negative bias to compensate for locals. */ |
|||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int)); |
|||
} |
|||
|
|||
/* Grow PC label array. Can be called after dasm_setup(), too. */ |
|||
void dasm_growpc(Dst_DECL, unsigned int maxpc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
size_t osz = D->pcsize; |
|||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int)); |
|||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz); |
|||
} |
|||
|
|||
/* Setup encoder. */ |
|||
void dasm_setup(Dst_DECL, const void *actionlist) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
D->actionlist = (dasm_ActList)actionlist; |
|||
D->status = DASM_S_OK; |
|||
D->section = &D->sections[0]; |
|||
memset((void *)D->lglabels, 0, D->lgsize); |
|||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize); |
|||
for (i = 0; i < D->maxsection; i++) { |
|||
D->sections[i].pos = DASM_SEC2POS(i); |
|||
D->sections[i].ofs = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) { \ |
|||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#define CKPL(kind, st) \ |
|||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ |
|||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#define CKPL(kind, st) ((void)0) |
|||
#endif |
|||
|
|||
static int dasm_imm12(unsigned int n) |
|||
{ |
|||
int i; |
|||
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30)) |
|||
if (n <= 255) return (int)(n + (i << 8)); |
|||
return -1; |
|||
} |
|||
|
|||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ |
|||
void dasm_put(Dst_DECL, int start, ...) |
|||
{ |
|||
va_list ap; |
|||
dasm_State *D = Dst_REF; |
|||
dasm_ActList p = D->actionlist + start; |
|||
dasm_Section *sec = D->section; |
|||
int pos = sec->pos, ofs = sec->ofs; |
|||
int *b; |
|||
|
|||
if (pos >= sec->epos) { |
|||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize, |
|||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int)); |
|||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos); |
|||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos); |
|||
} |
|||
|
|||
b = sec->rbuf; |
|||
b[pos++] = start; |
|||
|
|||
va_start(ap, start); |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
if (action >= DASM__MAX) { |
|||
ofs += 4; |
|||
} else { |
|||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0; |
|||
switch (action) { |
|||
case DASM_STOP: goto stop; |
|||
case DASM_SECTION: |
|||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC); |
|||
D->section = &D->sections[n]; goto stop; |
|||
case DASM_ESC: p++; ofs += 4; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break; |
|||
case DASM_REL_LG: |
|||
n = (ins & 2047) - 10; pl = D->lglabels + n; |
|||
/* Bkwd rel or global. */ |
|||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; } |
|||
pl += 10; n = *pl; |
|||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */ |
|||
goto linkrel; |
|||
case DASM_REL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putrel: |
|||
n = *pl; |
|||
if (n < 0) { /* Label exists. Get label pos and store it. */ |
|||
b[pos] = -n; |
|||
} else { |
|||
linkrel: |
|||
b[pos] = n; /* Else link to rel chain, anchored at label. */ |
|||
*pl = pos; |
|||
} |
|||
pos++; |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel; |
|||
case DASM_LABEL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putlabel: |
|||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; |
|||
} |
|||
*pl = -pos; /* Label exists now. */ |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
break; |
|||
case DASM_IMM: |
|||
case DASM_IMM16: |
|||
#ifdef DASM_CHECKS |
|||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I); |
|||
if ((ins & 0x8000)) |
|||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I); |
|||
else |
|||
CK((n>>((ins>>5)&31)) == 0, RANGE_I); |
|||
#endif |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMMV8: |
|||
CK((n & 3) == 0, RANGE_I); |
|||
n >>= 2; |
|||
/* fallthrough */ |
|||
case DASM_IMML8: |
|||
case DASM_IMML12: |
|||
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) : |
|||
(((-n)>>((ins>>5)&31)) == 0), RANGE_I); |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMM12: |
|||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I); |
|||
b[pos++] = n; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
stop: |
|||
va_end(ap); |
|||
sec->pos = pos; |
|||
sec->ofs = ofs; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */ |
|||
int dasm_link(Dst_DECL, size_t *szp) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int secnum; |
|||
int ofs = 0; |
|||
|
|||
#ifdef DASM_CHECKS |
|||
*szp = 0; |
|||
if (D->status != DASM_S_OK) return D->status; |
|||
{ |
|||
int pc; |
|||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++) |
|||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc; |
|||
} |
|||
#endif |
|||
|
|||
{ /* Handle globals not defined in this translation unit. */ |
|||
int idx; |
|||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) { |
|||
int n = D->lglabels[idx]; |
|||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; } |
|||
} |
|||
} |
|||
|
|||
/* Combine all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->rbuf; |
|||
int pos = DASM_SEC2POS(secnum); |
|||
int lastpos = sec->pos; |
|||
|
|||
while (pos != lastpos) { |
|||
dasm_ActList p = D->actionlist + b[pos++]; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: p++; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break; |
|||
case DASM_REL_LG: case DASM_REL_PC: pos++; break; |
|||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break; |
|||
case DASM_IMM: case DASM_IMM12: case DASM_IMM16: |
|||
case DASM_IMML8: case DASM_IMML12: case DASM_IMMV8: pos++; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
ofs += sec->ofs; /* Next section starts right after current section. */ |
|||
} |
|||
|
|||
D->codesize = ofs; /* Total size of all code sections */ |
|||
*szp = ofs; |
|||
return DASM_S_OK; |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 3: Encode sections. */ |
|||
int dasm_encode(Dst_DECL, void *buffer) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
char *base = (char *)buffer; |
|||
unsigned int *cp = (unsigned int *)buffer; |
|||
int secnum; |
|||
|
|||
/* Encode all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->buf; |
|||
int *endb = sec->rbuf + sec->pos; |
|||
|
|||
while (b != endb) { |
|||
dasm_ActList p = D->actionlist + *b++; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0; |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: *cp++ = *p++; break; |
|||
case DASM_REL_EXT: |
|||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048)); |
|||
goto patchrel; |
|||
case DASM_ALIGN: |
|||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000; |
|||
break; |
|||
case DASM_REL_LG: |
|||
if (n < 0) { |
|||
n = (int)((ptrdiff_t)D->globals[-n] - (ptrdiff_t)cp - 4); |
|||
goto patchrel; |
|||
} |
|||
/* fallthrough */ |
|||
case DASM_REL_PC: |
|||
CK(n >= 0, UNDEF_PC); |
|||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) - 4; |
|||
patchrel: |
|||
if ((ins & 0x800) == 0) { |
|||
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL); |
|||
cp[-1] |= ((n >> 2) & 0x00ffffff); |
|||
} else if ((ins & 0x1000)) { |
|||
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL); |
|||
goto patchimml8; |
|||
} else if ((ins & 0x2000) == 0) { |
|||
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL); |
|||
goto patchimml; |
|||
} else { |
|||
CK((n & 3) == 0 && -1020 <= n && n <= 1020, RANGE_REL); |
|||
n >>= 2; |
|||
goto patchimml; |
|||
} |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n); |
|||
break; |
|||
case DASM_LABEL_PC: break; |
|||
case DASM_IMM: |
|||
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31); |
|||
break; |
|||
case DASM_IMM12: |
|||
cp[-1] |= dasm_imm12((unsigned int)n); |
|||
break; |
|||
case DASM_IMM16: |
|||
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff); |
|||
break; |
|||
case DASM_IMML8: patchimml8: |
|||
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) : |
|||
((-n & 0x0f) | ((-n & 0xf0) << 4)); |
|||
break; |
|||
case DASM_IMML12: case DASM_IMMV8: patchimml: |
|||
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n); |
|||
break; |
|||
default: *cp++ = ins; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
} |
|||
|
|||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */ |
|||
return DASM_S_PHASE; |
|||
return DASM_S_OK; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Get PC label offset. */ |
|||
int dasm_getpclabel(Dst_DECL, unsigned int pc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (pc*sizeof(int) < D->pcsize) { |
|||
int pos = D->pclabels[pc]; |
|||
if (pos < 0) return *DASM_POS2PTR(D, -pos); |
|||
if (pos > 0) return -1; /* Undefined. */ |
|||
} |
|||
return -2; /* Unused or out of range. */ |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
/* Optional sanity checker to call between isolated encoding steps. */ |
|||
int dasm_checkstep(Dst_DECL, int secmatch) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (D->status == DASM_S_OK) { |
|||
int i; |
|||
for (i = 1; i <= 9; i++) { |
|||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; } |
|||
D->lglabels[i] = 0; |
|||
} |
|||
} |
|||
if (D->status == DASM_S_OK && secmatch >= 0 && |
|||
D->section != &D->sections[secmatch]) |
|||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections); |
|||
return D->status; |
|||
} |
|||
#endif |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,561 @@ |
|||
/*
|
|||
** DynASM ARM64 encoding engine. |
|||
** Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
** Released under the MIT license. See dynasm.lua for full copyright notice. |
|||
*/ |
|||
|
|||
#include <stddef.h> |
|||
#include <stdarg.h> |
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#define DASM_ARCH "arm64" |
|||
|
|||
#ifndef DASM_EXTERN |
|||
#define DASM_EXTERN(a,b,c,d) 0 |
|||
#endif |
|||
|
|||
/* Action definitions. */ |
|||
enum { |
|||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT, |
|||
/* The following actions need a buffer position. */ |
|||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG, |
|||
/* The following actions also have an argument. */ |
|||
DASM_REL_PC, DASM_LABEL_PC, DASM_REL_A, |
|||
DASM_IMM, DASM_IMM6, DASM_IMM12, DASM_IMM13W, DASM_IMM13X, DASM_IMML, |
|||
DASM_IMMV, DASM_VREG, |
|||
DASM__MAX |
|||
}; |
|||
|
|||
/* Maximum number of section buffer positions for a single dasm_put() call. */ |
|||
#define DASM_MAXSECPOS 25 |
|||
|
|||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */ |
|||
#define DASM_S_OK 0x00000000 |
|||
#define DASM_S_NOMEM 0x01000000 |
|||
#define DASM_S_PHASE 0x02000000 |
|||
#define DASM_S_MATCH_SEC 0x03000000 |
|||
#define DASM_S_RANGE_I 0x11000000 |
|||
#define DASM_S_RANGE_SEC 0x12000000 |
|||
#define DASM_S_RANGE_LG 0x13000000 |
|||
#define DASM_S_RANGE_PC 0x14000000 |
|||
#define DASM_S_RANGE_REL 0x15000000 |
|||
#define DASM_S_RANGE_VREG 0x16000000 |
|||
#define DASM_S_UNDEF_LG 0x21000000 |
|||
#define DASM_S_UNDEF_PC 0x22000000 |
|||
|
|||
/* Macros to convert positions (8 bit section + 24 bit index). */ |
|||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff) |
|||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000) |
|||
#define DASM_SEC2POS(sec) ((sec)<<24) |
|||
#define DASM_POS2SEC(pos) ((pos)>>24) |
|||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) |
|||
|
|||
/* Action list type. */ |
|||
typedef const unsigned int *dasm_ActList; |
|||
|
|||
/* Per-section structure. */ |
|||
typedef struct dasm_Section { |
|||
int *rbuf; /* Biased buffer pointer (negative section bias). */ |
|||
int *buf; /* True buffer pointer. */ |
|||
size_t bsize; /* Buffer size in bytes. */ |
|||
int pos; /* Biased buffer position. */ |
|||
int epos; /* End of biased buffer position - max single put. */ |
|||
int ofs; /* Byte offset into section. */ |
|||
} dasm_Section; |
|||
|
|||
/* Core structure holding the DynASM encoding state. */ |
|||
struct dasm_State { |
|||
size_t psize; /* Allocated size of this structure. */ |
|||
dasm_ActList actionlist; /* Current actionlist pointer. */ |
|||
int *lglabels; /* Local/global chain/pos ptrs. */ |
|||
size_t lgsize; |
|||
int *pclabels; /* PC label chains/pos ptrs. */ |
|||
size_t pcsize; |
|||
void **globals; /* Array of globals (bias -10). */ |
|||
dasm_Section *section; /* Pointer to active section. */ |
|||
size_t codesize; /* Total size of all code sections. */ |
|||
int maxsection; /* 0 <= sectionidx < maxsection. */ |
|||
int status; /* Status code. */ |
|||
dasm_Section sections[1]; /* All sections. Alloc-extended. */ |
|||
}; |
|||
|
|||
/* The size of the core structure depends on the max. number of sections. */ |
|||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) |
|||
|
|||
|
|||
/* Initialize DynASM state. */ |
|||
void dasm_init(Dst_DECL, int maxsection) |
|||
{ |
|||
dasm_State *D; |
|||
size_t psz = 0; |
|||
int i; |
|||
Dst_REF = NULL; |
|||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); |
|||
D = Dst_REF; |
|||
D->psize = psz; |
|||
D->lglabels = NULL; |
|||
D->lgsize = 0; |
|||
D->pclabels = NULL; |
|||
D->pcsize = 0; |
|||
D->globals = NULL; |
|||
D->maxsection = maxsection; |
|||
for (i = 0; i < maxsection; i++) { |
|||
D->sections[i].buf = NULL; /* Need this for pass3. */ |
|||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); |
|||
D->sections[i].bsize = 0; |
|||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ |
|||
} |
|||
} |
|||
|
|||
/* Free DynASM state. */ |
|||
void dasm_free(Dst_DECL) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
for (i = 0; i < D->maxsection; i++) |
|||
if (D->sections[i].buf) |
|||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); |
|||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize); |
|||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize); |
|||
DASM_M_FREE(Dst, D, D->psize); |
|||
} |
|||
|
|||
/* Setup global label array. Must be called before dasm_setup(). */ |
|||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
D->globals = gl - 10; /* Negative bias to compensate for locals. */ |
|||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int)); |
|||
} |
|||
|
|||
/* Grow PC label array. Can be called after dasm_setup(), too. */ |
|||
void dasm_growpc(Dst_DECL, unsigned int maxpc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
size_t osz = D->pcsize; |
|||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int)); |
|||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz); |
|||
} |
|||
|
|||
/* Setup encoder. */ |
|||
void dasm_setup(Dst_DECL, const void *actionlist) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
D->actionlist = (dasm_ActList)actionlist; |
|||
D->status = DASM_S_OK; |
|||
D->section = &D->sections[0]; |
|||
memset((void *)D->lglabels, 0, D->lgsize); |
|||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize); |
|||
for (i = 0; i < D->maxsection; i++) { |
|||
D->sections[i].pos = DASM_SEC2POS(i); |
|||
D->sections[i].ofs = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) { \ |
|||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#define CKPL(kind, st) \ |
|||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ |
|||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#define CKPL(kind, st) ((void)0) |
|||
#endif |
|||
|
|||
static int dasm_imm12(unsigned int n) |
|||
{ |
|||
if ((n >> 12) == 0) |
|||
return n; |
|||
else if ((n & 0xff000fff) == 0) |
|||
return (n >> 12) | 0x1000; |
|||
else |
|||
return -1; |
|||
} |
|||
|
|||
static int dasm_ffs(unsigned long long x) |
|||
{ |
|||
int n = -1; |
|||
while (x) { x >>= 1; n++; } |
|||
return n; |
|||
} |
|||
|
|||
static int dasm_imm13(int lo, int hi) |
|||
{ |
|||
int inv = 0, w = 64, s = 0xfff, xa, xb; |
|||
unsigned long long n = (((unsigned long long)hi) << 32) | (unsigned int)lo; |
|||
unsigned long long m = 1ULL, a, b, c; |
|||
if (n & 1) { n = ~n; inv = 1; } |
|||
a = n & -n; b = (n+a)&-(n+a); c = (n+a-b)&-(n+a-b); |
|||
xa = dasm_ffs(a); xb = dasm_ffs(b); |
|||
if (c) { |
|||
w = dasm_ffs(c) - xa; |
|||
if (w == 32) m = 0x0000000100000001UL; |
|||
else if (w == 16) m = 0x0001000100010001UL; |
|||
else if (w == 8) m = 0x0101010101010101UL; |
|||
else if (w == 4) m = 0x1111111111111111UL; |
|||
else if (w == 2) m = 0x5555555555555555UL; |
|||
else return -1; |
|||
s = (-2*w & 0x3f) - 1; |
|||
} else if (!a) { |
|||
return -1; |
|||
} else if (xb == -1) { |
|||
xb = 64; |
|||
} |
|||
if ((b-a) * m != n) return -1; |
|||
if (inv) { |
|||
return ((w - xb) << 6) | (s+w+xa-xb); |
|||
} else { |
|||
return ((w - xa) << 6) | (s+xb-xa); |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ |
|||
void dasm_put(Dst_DECL, int start, ...) |
|||
{ |
|||
va_list ap; |
|||
dasm_State *D = Dst_REF; |
|||
dasm_ActList p = D->actionlist + start; |
|||
dasm_Section *sec = D->section; |
|||
int pos = sec->pos, ofs = sec->ofs; |
|||
int *b; |
|||
|
|||
if (pos >= sec->epos) { |
|||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize, |
|||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int)); |
|||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos); |
|||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos); |
|||
} |
|||
|
|||
b = sec->rbuf; |
|||
b[pos++] = start; |
|||
|
|||
va_start(ap, start); |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
if (action >= DASM__MAX) { |
|||
ofs += 4; |
|||
} else { |
|||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0; |
|||
switch (action) { |
|||
case DASM_STOP: goto stop; |
|||
case DASM_SECTION: |
|||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC); |
|||
D->section = &D->sections[n]; goto stop; |
|||
case DASM_ESC: p++; ofs += 4; break; |
|||
case DASM_REL_EXT: if ((ins & 0x8000)) ofs += 8; break; |
|||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break; |
|||
case DASM_REL_LG: |
|||
n = (ins & 2047) - 10; pl = D->lglabels + n; |
|||
/* Bkwd rel or global. */ |
|||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; } |
|||
pl += 10; n = *pl; |
|||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */ |
|||
goto linkrel; |
|||
case DASM_REL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putrel: |
|||
n = *pl; |
|||
if (n < 0) { /* Label exists. Get label pos and store it. */ |
|||
b[pos] = -n; |
|||
} else { |
|||
linkrel: |
|||
b[pos] = n; /* Else link to rel chain, anchored at label. */ |
|||
*pl = pos; |
|||
} |
|||
pos++; |
|||
if ((ins & 0x8000)) ofs += 8; |
|||
break; |
|||
case DASM_REL_A: |
|||
b[pos++] = n; |
|||
b[pos++] = va_arg(ap, int); |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel; |
|||
case DASM_LABEL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putlabel: |
|||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; |
|||
} |
|||
*pl = -pos; /* Label exists now. */ |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
break; |
|||
case DASM_IMM: |
|||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I); |
|||
n >>= ((ins>>10)&31); |
|||
#ifdef DASM_CHECKS |
|||
if ((ins & 0x8000)) |
|||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I); |
|||
else |
|||
CK((n>>((ins>>5)&31)) == 0, RANGE_I); |
|||
#endif |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMM6: |
|||
CK((n >> 6) == 0, RANGE_I); |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMM12: |
|||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I); |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMM13W: |
|||
CK(dasm_imm13(n, n) != -1, RANGE_I); |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMM13X: { |
|||
int m = va_arg(ap, int); |
|||
CK(dasm_imm13(n, m) != -1, RANGE_I); |
|||
b[pos++] = n; |
|||
b[pos++] = m; |
|||
break; |
|||
} |
|||
case DASM_IMML: { |
|||
#ifdef DASM_CHECKS |
|||
int scale = (ins & 3); |
|||
CK((!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) || |
|||
(unsigned int)(n+256) < 512, RANGE_I); |
|||
#endif |
|||
b[pos++] = n; |
|||
break; |
|||
} |
|||
case DASM_IMMV: |
|||
ofs += 4; |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_VREG: |
|||
CK(n < 32, RANGE_VREG); |
|||
b[pos++] = n; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
stop: |
|||
va_end(ap); |
|||
sec->pos = pos; |
|||
sec->ofs = ofs; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */ |
|||
int dasm_link(Dst_DECL, size_t *szp) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int secnum; |
|||
int ofs = 0; |
|||
|
|||
#ifdef DASM_CHECKS |
|||
*szp = 0; |
|||
if (D->status != DASM_S_OK) return D->status; |
|||
{ |
|||
int pc; |
|||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++) |
|||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc; |
|||
} |
|||
#endif |
|||
|
|||
{ /* Handle globals not defined in this translation unit. */ |
|||
int idx; |
|||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) { |
|||
int n = D->lglabels[idx]; |
|||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; } |
|||
} |
|||
} |
|||
|
|||
/* Combine all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->rbuf; |
|||
int pos = DASM_SEC2POS(secnum); |
|||
int lastpos = sec->pos; |
|||
|
|||
while (pos != lastpos) { |
|||
dasm_ActList p = D->actionlist + b[pos++]; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: p++; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break; |
|||
case DASM_REL_LG: case DASM_REL_PC: pos++; break; |
|||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break; |
|||
case DASM_IMM: case DASM_IMM6: case DASM_IMM12: case DASM_IMM13W: |
|||
case DASM_IMML: case DASM_IMMV: case DASM_VREG: pos++; break; |
|||
case DASM_IMM13X: case DASM_REL_A: pos += 2; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
ofs += sec->ofs; /* Next section starts right after current section. */ |
|||
} |
|||
|
|||
D->codesize = ofs; /* Total size of all code sections */ |
|||
*szp = ofs; |
|||
return DASM_S_OK; |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 3: Encode sections. */ |
|||
int dasm_encode(Dst_DECL, void *buffer) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
char *base = (char *)buffer; |
|||
unsigned int *cp = (unsigned int *)buffer; |
|||
int secnum; |
|||
|
|||
/* Encode all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->buf; |
|||
int *endb = sec->rbuf + sec->pos; |
|||
|
|||
while (b != endb) { |
|||
dasm_ActList p = D->actionlist + *b++; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0; |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: *cp++ = *p++; break; |
|||
case DASM_REL_EXT: |
|||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048)); |
|||
goto patchrel; |
|||
case DASM_ALIGN: |
|||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xd503201f; |
|||
break; |
|||
case DASM_REL_LG: |
|||
if (n < 0) { |
|||
ptrdiff_t na = (ptrdiff_t)D->globals[-n] - (ptrdiff_t)cp + 4; |
|||
n = (int)na; |
|||
CK((ptrdiff_t)n == na, RANGE_REL); |
|||
goto patchrel; |
|||
} |
|||
/* fallthrough */ |
|||
case DASM_REL_PC: |
|||
CK(n >= 0, UNDEF_PC); |
|||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) + 4; |
|||
patchrel: |
|||
if (!(ins & 0xf800)) { /* B, BL */ |
|||
CK((n & 3) == 0 && ((n+0x08000000) >> 28) == 0, RANGE_REL); |
|||
cp[-1] |= ((n >> 2) & 0x03ffffff); |
|||
} else if ((ins & 0x800)) { /* B.cond, CBZ, CBNZ, LDR* literal */ |
|||
CK((n & 3) == 0 && ((n+0x00100000) >> 21) == 0, RANGE_REL); |
|||
cp[-1] |= ((n << 3) & 0x00ffffe0); |
|||
} else if ((ins & 0x3000) == 0x2000) { /* ADR */ |
|||
CK(((n+0x00100000) >> 21) == 0, RANGE_REL); |
|||
cp[-1] |= ((n << 3) & 0x00ffffe0) | ((n & 3) << 29); |
|||
} else if ((ins & 0x3000) == 0x3000) { /* ADRP */ |
|||
cp[-1] |= ((n >> 9) & 0x00ffffe0) | (((n >> 12) & 3) << 29); |
|||
} else if ((ins & 0x1000)) { /* TBZ, TBNZ */ |
|||
CK((n & 3) == 0 && ((n+0x00008000) >> 16) == 0, RANGE_REL); |
|||
cp[-1] |= ((n << 3) & 0x0007ffe0); |
|||
} else if ((ins & 0x8000)) { /* absolute */ |
|||
cp[0] = (unsigned int)((ptrdiff_t)cp - 4 + n); |
|||
cp[1] = (unsigned int)(((ptrdiff_t)cp - 4 + n) >> 32); |
|||
cp += 2; |
|||
} |
|||
break; |
|||
case DASM_REL_A: { |
|||
ptrdiff_t na = (((ptrdiff_t)(*b++) << 32) | (unsigned int)n); |
|||
if ((ins & 0x3000) == 0x3000) { /* ADRP */ |
|||
ins &= ~0x1000; |
|||
na = (na >> 12) - (((ptrdiff_t)cp - 4) >> 12); |
|||
} else { |
|||
na = na - (ptrdiff_t)cp + 4; |
|||
} |
|||
n = (int)na; |
|||
CK((ptrdiff_t)n == na, RANGE_REL); |
|||
goto patchrel; |
|||
} |
|||
case DASM_LABEL_LG: |
|||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n); |
|||
break; |
|||
case DASM_LABEL_PC: break; |
|||
case DASM_IMM: |
|||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31); |
|||
break; |
|||
case DASM_IMM6: |
|||
cp[-1] |= ((n&31) << 19) | ((n&32) << 26); |
|||
break; |
|||
case DASM_IMM12: |
|||
cp[-1] |= (dasm_imm12((unsigned int)n) << 10); |
|||
break; |
|||
case DASM_IMM13W: |
|||
cp[-1] |= (dasm_imm13(n, n) << 10); |
|||
break; |
|||
case DASM_IMM13X: |
|||
cp[-1] |= (dasm_imm13(n, *b++) << 10); |
|||
break; |
|||
case DASM_IMML: { |
|||
int scale = (ins & 3); |
|||
cp[-1] |= (!(n & ((1<<scale)-1)) && (unsigned int)(n>>scale) < 4096) ? |
|||
((n << (10-scale)) | 0x01000000) : ((n & 511) << 12); |
|||
break; |
|||
} |
|||
case DASM_IMMV: |
|||
*cp++ = n; |
|||
break; |
|||
case DASM_VREG: |
|||
cp[-1] |= (n & 0x1f) << (ins & 0x1f); |
|||
break; |
|||
default: *cp++ = ins; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
} |
|||
|
|||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */ |
|||
return DASM_S_PHASE; |
|||
return DASM_S_OK; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Get PC label offset. */ |
|||
int dasm_getpclabel(Dst_DECL, unsigned int pc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (pc*sizeof(int) < D->pcsize) { |
|||
int pos = D->pclabels[pc]; |
|||
if (pos < 0) return *DASM_POS2PTR(D, -pos); |
|||
if (pos > 0) return -1; /* Undefined. */ |
|||
} |
|||
return -2; /* Unused or out of range. */ |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
/* Optional sanity checker to call between isolated encoding steps. */ |
|||
int dasm_checkstep(Dst_DECL, int secmatch) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (D->status == DASM_S_OK) { |
|||
int i; |
|||
for (i = 1; i <= 9; i++) { |
|||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; } |
|||
D->lglabels[i] = 0; |
|||
} |
|||
} |
|||
if (D->status == DASM_S_OK && secmatch >= 0 && |
|||
D->section != &D->sections[secmatch]) |
|||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections); |
|||
return D->status; |
|||
} |
|||
#endif |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,424 @@ |
|||
/*
|
|||
** DynASM MIPS encoding engine. |
|||
** Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
** Released under the MIT license. See dynasm.lua for full copyright notice. |
|||
*/ |
|||
|
|||
#include <stddef.h> |
|||
#include <stdarg.h> |
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#define DASM_ARCH "mips" |
|||
|
|||
#ifndef DASM_EXTERN |
|||
#define DASM_EXTERN(a,b,c,d) 0 |
|||
#endif |
|||
|
|||
/* Action definitions. */ |
|||
enum { |
|||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT, |
|||
/* The following actions need a buffer position. */ |
|||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG, |
|||
/* The following actions also have an argument. */ |
|||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMS, |
|||
DASM__MAX |
|||
}; |
|||
|
|||
/* Maximum number of section buffer positions for a single dasm_put() call. */ |
|||
#define DASM_MAXSECPOS 25 |
|||
|
|||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */ |
|||
#define DASM_S_OK 0x00000000 |
|||
#define DASM_S_NOMEM 0x01000000 |
|||
#define DASM_S_PHASE 0x02000000 |
|||
#define DASM_S_MATCH_SEC 0x03000000 |
|||
#define DASM_S_RANGE_I 0x11000000 |
|||
#define DASM_S_RANGE_SEC 0x12000000 |
|||
#define DASM_S_RANGE_LG 0x13000000 |
|||
#define DASM_S_RANGE_PC 0x14000000 |
|||
#define DASM_S_RANGE_REL 0x15000000 |
|||
#define DASM_S_UNDEF_LG 0x21000000 |
|||
#define DASM_S_UNDEF_PC 0x22000000 |
|||
|
|||
/* Macros to convert positions (8 bit section + 24 bit index). */ |
|||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff) |
|||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000) |
|||
#define DASM_SEC2POS(sec) ((sec)<<24) |
|||
#define DASM_POS2SEC(pos) ((pos)>>24) |
|||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) |
|||
|
|||
/* Action list type. */ |
|||
typedef const unsigned int *dasm_ActList; |
|||
|
|||
/* Per-section structure. */ |
|||
typedef struct dasm_Section { |
|||
int *rbuf; /* Biased buffer pointer (negative section bias). */ |
|||
int *buf; /* True buffer pointer. */ |
|||
size_t bsize; /* Buffer size in bytes. */ |
|||
int pos; /* Biased buffer position. */ |
|||
int epos; /* End of biased buffer position - max single put. */ |
|||
int ofs; /* Byte offset into section. */ |
|||
} dasm_Section; |
|||
|
|||
/* Core structure holding the DynASM encoding state. */ |
|||
struct dasm_State { |
|||
size_t psize; /* Allocated size of this structure. */ |
|||
dasm_ActList actionlist; /* Current actionlist pointer. */ |
|||
int *lglabels; /* Local/global chain/pos ptrs. */ |
|||
size_t lgsize; |
|||
int *pclabels; /* PC label chains/pos ptrs. */ |
|||
size_t pcsize; |
|||
void **globals; /* Array of globals (bias -10). */ |
|||
dasm_Section *section; /* Pointer to active section. */ |
|||
size_t codesize; /* Total size of all code sections. */ |
|||
int maxsection; /* 0 <= sectionidx < maxsection. */ |
|||
int status; /* Status code. */ |
|||
dasm_Section sections[1]; /* All sections. Alloc-extended. */ |
|||
}; |
|||
|
|||
/* The size of the core structure depends on the max. number of sections. */ |
|||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) |
|||
|
|||
|
|||
/* Initialize DynASM state. */ |
|||
void dasm_init(Dst_DECL, int maxsection) |
|||
{ |
|||
dasm_State *D; |
|||
size_t psz = 0; |
|||
int i; |
|||
Dst_REF = NULL; |
|||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); |
|||
D = Dst_REF; |
|||
D->psize = psz; |
|||
D->lglabels = NULL; |
|||
D->lgsize = 0; |
|||
D->pclabels = NULL; |
|||
D->pcsize = 0; |
|||
D->globals = NULL; |
|||
D->maxsection = maxsection; |
|||
for (i = 0; i < maxsection; i++) { |
|||
D->sections[i].buf = NULL; /* Need this for pass3. */ |
|||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); |
|||
D->sections[i].bsize = 0; |
|||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ |
|||
} |
|||
} |
|||
|
|||
/* Free DynASM state. */ |
|||
void dasm_free(Dst_DECL) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
for (i = 0; i < D->maxsection; i++) |
|||
if (D->sections[i].buf) |
|||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); |
|||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize); |
|||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize); |
|||
DASM_M_FREE(Dst, D, D->psize); |
|||
} |
|||
|
|||
/* Setup global label array. Must be called before dasm_setup(). */ |
|||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
D->globals = gl - 10; /* Negative bias to compensate for locals. */ |
|||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int)); |
|||
} |
|||
|
|||
/* Grow PC label array. Can be called after dasm_setup(), too. */ |
|||
void dasm_growpc(Dst_DECL, unsigned int maxpc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
size_t osz = D->pcsize; |
|||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int)); |
|||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz); |
|||
} |
|||
|
|||
/* Setup encoder. */ |
|||
void dasm_setup(Dst_DECL, const void *actionlist) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
D->actionlist = (dasm_ActList)actionlist; |
|||
D->status = DASM_S_OK; |
|||
D->section = &D->sections[0]; |
|||
memset((void *)D->lglabels, 0, D->lgsize); |
|||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize); |
|||
for (i = 0; i < D->maxsection; i++) { |
|||
D->sections[i].pos = DASM_SEC2POS(i); |
|||
D->sections[i].ofs = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) { \ |
|||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#define CKPL(kind, st) \ |
|||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ |
|||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#define CKPL(kind, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ |
|||
void dasm_put(Dst_DECL, int start, ...) |
|||
{ |
|||
va_list ap; |
|||
dasm_State *D = Dst_REF; |
|||
dasm_ActList p = D->actionlist + start; |
|||
dasm_Section *sec = D->section; |
|||
int pos = sec->pos, ofs = sec->ofs; |
|||
int *b; |
|||
|
|||
if (pos >= sec->epos) { |
|||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize, |
|||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int)); |
|||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos); |
|||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos); |
|||
} |
|||
|
|||
b = sec->rbuf; |
|||
b[pos++] = start; |
|||
|
|||
va_start(ap, start); |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16) - 0xff00; |
|||
if (action >= DASM__MAX) { |
|||
ofs += 4; |
|||
} else { |
|||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0; |
|||
switch (action) { |
|||
case DASM_STOP: goto stop; |
|||
case DASM_SECTION: |
|||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC); |
|||
D->section = &D->sections[n]; goto stop; |
|||
case DASM_ESC: p++; ofs += 4; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break; |
|||
case DASM_REL_LG: |
|||
n = (ins & 2047) - 10; pl = D->lglabels + n; |
|||
/* Bkwd rel or global. */ |
|||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; } |
|||
pl += 10; n = *pl; |
|||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */ |
|||
goto linkrel; |
|||
case DASM_REL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putrel: |
|||
n = *pl; |
|||
if (n < 0) { /* Label exists. Get label pos and store it. */ |
|||
b[pos] = -n; |
|||
} else { |
|||
linkrel: |
|||
b[pos] = n; /* Else link to rel chain, anchored at label. */ |
|||
*pl = pos; |
|||
} |
|||
pos++; |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel; |
|||
case DASM_LABEL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putlabel: |
|||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; |
|||
} |
|||
*pl = -pos; /* Label exists now. */ |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
break; |
|||
case DASM_IMM: case DASM_IMMS: |
|||
#ifdef DASM_CHECKS |
|||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I); |
|||
#endif |
|||
n >>= ((ins>>10)&31); |
|||
#ifdef DASM_CHECKS |
|||
if (ins & 0x8000) |
|||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I); |
|||
else |
|||
CK((n>>((ins>>5)&31)) == 0, RANGE_I); |
|||
#endif |
|||
b[pos++] = n; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
stop: |
|||
va_end(ap); |
|||
sec->pos = pos; |
|||
sec->ofs = ofs; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */ |
|||
int dasm_link(Dst_DECL, size_t *szp) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int secnum; |
|||
int ofs = 0; |
|||
|
|||
#ifdef DASM_CHECKS |
|||
*szp = 0; |
|||
if (D->status != DASM_S_OK) return D->status; |
|||
{ |
|||
int pc; |
|||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++) |
|||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc; |
|||
} |
|||
#endif |
|||
|
|||
{ /* Handle globals not defined in this translation unit. */ |
|||
int idx; |
|||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) { |
|||
int n = D->lglabels[idx]; |
|||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; } |
|||
} |
|||
} |
|||
|
|||
/* Combine all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->rbuf; |
|||
int pos = DASM_SEC2POS(secnum); |
|||
int lastpos = sec->pos; |
|||
|
|||
while (pos != lastpos) { |
|||
dasm_ActList p = D->actionlist + b[pos++]; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16) - 0xff00; |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: p++; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break; |
|||
case DASM_REL_LG: case DASM_REL_PC: pos++; break; |
|||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break; |
|||
case DASM_IMM: case DASM_IMMS: pos++; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
ofs += sec->ofs; /* Next section starts right after current section. */ |
|||
} |
|||
|
|||
D->codesize = ofs; /* Total size of all code sections */ |
|||
*szp = ofs; |
|||
return DASM_S_OK; |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 3: Encode sections. */ |
|||
int dasm_encode(Dst_DECL, void *buffer) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
char *base = (char *)buffer; |
|||
unsigned int *cp = (unsigned int *)buffer; |
|||
int secnum; |
|||
|
|||
/* Encode all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->buf; |
|||
int *endb = sec->rbuf + sec->pos; |
|||
|
|||
while (b != endb) { |
|||
dasm_ActList p = D->actionlist + *b++; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16) - 0xff00; |
|||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0; |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: *cp++ = *p++; break; |
|||
case DASM_REL_EXT: |
|||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1); |
|||
goto patchrel; |
|||
case DASM_ALIGN: |
|||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000; |
|||
break; |
|||
case DASM_REL_LG: |
|||
if (n < 0) { |
|||
n = (int)((ptrdiff_t)D->globals[-n] - (ptrdiff_t)cp); |
|||
goto patchrel; |
|||
} |
|||
/* fallthrough */ |
|||
case DASM_REL_PC: |
|||
CK(n >= 0, UNDEF_PC); |
|||
n = *DASM_POS2PTR(D, n); |
|||
if (ins & 2048) |
|||
n = (n + (int)(size_t)base) & 0x0fffffff; |
|||
else |
|||
n = n - (int)((char *)cp - base); |
|||
patchrel: { |
|||
unsigned int e = 16 + ((ins >> 12) & 15); |
|||
CK((n & 3) == 0 && |
|||
((n + ((ins & 2048) ? 0 : (1<<(e+1)))) >> (e+2)) == 0, RANGE_REL); |
|||
cp[-1] |= ((n>>2) & ((1<<e)-1)); |
|||
} |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n); |
|||
break; |
|||
case DASM_LABEL_PC: break; |
|||
case DASM_IMMS: |
|||
cp[-1] |= ((n>>3) & 4); n &= 0x1f; |
|||
/* fallthrough */ |
|||
case DASM_IMM: |
|||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31); |
|||
break; |
|||
default: *cp++ = ins; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
} |
|||
|
|||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */ |
|||
return DASM_S_PHASE; |
|||
return DASM_S_OK; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Get PC label offset. */ |
|||
int dasm_getpclabel(Dst_DECL, unsigned int pc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (pc*sizeof(int) < D->pcsize) { |
|||
int pos = D->pclabels[pc]; |
|||
if (pos < 0) return *DASM_POS2PTR(D, -pos); |
|||
if (pos > 0) return -1; /* Undefined. */ |
|||
} |
|||
return -2; /* Unused or out of range. */ |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
/* Optional sanity checker to call between isolated encoding steps. */ |
|||
int dasm_checkstep(Dst_DECL, int secmatch) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (D->status == DASM_S_OK) { |
|||
int i; |
|||
for (i = 1; i <= 9; i++) { |
|||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; } |
|||
D->lglabels[i] = 0; |
|||
} |
|||
} |
|||
if (D->status == DASM_S_OK && secmatch >= 0 && |
|||
D->section != &D->sections[secmatch]) |
|||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections); |
|||
return D->status; |
|||
} |
|||
#endif |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,12 @@ |
|||
------------------------------------------------------------------------------ |
|||
-- DynASM MIPS64 module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- See dynasm.lua for full copyright notice. |
|||
------------------------------------------------------------------------------ |
|||
-- This module just sets 64 bit mode for the combined MIPS/MIPS64 module. |
|||
-- All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
mips64 = true -- Using a global is an ugly, but effective solution. |
|||
return require("dasm_mips") |
@ -0,0 +1,423 @@ |
|||
/*
|
|||
** DynASM PPC/PPC64 encoding engine. |
|||
** Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
** Released under the MIT license. See dynasm.lua for full copyright notice. |
|||
*/ |
|||
|
|||
#include <stddef.h> |
|||
#include <stdarg.h> |
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#define DASM_ARCH "ppc" |
|||
|
|||
#ifndef DASM_EXTERN |
|||
#define DASM_EXTERN(a,b,c,d) 0 |
|||
#endif |
|||
|
|||
/* Action definitions. */ |
|||
enum { |
|||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT, |
|||
/* The following actions need a buffer position. */ |
|||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG, |
|||
/* The following actions also have an argument. */ |
|||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM, DASM_IMMSH, |
|||
DASM__MAX |
|||
}; |
|||
|
|||
/* Maximum number of section buffer positions for a single dasm_put() call. */ |
|||
#define DASM_MAXSECPOS 25 |
|||
|
|||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */ |
|||
#define DASM_S_OK 0x00000000 |
|||
#define DASM_S_NOMEM 0x01000000 |
|||
#define DASM_S_PHASE 0x02000000 |
|||
#define DASM_S_MATCH_SEC 0x03000000 |
|||
#define DASM_S_RANGE_I 0x11000000 |
|||
#define DASM_S_RANGE_SEC 0x12000000 |
|||
#define DASM_S_RANGE_LG 0x13000000 |
|||
#define DASM_S_RANGE_PC 0x14000000 |
|||
#define DASM_S_RANGE_REL 0x15000000 |
|||
#define DASM_S_UNDEF_LG 0x21000000 |
|||
#define DASM_S_UNDEF_PC 0x22000000 |
|||
|
|||
/* Macros to convert positions (8 bit section + 24 bit index). */ |
|||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff) |
|||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000) |
|||
#define DASM_SEC2POS(sec) ((sec)<<24) |
|||
#define DASM_POS2SEC(pos) ((pos)>>24) |
|||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) |
|||
|
|||
/* Action list type. */ |
|||
typedef const unsigned int *dasm_ActList; |
|||
|
|||
/* Per-section structure. */ |
|||
typedef struct dasm_Section { |
|||
int *rbuf; /* Biased buffer pointer (negative section bias). */ |
|||
int *buf; /* True buffer pointer. */ |
|||
size_t bsize; /* Buffer size in bytes. */ |
|||
int pos; /* Biased buffer position. */ |
|||
int epos; /* End of biased buffer position - max single put. */ |
|||
int ofs; /* Byte offset into section. */ |
|||
} dasm_Section; |
|||
|
|||
/* Core structure holding the DynASM encoding state. */ |
|||
struct dasm_State { |
|||
size_t psize; /* Allocated size of this structure. */ |
|||
dasm_ActList actionlist; /* Current actionlist pointer. */ |
|||
int *lglabels; /* Local/global chain/pos ptrs. */ |
|||
size_t lgsize; |
|||
int *pclabels; /* PC label chains/pos ptrs. */ |
|||
size_t pcsize; |
|||
void **globals; /* Array of globals (bias -10). */ |
|||
dasm_Section *section; /* Pointer to active section. */ |
|||
size_t codesize; /* Total size of all code sections. */ |
|||
int maxsection; /* 0 <= sectionidx < maxsection. */ |
|||
int status; /* Status code. */ |
|||
dasm_Section sections[1]; /* All sections. Alloc-extended. */ |
|||
}; |
|||
|
|||
/* The size of the core structure depends on the max. number of sections. */ |
|||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) |
|||
|
|||
|
|||
/* Initialize DynASM state. */ |
|||
void dasm_init(Dst_DECL, int maxsection) |
|||
{ |
|||
dasm_State *D; |
|||
size_t psz = 0; |
|||
int i; |
|||
Dst_REF = NULL; |
|||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); |
|||
D = Dst_REF; |
|||
D->psize = psz; |
|||
D->lglabels = NULL; |
|||
D->lgsize = 0; |
|||
D->pclabels = NULL; |
|||
D->pcsize = 0; |
|||
D->globals = NULL; |
|||
D->maxsection = maxsection; |
|||
for (i = 0; i < maxsection; i++) { |
|||
D->sections[i].buf = NULL; /* Need this for pass3. */ |
|||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); |
|||
D->sections[i].bsize = 0; |
|||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ |
|||
} |
|||
} |
|||
|
|||
/* Free DynASM state. */ |
|||
void dasm_free(Dst_DECL) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
for (i = 0; i < D->maxsection; i++) |
|||
if (D->sections[i].buf) |
|||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); |
|||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize); |
|||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize); |
|||
DASM_M_FREE(Dst, D, D->psize); |
|||
} |
|||
|
|||
/* Setup global label array. Must be called before dasm_setup(). */ |
|||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
D->globals = gl - 10; /* Negative bias to compensate for locals. */ |
|||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int)); |
|||
} |
|||
|
|||
/* Grow PC label array. Can be called after dasm_setup(), too. */ |
|||
void dasm_growpc(Dst_DECL, unsigned int maxpc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
size_t osz = D->pcsize; |
|||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int)); |
|||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz); |
|||
} |
|||
|
|||
/* Setup encoder. */ |
|||
void dasm_setup(Dst_DECL, const void *actionlist) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
D->actionlist = (dasm_ActList)actionlist; |
|||
D->status = DASM_S_OK; |
|||
D->section = &D->sections[0]; |
|||
memset((void *)D->lglabels, 0, D->lgsize); |
|||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize); |
|||
for (i = 0; i < D->maxsection; i++) { |
|||
D->sections[i].pos = DASM_SEC2POS(i); |
|||
D->sections[i].ofs = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) { \ |
|||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#define CKPL(kind, st) \ |
|||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ |
|||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#define CKPL(kind, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ |
|||
void dasm_put(Dst_DECL, int start, ...) |
|||
{ |
|||
va_list ap; |
|||
dasm_State *D = Dst_REF; |
|||
dasm_ActList p = D->actionlist + start; |
|||
dasm_Section *sec = D->section; |
|||
int pos = sec->pos, ofs = sec->ofs; |
|||
int *b; |
|||
|
|||
if (pos >= sec->epos) { |
|||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize, |
|||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int)); |
|||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos); |
|||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos); |
|||
} |
|||
|
|||
b = sec->rbuf; |
|||
b[pos++] = start; |
|||
|
|||
va_start(ap, start); |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
if (action >= DASM__MAX) { |
|||
ofs += 4; |
|||
} else { |
|||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0; |
|||
switch (action) { |
|||
case DASM_STOP: goto stop; |
|||
case DASM_SECTION: |
|||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC); |
|||
D->section = &D->sections[n]; goto stop; |
|||
case DASM_ESC: p++; ofs += 4; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break; |
|||
case DASM_REL_LG: |
|||
n = (ins & 2047) - 10; pl = D->lglabels + n; |
|||
/* Bkwd rel or global. */ |
|||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; } |
|||
pl += 10; n = *pl; |
|||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */ |
|||
goto linkrel; |
|||
case DASM_REL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putrel: |
|||
n = *pl; |
|||
if (n < 0) { /* Label exists. Get label pos and store it. */ |
|||
b[pos] = -n; |
|||
} else { |
|||
linkrel: |
|||
b[pos] = n; /* Else link to rel chain, anchored at label. */ |
|||
*pl = pos; |
|||
} |
|||
pos++; |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel; |
|||
case DASM_LABEL_PC: |
|||
pl = D->pclabels + n; CKPL(pc, PC); |
|||
putlabel: |
|||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; |
|||
} |
|||
*pl = -pos; /* Label exists now. */ |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
break; |
|||
case DASM_IMM: |
|||
#ifdef DASM_CHECKS |
|||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I); |
|||
#endif |
|||
n >>= ((ins>>10)&31); |
|||
#ifdef DASM_CHECKS |
|||
if (ins & 0x8000) |
|||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I); |
|||
else |
|||
CK((n>>((ins>>5)&31)) == 0, RANGE_I); |
|||
#endif |
|||
b[pos++] = n; |
|||
break; |
|||
case DASM_IMMSH: |
|||
CK((n >> 6) == 0, RANGE_I); |
|||
b[pos++] = n; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
stop: |
|||
va_end(ap); |
|||
sec->pos = pos; |
|||
sec->ofs = ofs; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */ |
|||
int dasm_link(Dst_DECL, size_t *szp) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int secnum; |
|||
int ofs = 0; |
|||
|
|||
#ifdef DASM_CHECKS |
|||
*szp = 0; |
|||
if (D->status != DASM_S_OK) return D->status; |
|||
{ |
|||
int pc; |
|||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++) |
|||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc; |
|||
} |
|||
#endif |
|||
|
|||
{ /* Handle globals not defined in this translation unit. */ |
|||
int idx; |
|||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) { |
|||
int n = D->lglabels[idx]; |
|||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; } |
|||
} |
|||
} |
|||
|
|||
/* Combine all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->rbuf; |
|||
int pos = DASM_SEC2POS(secnum); |
|||
int lastpos = sec->pos; |
|||
|
|||
while (pos != lastpos) { |
|||
dasm_ActList p = D->actionlist + b[pos++]; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: p++; break; |
|||
case DASM_REL_EXT: break; |
|||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break; |
|||
case DASM_REL_LG: case DASM_REL_PC: pos++; break; |
|||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break; |
|||
case DASM_IMM: case DASM_IMMSH: pos++; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
ofs += sec->ofs; /* Next section starts right after current section. */ |
|||
} |
|||
|
|||
D->codesize = ofs; /* Total size of all code sections */ |
|||
*szp = ofs; |
|||
return DASM_S_OK; |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 3: Encode sections. */ |
|||
int dasm_encode(Dst_DECL, void *buffer) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
char *base = (char *)buffer; |
|||
unsigned int *cp = (unsigned int *)buffer; |
|||
int secnum; |
|||
|
|||
/* Encode all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->buf; |
|||
int *endb = sec->rbuf + sec->pos; |
|||
|
|||
while (b != endb) { |
|||
dasm_ActList p = D->actionlist + *b++; |
|||
while (1) { |
|||
unsigned int ins = *p++; |
|||
unsigned int action = (ins >> 16); |
|||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0; |
|||
switch (action) { |
|||
case DASM_STOP: case DASM_SECTION: goto stop; |
|||
case DASM_ESC: *cp++ = *p++; break; |
|||
case DASM_REL_EXT: |
|||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1) - 4; |
|||
goto patchrel; |
|||
case DASM_ALIGN: |
|||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000; |
|||
break; |
|||
case DASM_REL_LG: |
|||
if (n < 0) { |
|||
n = (int)((ptrdiff_t)D->globals[-n] - (ptrdiff_t)cp); |
|||
goto patchrel; |
|||
} |
|||
/* fallthrough */ |
|||
case DASM_REL_PC: |
|||
CK(n >= 0, UNDEF_PC); |
|||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base); |
|||
patchrel: |
|||
CK((n & 3) == 0 && |
|||
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >> |
|||
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL); |
|||
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc)); |
|||
break; |
|||
case DASM_LABEL_LG: |
|||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n); |
|||
break; |
|||
case DASM_LABEL_PC: break; |
|||
case DASM_IMM: |
|||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31); |
|||
break; |
|||
case DASM_IMMSH: |
|||
cp[-1] |= (ins & 1) ? ((n&31)<<11)|((n&32)>>4) : ((n&31)<<6)|(n&32); |
|||
break; |
|||
default: *cp++ = ins; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
} |
|||
|
|||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */ |
|||
return DASM_S_PHASE; |
|||
return DASM_S_OK; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Get PC label offset. */ |
|||
int dasm_getpclabel(Dst_DECL, unsigned int pc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (pc*sizeof(int) < D->pcsize) { |
|||
int pos = D->pclabels[pc]; |
|||
if (pos < 0) return *DASM_POS2PTR(D, -pos); |
|||
if (pos > 0) return -1; /* Undefined. */ |
|||
} |
|||
return -2; /* Unused or out of range. */ |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
/* Optional sanity checker to call between isolated encoding steps. */ |
|||
int dasm_checkstep(Dst_DECL, int secmatch) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (D->status == DASM_S_OK) { |
|||
int i; |
|||
for (i = 1; i <= 9; i++) { |
|||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; } |
|||
D->lglabels[i] = 0; |
|||
} |
|||
} |
|||
if (D->status == DASM_S_OK && secmatch >= 0 && |
|||
D->section != &D->sections[secmatch]) |
|||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections); |
|||
return D->status; |
|||
} |
|||
#endif |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,83 @@ |
|||
/*
|
|||
** DynASM encoding engine prototypes. |
|||
** Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
** Released under the MIT license. See dynasm.lua for full copyright notice. |
|||
*/ |
|||
|
|||
#ifndef _DASM_PROTO_H |
|||
#define _DASM_PROTO_H |
|||
|
|||
#include <stddef.h> |
|||
#include <stdarg.h> |
|||
|
|||
#define DASM_IDENT "DynASM 1.5.0" |
|||
#define DASM_VERSION 10500 /* 1.5.0 */ |
|||
|
|||
#ifndef Dst_DECL |
|||
#define Dst_DECL dasm_State **Dst |
|||
#endif |
|||
|
|||
#ifndef Dst_REF |
|||
#define Dst_REF (*Dst) |
|||
#endif |
|||
|
|||
#ifndef DASM_FDEF |
|||
#define DASM_FDEF extern |
|||
#endif |
|||
|
|||
#ifndef DASM_M_GROW |
|||
#define DASM_M_GROW(ctx, t, p, sz, need) \ |
|||
do { \ |
|||
size_t _sz = (sz), _need = (need); \ |
|||
if (_sz < _need) { \ |
|||
if (_sz < 16) _sz = 16; \ |
|||
while (_sz < _need) _sz += _sz; \ |
|||
(p) = (t *)realloc((p), _sz); \ |
|||
if ((p) == NULL) exit(1); \ |
|||
(sz) = _sz; \ |
|||
} \ |
|||
} while(0) |
|||
#endif |
|||
|
|||
#ifndef DASM_M_FREE |
|||
#define DASM_M_FREE(ctx, p, sz) free(p) |
|||
#endif |
|||
|
|||
/* Internal DynASM encoder state. */ |
|||
typedef struct dasm_State dasm_State; |
|||
|
|||
|
|||
/* Initialize and free DynASM state. */ |
|||
DASM_FDEF void dasm_init(Dst_DECL, int maxsection); |
|||
DASM_FDEF void dasm_free(Dst_DECL); |
|||
|
|||
/* Setup global array. Must be called before dasm_setup(). */ |
|||
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl); |
|||
|
|||
/* Grow PC label array. Can be called after dasm_setup(), too. */ |
|||
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc); |
|||
|
|||
/* Setup encoder. */ |
|||
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist); |
|||
|
|||
/* Feed encoder with actions. Calls are generated by pre-processor. */ |
|||
DASM_FDEF void dasm_put(Dst_DECL, int start, ...); |
|||
|
|||
/* Link sections and return the resulting size. */ |
|||
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp); |
|||
|
|||
/* Encode sections into buffer. */ |
|||
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer); |
|||
|
|||
/* Get PC label offset. */ |
|||
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc); |
|||
|
|||
#ifdef DASM_CHECKS |
|||
/* Optional sanity checker to call between isolated encoding steps. */ |
|||
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch); |
|||
#else |
|||
#define dasm_checkstep(a, b) 0 |
|||
#endif |
|||
|
|||
|
|||
#endif /* _DASM_PROTO_H */ |
@ -0,0 +1,12 @@ |
|||
------------------------------------------------------------------------------ |
|||
-- DynASM x64 module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- See dynasm.lua for full copyright notice. |
|||
------------------------------------------------------------------------------ |
|||
-- This module just sets 64 bit mode for the combined x86/x64 module. |
|||
-- All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
x64 = true -- Using a global is an ugly, but effective solution. |
|||
return require("dasm_x86") |
@ -0,0 +1,528 @@ |
|||
/*
|
|||
** DynASM x86 encoding engine. |
|||
** Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
** Released under the MIT license. See dynasm.lua for full copyright notice. |
|||
*/ |
|||
|
|||
#include <stddef.h> |
|||
#include <stdarg.h> |
|||
#include <string.h> |
|||
#include <stdlib.h> |
|||
|
|||
#define DASM_ARCH "x86" |
|||
|
|||
#ifndef DASM_EXTERN |
|||
#define DASM_EXTERN(a,b,c,d) 0 |
|||
#endif |
|||
|
|||
/* Action definitions. DASM_STOP must be 255. */ |
|||
enum { |
|||
DASM_DISP = 233, |
|||
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB, |
|||
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC, |
|||
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN, |
|||
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP |
|||
}; |
|||
|
|||
/* Maximum number of section buffer positions for a single dasm_put() call. */ |
|||
#define DASM_MAXSECPOS 25 |
|||
|
|||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */ |
|||
#define DASM_S_OK 0x00000000 |
|||
#define DASM_S_NOMEM 0x01000000 |
|||
#define DASM_S_PHASE 0x02000000 |
|||
#define DASM_S_MATCH_SEC 0x03000000 |
|||
#define DASM_S_RANGE_I 0x11000000 |
|||
#define DASM_S_RANGE_SEC 0x12000000 |
|||
#define DASM_S_RANGE_LG 0x13000000 |
|||
#define DASM_S_RANGE_PC 0x14000000 |
|||
#define DASM_S_RANGE_VREG 0x15000000 |
|||
#define DASM_S_UNDEF_L 0x21000000 |
|||
#define DASM_S_UNDEF_PC 0x22000000 |
|||
|
|||
/* Macros to convert positions (8 bit section + 24 bit index). */ |
|||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff) |
|||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000) |
|||
#define DASM_SEC2POS(sec) ((sec)<<24) |
|||
#define DASM_POS2SEC(pos) ((pos)>>24) |
|||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos)) |
|||
|
|||
/* Action list type. */ |
|||
typedef const unsigned char *dasm_ActList; |
|||
|
|||
/* Per-section structure. */ |
|||
typedef struct dasm_Section { |
|||
int *rbuf; /* Biased buffer pointer (negative section bias). */ |
|||
int *buf; /* True buffer pointer. */ |
|||
size_t bsize; /* Buffer size in bytes. */ |
|||
int pos; /* Biased buffer position. */ |
|||
int epos; /* End of biased buffer position - max single put. */ |
|||
int ofs; /* Byte offset into section. */ |
|||
} dasm_Section; |
|||
|
|||
/* Core structure holding the DynASM encoding state. */ |
|||
struct dasm_State { |
|||
size_t psize; /* Allocated size of this structure. */ |
|||
dasm_ActList actionlist; /* Current actionlist pointer. */ |
|||
int *lglabels; /* Local/global chain/pos ptrs. */ |
|||
size_t lgsize; |
|||
int *pclabels; /* PC label chains/pos ptrs. */ |
|||
size_t pcsize; |
|||
void **globals; /* Array of globals (bias -10). */ |
|||
dasm_Section *section; /* Pointer to active section. */ |
|||
size_t codesize; /* Total size of all code sections. */ |
|||
int maxsection; /* 0 <= sectionidx < maxsection. */ |
|||
int status; /* Status code. */ |
|||
dasm_Section sections[1]; /* All sections. Alloc-extended. */ |
|||
}; |
|||
|
|||
/* The size of the core structure depends on the max. number of sections. */ |
|||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section)) |
|||
|
|||
|
|||
/* Initialize DynASM state. */ |
|||
void dasm_init(Dst_DECL, int maxsection) |
|||
{ |
|||
dasm_State *D; |
|||
size_t psz = 0; |
|||
int i; |
|||
Dst_REF = NULL; |
|||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection)); |
|||
D = Dst_REF; |
|||
D->psize = psz; |
|||
D->lglabels = NULL; |
|||
D->lgsize = 0; |
|||
D->pclabels = NULL; |
|||
D->pcsize = 0; |
|||
D->globals = NULL; |
|||
D->maxsection = maxsection; |
|||
for (i = 0; i < maxsection; i++) { |
|||
D->sections[i].buf = NULL; /* Need this for pass3. */ |
|||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i); |
|||
D->sections[i].bsize = 0; |
|||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */ |
|||
} |
|||
} |
|||
|
|||
/* Free DynASM state. */ |
|||
void dasm_free(Dst_DECL) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
for (i = 0; i < D->maxsection; i++) |
|||
if (D->sections[i].buf) |
|||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize); |
|||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize); |
|||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize); |
|||
DASM_M_FREE(Dst, D, D->psize); |
|||
} |
|||
|
|||
/* Setup global label array. Must be called before dasm_setup(). */ |
|||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
D->globals = gl - 10; /* Negative bias to compensate for locals. */ |
|||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int)); |
|||
} |
|||
|
|||
/* Grow PC label array. Can be called after dasm_setup(), too. */ |
|||
void dasm_growpc(Dst_DECL, unsigned int maxpc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
size_t osz = D->pcsize; |
|||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int)); |
|||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz); |
|||
} |
|||
|
|||
/* Setup encoder. */ |
|||
void dasm_setup(Dst_DECL, const void *actionlist) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int i; |
|||
D->actionlist = (dasm_ActList)actionlist; |
|||
D->status = DASM_S_OK; |
|||
D->section = &D->sections[0]; |
|||
memset((void *)D->lglabels, 0, D->lgsize); |
|||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize); |
|||
for (i = 0; i < D->maxsection; i++) { |
|||
D->sections[i].pos = DASM_SEC2POS(i); |
|||
D->sections[i].ofs = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
#ifdef DASM_CHECKS |
|||
#define CK(x, st) \ |
|||
do { if (!(x)) { \ |
|||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0) |
|||
#define CKPL(kind, st) \ |
|||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \ |
|||
D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0) |
|||
#else |
|||
#define CK(x, st) ((void)0) |
|||
#define CKPL(kind, st) ((void)0) |
|||
#endif |
|||
|
|||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */ |
|||
void dasm_put(Dst_DECL, int start, ...) |
|||
{ |
|||
va_list ap; |
|||
dasm_State *D = Dst_REF; |
|||
dasm_ActList p = D->actionlist + start; |
|||
dasm_Section *sec = D->section; |
|||
int pos = sec->pos, ofs = sec->ofs, mrm = -1; |
|||
int *b; |
|||
|
|||
if (pos >= sec->epos) { |
|||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize, |
|||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int)); |
|||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos); |
|||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos); |
|||
} |
|||
|
|||
b = sec->rbuf; |
|||
b[pos++] = start; |
|||
|
|||
va_start(ap, start); |
|||
while (1) { |
|||
int action = *p++; |
|||
if (action < DASM_DISP) { |
|||
ofs++; |
|||
} else if (action <= DASM_REL_A) { |
|||
int n = va_arg(ap, int); |
|||
b[pos++] = n; |
|||
switch (action) { |
|||
case DASM_DISP: |
|||
if (n == 0) { if (mrm < 0) mrm = p[-2]; if ((mrm&7) != 5) break; } |
|||
/* fallthrough */ |
|||
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */ |
|||
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */ |
|||
case DASM_IMM_D: ofs += 4; break; |
|||
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob; |
|||
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break; |
|||
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob; /* fallthrough */ |
|||
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break; |
|||
case DASM_SPACE: p++; ofs += n; break; |
|||
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */ |
|||
case DASM_VREG: CK((n&-16) == 0 && (n != 4 || (*p>>5) != 2), RANGE_VREG); |
|||
if (*p < 0x40 && p[1] == DASM_DISP) mrm = n; |
|||
if (*p < 0x20 && (n&7) == 4) ofs++; |
|||
switch ((*p++ >> 3) & 3) { |
|||
case 3: n |= b[pos-3]; /* fallthrough */ |
|||
case 2: n |= b[pos-2]; /* fallthrough */ |
|||
case 1: if (n <= 7) { b[pos-1] |= 0x10; ofs--; } |
|||
} |
|||
continue; |
|||
} |
|||
mrm = -1; |
|||
} else { |
|||
int *pl, n; |
|||
switch (action) { |
|||
case DASM_REL_LG: |
|||
case DASM_IMM_LG: |
|||
n = *p++; pl = D->lglabels + n; |
|||
/* Bkwd rel or global. */ |
|||
if (n <= 246) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; } |
|||
pl -= 246; n = *pl; |
|||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */ |
|||
goto linkrel; |
|||
case DASM_REL_PC: |
|||
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC); |
|||
putrel: |
|||
n = *pl; |
|||
if (n < 0) { /* Label exists. Get label pos and store it. */ |
|||
b[pos] = -n; |
|||
} else { |
|||
linkrel: |
|||
b[pos] = n; /* Else link to rel chain, anchored at label. */ |
|||
*pl = pos; |
|||
} |
|||
pos++; |
|||
ofs += 4; /* Maximum offset needed. */ |
|||
if (action == DASM_REL_LG || action == DASM_REL_PC) { |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
} else if (sizeof(ptrdiff_t) == 8) { |
|||
ofs += 4; |
|||
} |
|||
break; |
|||
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel; |
|||
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC); |
|||
putlabel: |
|||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; } |
|||
*pl = -pos; /* Label exists now. */ |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
break; |
|||
case DASM_ALIGN: |
|||
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */ |
|||
b[pos++] = ofs; /* Store pass1 offset estimate. */ |
|||
break; |
|||
case DASM_EXTERN: p += 2; ofs += 4; break; |
|||
case DASM_ESC: p++; ofs++; break; |
|||
case DASM_MARK: mrm = p[-2]; break; |
|||
case DASM_SECTION: |
|||
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n]; |
|||
case DASM_STOP: goto stop; |
|||
} |
|||
} |
|||
} |
|||
stop: |
|||
va_end(ap); |
|||
sec->pos = pos; |
|||
sec->ofs = ofs; |
|||
} |
|||
#undef CK |
|||
|
|||
/* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */ |
|||
int dasm_link(Dst_DECL, size_t *szp) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
int secnum; |
|||
int ofs = 0; |
|||
|
|||
#ifdef DASM_CHECKS |
|||
*szp = 0; |
|||
if (D->status != DASM_S_OK) return D->status; |
|||
{ |
|||
int pc; |
|||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++) |
|||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc; |
|||
} |
|||
#endif |
|||
|
|||
{ /* Handle globals not defined in this translation unit. */ |
|||
int idx; |
|||
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) { |
|||
int n = D->lglabels[idx]; |
|||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */ |
|||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; } |
|||
} |
|||
} |
|||
|
|||
/* Combine all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->rbuf; |
|||
int pos = DASM_SEC2POS(secnum); |
|||
int lastpos = sec->pos; |
|||
|
|||
while (pos != lastpos) { |
|||
dasm_ActList p = D->actionlist + b[pos++]; |
|||
int op = 0; |
|||
while (1) { |
|||
int action = *p++; |
|||
switch (action) { |
|||
case DASM_REL_LG: p++; |
|||
/* fallthrough */ |
|||
case DASM_REL_PC: { |
|||
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0); |
|||
if (shrink) { /* Shrinkable branch opcode? */ |
|||
int lofs, lpos = b[pos]; |
|||
if (lpos < 0) goto noshrink; /* Ext global? */ |
|||
lofs = *DASM_POS2PTR(D, lpos); |
|||
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */ |
|||
int i; |
|||
for (i = secnum; i < DASM_POS2SEC(lpos); i++) |
|||
lofs += D->sections[i].ofs; |
|||
} else { |
|||
lofs -= ofs; /* Bkwd label: unfix offset. */ |
|||
} |
|||
lofs -= b[pos+1]; /* Short branch ok? */ |
|||
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */ |
|||
else { noshrink: shrink = 0; } /* No, cannot shrink op. */ |
|||
} |
|||
b[pos+1] = shrink; |
|||
pos += 2; |
|||
break; |
|||
} |
|||
/* fallthrough */ |
|||
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++; |
|||
/* fallthrough */ |
|||
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W: |
|||
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB: |
|||
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break; |
|||
case DASM_LABEL_LG: p++; |
|||
/* fallthrough */ |
|||
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */ |
|||
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */ |
|||
case DASM_EXTERN: p += 2; break; |
|||
case DASM_ESC: op = *p++; break; |
|||
case DASM_MARK: break; |
|||
case DASM_SECTION: case DASM_STOP: goto stop; |
|||
default: op = action; break; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
ofs += sec->ofs; /* Next section starts right after current section. */ |
|||
} |
|||
|
|||
D->codesize = ofs; /* Total size of all code sections */ |
|||
*szp = ofs; |
|||
return DASM_S_OK; |
|||
} |
|||
|
|||
#define dasmb(x) *cp++ = (unsigned char)(x) |
|||
#ifndef DASM_ALIGNED_WRITES |
|||
#define dasmw(x) \ |
|||
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0) |
|||
#define dasmd(x) \ |
|||
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0) |
|||
#define dasmq(x) \ |
|||
do { *((unsigned long long *)cp) = (unsigned long long)(x); cp+=8; } while (0) |
|||
#else |
|||
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0) |
|||
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0) |
|||
#define dasmq(x) do { dasmd(x); dasmd((x)>>32); } while (0) |
|||
#endif |
|||
static unsigned char *dasma_(unsigned char *cp, ptrdiff_t x) |
|||
{ |
|||
if (sizeof(ptrdiff_t) == 8) |
|||
dasmq((unsigned long long)x); |
|||
else |
|||
dasmd((unsigned int)x); |
|||
return cp; |
|||
} |
|||
#define dasma(x) (cp = dasma_(cp, (x))) |
|||
|
|||
/* Pass 3: Encode sections. */ |
|||
int dasm_encode(Dst_DECL, void *buffer) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
unsigned char *base = (unsigned char *)buffer; |
|||
unsigned char *cp = base; |
|||
int secnum; |
|||
|
|||
/* Encode all code sections. No support for data sections (yet). */ |
|||
for (secnum = 0; secnum < D->maxsection; secnum++) { |
|||
dasm_Section *sec = D->sections + secnum; |
|||
int *b = sec->buf; |
|||
int *endb = sec->rbuf + sec->pos; |
|||
|
|||
while (b != endb) { |
|||
dasm_ActList p = D->actionlist + *b++; |
|||
unsigned char *mark = NULL; |
|||
while (1) { |
|||
int action = *p++; |
|||
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0; |
|||
switch (action) { |
|||
case DASM_DISP: if (!mark) mark = cp; { |
|||
unsigned char *mm = mark; |
|||
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL; |
|||
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7; |
|||
if (mrm != 5) { mm[-1] -= 0x80; break; } } |
|||
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40; |
|||
} |
|||
/* fallthrough */ |
|||
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break; |
|||
case DASM_IMM_DB: if (((n+128)&-256) == 0) { |
|||
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb; |
|||
} else mark = NULL; |
|||
/* fallthrough */ |
|||
case DASM_IMM_D: wd: dasmd(n); break; |
|||
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL; |
|||
/* fallthrough */ |
|||
case DASM_IMM_W: dasmw(n); break; |
|||
case DASM_VREG: { |
|||
int t = *p++; |
|||
unsigned char *ex = cp - (t&7); |
|||
if ((n & 8) && t < 0xa0) { |
|||
if (*ex & 0x80) ex[1] ^= 0x20 << (t>>6); else *ex ^= 1 << (t>>6); |
|||
n &= 7; |
|||
} else if (n & 0x10) { |
|||
if (*ex & 0x80) { |
|||
*ex = 0xc5; ex[1] = (ex[1] & 0x80) | ex[2]; ex += 2; |
|||
} |
|||
while (++ex < cp) ex[-1] = *ex; |
|||
if (mark) mark--; |
|||
cp--; |
|||
n &= 7; |
|||
} |
|||
if (t >= 0xc0) n <<= 4; |
|||
else if (t >= 0x40) n <<= 3; |
|||
else if (n == 4 && t < 0x20) { cp[-1] ^= n; *cp++ = 0x20; } |
|||
cp[-1] ^= n; |
|||
break; |
|||
} |
|||
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc; |
|||
b++; n = (int)(ptrdiff_t)D->globals[-n]; |
|||
/* fallthrough */ |
|||
case DASM_REL_A: rel_a: |
|||
n -= (unsigned int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */ |
|||
case DASM_REL_PC: rel_pc: { |
|||
int shrink = *b++; |
|||
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; } |
|||
n = *pb - ((int)(cp-base) + 4-shrink); |
|||
if (shrink == 0) goto wd; |
|||
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb; |
|||
goto wb; |
|||
} |
|||
case DASM_IMM_LG: |
|||
p++; |
|||
if (n < 0) { dasma((ptrdiff_t)D->globals[-n]); break; } |
|||
/* fallthrough */ |
|||
case DASM_IMM_PC: { |
|||
int *pb = DASM_POS2PTR(D, n); |
|||
dasma(*pb < 0 ? (ptrdiff_t)pb[1] : (*pb + (ptrdiff_t)base)); |
|||
break; |
|||
} |
|||
case DASM_LABEL_LG: { |
|||
int idx = *p++; |
|||
if (idx >= 10) |
|||
D->globals[idx] = (void *)(base + (*p == DASM_SETLABEL ? *b : n)); |
|||
break; |
|||
} |
|||
case DASM_LABEL_PC: case DASM_SETLABEL: break; |
|||
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; } |
|||
case DASM_ALIGN: |
|||
n = *p++; |
|||
while (((cp-base) & n)) *cp++ = 0x90; /* nop */ |
|||
break; |
|||
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd; |
|||
case DASM_MARK: mark = cp; break; |
|||
case DASM_ESC: action = *p++; |
|||
/* fallthrough */ |
|||
default: *cp++ = action; break; |
|||
case DASM_SECTION: case DASM_STOP: goto stop; |
|||
} |
|||
} |
|||
stop: (void)0; |
|||
} |
|||
} |
|||
|
|||
if (base + D->codesize != cp) /* Check for phase errors. */ |
|||
return DASM_S_PHASE; |
|||
return DASM_S_OK; |
|||
} |
|||
|
|||
/* Get PC label offset. */ |
|||
int dasm_getpclabel(Dst_DECL, unsigned int pc) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (pc*sizeof(int) < D->pcsize) { |
|||
int pos = D->pclabels[pc]; |
|||
if (pos < 0) return *DASM_POS2PTR(D, -pos); |
|||
if (pos > 0) return -1; /* Undefined. */ |
|||
} |
|||
return -2; /* Unused or out of range. */ |
|||
} |
|||
|
|||
#ifdef DASM_CHECKS |
|||
/* Optional sanity checker to call between isolated encoding steps. */ |
|||
int dasm_checkstep(Dst_DECL, int secmatch) |
|||
{ |
|||
dasm_State *D = Dst_REF; |
|||
if (D->status == DASM_S_OK) { |
|||
int i; |
|||
for (i = 1; i <= 9; i++) { |
|||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; } |
|||
D->lglabels[i] = 0; |
|||
} |
|||
} |
|||
if (D->status == DASM_S_OK && secmatch >= 0 && |
|||
D->section != &D->sections[secmatch]) |
|||
D->status = DASM_S_MATCH_SEC|(int)(D->section-D->sections); |
|||
return D->status; |
|||
} |
|||
#endif |
|||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,88 @@ |
|||
.TH luajit 1 "" "" "LuaJIT documentation" |
|||
.SH NAME |
|||
luajit \- Just-In-Time Compiler for the Lua Language |
|||
\fB |
|||
.SH SYNOPSIS |
|||
.B luajit |
|||
[\fIoptions\fR]... [\fIscript\fR [\fIargs\fR]...] |
|||
.SH "WEB SITE" |
|||
.IR https://luajit.org |
|||
.SH DESCRIPTION |
|||
.PP |
|||
This is the command-line program to run Lua programs with \fBLuaJIT\fR. |
|||
.PP |
|||
\fBLuaJIT\fR is a just-in-time (JIT) compiler for the Lua language. |
|||
The virtual machine (VM) is based on a fast interpreter combined with |
|||
a trace compiler. It can significantly improve the performance of Lua programs. |
|||
.PP |
|||
\fBLuaJIT\fR is API\- and ABI-compatible with the VM of the standard |
|||
Lua\ 5.1 interpreter. When embedding the VM into an application, |
|||
the built library can be used as a drop-in replacement. |
|||
.SH OPTIONS |
|||
.TP |
|||
.BI "\-e " chunk |
|||
Run the given chunk of Lua code. |
|||
.TP |
|||
.BI "\-l " library |
|||
Load the named library, just like \fBrequire("\fR\fIlibrary\fR\fB")\fR. |
|||
.TP |
|||
.BI "\-b " ... |
|||
Save or list bytecode. Run without arguments to get help on options. |
|||
.TP |
|||
.BI "\-j " command |
|||
Perform LuaJIT control command (optional space after \fB\-j\fR). |
|||
.TP |
|||
.BI "\-O" [opt] |
|||
Control LuaJIT optimizations. |
|||
.TP |
|||
.B "\-i" |
|||
Run in interactive mode. |
|||
.TP |
|||
.B "\-v" |
|||
Show \fBLuaJIT\fR version. |
|||
.TP |
|||
.B "\-E" |
|||
Ignore environment variables. |
|||
.TP |
|||
.B "\-\-" |
|||
Stop processing options. |
|||
.TP |
|||
.B "\-" |
|||
Read script from stdin instead. |
|||
.PP |
|||
After all options are processed, the given \fIscript\fR is run. |
|||
The arguments are passed in the global \fIarg\fR table. |
|||
.PP |
|||
Interactive mode is only entered, if no \fIscript\fR and no \fB\-e\fR |
|||
option is given. Interactive mode can be left with EOF (\fICtrl\-Z\fB). |
|||
.SH EXAMPLES |
|||
.TP |
|||
luajit hello.lua world |
|||
|
|||
Prints "Hello world", assuming \fIhello.lua\fR contains: |
|||
.br |
|||
print("Hello", arg[1]) |
|||
.TP |
|||
luajit \-e "local x=0; for i=1,1e9 do x=x+i end; print(x)" |
|||
|
|||
Calculates the sum of the numbers from 1 to 1000000000. |
|||
.br |
|||
And finishes in a reasonable amount of time, too. |
|||
.TP |
|||
luajit \-jv \-e "for i=1,10 do for j=1,10 do for k=1,100 do end end end" |
|||
|
|||
Runs some nested loops and shows the resulting traces. |
|||
.SH COPYRIGHT |
|||
.PP |
|||
\fBLuaJIT\fR is Copyright \(co 2005-2022 Mike Pall. |
|||
.br |
|||
\fBLuaJIT\fR is open source software, released under the MIT license. |
|||
.SH SEE ALSO |
|||
.PP |
|||
More details in the provided HTML docs or at: |
|||
.IR https://luajit.org |
|||
.br |
|||
More about the Lua language can be found at: |
|||
.IR https://lua.org/docs.html |
|||
.PP |
|||
lua(1) |
@ -0,0 +1,25 @@ |
|||
# Package information for LuaJIT to be used by pkg-config. |
|||
majver=2 |
|||
minver=1 |
|||
relver=0 |
|||
version=${majver}.${minver}.${relver}-beta3 |
|||
abiver=5.1 |
|||
|
|||
prefix=/usr/local |
|||
multilib=lib |
|||
exec_prefix=${prefix} |
|||
libdir=${exec_prefix}/${multilib} |
|||
libname=luajit-${abiver} |
|||
includedir=${prefix}/include/luajit-${majver}.${minver} |
|||
|
|||
INSTALL_LMOD=${prefix}/share/lua/${abiver} |
|||
INSTALL_CMOD=${prefix}/${multilib}/lua/${abiver} |
|||
|
|||
Name: LuaJIT |
|||
Description: Just-in-time compiler for Lua |
|||
URL: https://luajit.org |
|||
Version: ${version} |
|||
Requires: |
|||
Libs: -L${libdir} -l${libname} |
|||
Libs.private: -Wl,-E -lm -ldl |
|||
Cflags: -I${includedir} |
@ -0,0 +1,7 @@ |
|||
luajit |
|||
lj_bcdef.h |
|||
lj_ffdef.h |
|||
lj_libdef.h |
|||
lj_recdef.h |
|||
lj_folddef.h |
|||
lj_vm.[sS] |
@ -0,0 +1,726 @@ |
|||
##############################################################################
|
|||
# LuaJIT Makefile. Requires GNU Make.
|
|||
#
|
|||
# Please read doc/install.html before changing any variables!
|
|||
#
|
|||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
|||
# Also works with MinGW and Cygwin on Windows.
|
|||
# Please check msvcbuild.bat for building with MSVC on Windows.
|
|||
#
|
|||
# Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h
|
|||
##############################################################################
|
|||
|
|||
MAJVER= 2 |
|||
MINVER= 1 |
|||
RELVER= 0 |
|||
ABIVER= 5.1 |
|||
NODOTABIVER= 51 |
|||
|
|||
##############################################################################
|
|||
############################# COMPILER OPTIONS #############################
|
|||
##############################################################################
|
|||
# These options mainly affect the speed of the JIT compiler itself, not the
|
|||
# speed of the JIT-compiled code. Turn any of the optional settings on by
|
|||
# removing the '#' in front of them. Make sure you force a full recompile
|
|||
# with "make clean", followed by "make" if you change any options.
|
|||
#
|
|||
DEFAULT_CC = gcc |
|||
#
|
|||
# LuaJIT builds as a native 32 or 64 bit binary by default.
|
|||
CC= $(DEFAULT_CC) |
|||
#
|
|||
# Use this if you want to force a 32 bit build on a 64 bit multilib OS.
|
|||
#CC= $(DEFAULT_CC) -m32
|
|||
#
|
|||
# Since the assembler part does NOT maintain a frame pointer, it's pointless
|
|||
# to slow down the C part by not omitting it. Debugging, tracebacks and
|
|||
# unwinding are not affected -- the assembler part has frame unwind
|
|||
# information and GCC emits it where needed (x64) or with -g (see CCDEBUG).
|
|||
CCOPT= -O2 -fomit-frame-pointer |
|||
# Use this if you want to generate a smaller binary (but it's slower):
|
|||
#CCOPT= -Os -fomit-frame-pointer
|
|||
# Note: it's no longer recommended to use -O3 with GCC 4.x.
|
|||
# The I-Cache bloat usually outweighs the benefits from aggressive inlining.
|
|||
#
|
|||
# Target-specific compiler options:
|
|||
#
|
|||
# x86/x64 only: For GCC 4.2 or higher and if you don't intend to distribute
|
|||
# the binaries to a different machine you could also use: -march=native
|
|||
#
|
|||
CCOPT_x86= -march=i686 -msse -msse2 -mfpmath=sse |
|||
CCOPT_x64= |
|||
CCOPT_arm= |
|||
CCOPT_arm64= |
|||
CCOPT_ppc= |
|||
CCOPT_mips= |
|||
#
|
|||
CCDEBUG= |
|||
# Uncomment the next line to generate debug information:
|
|||
#CCDEBUG= -g
|
|||
#
|
|||
CCWARN= -Wall |
|||
# Uncomment the next line to enable more warnings:
|
|||
#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith
|
|||
#
|
|||
##############################################################################
|
|||
|
|||
##############################################################################
|
|||
################################ BUILD MODE ################################
|
|||
##############################################################################
|
|||
# The default build mode is mixed mode on POSIX. On Windows this is the same
|
|||
# as dynamic mode.
|
|||
#
|
|||
# Mixed mode creates a static + dynamic library and a statically linked luajit.
|
|||
BUILDMODE= mixed |
|||
#
|
|||
# Static mode creates a static library and a statically linked luajit.
|
|||
#BUILDMODE= static
|
|||
#
|
|||
# Dynamic mode creates a dynamic library and a dynamically linked luajit.
|
|||
# Note: this executable will only run when the library is installed!
|
|||
#BUILDMODE= dynamic
|
|||
#
|
|||
##############################################################################
|
|||
|
|||
##############################################################################
|
|||
################################# FEATURES #################################
|
|||
##############################################################################
|
|||
# Enable/disable these features as needed, but make sure you force a full
|
|||
# recompile with "make clean", followed by "make".
|
|||
XCFLAGS= |
|||
#
|
|||
# Permanently disable the FFI extension to reduce the size of the LuaJIT
|
|||
# executable. But please consider that the FFI library is compiled-in,
|
|||
# but NOT loaded by default. It only allocates any memory, if you actually
|
|||
# make use of it.
|
|||
#XCFLAGS+= -DLUAJIT_DISABLE_FFI
|
|||
#
|
|||
# Features from Lua 5.2 that are unlikely to break existing code are
|
|||
# enabled by default. Some other features that *might* break some existing
|
|||
# code (e.g. __pairs or os.execute() return values) can be enabled here.
|
|||
# Note: this does not provide full compatibility with Lua 5.2 at this time.
|
|||
#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT
|
|||
#
|
|||
# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter.
|
|||
#XCFLAGS+= -DLUAJIT_DISABLE_JIT
|
|||
#
|
|||
# Some architectures (e.g. PPC) can use either single-number (1) or
|
|||
# dual-number (2) mode. Uncomment one of these lines to override the
|
|||
# default mode. Please see LJ_ARCH_NUMMODE in lj_arch.h for details.
|
|||
#XCFLAGS+= -DLUAJIT_NUMMODE=1
|
|||
#XCFLAGS+= -DLUAJIT_NUMMODE=2
|
|||
#
|
|||
# Disable LJ_GC64 mode for x64.
|
|||
#XCFLAGS+= -DLUAJIT_DISABLE_GC64
|
|||
#
|
|||
##############################################################################
|
|||
|
|||
##############################################################################
|
|||
############################ DEBUGGING SUPPORT #############################
|
|||
##############################################################################
|
|||
# Enable these options as needed, but make sure you force a full recompile
|
|||
# with "make clean", followed by "make".
|
|||
# Note that most of these are NOT suitable for benchmarking or release mode!
|
|||
#
|
|||
# Use the system provided memory allocator (realloc) instead of the
|
|||
# bundled memory allocator. This is slower, but sometimes helpful for
|
|||
# debugging. This option cannot be enabled on x64 without GC64, since
|
|||
# realloc usually doesn't return addresses in the right address range.
|
|||
# OTOH this option is mandatory for Valgrind's memcheck tool on x64 and
|
|||
# the only way to get useful results from it for all other architectures.
|
|||
#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
|||
#
|
|||
# This define is required to run LuaJIT under Valgrind. The Valgrind
|
|||
# header files must be installed. You should enable debug information, too.
|
|||
#XCFLAGS+= -DLUAJIT_USE_VALGRIND
|
|||
#
|
|||
# This is the client for the GDB JIT API. GDB 7.0 or higher is required
|
|||
# to make use of it. See lj_gdbjit.c for details. Enabling this causes
|
|||
# a non-negligible overhead, even when not running under GDB.
|
|||
#XCFLAGS+= -DLUAJIT_USE_GDBJIT
|
|||
#
|
|||
# Turn on assertions for the Lua/C API to debug problems with lua_* calls.
|
|||
# This is rather slow -- use only while developing C libraries/embeddings.
|
|||
#XCFLAGS+= -DLUA_USE_APICHECK
|
|||
#
|
|||
# Turn on assertions for the whole LuaJIT VM. This significantly slows down
|
|||
# everything. Use only if you suspect a problem with LuaJIT itself.
|
|||
#XCFLAGS+= -DLUA_USE_ASSERT
|
|||
#
|
|||
##############################################################################
|
|||
# You probably don't need to change anything below this line!
|
|||
##############################################################################
|
|||
|
|||
##############################################################################
|
|||
# Host system detection.
|
|||
##############################################################################
|
|||
|
|||
ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM)) |
|||
HOST_SYS= Windows |
|||
else |
|||
HOST_SYS:= $(shell uname -s) |
|||
ifneq (,$(findstring MINGW,$(HOST_SYS))) |
|||
HOST_SYS= Windows |
|||
HOST_MSYS= mingw |
|||
endif |
|||
ifneq (,$(findstring MSYS,$(HOST_SYS))) |
|||
HOST_SYS= Windows |
|||
HOST_MSYS= mingw |
|||
endif |
|||
ifneq (,$(findstring CYGWIN,$(HOST_SYS))) |
|||
HOST_SYS= Windows |
|||
HOST_MSYS= cygwin |
|||
endif |
|||
endif |
|||
|
|||
##############################################################################
|
|||
# Flags and options for host and target.
|
|||
##############################################################################
|
|||
|
|||
# You can override the following variables at the make command line:
|
|||
# CC HOST_CC STATIC_CC DYNAMIC_CC
|
|||
# CFLAGS HOST_CFLAGS TARGET_CFLAGS
|
|||
# LDFLAGS HOST_LDFLAGS TARGET_LDFLAGS TARGET_SHLDFLAGS
|
|||
# LIBS HOST_LIBS TARGET_LIBS
|
|||
# CROSS HOST_SYS TARGET_SYS TARGET_FLAGS
|
|||
#
|
|||
# Cross-compilation examples:
|
|||
# make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows
|
|||
# make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu-
|
|||
|
|||
ASOPTIONS= $(CCOPT) $(CCWARN) $(XCFLAGS) $(CFLAGS) |
|||
CCOPTIONS= $(CCDEBUG) $(ASOPTIONS) |
|||
LDOPTIONS= $(CCDEBUG) $(LDFLAGS) |
|||
|
|||
HOST_CC= $(CC) |
|||
HOST_RM?= rm -f |
|||
# If left blank, minilua is built and used. You can supply an installed
|
|||
# copy of (plain) Lua 5.1 or 5.2, plus Lua BitOp. E.g. with: HOST_LUA=lua
|
|||
HOST_LUA= |
|||
|
|||
HOST_XCFLAGS= -I. |
|||
HOST_XLDFLAGS= |
|||
HOST_XLIBS= |
|||
HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS) |
|||
HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS) |
|||
HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS) |
|||
|
|||
STATIC_CC = $(CROSS)$(CC) |
|||
DYNAMIC_CC = $(CROSS)$(CC) -fPIC |
|||
TARGET_CC= $(STATIC_CC) |
|||
TARGET_STCC= $(STATIC_CC) |
|||
TARGET_DYNCC= $(DYNAMIC_CC) |
|||
TARGET_LD= $(CROSS)$(CC) |
|||
TARGET_AR= $(CROSS)ar rcus |
|||
TARGET_STRIP= $(CROSS)strip |
|||
|
|||
TARGET_LIBPATH= $(or $(PREFIX),/usr/local)/$(or $(MULTILIB),lib) |
|||
TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER) |
|||
TARGET_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).dylib |
|||
TARGET_DYLIBPATH= $(TARGET_LIBPATH)/$(TARGET_DYLIBNAME) |
|||
TARGET_DLLNAME= lua$(NODOTABIVER).dll |
|||
TARGET_DLLDOTANAME= libluajit-$(ABIVER).dll.a |
|||
TARGET_XSHLDFLAGS= -shared -fPIC -Wl,-soname,$(TARGET_SONAME) |
|||
TARGET_DYNXLDOPTS= |
|||
|
|||
TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE |
|||
TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE |
|||
TARGET_XLDFLAGS= |
|||
TARGET_XLIBS= -lm |
|||
TARGET_TCFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) |
|||
TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) |
|||
TARGET_ASFLAGS= $(ASOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS) |
|||
TARGET_ALDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS) $(TARGET_FLAGS) $(TARGET_LDFLAGS) |
|||
TARGET_ASHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS) $(TARGET_FLAGS) $(TARGET_SHLDFLAGS) |
|||
TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS) |
|||
|
|||
TARGET_TESTARCH=$(shell $(TARGET_CC) $(TARGET_TCFLAGS) -E lj_arch.h -dM) |
|||
ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH))) |
|||
TARGET_LJARCH= x64 |
|||
else |
|||
ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH))) |
|||
TARGET_LJARCH= x86 |
|||
else |
|||
ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH))) |
|||
TARGET_LJARCH= arm |
|||
else |
|||
ifneq (,$(findstring LJ_TARGET_ARM64 ,$(TARGET_TESTARCH))) |
|||
ifneq (,$(findstring __AARCH64EB__ ,$(TARGET_TESTARCH))) |
|||
TARGET_ARCH= -D__AARCH64EB__=1 |
|||
endif |
|||
TARGET_LJARCH= arm64 |
|||
else |
|||
ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH))) |
|||
ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) |
|||
TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_LE |
|||
else |
|||
TARGET_ARCH= -DLJ_ARCH_ENDIAN=LUAJIT_BE |
|||
endif |
|||
TARGET_LJARCH= ppc |
|||
else |
|||
ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH))) |
|||
ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH))) |
|||
TARGET_ARCH= -D__MIPSEL__=1 |
|||
endif |
|||
ifneq (,$(findstring LJ_TARGET_MIPS64 ,$(TARGET_TESTARCH))) |
|||
TARGET_LJARCH= mips64 |
|||
else |
|||
TARGET_LJARCH= mips |
|||
endif |
|||
else |
|||
$(error Unsupported target architecture) |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH))) |
|||
TARGET_SYS= PS3 |
|||
TARGET_ARCH+= -D__CELLOS_LV2__ |
|||
TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC |
|||
TARGET_XLIBS+= -lpthread |
|||
endif |
|||
|
|||
TARGET_XCFLAGS+= $(CCOPT_$(TARGET_LJARCH)) |
|||
TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH)) |
|||
|
|||
ifneq (,$(PREFIX)) |
|||
ifneq (/usr/local,$(PREFIX)) |
|||
TARGET_XCFLAGS+= -DLUA_ROOT=\"$(PREFIX)\" |
|||
ifneq (/usr,$(PREFIX)) |
|||
TARGET_DYNXLDOPTS= -Wl,-rpath,$(TARGET_LIBPATH) |
|||
endif |
|||
endif |
|||
endif |
|||
ifneq (,$(MULTILIB)) |
|||
TARGET_XCFLAGS+= -DLUA_MULTILIB=\"$(MULTILIB)\" |
|||
endif |
|||
ifneq (,$(LMULTILIB)) |
|||
TARGET_XCFLAGS+= -DLUA_LMULTILIB=\"$(LMULTILIB)\" |
|||
endif |
|||
|
|||
##############################################################################
|
|||
# Target system detection.
|
|||
##############################################################################
|
|||
|
|||
TARGET_SYS?= $(HOST_SYS) |
|||
ifeq (Windows,$(TARGET_SYS)) |
|||
TARGET_STRIP+= --strip-unneeded |
|||
TARGET_XSHLDFLAGS= -shared -Wl,--out-implib,$(TARGET_DLLDOTANAME) |
|||
TARGET_DYNXLDOPTS= |
|||
else |
|||
TARGET_AR+= 2>/dev/null |
|||
ifeq (,$(shell $(TARGET_CC) -o /dev/null -c -x c /dev/null -fno-stack-protector 2>/dev/null || echo 1)) |
|||
TARGET_XCFLAGS+= -fno-stack-protector |
|||
endif |
|||
ifeq (Darwin,$(TARGET_SYS)) |
|||
ifeq (,$(MACOSX_DEPLOYMENT_TARGET)) |
|||
$(error missing: export MACOSX_DEPLOYMENT_TARGET=XX.YY) |
|||
endif |
|||
TARGET_STRIP+= -x |
|||
TARGET_XCFLAGS+= -DLUAJIT_UNWIND_EXTERNAL |
|||
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC |
|||
TARGET_DYNXLDOPTS= |
|||
TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) |
|||
else |
|||
ifeq (iOS,$(TARGET_SYS)) |
|||
TARGET_STRIP+= -x |
|||
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC |
|||
TARGET_DYNXLDOPTS= |
|||
TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER) |
|||
ifeq (arm64,$(TARGET_LJARCH)) |
|||
TARGET_XCFLAGS+= -fno-omit-frame-pointer |
|||
endif |
|||
else |
|||
ifeq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH))) |
|||
# Find out whether the target toolchain always generates unwind tables. |
|||
TARGET_TESTUNWIND=$(shell exec 2>/dev/null; echo 'extern void b(void);int a(void){b();return 0;}' | $(TARGET_CC) -c -x c - -o tmpunwind.o && { grep -qa -e eh_frame -e __unwind_info tmpunwind.o || grep -qU -e eh_frame -e __unwind_info tmpunwind.o; } && echo E; rm -f tmpunwind.o) |
|||
ifneq (,$(findstring E,$(TARGET_TESTUNWIND))) |
|||
TARGET_XCFLAGS+= -DLUAJIT_UNWIND_EXTERNAL |
|||
endif |
|||
endif |
|||
ifneq (SunOS,$(TARGET_SYS)) |
|||
ifneq (PS3,$(TARGET_SYS)) |
|||
TARGET_XLDFLAGS+= -Wl,-E |
|||
endif |
|||
endif |
|||
ifeq (Linux,$(TARGET_SYS)) |
|||
TARGET_XLIBS+= -ldl |
|||
endif |
|||
ifeq (GNU/kFreeBSD,$(TARGET_SYS)) |
|||
TARGET_XLIBS+= -ldl |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
ifneq ($(HOST_SYS),$(TARGET_SYS)) |
|||
ifeq (Windows,$(TARGET_SYS)) |
|||
HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS |
|||
else |
|||
ifeq (Linux,$(TARGET_SYS)) |
|||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX |
|||
else |
|||
ifeq (Darwin,$(TARGET_SYS)) |
|||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX |
|||
else |
|||
ifeq (iOS,$(TARGET_SYS)) |
|||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX -DTARGET_OS_IPHONE=1 |
|||
else |
|||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
ifneq (,$(CCDEBUG)) |
|||
TARGET_STRIP= @: |
|||
endif |
|||
|
|||
##############################################################################
|
|||
# Files and pathnames.
|
|||
##############################################################################
|
|||
|
|||
MINILUA_O= host/minilua.o |
|||
MINILUA_LIBS= -lm |
|||
MINILUA_T= host/minilua |
|||
MINILUA_X= $(MINILUA_T) |
|||
|
|||
ifeq (,$(HOST_LUA)) |
|||
HOST_LUA= $(MINILUA_X) |
|||
DASM_DEP= $(MINILUA_T) |
|||
endif |
|||
|
|||
DASM_DIR= ../dynasm |
|||
DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua |
|||
DASM_XFLAGS= |
|||
DASM_AFLAGS= |
|||
DASM_ARCH= $(TARGET_LJARCH) |
|||
|
|||
ifneq (,$(findstring LJ_LE 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D ENDIAN_LE |
|||
else |
|||
DASM_AFLAGS+= -D ENDIAN_BE |
|||
endif |
|||
ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D P64 |
|||
endif |
|||
ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D JIT |
|||
endif |
|||
ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D FFI |
|||
endif |
|||
ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D DUALNUM |
|||
endif |
|||
ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D FPU |
|||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=1 |
|||
else |
|||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=0 |
|||
endif |
|||
ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D HFABI |
|||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=0 |
|||
else |
|||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=1 |
|||
endif |
|||
ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D NO_UNWIND |
|||
TARGET_ARCH+= -DLUAJIT_NO_UNWIND |
|||
endif |
|||
DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH)))) |
|||
ifeq (Windows,$(TARGET_SYS)) |
|||
DASM_AFLAGS+= -D WIN |
|||
endif |
|||
ifeq (x64,$(TARGET_LJARCH)) |
|||
ifeq (,$(findstring LJ_FR2 1,$(TARGET_TESTARCH))) |
|||
DASM_ARCH= x86 |
|||
endif |
|||
else |
|||
ifeq (arm,$(TARGET_LJARCH)) |
|||
ifeq (iOS,$(TARGET_SYS)) |
|||
DASM_AFLAGS+= -D IOS |
|||
endif |
|||
else |
|||
ifneq (,$(findstring LJ_TARGET_MIPSR6 ,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D MIPSR6 |
|||
endif |
|||
ifeq (ppc,$(TARGET_LJARCH)) |
|||
ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D SQRT |
|||
endif |
|||
ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D ROUND |
|||
endif |
|||
ifneq (,$(findstring LJ_ARCH_PPC32ON64 1,$(TARGET_TESTARCH))) |
|||
DASM_AFLAGS+= -D GPR64 |
|||
endif |
|||
ifeq (PS3,$(TARGET_SYS)) |
|||
DASM_AFLAGS+= -D PPE -D TOC |
|||
endif |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS) |
|||
DASM_DASC= vm_$(DASM_ARCH).dasc |
|||
|
|||
BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \
|
|||
host/buildvm_lib.o host/buildvm_fold.o |
|||
BUILDVM_T= host/buildvm |
|||
BUILDVM_X= $(BUILDVM_T) |
|||
|
|||
HOST_O= $(MINILUA_O) $(BUILDVM_O) |
|||
HOST_T= $(MINILUA_T) $(BUILDVM_T) |
|||
|
|||
LJVM_S= lj_vm.S |
|||
LJVM_O= lj_vm.o |
|||
LJVM_BOUT= $(LJVM_S) |
|||
LJVM_MODE= elfasm |
|||
|
|||
LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \
|
|||
lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o \
|
|||
lib_buffer.o |
|||
LJLIB_C= $(LJLIB_O:.o=.c) |
|||
|
|||
LJCORE_O= lj_assert.o lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \
|
|||
lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
|
|||
lj_prng.o lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o \
|
|||
lj_strscan.o lj_strfmt.o lj_strfmt_num.o lj_serialize.o \
|
|||
lj_api.o lj_profile.o \
|
|||
lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
|
|||
lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
|
|||
lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
|
|||
lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
|
|||
lj_asm.o lj_trace.o lj_gdbjit.o \
|
|||
lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \
|
|||
lj_carith.o lj_clib.o lj_cparse.o \
|
|||
lj_lib.o lj_alloc.o lib_aux.o \
|
|||
$(LJLIB_O) lib_init.o |
|||
|
|||
LJVMCORE_O= $(LJVM_O) $(LJCORE_O) |
|||
LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) |
|||
|
|||
LIB_VMDEF= jit/vmdef.lua |
|||
LIB_VMDEFP= $(LIB_VMDEF) |
|||
|
|||
LUAJIT_O= luajit.o |
|||
LUAJIT_A= libluajit.a |
|||
LUAJIT_SO= libluajit.so |
|||
LUAJIT_T= luajit |
|||
|
|||
ALL_T= $(LUAJIT_T) $(LUAJIT_A) $(LUAJIT_SO) $(HOST_T) |
|||
ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \
|
|||
host/buildvm_arch.h |
|||
ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP) |
|||
WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk |
|||
ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o $(WIN_RM) |
|||
|
|||
##############################################################################
|
|||
# Build mode handling.
|
|||
##############################################################################
|
|||
|
|||
# Mixed mode defaults.
|
|||
TARGET_O= $(LUAJIT_A) |
|||
TARGET_T= $(LUAJIT_T) $(LUAJIT_SO) |
|||
TARGET_DEP= $(LIB_VMDEF) $(LUAJIT_SO) |
|||
|
|||
ifeq (Windows,$(TARGET_SYS)) |
|||
TARGET_DYNCC= $(STATIC_CC) |
|||
LJVM_MODE= peobj |
|||
LJVM_BOUT= $(LJVM_O) |
|||
LUAJIT_T= luajit.exe |
|||
ifeq (cygwin,$(HOST_MSYS)) |
|||
LUAJIT_SO= cyg$(TARGET_DLLNAME) |
|||
else |
|||
LUAJIT_SO= $(TARGET_DLLNAME) |
|||
endif |
|||
# Mixed mode is not supported on Windows. And static mode doesn't work well. |
|||
# C modules cannot be loaded, because they bind to lua51.dll. |
|||
ifneq (static,$(BUILDMODE)) |
|||
BUILDMODE= dynamic |
|||
TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL |
|||
endif |
|||
endif |
|||
ifeq (Darwin,$(TARGET_SYS)) |
|||
LJVM_MODE= machasm |
|||
endif |
|||
ifeq (iOS,$(TARGET_SYS)) |
|||
LJVM_MODE= machasm |
|||
endif |
|||
ifeq (SunOS,$(TARGET_SYS)) |
|||
BUILDMODE= static |
|||
endif |
|||
ifeq (PS3,$(TARGET_SYS)) |
|||
BUILDMODE= static |
|||
endif |
|||
|
|||
ifeq (Windows,$(HOST_SYS)) |
|||
MINILUA_T= host/minilua.exe |
|||
BUILDVM_T= host/buildvm.exe |
|||
ifeq (,$(HOST_MSYS)) |
|||
MINILUA_X= host\minilua |
|||
BUILDVM_X= host\buildvm |
|||
ALL_RM:= $(subst /,\,$(ALL_RM)) |
|||
HOST_RM= del |
|||
endif |
|||
endif |
|||
|
|||
ifeq (static,$(BUILDMODE)) |
|||
TARGET_DYNCC= @: |
|||
TARGET_T= $(LUAJIT_T) |
|||
TARGET_DEP= $(LIB_VMDEF) |
|||
else |
|||
ifeq (dynamic,$(BUILDMODE)) |
|||
ifneq (Windows,$(TARGET_SYS)) |
|||
TARGET_CC= $(DYNAMIC_CC) |
|||
endif |
|||
TARGET_DYNCC= @: |
|||
LJVMCORE_DYNO= $(LJVMCORE_O) |
|||
TARGET_O= $(LUAJIT_SO) |
|||
TARGET_XLDFLAGS+= $(TARGET_DYNXLDOPTS) |
|||
else |
|||
ifeq (Darwin,$(TARGET_SYS)) |
|||
TARGET_DYNCC= @: |
|||
LJVMCORE_DYNO= $(LJVMCORE_O) |
|||
endif |
|||
ifeq (iOS,$(TARGET_SYS)) |
|||
TARGET_DYNCC= @: |
|||
LJVMCORE_DYNO= $(LJVMCORE_O) |
|||
endif |
|||
endif |
|||
endif |
|||
|
|||
Q= @ |
|||
E= @echo |
|||
#Q=
|
|||
#E= @:
|
|||
|
|||
##############################################################################
|
|||
# Make targets.
|
|||
##############################################################################
|
|||
|
|||
default all: $(TARGET_T) |
|||
|
|||
amalg: |
|||
$(MAKE) all "LJCORE_O=ljamalg.o" |
|||
|
|||
clean: |
|||
$(HOST_RM) $(ALL_RM) |
|||
|
|||
libbc: |
|||
./$(LUAJIT_T) host/genlibbc.lua -o host/buildvm_libbc.h $(LJLIB_C) |
|||
$(MAKE) all |
|||
|
|||
depend: |
|||
@for file in $(ALL_HDRGEN); do \
|
|||
test -f $$file || touch $$file; \
|
|||
done |
|||
@$(HOST_CC) $(HOST_ACFLAGS) -MM *.c host/*.c | \
|
|||
sed -e "s| [^ ]*/dasm_\S*\.h||g" \
|
|||
-e "s|^\([^l ]\)|host/\1|" \
|
|||
-e "s| lj_target_\S*\.h| lj_target_*.h|g" \
|
|||
-e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \
|
|||
-e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >Makefile.dep |
|||
@for file in $(ALL_HDRGEN); do \
|
|||
test -s $$file || $(HOST_RM) $$file; \
|
|||
done |
|||
|
|||
.PHONY: default all amalg clean libbc depend |
|||
|
|||
##############################################################################
|
|||
# Rules for generated files.
|
|||
##############################################################################
|
|||
|
|||
$(MINILUA_T): $(MINILUA_O) |
|||
$(E) "HOSTLINK $@" |
|||
$(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(MINILUA_O) $(MINILUA_LIBS) $(HOST_ALIBS) |
|||
|
|||
host/buildvm_arch.h: $(DASM_DASC) $(DASM_DEP) $(DASM_DIR)/*.lua lj_arch.h lua.h luaconf.h |
|||
$(E) "DYNASM $@" |
|||
$(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC) |
|||
|
|||
host/buildvm.o: $(DASM_DIR)/dasm_*.h |
|||
|
|||
$(BUILDVM_T): $(BUILDVM_O) |
|||
$(E) "HOSTLINK $@" |
|||
$(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(BUILDVM_O) $(HOST_ALIBS) |
|||
|
|||
$(LJVM_BOUT): $(BUILDVM_T) |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@ |
|||
|
|||
lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C) |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C) |
|||
|
|||
lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C) |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C) |
|||
|
|||
lj_libdef.h: $(BUILDVM_T) $(LJLIB_C) |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C) |
|||
|
|||
lj_recdef.h: $(BUILDVM_T) $(LJLIB_C) |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C) |
|||
|
|||
$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C) |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C) |
|||
|
|||
lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c |
|||
$(E) "BUILDVM $@" |
|||
$(Q)$(BUILDVM_X) -m folddef -o $@ lj_opt_fold.c |
|||
|
|||
##############################################################################
|
|||
# Object file rules.
|
|||
##############################################################################
|
|||
|
|||
%.o: %.c |
|||
$(E) "CC $@" |
|||
$(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $< |
|||
$(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $< |
|||
|
|||
%.o: %.S |
|||
$(E) "ASM $@" |
|||
$(Q)$(TARGET_DYNCC) $(TARGET_ASFLAGS) -c -o $(@:.o=_dyn.o) $< |
|||
$(Q)$(TARGET_CC) $(TARGET_ASFLAGS) -c -o $@ $< |
|||
|
|||
$(LUAJIT_O): |
|||
$(E) "CC $@" |
|||
$(Q)$(TARGET_STCC) $(TARGET_ACFLAGS) -c -o $@ $< |
|||
|
|||
$(HOST_O): %.o: %.c |
|||
$(E) "HOSTCC $@" |
|||
$(Q)$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $< |
|||
|
|||
include Makefile.dep |
|||
|
|||
##############################################################################
|
|||
# Target file rules.
|
|||
##############################################################################
|
|||
|
|||
$(LUAJIT_A): $(LJVMCORE_O) |
|||
$(E) "AR $@" |
|||
$(Q)$(TARGET_AR) $@ $(LJVMCORE_O) |
|||
|
|||
# The dependency on _O, but linking with _DYNO is intentional.
|
|||
$(LUAJIT_SO): $(LJVMCORE_O) |
|||
$(E) "DYNLINK $@" |
|||
$(Q)$(TARGET_LD) $(TARGET_ASHLDFLAGS) -o $@ $(LJVMCORE_DYNO) $(TARGET_ALIBS) |
|||
$(Q)$(TARGET_STRIP) $@ |
|||
|
|||
$(LUAJIT_T): $(TARGET_O) $(LUAJIT_O) $(TARGET_DEP) |
|||
$(E) "LINK $@" |
|||
$(Q)$(TARGET_LD) $(TARGET_ALDFLAGS) -o $@ $(LUAJIT_O) $(TARGET_O) $(TARGET_ALIBS) |
|||
$(Q)$(TARGET_STRIP) $@ |
|||
$(E) "OK Successfully built LuaJIT" |
|||
|
|||
##############################################################################
|
@ -0,0 +1,259 @@ |
|||
lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \
|
|||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h |
|||
lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h \
|
|||
lj_str.h lj_tab.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h \
|
|||
lj_cconv.h lj_ff.h lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h \
|
|||
lj_strscan.h lj_strfmt.h lj_lib.h lj_libdef.h |
|||
lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_strscan.h \
|
|||
lj_strfmt.h lj_ctype.h lj_cdata.h lj_cconv.h lj_carith.h lj_ff.h \
|
|||
lj_ffdef.h lj_lib.h lj_libdef.h |
|||
lib_buffer.o: lib_buffer.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \
|
|||
lj_tab.h lj_udata.h lj_meta.h lj_ctype.h lj_cdata.h lj_cconv.h \
|
|||
lj_strfmt.h lj_serialize.h lj_lib.h lj_libdef.h |
|||
lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \
|
|||
lj_libdef.h |
|||
lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h \
|
|||
lj_ctype.h lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h \
|
|||
lj_ccallback.h lj_clib.h lj_strfmt.h lj_ff.h lj_ffdef.h lj_lib.h \
|
|||
lj_libdef.h |
|||
lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h |
|||
lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_state.h \
|
|||
lj_strfmt.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h |
|||
lib_jit.o: lib_jit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h \
|
|||
lj_state.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h \
|
|||
lj_target.h lj_target_*.h lj_trace.h lj_dispatch.h lj_traceerr.h \
|
|||
lj_vm.h lj_vmevent.h lj_lib.h luajit.h lj_libdef.h |
|||
lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_lib.h lj_vm.h lj_prng.h lj_libdef.h |
|||
lib_os.o: lib_os.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_lib.h \
|
|||
lj_libdef.h |
|||
lib_package.o: lib_package.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h |
|||
lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \
|
|||
lj_tab.h lj_meta.h lj_state.h lj_ff.h lj_ffdef.h lj_bcdump.h lj_lex.h \
|
|||
lj_char.h lj_strfmt.h lj_lib.h lj_libdef.h |
|||
lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h \
|
|||
lj_tab.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h |
|||
lj_alloc.o: lj_alloc.c lj_def.h lua.h luaconf.h lj_arch.h lj_alloc.h \ |
|||
lj_prng.h |
|||
lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \
|
|||
lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
|
|||
lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h lj_strfmt.h |
|||
lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_buf.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_ir.h \
|
|||
lj_jit.h lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h \
|
|||
lj_traceerr.h lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h \
|
|||
lj_emit_*.h lj_asm_*.h |
|||
lj_assert.o: lj_assert.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h |
|||
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \ |
|||
lj_bcdef.h |
|||
lj_bcread.o: lj_bcread.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_bc.h \
|
|||
lj_ctype.h lj_cdata.h lualib.h lj_lex.h lj_bcdump.h lj_state.h \
|
|||
lj_strfmt.h |
|||
lj_bcwrite.o: lj_bcwrite.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_buf.h lj_str.h lj_bc.h lj_ctype.h lj_dispatch.h lj_jit.h \
|
|||
lj_ir.h lj_strfmt.h lj_bcdump.h lj_lex.h lj_err.h lj_errmsg.h lj_vm.h |
|||
lj_buf.o: lj_buf.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_strfmt.h |
|||
lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ir.h lj_ctype.h \
|
|||
lj_cconv.h lj_cdata.h lj_carith.h lj_strscan.h |
|||
lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h \
|
|||
lj_ccall.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
|
|||
lj_traceerr.h |
|||
lj_ccallback.o: lj_ccallback.c lj_obj.h lua.h luaconf.h lj_def.h \ |
|||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_state.h lj_frame.h \
|
|||
lj_bc.h lj_ctype.h lj_cconv.h lj_ccall.h lj_ccallback.h lj_target.h \
|
|||
lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \
|
|||
lj_traceerr.h lj_vm.h |
|||
lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_tab.h lj_ctype.h \
|
|||
lj_cdata.h lj_cconv.h lj_ccallback.h |
|||
lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_cconv.h lj_cdata.h |
|||
lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h |
|||
lj_clib.o: lj_clib.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_tab.h lj_str.h lj_udata.h lj_ctype.h lj_cconv.h \
|
|||
lj_cdata.h lj_clib.h lj_strfmt.h |
|||
lj_cparse.o: lj_cparse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_ctype.h lj_cparse.h \
|
|||
lj_frame.h lj_bc.h lj_vm.h lj_char.h lj_strscan.h lj_strfmt.h |
|||
lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h lj_gc.h \
|
|||
lj_cdata.h lj_cparse.h lj_cconv.h lj_carith.h lj_clib.h lj_ccall.h \
|
|||
lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
|||
lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_snap.h \
|
|||
lj_crecord.h lj_strfmt.h |
|||
lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_strfmt.h lj_ctype.h \
|
|||
lj_ccallback.h lj_buf.h |
|||
lj_debug.o: lj_debug.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_debug.h lj_buf.h lj_gc.h lj_str.h lj_tab.h \
|
|||
lj_state.h lj_frame.h lj_bc.h lj_strfmt.h lj_jit.h lj_ir.h |
|||
lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_func.h lj_tab.h \
|
|||
lj_meta.h lj_debug.h lj_state.h lj_frame.h lj_bc.h lj_ff.h lj_ffdef.h \
|
|||
lj_strfmt.h lj_jit.h lj_ir.h lj_ccallback.h lj_ctype.h lj_trace.h \
|
|||
lj_dispatch.h lj_traceerr.h lj_profile.h lj_vm.h luajit.h |
|||
lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \ |
|||
lj_errmsg.h lj_debug.h lj_str.h lj_func.h lj_state.h lj_frame.h lj_bc.h \
|
|||
lj_ff.h lj_ffdef.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
|
|||
lj_traceerr.h lj_vm.h lj_strfmt.h |
|||
lj_ffrecord.o: lj_ffrecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_tab.h lj_frame.h \
|
|||
lj_bc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h \
|
|||
lj_trace.h lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h \
|
|||
lj_crecord.h lj_vm.h lj_strscan.h lj_strfmt.h lj_serialize.h lj_recdef.h |
|||
lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
|
|||
lj_traceerr.h lj_vm.h |
|||
lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_func.h lj_udata.h \
|
|||
lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h lj_trace.h \
|
|||
lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h |
|||
lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_frame.h lj_bc.h lj_buf.h \
|
|||
lj_str.h lj_strfmt.h lj_jit.h lj_ir.h lj_dispatch.h |
|||
lj_ir.o: lj_ir.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_buf.h lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h \
|
|||
lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_ctype.h lj_cdata.h \
|
|||
lj_carith.h lj_vm.h lj_strscan.h lj_serialize.h lj_strfmt.h lj_prng.h |
|||
lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_ctype.h lj_cdata.h \
|
|||
lualib.h lj_state.h lj_lex.h lj_parse.h lj_char.h lj_strscan.h \
|
|||
lj_strfmt.h |
|||
lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \
|
|||
lj_dispatch.h lj_jit.h lj_ir.h lj_ctype.h lj_vm.h lj_strscan.h \
|
|||
lj_strfmt.h lj_lex.h lj_bcdump.h lj_lib.h |
|||
lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \ |
|||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_func.h \
|
|||
lj_frame.h lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h |
|||
lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h \
|
|||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_prng.h lj_vm.h |
|||
lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_meta.h lj_frame.h \
|
|||
lj_bc.h lj_vm.h lj_strscan.h lj_strfmt.h lj_lib.h |
|||
lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h |
|||
lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_ir.h lj_jit.h lj_iropt.h |
|||
lj_opt_fold.o: lj_opt_fold.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_buf.h lj_gc.h lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_ircall.h \
|
|||
lj_iropt.h lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_ctype.h \
|
|||
lj_carith.h lj_vm.h lj_strscan.h lj_strfmt.h lj_folddef.h |
|||
lj_opt_loop.o: lj_opt_loop.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_ir.h lj_jit.h \
|
|||
lj_iropt.h lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_snap.h \
|
|||
lj_vm.h |
|||
lj_opt_mem.o: lj_opt_mem.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_tab.h lj_ir.h lj_jit.h lj_iropt.h lj_ircall.h lj_dispatch.h lj_bc.h |
|||
lj_opt_narrow.o: lj_opt_narrow.c lj_obj.h lua.h luaconf.h lj_def.h \ |
|||
lj_arch.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \
|
|||
lj_traceerr.h lj_vm.h lj_strscan.h |
|||
lj_opt_sink.o: lj_opt_sink.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_ir.h lj_jit.h lj_iropt.h lj_target.h lj_target_*.h |
|||
lj_opt_split.o: lj_opt_split.c lj_obj.h lua.h luaconf.h lj_def.h \ |
|||
lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_ir.h \
|
|||
lj_jit.h lj_ircall.h lj_iropt.h lj_dispatch.h lj_bc.h lj_vm.h |
|||
lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h lj_str.h lj_tab.h \
|
|||
lj_func.h lj_state.h lj_bc.h lj_ctype.h lj_strfmt.h lj_lex.h lj_parse.h \
|
|||
lj_vm.h lj_vmevent.h |
|||
lj_prng.o: lj_prng.c lj_def.h lua.h luaconf.h lj_arch.h lj_prng.h |
|||
lj_profile.o: lj_profile.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_buf.h lj_gc.h lj_str.h lj_frame.h lj_bc.h lj_debug.h lj_dispatch.h \
|
|||
lj_jit.h lj_ir.h lj_trace.h lj_traceerr.h lj_profile.h luajit.h |
|||
lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \
|
|||
lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_debug.h lj_ir.h lj_jit.h \
|
|||
lj_ircall.h lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h \
|
|||
lj_record.h lj_ffrecord.h lj_snap.h lj_vm.h lj_prng.h |
|||
lj_serialize.o: lj_serialize.c lj_obj.h lua.h luaconf.h lj_def.h \ |
|||
lj_arch.h lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_tab.h \
|
|||
lj_udata.h lj_ctype.h lj_cdata.h lj_ir.h lj_serialize.h |
|||
lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_tab.h lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h \
|
|||
lj_trace.h lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h \
|
|||
lj_target_*.h lj_ctype.h lj_cdata.h |
|||
lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_func.h \
|
|||
lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_trace.h lj_jit.h \
|
|||
lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_prng.h lj_lex.h \
|
|||
lj_alloc.h luajit.h |
|||
lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_str.h lj_char.h lj_prng.h |
|||
lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_meta.h lj_state.h \
|
|||
lj_char.h lj_strfmt.h lj_ctype.h lj_lib.h |
|||
lj_strfmt_num.o: lj_strfmt_num.c lj_obj.h lua.h luaconf.h lj_def.h \ |
|||
lj_arch.h lj_buf.h lj_gc.h lj_str.h lj_strfmt.h |
|||
lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_char.h lj_strscan.h |
|||
lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
|||
lj_err.h lj_errmsg.h lj_tab.h |
|||
lj_trace.o: lj_trace.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_frame.h lj_bc.h \
|
|||
lj_state.h lj_ir.h lj_jit.h lj_iropt.h lj_mcode.h lj_trace.h \
|
|||
lj_dispatch.h lj_traceerr.h lj_snap.h lj_gdbjit.h lj_record.h lj_asm.h \
|
|||
lj_vm.h lj_vmevent.h lj_target.h lj_target_*.h lj_prng.h |
|||
lj_udata.o: lj_udata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_gc.h lj_err.h lj_errmsg.h lj_udata.h |
|||
lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_str.h lj_tab.h lj_state.h lj_dispatch.h lj_bc.h lj_jit.h lj_ir.h \
|
|||
lj_vm.h lj_vmevent.h |
|||
lj_vmmath.o: lj_vmmath.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
|||
lj_ir.h lj_vm.h |
|||
ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_assert.c lj_obj.h \ |
|||
lj_def.h lj_arch.h lj_gc.c lj_gc.h lj_err.h lj_errmsg.h lj_buf.h \
|
|||
lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h lj_state.h lj_frame.h \
|
|||
lj_bc.h lj_ctype.h lj_cdata.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
|
|||
lj_traceerr.h lj_vm.h lj_err.c lj_debug.h lj_ff.h lj_ffdef.h lj_strfmt.h \
|
|||
lj_char.c lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c \
|
|||
lj_prng.h lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h \
|
|||
lj_debug.c lj_prng.c lj_state.c lj_lex.h lj_alloc.h luajit.h \
|
|||
lj_dispatch.c lj_ccallback.h lj_profile.h lj_vmevent.c lj_vmevent.h \
|
|||
lj_vmmath.c lj_strscan.c lj_strfmt.c lj_strfmt_num.c lj_serialize.c \
|
|||
lj_serialize.h lj_api.c lj_profile.c lj_lex.c lualib.h lj_parse.h \
|
|||
lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c lj_load.c lj_ctype.c \
|
|||
lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h lj_ccallback.c \
|
|||
lj_target.h lj_target_*.h lj_mcode.h lj_carith.c lj_carith.h lj_clib.c \
|
|||
lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c lj_ircall.h \
|
|||
lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c \
|
|||
lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c lj_opt_sink.c \
|
|||
lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h lj_crecord.c \
|
|||
lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h \
|
|||
lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c \
|
|||
lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c \
|
|||
lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_ffi.c \
|
|||
lib_buffer.c lib_init.c |
|||
luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h |
|||
host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \ |
|||
lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \
|
|||
lj_ircall.h lj_ir.h lj_jit.h lj_frame.h lj_bc.h lj_dispatch.h lj_ctype.h \
|
|||
lj_gc.h lj_ccall.h lj_ctype.h luajit.h \
|
|||
host/buildvm_arch.h lj_traceerr.h |
|||
host/buildvm_asm.o: host/buildvm_asm.c host/buildvm.h lj_def.h lua.h luaconf.h \ |
|||
lj_arch.h lj_bc.h lj_def.h lj_arch.h |
|||
host/buildvm_fold.o: host/buildvm_fold.c host/buildvm.h lj_def.h lua.h \ |
|||
luaconf.h lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_ir.h lj_obj.h |
|||
host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \ |
|||
lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_bc.h lj_lib.h lj_obj.h \
|
|||
host/buildvm_libbc.h |
|||
host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \ |
|||
luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h |
|||
host/minilua.o: host/minilua.c |
@ -0,0 +1,3 @@ |
|||
minilua |
|||
buildvm |
|||
buildvm_arch.h |
@ -0,0 +1,4 @@ |
|||
The files in this directory are only used during the build process of LuaJIT. |
|||
For cross-compilation, they must be executed on the host, not on the target. |
|||
|
|||
These files should NOT be installed! |
@ -0,0 +1,518 @@ |
|||
/*
|
|||
** LuaJIT VM builder. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** This is a tool to build the hand-tuned assembler code required for |
|||
** LuaJIT's bytecode interpreter. It supports a variety of output formats |
|||
** to feed different toolchains (see usage() below). |
|||
** |
|||
** This tool is not particularly optimized because it's only used while |
|||
** _building_ LuaJIT. There's no point in distributing or installing it. |
|||
** Only the object code generated by this tool is linked into LuaJIT. |
|||
** |
|||
** Caveat: some memory is not free'd, error handling is lazy. |
|||
** It's a one-shot tool -- any effort fixing this would be wasted. |
|||
*/ |
|||
|
|||
#include "buildvm.h" |
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_bc.h" |
|||
#include "lj_ir.h" |
|||
#include "lj_ircall.h" |
|||
#include "lj_frame.h" |
|||
#include "lj_dispatch.h" |
|||
#if LJ_HASFFI |
|||
#include "lj_ctype.h" |
|||
#include "lj_ccall.h" |
|||
#endif |
|||
#include "luajit.h" |
|||
|
|||
#if defined(_WIN32) |
|||
#include <fcntl.h> |
|||
#include <io.h> |
|||
#endif |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* DynASM glue definitions. */ |
|||
#define Dst ctx |
|||
#define Dst_DECL BuildCtx *ctx |
|||
#define Dst_REF (ctx->D) |
|||
#define DASM_CHECKS 1 |
|||
|
|||
#include "../dynasm/dasm_proto.h" |
|||
|
|||
/* Glue macros for DynASM. */ |
|||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type); |
|||
|
|||
#define DASM_EXTERN(ctx, addr, idx, type) \ |
|||
collect_reloc(ctx, addr, idx, type) |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */ |
|||
#define DASM_ALIGNED_WRITES 1 |
|||
|
|||
/* Embed architecture-specific DynASM encoder. */ |
|||
#if LJ_TARGET_X86ORX64 |
|||
#include "../dynasm/dasm_x86.h" |
|||
#elif LJ_TARGET_ARM |
|||
#include "../dynasm/dasm_arm.h" |
|||
#elif LJ_TARGET_ARM64 |
|||
#include "../dynasm/dasm_arm64.h" |
|||
#elif LJ_TARGET_PPC |
|||
#include "../dynasm/dasm_ppc.h" |
|||
#elif LJ_TARGET_MIPS |
|||
#include "../dynasm/dasm_mips.h" |
|||
#else |
|||
#error "No support for this architecture (yet)" |
|||
#endif |
|||
|
|||
/* Embed generated architecture-specific backend. */ |
|||
#include "buildvm_arch.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
void owrite(BuildCtx *ctx, const void *ptr, size_t sz) |
|||
{ |
|||
if (fwrite(ptr, 1, sz, ctx->fp) != sz) { |
|||
fprintf(stderr, "Error: cannot write to output file: %s\n", |
|||
strerror(errno)); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* Emit code as raw bytes. Only used for DynASM debugging. */ |
|||
static void emit_raw(BuildCtx *ctx) |
|||
{ |
|||
owrite(ctx, ctx->code, ctx->codesz); |
|||
} |
|||
|
|||
/* -- Build machine code -------------------------------------------------- */ |
|||
|
|||
static const char *sym_decorate(BuildCtx *ctx, |
|||
const char *prefix, const char *suffix) |
|||
{ |
|||
char name[256]; |
|||
char *p; |
|||
#if LJ_64 |
|||
const char *symprefix = ctx->mode == BUILD_machasm ? "_" : ""; |
|||
#elif LJ_TARGET_XBOX360 |
|||
const char *symprefix = ""; |
|||
#else |
|||
const char *symprefix = ctx->mode != BUILD_elfasm ? "_" : ""; |
|||
#endif |
|||
sprintf(name, "%s%s%s", symprefix, prefix, suffix); |
|||
p = strchr(name, '@'); |
|||
if (p) { |
|||
#if LJ_TARGET_X86ORX64 |
|||
if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj)) |
|||
name[0] = name[1] == 'R' ? '_' : '@'; /* Just for _RtlUnwind@16. */ |
|||
else |
|||
*p = '\0'; |
|||
#elif LJ_TARGET_PPC && !LJ_TARGET_CONSOLE |
|||
/* Keep @plt etc. */ |
|||
#else |
|||
*p = '\0'; |
|||
#endif |
|||
} |
|||
p = (char *)malloc(strlen(name)+1); /* MSVC doesn't like strdup. */ |
|||
strcpy(p, name); |
|||
return p; |
|||
} |
|||
|
|||
#define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1) |
|||
|
|||
static int relocmap[NRELOCSYM]; |
|||
|
|||
/* Collect external relocations. */ |
|||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type) |
|||
{ |
|||
if (ctx->nreloc >= BUILD_MAX_RELOC) { |
|||
fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n"); |
|||
exit(1); |
|||
} |
|||
if (relocmap[idx] < 0) { |
|||
relocmap[idx] = ctx->nrelocsym; |
|||
ctx->relocsym[ctx->nrelocsym] = sym_decorate(ctx, "", extnames[idx]); |
|||
ctx->nrelocsym++; |
|||
} |
|||
ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code); |
|||
ctx->reloc[ctx->nreloc].sym = relocmap[idx]; |
|||
ctx->reloc[ctx->nreloc].type = type; |
|||
ctx->nreloc++; |
|||
#if LJ_TARGET_XBOX360 |
|||
return (int)(ctx->code - addr) + 4; /* Encode symbol offset of .text. */ |
|||
#else |
|||
return 0; /* Encode symbol offset of 0. */ |
|||
#endif |
|||
} |
|||
|
|||
/* Naive insertion sort. Performance doesn't matter here. */ |
|||
static void sym_insert(BuildCtx *ctx, int32_t ofs, |
|||
const char *prefix, const char *suffix) |
|||
{ |
|||
ptrdiff_t i = ctx->nsym++; |
|||
while (i > 0) { |
|||
if (ctx->sym[i-1].ofs <= ofs) |
|||
break; |
|||
ctx->sym[i] = ctx->sym[i-1]; |
|||
i--; |
|||
} |
|||
ctx->sym[i].ofs = ofs; |
|||
ctx->sym[i].name = sym_decorate(ctx, prefix, suffix); |
|||
} |
|||
|
|||
/* Build the machine code. */ |
|||
static int build_code(BuildCtx *ctx) |
|||
{ |
|||
int status; |
|||
int i; |
|||
|
|||
/* Initialize DynASM structures. */ |
|||
ctx->nglob = GLOB__MAX; |
|||
ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *)); |
|||
memset(ctx->glob, 0, ctx->nglob*sizeof(void *)); |
|||
ctx->nreloc = 0; |
|||
|
|||
ctx->globnames = globnames; |
|||
ctx->extnames = extnames; |
|||
ctx->relocsym = (const char **)malloc(NRELOCSYM*sizeof(const char *)); |
|||
ctx->nrelocsym = 0; |
|||
for (i = 0; i < (int)NRELOCSYM; i++) relocmap[i] = -1; |
|||
|
|||
ctx->dasm_ident = DASM_IDENT; |
|||
ctx->dasm_arch = DASM_ARCH; |
|||
|
|||
dasm_init(Dst, DASM_MAXSECTION); |
|||
dasm_setupglobal(Dst, ctx->glob, ctx->nglob); |
|||
dasm_setup(Dst, build_actionlist); |
|||
|
|||
/* Call arch-specific backend to emit the code. */ |
|||
ctx->npc = build_backend(ctx); |
|||
|
|||
/* Finalize the code. */ |
|||
(void)dasm_checkstep(Dst, -1); |
|||
if ((status = dasm_link(Dst, &ctx->codesz))) return status; |
|||
ctx->code = (uint8_t *)malloc(ctx->codesz); |
|||
if ((status = dasm_encode(Dst, (void *)ctx->code))) return status; |
|||
|
|||
/* Allocate symbol table and bytecode offsets. */ |
|||
ctx->beginsym = sym_decorate(ctx, "", LABEL_PREFIX "vm_asm_begin"); |
|||
ctx->sym = (BuildSym *)malloc((ctx->npc+ctx->nglob+1)*sizeof(BuildSym)); |
|||
ctx->nsym = 0; |
|||
ctx->bc_ofs = (int32_t *)malloc(ctx->npc*sizeof(int32_t)); |
|||
|
|||
/* Collect the opcodes (PC labels). */ |
|||
for (i = 0; i < ctx->npc; i++) { |
|||
int32_t ofs = dasm_getpclabel(Dst, i); |
|||
if (ofs < 0) return 0x22000000|i; |
|||
ctx->bc_ofs[i] = ofs; |
|||
if ((LJ_HASJIT || |
|||
!(i == BC_JFORI || i == BC_JFORL || i == BC_JITERL || i == BC_JLOOP || |
|||
i == BC_IFORL || i == BC_IITERL || i == BC_ILOOP)) && |
|||
(LJ_HASFFI || i != BC_KCDATA)) |
|||
sym_insert(ctx, ofs, LABEL_PREFIX_BC, bc_names[i]); |
|||
} |
|||
|
|||
/* Collect the globals (named labels). */ |
|||
for (i = 0; i < ctx->nglob; i++) { |
|||
const char *gl = globnames[i]; |
|||
int len = (int)strlen(gl); |
|||
if (!ctx->glob[i]) { |
|||
fprintf(stderr, "Error: undefined global %s\n", gl); |
|||
exit(2); |
|||
} |
|||
/* Skip the _Z symbols. */ |
|||
if (!(len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z')) |
|||
sym_insert(ctx, (int32_t)((uint8_t *)(ctx->glob[i]) - ctx->code), |
|||
LABEL_PREFIX, globnames[i]); |
|||
} |
|||
|
|||
/* Close the address range. */ |
|||
sym_insert(ctx, (int32_t)ctx->codesz, "", ""); |
|||
ctx->nsym--; |
|||
|
|||
dasm_free(Dst); |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
/* -- Generate VM enums --------------------------------------------------- */ |
|||
|
|||
const char *const bc_names[] = { |
|||
#define BCNAME(name, ma, mb, mc, mt) #name, |
|||
BCDEF(BCNAME) |
|||
#undef BCNAME |
|||
NULL |
|||
}; |
|||
|
|||
const char *const ir_names[] = { |
|||
#define IRNAME(name, m, m1, m2) #name, |
|||
IRDEF(IRNAME) |
|||
#undef IRNAME |
|||
NULL |
|||
}; |
|||
|
|||
const char *const irt_names[] = { |
|||
#define IRTNAME(name, size) #name, |
|||
IRTDEF(IRTNAME) |
|||
#undef IRTNAME |
|||
NULL |
|||
}; |
|||
|
|||
const char *const irfpm_names[] = { |
|||
#define FPMNAME(name) #name, |
|||
IRFPMDEF(FPMNAME) |
|||
#undef FPMNAME |
|||
NULL |
|||
}; |
|||
|
|||
const char *const irfield_names[] = { |
|||
#define FLNAME(name, ofs) #name, |
|||
IRFLDEF(FLNAME) |
|||
#undef FLNAME |
|||
NULL |
|||
}; |
|||
|
|||
const char *const ircall_names[] = { |
|||
#define IRCALLNAME(cond, name, nargs, kind, type, flags) #name, |
|||
IRCALLDEF(IRCALLNAME) |
|||
#undef IRCALLNAME |
|||
NULL |
|||
}; |
|||
|
|||
static const char *const trace_errors[] = { |
|||
#define TREDEF(name, msg) msg, |
|||
#include "lj_traceerr.h" |
|||
NULL |
|||
}; |
|||
|
|||
static const char *lower(char *buf, const char *s) |
|||
{ |
|||
char *p = buf; |
|||
while (*s) { |
|||
*p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s; |
|||
s++; |
|||
} |
|||
*p = '\0'; |
|||
return buf; |
|||
} |
|||
|
|||
/* Emit C source code for bytecode-related definitions. */ |
|||
static void emit_bcdef(BuildCtx *ctx) |
|||
{ |
|||
int i; |
|||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); |
|||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n"); |
|||
for (i = 0; i < ctx->npc; i++) { |
|||
if (i != 0) |
|||
fprintf(ctx->fp, ",\n"); |
|||
fprintf(ctx->fp, "%d", ctx->bc_ofs[i]); |
|||
} |
|||
} |
|||
|
|||
/* Emit VM definitions as Lua code for debug modules. */ |
|||
static void emit_vmdef(BuildCtx *ctx) |
|||
{ |
|||
char buf[80]; |
|||
int i; |
|||
fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n"); |
|||
fprintf(ctx->fp, "return {\n\n"); |
|||
|
|||
fprintf(ctx->fp, "bcnames = \""); |
|||
for (i = 0; bc_names[i]; i++) fprintf(ctx->fp, "%-6s", bc_names[i]); |
|||
fprintf(ctx->fp, "\",\n\n"); |
|||
|
|||
fprintf(ctx->fp, "irnames = \""); |
|||
for (i = 0; ir_names[i]; i++) fprintf(ctx->fp, "%-6s", ir_names[i]); |
|||
fprintf(ctx->fp, "\",\n\n"); |
|||
|
|||
fprintf(ctx->fp, "irfpm = { [0]="); |
|||
for (i = 0; irfpm_names[i]; i++) |
|||
fprintf(ctx->fp, "\"%s\", ", lower(buf, irfpm_names[i])); |
|||
fprintf(ctx->fp, "},\n\n"); |
|||
|
|||
fprintf(ctx->fp, "irfield = { [0]="); |
|||
for (i = 0; irfield_names[i]; i++) { |
|||
char *p; |
|||
lower(buf, irfield_names[i]); |
|||
p = strchr(buf, '_'); |
|||
if (p) *p = '.'; |
|||
fprintf(ctx->fp, "\"%s\", ", buf); |
|||
} |
|||
fprintf(ctx->fp, "},\n\n"); |
|||
|
|||
fprintf(ctx->fp, "ircall = {\n[0]="); |
|||
for (i = 0; ircall_names[i]; i++) |
|||
fprintf(ctx->fp, "\"%s\",\n", ircall_names[i]); |
|||
fprintf(ctx->fp, "},\n\n"); |
|||
|
|||
fprintf(ctx->fp, "traceerr = {\n[0]="); |
|||
for (i = 0; trace_errors[i]; i++) |
|||
fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]); |
|||
fprintf(ctx->fp, "},\n\n"); |
|||
} |
|||
|
|||
/* -- Argument parsing ---------------------------------------------------- */ |
|||
|
|||
/* Build mode names. */ |
|||
static const char *const modenames[] = { |
|||
#define BUILDNAME(name) #name, |
|||
BUILDDEF(BUILDNAME) |
|||
#undef BUILDNAME |
|||
NULL |
|||
}; |
|||
|
|||
/* Print usage information and exit. */ |
|||
static void usage(void) |
|||
{ |
|||
int i; |
|||
fprintf(stderr, LUAJIT_VERSION " VM builder.\n"); |
|||
fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n"); |
|||
fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n"); |
|||
fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n"); |
|||
fprintf(stderr, "Available modes:\n"); |
|||
for (i = 0; i < BUILD__MAX; i++) |
|||
fprintf(stderr, " %s\n", modenames[i]); |
|||
exit(1); |
|||
} |
|||
|
|||
/* Parse the output mode name. */ |
|||
static BuildMode parsemode(const char *mode) |
|||
{ |
|||
int i; |
|||
for (i = 0; modenames[i]; i++) |
|||
if (!strcmp(mode, modenames[i])) |
|||
return (BuildMode)i; |
|||
usage(); |
|||
return (BuildMode)-1; |
|||
} |
|||
|
|||
/* Parse arguments. */ |
|||
static void parseargs(BuildCtx *ctx, char **argv) |
|||
{ |
|||
const char *a; |
|||
int i; |
|||
ctx->mode = (BuildMode)-1; |
|||
ctx->outname = "-"; |
|||
for (i = 1; (a = argv[i]) != NULL; i++) { |
|||
if (a[0] != '-') |
|||
break; |
|||
switch (a[1]) { |
|||
case '-': |
|||
if (a[2]) goto err; |
|||
i++; |
|||
goto ok; |
|||
case '\0': |
|||
goto ok; |
|||
case 'm': |
|||
i++; |
|||
if (a[2] || argv[i] == NULL) goto err; |
|||
ctx->mode = parsemode(argv[i]); |
|||
break; |
|||
case 'o': |
|||
i++; |
|||
if (a[2] || argv[i] == NULL) goto err; |
|||
ctx->outname = argv[i]; |
|||
break; |
|||
default: err: |
|||
usage(); |
|||
break; |
|||
} |
|||
} |
|||
ok: |
|||
ctx->args = argv+i; |
|||
if (ctx->mode == (BuildMode)-1) goto err; |
|||
} |
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
BuildCtx ctx_; |
|||
BuildCtx *ctx = &ctx_; |
|||
int status, binmode; |
|||
|
|||
if (sizeof(void *) != 4*LJ_32+8*LJ_64) { |
|||
fprintf(stderr,"Error: pointer size mismatch in cross-build.\n"); |
|||
fprintf(stderr,"Try: make HOST_CC=\"gcc -m32\" CROSS=...\n\n"); |
|||
return 1; |
|||
} |
|||
|
|||
UNUSED(argc); |
|||
parseargs(ctx, argv); |
|||
|
|||
if ((status = build_code(ctx))) { |
|||
fprintf(stderr,"Error: DASM error %08x\n", status); |
|||
return 1; |
|||
} |
|||
|
|||
switch (ctx->mode) { |
|||
case BUILD_peobj: |
|||
case BUILD_raw: |
|||
binmode = 1; |
|||
break; |
|||
default: |
|||
binmode = 0; |
|||
break; |
|||
} |
|||
|
|||
if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') { |
|||
ctx->fp = stdout; |
|||
#if defined(_WIN32) |
|||
if (binmode) |
|||
_setmode(_fileno(stdout), _O_BINARY); /* Yuck. */ |
|||
#endif |
|||
} else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) { |
|||
fprintf(stderr, "Error: cannot open output file '%s': %s\n", |
|||
ctx->outname, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
|
|||
switch (ctx->mode) { |
|||
case BUILD_elfasm: |
|||
case BUILD_coffasm: |
|||
case BUILD_machasm: |
|||
emit_asm(ctx); |
|||
emit_asm_debug(ctx); |
|||
break; |
|||
case BUILD_peobj: |
|||
emit_peobj(ctx); |
|||
break; |
|||
case BUILD_raw: |
|||
emit_raw(ctx); |
|||
break; |
|||
case BUILD_bcdef: |
|||
emit_bcdef(ctx); |
|||
emit_lib(ctx); |
|||
break; |
|||
case BUILD_vmdef: |
|||
emit_vmdef(ctx); |
|||
emit_lib(ctx); |
|||
fprintf(ctx->fp, "}\n\n"); |
|||
break; |
|||
case BUILD_ffdef: |
|||
case BUILD_libdef: |
|||
case BUILD_recdef: |
|||
emit_lib(ctx); |
|||
break; |
|||
case BUILD_folddef: |
|||
emit_fold(ctx); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
fflush(ctx->fp); |
|||
if (ferror(ctx->fp)) { |
|||
fprintf(stderr, "Error: cannot write to output file: %s\n", |
|||
strerror(errno)); |
|||
exit(1); |
|||
} |
|||
fclose(ctx->fp); |
|||
|
|||
return 0; |
|||
} |
|||
|
@ -0,0 +1,105 @@ |
|||
/*
|
|||
** LuaJIT VM builder. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#ifndef _BUILDVM_H |
|||
#define _BUILDVM_H |
|||
|
|||
#include <sys/types.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <errno.h> |
|||
|
|||
#include "lj_def.h" |
|||
#include "lj_arch.h" |
|||
|
|||
/* Hardcoded limits. Increase as needed. */ |
|||
#define BUILD_MAX_RELOC 200 /* Max. number of relocations. */ |
|||
#define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */ |
|||
|
|||
/* Prefix for scanned library definitions. */ |
|||
#define LIBDEF_PREFIX "LJLIB_" |
|||
|
|||
/* Prefix for scanned fold definitions. */ |
|||
#define FOLDDEF_PREFIX "LJFOLD" |
|||
|
|||
/* Prefixes for generated labels. */ |
|||
#define LABEL_PREFIX "lj_" |
|||
#define LABEL_PREFIX_BC LABEL_PREFIX "BC_" |
|||
#define LABEL_PREFIX_FF LABEL_PREFIX "ff_" |
|||
#define LABEL_PREFIX_CF LABEL_PREFIX "cf_" |
|||
#define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_" |
|||
#define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_" |
|||
#define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_" |
|||
|
|||
/* Forward declaration. */ |
|||
struct dasm_State; |
|||
|
|||
/* Build modes. */ |
|||
#define BUILDDEF(_) \ |
|||
_(elfasm) _(coffasm) _(machasm) _(peobj) _(raw) \ |
|||
_(bcdef) _(ffdef) _(libdef) _(recdef) _(vmdef) \ |
|||
_(folddef) |
|||
|
|||
typedef enum { |
|||
#define BUILDENUM(name) BUILD_##name, |
|||
BUILDDEF(BUILDENUM) |
|||
#undef BUILDENUM |
|||
BUILD__MAX |
|||
} BuildMode; |
|||
|
|||
/* Code relocation. */ |
|||
typedef struct BuildReloc { |
|||
int32_t ofs; |
|||
int sym; |
|||
int type; |
|||
} BuildReloc; |
|||
|
|||
typedef struct BuildSym { |
|||
const char *name; |
|||
int32_t ofs; |
|||
} BuildSym; |
|||
|
|||
/* Build context structure. */ |
|||
typedef struct BuildCtx { |
|||
/* DynASM state pointer. Should be first member. */ |
|||
struct dasm_State *D; |
|||
/* Parsed command line. */ |
|||
BuildMode mode; |
|||
FILE *fp; |
|||
const char *outname; |
|||
char **args; |
|||
/* Code and symbols generated by DynASM. */ |
|||
uint8_t *code; |
|||
size_t codesz; |
|||
int npc, nglob, nsym, nreloc, nrelocsym; |
|||
void **glob; |
|||
BuildSym *sym; |
|||
const char **relocsym; |
|||
int32_t *bc_ofs; |
|||
const char *beginsym; |
|||
/* Strings generated by DynASM. */ |
|||
const char *const *globnames; |
|||
const char *const *extnames; |
|||
const char *dasm_ident; |
|||
const char *dasm_arch; |
|||
/* Relocations. */ |
|||
BuildReloc reloc[BUILD_MAX_RELOC]; |
|||
} BuildCtx; |
|||
|
|||
extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz); |
|||
extern void emit_asm(BuildCtx *ctx); |
|||
extern void emit_peobj(BuildCtx *ctx); |
|||
extern void emit_lib(BuildCtx *ctx); |
|||
extern void emit_fold(BuildCtx *ctx); |
|||
|
|||
extern const char *const bc_names[]; |
|||
extern const char *const ir_names[]; |
|||
extern const char *const irt_names[]; |
|||
extern const char *const irfpm_names[]; |
|||
extern const char *const irfield_names[]; |
|||
extern const char *const ircall_names[]; |
|||
|
|||
#endif |
@ -0,0 +1,348 @@ |
|||
/*
|
|||
** LuaJIT VM builder: Assembler source code emitter. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#include "buildvm.h" |
|||
#include "lj_bc.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#if LJ_TARGET_X86ORX64 |
|||
/* Emit bytes piecewise as assembler text. */ |
|||
static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n) |
|||
{ |
|||
int i; |
|||
for (i = 0; i < n; i++) { |
|||
if ((i & 15) == 0) |
|||
fprintf(ctx->fp, "\t.byte %d", p[i]); |
|||
else |
|||
fprintf(ctx->fp, ",%d", p[i]); |
|||
if ((i & 15) == 15) putc('\n', ctx->fp); |
|||
} |
|||
if ((n & 15) != 0) putc('\n', ctx->fp); |
|||
} |
|||
|
|||
/* Emit relocation */ |
|||
static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym) |
|||
{ |
|||
switch (ctx->mode) { |
|||
case BUILD_elfasm: |
|||
if (type) |
|||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym); |
|||
else |
|||
fprintf(ctx->fp, "\t.long %s\n", sym); |
|||
break; |
|||
case BUILD_coffasm: |
|||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym); |
|||
if (type) |
|||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym); |
|||
else |
|||
fprintf(ctx->fp, "\t.long %s\n", sym); |
|||
break; |
|||
default: /* BUILD_machasm for relative relocations handled below. */ |
|||
fprintf(ctx->fp, "\t.long %s\n", sym); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
static const char *const jccnames[] = { |
|||
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja", |
|||
"js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg" |
|||
}; |
|||
|
|||
/* Emit x86/x64 text relocations. */ |
|||
static void emit_asm_reloc_text(BuildCtx *ctx, uint8_t *cp, int n, |
|||
const char *sym) |
|||
{ |
|||
const char *opname = NULL; |
|||
if (--n < 0) goto err; |
|||
if (cp[n] == 0xe8) { |
|||
opname = "call"; |
|||
} else if (cp[n] == 0xe9) { |
|||
opname = "jmp"; |
|||
} else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) { |
|||
opname = jccnames[cp[n]-0x80]; |
|||
n--; |
|||
} else { |
|||
err: |
|||
fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n", |
|||
sym); |
|||
exit(1); |
|||
} |
|||
emit_asm_bytes(ctx, cp, n); |
|||
if (strncmp(sym+(*sym == '_'), LABEL_PREFIX, sizeof(LABEL_PREFIX)-1)) { |
|||
/* Various fixups for external symbols outside of our binary. */ |
|||
if (ctx->mode == BUILD_elfasm) { |
|||
if (LJ_32) |
|||
fprintf(ctx->fp, "#if __PIC__\n\t%s lj_wrap_%s\n#else\n", opname, sym); |
|||
fprintf(ctx->fp, "\t%s %s@PLT\n", opname, sym); |
|||
if (LJ_32) |
|||
fprintf(ctx->fp, "#endif\n"); |
|||
return; |
|||
} else if (LJ_32 && ctx->mode == BUILD_machasm) { |
|||
fprintf(ctx->fp, "\t%s L%s$stub\n", opname, sym); |
|||
return; |
|||
} |
|||
} |
|||
fprintf(ctx->fp, "\t%s %s\n", opname, sym); |
|||
} |
|||
#else |
|||
/* Emit words piecewise as assembler text. */ |
|||
static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n) |
|||
{ |
|||
int i; |
|||
for (i = 0; i < n; i += 4) { |
|||
uint32_t ins = *(uint32_t *)(p+i); |
|||
#if LJ_TARGET_ARM64 && LJ_BE |
|||
ins = lj_bswap(ins); /* ARM64 instructions are always little-endian. */ |
|||
#endif |
|||
if ((i & 15) == 0) |
|||
fprintf(ctx->fp, "\t.long 0x%08x", ins); |
|||
else |
|||
fprintf(ctx->fp, ",0x%08x", ins); |
|||
if ((i & 15) == 12) putc('\n', ctx->fp); |
|||
} |
|||
if ((n & 15) != 0) putc('\n', ctx->fp); |
|||
} |
|||
|
|||
/* Emit relocation as part of an instruction. */ |
|||
static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n, |
|||
const char *sym) |
|||
{ |
|||
uint32_t ins; |
|||
emit_asm_words(ctx, p, n-4); |
|||
ins = *(uint32_t *)(p+n-4); |
|||
#if LJ_TARGET_ARM |
|||
if ((ins & 0xff000000u) == 0xfa000000u) { |
|||
fprintf(ctx->fp, "\tblx %s\n", sym); |
|||
} else if ((ins & 0x0e000000u) == 0x0a000000u) { |
|||
fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b", |
|||
&"eqnecsccmiplvsvchilsgeltgtle"[2*(ins >> 28)], sym); |
|||
} else { |
|||
fprintf(stderr, |
|||
"Error: unsupported opcode %08x for %s symbol relocation.\n", |
|||
ins, sym); |
|||
exit(1); |
|||
} |
|||
#elif LJ_TARGET_ARM64 |
|||
if ((ins >> 26) == 0x25u) { |
|||
fprintf(ctx->fp, "\tbl %s\n", sym); |
|||
} else { |
|||
fprintf(stderr, |
|||
"Error: unsupported opcode %08x for %s symbol relocation.\n", |
|||
ins, sym); |
|||
exit(1); |
|||
} |
|||
#elif LJ_TARGET_PPC |
|||
#if LJ_TARGET_PS3 |
|||
#define TOCPREFIX "." |
|||
#else |
|||
#define TOCPREFIX "" |
|||
#endif |
|||
if ((ins >> 26) == 16) { |
|||
fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n", |
|||
(ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym); |
|||
} else if ((ins >> 26) == 18) { |
|||
fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym); |
|||
} else { |
|||
fprintf(stderr, |
|||
"Error: unsupported opcode %08x for %s symbol relocation.\n", |
|||
ins, sym); |
|||
exit(1); |
|||
} |
|||
#elif LJ_TARGET_MIPS |
|||
fprintf(stderr, |
|||
"Error: unsupported opcode %08x for %s symbol relocation.\n", |
|||
ins, sym); |
|||
exit(1); |
|||
#else |
|||
#error "missing relocation support for this architecture" |
|||
#endif |
|||
} |
|||
#endif |
|||
|
|||
#if LJ_TARGET_ARM |
|||
#define ELFASM_PX "%%" |
|||
#else |
|||
#define ELFASM_PX "@" |
|||
#endif |
|||
|
|||
/* Emit an assembler label. */ |
|||
static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc) |
|||
{ |
|||
switch (ctx->mode) { |
|||
case BUILD_elfasm: |
|||
#if LJ_TARGET_PS3 |
|||
if (!strncmp(name, "lj_vm_", 6) && |
|||
strcmp(name, ctx->beginsym) && |
|||
!strstr(name, "hook")) { |
|||
fprintf(ctx->fp, |
|||
"\n\t.globl %s\n" |
|||
"\t.section \".opd\",\"aw\"\n" |
|||
"%s:\n" |
|||
"\t.long .%s,.TOC.@tocbase32\n" |
|||
"\t.size %s,8\n" |
|||
"\t.previous\n" |
|||
"\t.globl .%s\n" |
|||
"\t.hidden .%s\n" |
|||
"\t.type .%s, " ELFASM_PX "function\n" |
|||
"\t.size .%s, %d\n" |
|||
".%s:\n", |
|||
name, name, name, name, name, name, name, name, size, name); |
|||
break; |
|||
} |
|||
#endif |
|||
fprintf(ctx->fp, |
|||
"\n\t.globl %s\n" |
|||
"\t.hidden %s\n" |
|||
"\t.type %s, " ELFASM_PX "%s\n" |
|||
"\t.size %s, %d\n" |
|||
"%s:\n", |
|||
name, name, name, isfunc ? "function" : "object", name, size, name); |
|||
break; |
|||
case BUILD_coffasm: |
|||
fprintf(ctx->fp, "\n\t.globl %s\n", name); |
|||
if (isfunc) |
|||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name); |
|||
fprintf(ctx->fp, "%s:\n", name); |
|||
break; |
|||
case BUILD_machasm: |
|||
fprintf(ctx->fp, |
|||
"\n\t.private_extern %s\n" |
|||
"\t.no_dead_strip %s\n" |
|||
"%s:\n", name, name, name); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* Emit alignment. */ |
|||
static void emit_asm_align(BuildCtx *ctx, int bits) |
|||
{ |
|||
switch (ctx->mode) { |
|||
case BUILD_elfasm: |
|||
case BUILD_coffasm: |
|||
fprintf(ctx->fp, "\t.p2align %d\n", bits); |
|||
break; |
|||
case BUILD_machasm: |
|||
fprintf(ctx->fp, "\t.align %d\n", bits); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* Emit assembler source code. */ |
|||
void emit_asm(BuildCtx *ctx) |
|||
{ |
|||
int i, rel; |
|||
|
|||
fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch); |
|||
fprintf(ctx->fp, "\t.text\n"); |
|||
emit_asm_align(ctx, 4); |
|||
|
|||
#if LJ_TARGET_PS3 |
|||
emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0); |
|||
#else |
|||
emit_asm_label(ctx, ctx->beginsym, 0, 0); |
|||
#endif |
|||
if (ctx->mode != BUILD_machasm) |
|||
fprintf(ctx->fp, ".Lbegin:\n"); |
|||
|
|||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND |
|||
/* This should really be moved into buildvm_arm.dasc. */ |
|||
#if LJ_ARCH_HASFPU |
|||
fprintf(ctx->fp, |
|||
".fnstart\n" |
|||
".save {r5, r6, r7, r8, r9, r10, r11, lr}\n" |
|||
".vsave {d8-d15}\n" |
|||
".save {r4}\n" |
|||
".pad #28\n"); |
|||
#else |
|||
fprintf(ctx->fp, |
|||
".fnstart\n" |
|||
".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n" |
|||
".pad #28\n"); |
|||
#endif |
|||
#endif |
|||
#if LJ_TARGET_MIPS |
|||
fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n"); |
|||
#endif |
|||
|
|||
for (i = rel = 0; i < ctx->nsym; i++) { |
|||
int32_t ofs = ctx->sym[i].ofs; |
|||
int32_t next = ctx->sym[i+1].ofs; |
|||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI |
|||
if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call")) |
|||
fprintf(ctx->fp, |
|||
".globl lj_err_unwind_arm\n" |
|||
".personality lj_err_unwind_arm\n" |
|||
".fnend\n" |
|||
".fnstart\n" |
|||
".save {r4, r5, r11, lr}\n" |
|||
".setfp r11, sp\n"); |
|||
#endif |
|||
emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1); |
|||
while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) { |
|||
BuildReloc *r = &ctx->reloc[rel]; |
|||
int n = r->ofs - ofs; |
|||
#if LJ_TARGET_X86ORX64 |
|||
if (r->type != 0 && |
|||
(ctx->mode == BUILD_elfasm || ctx->mode == BUILD_machasm)) { |
|||
emit_asm_reloc_text(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); |
|||
} else { |
|||
emit_asm_bytes(ctx, ctx->code+ofs, n); |
|||
emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]); |
|||
} |
|||
ofs += n+4; |
|||
#else |
|||
emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]); |
|||
ofs += n; |
|||
#endif |
|||
rel++; |
|||
} |
|||
#if LJ_TARGET_X86ORX64 |
|||
emit_asm_bytes(ctx, ctx->code+ofs, next-ofs); |
|||
#else |
|||
emit_asm_words(ctx, ctx->code+ofs, next-ofs); |
|||
#endif |
|||
} |
|||
|
|||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND |
|||
fprintf(ctx->fp, |
|||
#if !LJ_HASFFI |
|||
".globl lj_err_unwind_arm\n" |
|||
".personality lj_err_unwind_arm\n" |
|||
#endif |
|||
".fnend\n"); |
|||
#endif |
|||
|
|||
fprintf(ctx->fp, "\n"); |
|||
switch (ctx->mode) { |
|||
case BUILD_elfasm: |
|||
#if !(LJ_TARGET_PS3 || LJ_TARGET_PSVITA) |
|||
fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n"); |
|||
#endif |
|||
#if LJ_TARGET_PPC && !LJ_TARGET_PS3 && !LJ_ABI_SOFTFP |
|||
/* Hard-float ABI. */ |
|||
fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n"); |
|||
#endif |
|||
/* fallthrough */ |
|||
case BUILD_coffasm: |
|||
fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident); |
|||
break; |
|||
case BUILD_machasm: |
|||
fprintf(ctx->fp, |
|||
"\t.cstring\n" |
|||
"\t.ascii \"%s\\0\"\n", ctx->dasm_ident); |
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
fprintf(ctx->fp, "\n"); |
|||
} |
|||
|
@ -0,0 +1,229 @@ |
|||
/*
|
|||
** LuaJIT VM builder: IR folding hash table generator. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#include "buildvm.h" |
|||
#include "lj_obj.h" |
|||
#include "lj_ir.h" |
|||
|
|||
/* Context for the folding hash table generator. */ |
|||
static int lineno; |
|||
static uint32_t funcidx; |
|||
static uint32_t foldkeys[BUILD_MAX_FOLD]; |
|||
static uint32_t nkeys; |
|||
|
|||
/* Try to fill the hash table with keys using the hash parameters. */ |
|||
static int tryhash(uint32_t *htab, uint32_t sz, uint32_t r, int dorol) |
|||
{ |
|||
uint32_t i; |
|||
if (dorol && ((r & 31) == 0 || (r>>5) == 0)) |
|||
return 0; /* Avoid zero rotates. */ |
|||
memset(htab, 0xff, (sz+1)*sizeof(uint32_t)); |
|||
for (i = 0; i < nkeys; i++) { |
|||
uint32_t key = foldkeys[i]; |
|||
uint32_t k = key & 0xffffff; |
|||
uint32_t h = (dorol ? lj_rol(lj_rol(k, r>>5) - k, r&31) : |
|||
(((k << (r>>5)) - k) << (r&31))) % sz; |
|||
if (htab[h] != 0xffffffff) { /* Collision on primary slot. */ |
|||
if (htab[h+1] != 0xffffffff) { /* Collision on secondary slot. */ |
|||
/* Try to move the colliding key, if possible. */ |
|||
if (h < sz-1 && htab[h+2] == 0xffffffff) { |
|||
uint32_t k2 = htab[h+1] & 0xffffff; |
|||
uint32_t h2 = (dorol ? lj_rol(lj_rol(k2, r>>5) - k2, r&31) : |
|||
(((k2 << (r>>5)) - k2) << (r&31))) % sz; |
|||
if (h2 != h+1) return 0; /* Cannot resolve collision. */ |
|||
htab[h+2] = htab[h+1]; /* Move colliding key to secondary slot. */ |
|||
} else { |
|||
return 0; /* Collision. */ |
|||
} |
|||
} |
|||
htab[h+1] = key; |
|||
} else { |
|||
htab[h] = key; |
|||
} |
|||
} |
|||
return 1; /* Success, all keys could be stored. */ |
|||
} |
|||
|
|||
/* Print the generated hash table. */ |
|||
static void printhash(BuildCtx *ctx, uint32_t *htab, uint32_t sz) |
|||
{ |
|||
uint32_t i; |
|||
fprintf(ctx->fp, "static const uint32_t fold_hash[%d] = {\n0x%08x", |
|||
sz+1, htab[0]); |
|||
for (i = 1; i < sz+1; i++) |
|||
fprintf(ctx->fp, ",\n0x%08x", htab[i]); |
|||
fprintf(ctx->fp, "\n};\n\n"); |
|||
} |
|||
|
|||
/* Exhaustive search for the shortest semi-perfect hash table. */ |
|||
static void makehash(BuildCtx *ctx) |
|||
{ |
|||
uint32_t htab[BUILD_MAX_FOLD*2+1]; |
|||
uint32_t sz, r; |
|||
/* Search for the smallest hash table with an odd size. */ |
|||
for (sz = (nkeys|1); sz < BUILD_MAX_FOLD*2; sz += 2) { |
|||
/* First try all shift hash combinations. */ |
|||
for (r = 0; r < 32*32; r++) { |
|||
if (tryhash(htab, sz, r, 0)) { |
|||
printhash(ctx, htab, sz); |
|||
fprintf(ctx->fp, |
|||
"#define fold_hashkey(k)\t(((((k)<<%u)-(k))<<%u)%%%u)\n\n", |
|||
r>>5, r&31, sz); |
|||
return; |
|||
} |
|||
} |
|||
/* Then try all rotate hash combinations. */ |
|||
for (r = 0; r < 32*32; r++) { |
|||
if (tryhash(htab, sz, r, 1)) { |
|||
printhash(ctx, htab, sz); |
|||
fprintf(ctx->fp, |
|||
"#define fold_hashkey(k)\t(lj_rol(lj_rol((k),%u)-(k),%u)%%%u)\n\n", |
|||
r>>5, r&31, sz); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
fprintf(stderr, "Error: search for perfect hash failed\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
/* Parse one token of a fold rule. */ |
|||
static uint32_t nexttoken(char **pp, int allowlit, int allowany) |
|||
{ |
|||
char *p = *pp; |
|||
if (p) { |
|||
uint32_t i; |
|||
char *q = strchr(p, ' '); |
|||
if (q) *q++ = '\0'; |
|||
*pp = q; |
|||
if (allowlit && !strncmp(p, "IRFPM_", 6)) { |
|||
for (i = 0; irfpm_names[i]; i++) |
|||
if (!strcmp(irfpm_names[i], p+6)) |
|||
return i; |
|||
} else if (allowlit && !strncmp(p, "IRFL_", 5)) { |
|||
for (i = 0; irfield_names[i]; i++) |
|||
if (!strcmp(irfield_names[i], p+5)) |
|||
return i; |
|||
} else if (allowlit && !strncmp(p, "IRCALL_", 7)) { |
|||
for (i = 0; ircall_names[i]; i++) |
|||
if (!strcmp(ircall_names[i], p+7)) |
|||
return i; |
|||
} else if (allowlit && !strncmp(p, "IRCONV_", 7)) { |
|||
for (i = 0; irt_names[i]; i++) { |
|||
const char *r = strchr(p+7, '_'); |
|||
if (r && !strncmp(irt_names[i], p+7, r-(p+7))) { |
|||
uint32_t j; |
|||
for (j = 0; irt_names[j]; j++) |
|||
if (!strcmp(irt_names[j], r+1)) |
|||
return (i << 5) + j; |
|||
} |
|||
} |
|||
} else if (allowlit && *p >= '0' && *p <= '9') { |
|||
for (i = 0; *p >= '0' && *p <= '9'; p++) |
|||
i = i*10 + (*p - '0'); |
|||
if (*p == '\0') |
|||
return i; |
|||
} else if (allowany && !strcmp("any", p)) { |
|||
return allowany; |
|||
} else { |
|||
for (i = 0; ir_names[i]; i++) |
|||
if (!strcmp(ir_names[i], p)) |
|||
return i; |
|||
} |
|||
fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno); |
|||
exit(1); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* Parse a fold rule. */ |
|||
static void foldrule(char *p) |
|||
{ |
|||
uint32_t op = nexttoken(&p, 0, 0); |
|||
uint32_t left = nexttoken(&p, 0, 0x7f); |
|||
uint32_t right = nexttoken(&p, 1, 0x3ff); |
|||
uint32_t key = (funcidx << 24) | (op << 17) | (left << 10) | right; |
|||
uint32_t i; |
|||
if (nkeys >= BUILD_MAX_FOLD) { |
|||
fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n"); |
|||
exit(1); |
|||
} |
|||
/* Simple insertion sort to detect duplicates. */ |
|||
for (i = nkeys; i > 0; i--) { |
|||
if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff)) |
|||
break; |
|||
if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) { |
|||
fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno); |
|||
exit(1); |
|||
} |
|||
foldkeys[i] = foldkeys[i-1]; |
|||
} |
|||
foldkeys[i] = key; |
|||
nkeys++; |
|||
} |
|||
|
|||
/* Emit C source code for IR folding hash table. */ |
|||
void emit_fold(BuildCtx *ctx) |
|||
{ |
|||
char buf[256]; /* We don't care about analyzing lines longer than that. */ |
|||
const char *fname = ctx->args[0]; |
|||
FILE *fp; |
|||
|
|||
if (fname == NULL) { |
|||
fprintf(stderr, "Error: missing input filename\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
if (fname[0] == '-' && fname[1] == '\0') { |
|||
fp = stdin; |
|||
} else { |
|||
fp = fopen(fname, "r"); |
|||
if (!fp) { |
|||
fprintf(stderr, "Error: cannot open input file '%s': %s\n", |
|||
fname, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); |
|||
fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n"); |
|||
|
|||
lineno = 0; |
|||
funcidx = 0; |
|||
nkeys = 0; |
|||
while (fgets(buf, sizeof(buf), fp) != NULL) { |
|||
lineno++; |
|||
/* The prefix must be at the start of a line, otherwise it's ignored. */ |
|||
if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) { |
|||
char *p = buf+sizeof(FOLDDEF_PREFIX)-1; |
|||
char *q = strchr(p, ')'); |
|||
if (p[0] == '(' && q) { |
|||
p++; |
|||
*q = '\0'; |
|||
foldrule(p); |
|||
} else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) { |
|||
p += 2; |
|||
*q = '\0'; |
|||
if (funcidx) |
|||
fprintf(ctx->fp, ",\n"); |
|||
if (p[-2] == 'X') |
|||
fprintf(ctx->fp, " %s", p); |
|||
else |
|||
fprintf(ctx->fp, " fold_%s", p); |
|||
funcidx++; |
|||
} else { |
|||
buf[strlen(buf)-1] = '\0'; |
|||
fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n", |
|||
FOLDDEF_PREFIX, p, lineno); |
|||
exit(1); |
|||
} |
|||
} |
|||
} |
|||
fclose(fp); |
|||
fprintf(ctx->fp, "\n};\n\n"); |
|||
|
|||
makehash(ctx); |
|||
} |
|||
|
@ -0,0 +1,459 @@ |
|||
/*
|
|||
** LuaJIT VM builder: library definition compiler. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#include "buildvm.h" |
|||
#include "lj_obj.h" |
|||
#include "lj_bc.h" |
|||
#include "lj_lib.h" |
|||
#include "buildvm_libbc.h" |
|||
|
|||
/* Context for library definitions. */ |
|||
static uint8_t obuf[8192]; |
|||
static uint8_t *optr; |
|||
static char modname[80]; |
|||
static size_t modnamelen; |
|||
static char funcname[80]; |
|||
static int modstate, regfunc; |
|||
static int ffid, recffid, ffasmfunc; |
|||
|
|||
enum { |
|||
REGFUNC_OK, |
|||
REGFUNC_NOREG, |
|||
REGFUNC_NOREGUV |
|||
}; |
|||
|
|||
static void libdef_name(const char *p, int kind) |
|||
{ |
|||
size_t n = strlen(p); |
|||
if (kind != LIBINIT_STRING) { |
|||
if (n > modnamelen && p[modnamelen] == '_' && |
|||
!strncmp(p, modname, modnamelen)) { |
|||
p += modnamelen+1; |
|||
n -= modnamelen+1; |
|||
} |
|||
} |
|||
if (n > LIBINIT_MAXSTR) { |
|||
fprintf(stderr, "Error: string too long: '%s'\n", p); |
|||
exit(1); |
|||
} |
|||
if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */ |
|||
fprintf(stderr, "Error: output buffer overflow\n"); |
|||
exit(1); |
|||
} |
|||
*optr++ = (uint8_t)(n | kind); |
|||
memcpy(optr, p, n); |
|||
optr += n; |
|||
} |
|||
|
|||
static void libdef_endmodule(BuildCtx *ctx) |
|||
{ |
|||
if (modstate != 0) { |
|||
char line[80]; |
|||
const uint8_t *p; |
|||
int n; |
|||
if (modstate == 1) |
|||
fprintf(ctx->fp, " (lua_CFunction)0"); |
|||
fprintf(ctx->fp, "\n};\n"); |
|||
fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n", |
|||
LABEL_PREFIX_LIBINIT, modname); |
|||
line[0] = '\0'; |
|||
for (n = 0, p = obuf; p < optr; p++) { |
|||
n += sprintf(line+n, "%d,", *p); |
|||
if (n >= 75) { |
|||
fprintf(ctx->fp, "%s\n", line); |
|||
n = 0; |
|||
line[0] = '\0'; |
|||
} |
|||
} |
|||
fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END); |
|||
} |
|||
} |
|||
|
|||
static void libdef_module(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
UNUSED(arg); |
|||
if (ctx->mode == BUILD_libdef) { |
|||
libdef_endmodule(ctx); |
|||
optr = obuf; |
|||
*optr++ = (uint8_t)ffid; |
|||
*optr++ = (uint8_t)ffasmfunc; |
|||
*optr++ = 0; /* Hash table size. */ |
|||
modstate = 1; |
|||
fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p); |
|||
fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p); |
|||
fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n", |
|||
LABEL_PREFIX_LIBCF, p); |
|||
} |
|||
modnamelen = strlen(p); |
|||
if (modnamelen > sizeof(modname)-1) { |
|||
fprintf(stderr, "Error: module name too long: '%s'\n", p); |
|||
exit(1); |
|||
} |
|||
strcpy(modname, p); |
|||
} |
|||
|
|||
static int find_ffofs(BuildCtx *ctx, const char *name) |
|||
{ |
|||
int i; |
|||
for (i = 0; i < ctx->nglob; i++) { |
|||
const char *gl = ctx->globnames[i]; |
|||
if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) { |
|||
return (int)((uint8_t *)ctx->glob[i] - ctx->code); |
|||
} |
|||
} |
|||
fprintf(stderr, "Error: undefined fast function %s%s\n", |
|||
LABEL_PREFIX_FF, name); |
|||
exit(1); |
|||
} |
|||
|
|||
static void libdef_func(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
if (arg != LIBINIT_CF) |
|||
ffasmfunc++; |
|||
if (ctx->mode == BUILD_libdef) { |
|||
if (modstate == 0) { |
|||
fprintf(stderr, "Error: no module for function definition %s\n", p); |
|||
exit(1); |
|||
} |
|||
if (regfunc == REGFUNC_NOREG) { |
|||
if (optr+1 > obuf+sizeof(obuf)) { |
|||
fprintf(stderr, "Error: output buffer overflow\n"); |
|||
exit(1); |
|||
} |
|||
*optr++ = LIBINIT_FFID; |
|||
} else { |
|||
if (arg != LIBINIT_ASM_) { |
|||
if (modstate != 1) fprintf(ctx->fp, ",\n"); |
|||
modstate = 2; |
|||
fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p); |
|||
} |
|||
if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */ |
|||
libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg); |
|||
} |
|||
} else if (ctx->mode == BUILD_ffdef) { |
|||
fprintf(ctx->fp, "FFDEF(%s)\n", p); |
|||
} else if (ctx->mode == BUILD_recdef) { |
|||
if (strlen(p) > sizeof(funcname)-1) { |
|||
fprintf(stderr, "Error: function name too long: '%s'\n", p); |
|||
exit(1); |
|||
} |
|||
strcpy(funcname, p); |
|||
} else if (ctx->mode == BUILD_vmdef) { |
|||
int i; |
|||
for (i = 1; p[i] && modname[i-1]; i++) |
|||
if (p[i] == '_') p[i] = '.'; |
|||
fprintf(ctx->fp, "\"%s\",\n", p); |
|||
} else if (ctx->mode == BUILD_bcdef) { |
|||
if (arg != LIBINIT_CF) |
|||
fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p)); |
|||
} |
|||
ffid++; |
|||
regfunc = REGFUNC_OK; |
|||
} |
|||
|
|||
static uint8_t *libdef_uleb128(uint8_t *p, uint32_t *vv) |
|||
{ |
|||
uint32_t v = *p++; |
|||
if (v >= 0x80) { |
|||
int sh = 0; v &= 0x7f; |
|||
do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80); |
|||
} |
|||
*vv = v; |
|||
return p; |
|||
} |
|||
|
|||
static void libdef_fixupbc(uint8_t *p) |
|||
{ |
|||
uint32_t i, sizebc; |
|||
p += 4; |
|||
p = libdef_uleb128(p, &sizebc); |
|||
p = libdef_uleb128(p, &sizebc); |
|||
p = libdef_uleb128(p, &sizebc); |
|||
for (i = 0; i < sizebc; i++, p += 4) { |
|||
uint8_t op = p[libbc_endian ? 3 : 0]; |
|||
uint8_t ra = p[libbc_endian ? 2 : 1]; |
|||
uint8_t rc = p[libbc_endian ? 1 : 2]; |
|||
uint8_t rb = p[libbc_endian ? 0 : 3]; |
|||
if (!LJ_DUALNUM && op == BC_ISTYPE && rc == ~LJ_TNUMX+1) { |
|||
op = BC_ISNUM; rc++; |
|||
} |
|||
p[LJ_ENDIAN_SELECT(0, 3)] = op; |
|||
p[LJ_ENDIAN_SELECT(1, 2)] = ra; |
|||
p[LJ_ENDIAN_SELECT(2, 1)] = rc; |
|||
p[LJ_ENDIAN_SELECT(3, 0)] = rb; |
|||
} |
|||
} |
|||
|
|||
static void libdef_lua(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
UNUSED(arg); |
|||
if (ctx->mode == BUILD_libdef) { |
|||
int i; |
|||
for (i = 0; libbc_map[i].name != NULL; i++) { |
|||
if (!strcmp(libbc_map[i].name, p)) { |
|||
int ofs = libbc_map[i].ofs; |
|||
int len = libbc_map[i+1].ofs - ofs; |
|||
obuf[2]++; /* Bump hash table size. */ |
|||
*optr++ = LIBINIT_LUA; |
|||
libdef_name(p, 0); |
|||
memcpy(optr, libbc_code + ofs, len); |
|||
libdef_fixupbc(optr); |
|||
optr += len; |
|||
return; |
|||
} |
|||
} |
|||
fprintf(stderr, "Error: missing libbc definition for %s\n", p); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
static uint32_t find_rec(char *name) |
|||
{ |
|||
char *p = (char *)obuf; |
|||
uint32_t n; |
|||
for (n = 2; *p; n++) { |
|||
if (strcmp(p, name) == 0) |
|||
return n; |
|||
p += strlen(p)+1; |
|||
} |
|||
if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) { |
|||
fprintf(stderr, "Error: output buffer overflow\n"); |
|||
exit(1); |
|||
} |
|||
strcpy(p, name); |
|||
return n; |
|||
} |
|||
|
|||
static void libdef_rec(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
UNUSED(arg); |
|||
if (ctx->mode == BUILD_recdef) { |
|||
char *q; |
|||
uint32_t n; |
|||
for (; recffid+1 < ffid; recffid++) |
|||
fprintf(ctx->fp, ",\n0"); |
|||
recffid = ffid; |
|||
if (*p == '.') p = funcname; |
|||
q = strchr(p, ' '); |
|||
if (q) *q++ = '\0'; |
|||
n = find_rec(p); |
|||
if (q) |
|||
fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q); |
|||
else |
|||
fprintf(ctx->fp, ",\n0x%02x00", n); |
|||
} |
|||
} |
|||
|
|||
static void memcpy_endian(void *dst, void *src, size_t n) |
|||
{ |
|||
union { uint8_t b; uint32_t u; } host_endian; |
|||
host_endian.u = 1; |
|||
if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) { |
|||
memcpy(dst, src, n); |
|||
} else { |
|||
size_t i; |
|||
for (i = 0; i < n; i++) |
|||
((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1]; |
|||
} |
|||
} |
|||
|
|||
static void libdef_push(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
UNUSED(arg); |
|||
if (ctx->mode == BUILD_libdef) { |
|||
int len = (int)strlen(p); |
|||
if (*p == '"') { |
|||
if (len > 1 && p[len-1] == '"') { |
|||
p[len-1] = '\0'; |
|||
libdef_name(p+1, LIBINIT_STRING); |
|||
return; |
|||
} |
|||
} else if (*p >= '0' && *p <= '9') { |
|||
char *ep; |
|||
double d = strtod(p, &ep); |
|||
if (*ep == '\0') { |
|||
if (optr+1+sizeof(double) > obuf+sizeof(obuf)) { |
|||
fprintf(stderr, "Error: output buffer overflow\n"); |
|||
exit(1); |
|||
} |
|||
*optr++ = LIBINIT_NUMBER; |
|||
memcpy_endian(optr, &d, sizeof(double)); |
|||
optr += sizeof(double); |
|||
return; |
|||
} |
|||
} else if (!strcmp(p, "lastcl")) { |
|||
if (optr+1 > obuf+sizeof(obuf)) { |
|||
fprintf(stderr, "Error: output buffer overflow\n"); |
|||
exit(1); |
|||
} |
|||
*optr++ = LIBINIT_LASTCL; |
|||
return; |
|||
} else if (len > 4 && !strncmp(p, "top-", 4)) { |
|||
if (optr+2 > obuf+sizeof(obuf)) { |
|||
fprintf(stderr, "Error: output buffer overflow\n"); |
|||
exit(1); |
|||
} |
|||
*optr++ = LIBINIT_COPY; |
|||
*optr++ = (uint8_t)atoi(p+4); |
|||
return; |
|||
} |
|||
fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p); |
|||
exit(1); |
|||
} |
|||
} |
|||
|
|||
static void libdef_set(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
UNUSED(arg); |
|||
if (ctx->mode == BUILD_libdef) { |
|||
if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */ |
|||
libdef_name(p, LIBINIT_STRING); |
|||
*optr++ = LIBINIT_SET; |
|||
obuf[2]++; /* Bump hash table size. */ |
|||
} |
|||
} |
|||
|
|||
static void libdef_regfunc(BuildCtx *ctx, char *p, int arg) |
|||
{ |
|||
UNUSED(ctx); UNUSED(p); |
|||
regfunc = arg; |
|||
} |
|||
|
|||
typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg); |
|||
|
|||
typedef struct LibDefHandler { |
|||
const char *suffix; |
|||
const char *stop; |
|||
const LibDefFunc func; |
|||
const int arg; |
|||
} LibDefHandler; |
|||
|
|||
static const LibDefHandler libdef_handlers[] = { |
|||
{ "MODULE_", " \t\r\n", libdef_module, 0 }, |
|||
{ "CF(", ")", libdef_func, LIBINIT_CF }, |
|||
{ "ASM(", ")", libdef_func, LIBINIT_ASM }, |
|||
{ "ASM_(", ")", libdef_func, LIBINIT_ASM_ }, |
|||
{ "LUA(", ")", libdef_lua, 0 }, |
|||
{ "REC(", ")", libdef_rec, 0 }, |
|||
{ "PUSH(", ")", libdef_push, 0 }, |
|||
{ "SET(", ")", libdef_set, 0 }, |
|||
{ "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV }, |
|||
{ "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG }, |
|||
{ NULL, NULL, (LibDefFunc)0, 0 } |
|||
}; |
|||
|
|||
/* Emit C source code for library function definitions. */ |
|||
void emit_lib(BuildCtx *ctx) |
|||
{ |
|||
const char *fname; |
|||
|
|||
if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef || |
|||
ctx->mode == BUILD_recdef) |
|||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n"); |
|||
else if (ctx->mode == BUILD_vmdef) |
|||
fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n"); |
|||
if (ctx->mode == BUILD_recdef) |
|||
fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100"); |
|||
recffid = ffid = FF_C+1; |
|||
ffasmfunc = 0; |
|||
|
|||
while ((fname = *ctx->args++)) { |
|||
char buf[256]; /* We don't care about analyzing lines longer than that. */ |
|||
FILE *fp; |
|||
if (fname[0] == '-' && fname[1] == '\0') { |
|||
fp = stdin; |
|||
} else { |
|||
fp = fopen(fname, "r"); |
|||
if (!fp) { |
|||
fprintf(stderr, "Error: cannot open input file '%s': %s\n", |
|||
fname, strerror(errno)); |
|||
exit(1); |
|||
} |
|||
} |
|||
modstate = 0; |
|||
regfunc = REGFUNC_OK; |
|||
while (fgets(buf, sizeof(buf), fp) != NULL) { |
|||
char *p; |
|||
/* Simplistic pre-processor. Only handles top-level #if/#endif. */ |
|||
if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') { |
|||
int ok = 1; |
|||
if (!strcmp(buf, "#if LJ_52\n")) |
|||
ok = LJ_52; |
|||
else if (!strcmp(buf, "#if LJ_HASJIT\n")) |
|||
ok = LJ_HASJIT; |
|||
else if (!strcmp(buf, "#if LJ_HASFFI\n")) |
|||
ok = LJ_HASFFI; |
|||
else if (!strcmp(buf, "#if LJ_HASBUFFER\n")) |
|||
ok = LJ_HASBUFFER; |
|||
if (!ok) { |
|||
int lvl = 1; |
|||
while (fgets(buf, sizeof(buf), fp) != NULL) { |
|||
if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') { |
|||
if (--lvl == 0) break; |
|||
} else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') { |
|||
lvl++; |
|||
} |
|||
} |
|||
continue; |
|||
} |
|||
} |
|||
for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) { |
|||
const LibDefHandler *ldh; |
|||
p += sizeof(LIBDEF_PREFIX)-1; |
|||
for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) { |
|||
size_t n, len = strlen(ldh->suffix); |
|||
if (!strncmp(p, ldh->suffix, len)) { |
|||
p += len; |
|||
n = ldh->stop ? strcspn(p, ldh->stop) : 0; |
|||
if (!p[n]) break; |
|||
p[n] = '\0'; |
|||
ldh->func(ctx, p, ldh->arg); |
|||
p += n+1; |
|||
break; |
|||
} |
|||
} |
|||
if (ldh->suffix == NULL) { |
|||
buf[strlen(buf)-1] = '\0'; |
|||
fprintf(stderr, "Error: unknown library definition tag %s%s\n", |
|||
LIBDEF_PREFIX, p); |
|||
exit(1); |
|||
} |
|||
} |
|||
} |
|||
fclose(fp); |
|||
if (ctx->mode == BUILD_libdef) { |
|||
libdef_endmodule(ctx); |
|||
} |
|||
} |
|||
|
|||
if (ctx->mode == BUILD_ffdef) { |
|||
fprintf(ctx->fp, "\n#undef FFDEF\n\n"); |
|||
fprintf(ctx->fp, |
|||
"#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n", |
|||
ffasmfunc); |
|||
} else if (ctx->mode == BUILD_vmdef) { |
|||
fprintf(ctx->fp, "},\n\n"); |
|||
} else if (ctx->mode == BUILD_bcdef) { |
|||
int i; |
|||
fprintf(ctx->fp, "\n};\n\n"); |
|||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n"); |
|||
fprintf(ctx->fp, "BCDEF(BCMODE)\n"); |
|||
for (i = ffasmfunc-1; i > 0; i--) |
|||
fprintf(ctx->fp, "BCMODE_FF,\n"); |
|||
fprintf(ctx->fp, "BCMODE_FF\n};\n\n"); |
|||
} else if (ctx->mode == BUILD_recdef) { |
|||
char *p = (char *)obuf; |
|||
fprintf(ctx->fp, "\n};\n\n"); |
|||
fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n" |
|||
"recff_nyi,\n" |
|||
"recff_c"); |
|||
while (*p) { |
|||
fprintf(ctx->fp, ",\nrecff_%s", p); |
|||
p += strlen(p)+1; |
|||
} |
|||
fprintf(ctx->fp, "\n};\n\n"); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,56 @@ |
|||
/* This is a generated file. DO NOT EDIT! */ |
|||
|
|||
static const int libbc_endian = 0; |
|||
|
|||
static const uint8_t libbc_code[] = { |
|||
#if LJ_FR2 |
|||
0,1,2,0,0,1,2,24,1,0,0,76,1,2,0,241,135,158,166,3,220,203,178,130,4,0,1,2,0, |
|||
0,1,2,24,1,0,0,76,1,2,0,243,244,148,165,20,198,190,199,252,3,0,1,2,0,0,0,3, |
|||
16,0,5,0,21,1,0,0,76,1,2,0,0,2,10,0,0,0,15,16,0,12,0,16,1,9,0,41,2,1,0,21,3, |
|||
0,0,41,4,1,0,77,2,8,128,18,6,1,0,18,8,5,0,59,9,5,0,66,6,3,2,10,6,0,0,88,7,1, |
|||
128,76,6,2,0,79,2,248,127,75,0,1,0,0,2,11,0,0,0,16,16,0,12,0,16,1,9,0,43,2, |
|||
0,0,18,3,0,0,41,4,0,0,88,5,7,128,18,7,1,0,18,9,5,0,18,10,6,0,66,7,3,2,10,7, |
|||
0,0,88,8,1,128,76,7,2,0,70,5,3,3,82,5,247,127,75,0,1,0,0,1,2,0,0,0,3,16,0,12, |
|||
0,21,1,0,0,76,1,2,0,0,2,10,0,0,2,30,16,0,12,0,21,2,0,0,11,1,0,0,88,3,7,128, |
|||
8,2,0,0,88,3,23,128,59,3,2,0,43,4,0,0,64,4,2,0,76,3,2,0,88,3,18,128,16,1,14, |
|||
0,41,3,1,0,3,3,1,0,88,3,14,128,3,1,2,0,88,3,12,128,59,3,1,0,22,4,1,1,18,5,2, |
|||
0,41,6,1,0,77,4,4,128,23,8,1,7,59,9,7,0,64,9,8,0,79,4,252,127,43,4,0,0,64,4, |
|||
2,0,76,3,2,0,75,0,1,0,0,2,0,5,12,0,0,0,35,16,0,12,0,16,1,14,0,16,2,14,0,16, |
|||
3,14,0,11,4,0,0,88,5,1,128,18,4,0,0,16,4,12,0,3,1,2,0,88,5,24,128,33,5,1,3, |
|||
0,2,3,0,88,6,4,128,2,3,1,0,88,6,2,128,4,4,0,0,88,6,9,128,18,6,1,0,18,7,2,0, |
|||
41,8,1,0,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79,6,252,127,88,6,8,128, |
|||
18,6,2,0,18,7,1,0,41,8,255,255,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79, |
|||
6,252,127,76,4,2,0,0 |
|||
#else |
|||
0,1,2,0,0,1,2,24,1,0,0,76,1,2,0,241,135,158,166,3,220,203,178,130,4,0,1,2,0, |
|||
0,1,2,24,1,0,0,76,1,2,0,243,244,148,165,20,198,190,199,252,3,0,1,2,0,0,0,3, |
|||
16,0,5,0,21,1,0,0,76,1,2,0,0,2,9,0,0,0,15,16,0,12,0,16,1,9,0,41,2,1,0,21,3, |
|||
0,0,41,4,1,0,77,2,8,128,18,6,1,0,18,7,5,0,59,8,5,0,66,6,3,2,10,6,0,0,88,7,1, |
|||
128,76,6,2,0,79,2,248,127,75,0,1,0,0,2,10,0,0,0,16,16,0,12,0,16,1,9,0,43,2, |
|||
0,0,18,3,0,0,41,4,0,0,88,5,7,128,18,7,1,0,18,8,5,0,18,9,6,0,66,7,3,2,10,7,0, |
|||
0,88,8,1,128,76,7,2,0,70,5,3,3,82,5,247,127,75,0,1,0,0,1,2,0,0,0,3,16,0,12, |
|||
0,21,1,0,0,76,1,2,0,0,2,10,0,0,2,30,16,0,12,0,21,2,0,0,11,1,0,0,88,3,7,128, |
|||
8,2,0,0,88,3,23,128,59,3,2,0,43,4,0,0,64,4,2,0,76,3,2,0,88,3,18,128,16,1,14, |
|||
0,41,3,1,0,3,3,1,0,88,3,14,128,3,1,2,0,88,3,12,128,59,3,1,0,22,4,1,1,18,5,2, |
|||
0,41,6,1,0,77,4,4,128,23,8,1,7,59,9,7,0,64,9,8,0,79,4,252,127,43,4,0,0,64,4, |
|||
2,0,76,3,2,0,75,0,1,0,0,2,0,5,12,0,0,0,35,16,0,12,0,16,1,14,0,16,2,14,0,16, |
|||
3,14,0,11,4,0,0,88,5,1,128,18,4,0,0,16,4,12,0,3,1,2,0,88,5,24,128,33,5,1,3, |
|||
0,2,3,0,88,6,4,128,2,3,1,0,88,6,2,128,4,4,0,0,88,6,9,128,18,6,1,0,18,7,2,0, |
|||
41,8,1,0,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79,6,252,127,88,6,8,128, |
|||
18,6,2,0,18,7,1,0,41,8,255,255,77,6,4,128,32,10,5,9,59,11,9,0,64,11,10,4,79, |
|||
6,252,127,76,4,2,0,0 |
|||
#endif |
|||
}; |
|||
|
|||
static const struct { const char *name; int ofs; } libbc_map[] = { |
|||
{"math_deg",0}, |
|||
{"math_rad",25}, |
|||
{"string_len",50}, |
|||
{"table_foreachi",69}, |
|||
{"table_foreach",136}, |
|||
{"table_getn",207}, |
|||
{"table_remove",226}, |
|||
{"table_move",355}, |
|||
{NULL,502} |
|||
}; |
|||
|
@ -0,0 +1,379 @@ |
|||
/*
|
|||
** LuaJIT VM builder: PE object emitter. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Only used for building on Windows, since we cannot assume the presence |
|||
** of a suitable assembler. The host and target byte order must match. |
|||
*/ |
|||
|
|||
#include "buildvm.h" |
|||
#include "lj_bc.h" |
|||
|
|||
#if LJ_TARGET_X86ORX64 |
|||
|
|||
/* Context for PE object emitter. */ |
|||
static char *strtab; |
|||
static size_t strtabofs; |
|||
|
|||
/* -- PE object definitions ----------------------------------------------- */ |
|||
|
|||
/* PE header. */ |
|||
typedef struct PEheader { |
|||
uint16_t arch; |
|||
uint16_t nsects; |
|||
uint32_t time; |
|||
uint32_t symtabofs; |
|||
uint32_t nsyms; |
|||
uint16_t opthdrsz; |
|||
uint16_t flags; |
|||
} PEheader; |
|||
|
|||
/* PE section. */ |
|||
typedef struct PEsection { |
|||
char name[8]; |
|||
uint32_t vsize; |
|||
uint32_t vaddr; |
|||
uint32_t size; |
|||
uint32_t ofs; |
|||
uint32_t relocofs; |
|||
uint32_t lineofs; |
|||
uint16_t nreloc; |
|||
uint16_t nline; |
|||
uint32_t flags; |
|||
} PEsection; |
|||
|
|||
/* PE relocation. */ |
|||
typedef struct PEreloc { |
|||
uint32_t vaddr; |
|||
uint32_t symidx; |
|||
uint16_t type; |
|||
} PEreloc; |
|||
|
|||
/* Cannot use sizeof, because it pads up to the max. alignment. */ |
|||
#define PEOBJ_RELOC_SIZE (4+4+2) |
|||
|
|||
/* PE symbol table entry. */ |
|||
typedef struct PEsym { |
|||
union { |
|||
char name[8]; |
|||
uint32_t nameref[2]; |
|||
} n; |
|||
uint32_t value; |
|||
int16_t sect; |
|||
uint16_t type; |
|||
uint8_t scl; |
|||
uint8_t naux; |
|||
} PEsym; |
|||
|
|||
/* PE symbol table auxiliary entry for a section. */ |
|||
typedef struct PEsymaux { |
|||
uint32_t size; |
|||
uint16_t nreloc; |
|||
uint16_t nline; |
|||
uint32_t cksum; |
|||
uint16_t assoc; |
|||
uint8_t comdatsel; |
|||
uint8_t unused[3]; |
|||
} PEsymaux; |
|||
|
|||
/* Cannot use sizeof, because it pads up to the max. alignment. */ |
|||
#define PEOBJ_SYM_SIZE (8+4+2+2+1+1) |
|||
|
|||
/* PE object CPU specific defines. */ |
|||
#if LJ_TARGET_X86 |
|||
#define PEOBJ_ARCH_TARGET 0x014c |
|||
#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */ |
|||
#define PEOBJ_RELOC_DIR32 0x06 |
|||
#define PEOBJ_RELOC_OFS 0 |
|||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */ |
|||
#elif LJ_TARGET_X64 |
|||
#define PEOBJ_ARCH_TARGET 0x8664 |
|||
#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */ |
|||
#define PEOBJ_RELOC_DIR32 0x02 |
|||
#define PEOBJ_RELOC_ADDR32NB 0x03 |
|||
#define PEOBJ_RELOC_OFS 0 |
|||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */ |
|||
#endif |
|||
|
|||
/* Section numbers (0-based). */ |
|||
enum { |
|||
PEOBJ_SECT_ABS = -2, |
|||
PEOBJ_SECT_UNDEF = -1, |
|||
PEOBJ_SECT_TEXT, |
|||
#if LJ_TARGET_X64 |
|||
PEOBJ_SECT_PDATA, |
|||
PEOBJ_SECT_XDATA, |
|||
#elif LJ_TARGET_X86 |
|||
PEOBJ_SECT_SXDATA, |
|||
#endif |
|||
PEOBJ_SECT_RDATA_Z, |
|||
PEOBJ_NSECTIONS |
|||
}; |
|||
|
|||
/* Symbol types. */ |
|||
#define PEOBJ_TYPE_NULL 0 |
|||
#define PEOBJ_TYPE_FUNC 0x20 |
|||
|
|||
/* Symbol storage class. */ |
|||
#define PEOBJ_SCL_EXTERN 2 |
|||
#define PEOBJ_SCL_STATIC 3 |
|||
|
|||
/* -- PE object emitter --------------------------------------------------- */ |
|||
|
|||
/* Emit PE object symbol. */ |
|||
static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value, |
|||
int sect, int type, int scl) |
|||
{ |
|||
PEsym sym; |
|||
size_t len = strlen(name); |
|||
if (!strtab) { /* Pass 1: only calculate string table length. */ |
|||
if (len > 8) strtabofs += len+1; |
|||
return; |
|||
} |
|||
if (len <= 8) { |
|||
memcpy(sym.n.name, name, len); |
|||
memset(sym.n.name+len, 0, 8-len); |
|||
} else { |
|||
sym.n.nameref[0] = 0; |
|||
sym.n.nameref[1] = (uint32_t)strtabofs; |
|||
memcpy(strtab + strtabofs, name, len); |
|||
strtab[strtabofs+len] = 0; |
|||
strtabofs += len+1; |
|||
} |
|||
sym.value = value; |
|||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */ |
|||
sym.type = (uint16_t)type; |
|||
sym.scl = (uint8_t)scl; |
|||
sym.naux = 0; |
|||
owrite(ctx, &sym, PEOBJ_SYM_SIZE); |
|||
} |
|||
|
|||
/* Emit PE object section symbol. */ |
|||
static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect) |
|||
{ |
|||
PEsym sym; |
|||
PEsymaux aux; |
|||
if (!strtab) return; /* Pass 1: no output. */ |
|||
memcpy(sym.n.name, pesect[sect].name, 8); |
|||
sym.value = 0; |
|||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */ |
|||
sym.type = PEOBJ_TYPE_NULL; |
|||
sym.scl = PEOBJ_SCL_STATIC; |
|||
sym.naux = 1; |
|||
owrite(ctx, &sym, PEOBJ_SYM_SIZE); |
|||
memset(&aux, 0, sizeof(PEsymaux)); |
|||
aux.size = pesect[sect].size; |
|||
aux.nreloc = pesect[sect].nreloc; |
|||
owrite(ctx, &aux, PEOBJ_SYM_SIZE); |
|||
} |
|||
|
|||
/* Emit Windows PE object file. */ |
|||
void emit_peobj(BuildCtx *ctx) |
|||
{ |
|||
PEheader pehdr; |
|||
PEsection pesect[PEOBJ_NSECTIONS]; |
|||
uint32_t sofs; |
|||
int i, nrsym; |
|||
union { uint8_t b; uint32_t u; } host_endian; |
|||
|
|||
sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection); |
|||
|
|||
/* Fill in PE sections. */ |
|||
memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection)); |
|||
memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1); |
|||
pesect[PEOBJ_SECT_TEXT].ofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz); |
|||
pesect[PEOBJ_SECT_TEXT].relocofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE; |
|||
/* Flags: 60 = read+execute, 50 = align16, 20 = code. */ |
|||
pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS; |
|||
|
|||
#if LJ_TARGET_X64 |
|||
memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1); |
|||
pesect[PEOBJ_SECT_PDATA].ofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4); |
|||
pesect[PEOBJ_SECT_PDATA].relocofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE; |
|||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */ |
|||
pesect[PEOBJ_SECT_PDATA].flags = 0x40300040; |
|||
|
|||
memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1); |
|||
pesect[PEOBJ_SECT_XDATA].ofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2); /* See below. */ |
|||
pesect[PEOBJ_SECT_XDATA].relocofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE; |
|||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */ |
|||
pesect[PEOBJ_SECT_XDATA].flags = 0x40300040; |
|||
#elif LJ_TARGET_X86 |
|||
memcpy(pesect[PEOBJ_SECT_SXDATA].name, ".sxdata", sizeof(".sxdata")-1); |
|||
pesect[PEOBJ_SECT_SXDATA].ofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_SXDATA].size = 4); |
|||
pesect[PEOBJ_SECT_SXDATA].relocofs = sofs; |
|||
/* Flags: 40 = read, 30 = align4, 02 = lnk_info, 40 = initialized data. */ |
|||
pesect[PEOBJ_SECT_SXDATA].flags = 0x40300240; |
|||
#endif |
|||
|
|||
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1); |
|||
pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs; |
|||
sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1); |
|||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */ |
|||
pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040; |
|||
|
|||
/* Fill in PE header. */ |
|||
pehdr.arch = PEOBJ_ARCH_TARGET; |
|||
pehdr.nsects = PEOBJ_NSECTIONS; |
|||
pehdr.time = 0; /* Timestamp is optional. */ |
|||
pehdr.symtabofs = sofs; |
|||
pehdr.opthdrsz = 0; |
|||
pehdr.flags = 0; |
|||
|
|||
/* Compute the size of the symbol table:
|
|||
** @feat.00 + nsections*2 |
|||
** + asm_start + nsym |
|||
** + nrsym |
|||
*/ |
|||
nrsym = ctx->nrelocsym; |
|||
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym; |
|||
#if LJ_TARGET_X64 |
|||
pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win. */ |
|||
#endif |
|||
|
|||
/* Write PE object header and all sections. */ |
|||
owrite(ctx, &pehdr, sizeof(PEheader)); |
|||
owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS); |
|||
|
|||
/* Write .text section. */ |
|||
host_endian.u = 1; |
|||
if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) { |
|||
fprintf(stderr, "Error: different byte order for host and target\n"); |
|||
exit(1); |
|||
} |
|||
owrite(ctx, ctx->code, ctx->codesz); |
|||
for (i = 0; i < ctx->nreloc; i++) { |
|||
PEreloc reloc; |
|||
reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS; |
|||
reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */ |
|||
reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
} |
|||
|
|||
#if LJ_TARGET_X64 |
|||
{ /* Write .pdata section. */ |
|||
uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs; |
|||
uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */ |
|||
PEreloc reloc; |
|||
pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0; |
|||
owrite(ctx, &pdata, sizeof(pdata)); |
|||
pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20; |
|||
owrite(ctx, &pdata, sizeof(pdata)); |
|||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
} |
|||
{ /* Write .xdata section. */ |
|||
uint16_t xdata[8+2+6]; |
|||
PEreloc reloc; |
|||
xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */ |
|||
xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */ |
|||
xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */ |
|||
xdata[3] = 0x3000; /* Push rbx. */ |
|||
xdata[4] = 0x6000; /* Push rsi. */ |
|||
xdata[5] = 0x7000; /* Push rdi. */ |
|||
xdata[6] = 0x5000; /* Push rbp. */ |
|||
xdata[7] = 0; /* Alignment. */ |
|||
xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */ |
|||
xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */ |
|||
xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */ |
|||
xdata[12] = 0x0300; /* set_fpreg. */ |
|||
xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */ |
|||
xdata[14] = 0x3000; /* Push rbx. */ |
|||
xdata[15] = 0x5000; /* Push rbp. */ |
|||
owrite(ctx, &xdata, sizeof(xdata)); |
|||
reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2; |
|||
reloc.type = PEOBJ_RELOC_ADDR32NB; |
|||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); |
|||
} |
|||
#elif LJ_TARGET_X86 |
|||
/* Write .sxdata section. */ |
|||
for (i = 0; i < nrsym; i++) { |
|||
if (!strcmp(ctx->relocsym[i], "_lj_err_unwind_win")) { |
|||
uint32_t symidx = 1+2+i; |
|||
owrite(ctx, &symidx, 4); |
|||
break; |
|||
} |
|||
} |
|||
if (i == nrsym) { |
|||
fprintf(stderr, "Error: extern lj_err_unwind_win not used\n"); |
|||
exit(1); |
|||
} |
|||
#endif |
|||
|
|||
/* Write .rdata$Z section. */ |
|||
owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1); |
|||
|
|||
/* Write symbol table. */ |
|||
strtab = NULL; /* 1st pass: collect string sizes. */ |
|||
for (;;) { |
|||
strtabofs = 4; |
|||
/* Mark as SafeSEH compliant. */ |
|||
emit_peobj_sym(ctx, "@feat.00", 1, |
|||
PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC); |
|||
|
|||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT); |
|||
for (i = 0; i < nrsym; i++) |
|||
emit_peobj_sym(ctx, ctx->relocsym[i], 0, |
|||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); |
|||
|
|||
#if LJ_TARGET_X64 |
|||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA); |
|||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA); |
|||
emit_peobj_sym(ctx, "lj_err_unwind_win", 0, |
|||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); |
|||
#elif LJ_TARGET_X86 |
|||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_SXDATA); |
|||
#endif |
|||
|
|||
emit_peobj_sym(ctx, ctx->beginsym, 0, |
|||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN); |
|||
for (i = 0; i < ctx->nsym; i++) |
|||
emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs, |
|||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); |
|||
|
|||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z); |
|||
|
|||
if (strtab) |
|||
break; |
|||
/* 2nd pass: alloc strtab, write syms and copy strings. */ |
|||
strtab = (char *)malloc(strtabofs); |
|||
*(uint32_t *)strtab = (uint32_t)strtabofs; |
|||
} |
|||
|
|||
/* Write string table. */ |
|||
owrite(ctx, strtab, strtabofs); |
|||
} |
|||
|
|||
#else |
|||
|
|||
void emit_peobj(BuildCtx *ctx) |
|||
{ |
|||
UNUSED(ctx); |
|||
fprintf(stderr, "Error: no PE object support for this target\n"); |
|||
exit(1); |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,197 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- Lua script to dump the bytecode of the library functions written in Lua. |
|||
-- The resulting 'buildvm_libbc.h' is used for the build process of LuaJIT. |
|||
---------------------------------------------------------------------------- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
|
|||
local ffi = require("ffi") |
|||
local bit = require("bit") |
|||
local vmdef = require("jit.vmdef") |
|||
local bcnames = vmdef.bcnames |
|||
|
|||
local format = string.format |
|||
|
|||
local isbe = (string.byte(string.dump(function() end), 5) % 2 == 1) |
|||
|
|||
local function usage(arg) |
|||
io.stderr:write("Usage: ", arg and arg[0] or "genlibbc", |
|||
" [-o buildvm_libbc.h] lib_*.c\n") |
|||
os.exit(1) |
|||
end |
|||
|
|||
local function parse_arg(arg) |
|||
local outfile = "-" |
|||
if not (arg and arg[1]) then |
|||
usage(arg) |
|||
end |
|||
if arg[1] == "-o" then |
|||
outfile = arg[2] |
|||
if not outfile then usage(arg) end |
|||
table.remove(arg, 1) |
|||
table.remove(arg, 1) |
|||
end |
|||
return outfile |
|||
end |
|||
|
|||
local function read_files(names) |
|||
local src = "" |
|||
for _,name in ipairs(names) do |
|||
local fp = assert(io.open(name)) |
|||
src = src .. fp:read("*a") |
|||
fp:close() |
|||
end |
|||
return src |
|||
end |
|||
|
|||
local function transform_lua(code) |
|||
local fixup = {} |
|||
local n = -30000 |
|||
code = string.gsub(code, "CHECK_(%w*)%((.-)%)", function(tp, var) |
|||
n = n + 1 |
|||
fixup[n] = { "CHECK", tp } |
|||
return format("%s=%d", var, n) |
|||
end) |
|||
code = string.gsub(code, "PAIRS%((.-)%)", function(var) |
|||
fixup.PAIRS = true |
|||
return format("nil, %s, 0", var) |
|||
end) |
|||
return "return "..code, fixup |
|||
end |
|||
|
|||
local function read_uleb128(p) |
|||
local v = p[0]; p = p + 1 |
|||
if v >= 128 then |
|||
local sh = 7; v = v - 128 |
|||
repeat |
|||
local r = p[0] |
|||
v = v + bit.lshift(bit.band(r, 127), sh) |
|||
sh = sh + 7 |
|||
p = p + 1 |
|||
until r < 128 |
|||
end |
|||
return p, v |
|||
end |
|||
|
|||
-- ORDER LJ_T |
|||
local name2itype = { |
|||
str = 5, func = 9, tab = 12, int = 14, num = 15 |
|||
} |
|||
|
|||
local BC = {} |
|||
for i=0,#bcnames/6-1 do |
|||
BC[string.gsub(string.sub(bcnames, i*6+1, i*6+6), " ", "")] = i |
|||
end |
|||
local xop, xra = isbe and 3 or 0, isbe and 2 or 1 |
|||
local xrc, xrb = isbe and 1 or 2, isbe and 0 or 3 |
|||
|
|||
local function fixup_dump(dump, fixup) |
|||
local buf = ffi.new("uint8_t[?]", #dump+1, dump) |
|||
local p = buf+5 |
|||
local n, sizebc |
|||
p, n = read_uleb128(p) |
|||
local start = p |
|||
p = p + 4 |
|||
p = read_uleb128(p) |
|||
p = read_uleb128(p) |
|||
p, sizebc = read_uleb128(p) |
|||
local rawtab = {} |
|||
for i=0,sizebc-1 do |
|||
local op = p[xop] |
|||
if op == BC.KSHORT then |
|||
local rd = p[xrc] + 256*p[xrb] |
|||
rd = bit.arshift(bit.lshift(rd, 16), 16) |
|||
local f = fixup[rd] |
|||
if f then |
|||
if f[1] == "CHECK" then |
|||
local tp = f[2] |
|||
if tp == "tab" then rawtab[p[xra]] = true end |
|||
p[xop] = tp == "num" and BC.ISNUM or BC.ISTYPE |
|||
p[xrb] = 0 |
|||
p[xrc] = name2itype[tp] |
|||
else |
|||
error("unhandled fixup type: "..f[1]) |
|||
end |
|||
end |
|||
elseif op == BC.TGETV then |
|||
if rawtab[p[xrb]] then |
|||
p[xop] = BC.TGETR |
|||
end |
|||
elseif op == BC.TSETV then |
|||
if rawtab[p[xrb]] then |
|||
p[xop] = BC.TSETR |
|||
end |
|||
elseif op == BC.ITERC then |
|||
if fixup.PAIRS then |
|||
p[xop] = BC.ITERN |
|||
end |
|||
end |
|||
p = p + 4 |
|||
end |
|||
return ffi.string(start, n) |
|||
end |
|||
|
|||
local function find_defs(src) |
|||
local defs = {} |
|||
for name, code in string.gmatch(src, "LJLIB_LUA%(([^)]*)%)%s*/%*(.-)%*/") do |
|||
local env = {} |
|||
local tcode, fixup = transform_lua(code) |
|||
local func = assert(load(tcode, "", nil, env))() |
|||
defs[name] = fixup_dump(string.dump(func, true), fixup) |
|||
defs[#defs+1] = name |
|||
end |
|||
return defs |
|||
end |
|||
|
|||
local function gen_header(defs) |
|||
local t = {} |
|||
local function w(x) t[#t+1] = x end |
|||
w("/* This is a generated file. DO NOT EDIT! */\n\n") |
|||
w("static const int libbc_endian = ") w(isbe and 1 or 0) w(";\n\n") |
|||
local s = "" |
|||
for _,name in ipairs(defs) do |
|||
s = s .. defs[name] |
|||
end |
|||
w("static const uint8_t libbc_code[] = {\n") |
|||
local n = 0 |
|||
for i=1,#s do |
|||
local x = string.byte(s, i) |
|||
w(x); w(",") |
|||
n = n + (x < 10 and 2 or (x < 100 and 3 or 4)) |
|||
if n >= 75 then n = 0; w("\n") end |
|||
end |
|||
w("0\n};\n\n") |
|||
w("static const struct { const char *name; int ofs; } libbc_map[] = {\n") |
|||
local m = 0 |
|||
for _,name in ipairs(defs) do |
|||
w('{"'); w(name); w('",'); w(m) w('},\n') |
|||
m = m + #defs[name] |
|||
end |
|||
w("{NULL,"); w(m); w("}\n};\n\n") |
|||
return table.concat(t) |
|||
end |
|||
|
|||
local function write_file(name, data) |
|||
if name == "-" then |
|||
assert(io.write(data)) |
|||
assert(io.flush()) |
|||
else |
|||
local fp = io.open(name) |
|||
if fp then |
|||
local old = fp:read("*a") |
|||
fp:close() |
|||
if data == old then return end |
|||
end |
|||
fp = assert(io.open(name, "w")) |
|||
assert(fp:write(data)) |
|||
assert(fp:close()) |
|||
end |
|||
end |
|||
|
|||
local outfile = parse_arg(arg) |
|||
local src = read_files(arg) |
|||
local defs = find_defs(src) |
|||
local hdr = gen_header(defs) |
|||
write_file(outfile, hdr) |
|||
|
@ -0,0 +1,429 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- Lua script to generate a customized, minified version of Lua. |
|||
-- The resulting 'minilua' is used for the build process of LuaJIT. |
|||
---------------------------------------------------------------------------- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
|
|||
local sub, match, gsub = string.sub, string.match, string.gsub |
|||
|
|||
local LUA_VERSION = "5.1.5" |
|||
local LUA_SOURCE |
|||
|
|||
local function usage() |
|||
io.stderr:write("Usage: ", arg and arg[0] or "genminilua", |
|||
" lua-", LUA_VERSION, "-source-dir\n") |
|||
os.exit(1) |
|||
end |
|||
|
|||
local function find_sources() |
|||
LUA_SOURCE = arg and arg[1] |
|||
if not LUA_SOURCE then usage() end |
|||
if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end |
|||
local fp = io.open(LUA_SOURCE .. "lua.h") |
|||
if not fp then |
|||
LUA_SOURCE = LUA_SOURCE.."src/" |
|||
fp = io.open(LUA_SOURCE .. "lua.h") |
|||
if not fp then usage() end |
|||
end |
|||
local all = fp:read("*a") |
|||
fp:close() |
|||
if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then |
|||
io.stderr:write("Error: version mismatch\n") |
|||
usage() |
|||
end |
|||
end |
|||
|
|||
local LUA_FILES = { |
|||
"lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c", |
|||
"lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c", |
|||
"llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c", |
|||
"lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c", |
|||
} |
|||
|
|||
local REMOVE_LIB = {} |
|||
gsub([[ |
|||
collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset |
|||
select tostring xpcall |
|||
foreach foreachi getn maxn setn |
|||
popen tmpfile seek setvbuf __tostring |
|||
clock date difftime execute getenv rename setlocale time tmpname |
|||
dump gfind len reverse |
|||
LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME |
|||
]], "%S+", function(name) |
|||
REMOVE_LIB[name] = true |
|||
end) |
|||
|
|||
local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, } |
|||
|
|||
local CUSTOM_MAIN = [[ |
|||
typedef unsigned int UB; |
|||
static UB barg(lua_State *L,int idx){ |
|||
union{lua_Number n;U64 b;}bn; |
|||
bn.n=lua_tonumber(L,idx)+6755399441055744.0; |
|||
if (bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number"); |
|||
return(UB)bn.b; |
|||
} |
|||
#define BRET(b) lua_pushnumber(L,(lua_Number)(int)(b));return 1; |
|||
static int tobit(lua_State *L){ |
|||
BRET(barg(L,1))} |
|||
static int bnot(lua_State *L){ |
|||
BRET(~barg(L,1))} |
|||
static int band(lua_State *L){ |
|||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)} |
|||
static int bor(lua_State *L){ |
|||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)} |
|||
static int bxor(lua_State *L){ |
|||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)} |
|||
static int lshift(lua_State *L){ |
|||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)} |
|||
static int rshift(lua_State *L){ |
|||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)} |
|||
static int arshift(lua_State *L){ |
|||
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)} |
|||
static int rol(lua_State *L){ |
|||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))} |
|||
static int ror(lua_State *L){ |
|||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))} |
|||
static int bswap(lua_State *L){ |
|||
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)} |
|||
static int tohex(lua_State *L){ |
|||
UB b=barg(L,1); |
|||
int n=lua_isnone(L,2)?8:(int)barg(L,2); |
|||
const char *hexdigits="0123456789abcdef"; |
|||
char buf[8]; |
|||
int i; |
|||
if(n<0){n=-n;hexdigits="0123456789ABCDEF";} |
|||
if(n>8)n=8; |
|||
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;} |
|||
lua_pushlstring(L,buf,(size_t)n); |
|||
return 1; |
|||
} |
|||
static const struct luaL_Reg bitlib[] = { |
|||
{"tobit",tobit}, |
|||
{"bnot",bnot}, |
|||
{"band",band}, |
|||
{"bor",bor}, |
|||
{"bxor",bxor}, |
|||
{"lshift",lshift}, |
|||
{"rshift",rshift}, |
|||
{"arshift",arshift}, |
|||
{"rol",rol}, |
|||
{"ror",ror}, |
|||
{"bswap",bswap}, |
|||
{"tohex",tohex}, |
|||
{NULL,NULL} |
|||
}; |
|||
int main(int argc, char **argv){ |
|||
lua_State *L = luaL_newstate(); |
|||
int i; |
|||
luaL_openlibs(L); |
|||
luaL_register(L, "bit", bitlib); |
|||
if (argc < 2) return sizeof(void *); |
|||
lua_createtable(L, 0, 1); |
|||
lua_pushstring(L, argv[1]); |
|||
lua_rawseti(L, -2, 0); |
|||
lua_setglobal(L, "arg"); |
|||
if (luaL_loadfile(L, argv[1])) |
|||
goto err; |
|||
for (i = 2; i < argc; i++) |
|||
lua_pushstring(L, argv[i]); |
|||
if (lua_pcall(L, argc - 2, 0, 0)) { |
|||
err: |
|||
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1)); |
|||
return 1; |
|||
} |
|||
lua_close(L); |
|||
return 0; |
|||
} |
|||
]] |
|||
|
|||
local function read_sources() |
|||
local t = {} |
|||
for i, name in ipairs(LUA_FILES) do |
|||
local fp = assert(io.open(LUA_SOURCE..name, "r")) |
|||
t[i] = fp:read("*a") |
|||
assert(fp:close()) |
|||
end |
|||
t[#t+1] = CUSTOM_MAIN |
|||
return table.concat(t) |
|||
end |
|||
|
|||
local includes = {} |
|||
|
|||
local function merge_includes(src) |
|||
return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name) |
|||
if includes[name] then return "" end |
|||
includes[name] = true |
|||
local fp = assert(io.open(LUA_SOURCE..name, "r")) |
|||
local inc = fp:read("*a") |
|||
assert(fp:close()) |
|||
inc = gsub(inc, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "") |
|||
inc = gsub(inc, "#endif%s*$", "") |
|||
return merge_includes(inc) |
|||
end) |
|||
end |
|||
|
|||
local function get_license(src) |
|||
return match(src, "/%*+\n%* Copyright %(.-%*/\n") |
|||
end |
|||
|
|||
local function fold_lines(src) |
|||
return gsub(src, "\\\n", " ") |
|||
end |
|||
|
|||
local strings = {} |
|||
|
|||
local function save_str(str) |
|||
local n = #strings+1 |
|||
strings[n] = str |
|||
return "\1"..n.."\2" |
|||
end |
|||
|
|||
local function save_strings(src) |
|||
src = gsub(src, '"[^"\n]*"', save_str) |
|||
return gsub(src, "'[^'\n]*'", save_str) |
|||
end |
|||
|
|||
local function restore_strings(src) |
|||
return gsub(src, "\1(%d+)\2", function(numstr) |
|||
return strings[tonumber(numstr)] |
|||
end) |
|||
end |
|||
|
|||
local function def_istrue(def) |
|||
return def == "INT_MAX > 2147483640L" or |
|||
def == "LUAI_BITSINT >= 32" or |
|||
def == "SIZE_Bx < LUAI_BITSINT-1" or |
|||
def == "cast" or |
|||
def == "defined(LUA_CORE)" or |
|||
def == "MINSTRTABSIZE" or |
|||
def == "LUA_MINBUFFER" or |
|||
def == "HARDSTACKTESTS" or |
|||
def == "UNUSED" |
|||
end |
|||
|
|||
local head, defs = {[[ |
|||
#ifdef _MSC_VER |
|||
typedef unsigned __int64 U64; |
|||
#else |
|||
typedef unsigned long long U64; |
|||
#endif |
|||
int _CRT_glob = 0; |
|||
]]}, {} |
|||
|
|||
local function preprocess(src) |
|||
local t = { match(src, "^(.-)#") } |
|||
local lvl, on, oldon = 0, true, {} |
|||
for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do |
|||
if pp == "if" or pp == "ifdef" or pp == "ifndef" then |
|||
lvl = lvl + 1 |
|||
oldon[lvl] = on |
|||
on = def_istrue(def) |
|||
elseif pp == "else" then |
|||
if oldon[lvl] then |
|||
if on == false then on = true else on = false end |
|||
end |
|||
elseif pp == "elif" then |
|||
if oldon[lvl] then |
|||
on = def_istrue(def) |
|||
end |
|||
elseif pp == "endif" then |
|||
on = oldon[lvl] |
|||
lvl = lvl - 1 |
|||
elseif on then |
|||
if pp == "include" then |
|||
if not head[def] and not REMOVE_EXTINC[def] then |
|||
head[def] = true |
|||
head[#head+1] = "#include "..def.."\n" |
|||
end |
|||
elseif pp == "define" then |
|||
local k, sp, v = match(def, "([%w_]+)(%s*)(.*)") |
|||
if k and not (sp == "" and sub(v, 1, 1) == "(") then |
|||
defs[k] = gsub(v, "%a[%w_]*", function(tok) |
|||
return defs[tok] or tok |
|||
end) |
|||
else |
|||
t[#t+1] = "#define "..def.."\n" |
|||
end |
|||
elseif pp ~= "undef" then |
|||
error("unexpected directive: "..pp.." "..def) |
|||
end |
|||
end |
|||
if on then t[#t+1] = txt end |
|||
end |
|||
return gsub(table.concat(t), "%a[%w_]*", function(tok) |
|||
return defs[tok] or tok |
|||
end) |
|||
end |
|||
|
|||
local function merge_header(src, license) |
|||
local hdr = string.format([[ |
|||
/* This is a heavily customized and minimized copy of Lua %s. */ |
|||
/* It's only used to build LuaJIT. It does NOT have all standard functions! */ |
|||
]], LUA_VERSION) |
|||
return hdr..license..table.concat(head)..src |
|||
end |
|||
|
|||
local function strip_unused1(src) |
|||
return gsub(src, '( {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func) |
|||
return REMOVE_LIB[func] and "" or line |
|||
end) |
|||
end |
|||
|
|||
local function strip_unused2(src) |
|||
return gsub(src, "Symbolic Execution.-}=", "") |
|||
end |
|||
|
|||
local function strip_unused3(src) |
|||
src = gsub(src, "extern", "static") |
|||
src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(") |
|||
src = gsub(src, "#define lua_assert[^\n]*\n", "") |
|||
src = gsub(src, "lua_assert%b();?", "") |
|||
src = gsub(src, "default:\n}", "default:;\n}") |
|||
src = gsub(src, "lua_lock%b();", "") |
|||
src = gsub(src, "lua_unlock%b();", "") |
|||
src = gsub(src, "luai_threadyield%b();", "") |
|||
src = gsub(src, "luai_userstateopen%b();", "{}") |
|||
src = gsub(src, "luai_userstate%w+%b();", "") |
|||
src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser") |
|||
src = gsub(src, "trydecpoint%(ls,seminfo%)", |
|||
"luaX_lexerror(ls,\"malformed number\",TK_NUMBER)") |
|||
src = gsub(src, "int c=luaZ_lookahead%b();", "") |
|||
src = gsub(src, "luaL_register%(L,[^,]*,co_funcs%);\nreturn 2;", |
|||
"return 1;") |
|||
src = gsub(src, "getfuncname%b():", "NULL:") |
|||
src = gsub(src, "getobjname%b():", "NULL:") |
|||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "") |
|||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "") |
|||
src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "") |
|||
src = gsub(src, "(twoto%b()%()", "%1(size_t)") |
|||
src = gsub(src, "i<sizenode", "i<(int)sizenode") |
|||
src = gsub(src, "cast%(unsigned int,key%-1%)", "cast(unsigned int,key)-1") |
|||
return gsub(src, "\n\n+", "\n") |
|||
end |
|||
|
|||
local function strip_comments(src) |
|||
return gsub(src, "/%*.-%*/", " ") |
|||
end |
|||
|
|||
local function strip_whitespace(src) |
|||
src = gsub(src, "^%s+", "") |
|||
src = gsub(src, "%s*\n%s*", "\n") |
|||
src = gsub(src, "[ \t]+", " ") |
|||
src = gsub(src, "(%W) ", "%1") |
|||
return gsub(src, " (%W)", "%1") |
|||
end |
|||
|
|||
local function rename_tokens1(src) |
|||
src = gsub(src, "getline", "getline_") |
|||
src = gsub(src, "struct ([%w_]+)", "ZX%1") |
|||
return gsub(src, "union ([%w_]+)", "ZY%1") |
|||
end |
|||
|
|||
local function rename_tokens2(src) |
|||
src = gsub(src, "ZX([%w_]+)", "struct %1") |
|||
return gsub(src, "ZY([%w_]+)", "union %1") |
|||
end |
|||
|
|||
local function func_gather(src) |
|||
local nodes, list = {}, {} |
|||
local pos, len = 1, #src |
|||
while pos < len do |
|||
local d, w = match(src, "^(#define ([%w_]+)[^\n]*\n)", pos) |
|||
if d then |
|||
local n = #list+1 |
|||
list[n] = d |
|||
nodes[w] = n |
|||
else |
|||
local s |
|||
d, w, s = match(src, "^(([%w_]+)[^\n]*([{;])\n)", pos) |
|||
if not d then |
|||
d, w, s = match(src, "^(([%w_]+)[^(]*%b()([{;])\n)", pos) |
|||
if not d then d = match(src, "^[^\n]*\n", pos) end |
|||
end |
|||
if s == "{" then |
|||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3) |
|||
if sub(d, -2) == "{\n" then |
|||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3) |
|||
end |
|||
end |
|||
local k, v = nil, d |
|||
if w == "typedef" then |
|||
if match(d, "^typedef enum") then |
|||
head[#head+1] = d |
|||
else |
|||
k = match(d, "([%w_]+);\n$") |
|||
if not k then k = match(d, "^.-%(.-([%w_]+)%)%(") end |
|||
end |
|||
elseif w == "enum" then |
|||
head[#head+1] = v |
|||
elseif w ~= nil then |
|||
k = match(d, "^[^\n]-([%w_]+)[(%[=]") |
|||
if k then |
|||
if w ~= "static" and k ~= "main" then v = "static "..d end |
|||
else |
|||
k = w |
|||
end |
|||
end |
|||
if w and k then |
|||
local o = nodes[k] |
|||
if o then nodes["*"..k] = o end |
|||
local n = #list+1 |
|||
list[n] = v |
|||
nodes[k] = n |
|||
end |
|||
end |
|||
pos = pos + #d |
|||
end |
|||
return nodes, list |
|||
end |
|||
|
|||
local function func_visit(nodes, list, used, n) |
|||
local i = nodes[n] |
|||
for m in string.gmatch(list[i], "[%w_]+") do |
|||
if nodes[m] then |
|||
local j = used[m] |
|||
if not j then |
|||
used[m] = i |
|||
func_visit(nodes, list, used, m) |
|||
elseif i < j then |
|||
used[m] = i |
|||
end |
|||
end |
|||
end |
|||
end |
|||
|
|||
local function func_collect(src) |
|||
local nodes, list = func_gather(src) |
|||
local used = {} |
|||
func_visit(nodes, list, used, "main") |
|||
for n,i in pairs(nodes) do |
|||
local j = used[n] |
|||
if j and j < i then used["*"..n] = j end |
|||
end |
|||
for n,i in pairs(nodes) do |
|||
if not used[n] then list[i] = "" end |
|||
end |
|||
return table.concat(list) |
|||
end |
|||
|
|||
find_sources() |
|||
local src = read_sources() |
|||
src = merge_includes(src) |
|||
local license = get_license(src) |
|||
src = fold_lines(src) |
|||
src = strip_unused1(src) |
|||
src = save_strings(src) |
|||
src = strip_unused2(src) |
|||
src = strip_comments(src) |
|||
src = preprocess(src) |
|||
src = strip_whitespace(src) |
|||
src = strip_unused3(src) |
|||
src = rename_tokens1(src) |
|||
src = func_collect(src) |
|||
src = rename_tokens2(src) |
|||
src = restore_strings(src) |
|||
src = merge_header(src, license) |
|||
io.write(src) |
File diff suppressed because it is too large
@ -0,0 +1 @@ |
|||
vmdef.lua |
@ -0,0 +1,190 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT bytecode listing module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- |
|||
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc |
|||
-- it hooks into the parser and lists all functions of a chunk as they |
|||
-- are parsed. |
|||
-- |
|||
-- Example usage: |
|||
-- |
|||
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)' |
|||
-- luajit -jbc=- foo.lua |
|||
-- luajit -jbc=foo.list foo.lua |
|||
-- |
|||
-- Default output is to stderr. To redirect the output to a file, pass a |
|||
-- filename as an argument (use '-' for stdout) or set the environment |
|||
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module |
|||
-- is started. |
|||
-- |
|||
-- This module can also be used programmatically: |
|||
-- |
|||
-- local bc = require("jit.bc") |
|||
-- |
|||
-- local function foo() print("hello") end |
|||
-- |
|||
-- bc.dump(foo) --> -- BYTECODE -- [...] |
|||
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello" |
|||
-- |
|||
-- local out = { |
|||
-- -- Do something with each line: |
|||
-- write = function(t, ...) io.write(...) end, |
|||
-- close = function(t) end, |
|||
-- flush = function(t) end, |
|||
-- } |
|||
-- bc.dump(foo, out) |
|||
-- |
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Cache some library functions and objects. |
|||
local jit = require("jit") |
|||
assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") |
|||
local jutil = require("jit.util") |
|||
local vmdef = require("jit.vmdef") |
|||
local bit = require("bit") |
|||
local sub, gsub, format = string.sub, string.gsub, string.format |
|||
local byte, band, shr = string.byte, bit.band, bit.rshift |
|||
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck |
|||
local funcuvname = jutil.funcuvname |
|||
local bcnames = vmdef.bcnames |
|||
local stdout, stderr = io.stdout, io.stderr |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function ctlsub(c) |
|||
if c == "\n" then return "\\n" |
|||
elseif c == "\r" then return "\\r" |
|||
elseif c == "\t" then return "\\t" |
|||
else return format("\\%03d", byte(c)) |
|||
end |
|||
end |
|||
|
|||
-- Return one bytecode line. |
|||
local function bcline(func, pc, prefix) |
|||
local ins, m = funcbc(func, pc) |
|||
if not ins then return end |
|||
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128) |
|||
local a = band(shr(ins, 8), 0xff) |
|||
local oidx = 6*band(ins, 0xff) |
|||
local op = sub(bcnames, oidx+1, oidx+6) |
|||
local s = format("%04d %s %-6s %3s ", |
|||
pc, prefix or " ", op, ma == 0 and "" or a) |
|||
local d = shr(ins, 16) |
|||
if mc == 13*128 then -- BCMjump |
|||
return format("%s=> %04d\n", s, pc+d-0x7fff) |
|||
end |
|||
if mb ~= 0 then |
|||
d = band(d, 0xff) |
|||
elseif mc == 0 then |
|||
return s.."\n" |
|||
end |
|||
local kc |
|||
if mc == 10*128 then -- BCMstr |
|||
kc = funck(func, -d-1) |
|||
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub)) |
|||
elseif mc == 9*128 then -- BCMnum |
|||
kc = funck(func, d) |
|||
if op == "TSETM " then kc = kc - 2^52 end |
|||
elseif mc == 12*128 then -- BCMfunc |
|||
local fi = funcinfo(funck(func, -d-1)) |
|||
if fi.ffid then |
|||
kc = vmdef.ffnames[fi.ffid] |
|||
else |
|||
kc = fi.loc |
|||
end |
|||
elseif mc == 5*128 then -- BCMuv |
|||
kc = funcuvname(func, d) |
|||
end |
|||
if ma == 5 then -- BCMuv |
|||
local ka = funcuvname(func, a) |
|||
if kc then kc = ka.." ; "..kc else kc = ka end |
|||
end |
|||
if mb ~= 0 then |
|||
local b = shr(ins, 24) |
|||
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end |
|||
return format("%s%3d %3d\n", s, b, d) |
|||
end |
|||
if kc then return format("%s%3d ; %s\n", s, d, kc) end |
|||
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits |
|||
return format("%s%3d\n", s, d) |
|||
end |
|||
|
|||
-- Collect branch targets of a function. |
|||
local function bctargets(func) |
|||
local target = {} |
|||
for pc=1,1000000000 do |
|||
local ins, m = funcbc(func, pc) |
|||
if not ins then break end |
|||
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end |
|||
end |
|||
return target |
|||
end |
|||
|
|||
-- Dump bytecode instructions of a function. |
|||
local function bcdump(func, out, all) |
|||
if not out then out = stdout end |
|||
local fi = funcinfo(func) |
|||
if all and fi.children then |
|||
for n=-1,-1000000000,-1 do |
|||
local k = funck(func, n) |
|||
if not k then break end |
|||
if type(k) == "proto" then bcdump(k, out, true) end |
|||
end |
|||
end |
|||
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined)) |
|||
local target = bctargets(func) |
|||
for pc=1,1000000000 do |
|||
local s = bcline(func, pc, target[pc] and "=>") |
|||
if not s then break end |
|||
out:write(s) |
|||
end |
|||
out:write("\n") |
|||
out:flush() |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Active flag and output file handle. |
|||
local active, out |
|||
|
|||
-- List handler. |
|||
local function h_list(func) |
|||
return bcdump(func, out) |
|||
end |
|||
|
|||
-- Detach list handler. |
|||
local function bclistoff() |
|||
if active then |
|||
active = false |
|||
jit.attach(h_list) |
|||
if out and out ~= stdout and out ~= stderr then out:close() end |
|||
out = nil |
|||
end |
|||
end |
|||
|
|||
-- Open the output file and attach list handler. |
|||
local function bcliston(outfile) |
|||
if active then bclistoff() end |
|||
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end |
|||
if outfile then |
|||
out = outfile == "-" and stdout or assert(io.open(outfile, "w")) |
|||
else |
|||
out = stderr |
|||
end |
|||
jit.attach(h_list, "bc") |
|||
active = true |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
line = bcline, |
|||
dump = bcdump, |
|||
targets = bctargets, |
|||
on = bcliston, |
|||
off = bclistoff, |
|||
start = bcliston -- For -j command line option. |
|||
} |
|||
|
@ -0,0 +1,678 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT module to save/list bytecode. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- |
|||
-- This module saves or lists the bytecode for an input file. |
|||
-- It's run by the -b command line option. |
|||
-- |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local jit = require("jit") |
|||
assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") |
|||
local bit = require("bit") |
|||
|
|||
-- Symbol name prefix for LuaJIT bytecode. |
|||
local LJBC_PREFIX = "luaJIT_BC_" |
|||
|
|||
local type, assert = type, assert |
|||
local format = string.format |
|||
local tremove, tconcat = table.remove, table.concat |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function usage() |
|||
io.stderr:write[[ |
|||
Save LuaJIT bytecode: luajit -b[options] input output |
|||
-l Only list bytecode. |
|||
-s Strip debug info (default). |
|||
-g Keep debug info. |
|||
-n name Set module name (default: auto-detect from input name). |
|||
-t type Set output file type (default: auto-detect from output name). |
|||
-a arch Override architecture for object files (default: native). |
|||
-o os Override OS for object files (default: native). |
|||
-e chunk Use chunk string as input. |
|||
-- Stop handling options. |
|||
- Use stdin as input and/or stdout as output. |
|||
|
|||
File types: c h obj o raw (default) |
|||
]] |
|||
os.exit(1) |
|||
end |
|||
|
|||
local function check(ok, ...) |
|||
if ok then return ok, ... end |
|||
io.stderr:write("luajit: ", ...) |
|||
io.stderr:write("\n") |
|||
os.exit(1) |
|||
end |
|||
|
|||
local function readfile(input) |
|||
if type(input) == "function" then return input end |
|||
if input == "-" then input = nil end |
|||
return check(loadfile(input)) |
|||
end |
|||
|
|||
local function savefile(name, mode) |
|||
if name == "-" then return io.stdout end |
|||
return check(io.open(name, mode)) |
|||
end |
|||
|
|||
local function set_stdout_binary(ffi) |
|||
ffi.cdef[[int _setmode(int fd, int mode);]] |
|||
ffi.C._setmode(1, 0x8000) |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_type = { |
|||
raw = "raw", c = "c", h = "h", o = "obj", obj = "obj", |
|||
} |
|||
|
|||
local map_arch = { |
|||
x86 = { e = "le", b = 32, m = 3, p = 0x14c, }, |
|||
x64 = { e = "le", b = 64, m = 62, p = 0x8664, }, |
|||
arm = { e = "le", b = 32, m = 40, p = 0x1c0, }, |
|||
arm64 = { e = "le", b = 64, m = 183, p = 0xaa64, }, |
|||
arm64be = { e = "be", b = 64, m = 183, }, |
|||
ppc = { e = "be", b = 32, m = 20, }, |
|||
mips = { e = "be", b = 32, m = 8, f = 0x50001006, }, |
|||
mipsel = { e = "le", b = 32, m = 8, f = 0x50001006, }, |
|||
mips64 = { e = "be", b = 64, m = 8, f = 0x80000007, }, |
|||
mips64el = { e = "le", b = 64, m = 8, f = 0x80000007, }, |
|||
mips64r6 = { e = "be", b = 64, m = 8, f = 0xa0000407, }, |
|||
mips64r6el = { e = "le", b = 64, m = 8, f = 0xa0000407, }, |
|||
} |
|||
|
|||
local map_os = { |
|||
linux = true, windows = true, osx = true, freebsd = true, netbsd = true, |
|||
openbsd = true, dragonfly = true, solaris = true, |
|||
} |
|||
|
|||
local function checkarg(str, map, err) |
|||
str = str:lower() |
|||
local s = check(map[str], "unknown ", err) |
|||
return type(s) == "string" and s or str |
|||
end |
|||
|
|||
local function detecttype(str) |
|||
local ext = str:lower():match("%.(%a+)$") |
|||
return map_type[ext] or "raw" |
|||
end |
|||
|
|||
local function checkmodname(str) |
|||
check(str:match("^[%w_.%-]+$"), "bad module name") |
|||
return str:gsub("[%.%-]", "_") |
|||
end |
|||
|
|||
local function detectmodname(str) |
|||
if type(str) == "string" then |
|||
local tail = str:match("[^/\\]+$") |
|||
if tail then str = tail end |
|||
local head = str:match("^(.*)%.[^.]*$") |
|||
if head then str = head end |
|||
str = str:match("^[%w_.%-]+") |
|||
else |
|||
str = nil |
|||
end |
|||
check(str, "cannot derive module name, use -n name") |
|||
return str:gsub("[%.%-]", "_") |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function bcsave_tail(fp, output, s) |
|||
local ok, err = fp:write(s) |
|||
if ok and output ~= "-" then ok, err = fp:close() end |
|||
check(ok, "cannot write ", output, ": ", err) |
|||
end |
|||
|
|||
local function bcsave_raw(output, s) |
|||
if output == "-" and jit.os == "Windows" then |
|||
local ok, ffi = pcall(require, "ffi") |
|||
check(ok, "FFI library required to write binary file to stdout") |
|||
set_stdout_binary(ffi) |
|||
end |
|||
local fp = savefile(output, "wb") |
|||
bcsave_tail(fp, output, s) |
|||
end |
|||
|
|||
local function bcsave_c(ctx, output, s) |
|||
local fp = savefile(output, "w") |
|||
if ctx.type == "c" then |
|||
fp:write(format([[ |
|||
#ifdef __cplusplus |
|||
extern "C" |
|||
#endif |
|||
#ifdef _WIN32 |
|||
__declspec(dllexport) |
|||
#endif |
|||
const unsigned char %s%s[] = { |
|||
]], LJBC_PREFIX, ctx.modname)) |
|||
else |
|||
fp:write(format([[ |
|||
#define %s%s_SIZE %d |
|||
static const unsigned char %s%s[] = { |
|||
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname)) |
|||
end |
|||
local t, n, m = {}, 0, 0 |
|||
for i=1,#s do |
|||
local b = tostring(string.byte(s, i)) |
|||
m = m + #b + 1 |
|||
if m > 78 then |
|||
fp:write(tconcat(t, ",", 1, n), ",\n") |
|||
n, m = 0, #b + 1 |
|||
end |
|||
n = n + 1 |
|||
t[n] = b |
|||
end |
|||
bcsave_tail(fp, output, tconcat(t, ",", 1, n).."\n};\n") |
|||
end |
|||
|
|||
local function bcsave_elfobj(ctx, output, s, ffi) |
|||
ffi.cdef[[ |
|||
typedef struct { |
|||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; |
|||
uint16_t type, machine; |
|||
uint32_t version; |
|||
uint32_t entry, phofs, shofs; |
|||
uint32_t flags; |
|||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; |
|||
} ELF32header; |
|||
typedef struct { |
|||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7]; |
|||
uint16_t type, machine; |
|||
uint32_t version; |
|||
uint64_t entry, phofs, shofs; |
|||
uint32_t flags; |
|||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx; |
|||
} ELF64header; |
|||
typedef struct { |
|||
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize; |
|||
} ELF32sectheader; |
|||
typedef struct { |
|||
uint32_t name, type; |
|||
uint64_t flags, addr, ofs, size; |
|||
uint32_t link, info; |
|||
uint64_t align, entsize; |
|||
} ELF64sectheader; |
|||
typedef struct { |
|||
uint32_t name, value, size; |
|||
uint8_t info, other; |
|||
uint16_t sectidx; |
|||
} ELF32symbol; |
|||
typedef struct { |
|||
uint32_t name; |
|||
uint8_t info, other; |
|||
uint16_t sectidx; |
|||
uint64_t value, size; |
|||
} ELF64symbol; |
|||
typedef struct { |
|||
ELF32header hdr; |
|||
ELF32sectheader sect[6]; |
|||
ELF32symbol sym[2]; |
|||
uint8_t space[4096]; |
|||
} ELF32obj; |
|||
typedef struct { |
|||
ELF64header hdr; |
|||
ELF64sectheader sect[6]; |
|||
ELF64symbol sym[2]; |
|||
uint8_t space[4096]; |
|||
} ELF64obj; |
|||
]] |
|||
local symname = LJBC_PREFIX..ctx.modname |
|||
local ai = assert(map_arch[ctx.arch]) |
|||
local is64, isbe = ai.b == 64, ai.e == "be" |
|||
|
|||
-- Handle different host/target endianess. |
|||
local function f32(x) return x end |
|||
local f16, fofs = f32, f32 |
|||
if ffi.abi("be") ~= isbe then |
|||
f32 = bit.bswap |
|||
function f16(x) return bit.rshift(bit.bswap(x), 16) end |
|||
if is64 then |
|||
local two32 = ffi.cast("int64_t", 2^32) |
|||
function fofs(x) return bit.bswap(x)*two32 end |
|||
else |
|||
fofs = f32 |
|||
end |
|||
end |
|||
|
|||
-- Create ELF object and fill in header. |
|||
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj") |
|||
local hdr = o.hdr |
|||
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi. |
|||
local bf = assert(io.open("/bin/ls", "rb")) |
|||
local bs = bf:read(9) |
|||
bf:close() |
|||
ffi.copy(o, bs, 9) |
|||
check(hdr.emagic[0] == 127, "no support for writing native object files") |
|||
else |
|||
hdr.emagic = "\127ELF" |
|||
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0 |
|||
end |
|||
hdr.eclass = is64 and 2 or 1 |
|||
hdr.eendian = isbe and 2 or 1 |
|||
hdr.eversion = 1 |
|||
hdr.type = f16(1) |
|||
hdr.machine = f16(ai.m) |
|||
hdr.flags = f32(ai.f or 0) |
|||
hdr.version = f32(1) |
|||
hdr.shofs = fofs(ffi.offsetof(o, "sect")) |
|||
hdr.ehsize = f16(ffi.sizeof(hdr)) |
|||
hdr.shentsize = f16(ffi.sizeof(o.sect[0])) |
|||
hdr.shnum = f16(6) |
|||
hdr.shstridx = f16(2) |
|||
|
|||
-- Fill in sections and symbols. |
|||
local sofs, ofs = ffi.offsetof(o, "space"), 1 |
|||
for i,name in ipairs{ |
|||
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack", |
|||
} do |
|||
local sect = o.sect[i] |
|||
sect.align = fofs(1) |
|||
sect.name = f32(ofs) |
|||
ffi.copy(o.space+ofs, name) |
|||
ofs = ofs + #name+1 |
|||
end |
|||
o.sect[1].type = f32(2) -- .symtab |
|||
o.sect[1].link = f32(3) |
|||
o.sect[1].info = f32(1) |
|||
o.sect[1].align = fofs(8) |
|||
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym")) |
|||
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0])) |
|||
o.sect[1].size = fofs(ffi.sizeof(o.sym)) |
|||
o.sym[1].name = f32(1) |
|||
o.sym[1].sectidx = f16(4) |
|||
o.sym[1].size = fofs(#s) |
|||
o.sym[1].info = 17 |
|||
o.sect[2].type = f32(3) -- .shstrtab |
|||
o.sect[2].ofs = fofs(sofs) |
|||
o.sect[2].size = fofs(ofs) |
|||
o.sect[3].type = f32(3) -- .strtab |
|||
o.sect[3].ofs = fofs(sofs + ofs) |
|||
o.sect[3].size = fofs(#symname+2) |
|||
ffi.copy(o.space+ofs+1, symname) |
|||
ofs = ofs + #symname + 2 |
|||
o.sect[4].type = f32(1) -- .rodata |
|||
o.sect[4].flags = fofs(2) |
|||
o.sect[4].ofs = fofs(sofs + ofs) |
|||
o.sect[4].size = fofs(#s) |
|||
o.sect[5].type = f32(1) -- .note.GNU-stack |
|||
o.sect[5].ofs = fofs(sofs + ofs + #s) |
|||
|
|||
-- Write ELF object file. |
|||
local fp = savefile(output, "wb") |
|||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) |
|||
bcsave_tail(fp, output, s) |
|||
end |
|||
|
|||
local function bcsave_peobj(ctx, output, s, ffi) |
|||
ffi.cdef[[ |
|||
typedef struct { |
|||
uint16_t arch, nsects; |
|||
uint32_t time, symtabofs, nsyms; |
|||
uint16_t opthdrsz, flags; |
|||
} PEheader; |
|||
typedef struct { |
|||
char name[8]; |
|||
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs; |
|||
uint16_t nreloc, nline; |
|||
uint32_t flags; |
|||
} PEsection; |
|||
typedef struct __attribute((packed)) { |
|||
union { |
|||
char name[8]; |
|||
uint32_t nameref[2]; |
|||
}; |
|||
uint32_t value; |
|||
int16_t sect; |
|||
uint16_t type; |
|||
uint8_t scl, naux; |
|||
} PEsym; |
|||
typedef struct __attribute((packed)) { |
|||
uint32_t size; |
|||
uint16_t nreloc, nline; |
|||
uint32_t cksum; |
|||
uint16_t assoc; |
|||
uint8_t comdatsel, unused[3]; |
|||
} PEsymaux; |
|||
typedef struct { |
|||
PEheader hdr; |
|||
PEsection sect[2]; |
|||
// Must be an even number of symbol structs. |
|||
PEsym sym0; |
|||
PEsymaux sym0aux; |
|||
PEsym sym1; |
|||
PEsymaux sym1aux; |
|||
PEsym sym2; |
|||
PEsym sym3; |
|||
uint32_t strtabsize; |
|||
uint8_t space[4096]; |
|||
} PEobj; |
|||
]] |
|||
local symname = LJBC_PREFIX..ctx.modname |
|||
local ai = assert(map_arch[ctx.arch]) |
|||
local is64 = ai.b == 64 |
|||
local symexport = " /EXPORT:"..symname..",DATA " |
|||
|
|||
-- The file format is always little-endian. Swap if the host is big-endian. |
|||
local function f32(x) return x end |
|||
local f16 = f32 |
|||
if ffi.abi("be") then |
|||
f32 = bit.bswap |
|||
function f16(x) return bit.rshift(bit.bswap(x), 16) end |
|||
end |
|||
|
|||
-- Create PE object and fill in header. |
|||
local o = ffi.new("PEobj") |
|||
local hdr = o.hdr |
|||
hdr.arch = f16(assert(ai.p)) |
|||
hdr.nsects = f16(2) |
|||
hdr.symtabofs = f32(ffi.offsetof(o, "sym0")) |
|||
hdr.nsyms = f32(6) |
|||
|
|||
-- Fill in sections and symbols. |
|||
o.sect[0].name = ".drectve" |
|||
o.sect[0].size = f32(#symexport) |
|||
o.sect[0].flags = f32(0x00100a00) |
|||
o.sym0.sect = f16(1) |
|||
o.sym0.scl = 3 |
|||
o.sym0.name = ".drectve" |
|||
o.sym0.naux = 1 |
|||
o.sym0aux.size = f32(#symexport) |
|||
o.sect[1].name = ".rdata" |
|||
o.sect[1].size = f32(#s) |
|||
o.sect[1].flags = f32(0x40300040) |
|||
o.sym1.sect = f16(2) |
|||
o.sym1.scl = 3 |
|||
o.sym1.name = ".rdata" |
|||
o.sym1.naux = 1 |
|||
o.sym1aux.size = f32(#s) |
|||
o.sym2.sect = f16(2) |
|||
o.sym2.scl = 2 |
|||
o.sym2.nameref[1] = f32(4) |
|||
o.sym3.sect = f16(-1) |
|||
o.sym3.scl = 2 |
|||
o.sym3.value = f32(1) |
|||
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant. |
|||
ffi.copy(o.space, symname) |
|||
local ofs = #symname + 1 |
|||
o.strtabsize = f32(ofs + 4) |
|||
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs) |
|||
ffi.copy(o.space + ofs, symexport) |
|||
ofs = ofs + #symexport |
|||
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs) |
|||
|
|||
-- Write PE object file. |
|||
local fp = savefile(output, "wb") |
|||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs)) |
|||
bcsave_tail(fp, output, s) |
|||
end |
|||
|
|||
local function bcsave_machobj(ctx, output, s, ffi) |
|||
ffi.cdef[[ |
|||
typedef struct |
|||
{ |
|||
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags; |
|||
} mach_header; |
|||
typedef struct |
|||
{ |
|||
mach_header; uint32_t reserved; |
|||
} mach_header_64; |
|||
typedef struct { |
|||
uint32_t cmd, cmdsize; |
|||
char segname[16]; |
|||
uint32_t vmaddr, vmsize, fileoff, filesize; |
|||
uint32_t maxprot, initprot, nsects, flags; |
|||
} mach_segment_command; |
|||
typedef struct { |
|||
uint32_t cmd, cmdsize; |
|||
char segname[16]; |
|||
uint64_t vmaddr, vmsize, fileoff, filesize; |
|||
uint32_t maxprot, initprot, nsects, flags; |
|||
} mach_segment_command_64; |
|||
typedef struct { |
|||
char sectname[16], segname[16]; |
|||
uint32_t addr, size; |
|||
uint32_t offset, align, reloff, nreloc, flags; |
|||
uint32_t reserved1, reserved2; |
|||
} mach_section; |
|||
typedef struct { |
|||
char sectname[16], segname[16]; |
|||
uint64_t addr, size; |
|||
uint32_t offset, align, reloff, nreloc, flags; |
|||
uint32_t reserved1, reserved2, reserved3; |
|||
} mach_section_64; |
|||
typedef struct { |
|||
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize; |
|||
} mach_symtab_command; |
|||
typedef struct { |
|||
int32_t strx; |
|||
uint8_t type, sect; |
|||
int16_t desc; |
|||
uint32_t value; |
|||
} mach_nlist; |
|||
typedef struct { |
|||
uint32_t strx; |
|||
uint8_t type, sect; |
|||
uint16_t desc; |
|||
uint64_t value; |
|||
} mach_nlist_64; |
|||
typedef struct |
|||
{ |
|||
uint32_t magic, nfat_arch; |
|||
} mach_fat_header; |
|||
typedef struct |
|||
{ |
|||
uint32_t cputype, cpusubtype, offset, size, align; |
|||
} mach_fat_arch; |
|||
typedef struct { |
|||
struct { |
|||
mach_header hdr; |
|||
mach_segment_command seg; |
|||
mach_section sec; |
|||
mach_symtab_command sym; |
|||
} arch[1]; |
|||
mach_nlist sym_entry; |
|||
uint8_t space[4096]; |
|||
} mach_obj; |
|||
typedef struct { |
|||
struct { |
|||
mach_header_64 hdr; |
|||
mach_segment_command_64 seg; |
|||
mach_section_64 sec; |
|||
mach_symtab_command sym; |
|||
} arch[1]; |
|||
mach_nlist_64 sym_entry; |
|||
uint8_t space[4096]; |
|||
} mach_obj_64; |
|||
typedef struct { |
|||
mach_fat_header fat; |
|||
mach_fat_arch fat_arch[2]; |
|||
struct { |
|||
mach_header hdr; |
|||
mach_segment_command seg; |
|||
mach_section sec; |
|||
mach_symtab_command sym; |
|||
} arch[2]; |
|||
mach_nlist sym_entry; |
|||
uint8_t space[4096]; |
|||
} mach_fat_obj; |
|||
]] |
|||
local symname = '_'..LJBC_PREFIX..ctx.modname |
|||
local isfat, is64, align, mobj = false, false, 4, "mach_obj" |
|||
if ctx.arch == "x64" then |
|||
is64, align, mobj = true, 8, "mach_obj_64" |
|||
elseif ctx.arch == "arm" then |
|||
isfat, mobj = true, "mach_fat_obj" |
|||
elseif ctx.arch == "arm64" then |
|||
is64, align, isfat, mobj = true, 8, true, "mach_fat_obj" |
|||
else |
|||
check(ctx.arch == "x86", "unsupported architecture for OSX") |
|||
end |
|||
local function aligned(v, a) return bit.band(v+a-1, -a) end |
|||
local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE. |
|||
|
|||
-- Create Mach-O object and fill in header. |
|||
local o = ffi.new(mobj) |
|||
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align) |
|||
local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12}, arm64={0x01000007,0x0100000c} })[ctx.arch] |
|||
local cpusubtype = ({ x86={3}, x64={3}, arm={3,9}, arm64={3,0} })[ctx.arch] |
|||
if isfat then |
|||
o.fat.magic = be32(0xcafebabe) |
|||
o.fat.nfat_arch = be32(#cpusubtype) |
|||
end |
|||
|
|||
-- Fill in sections and symbols. |
|||
for i=0,#cpusubtype-1 do |
|||
local ofs = 0 |
|||
if isfat then |
|||
local a = o.fat_arch[i] |
|||
a.cputype = be32(cputype[i+1]) |
|||
a.cpusubtype = be32(cpusubtype[i+1]) |
|||
-- Subsequent slices overlap each other to share data. |
|||
ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0]) |
|||
a.offset = be32(ofs) |
|||
a.size = be32(mach_size-ofs+#s) |
|||
end |
|||
local a = o.arch[i] |
|||
a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface |
|||
a.hdr.cputype = cputype[i+1] |
|||
a.hdr.cpusubtype = cpusubtype[i+1] |
|||
a.hdr.filetype = 1 |
|||
a.hdr.ncmds = 2 |
|||
a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym) |
|||
a.seg.cmd = is64 and 0x19 or 0x1 |
|||
a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec) |
|||
a.seg.vmsize = #s |
|||
a.seg.fileoff = mach_size-ofs |
|||
a.seg.filesize = #s |
|||
a.seg.maxprot = 1 |
|||
a.seg.initprot = 1 |
|||
a.seg.nsects = 1 |
|||
ffi.copy(a.sec.sectname, "__data") |
|||
ffi.copy(a.sec.segname, "__DATA") |
|||
a.sec.size = #s |
|||
a.sec.offset = mach_size-ofs |
|||
a.sym.cmd = 2 |
|||
a.sym.cmdsize = ffi.sizeof(a.sym) |
|||
a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs |
|||
a.sym.nsyms = 1 |
|||
a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs |
|||
a.sym.strsize = aligned(#symname+2, align) |
|||
end |
|||
o.sym_entry.type = 0xf |
|||
o.sym_entry.sect = 1 |
|||
o.sym_entry.strx = 1 |
|||
ffi.copy(o.space+1, symname) |
|||
|
|||
-- Write Macho-O object file. |
|||
local fp = savefile(output, "wb") |
|||
fp:write(ffi.string(o, mach_size)) |
|||
bcsave_tail(fp, output, s) |
|||
end |
|||
|
|||
local function bcsave_obj(ctx, output, s) |
|||
local ok, ffi = pcall(require, "ffi") |
|||
check(ok, "FFI library required to write this file type") |
|||
if output == "-" and jit.os == "Windows" then |
|||
set_stdout_binary(ffi) |
|||
end |
|||
if ctx.os == "windows" then |
|||
return bcsave_peobj(ctx, output, s, ffi) |
|||
elseif ctx.os == "osx" then |
|||
return bcsave_machobj(ctx, output, s, ffi) |
|||
else |
|||
return bcsave_elfobj(ctx, output, s, ffi) |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function bclist(input, output) |
|||
local f = readfile(input) |
|||
require("jit.bc").dump(f, savefile(output, "w"), true) |
|||
end |
|||
|
|||
local function bcsave(ctx, input, output) |
|||
local f = readfile(input) |
|||
local s = string.dump(f, ctx.strip) |
|||
local t = ctx.type |
|||
if not t then |
|||
t = detecttype(output) |
|||
ctx.type = t |
|||
end |
|||
if t == "raw" then |
|||
bcsave_raw(output, s) |
|||
else |
|||
if not ctx.modname then ctx.modname = detectmodname(input) end |
|||
if t == "obj" then |
|||
bcsave_obj(ctx, output, s) |
|||
else |
|||
bcsave_c(ctx, output, s) |
|||
end |
|||
end |
|||
end |
|||
|
|||
local function docmd(...) |
|||
local arg = {...} |
|||
local n = 1 |
|||
local list = false |
|||
local ctx = { |
|||
strip = true, arch = jit.arch, os = jit.os:lower(), |
|||
type = false, modname = false, |
|||
} |
|||
while n <= #arg do |
|||
local a = arg[n] |
|||
if type(a) == "string" and a:sub(1, 1) == "-" and a ~= "-" then |
|||
tremove(arg, n) |
|||
if a == "--" then break end |
|||
for m=2,#a do |
|||
local opt = a:sub(m, m) |
|||
if opt == "l" then |
|||
list = true |
|||
elseif opt == "s" then |
|||
ctx.strip = true |
|||
elseif opt == "g" then |
|||
ctx.strip = false |
|||
else |
|||
if arg[n] == nil or m ~= #a then usage() end |
|||
if opt == "e" then |
|||
if n ~= 1 then usage() end |
|||
arg[1] = check(loadstring(arg[1])) |
|||
elseif opt == "n" then |
|||
ctx.modname = checkmodname(tremove(arg, n)) |
|||
elseif opt == "t" then |
|||
ctx.type = checkarg(tremove(arg, n), map_type, "file type") |
|||
elseif opt == "a" then |
|||
ctx.arch = checkarg(tremove(arg, n), map_arch, "architecture") |
|||
elseif opt == "o" then |
|||
ctx.os = checkarg(tremove(arg, n), map_os, "OS name") |
|||
else |
|||
usage() |
|||
end |
|||
end |
|||
end |
|||
else |
|||
n = n + 1 |
|||
end |
|||
end |
|||
if list then |
|||
if #arg == 0 or #arg > 2 then usage() end |
|||
bclist(arg[1], arg[2] or "-") |
|||
else |
|||
if #arg ~= 2 then usage() end |
|||
bcsave(ctx, arg[1], arg[2]) |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
start = docmd -- Process -b command line option. |
|||
} |
|||
|
@ -0,0 +1,689 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT ARM disassembler module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This is a helper module used by the LuaJIT machine code dumper module. |
|||
-- |
|||
-- It disassembles most user-mode ARMv7 instructions |
|||
-- NYI: Advanced SIMD and VFP instructions. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local type = type |
|||
local sub, byte, format = string.sub, string.byte, string.format |
|||
local match, gmatch = string.match, string.gmatch |
|||
local concat = table.concat |
|||
local bit = require("bit") |
|||
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex |
|||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Opcode maps |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_loadc = { |
|||
shift = 8, mask = 15, |
|||
[10] = { |
|||
shift = 20, mask = 1, |
|||
[0] = { |
|||
shift = 23, mask = 3, |
|||
[0] = "vmovFmDN", "vstmFNdr", |
|||
_ = { |
|||
shift = 21, mask = 1, |
|||
[0] = "vstrFdl", |
|||
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", } |
|||
}, |
|||
}, |
|||
{ |
|||
shift = 23, mask = 3, |
|||
[0] = "vmovFDNm", |
|||
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", }, |
|||
_ = { |
|||
shift = 21, mask = 1, |
|||
[0] = "vldrFdl", "vldmdbFNdr", |
|||
}, |
|||
}, |
|||
}, |
|||
[11] = { |
|||
shift = 20, mask = 1, |
|||
[0] = { |
|||
shift = 23, mask = 3, |
|||
[0] = "vmovGmDN", "vstmGNdr", |
|||
_ = { |
|||
shift = 21, mask = 1, |
|||
[0] = "vstrGdl", |
|||
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", } |
|||
}, |
|||
}, |
|||
{ |
|||
shift = 23, mask = 3, |
|||
[0] = "vmovGDNm", |
|||
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", }, |
|||
_ = { |
|||
shift = 21, mask = 1, |
|||
[0] = "vldrGdl", "vldmdbGNdr", |
|||
}, |
|||
}, |
|||
}, |
|||
_ = { |
|||
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc. |
|||
}, |
|||
} |
|||
|
|||
local map_vfps = { |
|||
shift = 6, mask = 0x2c001, |
|||
[0] = "vmlaF.dnm", "vmlsF.dnm", |
|||
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm", |
|||
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm", |
|||
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm", |
|||
[0x20000] = "vdivF.dnm", |
|||
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm", |
|||
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm", |
|||
[0x2c000] = "vmovF.dY", |
|||
[0x2c001] = { |
|||
shift = 7, mask = 0x1e01, |
|||
[0] = "vmovF.dm", "vabsF.dm", |
|||
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm", |
|||
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm", |
|||
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d", |
|||
[0x0e01] = "vcvtG.dF.m", |
|||
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm", |
|||
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm", |
|||
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm", |
|||
}, |
|||
} |
|||
|
|||
local map_vfpd = { |
|||
shift = 6, mask = 0x2c001, |
|||
[0] = "vmlaG.dnm", "vmlsG.dnm", |
|||
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm", |
|||
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm", |
|||
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm", |
|||
[0x20000] = "vdivG.dnm", |
|||
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm", |
|||
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm", |
|||
[0x2c000] = "vmovG.dY", |
|||
[0x2c001] = { |
|||
shift = 7, mask = 0x1e01, |
|||
[0] = "vmovG.dm", "vabsG.dm", |
|||
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm", |
|||
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm", |
|||
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d", |
|||
[0x0e01] = "vcvtF.dG.m", |
|||
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm", |
|||
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m", |
|||
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m", |
|||
}, |
|||
} |
|||
|
|||
local map_datac = { |
|||
shift = 24, mask = 1, |
|||
[0] = { |
|||
shift = 4, mask = 1, |
|||
[0] = { |
|||
shift = 8, mask = 15, |
|||
[10] = map_vfps, |
|||
[11] = map_vfpd, |
|||
-- NYI cdp, mcr, mrc. |
|||
}, |
|||
{ |
|||
shift = 8, mask = 15, |
|||
[10] = { |
|||
shift = 20, mask = 15, |
|||
[0] = "vmovFnD", "vmovFDn", |
|||
[14] = "vmsrD", |
|||
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", }, |
|||
}, |
|||
}, |
|||
}, |
|||
"svcT", |
|||
} |
|||
|
|||
local map_loadcu = { |
|||
shift = 0, mask = 0, -- NYI unconditional CP load/store. |
|||
} |
|||
|
|||
local map_datacu = { |
|||
shift = 0, mask = 0, -- NYI unconditional CP data. |
|||
} |
|||
|
|||
local map_simddata = { |
|||
shift = 0, mask = 0, -- NYI SIMD data. |
|||
} |
|||
|
|||
local map_simdload = { |
|||
shift = 0, mask = 0, -- NYI SIMD load/store, preload. |
|||
} |
|||
|
|||
local map_preload = { |
|||
shift = 0, mask = 0, -- NYI preload. |
|||
} |
|||
|
|||
local map_media = { |
|||
shift = 20, mask = 31, |
|||
[0] = false, |
|||
{ --01 |
|||
shift = 5, mask = 7, |
|||
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM", |
|||
"sadd8DNM", false, false, "ssub8DNM", |
|||
}, |
|||
{ --02 |
|||
shift = 5, mask = 7, |
|||
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM", |
|||
"qadd8DNM", false, false, "qsub8DNM", |
|||
}, |
|||
{ --03 |
|||
shift = 5, mask = 7, |
|||
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM", |
|||
"shadd8DNM", false, false, "shsub8DNM", |
|||
}, |
|||
false, |
|||
{ --05 |
|||
shift = 5, mask = 7, |
|||
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM", |
|||
"uadd8DNM", false, false, "usub8DNM", |
|||
}, |
|||
{ --06 |
|||
shift = 5, mask = 7, |
|||
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM", |
|||
"uqadd8DNM", false, false, "uqsub8DNM", |
|||
}, |
|||
{ --07 |
|||
shift = 5, mask = 7, |
|||
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM", |
|||
"uhadd8DNM", false, false, "uhsub8DNM", |
|||
}, |
|||
{ --08 |
|||
shift = 5, mask = 7, |
|||
[0] = "pkhbtDNMU", false, "pkhtbDNMU", |
|||
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", }, |
|||
"pkhbtDNMU", "selDNM", "pkhtbDNMU", |
|||
}, |
|||
false, |
|||
{ --0a |
|||
shift = 5, mask = 7, |
|||
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu", |
|||
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", }, |
|||
"ssatDxMu", false, "ssatDxMu", |
|||
}, |
|||
{ --0b |
|||
shift = 5, mask = 7, |
|||
[0] = "ssatDxMu", "revDM", "ssatDxMu", |
|||
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", }, |
|||
"ssatDxMu", "rev16DM", "ssatDxMu", |
|||
}, |
|||
{ --0c |
|||
shift = 5, mask = 7, |
|||
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", }, |
|||
}, |
|||
false, |
|||
{ --0e |
|||
shift = 5, mask = 7, |
|||
[0] = "usatDwMu", "usat16DwM", "usatDwMu", |
|||
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", }, |
|||
"usatDwMu", false, "usatDwMu", |
|||
}, |
|||
{ --0f |
|||
shift = 5, mask = 7, |
|||
[0] = "usatDwMu", "rbitDM", "usatDwMu", |
|||
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", }, |
|||
"usatDwMu", "revshDM", "usatDwMu", |
|||
}, |
|||
{ --10 |
|||
shift = 12, mask = 15, |
|||
[15] = { |
|||
shift = 5, mask = 7, |
|||
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS", |
|||
}, |
|||
_ = { |
|||
shift = 5, mask = 7, |
|||
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD", |
|||
}, |
|||
}, |
|||
false, false, false, |
|||
{ --14 |
|||
shift = 5, mask = 7, |
|||
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS", |
|||
}, |
|||
{ --15 |
|||
shift = 5, mask = 7, |
|||
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", }, |
|||
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", }, |
|||
false, false, false, false, |
|||
"smmlsNMSD", "smmlsrNMSD", |
|||
}, |
|||
false, false, |
|||
{ --18 |
|||
shift = 5, mask = 7, |
|||
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", }, |
|||
}, |
|||
false, |
|||
{ --1a |
|||
shift = 5, mask = 3, [2] = "sbfxDMvw", |
|||
}, |
|||
{ --1b |
|||
shift = 5, mask = 3, [2] = "sbfxDMvw", |
|||
}, |
|||
{ --1c |
|||
shift = 5, mask = 3, |
|||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, |
|||
}, |
|||
{ --1d |
|||
shift = 5, mask = 3, |
|||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", }, |
|||
}, |
|||
{ --1e |
|||
shift = 5, mask = 3, [2] = "ubfxDMvw", |
|||
}, |
|||
{ --1f |
|||
shift = 5, mask = 3, [2] = "ubfxDMvw", |
|||
}, |
|||
} |
|||
|
|||
local map_load = { |
|||
shift = 21, mask = 9, |
|||
{ |
|||
shift = 20, mask = 5, |
|||
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL", |
|||
}, |
|||
_ = { |
|||
shift = 20, mask = 5, |
|||
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL", |
|||
} |
|||
} |
|||
|
|||
local map_load1 = { |
|||
shift = 4, mask = 1, |
|||
[0] = map_load, map_media, |
|||
} |
|||
|
|||
local map_loadm = { |
|||
shift = 20, mask = 1, |
|||
[0] = { |
|||
shift = 23, mask = 3, |
|||
[0] = "stmdaNR", "stmNR", |
|||
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR", |
|||
}, |
|||
{ |
|||
shift = 23, mask = 3, |
|||
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", }, |
|||
"ldmdbNR", "ldmibNR", |
|||
}, |
|||
} |
|||
|
|||
local map_data = { |
|||
shift = 21, mask = 15, |
|||
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs", |
|||
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs", |
|||
"tstNP", "teqNP", "cmpNP", "cmnNP", |
|||
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs", |
|||
} |
|||
|
|||
local map_mul = { |
|||
shift = 21, mask = 7, |
|||
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS", |
|||
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs", |
|||
} |
|||
|
|||
local map_sync = { |
|||
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd. |
|||
[0] = "swpDMN", false, false, false, |
|||
"swpbDMN", false, false, false, |
|||
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN", |
|||
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN", |
|||
} |
|||
|
|||
local map_mulh = { |
|||
shift = 21, mask = 3, |
|||
[0] = { shift = 5, mask = 3, |
|||
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", }, |
|||
{ shift = 5, mask = 3, |
|||
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", }, |
|||
{ shift = 5, mask = 3, |
|||
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", }, |
|||
{ shift = 5, mask = 3, |
|||
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", }, |
|||
} |
|||
|
|||
local map_misc = { |
|||
shift = 4, mask = 7, |
|||
-- NYI: decode PSR bits of msr. |
|||
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", }, |
|||
{ shift = 21, mask = 3, "bxM", false, "clzDM", }, |
|||
{ shift = 21, mask = 3, "bxjM", }, |
|||
{ shift = 21, mask = 3, "blxM", }, |
|||
false, |
|||
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", }, |
|||
false, |
|||
{ shift = 21, mask = 3, "bkptK", }, |
|||
} |
|||
|
|||
local map_datar = { |
|||
shift = 4, mask = 9, |
|||
[9] = { |
|||
shift = 5, mask = 3, |
|||
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, }, |
|||
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", }, |
|||
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", }, |
|||
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", }, |
|||
}, |
|||
_ = { |
|||
shift = 20, mask = 25, |
|||
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, }, |
|||
_ = { |
|||
shift = 0, mask = 0xffffffff, |
|||
[bor(0xe1a00000)] = "nop", |
|||
_ = map_data, |
|||
} |
|||
}, |
|||
} |
|||
|
|||
local map_datai = { |
|||
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12. |
|||
[16] = "movwDW", [20] = "movtDW", |
|||
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", }, |
|||
[22] = "msrNW", |
|||
_ = map_data, |
|||
} |
|||
|
|||
local map_branch = { |
|||
shift = 24, mask = 1, |
|||
[0] = "bB", "blB" |
|||
} |
|||
|
|||
local map_condins = { |
|||
[0] = map_datar, map_datai, map_load, map_load1, |
|||
map_loadm, map_branch, map_loadc, map_datac |
|||
} |
|||
|
|||
-- NYI: setend. |
|||
local map_uncondins = { |
|||
[0] = false, map_simddata, map_simdload, map_preload, |
|||
false, "blxB", map_loadcu, map_datacu, |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_gpr = { |
|||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
|||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", |
|||
} |
|||
|
|||
local map_cond = { |
|||
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", |
|||
"hi", "ls", "ge", "lt", "gt", "le", "al", |
|||
} |
|||
|
|||
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", } |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Output a nicely formatted line with an opcode and operands. |
|||
local function putop(ctx, text, operands) |
|||
local pos = ctx.pos |
|||
local extra = "" |
|||
if ctx.rel then |
|||
local sym = ctx.symtab[ctx.rel] |
|||
if sym then |
|||
extra = "\t->"..sym |
|||
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then |
|||
extra = "\t; 0x"..tohex(ctx.rel) |
|||
end |
|||
end |
|||
if ctx.hexdump > 0 then |
|||
ctx.out(format("%08x %s %-5s %s%s\n", |
|||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) |
|||
else |
|||
ctx.out(format("%08x %-5s %s%s\n", |
|||
ctx.addr+pos, text, concat(operands, ", "), extra)) |
|||
end |
|||
ctx.pos = pos + 4 |
|||
end |
|||
|
|||
-- Fallback for unknown opcodes. |
|||
local function unknown(ctx) |
|||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) |
|||
end |
|||
|
|||
-- Format operand 2 of load/store opcodes. |
|||
local function fmtload(ctx, op, pos) |
|||
local base = map_gpr[band(rshift(op, 16), 15)] |
|||
local x, ofs |
|||
local ext = (band(op, 0x04000000) == 0) |
|||
if not ext and band(op, 0x02000000) == 0 then |
|||
ofs = band(op, 4095) |
|||
if band(op, 0x00800000) == 0 then ofs = -ofs end |
|||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end |
|||
ofs = "#"..ofs |
|||
elseif ext and band(op, 0x00400000) ~= 0 then |
|||
ofs = band(op, 15) + band(rshift(op, 4), 0xf0) |
|||
if band(op, 0x00800000) == 0 then ofs = -ofs end |
|||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end |
|||
ofs = "#"..ofs |
|||
else |
|||
ofs = map_gpr[band(op, 15)] |
|||
if ext or band(op, 0xfe0) == 0 then |
|||
elseif band(op, 0xfe0) == 0x60 then |
|||
ofs = format("%s, rrx", ofs) |
|||
else |
|||
local sh = band(rshift(op, 7), 31) |
|||
if sh == 0 then sh = 32 end |
|||
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh) |
|||
end |
|||
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end |
|||
end |
|||
if ofs == "#0" then |
|||
x = format("[%s]", base) |
|||
elseif band(op, 0x01000000) == 0 then |
|||
x = format("[%s], %s", base, ofs) |
|||
else |
|||
x = format("[%s, %s]", base, ofs) |
|||
end |
|||
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end |
|||
return x |
|||
end |
|||
|
|||
-- Format operand 2 of vector load/store opcodes. |
|||
local function fmtvload(ctx, op, pos) |
|||
local base = map_gpr[band(rshift(op, 16), 15)] |
|||
local ofs = band(op, 255)*4 |
|||
if band(op, 0x00800000) == 0 then ofs = -ofs end |
|||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end |
|||
if ofs == 0 then |
|||
return format("[%s]", base) |
|||
else |
|||
return format("[%s, #%d]", base, ofs) |
|||
end |
|||
end |
|||
|
|||
local function fmtvr(op, vr, sh0, sh1) |
|||
if vr == "s" then |
|||
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1)) |
|||
else |
|||
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16)) |
|||
end |
|||
end |
|||
|
|||
-- Disassemble a single instruction. |
|||
local function disass_ins(ctx) |
|||
local pos = ctx.pos |
|||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) |
|||
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) |
|||
local operands = {} |
|||
local suffix = "" |
|||
local last, name, pat |
|||
local vr |
|||
ctx.op = op |
|||
ctx.rel = nil |
|||
|
|||
local cond = rshift(op, 28) |
|||
local opat |
|||
if cond == 15 then |
|||
opat = map_uncondins[band(rshift(op, 25), 7)] |
|||
else |
|||
if cond ~= 14 then suffix = map_cond[cond] end |
|||
opat = map_condins[band(rshift(op, 25), 7)] |
|||
end |
|||
while type(opat) ~= "string" do |
|||
if not opat then return unknown(ctx) end |
|||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ |
|||
end |
|||
name, pat = match(opat, "^([a-z0-9]*)(.*)") |
|||
if sub(pat, 1, 1) == "." then |
|||
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)") |
|||
suffix = suffix..s2 |
|||
pat = p2 |
|||
end |
|||
|
|||
for p in gmatch(pat, ".") do |
|||
local x = nil |
|||
if p == "D" then |
|||
x = map_gpr[band(rshift(op, 12), 15)] |
|||
elseif p == "N" then |
|||
x = map_gpr[band(rshift(op, 16), 15)] |
|||
elseif p == "S" then |
|||
x = map_gpr[band(rshift(op, 8), 15)] |
|||
elseif p == "M" then |
|||
x = map_gpr[band(op, 15)] |
|||
elseif p == "d" then |
|||
x = fmtvr(op, vr, 12, 22) |
|||
elseif p == "n" then |
|||
x = fmtvr(op, vr, 16, 7) |
|||
elseif p == "m" then |
|||
x = fmtvr(op, vr, 0, 5) |
|||
elseif p == "P" then |
|||
if band(op, 0x02000000) ~= 0 then |
|||
x = ror(band(op, 255), 2*band(rshift(op, 8), 15)) |
|||
else |
|||
x = map_gpr[band(op, 15)] |
|||
if band(op, 0xff0) ~= 0 then |
|||
operands[#operands+1] = x |
|||
local s = map_shift[band(rshift(op, 5), 3)] |
|||
local r = nil |
|||
if band(op, 0xf90) == 0 then |
|||
if s == "ror" then s = "rrx" else r = "#32" end |
|||
elseif band(op, 0x10) == 0 then |
|||
r = "#"..band(rshift(op, 7), 31) |
|||
else |
|||
r = map_gpr[band(rshift(op, 8), 15)] |
|||
end |
|||
if name == "mov" then name = s; x = r |
|||
elseif r then x = format("%s %s", s, r) |
|||
else x = s end |
|||
end |
|||
end |
|||
elseif p == "L" then |
|||
x = fmtload(ctx, op, pos) |
|||
elseif p == "l" then |
|||
x = fmtvload(ctx, op, pos) |
|||
elseif p == "B" then |
|||
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6) |
|||
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end |
|||
ctx.rel = addr |
|||
x = "0x"..tohex(addr) |
|||
elseif p == "F" then |
|||
vr = "s" |
|||
elseif p == "G" then |
|||
vr = "d" |
|||
elseif p == "." then |
|||
suffix = suffix..(vr == "s" and ".f32" or ".f64") |
|||
elseif p == "R" then |
|||
if band(op, 0x00200000) ~= 0 and #operands == 1 then |
|||
operands[1] = operands[1].."!" |
|||
end |
|||
local t = {} |
|||
for i=0,15 do |
|||
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end |
|||
end |
|||
x = "{"..concat(t, ", ").."}" |
|||
elseif p == "r" then |
|||
if band(op, 0x00200000) ~= 0 and #operands == 2 then |
|||
operands[1] = operands[1].."!" |
|||
end |
|||
local s = tonumber(sub(last, 2)) |
|||
local n = band(op, 255) |
|||
if vr == "d" then n = rshift(n, 1) end |
|||
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1) |
|||
elseif p == "W" then |
|||
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000) |
|||
elseif p == "T" then |
|||
x = "#0x"..tohex(band(op, 0x00ffffff), 6) |
|||
elseif p == "U" then |
|||
x = band(rshift(op, 7), 31) |
|||
if x == 0 then x = nil end |
|||
elseif p == "u" then |
|||
x = band(rshift(op, 7), 31) |
|||
if band(op, 0x40) == 0 then |
|||
if x == 0 then x = nil else x = "lsl #"..x end |
|||
else |
|||
if x == 0 then x = "asr #32" else x = "asr #"..x end |
|||
end |
|||
elseif p == "v" then |
|||
x = band(rshift(op, 7), 31) |
|||
elseif p == "w" then |
|||
x = band(rshift(op, 16), 31) |
|||
elseif p == "x" then |
|||
x = band(rshift(op, 16), 31) + 1 |
|||
elseif p == "X" then |
|||
x = band(rshift(op, 16), 31) - last + 1 |
|||
elseif p == "Y" then |
|||
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f) |
|||
elseif p == "K" then |
|||
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4) |
|||
elseif p == "s" then |
|||
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end |
|||
else |
|||
assert(false) |
|||
end |
|||
if x then |
|||
last = x |
|||
if type(x) == "number" then x = "#"..x end |
|||
operands[#operands+1] = x |
|||
end |
|||
end |
|||
|
|||
return putop(ctx, name..suffix, operands) |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Disassemble a block of code. |
|||
local function disass_block(ctx, ofs, len) |
|||
if not ofs then ofs = 0 end |
|||
local stop = len and ofs+len or #ctx.code |
|||
ctx.pos = ofs |
|||
ctx.rel = nil |
|||
while ctx.pos < stop do disass_ins(ctx) end |
|||
end |
|||
|
|||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). |
|||
local function create(code, addr, out) |
|||
local ctx = {} |
|||
ctx.code = code |
|||
ctx.addr = addr or 0 |
|||
ctx.out = out or io.write |
|||
ctx.symtab = {} |
|||
ctx.disass = disass_block |
|||
ctx.hexdump = 8 |
|||
return ctx |
|||
end |
|||
|
|||
-- Simple API: disassemble code (a string) at address and output via out. |
|||
local function disass(code, addr, out) |
|||
create(code, addr, out):disass() |
|||
end |
|||
|
|||
-- Return register name for RID. |
|||
local function regname(r) |
|||
if r < 16 then return map_gpr[r] end |
|||
return "d"..(r-16) |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
create = create, |
|||
disass = disass, |
|||
regname = regname |
|||
} |
|||
|
File diff suppressed because it is too large
@ -0,0 +1,12 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT ARM64BE disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- ARM64 instructions are always little-endian. So just forward to the |
|||
-- common ARM64 disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
return require((string.match(..., ".*%.") or "").."dis_arm64") |
|||
|
@ -0,0 +1,694 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT MIPS disassembler module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT/X license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This is a helper module used by the LuaJIT machine code dumper module. |
|||
-- |
|||
-- It disassembles all standard MIPS32R1/R2 instructions. |
|||
-- Default mode is big-endian, but see: dis_mipsel.lua |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local type = type |
|||
local byte, format = string.byte, string.format |
|||
local match, gmatch = string.match, string.gmatch |
|||
local concat = table.concat |
|||
local bit = require("bit") |
|||
local band, bor, tohex = bit.band, bit.bor, bit.tohex |
|||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Extended opcode maps common to all MIPS releases |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", } |
|||
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", } |
|||
|
|||
local map_cop0 = { |
|||
shift = 25, mask = 1, |
|||
[0] = { |
|||
shift = 21, mask = 15, |
|||
[0] = "mfc0TDW", [4] = "mtc0TDW", |
|||
[10] = "rdpgprDT", |
|||
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", }, |
|||
[14] = "wrpgprDT", |
|||
}, { |
|||
shift = 0, mask = 63, |
|||
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp", |
|||
[24] = "eret", [31] = "deret", |
|||
[32] = "wait", |
|||
}, |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Primary and extended opcode maps for MIPS R1-R5 |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", } |
|||
|
|||
local map_special = { |
|||
shift = 0, mask = 63, |
|||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" }, |
|||
map_movci, map_srl, "sraDTA", |
|||
"sllvDTS", false, map_srlv, "sravDTS", |
|||
"jrS", "jalrD1S", "movzDST", "movnDST", |
|||
"syscallY", "breakY", false, "sync", |
|||
"mfhiD", "mthiS", "mfloD", "mtloS", |
|||
"dsllvDST", false, "dsrlvDST", "dsravDST", |
|||
"multST", "multuST", "divST", "divuST", |
|||
"dmultST", "dmultuST", "ddivST", "ddivuST", |
|||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T", |
|||
"andDST", "or|moveDST0", "xorDST", "nor|notDST0", |
|||
false, false, "sltDST", "sltuDST", |
|||
"daddDST", "dadduDST", "dsubDST", "dsubuDST", |
|||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ", |
|||
"teqSTZ", false, "tneSTZ", false, |
|||
"dsllDTA", false, "dsrlDTA", "dsraDTA", |
|||
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA", |
|||
} |
|||
|
|||
local map_special2 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "maddST", "madduST", "mulDST", false, |
|||
"msubST", "msubuST", |
|||
[32] = "clzDS", [33] = "cloDS", |
|||
[63] = "sdbbpY", |
|||
} |
|||
|
|||
local map_bshfl = { |
|||
shift = 6, mask = 31, |
|||
[2] = "wsbhDT", |
|||
[16] = "sebDT", |
|||
[24] = "sehDT", |
|||
} |
|||
|
|||
local map_dbshfl = { |
|||
shift = 6, mask = 31, |
|||
[2] = "dsbhDT", |
|||
[5] = "dshdDT", |
|||
} |
|||
|
|||
local map_special3 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK", |
|||
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL", |
|||
[32] = map_bshfl, [36] = map_dbshfl, [59] = "rdhwrTD", |
|||
} |
|||
|
|||
local map_regimm = { |
|||
shift = 16, mask = 31, |
|||
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB", |
|||
false, false, false, false, |
|||
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI", |
|||
"teqiSI", false, "tneiSI", false, |
|||
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB", |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
false, false, false, "synciSO", |
|||
} |
|||
|
|||
local map_cop1s = { |
|||
shift = 0, mask = 63, |
|||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH", |
|||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG", |
|||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG", |
|||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG", |
|||
false, |
|||
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" }, |
|||
"movz.sFGT", "movn.sFGT", |
|||
false, "recip.sFG", "rsqrt.sFG", false, |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
false, "cvt.d.sFG", false, false, |
|||
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false, |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH", |
|||
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH", |
|||
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH", |
|||
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH", |
|||
} |
|||
|
|||
local map_cop1d = { |
|||
shift = 0, mask = 63, |
|||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH", |
|||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG", |
|||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG", |
|||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG", |
|||
false, |
|||
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" }, |
|||
"movz.dFGT", "movn.dFGT", |
|||
false, "recip.dFG", "rsqrt.dFG", false, |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
"cvt.s.dFG", false, false, false, |
|||
"cvt.w.dFG", "cvt.l.dFG", false, false, |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH", |
|||
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH", |
|||
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH", |
|||
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH", |
|||
} |
|||
|
|||
local map_cop1ps = { |
|||
shift = 0, mask = 63, |
|||
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false, |
|||
false, "abs.psFG", "mov.psFG", "neg.psFG", |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
false, |
|||
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" }, |
|||
"movz.psFGT", "movn.psFGT", |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
"cvt.s.puFG", false, false, false, |
|||
false, false, false, false, |
|||
"cvt.s.plFG", false, false, false, |
|||
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH", |
|||
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH", |
|||
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH", |
|||
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH", |
|||
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH", |
|||
} |
|||
|
|||
local map_cop1w = { |
|||
shift = 0, mask = 63, |
|||
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG", |
|||
} |
|||
|
|||
local map_cop1l = { |
|||
shift = 0, mask = 63, |
|||
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG", |
|||
} |
|||
|
|||
local map_cop1bc = { |
|||
shift = 16, mask = 3, |
|||
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB", |
|||
} |
|||
|
|||
local map_cop1 = { |
|||
shift = 21, mask = 31, |
|||
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG", |
|||
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG", |
|||
map_cop1bc, false, false, false, |
|||
false, false, false, false, |
|||
map_cop1s, map_cop1d, false, false, |
|||
map_cop1w, map_cop1l, map_cop1ps, |
|||
} |
|||
|
|||
local map_cop1x = { |
|||
shift = 0, mask = 63, |
|||
[0] = "lwxc1FSX", "ldxc1FSX", false, false, |
|||
false, "luxc1FSX", false, false, |
|||
"swxc1FSX", "sdxc1FSX", false, false, |
|||
false, "suxc1FSX", false, "prefxMSX", |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
false, false, false, false, |
|||
false, false, "alnv.psFGHS", false, |
|||
"madd.sFRGH", "madd.dFRGH", false, false, |
|||
false, false, "madd.psFRGH", false, |
|||
"msub.sFRGH", "msub.dFRGH", false, false, |
|||
false, false, "msub.psFRGH", false, |
|||
"nmadd.sFRGH", "nmadd.dFRGH", false, false, |
|||
false, false, "nmadd.psFRGH", false, |
|||
"nmsub.sFRGH", "nmsub.dFRGH", false, false, |
|||
false, false, "nmsub.psFRGH", false, |
|||
} |
|||
|
|||
local map_pri = { |
|||
[0] = map_special, map_regimm, "jJ", "jalJ", |
|||
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB", |
|||
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI", |
|||
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU", |
|||
map_cop0, map_cop1, false, map_cop1x, |
|||
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB", |
|||
"daddiTSI", "daddiuTSI", false, false, |
|||
map_special2, "jalxJ", false, map_special3, |
|||
"lbTSO", "lhTSO", "lwlTSO", "lwTSO", |
|||
"lbuTSO", "lhuTSO", "lwrTSO", false, |
|||
"sbTSO", "shTSO", "swlTSO", "swTSO", |
|||
false, false, "swrTSO", "cacheNSO", |
|||
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO", |
|||
false, "ldc1HSO", "ldc2TSO", "ldTSO", |
|||
"scTSO", "swc1HSO", "swc2TSO", false, |
|||
false, "sdc1HSO", "sdc2TSO", "sdTSO", |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Primary and extended opcode maps for MIPS R6 |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_mul_r6 = { shift = 6, mask = 3, [2] = "mulDST", [3] = "muhDST" } |
|||
local map_mulu_r6 = { shift = 6, mask = 3, [2] = "muluDST", [3] = "muhuDST" } |
|||
local map_div_r6 = { shift = 6, mask = 3, [2] = "divDST", [3] = "modDST" } |
|||
local map_divu_r6 = { shift = 6, mask = 3, [2] = "divuDST", [3] = "moduDST" } |
|||
local map_dmul_r6 = { shift = 6, mask = 3, [2] = "dmulDST", [3] = "dmuhDST" } |
|||
local map_dmulu_r6 = { shift = 6, mask = 3, [2] = "dmuluDST", [3] = "dmuhuDST" } |
|||
local map_ddiv_r6 = { shift = 6, mask = 3, [2] = "ddivDST", [3] = "dmodDST" } |
|||
local map_ddivu_r6 = { shift = 6, mask = 3, [2] = "ddivuDST", [3] = "dmoduDST" } |
|||
|
|||
local map_special_r6 = { |
|||
shift = 0, mask = 63, |
|||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" }, |
|||
false, map_srl, "sraDTA", |
|||
"sllvDTS", false, map_srlv, "sravDTS", |
|||
"jrS", "jalrD1S", false, false, |
|||
"syscallY", "breakY", false, "sync", |
|||
"clzDS", "cloDS", "dclzDS", "dcloDS", |
|||
"dsllvDST", "dlsaDSTA", "dsrlvDST", "dsravDST", |
|||
map_mul_r6, map_mulu_r6, map_div_r6, map_divu_r6, |
|||
map_dmul_r6, map_dmulu_r6, map_ddiv_r6, map_ddivu_r6, |
|||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T", |
|||
"andDST", "or|moveDST0", "xorDST", "nor|notDST0", |
|||
false, false, "sltDST", "sltuDST", |
|||
"daddDST", "dadduDST", "dsubDST", "dsubuDST", |
|||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ", |
|||
"teqSTZ", "seleqzDST", "tneSTZ", "selnezDST", |
|||
"dsllDTA", false, "dsrlDTA", "dsraDTA", |
|||
"dsll32DTA", false, "dsrl32DTA", "dsra32DTA", |
|||
} |
|||
|
|||
local map_bshfl_r6 = { |
|||
shift = 9, mask = 3, |
|||
[1] = "alignDSTa", |
|||
_ = { |
|||
shift = 6, mask = 31, |
|||
[0] = "bitswapDT", |
|||
[2] = "wsbhDT", |
|||
[16] = "sebDT", |
|||
[24] = "sehDT", |
|||
} |
|||
} |
|||
|
|||
local map_dbshfl_r6 = { |
|||
shift = 9, mask = 3, |
|||
[1] = "dalignDSTa", |
|||
_ = { |
|||
shift = 6, mask = 31, |
|||
[0] = "dbitswapDT", |
|||
[2] = "dsbhDT", |
|||
[5] = "dshdDT", |
|||
} |
|||
} |
|||
|
|||
local map_special3_r6 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "extTSAK", [1] = "dextmTSAP", [3] = "dextTSAK", |
|||
[4] = "insTSAL", [6] = "dinsuTSEQ", [7] = "dinsTSAL", |
|||
[32] = map_bshfl_r6, [36] = map_dbshfl_r6, [59] = "rdhwrTD", |
|||
} |
|||
|
|||
local map_regimm_r6 = { |
|||
shift = 16, mask = 31, |
|||
[0] = "bltzSB", [1] = "bgezSB", |
|||
[6] = "dahiSI", [30] = "datiSI", |
|||
[23] = "sigrieI", [31] = "synciSO", |
|||
} |
|||
|
|||
local map_pcrel_r6 = { |
|||
shift = 19, mask = 3, |
|||
[0] = "addiupcS2", "lwpcS2", "lwupcS2", { |
|||
shift = 18, mask = 1, |
|||
[0] = "ldpcS3", { shift = 16, mask = 3, [2] = "auipcSI", [3] = "aluipcSI" } |
|||
} |
|||
} |
|||
|
|||
local map_cop1s_r6 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH", |
|||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG", |
|||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG", |
|||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG", |
|||
"sel.sFGH", false, false, false, |
|||
"seleqz.sFGH", "recip.sFG", "rsqrt.sFG", "selnez.sFGH", |
|||
"maddf.sFGH", "msubf.sFGH", "rint.sFG", "class.sFG", |
|||
"min.sFGH", "mina.sFGH", "max.sFGH", "maxa.sFGH", |
|||
false, "cvt.d.sFG", false, false, |
|||
"cvt.w.sFG", "cvt.l.sFG", |
|||
} |
|||
|
|||
local map_cop1d_r6 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH", |
|||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG", |
|||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG", |
|||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG", |
|||
"sel.dFGH", false, false, false, |
|||
"seleqz.dFGH", "recip.dFG", "rsqrt.dFG", "selnez.dFGH", |
|||
"maddf.dFGH", "msubf.dFGH", "rint.dFG", "class.dFG", |
|||
"min.dFGH", "mina.dFGH", "max.dFGH", "maxa.dFGH", |
|||
"cvt.s.dFG", false, false, false, |
|||
"cvt.w.dFG", "cvt.l.dFG", |
|||
} |
|||
|
|||
local map_cop1w_r6 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "cmp.af.sFGH", "cmp.un.sFGH", "cmp.eq.sFGH", "cmp.ueq.sFGH", |
|||
"cmp.lt.sFGH", "cmp.ult.sFGH", "cmp.le.sFGH", "cmp.ule.sFGH", |
|||
"cmp.saf.sFGH", "cmp.sun.sFGH", "cmp.seq.sFGH", "cmp.sueq.sFGH", |
|||
"cmp.slt.sFGH", "cmp.sult.sFGH", "cmp.sle.sFGH", "cmp.sule.sFGH", |
|||
false, "cmp.or.sFGH", "cmp.une.sFGH", "cmp.ne.sFGH", |
|||
false, false, false, false, |
|||
false, "cmp.sor.sFGH", "cmp.sune.sFGH", "cmp.sne.sFGH", |
|||
false, false, false, false, |
|||
"cvt.s.wFG", "cvt.d.wFG", |
|||
} |
|||
|
|||
local map_cop1l_r6 = { |
|||
shift = 0, mask = 63, |
|||
[0] = "cmp.af.dFGH", "cmp.un.dFGH", "cmp.eq.dFGH", "cmp.ueq.dFGH", |
|||
"cmp.lt.dFGH", "cmp.ult.dFGH", "cmp.le.dFGH", "cmp.ule.dFGH", |
|||
"cmp.saf.dFGH", "cmp.sun.dFGH", "cmp.seq.dFGH", "cmp.sueq.dFGH", |
|||
"cmp.slt.dFGH", "cmp.sult.dFGH", "cmp.sle.dFGH", "cmp.sule.dFGH", |
|||
false, "cmp.or.dFGH", "cmp.une.dFGH", "cmp.ne.dFGH", |
|||
false, false, false, false, |
|||
false, "cmp.sor.dFGH", "cmp.sune.dFGH", "cmp.sne.dFGH", |
|||
false, false, false, false, |
|||
"cvt.s.lFG", "cvt.d.lFG", |
|||
} |
|||
|
|||
local map_cop1_r6 = { |
|||
shift = 21, mask = 31, |
|||
[0] = "mfc1TG", "dmfc1TG", "cfc1TG", "mfhc1TG", |
|||
"mtc1TG", "dmtc1TG", "ctc1TG", "mthc1TG", |
|||
false, "bc1eqzHB", false, false, |
|||
false, "bc1nezHB", false, false, |
|||
map_cop1s_r6, map_cop1d_r6, false, false, |
|||
map_cop1w_r6, map_cop1l_r6, |
|||
} |
|||
|
|||
local function maprs_popTS(rs, rt) |
|||
if rt == 0 then return 0 elseif rs == 0 then return 1 |
|||
elseif rs == rt then return 2 else return 3 end |
|||
end |
|||
|
|||
local map_pop06_r6 = { |
|||
maprs = maprs_popTS, [0] = "blezSB", "blezalcTB", "bgezalcTB", "bgeucSTB" |
|||
} |
|||
local map_pop07_r6 = { |
|||
maprs = maprs_popTS, [0] = "bgtzSB", "bgtzalcTB", "bltzalcTB", "bltucSTB" |
|||
} |
|||
local map_pop26_r6 = { |
|||
maprs = maprs_popTS, "blezcTB", "bgezcTB", "bgecSTB" |
|||
} |
|||
local map_pop27_r6 = { |
|||
maprs = maprs_popTS, "bgtzcTB", "bltzcTB", "bltcSTB" |
|||
} |
|||
|
|||
local function maprs_popS(rs, rt) |
|||
if rs == 0 then return 0 else return 1 end |
|||
end |
|||
|
|||
local map_pop66_r6 = { |
|||
maprs = maprs_popS, [0] = "jicTI", "beqzcSb" |
|||
} |
|||
local map_pop76_r6 = { |
|||
maprs = maprs_popS, [0] = "jialcTI", "bnezcSb" |
|||
} |
|||
|
|||
local function maprs_popST(rs, rt) |
|||
if rs >= rt then return 0 elseif rs == 0 then return 1 else return 2 end |
|||
end |
|||
|
|||
local map_pop10_r6 = { |
|||
maprs = maprs_popST, [0] = "bovcSTB", "beqzalcTB", "beqcSTB" |
|||
} |
|||
local map_pop30_r6 = { |
|||
maprs = maprs_popST, [0] = "bnvcSTB", "bnezalcTB", "bnecSTB" |
|||
} |
|||
|
|||
local map_pri_r6 = { |
|||
[0] = map_special_r6, map_regimm_r6, "jJ", "jalJ", |
|||
"beq|beqz|bST00B", "bne|bnezST0B", map_pop06_r6, map_pop07_r6, |
|||
map_pop10_r6, "addiu|liTS0I", "sltiTSI", "sltiuTSI", |
|||
"andiTSU", "ori|liTS0U", "xoriTSU", "aui|luiTS0U", |
|||
map_cop0, map_cop1_r6, false, false, |
|||
false, false, map_pop26_r6, map_pop27_r6, |
|||
map_pop30_r6, "daddiuTSI", false, false, |
|||
false, "dauiTSI", false, map_special3_r6, |
|||
"lbTSO", "lhTSO", false, "lwTSO", |
|||
"lbuTSO", "lhuTSO", false, false, |
|||
"sbTSO", "shTSO", false, "swTSO", |
|||
false, false, false, false, |
|||
false, "lwc1HSO", "bc#", false, |
|||
false, "ldc1HSO", map_pop66_r6, "ldTSO", |
|||
false, "swc1HSO", "balc#", map_pcrel_r6, |
|||
false, "sdc1HSO", map_pop76_r6, "sdTSO", |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_gpr = { |
|||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
|||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", |
|||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", |
|||
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra", |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Output a nicely formatted line with an opcode and operands. |
|||
local function putop(ctx, text, operands) |
|||
local pos = ctx.pos |
|||
local extra = "" |
|||
if ctx.rel then |
|||
local sym = ctx.symtab[ctx.rel] |
|||
if sym then extra = "\t->"..sym end |
|||
end |
|||
if ctx.hexdump > 0 then |
|||
ctx.out(format("%08x %s %-7s %s%s\n", |
|||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) |
|||
else |
|||
ctx.out(format("%08x %-7s %s%s\n", |
|||
ctx.addr+pos, text, concat(operands, ", "), extra)) |
|||
end |
|||
ctx.pos = pos + 4 |
|||
end |
|||
|
|||
-- Fallback for unknown opcodes. |
|||
local function unknown(ctx) |
|||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) |
|||
end |
|||
|
|||
local function get_be(ctx) |
|||
local pos = ctx.pos |
|||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) |
|||
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) |
|||
end |
|||
|
|||
local function get_le(ctx) |
|||
local pos = ctx.pos |
|||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) |
|||
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0) |
|||
end |
|||
|
|||
-- Disassemble a single instruction. |
|||
local function disass_ins(ctx) |
|||
local op = ctx:get() |
|||
local operands = {} |
|||
local last = nil |
|||
ctx.op = op |
|||
ctx.rel = nil |
|||
|
|||
local opat = ctx.map_pri[rshift(op, 26)] |
|||
while type(opat) ~= "string" do |
|||
if not opat then return unknown(ctx) end |
|||
if opat.maprs then |
|||
opat = opat[opat.maprs(band(rshift(op,21),31), band(rshift(op,16),31))] |
|||
else |
|||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._ |
|||
end |
|||
end |
|||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") |
|||
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)") |
|||
if altname then pat = pat2 end |
|||
|
|||
for p in gmatch(pat, ".") do |
|||
local x = nil |
|||
if p == "S" then |
|||
x = map_gpr[band(rshift(op, 21), 31)] |
|||
elseif p == "T" then |
|||
x = map_gpr[band(rshift(op, 16), 31)] |
|||
elseif p == "D" then |
|||
x = map_gpr[band(rshift(op, 11), 31)] |
|||
elseif p == "F" then |
|||
x = "f"..band(rshift(op, 6), 31) |
|||
elseif p == "G" then |
|||
x = "f"..band(rshift(op, 11), 31) |
|||
elseif p == "H" then |
|||
x = "f"..band(rshift(op, 16), 31) |
|||
elseif p == "R" then |
|||
x = "f"..band(rshift(op, 21), 31) |
|||
elseif p == "A" then |
|||
x = band(rshift(op, 6), 31) |
|||
elseif p == "a" then |
|||
x = band(rshift(op, 6), 7) |
|||
elseif p == "E" then |
|||
x = band(rshift(op, 6), 31) + 32 |
|||
elseif p == "M" then |
|||
x = band(rshift(op, 11), 31) |
|||
elseif p == "N" then |
|||
x = band(rshift(op, 16), 31) |
|||
elseif p == "C" then |
|||
x = band(rshift(op, 18), 7) |
|||
if x == 0 then x = nil end |
|||
elseif p == "K" then |
|||
x = band(rshift(op, 11), 31) + 1 |
|||
elseif p == "P" then |
|||
x = band(rshift(op, 11), 31) + 33 |
|||
elseif p == "L" then |
|||
x = band(rshift(op, 11), 31) - last + 1 |
|||
elseif p == "Q" then |
|||
x = band(rshift(op, 11), 31) - last + 33 |
|||
elseif p == "I" then |
|||
x = arshift(lshift(op, 16), 16) |
|||
elseif p == "2" then |
|||
x = arshift(lshift(op, 13), 11) |
|||
elseif p == "3" then |
|||
x = arshift(lshift(op, 14), 11) |
|||
elseif p == "U" then |
|||
x = band(op, 0xffff) |
|||
elseif p == "O" then |
|||
local disp = arshift(lshift(op, 16), 16) |
|||
operands[#operands] = format("%d(%s)", disp, last) |
|||
elseif p == "X" then |
|||
local index = map_gpr[band(rshift(op, 16), 31)] |
|||
operands[#operands] = format("%s(%s)", index, last) |
|||
elseif p == "B" then |
|||
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 14) + 4 |
|||
ctx.rel = x |
|||
x = format("0x%08x", x) |
|||
elseif p == "b" then |
|||
x = ctx.addr + ctx.pos + arshift(lshift(op, 11), 9) + 4 |
|||
ctx.rel = x |
|||
x = format("0x%08x", x) |
|||
elseif p == "#" then |
|||
x = ctx.addr + ctx.pos + arshift(lshift(op, 6), 4) + 4 |
|||
ctx.rel = x |
|||
x = format("0x%08x", x) |
|||
elseif p == "J" then |
|||
local a = ctx.addr + ctx.pos |
|||
x = a - band(a, 0x0fffffff) + band(op, 0x03ffffff)*4 |
|||
ctx.rel = x |
|||
x = format("0x%08x", x) |
|||
elseif p == "V" then |
|||
x = band(rshift(op, 8), 7) |
|||
if x == 0 then x = nil end |
|||
elseif p == "W" then |
|||
x = band(op, 7) |
|||
if x == 0 then x = nil end |
|||
elseif p == "Y" then |
|||
x = band(rshift(op, 6), 0x000fffff) |
|||
if x == 0 then x = nil end |
|||
elseif p == "Z" then |
|||
x = band(rshift(op, 6), 1023) |
|||
if x == 0 then x = nil end |
|||
elseif p == "0" then |
|||
if last == "r0" or last == 0 then |
|||
local n = #operands |
|||
operands[n] = nil |
|||
last = operands[n-1] |
|||
if altname then |
|||
local a1, a2 = match(altname, "([^|]*)|(.*)") |
|||
if a1 then name, altname = a1, a2 |
|||
else name = altname end |
|||
end |
|||
end |
|||
elseif p == "1" then |
|||
if last == "ra" then |
|||
operands[#operands] = nil |
|||
end |
|||
else |
|||
assert(false) |
|||
end |
|||
if x then operands[#operands+1] = x; last = x end |
|||
end |
|||
|
|||
return putop(ctx, name, operands) |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Disassemble a block of code. |
|||
local function disass_block(ctx, ofs, len) |
|||
if not ofs then ofs = 0 end |
|||
local stop = len and ofs+len or #ctx.code |
|||
stop = stop - stop % 4 |
|||
ctx.pos = ofs - ofs % 4 |
|||
ctx.rel = nil |
|||
while ctx.pos < stop do disass_ins(ctx) end |
|||
end |
|||
|
|||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). |
|||
local function create(code, addr, out) |
|||
local ctx = {} |
|||
ctx.code = code |
|||
ctx.addr = addr or 0 |
|||
ctx.out = out or io.write |
|||
ctx.symtab = {} |
|||
ctx.disass = disass_block |
|||
ctx.hexdump = 8 |
|||
ctx.get = get_be |
|||
ctx.map_pri = map_pri |
|||
return ctx |
|||
end |
|||
|
|||
local function create_el(code, addr, out) |
|||
local ctx = create(code, addr, out) |
|||
ctx.get = get_le |
|||
return ctx |
|||
end |
|||
|
|||
local function create_r6(code, addr, out) |
|||
local ctx = create(code, addr, out) |
|||
ctx.map_pri = map_pri_r6 |
|||
return ctx |
|||
end |
|||
|
|||
local function create_r6_el(code, addr, out) |
|||
local ctx = create(code, addr, out) |
|||
ctx.get = get_le |
|||
ctx.map_pri = map_pri_r6 |
|||
return ctx |
|||
end |
|||
|
|||
-- Simple API: disassemble code (a string) at address and output via out. |
|||
local function disass(code, addr, out) |
|||
create(code, addr, out):disass() |
|||
end |
|||
|
|||
local function disass_el(code, addr, out) |
|||
create_el(code, addr, out):disass() |
|||
end |
|||
|
|||
local function disass_r6(code, addr, out) |
|||
create_r6(code, addr, out):disass() |
|||
end |
|||
|
|||
local function disass_r6_el(code, addr, out) |
|||
create_r6_el(code, addr, out):disass() |
|||
end |
|||
|
|||
-- Return register name for RID. |
|||
local function regname(r) |
|||
if r < 32 then return map_gpr[r] end |
|||
return "f"..(r-32) |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
create = create, |
|||
create_el = create_el, |
|||
create_r6 = create_r6, |
|||
create_r6_el = create_r6_el, |
|||
disass = disass, |
|||
disass_el = disass_el, |
|||
disass_r6 = disass_r6, |
|||
disass_r6_el = disass_r6_el, |
|||
regname = regname |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT MIPS64 disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This module just exports the big-endian functions from the |
|||
-- MIPS disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") |
|||
return { |
|||
create = dis_mips.create, |
|||
disass = dis_mips.disass, |
|||
regname = dis_mips.regname |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT MIPS64EL disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This module just exports the little-endian functions from the |
|||
-- MIPS disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") |
|||
return { |
|||
create = dis_mips.create_el, |
|||
disass = dis_mips.disass_el, |
|||
regname = dis_mips.regname |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT MIPS64R6 disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This module just exports the r6 big-endian functions from the |
|||
-- MIPS disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") |
|||
return { |
|||
create = dis_mips.create_r6, |
|||
disass = dis_mips.disass_r6, |
|||
regname = dis_mips.regname |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT MIPS64R6EL disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This module just exports the r6 little-endian functions from the |
|||
-- MIPS disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") |
|||
return { |
|||
create = dis_mips.create_r6_el, |
|||
disass = dis_mips.disass_r6_el, |
|||
regname = dis_mips.regname |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT MIPSEL disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This module just exports the little-endian functions from the |
|||
-- MIPS disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local dis_mips = require((string.match(..., ".*%.") or "").."dis_mips") |
|||
return { |
|||
create = dis_mips.create_el, |
|||
disass = dis_mips.disass_el, |
|||
regname = dis_mips.regname |
|||
} |
|||
|
@ -0,0 +1,591 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT PPC disassembler module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT/X license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This is a helper module used by the LuaJIT machine code dumper module. |
|||
-- |
|||
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions |
|||
-- plus the e500 SPE instructions and some Cell/Xenon extensions. |
|||
-- |
|||
-- NYI: VMX, VMX128 |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local type = type |
|||
local byte, format = string.byte, string.format |
|||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub |
|||
local concat = table.concat |
|||
local bit = require("bit") |
|||
local band, bor, tohex = bit.band, bit.bor, bit.tohex |
|||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift |
|||
|
|||
------------------------------------------------------------------------------ |
|||
-- Primary and extended opcode maps |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_crops = { |
|||
shift = 1, mask = 1023, |
|||
[0] = "mcrfXX", |
|||
[33] = "crnor|crnotCCC=", [129] = "crandcCCC", |
|||
[193] = "crxor|crclrCCC%", [225] = "crnandCCC", |
|||
[257] = "crandCCC", [289] = "creqv|crsetCCC%", |
|||
[417] = "crorcCCC", [449] = "cror|crmoveCCC=", |
|||
[16] = "b_lrKB", [528] = "b_ctrKB", |
|||
[150] = "isync", |
|||
} |
|||
|
|||
local map_rlwinm = setmetatable({ |
|||
shift = 0, mask = -1, |
|||
}, |
|||
{ __index = function(t, x) |
|||
local rot = band(rshift(x, 11), 31) |
|||
local mb = band(rshift(x, 6), 31) |
|||
local me = band(rshift(x, 1), 31) |
|||
if mb == 0 and me == 31-rot then |
|||
return "slwiRR~A." |
|||
elseif me == 31 and mb == 32-rot then |
|||
return "srwiRR~-A." |
|||
else |
|||
return "rlwinmRR~AAA." |
|||
end |
|||
end |
|||
}) |
|||
|
|||
local map_rld = { |
|||
shift = 2, mask = 7, |
|||
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.", |
|||
{ |
|||
shift = 1, mask = 1, |
|||
[0] = "rldclRR~RM.", "rldcrRR~RM.", |
|||
}, |
|||
} |
|||
|
|||
local map_ext = setmetatable({ |
|||
shift = 1, mask = 1023, |
|||
|
|||
[0] = "cmp_YLRR", [32] = "cmpl_YLRR", |
|||
[4] = "twARR", [68] = "tdARR", |
|||
|
|||
[8] = "subfcRRR.", [40] = "subfRRR.", |
|||
[104] = "negRR.", [136] = "subfeRRR.", |
|||
[200] = "subfzeRR.", [232] = "subfmeRR.", |
|||
[520] = "subfcoRRR.", [552] = "subfoRRR.", |
|||
[616] = "negoRR.", [648] = "subfeoRRR.", |
|||
[712] = "subfzeoRR.", [744] = "subfmeoRR.", |
|||
|
|||
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.", |
|||
[457] = "divduRRR.", [489] = "divdRRR.", |
|||
[745] = "mulldoRRR.", |
|||
[969] = "divduoRRR.", [1001] = "divdoRRR.", |
|||
|
|||
[10] = "addcRRR.", [138] = "addeRRR.", |
|||
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.", |
|||
[522] = "addcoRRR.", [650] = "addeoRRR.", |
|||
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.", |
|||
|
|||
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.", |
|||
[459] = "divwuRRR.", [491] = "divwRRR.", |
|||
[747] = "mullwoRRR.", |
|||
[971] = "divwouRRR.", [1003] = "divwoRRR.", |
|||
|
|||
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR", |
|||
|
|||
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", }, |
|||
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", }, |
|||
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", }, |
|||
[339] = { |
|||
shift = 11, mask = 1023, |
|||
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR", |
|||
}, |
|||
[467] = { |
|||
shift = 11, mask = 1023, |
|||
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR", |
|||
}, |
|||
|
|||
[20] = "lwarxRR0R", [84] = "ldarxRR0R", |
|||
|
|||
[21] = "ldxRR0R", [53] = "lduxRRR", |
|||
[149] = "stdxRR0R", [181] = "stduxRRR", |
|||
[341] = "lwaxRR0R", [373] = "lwauxRRR", |
|||
|
|||
[23] = "lwzxRR0R", [55] = "lwzuxRRR", |
|||
[87] = "lbzxRR0R", [119] = "lbzuxRRR", |
|||
[151] = "stwxRR0R", [183] = "stwuxRRR", |
|||
[215] = "stbxRR0R", [247] = "stbuxRRR", |
|||
[279] = "lhzxRR0R", [311] = "lhzuxRRR", |
|||
[343] = "lhaxRR0R", [375] = "lhauxRRR", |
|||
[407] = "sthxRR0R", [439] = "sthuxRRR", |
|||
|
|||
[54] = "dcbst-R0R", [86] = "dcbf-R0R", |
|||
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.", |
|||
[246] = "dcbtst-R0R", [278] = "dcbt-R0R", |
|||
[310] = "eciwxRR0R", [438] = "ecowxRR0R", |
|||
[470] = "dcbi-RR", |
|||
|
|||
[598] = { |
|||
shift = 21, mask = 3, |
|||
[0] = "sync", "lwsync", "ptesync", |
|||
}, |
|||
[758] = "dcba-RR", |
|||
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R", |
|||
|
|||
[26] = "cntlzwRR~", [58] = "cntlzdRR~", |
|||
[122] = "popcntbRR~", |
|||
[154] = "prtywRR~", [186] = "prtydRR~", |
|||
|
|||
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.", |
|||
[284] = "eqvRR~R.", [316] = "xorRR~R.", |
|||
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.", |
|||
[508] = "cmpbRR~R", |
|||
|
|||
[512] = "mcrxrX", |
|||
|
|||
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R", |
|||
|
|||
[533] = "lswxRR0R", [597] = "lswiRR0A", |
|||
[661] = "stswxRR0R", [725] = "stswiRR0A", |
|||
|
|||
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R", |
|||
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R", |
|||
|
|||
[535] = "lfsxFR0R", [567] = "lfsuxFRR", |
|||
[599] = "lfdxFR0R", [631] = "lfduxFRR", |
|||
[663] = "stfsxFR0R", [695] = "stfsuxFRR", |
|||
[727] = "stfdxFR0R", [759] = "stfduxFR0R", |
|||
[855] = "lfiwaxFR0R", |
|||
[983] = "stfiwxFR0R", |
|||
|
|||
[24] = "slwRR~R.", |
|||
|
|||
[27] = "sldRR~R.", [536] = "srwRR~R.", |
|||
[792] = "srawRR~R.", [824] = "srawiRR~A.", |
|||
|
|||
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.", |
|||
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.", |
|||
|
|||
[539] = "srdRR~R.", |
|||
}, |
|||
{ __index = function(t, x) |
|||
if band(x, 31) == 15 then return "iselRRRC" end |
|||
end |
|||
}) |
|||
|
|||
local map_ld = { |
|||
shift = 0, mask = 3, |
|||
[0] = "ldRRE", "lduRRE", "lwaRRE", |
|||
} |
|||
|
|||
local map_std = { |
|||
shift = 0, mask = 3, |
|||
[0] = "stdRRE", "stduRRE", |
|||
} |
|||
|
|||
local map_fps = { |
|||
shift = 5, mask = 1, |
|||
{ |
|||
shift = 1, mask = 15, |
|||
[0] = false, false, "fdivsFFF.", false, |
|||
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false, |
|||
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false, |
|||
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.", |
|||
} |
|||
} |
|||
|
|||
local map_fpd = { |
|||
shift = 5, mask = 1, |
|||
[0] = { |
|||
shift = 1, mask = 1023, |
|||
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX", |
|||
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>", |
|||
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.", |
|||
[136] = "fnabsF-F.", [264] = "fabsF-F.", |
|||
[12] = "frspF-F.", |
|||
[14] = "fctiwF-F.", [15] = "fctiwzF-F.", |
|||
[583] = "mffsF.", [711] = "mtfsfZF.", |
|||
[392] = "frinF-F.", [424] = "frizF-F.", |
|||
[456] = "fripF-F.", [488] = "frimF-F.", |
|||
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.", |
|||
}, |
|||
{ |
|||
shift = 1, mask = 15, |
|||
[0] = false, false, "fdivFFF.", false, |
|||
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.", |
|||
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false, |
|||
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.", |
|||
} |
|||
} |
|||
|
|||
local map_spe = { |
|||
shift = 0, mask = 2047, |
|||
|
|||
[512] = "evaddwRRR", [514] = "evaddiwRAR~", |
|||
[516] = "evsubwRRR~", [518] = "evsubiwRAR~", |
|||
[520] = "evabsRR", [521] = "evnegRR", |
|||
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR", |
|||
[525] = "evcntlzwRR", [526] = "evcntlswRR", |
|||
|
|||
[527] = "brincRRR", |
|||
|
|||
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR", |
|||
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=", |
|||
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR", |
|||
|
|||
[544] = "evsrwuRRR", [545] = "evsrwsRRR", |
|||
[546] = "evsrwiuRRA", [547] = "evsrwisRRA", |
|||
[548] = "evslwRRR", [550] = "evslwiRRA", |
|||
[552] = "evrlwRRR", [553] = "evsplatiRS", |
|||
[554] = "evrlwiRRA", [555] = "evsplatfiRS", |
|||
[556] = "evmergehiRRR", [557] = "evmergeloRRR", |
|||
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR", |
|||
|
|||
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR", |
|||
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR", |
|||
[564] = "evcmpeqYRR", |
|||
|
|||
[632] = "evselRRR", [633] = "evselRRRW", |
|||
[634] = "evselRRRW", [635] = "evselRRRW", |
|||
[636] = "evselRRRW", [637] = "evselRRRW", |
|||
[638] = "evselRRRW", [639] = "evselRRRW", |
|||
|
|||
[640] = "evfsaddRRR", [641] = "evfssubRRR", |
|||
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR", |
|||
[648] = "evfsmulRRR", [649] = "evfsdivRRR", |
|||
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR", |
|||
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R", |
|||
[658] = "evfscfufR-R", [659] = "evfscfsfR-R", |
|||
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R", |
|||
[662] = "evfsctufR-R", [663] = "evfsctsfR-R", |
|||
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R", |
|||
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR", |
|||
|
|||
[704] = "efsaddRRR", [705] = "efssubRRR", |
|||
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR", |
|||
[712] = "efsmulRRR", [713] = "efsdivRRR", |
|||
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR", |
|||
[719] = "efscfdR-R", |
|||
[720] = "efscfuiR-R", [721] = "efscfsiR-R", |
|||
[722] = "efscfufR-R", [723] = "efscfsfR-R", |
|||
[724] = "efsctuiR-R", [725] = "efsctsiR-R", |
|||
[726] = "efsctufR-R", [727] = "efsctsfR-R", |
|||
[728] = "efsctuizR-R", [730] = "efsctsizR-R", |
|||
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR", |
|||
|
|||
[736] = "efdaddRRR", [737] = "efdsubRRR", |
|||
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R", |
|||
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR", |
|||
[744] = "efdmulRRR", [745] = "efddivRRR", |
|||
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R", |
|||
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR", |
|||
[751] = "efdcfsR-R", |
|||
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R", |
|||
[754] = "efdcfufR-R", [755] = "efdcfsfR-R", |
|||
[756] = "efdctuiR-R", [757] = "efdctsiR-R", |
|||
[758] = "efdctufR-R", [759] = "efdctsfR-R", |
|||
[760] = "efdctuizR-R", [762] = "efdctsizR-R", |
|||
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR", |
|||
|
|||
[768] = "evlddxRR0R", [769] = "evlddRR8", |
|||
[770] = "evldwxRR0R", [771] = "evldwRR8", |
|||
[772] = "evldhxRR0R", [773] = "evldhRR8", |
|||
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2", |
|||
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2", |
|||
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2", |
|||
[784] = "evlwhexRR0R", [785] = "evlwheRR4", |
|||
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4", |
|||
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4", |
|||
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4", |
|||
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4", |
|||
|
|||
[800] = "evstddxRR0R", [801] = "evstddRR8", |
|||
[802] = "evstdwxRR0R", [803] = "evstdwRR8", |
|||
[804] = "evstdhxRR0R", [805] = "evstdhRR8", |
|||
[816] = "evstwhexRR0R", [817] = "evstwheRR4", |
|||
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4", |
|||
[824] = "evstwwexRR0R", [825] = "evstwweRR4", |
|||
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4", |
|||
|
|||
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR", |
|||
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR", |
|||
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR", |
|||
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR", |
|||
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR", |
|||
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR", |
|||
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR", |
|||
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR", |
|||
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR", |
|||
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR", |
|||
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR", |
|||
[1147] = "evmwsmfaRRR", |
|||
|
|||
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR", |
|||
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR", |
|||
[1220] = "evmraRR", |
|||
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR", |
|||
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR", |
|||
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR", |
|||
|
|||
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR", |
|||
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR", |
|||
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR", |
|||
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR", |
|||
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR", |
|||
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR", |
|||
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR", |
|||
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR", |
|||
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR", |
|||
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR", |
|||
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR", |
|||
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR", |
|||
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR", |
|||
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR", |
|||
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR", |
|||
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR", |
|||
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR", |
|||
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR", |
|||
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR", |
|||
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR", |
|||
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR", |
|||
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR", |
|||
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR", |
|||
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR", |
|||
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR", |
|||
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR", |
|||
} |
|||
|
|||
local map_pri = { |
|||
[0] = false, false, "tdiARI", "twiARI", |
|||
map_spe, false, false, "mulliRRI", |
|||
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI", |
|||
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I", |
|||
"b_KBJ", "sc", "bKJ", map_crops, |
|||
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.", |
|||
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U", |
|||
"andi.RR~U", "andis.RR~U", map_rld, map_ext, |
|||
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD", |
|||
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD", |
|||
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD", |
|||
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD", |
|||
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD", |
|||
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD", |
|||
false, false, map_ld, map_fps, |
|||
false, false, map_std, map_fpd, |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local map_gpr = { |
|||
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", |
|||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", |
|||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", |
|||
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", |
|||
} |
|||
|
|||
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", } |
|||
|
|||
-- Format a condition bit. |
|||
local function condfmt(cond) |
|||
if cond <= 3 then |
|||
return map_cond[band(cond, 3)] |
|||
else |
|||
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)]) |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Output a nicely formatted line with an opcode and operands. |
|||
local function putop(ctx, text, operands) |
|||
local pos = ctx.pos |
|||
local extra = "" |
|||
if ctx.rel then |
|||
local sym = ctx.symtab[ctx.rel] |
|||
if sym then extra = "\t->"..sym end |
|||
end |
|||
if ctx.hexdump > 0 then |
|||
ctx.out(format("%08x %s %-7s %s%s\n", |
|||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra)) |
|||
else |
|||
ctx.out(format("%08x %-7s %s%s\n", |
|||
ctx.addr+pos, text, concat(operands, ", "), extra)) |
|||
end |
|||
ctx.pos = pos + 4 |
|||
end |
|||
|
|||
-- Fallback for unknown opcodes. |
|||
local function unknown(ctx) |
|||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) }) |
|||
end |
|||
|
|||
-- Disassemble a single instruction. |
|||
local function disass_ins(ctx) |
|||
local pos = ctx.pos |
|||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4) |
|||
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3) |
|||
local operands = {} |
|||
local last = nil |
|||
local rs = 21 |
|||
ctx.op = op |
|||
ctx.rel = nil |
|||
|
|||
local opat = map_pri[rshift(b0, 2)] |
|||
while type(opat) ~= "string" do |
|||
if not opat then return unknown(ctx) end |
|||
opat = opat[band(rshift(op, opat.shift), opat.mask)] |
|||
end |
|||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)") |
|||
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)") |
|||
if altname then pat = pat2 end |
|||
|
|||
for p in gmatch(pat, ".") do |
|||
local x = nil |
|||
if p == "R" then |
|||
x = map_gpr[band(rshift(op, rs), 31)] |
|||
rs = rs - 5 |
|||
elseif p == "F" then |
|||
x = "f"..band(rshift(op, rs), 31) |
|||
rs = rs - 5 |
|||
elseif p == "A" then |
|||
x = band(rshift(op, rs), 31) |
|||
rs = rs - 5 |
|||
elseif p == "S" then |
|||
x = arshift(lshift(op, 27-rs), 27) |
|||
rs = rs - 5 |
|||
elseif p == "I" then |
|||
x = arshift(lshift(op, 16), 16) |
|||
elseif p == "U" then |
|||
x = band(op, 0xffff) |
|||
elseif p == "D" or p == "E" then |
|||
local disp = arshift(lshift(op, 16), 16) |
|||
if p == "E" then disp = band(disp, -4) end |
|||
if last == "r0" then last = "0" end |
|||
operands[#operands] = format("%d(%s)", disp, last) |
|||
elseif p >= "2" and p <= "8" then |
|||
local disp = band(rshift(op, rs), 31) * p |
|||
if last == "r0" then last = "0" end |
|||
operands[#operands] = format("%d(%s)", disp, last) |
|||
elseif p == "H" then |
|||
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4) |
|||
rs = rs - 5 |
|||
elseif p == "M" then |
|||
x = band(rshift(op, rs), 31) + band(op, 0x20) |
|||
elseif p == "C" then |
|||
x = condfmt(band(rshift(op, rs), 31)) |
|||
rs = rs - 5 |
|||
elseif p == "B" then |
|||
local bo = rshift(op, 21) |
|||
local cond = band(rshift(op, 16), 31) |
|||
local cn = "" |
|||
rs = rs - 10 |
|||
if band(bo, 4) == 0 then |
|||
cn = band(bo, 2) == 0 and "dnz" or "dz" |
|||
if band(bo, 0x10) == 0 then |
|||
cn = cn..(band(bo, 8) == 0 and "f" or "t") |
|||
end |
|||
if band(bo, 0x10) == 0 then x = condfmt(cond) end |
|||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") |
|||
elseif band(bo, 0x10) == 0 then |
|||
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)] |
|||
if cond > 3 then x = "cr"..rshift(cond, 2) end |
|||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+") |
|||
end |
|||
name = gsub(name, "_", cn) |
|||
elseif p == "J" then |
|||
x = arshift(lshift(op, 27-rs), 29-rs)*4 |
|||
if band(op, 2) == 0 then x = ctx.addr + pos + x end |
|||
ctx.rel = x |
|||
x = "0x"..tohex(x) |
|||
elseif p == "K" then |
|||
if band(op, 1) ~= 0 then name = name.."l" end |
|||
if band(op, 2) ~= 0 then name = name.."a" end |
|||
elseif p == "X" or p == "Y" then |
|||
x = band(rshift(op, rs+2), 7) |
|||
if x == 0 and p == "Y" then x = nil else x = "cr"..x end |
|||
rs = rs - 5 |
|||
elseif p == "W" then |
|||
x = "cr"..band(op, 7) |
|||
elseif p == "Z" then |
|||
x = band(rshift(op, rs-4), 255) |
|||
rs = rs - 10 |
|||
elseif p == ">" then |
|||
operands[#operands] = rshift(operands[#operands], 1) |
|||
elseif p == "0" then |
|||
if last == "r0" then |
|||
operands[#operands] = nil |
|||
if altname then name = altname end |
|||
end |
|||
elseif p == "L" then |
|||
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w") |
|||
elseif p == "." then |
|||
if band(op, 1) == 1 then name = name.."." end |
|||
elseif p == "N" then |
|||
if op == 0x60000000 then name = "nop"; break end |
|||
elseif p == "~" then |
|||
local n = #operands |
|||
operands[n-1], operands[n] = operands[n], operands[n-1] |
|||
elseif p == "=" then |
|||
local n = #operands |
|||
if last == operands[n-1] then |
|||
operands[n] = nil |
|||
name = altname |
|||
end |
|||
elseif p == "%" then |
|||
local n = #operands |
|||
if last == operands[n-1] and last == operands[n-2] then |
|||
operands[n] = nil |
|||
operands[n-1] = nil |
|||
name = altname |
|||
end |
|||
elseif p == "-" then |
|||
rs = rs - 5 |
|||
else |
|||
assert(false) |
|||
end |
|||
if x then operands[#operands+1] = x; last = x end |
|||
end |
|||
|
|||
return putop(ctx, name, operands) |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Disassemble a block of code. |
|||
local function disass_block(ctx, ofs, len) |
|||
if not ofs then ofs = 0 end |
|||
local stop = len and ofs+len or #ctx.code |
|||
stop = stop - stop % 4 |
|||
ctx.pos = ofs - ofs % 4 |
|||
ctx.rel = nil |
|||
while ctx.pos < stop do disass_ins(ctx) end |
|||
end |
|||
|
|||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). |
|||
local function create(code, addr, out) |
|||
local ctx = {} |
|||
ctx.code = code |
|||
ctx.addr = addr or 0 |
|||
ctx.out = out or io.write |
|||
ctx.symtab = {} |
|||
ctx.disass = disass_block |
|||
ctx.hexdump = 8 |
|||
return ctx |
|||
end |
|||
|
|||
-- Simple API: disassemble code (a string) at address and output via out. |
|||
local function disass(code, addr, out) |
|||
create(code, addr, out):disass() |
|||
end |
|||
|
|||
-- Return register name for RID. |
|||
local function regname(r) |
|||
if r < 32 then return map_gpr[r] end |
|||
return "f"..(r-32) |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
create = create, |
|||
disass = disass, |
|||
regname = regname |
|||
} |
|||
|
@ -0,0 +1,17 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT x64 disassembler wrapper module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This module just exports the 64 bit functions from the combined |
|||
-- x86/x64 disassembler module. All the interesting stuff is there. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local dis_x86 = require((string.match(..., ".*%.") or "").."dis_x86") |
|||
return { |
|||
create = dis_x86.create64, |
|||
disass = dis_x86.disass64, |
|||
regname = dis_x86.regname64 |
|||
} |
|||
|
@ -0,0 +1,953 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT x86/x64 disassembler module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- This is a helper module used by the LuaJIT machine code dumper module. |
|||
-- |
|||
-- Sending small code snippets to an external disassembler and mixing the |
|||
-- output with our own stuff was too fragile. So I had to bite the bullet |
|||
-- and write yet another x86 disassembler. Oh well ... |
|||
-- |
|||
-- The output format is very similar to what ndisasm generates. But it has |
|||
-- been developed independently by looking at the opcode tables from the |
|||
-- Intel and AMD manuals. The supported instruction set is quite extensive |
|||
-- and reflects what a current generation Intel or AMD CPU implements in |
|||
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3, |
|||
-- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor |
|||
-- (VMX/SVM) instructions. |
|||
-- |
|||
-- Notes: |
|||
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported. |
|||
-- * No attempt at optimization has been made -- it's fast enough for my needs. |
|||
------------------------------------------------------------------------------ |
|||
|
|||
local type = type |
|||
local sub, byte, format = string.sub, string.byte, string.format |
|||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub |
|||
local lower, rep = string.lower, string.rep |
|||
local bit = require("bit") |
|||
local tohex = bit.tohex |
|||
|
|||
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on. |
|||
local map_opc1_32 = { |
|||
--0x |
|||
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es", |
|||
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*", |
|||
--1x |
|||
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss", |
|||
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds", |
|||
--2x |
|||
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa", |
|||
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das", |
|||
--3x |
|||
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa", |
|||
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas", |
|||
--4x |
|||
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR", |
|||
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR", |
|||
--5x |
|||
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR", |
|||
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR", |
|||
--6x |
|||
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr", |
|||
"fs:seg","gs:seg","o16:","a16", |
|||
"pushUi","imulVrmi","pushBs","imulVrms", |
|||
"insb","insVS","outsb","outsVS", |
|||
--7x |
|||
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj", |
|||
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj", |
|||
--8x |
|||
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms", |
|||
"testBmr","testVmr","xchgBrm","xchgVrm", |
|||
"movBmr","movVmr","movBrm","movVrm", |
|||
"movVmg","leaVrm","movWgm","popUm", |
|||
--9x |
|||
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR", |
|||
"xchgVaR","xchgVaR","xchgVaR","xchgVaR", |
|||
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait", |
|||
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf", |
|||
--Ax |
|||
"movBao","movVao","movBoa","movVoa", |
|||
"movsb","movsVS","cmpsb","cmpsVS", |
|||
"testBai","testVai","stosb","stosVS", |
|||
"lodsb","lodsVS","scasb","scasVS", |
|||
--Bx |
|||
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi", |
|||
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI", |
|||
--Cx |
|||
"shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi", |
|||
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS", |
|||
--Dx |
|||
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb", |
|||
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7", |
|||
--Ex |
|||
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj", |
|||
"inBau","inVau","outBua","outVua", |
|||
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda", |
|||
--Fx |
|||
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm", |
|||
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm", |
|||
} |
|||
assert(#map_opc1_32 == 255) |
|||
|
|||
-- Map for 1st opcode byte in 64 bit mode (overrides only). |
|||
local map_opc1_64 = setmetatable({ |
|||
[0x06]=false, [0x07]=false, [0x0e]=false, |
|||
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false, |
|||
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false, |
|||
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:", |
|||
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb", |
|||
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb", |
|||
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb", |
|||
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb", |
|||
[0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false, |
|||
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false, |
|||
}, { __index = map_opc1_32 }) |
|||
|
|||
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you. |
|||
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2 |
|||
local map_opc2 = { |
|||
--0x |
|||
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret", |
|||
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu", |
|||
--1x |
|||
"movupsXrm|movssXrvm|movupdXrm|movsdXrvm", |
|||
"movupsXmr|movssXmvr|movupdXmr|movsdXmvr", |
|||
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm", |
|||
"movlpsXmr||movlpdXmr", |
|||
"unpcklpsXrvm||unpcklpdXrvm", |
|||
"unpckhpsXrvm||unpckhpdXrvm", |
|||
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm", |
|||
"movhpsXmr||movhpdXmr", |
|||
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm", |
|||
"hintnopVm","hintnopVm","hintnopVm","hintnopVm", |
|||
--2x |
|||
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil, |
|||
"movapsXrm||movapdXrm", |
|||
"movapsXmr||movapdXmr", |
|||
"cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt", |
|||
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr", |
|||
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm", |
|||
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm", |
|||
"ucomissXrm||ucomisdXrm", |
|||
"comissXrm||comisdXrm", |
|||
--3x |
|||
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec", |
|||
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil, |
|||
--4x |
|||
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm", |
|||
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm", |
|||
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm", |
|||
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm", |
|||
--5x |
|||
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm", |
|||
"rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm", |
|||
"andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm", |
|||
"orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm", |
|||
"addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm", |
|||
"cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm", |
|||
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm", |
|||
"subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm", |
|||
"divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm", |
|||
--6x |
|||
"punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm", |
|||
"pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm", |
|||
"punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm", |
|||
"||punpcklqdqXrvm","||punpckhqdqXrvm", |
|||
"movPrVSm","movqMrm|movdquXrm|movdqaXrm", |
|||
--7x |
|||
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu", |
|||
"pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu", |
|||
"pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|", |
|||
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$", |
|||
nil,nil, |
|||
"||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm", |
|||
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr", |
|||
--8x |
|||
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj", |
|||
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj", |
|||
--9x |
|||
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm", |
|||
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm", |
|||
--Ax |
|||
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil, |
|||
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm", |
|||
--Bx |
|||
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr", |
|||
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt", |
|||
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr", |
|||
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt", |
|||
--Cx |
|||
"xaddBmr","xaddVmr", |
|||
"cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|", |
|||
"pinsrwPrvWmu","pextrwDrPmu", |
|||
"shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp", |
|||
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR", |
|||
--Dx |
|||
"||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm", |
|||
"paddqPrvm","pmullwPrvm", |
|||
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm", |
|||
"psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm", |
|||
"paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm", |
|||
--Ex |
|||
"pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm", |
|||
"pmulhuwPrvm","pmulhwPrvm", |
|||
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr", |
|||
"psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm", |
|||
"paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm", |
|||
--Fx |
|||
"|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm", |
|||
"pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$", |
|||
"psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm", |
|||
"paddbPrvm","paddwPrvm","padddPrvm","ud", |
|||
} |
|||
assert(map_opc2[255] == "ud") |
|||
|
|||
-- Map for three-byte opcodes. Can't wait for their next invention. |
|||
local map_opc3 = { |
|||
["38"] = { -- [66] 0f 38 xx |
|||
--0x |
|||
[0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm", |
|||
"pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm", |
|||
"psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm", |
|||
"||permilpsXrvm","||permilpdXrvm",nil,nil, |
|||
--1x |
|||
"||pblendvbXrma",nil,nil,nil, |
|||
"||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm", |
|||
"||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil, |
|||
"pabsbPrm","pabswPrm","pabsdPrm",nil, |
|||
--2x |
|||
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm", |
|||
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil, |
|||
"||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm", |
|||
"||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr", |
|||
--3x |
|||
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm", |
|||
"||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm", |
|||
"||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm", |
|||
"||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm", |
|||
--4x |
|||
"||pmulddXrvm","||phminposuwXrm",nil,nil, |
|||
nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm", |
|||
--5x |
|||
[0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm", |
|||
[0x5a] = "||broadcasti128XrlXm", |
|||
--7x |
|||
[0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm", |
|||
--8x |
|||
[0x8c] = "||pmaskmovXrvVSm", |
|||
[0x8e] = "||pmaskmovVSmXvr", |
|||
--9x |
|||
[0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm", |
|||
[0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm", |
|||
[0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm", |
|||
[0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm", |
|||
[0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm", |
|||
--Ax |
|||
[0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm", |
|||
[0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm", |
|||
[0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm", |
|||
[0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm", |
|||
[0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm", |
|||
--Bx |
|||
[0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm", |
|||
[0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm", |
|||
[0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm", |
|||
[0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm", |
|||
[0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm", |
|||
--Dx |
|||
[0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm", |
|||
[0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm", |
|||
--Fx |
|||
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt", |
|||
[0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv", |
|||
}, |
|||
|
|||
["3a"] = { -- [66] 0f 3a xx |
|||
--0x |
|||
[0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil, |
|||
"||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil, |
|||
"||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu", |
|||
"||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu", |
|||
--1x |
|||
nil,nil,nil,nil, |
|||
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru", |
|||
"||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil, |
|||
nil,nil,nil,nil, |
|||
--2x |
|||
"||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil, |
|||
--3x |
|||
[0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru", |
|||
--4x |
|||
[0x40] = "||dppsXrvmu", |
|||
[0x41] = "||dppdXrvmu", |
|||
[0x42] = "||mpsadbwXrvmu", |
|||
[0x44] = "||pclmulqdqXrvmu", |
|||
[0x46] = "||perm2i128Xrvmu", |
|||
[0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb", |
|||
[0x4c] = "||pblendvbXrvmb", |
|||
--6x |
|||
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu", |
|||
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu", |
|||
[0xdf] = "||aeskeygenassistXrmu", |
|||
--Fx |
|||
[0xf0] = "||| rorxVrmu", |
|||
}, |
|||
} |
|||
|
|||
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands). |
|||
local map_opcvm = { |
|||
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff", |
|||
[0xc8]="monitor",[0xc9]="mwait", |
|||
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave", |
|||
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga", |
|||
[0xf8]="swapgs",[0xf9]="rdtscp", |
|||
} |
|||
|
|||
-- Map for FP opcodes. And you thought stack machines are simple? |
|||
local map_opcfp = { |
|||
-- D8-DF 00-BF: opcodes with a memory operand. |
|||
-- D8 |
|||
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm", |
|||
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm", |
|||
-- DA |
|||
"fiaddDm","fimulDm","ficomDm","ficompDm", |
|||
"fisubDm","fisubrDm","fidivDm","fidivrDm", |
|||
-- DB |
|||
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp", |
|||
-- DC |
|||
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm", |
|||
-- DD |
|||
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm", |
|||
-- DE |
|||
"fiaddWm","fimulWm","ficomWm","ficompWm", |
|||
"fisubWm","fisubrWm","fidivWm","fidivrWm", |
|||
-- DF |
|||
"fildWm","fisttpWm","fistWm","fistpWm", |
|||
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm", |
|||
-- xx C0-FF: opcodes with a pseudo-register operand. |
|||
-- D8 |
|||
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf", |
|||
-- D9 |
|||
"fldFf","fxchFf",{"fnop"},nil, |
|||
{"fchs","fabs",nil,nil,"ftst","fxam"}, |
|||
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"}, |
|||
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"}, |
|||
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"}, |
|||
-- DA |
|||
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil, |
|||
-- DB |
|||
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf", |
|||
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil, |
|||
-- DC |
|||
"fadd toFf","fmul toFf",nil,nil, |
|||
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf", |
|||
-- DD |
|||
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil, |
|||
-- DE |
|||
"faddpFf","fmulpFf",nil,{nil,"fcompp"}, |
|||
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf", |
|||
-- DF |
|||
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil, |
|||
} |
|||
assert(map_opcfp[126] == "fcomipFf") |
|||
|
|||
-- Map for opcode groups. The subkey is sp from the ModRM byte. |
|||
local map_opcgroup = { |
|||
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" }, |
|||
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" }, |
|||
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" }, |
|||
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" }, |
|||
incb = { "inc", "dec" }, |
|||
incd = { "inc", "dec", "callUmp", "$call farDmp", |
|||
"jmpUmp", "$jmp farDmp", "pushUm" }, |
|||
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" }, |
|||
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt", |
|||
"smsw", nil, "lmsw", "vm*$invlpg" }, |
|||
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" }, |
|||
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil, |
|||
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" }, |
|||
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" }, |
|||
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" }, |
|||
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" }, |
|||
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" }, |
|||
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr", |
|||
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" }, |
|||
prefetch = { "prefetch", "prefetchw" }, |
|||
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" }, |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Maps for register names. |
|||
local map_regs = { |
|||
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", |
|||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, |
|||
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil", |
|||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }, |
|||
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", |
|||
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }, |
|||
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", |
|||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }, |
|||
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", |
|||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" }, |
|||
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", |
|||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext! |
|||
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", |
|||
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" }, |
|||
Y = { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7", |
|||
"ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" }, |
|||
} |
|||
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" } |
|||
|
|||
-- Maps for size names. |
|||
local map_sz2n = { |
|||
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16, Y = 32, |
|||
} |
|||
local map_sz2prefix = { |
|||
B = "byte", W = "word", D = "dword", |
|||
Q = "qword", |
|||
M = "qword", X = "xword", Y = "yword", |
|||
F = "dword", G = "qword", -- No need for sizes/register names for these two. |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Output a nicely formatted line with an opcode and operands. |
|||
local function putop(ctx, text, operands) |
|||
local code, pos, hex = ctx.code, ctx.pos, "" |
|||
local hmax = ctx.hexdump |
|||
if hmax > 0 then |
|||
for i=ctx.start,pos-1 do |
|||
hex = hex..format("%02X", byte(code, i, i)) |
|||
end |
|||
if #hex > hmax then hex = sub(hex, 1, hmax)..". " |
|||
else hex = hex..rep(" ", hmax-#hex+2) end |
|||
end |
|||
if operands then text = text.." "..operands end |
|||
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end |
|||
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end |
|||
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end |
|||
if ctx.rex then |
|||
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "").. |
|||
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "").. |
|||
(ctx.vexl and "l" or "") |
|||
if ctx.vexv and ctx.vexv ~= 0 then t = t.."v"..ctx.vexv end |
|||
if t ~= "" then text = ctx.rex.."."..t.." "..gsub(text, "^ ", "") |
|||
elseif ctx.rex == "vex" then text = gsub("v"..text, "^v ", "") end |
|||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false |
|||
ctx.rex = false; ctx.vexl = false; ctx.vexv = false |
|||
end |
|||
if ctx.seg then |
|||
local text2, n = gsub(text, "%[", "["..ctx.seg..":") |
|||
if n == 0 then text = ctx.seg.." "..text else text = text2 end |
|||
ctx.seg = false |
|||
end |
|||
if ctx.lock then text = "lock "..text; ctx.lock = false end |
|||
local imm = ctx.imm |
|||
if imm then |
|||
local sym = ctx.symtab[imm] |
|||
if sym then text = text.."\t->"..sym end |
|||
end |
|||
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text)) |
|||
ctx.mrm = false |
|||
ctx.vexv = false |
|||
ctx.start = pos |
|||
ctx.imm = nil |
|||
end |
|||
|
|||
-- Clear all prefix flags. |
|||
local function clearprefixes(ctx) |
|||
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false |
|||
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false |
|||
ctx.rex = false; ctx.a32 = false; ctx.vexl = false |
|||
end |
|||
|
|||
-- Fallback for incomplete opcodes at the end. |
|||
local function incomplete(ctx) |
|||
ctx.pos = ctx.stop+1 |
|||
clearprefixes(ctx) |
|||
return putop(ctx, "(incomplete)") |
|||
end |
|||
|
|||
-- Fallback for unknown opcodes. |
|||
local function unknown(ctx) |
|||
clearprefixes(ctx) |
|||
return putop(ctx, "(unknown)") |
|||
end |
|||
|
|||
-- Return an immediate of the specified size. |
|||
local function getimm(ctx, pos, n) |
|||
if pos+n-1 > ctx.stop then return incomplete(ctx) end |
|||
local code = ctx.code |
|||
if n == 1 then |
|||
local b1 = byte(code, pos, pos) |
|||
return b1 |
|||
elseif n == 2 then |
|||
local b1, b2 = byte(code, pos, pos+1) |
|||
return b1+b2*256 |
|||
else |
|||
local b1, b2, b3, b4 = byte(code, pos, pos+3) |
|||
local imm = b1+b2*256+b3*65536+b4*16777216 |
|||
ctx.imm = imm |
|||
return imm |
|||
end |
|||
end |
|||
|
|||
-- Process pattern string and generate the operands. |
|||
local function putpat(ctx, name, pat) |
|||
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp |
|||
local code, pos, stop, vexl = ctx.code, ctx.pos, ctx.stop, ctx.vexl |
|||
|
|||
-- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz |
|||
for p in gmatch(pat, ".") do |
|||
local x = nil |
|||
if p == "V" or p == "U" then |
|||
if ctx.rexw then sz = "Q"; ctx.rexw = false |
|||
elseif ctx.o16 then sz = "W"; ctx.o16 = false |
|||
elseif p == "U" and ctx.x64 then sz = "Q" |
|||
else sz = "D" end |
|||
regs = map_regs[sz] |
|||
elseif p == "T" then |
|||
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end |
|||
regs = map_regs[sz] |
|||
elseif p == "B" then |
|||
sz = "B" |
|||
regs = ctx.rex and map_regs.B64 or map_regs.B |
|||
elseif match(p, "[WDQMXYFG]") then |
|||
sz = p |
|||
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end |
|||
regs = map_regs[sz] |
|||
elseif p == "P" then |
|||
sz = ctx.o16 and "X" or "M"; ctx.o16 = false |
|||
if sz == "X" and vexl then sz = "Y"; ctx.vexl = false end |
|||
regs = map_regs[sz] |
|||
elseif p == "H" then |
|||
name = name..(ctx.rexw and "d" or "s") |
|||
ctx.rexw = false |
|||
elseif p == "S" then |
|||
name = name..lower(sz) |
|||
elseif p == "s" then |
|||
local imm = getimm(ctx, pos, 1); if not imm then return end |
|||
x = imm <= 127 and format("+0x%02x", imm) |
|||
or format("-0x%02x", 256-imm) |
|||
pos = pos+1 |
|||
elseif p == "u" then |
|||
local imm = getimm(ctx, pos, 1); if not imm then return end |
|||
x = format("0x%02x", imm) |
|||
pos = pos+1 |
|||
elseif p == "b" then |
|||
local imm = getimm(ctx, pos, 1); if not imm then return end |
|||
x = regs[imm/16+1] |
|||
pos = pos+1 |
|||
elseif p == "w" then |
|||
local imm = getimm(ctx, pos, 2); if not imm then return end |
|||
x = format("0x%x", imm) |
|||
pos = pos+2 |
|||
elseif p == "o" then -- [offset] |
|||
if ctx.x64 then |
|||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end |
|||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end |
|||
x = format("[0x%08x%08x]", imm2, imm1) |
|||
pos = pos+8 |
|||
else |
|||
local imm = getimm(ctx, pos, 4); if not imm then return end |
|||
x = format("[0x%08x]", imm) |
|||
pos = pos+4 |
|||
end |
|||
elseif p == "i" or p == "I" then |
|||
local n = map_sz2n[sz] |
|||
if n == 8 and ctx.x64 and p == "I" then |
|||
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end |
|||
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end |
|||
x = format("0x%08x%08x", imm2, imm1) |
|||
else |
|||
if n == 8 then n = 4 end |
|||
local imm = getimm(ctx, pos, n); if not imm then return end |
|||
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then |
|||
imm = (0xffffffff+1)-imm |
|||
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm) |
|||
else |
|||
x = format(imm > 65535 and "0x%08x" or "0x%x", imm) |
|||
end |
|||
end |
|||
pos = pos+n |
|||
elseif p == "j" then |
|||
local n = map_sz2n[sz] |
|||
if n == 8 then n = 4 end |
|||
local imm = getimm(ctx, pos, n); if not imm then return end |
|||
if sz == "B" and imm > 127 then imm = imm-256 |
|||
elseif imm > 2147483647 then imm = imm-4294967296 end |
|||
pos = pos+n |
|||
imm = imm + pos + ctx.addr |
|||
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end |
|||
ctx.imm = imm |
|||
if sz == "W" then |
|||
x = format("word 0x%04x", imm%65536) |
|||
elseif ctx.x64 then |
|||
local lo = imm % 0x1000000 |
|||
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo) |
|||
else |
|||
x = "0x"..tohex(imm) |
|||
end |
|||
elseif p == "R" then |
|||
local r = byte(code, pos-1, pos-1)%8 |
|||
if ctx.rexb then r = r + 8; ctx.rexb = false end |
|||
x = regs[r+1] |
|||
elseif p == "a" then x = regs[1] |
|||
elseif p == "c" then x = "cl" |
|||
elseif p == "d" then x = "dx" |
|||
elseif p == "1" then x = "1" |
|||
else |
|||
if not mode then |
|||
mode = ctx.mrm |
|||
if not mode then |
|||
if pos > stop then return incomplete(ctx) end |
|||
mode = byte(code, pos, pos) |
|||
pos = pos+1 |
|||
end |
|||
rm = mode%8; mode = (mode-rm)/8 |
|||
sp = mode%8; mode = (mode-sp)/8 |
|||
sdisp = "" |
|||
if mode < 3 then |
|||
if rm == 4 then |
|||
if pos > stop then return incomplete(ctx) end |
|||
sc = byte(code, pos, pos) |
|||
pos = pos+1 |
|||
rm = sc%8; sc = (sc-rm)/8 |
|||
rx = sc%8; sc = (sc-rx)/8 |
|||
if ctx.rexx then rx = rx + 8; ctx.rexx = false end |
|||
if rx == 4 then rx = nil end |
|||
end |
|||
if mode > 0 or rm == 5 then |
|||
local dsz = mode |
|||
if dsz ~= 1 then dsz = 4 end |
|||
local disp = getimm(ctx, pos, dsz); if not disp then return end |
|||
if mode == 0 then rm = nil end |
|||
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then |
|||
if dsz == 1 and disp > 127 then |
|||
sdisp = format("-0x%x", 256-disp) |
|||
elseif disp >= 0 and disp <= 0x7fffffff then |
|||
sdisp = format("+0x%x", disp) |
|||
else |
|||
sdisp = format("-0x%x", (0xffffffff+1)-disp) |
|||
end |
|||
else |
|||
sdisp = format(ctx.x64 and not ctx.a32 and |
|||
not (disp >= 0 and disp <= 0x7fffffff) |
|||
and "0xffffffff%08x" or "0x%08x", disp) |
|||
end |
|||
pos = pos+dsz |
|||
end |
|||
end |
|||
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end |
|||
if ctx.rexr then sp = sp + 8; ctx.rexr = false end |
|||
end |
|||
if p == "m" then |
|||
if mode == 3 then x = regs[rm+1] |
|||
else |
|||
local aregs = ctx.a32 and map_regs.D or ctx.aregs |
|||
local srm, srx = "", "" |
|||
if rm then srm = aregs[rm+1] |
|||
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end |
|||
ctx.a32 = false |
|||
if rx then |
|||
if rm then srm = srm.."+" end |
|||
srx = aregs[rx+1] |
|||
if sc > 0 then srx = srx.."*"..(2^sc) end |
|||
end |
|||
x = format("[%s%s%s]", srm, srx, sdisp) |
|||
end |
|||
if mode < 3 and |
|||
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck. |
|||
x = map_sz2prefix[sz].." "..x |
|||
end |
|||
elseif p == "r" then x = regs[sp+1] |
|||
elseif p == "g" then x = map_segregs[sp+1] |
|||
elseif p == "p" then -- Suppress prefix. |
|||
elseif p == "f" then x = "st"..rm |
|||
elseif p == "x" then |
|||
if sp == 0 and ctx.lock and not ctx.x64 then |
|||
x = "CR8"; ctx.lock = false |
|||
else |
|||
x = "CR"..sp |
|||
end |
|||
elseif p == "v" then |
|||
if ctx.vexv then |
|||
x = regs[ctx.vexv+1]; ctx.vexv = false |
|||
end |
|||
elseif p == "y" then x = "DR"..sp |
|||
elseif p == "z" then x = "TR"..sp |
|||
elseif p == "l" then vexl = false |
|||
elseif p == "t" then |
|||
else |
|||
error("bad pattern `"..pat.."'") |
|||
end |
|||
end |
|||
if x then operands = operands and operands..", "..x or x end |
|||
end |
|||
ctx.pos = pos |
|||
return putop(ctx, name, operands) |
|||
end |
|||
|
|||
-- Forward declaration. |
|||
local map_act |
|||
|
|||
-- Fetch and cache MRM byte. |
|||
local function getmrm(ctx) |
|||
local mrm = ctx.mrm |
|||
if not mrm then |
|||
local pos = ctx.pos |
|||
if pos > ctx.stop then return nil end |
|||
mrm = byte(ctx.code, pos, pos) |
|||
ctx.pos = pos+1 |
|||
ctx.mrm = mrm |
|||
end |
|||
return mrm |
|||
end |
|||
|
|||
-- Dispatch to handler depending on pattern. |
|||
local function dispatch(ctx, opat, patgrp) |
|||
if not opat then return unknown(ctx) end |
|||
if match(opat, "%|") then -- MMX/SSE variants depending on prefix. |
|||
local p |
|||
if ctx.rep then |
|||
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)" |
|||
ctx.rep = false |
|||
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false |
|||
else p = "^[^%|]*" end |
|||
opat = match(opat, p) |
|||
if not opat then return unknown(ctx) end |
|||
-- ctx.rep = false; ctx.o16 = false |
|||
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi] |
|||
--XXX remove in branches? |
|||
end |
|||
if match(opat, "%$") then -- reg$mem variants. |
|||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end |
|||
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)") |
|||
if opat == "" then return unknown(ctx) end |
|||
end |
|||
if opat == "" then return unknown(ctx) end |
|||
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)") |
|||
if pat == "" and patgrp then pat = patgrp end |
|||
return map_act[sub(pat, 1, 1)](ctx, name, pat) |
|||
end |
|||
|
|||
-- Get a pattern from an opcode map and dispatch to handler. |
|||
local function dispatchmap(ctx, opcmap) |
|||
local pos = ctx.pos |
|||
local opat = opcmap[byte(ctx.code, pos, pos)] |
|||
pos = pos + 1 |
|||
ctx.pos = pos |
|||
return dispatch(ctx, opat) |
|||
end |
|||
|
|||
-- Map for action codes. The key is the first char after the name. |
|||
map_act = { |
|||
-- Simple opcodes without operands. |
|||
[""] = function(ctx, name, pat) |
|||
return putop(ctx, name) |
|||
end, |
|||
|
|||
-- Operand size chars fall right through. |
|||
B = putpat, W = putpat, D = putpat, Q = putpat, |
|||
V = putpat, U = putpat, T = putpat, |
|||
M = putpat, X = putpat, P = putpat, |
|||
F = putpat, G = putpat, Y = putpat, |
|||
H = putpat, |
|||
|
|||
-- Collect prefixes. |
|||
[":"] = function(ctx, name, pat) |
|||
ctx[pat == ":" and name or sub(pat, 2)] = name |
|||
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes. |
|||
end, |
|||
|
|||
-- Chain to special handler specified by name. |
|||
["*"] = function(ctx, name, pat) |
|||
return map_act[name](ctx, name, sub(pat, 2)) |
|||
end, |
|||
|
|||
-- Use named subtable for opcode group. |
|||
["!"] = function(ctx, name, pat) |
|||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end |
|||
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2)) |
|||
end, |
|||
|
|||
-- o16,o32[,o64] variants. |
|||
sz = function(ctx, name, pat) |
|||
if ctx.o16 then ctx.o16 = false |
|||
else |
|||
pat = match(pat, ",(.*)") |
|||
if ctx.rexw then |
|||
local p = match(pat, ",(.*)") |
|||
if p then pat = p; ctx.rexw = false end |
|||
end |
|||
end |
|||
pat = match(pat, "^[^,]*") |
|||
return dispatch(ctx, pat) |
|||
end, |
|||
|
|||
-- Two-byte opcode dispatch. |
|||
opc2 = function(ctx, name, pat) |
|||
return dispatchmap(ctx, map_opc2) |
|||
end, |
|||
|
|||
-- Three-byte opcode dispatch. |
|||
opc3 = function(ctx, name, pat) |
|||
return dispatchmap(ctx, map_opc3[pat]) |
|||
end, |
|||
|
|||
-- VMX/SVM dispatch. |
|||
vm = function(ctx, name, pat) |
|||
return dispatch(ctx, map_opcvm[ctx.mrm]) |
|||
end, |
|||
|
|||
-- Floating point opcode dispatch. |
|||
fp = function(ctx, name, pat) |
|||
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end |
|||
local rm = mrm%8 |
|||
local idx = pat*8 + ((mrm-rm)/8)%8 |
|||
if mrm >= 192 then idx = idx + 64 end |
|||
local opat = map_opcfp[idx] |
|||
if type(opat) == "table" then opat = opat[rm+1] end |
|||
return dispatch(ctx, opat) |
|||
end, |
|||
|
|||
-- REX prefix. |
|||
rex = function(ctx, name, pat) |
|||
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed. |
|||
for p in gmatch(pat, ".") do ctx["rex"..p] = true end |
|||
ctx.rex = "rex" |
|||
end, |
|||
|
|||
-- VEX prefix. |
|||
vex = function(ctx, name, pat) |
|||
if ctx.rex then return unknown(ctx) end -- Only 1 REX or VEX prefix allowed. |
|||
ctx.rex = "vex" |
|||
local pos = ctx.pos |
|||
if ctx.mrm then |
|||
ctx.mrm = nil |
|||
pos = pos-1 |
|||
end |
|||
local b = byte(ctx.code, pos, pos) |
|||
if not b then return incomplete(ctx) end |
|||
pos = pos+1 |
|||
if b < 128 then ctx.rexr = true end |
|||
local m = 1 |
|||
if pat == "3" then |
|||
m = b%32; b = (b-m)/32 |
|||
local nb = b%2; b = (b-nb)/2 |
|||
if nb == 0 then ctx.rexb = true end |
|||
local nx = b%2 |
|||
if nx == 0 then ctx.rexx = true end |
|||
b = byte(ctx.code, pos, pos) |
|||
if not b then return incomplete(ctx) end |
|||
pos = pos+1 |
|||
if b >= 128 then ctx.rexw = true end |
|||
end |
|||
ctx.pos = pos |
|||
local map |
|||
if m == 1 then map = map_opc2 |
|||
elseif m == 2 then map = map_opc3["38"] |
|||
elseif m == 3 then map = map_opc3["3a"] |
|||
else return unknown(ctx) end |
|||
local p = b%4; b = (b-p)/4 |
|||
if p == 1 then ctx.o16 = "o16" |
|||
elseif p == 2 then ctx.rep = "rep" |
|||
elseif p == 3 then ctx.rep = "repne" end |
|||
local l = b%2; b = (b-l)/2 |
|||
if l ~= 0 then ctx.vexl = true end |
|||
ctx.vexv = (-1-b)%16 |
|||
return dispatchmap(ctx, map) |
|||
end, |
|||
|
|||
-- Special case for nop with REX prefix. |
|||
nop = function(ctx, name, pat) |
|||
return dispatch(ctx, ctx.rex and pat or "nop") |
|||
end, |
|||
|
|||
-- Special case for 0F 77. |
|||
emms = function(ctx, name, pat) |
|||
if ctx.rex ~= "vex" then |
|||
return putop(ctx, "emms") |
|||
elseif ctx.vexl then |
|||
ctx.vexl = false |
|||
return putop(ctx, "zeroall") |
|||
else |
|||
return putop(ctx, "zeroupper") |
|||
end |
|||
end, |
|||
} |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Disassemble a block of code. |
|||
local function disass_block(ctx, ofs, len) |
|||
if not ofs then ofs = 0 end |
|||
local stop = len and ofs+len or #ctx.code |
|||
ofs = ofs + 1 |
|||
ctx.start = ofs |
|||
ctx.pos = ofs |
|||
ctx.stop = stop |
|||
ctx.imm = nil |
|||
ctx.mrm = false |
|||
clearprefixes(ctx) |
|||
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end |
|||
if ctx.pos ~= ctx.start then incomplete(ctx) end |
|||
end |
|||
|
|||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len). |
|||
local function create(code, addr, out) |
|||
local ctx = {} |
|||
ctx.code = code |
|||
ctx.addr = (addr or 0) - 1 |
|||
ctx.out = out or io.write |
|||
ctx.symtab = {} |
|||
ctx.disass = disass_block |
|||
ctx.hexdump = 16 |
|||
ctx.x64 = false |
|||
ctx.map1 = map_opc1_32 |
|||
ctx.aregs = map_regs.D |
|||
return ctx |
|||
end |
|||
|
|||
local function create64(code, addr, out) |
|||
local ctx = create(code, addr, out) |
|||
ctx.x64 = true |
|||
ctx.map1 = map_opc1_64 |
|||
ctx.aregs = map_regs.Q |
|||
return ctx |
|||
end |
|||
|
|||
-- Simple API: disassemble code (a string) at address and output via out. |
|||
local function disass(code, addr, out) |
|||
create(code, addr, out):disass() |
|||
end |
|||
|
|||
local function disass64(code, addr, out) |
|||
create64(code, addr, out):disass() |
|||
end |
|||
|
|||
-- Return register name for RID. |
|||
local function regname(r) |
|||
if r < 8 then return map_regs.D[r+1] end |
|||
return map_regs.X[r-7] |
|||
end |
|||
|
|||
local function regname64(r) |
|||
if r < 16 then return map_regs.Q[r+1] end |
|||
return map_regs.X[r-15] |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
create = create, |
|||
create64 = create64, |
|||
disass = disass, |
|||
disass64 = disass64, |
|||
regname = regname, |
|||
regname64 = regname64 |
|||
} |
|||
|
@ -0,0 +1,726 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT compiler dump module. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- |
|||
-- This module can be used to debug the JIT compiler itself. It dumps the |
|||
-- code representations and structures used in various compiler stages. |
|||
-- |
|||
-- Example usage: |
|||
-- |
|||
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)" |
|||
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R |
|||
-- luajit -jdump=is myapp.lua | less -R |
|||
-- luajit -jdump=-b myapp.lua |
|||
-- luajit -jdump=+aH,myapp.html myapp.lua |
|||
-- luajit -jdump=ixT,myapp.dump myapp.lua |
|||
-- |
|||
-- The first argument specifies the dump mode. The second argument gives |
|||
-- the output file name. Default output is to stdout, unless the environment |
|||
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the |
|||
-- module is started. |
|||
-- |
|||
-- Different features can be turned on or off with the dump mode. If the |
|||
-- mode starts with a '+', the following features are added to the default |
|||
-- set of features; a '-' removes them. Otherwise the features are replaced. |
|||
-- |
|||
-- The following dump features are available (* marks the default): |
|||
-- |
|||
-- * t Print a line for each started, ended or aborted trace (see also -jv). |
|||
-- * b Dump the traced bytecode. |
|||
-- * i Dump the IR (intermediate representation). |
|||
-- r Augment the IR with register/stack slots. |
|||
-- s Dump the snapshot map. |
|||
-- * m Dump the generated machine code. |
|||
-- x Print each taken trace exit. |
|||
-- X Print each taken trace exit and the contents of all registers. |
|||
-- a Print the IR of aborted traces, too. |
|||
-- |
|||
-- The output format can be set with the following characters: |
|||
-- |
|||
-- T Plain text output. |
|||
-- A ANSI-colored text output |
|||
-- H Colorized HTML + CSS output. |
|||
-- |
|||
-- The default output format is plain text. It's set to ANSI-colored text |
|||
-- if the COLORTERM variable is set. Note: this is independent of any output |
|||
-- redirection, which is actually considered a feature. |
|||
-- |
|||
-- You probably want to use less -R to enjoy viewing ANSI-colored text from |
|||
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R" |
|||
-- |
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Cache some library functions and objects. |
|||
local jit = require("jit") |
|||
assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") |
|||
local jutil = require("jit.util") |
|||
local vmdef = require("jit.vmdef") |
|||
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc |
|||
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek |
|||
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap |
|||
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr |
|||
local bit = require("bit") |
|||
local band, shr, tohex = bit.band, bit.rshift, bit.tohex |
|||
local sub, gsub, format = string.sub, string.gsub, string.format |
|||
local byte, rep = string.byte, string.rep |
|||
local type, tostring = type, tostring |
|||
local stdout, stderr = io.stdout, io.stderr |
|||
|
|||
-- Load other modules on-demand. |
|||
local bcline, disass |
|||
|
|||
-- Active flag, output file handle and dump mode. |
|||
local active, out, dumpmode |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local symtabmt = { __index = false } |
|||
local symtab = {} |
|||
local nexitsym = 0 |
|||
|
|||
-- Fill nested symbol table with per-trace exit stub addresses. |
|||
local function fillsymtab_tr(tr, nexit) |
|||
local t = {} |
|||
symtabmt.__index = t |
|||
if jit.arch:sub(1, 4) == "mips" then |
|||
t[traceexitstub(tr, 0)] = "exit" |
|||
return |
|||
end |
|||
for i=0,nexit-1 do |
|||
local addr = traceexitstub(tr, i) |
|||
if addr < 0 then addr = addr + 2^32 end |
|||
t[addr] = tostring(i) |
|||
end |
|||
local addr = traceexitstub(tr, nexit) |
|||
if addr then t[addr] = "stack_check" end |
|||
end |
|||
|
|||
-- Fill symbol table with trace exit stub addresses. |
|||
local function fillsymtab(tr, nexit) |
|||
local t = symtab |
|||
if nexitsym == 0 then |
|||
local maskaddr = jit.arch == "arm" and -2 |
|||
local ircall = vmdef.ircall |
|||
for i=0,#ircall do |
|||
local addr = ircalladdr(i) |
|||
if addr ~= 0 then |
|||
if maskaddr then addr = band(addr, maskaddr) end |
|||
if addr < 0 then addr = addr + 2^32 end |
|||
t[addr] = ircall[i] |
|||
end |
|||
end |
|||
end |
|||
if nexitsym == 1000000 then -- Per-trace exit stubs. |
|||
fillsymtab_tr(tr, nexit) |
|||
elseif nexit > nexitsym then -- Shared exit stubs. |
|||
for i=nexitsym,nexit-1 do |
|||
local addr = traceexitstub(i) |
|||
if addr == nil then -- Fall back to per-trace exit stubs. |
|||
fillsymtab_tr(tr, nexit) |
|||
setmetatable(symtab, symtabmt) |
|||
nexit = 1000000 |
|||
break |
|||
end |
|||
if addr < 0 then addr = addr + 2^32 end |
|||
t[addr] = tostring(i) |
|||
end |
|||
nexitsym = nexit |
|||
end |
|||
return t |
|||
end |
|||
|
|||
local function dumpwrite(s) |
|||
out:write(s) |
|||
end |
|||
|
|||
-- Disassemble machine code. |
|||
local function dump_mcode(tr) |
|||
local info = traceinfo(tr) |
|||
if not info then return end |
|||
local mcode, addr, loop = tracemc(tr) |
|||
if not mcode then return end |
|||
if not disass then disass = require("jit.dis_"..jit.arch) end |
|||
if addr < 0 then addr = addr + 2^32 end |
|||
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n") |
|||
local ctx = disass.create(mcode, addr, dumpwrite) |
|||
ctx.hexdump = 0 |
|||
ctx.symtab = fillsymtab(tr, info.nexit) |
|||
if loop ~= 0 then |
|||
symtab[addr+loop] = "LOOP" |
|||
ctx:disass(0, loop) |
|||
out:write("->LOOP:\n") |
|||
ctx:disass(loop, #mcode-loop) |
|||
symtab[addr+loop] = nil |
|||
else |
|||
ctx:disass(0, #mcode) |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local irtype_text = { |
|||
[0] = "nil", |
|||
"fal", |
|||
"tru", |
|||
"lud", |
|||
"str", |
|||
"p32", |
|||
"thr", |
|||
"pro", |
|||
"fun", |
|||
"p64", |
|||
"cdt", |
|||
"tab", |
|||
"udt", |
|||
"flt", |
|||
"num", |
|||
"i8 ", |
|||
"u8 ", |
|||
"i16", |
|||
"u16", |
|||
"int", |
|||
"u32", |
|||
"i64", |
|||
"u64", |
|||
"sfp", |
|||
} |
|||
|
|||
local colortype_ansi = { |
|||
[0] = "%s", |
|||
"%s", |
|||
"%s", |
|||
"\027[36m%s\027[m", |
|||
"\027[32m%s\027[m", |
|||
"%s", |
|||
"\027[1m%s\027[m", |
|||
"%s", |
|||
"\027[1m%s\027[m", |
|||
"%s", |
|||
"\027[33m%s\027[m", |
|||
"\027[31m%s\027[m", |
|||
"\027[36m%s\027[m", |
|||
"\027[34m%s\027[m", |
|||
"\027[34m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
"\027[35m%s\027[m", |
|||
} |
|||
|
|||
local function colorize_text(s) |
|||
return s |
|||
end |
|||
|
|||
local function colorize_ansi(s, t, extra) |
|||
local out = format(colortype_ansi[t], s) |
|||
if extra then out = "\027[3m"..out end |
|||
return out |
|||
end |
|||
|
|||
local irtype_ansi = setmetatable({}, |
|||
{ __index = function(tab, t) |
|||
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end }) |
|||
|
|||
local html_escape = { ["<"] = "<", [">"] = ">", ["&"] = "&", } |
|||
|
|||
local function colorize_html(s, t, extra) |
|||
s = gsub(s, "[<>&]", html_escape) |
|||
return format('<span class="irt_%s%s">%s</span>', |
|||
irtype_text[t], extra and " irt_extra" or "", s) |
|||
end |
|||
|
|||
local irtype_html = setmetatable({}, |
|||
{ __index = function(tab, t) |
|||
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end }) |
|||
|
|||
local header_html = [[ |
|||
<style type="text/css"> |
|||
background { background: #ffffff; color: #000000; } |
|||
pre.ljdump { |
|||
font-size: 10pt; |
|||
background: #f0f4ff; |
|||
color: #000000; |
|||
border: 1px solid #bfcfff; |
|||
padding: 0.5em; |
|||
margin-left: 2em; |
|||
margin-right: 2em; |
|||
} |
|||
span.irt_str { color: #00a000; } |
|||
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; } |
|||
span.irt_tab { color: #c00000; } |
|||
span.irt_udt, span.irt_lud { color: #00c0c0; } |
|||
span.irt_num { color: #4040c0; } |
|||
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; } |
|||
span.irt_extra { font-style: italic; } |
|||
</style> |
|||
]] |
|||
|
|||
local colorize, irtype |
|||
|
|||
-- Lookup tables to convert some literals into names. |
|||
local litname = { |
|||
["SLOAD "] = setmetatable({}, { __index = function(t, mode) |
|||
local s = "" |
|||
if band(mode, 1) ~= 0 then s = s.."P" end |
|||
if band(mode, 2) ~= 0 then s = s.."F" end |
|||
if band(mode, 4) ~= 0 then s = s.."T" end |
|||
if band(mode, 8) ~= 0 then s = s.."C" end |
|||
if band(mode, 16) ~= 0 then s = s.."R" end |
|||
if band(mode, 32) ~= 0 then s = s.."I" end |
|||
if band(mode, 64) ~= 0 then s = s.."K" end |
|||
t[mode] = s |
|||
return s |
|||
end}), |
|||
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", }, |
|||
["CONV "] = setmetatable({}, { __index = function(t, mode) |
|||
local s = irtype[band(mode, 31)] |
|||
s = irtype[band(shr(mode, 5), 31)].."."..s |
|||
if band(mode, 0x800) ~= 0 then s = s.." sext" end |
|||
local c = shr(mode, 12) |
|||
if c == 1 then s = s.." none" |
|||
elseif c == 2 then s = s.." index" |
|||
elseif c == 3 then s = s.." check" end |
|||
t[mode] = s |
|||
return s |
|||
end}), |
|||
["FLOAD "] = vmdef.irfield, |
|||
["FREF "] = vmdef.irfield, |
|||
["FPMATH"] = vmdef.irfpm, |
|||
["TMPREF"] = { [0] = "", "IN", "OUT", "INOUT", "", "", "OUT2", "INOUT2" }, |
|||
["BUFHDR"] = { [0] = "RESET", "APPEND", "WRITE" }, |
|||
["TOSTR "] = { [0] = "INT", "NUM", "CHAR" }, |
|||
} |
|||
|
|||
local function ctlsub(c) |
|||
if c == "\n" then return "\\n" |
|||
elseif c == "\r" then return "\\r" |
|||
elseif c == "\t" then return "\\t" |
|||
else return format("\\%03d", byte(c)) |
|||
end |
|||
end |
|||
|
|||
local function fmtfunc(func, pc) |
|||
local fi = funcinfo(func, pc) |
|||
if fi.loc then |
|||
return fi.loc |
|||
elseif fi.ffid then |
|||
return vmdef.ffnames[fi.ffid] |
|||
elseif fi.addr then |
|||
return format("C:%x", fi.addr) |
|||
else |
|||
return "(?)" |
|||
end |
|||
end |
|||
|
|||
local function formatk(tr, idx, sn) |
|||
local k, t, slot = tracek(tr, idx) |
|||
local tn = type(k) |
|||
local s |
|||
if tn == "number" then |
|||
if t < 12 then |
|||
s = k == 0 and "NULL" or format("[0x%08x]", k) |
|||
elseif band(sn or 0, 0x30000) ~= 0 then |
|||
s = band(sn, 0x20000) ~= 0 and "contpc" or "ftsz" |
|||
elseif k == 2^52+2^51 then |
|||
s = "bias" |
|||
else |
|||
s = format(0 < k and k < 0x1p-1026 and "%+a" or "%+.14g", k) |
|||
end |
|||
elseif tn == "string" then |
|||
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub)) |
|||
elseif tn == "function" then |
|||
s = fmtfunc(k) |
|||
elseif tn == "table" then |
|||
s = format("{%p}", k) |
|||
elseif tn == "userdata" then |
|||
if t == 12 then |
|||
s = format("userdata:%p", k) |
|||
else |
|||
s = format("[%p]", k) |
|||
if s == "[NULL]" then s = "NULL" end |
|||
end |
|||
elseif t == 21 then -- int64_t |
|||
s = sub(tostring(k), 1, -3) |
|||
if sub(s, 1, 1) ~= "-" then s = "+"..s end |
|||
elseif sn == 0x1057fff then -- SNAP(1, SNAP_FRAME | SNAP_NORESTORE, REF_NIL) |
|||
return "----" -- Special case for LJ_FR2 slot 1. |
|||
else |
|||
s = tostring(k) -- For primitives. |
|||
end |
|||
s = colorize(format("%-4s", s), t, band(sn or 0, 0x100000) ~= 0) |
|||
if slot then |
|||
s = format("%s @%d", s, slot) |
|||
end |
|||
return s |
|||
end |
|||
|
|||
local function printsnap(tr, snap) |
|||
local n = 2 |
|||
for s=0,snap[1]-1 do |
|||
local sn = snap[n] |
|||
if shr(sn, 24) == s then |
|||
n = n + 1 |
|||
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS |
|||
if ref < 0 then |
|||
out:write(formatk(tr, ref, sn)) |
|||
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM |
|||
out:write(colorize(format("%04d/%04d", ref, ref+1), 14)) |
|||
else |
|||
local m, ot, op1, op2 = traceir(tr, ref) |
|||
out:write(colorize(format("%04d", ref), band(ot, 31), band(sn, 0x100000) ~= 0)) |
|||
end |
|||
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME |
|||
else |
|||
out:write("---- ") |
|||
end |
|||
end |
|||
out:write("]\n") |
|||
end |
|||
|
|||
-- Dump snapshots (not interleaved with IR). |
|||
local function dump_snap(tr) |
|||
out:write("---- TRACE ", tr, " snapshots\n") |
|||
for i=0,1000000000 do |
|||
local snap = tracesnap(tr, i) |
|||
if not snap then break end |
|||
out:write(format("#%-3d %04d [ ", i, snap[0])) |
|||
printsnap(tr, snap) |
|||
end |
|||
end |
|||
|
|||
-- Return a register name or stack slot for a rid/sp location. |
|||
local function ridsp_name(ridsp, ins) |
|||
if not disass then disass = require("jit.dis_"..jit.arch) end |
|||
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8) |
|||
if rid == 253 or rid == 254 then |
|||
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot) |
|||
end |
|||
if ridsp > 255 then return format("[%x]", slot*4) end |
|||
if rid < 128 then return disass.regname(rid) end |
|||
return "" |
|||
end |
|||
|
|||
-- Dump CALL* function ref and return optional ctype. |
|||
local function dumpcallfunc(tr, ins) |
|||
local ctype |
|||
if ins > 0 then |
|||
local m, ot, op1, op2 = traceir(tr, ins) |
|||
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype). |
|||
ins = op1 |
|||
ctype = formatk(tr, op2) |
|||
end |
|||
end |
|||
if ins < 0 then |
|||
out:write(format("[0x%x](", tonumber((tracek(tr, ins))))) |
|||
else |
|||
out:write(format("%04d (", ins)) |
|||
end |
|||
return ctype |
|||
end |
|||
|
|||
-- Recursively gather CALL* args and dump them. |
|||
local function dumpcallargs(tr, ins) |
|||
if ins < 0 then |
|||
out:write(formatk(tr, ins)) |
|||
else |
|||
local m, ot, op1, op2 = traceir(tr, ins) |
|||
local oidx = 6*shr(ot, 8) |
|||
local op = sub(vmdef.irnames, oidx+1, oidx+6) |
|||
if op == "CARG " then |
|||
dumpcallargs(tr, op1) |
|||
if op2 < 0 then |
|||
out:write(" ", formatk(tr, op2)) |
|||
else |
|||
out:write(" ", format("%04d", op2)) |
|||
end |
|||
else |
|||
out:write(format("%04d", ins)) |
|||
end |
|||
end |
|||
end |
|||
|
|||
-- Dump IR and interleaved snapshots. |
|||
local function dump_ir(tr, dumpsnap, dumpreg) |
|||
local info = traceinfo(tr) |
|||
if not info then return end |
|||
local nins = info.nins |
|||
out:write("---- TRACE ", tr, " IR\n") |
|||
local irnames = vmdef.irnames |
|||
local snapref = 65536 |
|||
local snap, snapno |
|||
if dumpsnap then |
|||
snap = tracesnap(tr, 0) |
|||
snapref = snap[0] |
|||
snapno = 0 |
|||
end |
|||
for ins=1,nins do |
|||
if ins >= snapref then |
|||
if dumpreg then |
|||
out:write(format(".... SNAP #%-3d [ ", snapno)) |
|||
else |
|||
out:write(format(".... SNAP #%-3d [ ", snapno)) |
|||
end |
|||
printsnap(tr, snap) |
|||
snapno = snapno + 1 |
|||
snap = tracesnap(tr, snapno) |
|||
snapref = snap and snap[0] or 65536 |
|||
end |
|||
local m, ot, op1, op2, ridsp = traceir(tr, ins) |
|||
local oidx, t = 6*shr(ot, 8), band(ot, 31) |
|||
local op = sub(irnames, oidx+1, oidx+6) |
|||
if op == "LOOP " then |
|||
if dumpreg then |
|||
out:write(format("%04d ------------ LOOP ------------\n", ins)) |
|||
else |
|||
out:write(format("%04d ------ LOOP ------------\n", ins)) |
|||
end |
|||
elseif op ~= "NOP " and op ~= "CARG " and |
|||
(dumpreg or op ~= "RENAME") then |
|||
local rid = band(ridsp, 255) |
|||
if dumpreg then |
|||
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins))) |
|||
else |
|||
out:write(format("%04d ", ins)) |
|||
end |
|||
out:write(format("%s%s %s %s ", |
|||
(rid == 254 or rid == 253) and "}" or |
|||
(band(ot, 128) == 0 and " " or ">"), |
|||
band(ot, 64) == 0 and " " or "+", |
|||
irtype[t], op)) |
|||
local m1, m2 = band(m, 3), band(m, 3*4) |
|||
if sub(op, 1, 4) == "CALL" then |
|||
local ctype |
|||
if m2 == 1*4 then -- op2 == IRMlit |
|||
out:write(format("%-10s (", vmdef.ircall[op2])) |
|||
else |
|||
ctype = dumpcallfunc(tr, op2) |
|||
end |
|||
if op1 ~= -1 then dumpcallargs(tr, op1) end |
|||
out:write(")") |
|||
if ctype then out:write(" ctype ", ctype) end |
|||
elseif op == "CNEW " and op2 == -1 then |
|||
out:write(formatk(tr, op1)) |
|||
elseif m1 ~= 3 then -- op1 != IRMnone |
|||
if op1 < 0 then |
|||
out:write(formatk(tr, op1)) |
|||
else |
|||
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1)) |
|||
end |
|||
if m2 ~= 3*4 then -- op2 != IRMnone |
|||
if m2 == 1*4 then -- op2 == IRMlit |
|||
local litn = litname[op] |
|||
if litn and litn[op2] then |
|||
out:write(" ", litn[op2]) |
|||
elseif op == "UREFO " or op == "UREFC " then |
|||
out:write(format(" #%-3d", shr(op2, 8))) |
|||
else |
|||
out:write(format(" #%-3d", op2)) |
|||
end |
|||
elseif op2 < 0 then |
|||
out:write(" ", formatk(tr, op2)) |
|||
else |
|||
out:write(format(" %04d", op2)) |
|||
end |
|||
end |
|||
end |
|||
out:write("\n") |
|||
end |
|||
end |
|||
if snap then |
|||
if dumpreg then |
|||
out:write(format(".... SNAP #%-3d [ ", snapno)) |
|||
else |
|||
out:write(format(".... SNAP #%-3d [ ", snapno)) |
|||
end |
|||
printsnap(tr, snap) |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local recprefix = "" |
|||
local recdepth = 0 |
|||
|
|||
-- Format trace error message. |
|||
local function fmterr(err, info) |
|||
if type(err) == "number" then |
|||
if type(info) == "function" then info = fmtfunc(info) end |
|||
err = format(vmdef.traceerr[err], info) |
|||
end |
|||
return err |
|||
end |
|||
|
|||
-- Dump trace states. |
|||
local function dump_trace(what, tr, func, pc, otr, oex) |
|||
if what == "stop" or (what == "abort" and dumpmode.a) then |
|||
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop") |
|||
elseif dumpmode.s then dump_snap(tr) end |
|||
if dumpmode.m then dump_mcode(tr) end |
|||
end |
|||
if what == "start" then |
|||
if dumpmode.H then out:write('<pre class="ljdump">\n') end |
|||
out:write("---- TRACE ", tr, " ", what) |
|||
if otr then out:write(" ", otr, "/", oex == -1 and "stitch" or oex) end |
|||
out:write(" ", fmtfunc(func, pc), "\n") |
|||
elseif what == "stop" or what == "abort" then |
|||
out:write("---- TRACE ", tr, " ", what) |
|||
if what == "abort" then |
|||
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n") |
|||
else |
|||
local info = traceinfo(tr) |
|||
local link, ltype = info.link, info.linktype |
|||
if link == tr or link == 0 then |
|||
out:write(" -> ", ltype, "\n") |
|||
elseif ltype == "root" then |
|||
out:write(" -> ", link, "\n") |
|||
else |
|||
out:write(" -> ", link, " ", ltype, "\n") |
|||
end |
|||
end |
|||
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end |
|||
else |
|||
if what == "flush" then symtab, nexitsym = {}, 0 end |
|||
out:write("---- TRACE ", what, "\n\n") |
|||
end |
|||
out:flush() |
|||
end |
|||
|
|||
-- Dump recorded bytecode. |
|||
local function dump_record(tr, func, pc, depth) |
|||
if depth ~= recdepth then |
|||
recdepth = depth |
|||
recprefix = rep(" .", depth) |
|||
end |
|||
local line |
|||
if pc >= 0 then |
|||
line = bcline(func, pc, recprefix) |
|||
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end |
|||
else |
|||
line = "0000 "..recprefix.." FUNCC \n" |
|||
end |
|||
if pc <= 0 then |
|||
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n") |
|||
else |
|||
out:write(line) |
|||
end |
|||
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC |
|||
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond. |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local gpr64 = jit.arch:match("64") |
|||
local fprmips32 = jit.arch == "mips" or jit.arch == "mipsel" |
|||
|
|||
-- Dump taken trace exits. |
|||
local function dump_texit(tr, ex, ngpr, nfpr, ...) |
|||
out:write("---- TRACE ", tr, " exit ", ex, "\n") |
|||
if dumpmode.X then |
|||
local regs = {...} |
|||
if gpr64 then |
|||
for i=1,ngpr do |
|||
out:write(format(" %016x", regs[i])) |
|||
if i % 4 == 0 then out:write("\n") end |
|||
end |
|||
else |
|||
for i=1,ngpr do |
|||
out:write(" ", tohex(regs[i])) |
|||
if i % 8 == 0 then out:write("\n") end |
|||
end |
|||
end |
|||
if fprmips32 then |
|||
for i=1,nfpr,2 do |
|||
out:write(format(" %+17.14g", regs[ngpr+i])) |
|||
if i % 8 == 7 then out:write("\n") end |
|||
end |
|||
else |
|||
for i=1,nfpr do |
|||
out:write(format(" %+17.14g", regs[ngpr+i])) |
|||
if i % 4 == 0 then out:write("\n") end |
|||
end |
|||
end |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Detach dump handlers. |
|||
local function dumpoff() |
|||
if active then |
|||
active = false |
|||
jit.attach(dump_texit) |
|||
jit.attach(dump_record) |
|||
jit.attach(dump_trace) |
|||
if out and out ~= stdout and out ~= stderr then out:close() end |
|||
out = nil |
|||
end |
|||
end |
|||
|
|||
-- Open the output file and attach dump handlers. |
|||
local function dumpon(opt, outfile) |
|||
if active then dumpoff() end |
|||
|
|||
local term = os.getenv("TERM") |
|||
local colormode = (term and term:match("color") or os.getenv("COLORTERM")) and "A" or "T" |
|||
if opt then |
|||
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end) |
|||
end |
|||
|
|||
local m = { t=true, b=true, i=true, m=true, } |
|||
if opt and opt ~= "" then |
|||
local o = sub(opt, 1, 1) |
|||
if o ~= "+" and o ~= "-" then m = {} end |
|||
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end |
|||
end |
|||
dumpmode = m |
|||
|
|||
if m.t or m.b or m.i or m.s or m.m then |
|||
jit.attach(dump_trace, "trace") |
|||
end |
|||
if m.b then |
|||
jit.attach(dump_record, "record") |
|||
if not bcline then bcline = require("jit.bc").line end |
|||
end |
|||
if m.x or m.X then |
|||
jit.attach(dump_texit, "texit") |
|||
end |
|||
|
|||
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end |
|||
if outfile then |
|||
out = outfile == "-" and stdout or assert(io.open(outfile, "w")) |
|||
else |
|||
out = stdout |
|||
end |
|||
|
|||
m[colormode] = true |
|||
if colormode == "A" then |
|||
colorize = colorize_ansi |
|||
irtype = irtype_ansi |
|||
elseif colormode == "H" then |
|||
colorize = colorize_html |
|||
irtype = irtype_html |
|||
out:write(header_html) |
|||
else |
|||
colorize = colorize_text |
|||
irtype = irtype_text |
|||
end |
|||
|
|||
active = true |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
on = dumpon, |
|||
off = dumpoff, |
|||
start = dumpon -- For -j command line option. |
|||
} |
|||
|
@ -0,0 +1,312 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT profiler. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- |
|||
-- This module is a simple command line interface to the built-in |
|||
-- low-overhead profiler of LuaJIT. |
|||
-- |
|||
-- The lower-level API of the profiler is accessible via the "jit.profile" |
|||
-- module or the luaJIT_profile_* C API. |
|||
-- |
|||
-- Example usage: |
|||
-- |
|||
-- luajit -jp myapp.lua |
|||
-- luajit -jp=s myapp.lua |
|||
-- luajit -jp=-s myapp.lua |
|||
-- luajit -jp=vl myapp.lua |
|||
-- luajit -jp=G,profile.txt myapp.lua |
|||
-- |
|||
-- The following dump features are available: |
|||
-- |
|||
-- f Stack dump: function name, otherwise module:line. Default mode. |
|||
-- F Stack dump: ditto, but always prepend module. |
|||
-- l Stack dump: module:line. |
|||
-- <number> stack dump depth (callee < caller). Default: 1. |
|||
-- -<number> Inverse stack dump depth (caller > callee). |
|||
-- s Split stack dump after first stack level. Implies abs(depth) >= 2. |
|||
-- p Show full path for module names. |
|||
-- v Show VM states. Can be combined with stack dumps, e.g. vf or fv. |
|||
-- z Show zones. Can be combined with stack dumps, e.g. zf or fz. |
|||
-- r Show raw sample counts. Default: show percentages. |
|||
-- a Annotate excerpts from source code files. |
|||
-- A Annotate complete source code files. |
|||
-- G Produce raw output suitable for graphical tools (e.g. flame graphs). |
|||
-- m<number> Minimum sample percentage to be shown. Default: 3. |
|||
-- i<number> Sampling interval in milliseconds. Default: 10. |
|||
-- |
|||
---------------------------------------------------------------------------- |
|||
|
|||
-- Cache some library functions and objects. |
|||
local jit = require("jit") |
|||
assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") |
|||
local profile = require("jit.profile") |
|||
local vmdef = require("jit.vmdef") |
|||
local math = math |
|||
local pairs, ipairs, tonumber, floor = pairs, ipairs, tonumber, math.floor |
|||
local sort, format = table.sort, string.format |
|||
local stdout = io.stdout |
|||
local zone -- Load jit.zone module on demand. |
|||
|
|||
-- Output file handle. |
|||
local out |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local prof_ud |
|||
local prof_states, prof_split, prof_min, prof_raw, prof_fmt, prof_depth |
|||
local prof_ann, prof_count1, prof_count2, prof_samples |
|||
|
|||
local map_vmmode = { |
|||
N = "Compiled", |
|||
I = "Interpreted", |
|||
C = "C code", |
|||
G = "Garbage Collector", |
|||
J = "JIT Compiler", |
|||
} |
|||
|
|||
-- Profiler callback. |
|||
local function prof_cb(th, samples, vmmode) |
|||
prof_samples = prof_samples + samples |
|||
local key_stack, key_stack2, key_state |
|||
-- Collect keys for sample. |
|||
if prof_states then |
|||
if prof_states == "v" then |
|||
key_state = map_vmmode[vmmode] or vmmode |
|||
else |
|||
key_state = zone:get() or "(none)" |
|||
end |
|||
end |
|||
if prof_fmt then |
|||
key_stack = profile.dumpstack(th, prof_fmt, prof_depth) |
|||
key_stack = key_stack:gsub("%[builtin#(%d+)%]", function(x) |
|||
return vmdef.ffnames[tonumber(x)] |
|||
end) |
|||
if prof_split == 2 then |
|||
local k1, k2 = key_stack:match("(.-) [<>] (.*)") |
|||
if k2 then key_stack, key_stack2 = k1, k2 end |
|||
elseif prof_split == 3 then |
|||
key_stack2 = profile.dumpstack(th, "l", 1) |
|||
end |
|||
end |
|||
-- Order keys. |
|||
local k1, k2 |
|||
if prof_split == 1 then |
|||
if key_state then |
|||
k1 = key_state |
|||
if key_stack then k2 = key_stack end |
|||
end |
|||
elseif key_stack then |
|||
k1 = key_stack |
|||
if key_stack2 then k2 = key_stack2 elseif key_state then k2 = key_state end |
|||
end |
|||
-- Coalesce samples in one or two levels. |
|||
if k1 then |
|||
local t1 = prof_count1 |
|||
t1[k1] = (t1[k1] or 0) + samples |
|||
if k2 then |
|||
local t2 = prof_count2 |
|||
local t3 = t2[k1] |
|||
if not t3 then t3 = {}; t2[k1] = t3 end |
|||
t3[k2] = (t3[k2] or 0) + samples |
|||
end |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Show top N list. |
|||
local function prof_top(count1, count2, samples, indent) |
|||
local t, n = {}, 0 |
|||
for k in pairs(count1) do |
|||
n = n + 1 |
|||
t[n] = k |
|||
end |
|||
sort(t, function(a, b) return count1[a] > count1[b] end) |
|||
for i=1,n do |
|||
local k = t[i] |
|||
local v = count1[k] |
|||
local pct = floor(v*100/samples + 0.5) |
|||
if pct < prof_min then break end |
|||
if not prof_raw then |
|||
out:write(format("%s%2d%% %s\n", indent, pct, k)) |
|||
elseif prof_raw == "r" then |
|||
out:write(format("%s%5d %s\n", indent, v, k)) |
|||
else |
|||
out:write(format("%s %d\n", k, v)) |
|||
end |
|||
if count2 then |
|||
local r = count2[k] |
|||
if r then |
|||
prof_top(r, nil, v, (prof_split == 3 or prof_split == 1) and " -- " or |
|||
(prof_depth < 0 and " -> " or " <- ")) |
|||
end |
|||
end |
|||
end |
|||
end |
|||
|
|||
-- Annotate source code |
|||
local function prof_annotate(count1, samples) |
|||
local files = {} |
|||
local ms = 0 |
|||
for k, v in pairs(count1) do |
|||
local pct = floor(v*100/samples + 0.5) |
|||
ms = math.max(ms, v) |
|||
if pct >= prof_min then |
|||
local file, line = k:match("^(.*):(%d+)$") |
|||
if not file then file = k; line = 0 end |
|||
local fl = files[file] |
|||
if not fl then fl = {}; files[file] = fl; files[#files+1] = file end |
|||
line = tonumber(line) |
|||
fl[line] = prof_raw and v or pct |
|||
end |
|||
end |
|||
sort(files) |
|||
local fmtv, fmtn = " %3d%% | %s\n", " | %s\n" |
|||
if prof_raw then |
|||
local n = math.max(5, math.ceil(math.log10(ms))) |
|||
fmtv = "%"..n.."d | %s\n" |
|||
fmtn = (" "):rep(n).." | %s\n" |
|||
end |
|||
local ann = prof_ann |
|||
for _, file in ipairs(files) do |
|||
local f0 = file:byte() |
|||
if f0 == 40 or f0 == 91 then |
|||
out:write(format("\n====== %s ======\n[Cannot annotate non-file]\n", file)) |
|||
break |
|||
end |
|||
local fp, err = io.open(file) |
|||
if not fp then |
|||
out:write(format("====== ERROR: %s: %s\n", file, err)) |
|||
break |
|||
end |
|||
out:write(format("\n====== %s ======\n", file)) |
|||
local fl = files[file] |
|||
local n, show = 1, false |
|||
if ann ~= 0 then |
|||
for i=1,ann do |
|||
if fl[i] then show = true; out:write("@@ 1 @@\n"); break end |
|||
end |
|||
end |
|||
for line in fp:lines() do |
|||
if line:byte() == 27 then |
|||
out:write("[Cannot annotate bytecode file]\n") |
|||
break |
|||
end |
|||
local v = fl[n] |
|||
if ann ~= 0 then |
|||
local v2 = fl[n+ann] |
|||
if show then |
|||
if v2 then show = n+ann elseif v then show = n |
|||
elseif show+ann < n then show = false end |
|||
elseif v2 then |
|||
show = n+ann |
|||
out:write(format("@@ %d @@\n", n)) |
|||
end |
|||
if not show then goto next end |
|||
end |
|||
if v then |
|||
out:write(format(fmtv, v, line)) |
|||
else |
|||
out:write(format(fmtn, line)) |
|||
end |
|||
::next:: |
|||
n = n + 1 |
|||
end |
|||
fp:close() |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Finish profiling and dump result. |
|||
local function prof_finish() |
|||
if prof_ud then |
|||
profile.stop() |
|||
local samples = prof_samples |
|||
if samples == 0 then |
|||
if prof_raw ~= true then out:write("[No samples collected]\n") end |
|||
return |
|||
end |
|||
if prof_ann then |
|||
prof_annotate(prof_count1, samples) |
|||
else |
|||
prof_top(prof_count1, prof_count2, samples, "") |
|||
end |
|||
prof_count1 = nil |
|||
prof_count2 = nil |
|||
prof_ud = nil |
|||
if out ~= stdout then out:close() end |
|||
end |
|||
end |
|||
|
|||
-- Start profiling. |
|||
local function prof_start(mode) |
|||
local interval = "" |
|||
mode = mode:gsub("i%d*", function(s) interval = s; return "" end) |
|||
prof_min = 3 |
|||
mode = mode:gsub("m(%d+)", function(s) prof_min = tonumber(s); return "" end) |
|||
prof_depth = 1 |
|||
mode = mode:gsub("%-?%d+", function(s) prof_depth = tonumber(s); return "" end) |
|||
local m = {} |
|||
for c in mode:gmatch(".") do m[c] = c end |
|||
prof_states = m.z or m.v |
|||
if prof_states == "z" then zone = require("jit.zone") end |
|||
local scope = m.l or m.f or m.F or (prof_states and "" or "f") |
|||
local flags = (m.p or "") |
|||
prof_raw = m.r |
|||
if m.s then |
|||
prof_split = 2 |
|||
if prof_depth == -1 or m["-"] then prof_depth = -2 |
|||
elseif prof_depth == 1 then prof_depth = 2 end |
|||
elseif mode:find("[fF].*l") then |
|||
scope = "l" |
|||
prof_split = 3 |
|||
else |
|||
prof_split = (scope == "" or mode:find("[zv].*[lfF]")) and 1 or 0 |
|||
end |
|||
prof_ann = m.A and 0 or (m.a and 3) |
|||
if prof_ann then |
|||
scope = "l" |
|||
prof_fmt = "pl" |
|||
prof_split = 0 |
|||
prof_depth = 1 |
|||
elseif m.G and scope ~= "" then |
|||
prof_fmt = flags..scope.."Z;" |
|||
prof_depth = -100 |
|||
prof_raw = true |
|||
prof_min = 0 |
|||
elseif scope == "" then |
|||
prof_fmt = false |
|||
else |
|||
local sc = prof_split == 3 and m.f or m.F or scope |
|||
prof_fmt = flags..sc..(prof_depth >= 0 and "Z < " or "Z > ") |
|||
end |
|||
prof_count1 = {} |
|||
prof_count2 = {} |
|||
prof_samples = 0 |
|||
profile.start(scope:lower()..interval, prof_cb) |
|||
prof_ud = newproxy(true) |
|||
getmetatable(prof_ud).__gc = prof_finish |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local function start(mode, outfile) |
|||
if not outfile then outfile = os.getenv("LUAJIT_PROFILEFILE") end |
|||
if outfile then |
|||
out = outfile == "-" and stdout or assert(io.open(outfile, "w")) |
|||
else |
|||
out = stdout |
|||
end |
|||
prof_start(mode or "f") |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
start = start, -- For -j command line option. |
|||
stop = prof_finish |
|||
} |
|||
|
@ -0,0 +1,170 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- Verbose mode of the LuaJIT compiler. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- |
|||
-- This module shows verbose information about the progress of the |
|||
-- JIT compiler. It prints one line for each generated trace. This module |
|||
-- is useful to see which code has been compiled or where the compiler |
|||
-- punts and falls back to the interpreter. |
|||
-- |
|||
-- Example usage: |
|||
-- |
|||
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end" |
|||
-- luajit -jv=myapp.out myapp.lua |
|||
-- |
|||
-- Default output is to stderr. To redirect the output to a file, pass a |
|||
-- filename as an argument (use '-' for stdout) or set the environment |
|||
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the |
|||
-- module is started. |
|||
-- |
|||
-- The output from the first example should look like this: |
|||
-- |
|||
-- [TRACE 1 (command line):1 loop] |
|||
-- [TRACE 2 (1/3) (command line):1 -> 1] |
|||
-- |
|||
-- The first number in each line is the internal trace number. Next are |
|||
-- the file name ('(command line)') and the line number (':1') where the |
|||
-- trace has started. Side traces also show the parent trace number and |
|||
-- the exit number where they are attached to in parentheses ('(1/3)'). |
|||
-- An arrow at the end shows where the trace links to ('-> 1'), unless |
|||
-- it loops to itself. |
|||
-- |
|||
-- In this case the inner loop gets hot and is traced first, generating |
|||
-- a root trace. Then the last exit from the 1st trace gets hot, too, |
|||
-- and triggers generation of the 2nd trace. The side trace follows the |
|||
-- path along the outer loop and *around* the inner loop, back to its |
|||
-- start, and then links to the 1st trace. Yes, this may seem unusual, |
|||
-- if you know how traditional compilers work. Trace compilers are full |
|||
-- of surprises like this -- have fun! :-) |
|||
-- |
|||
-- Aborted traces are shown like this: |
|||
-- |
|||
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50] |
|||
-- |
|||
-- Don't worry -- trace aborts are quite common, even in programs which |
|||
-- can be fully compiled. The compiler may retry several times until it |
|||
-- finds a suitable trace. |
|||
-- |
|||
-- Of course this doesn't work with features that are not-yet-implemented |
|||
-- (NYI error messages). The VM simply falls back to the interpreter. This |
|||
-- may not matter at all if the particular trace is not very high up in |
|||
-- the CPU usage profile. Oh, and the interpreter is quite fast, too. |
|||
-- |
|||
-- Also check out the -jdump module, which prints all the gory details. |
|||
-- |
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Cache some library functions and objects. |
|||
local jit = require("jit") |
|||
assert(jit.version_num == 20100, "LuaJIT core/library version mismatch") |
|||
local jutil = require("jit.util") |
|||
local vmdef = require("jit.vmdef") |
|||
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo |
|||
local type, format = type, string.format |
|||
local stdout, stderr = io.stdout, io.stderr |
|||
|
|||
-- Active flag and output file handle. |
|||
local active, out |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
local startloc, startex |
|||
|
|||
local function fmtfunc(func, pc) |
|||
local fi = funcinfo(func, pc) |
|||
if fi.loc then |
|||
return fi.loc |
|||
elseif fi.ffid then |
|||
return vmdef.ffnames[fi.ffid] |
|||
elseif fi.addr then |
|||
return format("C:%x", fi.addr) |
|||
else |
|||
return "(?)" |
|||
end |
|||
end |
|||
|
|||
-- Format trace error message. |
|||
local function fmterr(err, info) |
|||
if type(err) == "number" then |
|||
if type(info) == "function" then info = fmtfunc(info) end |
|||
err = format(vmdef.traceerr[err], info) |
|||
end |
|||
return err |
|||
end |
|||
|
|||
-- Dump trace states. |
|||
local function dump_trace(what, tr, func, pc, otr, oex) |
|||
if what == "start" then |
|||
startloc = fmtfunc(func, pc) |
|||
startex = otr and "("..otr.."/"..(oex == -1 and "stitch" or oex)..") " or "" |
|||
else |
|||
if what == "abort" then |
|||
local loc = fmtfunc(func, pc) |
|||
if loc ~= startloc then |
|||
out:write(format("[TRACE --- %s%s -- %s at %s]\n", |
|||
startex, startloc, fmterr(otr, oex), loc)) |
|||
else |
|||
out:write(format("[TRACE --- %s%s -- %s]\n", |
|||
startex, startloc, fmterr(otr, oex))) |
|||
end |
|||
elseif what == "stop" then |
|||
local info = traceinfo(tr) |
|||
local link, ltype = info.link, info.linktype |
|||
if ltype == "interpreter" then |
|||
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n", |
|||
tr, startex, startloc)) |
|||
elseif ltype == "stitch" then |
|||
out:write(format("[TRACE %3s %s%s %s %s]\n", |
|||
tr, startex, startloc, ltype, fmtfunc(func, pc))) |
|||
elseif link == tr or link == 0 then |
|||
out:write(format("[TRACE %3s %s%s %s]\n", |
|||
tr, startex, startloc, ltype)) |
|||
elseif ltype == "root" then |
|||
out:write(format("[TRACE %3s %s%s -> %d]\n", |
|||
tr, startex, startloc, link)) |
|||
else |
|||
out:write(format("[TRACE %3s %s%s -> %d %s]\n", |
|||
tr, startex, startloc, link, ltype)) |
|||
end |
|||
else |
|||
out:write(format("[TRACE %s]\n", what)) |
|||
end |
|||
out:flush() |
|||
end |
|||
end |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
|||
-- Detach dump handlers. |
|||
local function dumpoff() |
|||
if active then |
|||
active = false |
|||
jit.attach(dump_trace) |
|||
if out and out ~= stdout and out ~= stderr then out:close() end |
|||
out = nil |
|||
end |
|||
end |
|||
|
|||
-- Open the output file and attach dump handlers. |
|||
local function dumpon(outfile) |
|||
if active then dumpoff() end |
|||
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end |
|||
if outfile then |
|||
out = outfile == "-" and stdout or assert(io.open(outfile, "w")) |
|||
else |
|||
out = stderr |
|||
end |
|||
jit.attach(dump_trace, "trace") |
|||
active = true |
|||
end |
|||
|
|||
-- Public module functions. |
|||
return { |
|||
on = dumpon, |
|||
off = dumpoff, |
|||
start = dumpon -- For -j command line option. |
|||
} |
|||
|
@ -0,0 +1,45 @@ |
|||
---------------------------------------------------------------------------- |
|||
-- LuaJIT profiler zones. |
|||
-- |
|||
-- Copyright (C) 2005-2022 Mike Pall. All rights reserved. |
|||
-- Released under the MIT license. See Copyright Notice in luajit.h |
|||
---------------------------------------------------------------------------- |
|||
-- |
|||
-- This module implements a simple hierarchical zone model. |
|||
-- |
|||
-- Example usage: |
|||
-- |
|||
-- local zone = require("jit.zone") |
|||
-- zone("AI") |
|||
-- ... |
|||
-- zone("A*") |
|||
-- ... |
|||
-- print(zone:get()) --> "A*" |
|||
-- ... |
|||
-- zone() |
|||
-- ... |
|||
-- print(zone:get()) --> "AI" |
|||
-- ... |
|||
-- zone() |
|||
-- |
|||
---------------------------------------------------------------------------- |
|||
|
|||
local remove = table.remove |
|||
|
|||
return setmetatable({ |
|||
flush = function(t) |
|||
for i=#t,1,-1 do t[i] = nil end |
|||
end, |
|||
get = function(t) |
|||
return t[#t] |
|||
end |
|||
}, { |
|||
__call = function(t, zone) |
|||
if zone then |
|||
t[#t+1] = zone |
|||
else |
|||
return (assert(remove(t), "empty zone stack")) |
|||
end |
|||
end |
|||
}) |
|||
|
@ -0,0 +1,161 @@ |
|||
/*
|
|||
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ |
|||
** Auxiliary functions for building Lua libraries |
|||
** See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
|
|||
#ifndef lauxlib_h |
|||
#define lauxlib_h |
|||
|
|||
|
|||
#include <stddef.h> |
|||
#include <stdio.h> |
|||
|
|||
#include "lua.h" |
|||
|
|||
|
|||
/* extra error code for `luaL_load' */ |
|||
#define LUA_ERRFILE (LUA_ERRERR+1) |
|||
|
|||
typedef struct luaL_Reg { |
|||
const char *name; |
|||
lua_CFunction func; |
|||
} luaL_Reg; |
|||
|
|||
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, |
|||
const luaL_Reg *l, int nup); |
|||
LUALIB_API void (luaL_register) (lua_State *L, const char *libname, |
|||
const luaL_Reg *l); |
|||
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); |
|||
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); |
|||
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); |
|||
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); |
|||
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, |
|||
size_t *l); |
|||
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, |
|||
const char *def, size_t *l); |
|||
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); |
|||
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); |
|||
|
|||
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); |
|||
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, |
|||
lua_Integer def); |
|||
|
|||
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); |
|||
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); |
|||
LUALIB_API void (luaL_checkany) (lua_State *L, int narg); |
|||
|
|||
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); |
|||
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); |
|||
|
|||
LUALIB_API void (luaL_where) (lua_State *L, int lvl); |
|||
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); |
|||
|
|||
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, |
|||
const char *const lst[]); |
|||
|
|||
/* pre-defined references */ |
|||
#define LUA_NOREF (-2) |
|||
#define LUA_REFNIL (-1) |
|||
|
|||
LUALIB_API int (luaL_ref) (lua_State *L, int t); |
|||
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); |
|||
|
|||
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); |
|||
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, |
|||
const char *name); |
|||
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); |
|||
|
|||
LUALIB_API lua_State *(luaL_newstate) (void); |
|||
|
|||
|
|||
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, |
|||
const char *r); |
|||
|
|||
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, |
|||
const char *fname, int szhint); |
|||
|
|||
/* From Lua 5.2. */ |
|||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname); |
|||
LUALIB_API int luaL_execresult(lua_State *L, int stat); |
|||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, |
|||
const char *mode); |
|||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, |
|||
const char *name, const char *mode); |
|||
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, |
|||
int level); |
|||
LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); |
|||
LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, |
|||
int sizehint); |
|||
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); |
|||
LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); |
|||
|
|||
|
|||
/*
|
|||
** =============================================================== |
|||
** some useful macros |
|||
** =============================================================== |
|||
*/ |
|||
|
|||
#define luaL_argcheck(L, cond,numarg,extramsg) \ |
|||
((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) |
|||
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) |
|||
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) |
|||
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) |
|||
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) |
|||
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) |
|||
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) |
|||
|
|||
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) |
|||
|
|||
#define luaL_dofile(L, fn) \ |
|||
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) |
|||
|
|||
#define luaL_dostring(L, s) \ |
|||
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) |
|||
|
|||
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) |
|||
|
|||
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) |
|||
|
|||
/* From Lua 5.2. */ |
|||
#define luaL_newlibtable(L, l) \ |
|||
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) |
|||
#define luaL_newlib(L, l) (luaL_newlibtable(L, l), luaL_setfuncs(L, l, 0)) |
|||
|
|||
/*
|
|||
** {====================================================== |
|||
** Generic Buffer manipulation |
|||
** ======================================================= |
|||
*/ |
|||
|
|||
|
|||
|
|||
typedef struct luaL_Buffer { |
|||
char *p; /* current position in buffer */ |
|||
int lvl; /* number of strings in the stack (level) */ |
|||
lua_State *L; |
|||
char buffer[LUAL_BUFFERSIZE]; |
|||
} luaL_Buffer; |
|||
|
|||
#define luaL_addchar(B,c) \ |
|||
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ |
|||
(*(B)->p++ = (char)(c))) |
|||
|
|||
/* compatibility only */ |
|||
#define luaL_putchar(B,c) luaL_addchar(B,c) |
|||
|
|||
#define luaL_addsize(B,n) ((B)->p += (n)) |
|||
|
|||
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); |
|||
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); |
|||
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); |
|||
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); |
|||
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); |
|||
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); |
|||
|
|||
|
|||
/* }====================================================== */ |
|||
|
|||
#endif |
@ -0,0 +1,370 @@ |
|||
/*
|
|||
** Auxiliary library for the Lua/C API. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major parts taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#include <errno.h> |
|||
#include <stdarg.h> |
|||
#include <stdio.h> |
|||
|
|||
#define lib_aux_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_err.h" |
|||
#include "lj_state.h" |
|||
#include "lj_trace.h" |
|||
#include "lj_lib.h" |
|||
|
|||
#if LJ_TARGET_POSIX |
|||
#include <sys/wait.h> |
|||
#endif |
|||
|
|||
/* -- I/O error handling -------------------------------------------------- */ |
|||
|
|||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname) |
|||
{ |
|||
if (stat) { |
|||
setboolV(L->top++, 1); |
|||
return 1; |
|||
} else { |
|||
int en = errno; /* Lua API calls may change this value. */ |
|||
setnilV(L->top++); |
|||
if (fname) |
|||
lua_pushfstring(L, "%s: %s", fname, strerror(en)); |
|||
else |
|||
lua_pushfstring(L, "%s", strerror(en)); |
|||
setintV(L->top++, en); |
|||
lj_trace_abort(G(L)); |
|||
return 3; |
|||
} |
|||
} |
|||
|
|||
LUALIB_API int luaL_execresult(lua_State *L, int stat) |
|||
{ |
|||
if (stat != -1) { |
|||
#if LJ_TARGET_POSIX |
|||
if (WIFSIGNALED(stat)) { |
|||
stat = WTERMSIG(stat); |
|||
setnilV(L->top++); |
|||
lua_pushliteral(L, "signal"); |
|||
} else { |
|||
if (WIFEXITED(stat)) |
|||
stat = WEXITSTATUS(stat); |
|||
if (stat == 0) |
|||
setboolV(L->top++, 1); |
|||
else |
|||
setnilV(L->top++); |
|||
lua_pushliteral(L, "exit"); |
|||
} |
|||
#else |
|||
if (stat == 0) |
|||
setboolV(L->top++, 1); |
|||
else |
|||
setnilV(L->top++); |
|||
lua_pushliteral(L, "exit"); |
|||
#endif |
|||
setintV(L->top++, stat); |
|||
return 3; |
|||
} |
|||
return luaL_fileresult(L, 0, NULL); |
|||
} |
|||
|
|||
/* -- Module registration ------------------------------------------------- */ |
|||
|
|||
LUALIB_API const char *luaL_findtable(lua_State *L, int idx, |
|||
const char *fname, int szhint) |
|||
{ |
|||
const char *e; |
|||
lua_pushvalue(L, idx); |
|||
do { |
|||
e = strchr(fname, '.'); |
|||
if (e == NULL) e = fname + strlen(fname); |
|||
lua_pushlstring(L, fname, (size_t)(e - fname)); |
|||
lua_rawget(L, -2); |
|||
if (lua_isnil(L, -1)) { /* no such field? */ |
|||
lua_pop(L, 1); /* remove this nil */ |
|||
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ |
|||
lua_pushlstring(L, fname, (size_t)(e - fname)); |
|||
lua_pushvalue(L, -2); |
|||
lua_settable(L, -4); /* set new table into field */ |
|||
} else if (!lua_istable(L, -1)) { /* field has a non-table value? */ |
|||
lua_pop(L, 2); /* remove table and value */ |
|||
return fname; /* return problematic part of the name */ |
|||
} |
|||
lua_remove(L, -2); /* remove previous table */ |
|||
fname = e + 1; |
|||
} while (*e == '.'); |
|||
return NULL; |
|||
} |
|||
|
|||
static int libsize(const luaL_Reg *l) |
|||
{ |
|||
int size = 0; |
|||
for (; l && l->name; l++) size++; |
|||
return size; |
|||
} |
|||
|
|||
LUALIB_API void luaL_pushmodule(lua_State *L, const char *modname, int sizehint) |
|||
{ |
|||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16); |
|||
lua_getfield(L, -1, modname); |
|||
if (!lua_istable(L, -1)) { |
|||
lua_pop(L, 1); |
|||
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, sizehint) != NULL) |
|||
lj_err_callerv(L, LJ_ERR_BADMODN, modname); |
|||
lua_pushvalue(L, -1); |
|||
lua_setfield(L, -3, modname); /* _LOADED[modname] = new table. */ |
|||
} |
|||
lua_remove(L, -2); /* Remove _LOADED table. */ |
|||
} |
|||
|
|||
LUALIB_API void luaL_openlib(lua_State *L, const char *libname, |
|||
const luaL_Reg *l, int nup) |
|||
{ |
|||
lj_lib_checkfpu(L); |
|||
if (libname) { |
|||
luaL_pushmodule(L, libname, libsize(l)); |
|||
lua_insert(L, -(nup + 1)); /* Move module table below upvalues. */ |
|||
} |
|||
if (l) |
|||
luaL_setfuncs(L, l, nup); |
|||
else |
|||
lua_pop(L, nup); /* Remove upvalues. */ |
|||
} |
|||
|
|||
LUALIB_API void luaL_register(lua_State *L, const char *libname, |
|||
const luaL_Reg *l) |
|||
{ |
|||
luaL_openlib(L, libname, l, 0); |
|||
} |
|||
|
|||
LUALIB_API void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup) |
|||
{ |
|||
luaL_checkstack(L, nup, "too many upvalues"); |
|||
for (; l->name; l++) { |
|||
int i; |
|||
for (i = 0; i < nup; i++) /* Copy upvalues to the top. */ |
|||
lua_pushvalue(L, -nup); |
|||
lua_pushcclosure(L, l->func, nup); |
|||
lua_setfield(L, -(nup + 2), l->name); |
|||
} |
|||
lua_pop(L, nup); /* Remove upvalues. */ |
|||
} |
|||
|
|||
LUALIB_API const char *luaL_gsub(lua_State *L, const char *s, |
|||
const char *p, const char *r) |
|||
{ |
|||
const char *wild; |
|||
size_t l = strlen(p); |
|||
luaL_Buffer b; |
|||
luaL_buffinit(L, &b); |
|||
while ((wild = strstr(s, p)) != NULL) { |
|||
luaL_addlstring(&b, s, (size_t)(wild - s)); /* push prefix */ |
|||
luaL_addstring(&b, r); /* push replacement in place of pattern */ |
|||
s = wild + l; /* continue after `p' */ |
|||
} |
|||
luaL_addstring(&b, s); /* push last suffix */ |
|||
luaL_pushresult(&b); |
|||
return lua_tostring(L, -1); |
|||
} |
|||
|
|||
/* -- Buffer handling ----------------------------------------------------- */ |
|||
|
|||
#define bufflen(B) ((size_t)((B)->p - (B)->buffer)) |
|||
#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) |
|||
|
|||
static int emptybuffer(luaL_Buffer *B) |
|||
{ |
|||
size_t l = bufflen(B); |
|||
if (l == 0) |
|||
return 0; /* put nothing on stack */ |
|||
lua_pushlstring(B->L, B->buffer, l); |
|||
B->p = B->buffer; |
|||
B->lvl++; |
|||
return 1; |
|||
} |
|||
|
|||
static void adjuststack(luaL_Buffer *B) |
|||
{ |
|||
if (B->lvl > 1) { |
|||
lua_State *L = B->L; |
|||
int toget = 1; /* number of levels to concat */ |
|||
size_t toplen = lua_strlen(L, -1); |
|||
do { |
|||
size_t l = lua_strlen(L, -(toget+1)); |
|||
if (!(B->lvl - toget + 1 >= LUA_MINSTACK/2 || toplen > l)) |
|||
break; |
|||
toplen += l; |
|||
toget++; |
|||
} while (toget < B->lvl); |
|||
lua_concat(L, toget); |
|||
B->lvl = B->lvl - toget + 1; |
|||
} |
|||
} |
|||
|
|||
LUALIB_API char *luaL_prepbuffer(luaL_Buffer *B) |
|||
{ |
|||
if (emptybuffer(B)) |
|||
adjuststack(B); |
|||
return B->buffer; |
|||
} |
|||
|
|||
LUALIB_API void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l) |
|||
{ |
|||
if (l <= bufffree(B)) { |
|||
memcpy(B->p, s, l); |
|||
B->p += l; |
|||
} else { |
|||
emptybuffer(B); |
|||
lua_pushlstring(B->L, s, l); |
|||
B->lvl++; |
|||
adjuststack(B); |
|||
} |
|||
} |
|||
|
|||
LUALIB_API void luaL_addstring(luaL_Buffer *B, const char *s) |
|||
{ |
|||
luaL_addlstring(B, s, strlen(s)); |
|||
} |
|||
|
|||
LUALIB_API void luaL_pushresult(luaL_Buffer *B) |
|||
{ |
|||
emptybuffer(B); |
|||
lua_concat(B->L, B->lvl); |
|||
B->lvl = 1; |
|||
} |
|||
|
|||
LUALIB_API void luaL_addvalue(luaL_Buffer *B) |
|||
{ |
|||
lua_State *L = B->L; |
|||
size_t vl; |
|||
const char *s = lua_tolstring(L, -1, &vl); |
|||
if (vl <= bufffree(B)) { /* fit into buffer? */ |
|||
memcpy(B->p, s, vl); /* put it there */ |
|||
B->p += vl; |
|||
lua_pop(L, 1); /* remove from stack */ |
|||
} else { |
|||
if (emptybuffer(B)) |
|||
lua_insert(L, -2); /* put buffer before new value */ |
|||
B->lvl++; /* add new value into B stack */ |
|||
adjuststack(B); |
|||
} |
|||
} |
|||
|
|||
LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B) |
|||
{ |
|||
B->L = L; |
|||
B->p = B->buffer; |
|||
B->lvl = 0; |
|||
} |
|||
|
|||
/* -- Reference management ------------------------------------------------ */ |
|||
|
|||
#define FREELIST_REF 0 |
|||
|
|||
/* Convert a stack index to an absolute index. */ |
|||
#define abs_index(L, i) \ |
|||
((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1) |
|||
|
|||
LUALIB_API int luaL_ref(lua_State *L, int t) |
|||
{ |
|||
int ref; |
|||
t = abs_index(L, t); |
|||
if (lua_isnil(L, -1)) { |
|||
lua_pop(L, 1); /* remove from stack */ |
|||
return LUA_REFNIL; /* `nil' has a unique fixed reference */ |
|||
} |
|||
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ |
|||
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ |
|||
lua_pop(L, 1); /* remove it from stack */ |
|||
if (ref != 0) { /* any free element? */ |
|||
lua_rawgeti(L, t, ref); /* remove it from list */ |
|||
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ |
|||
} else { /* no free elements */ |
|||
ref = (int)lua_objlen(L, t); |
|||
ref++; /* create new reference */ |
|||
} |
|||
lua_rawseti(L, t, ref); |
|||
return ref; |
|||
} |
|||
|
|||
LUALIB_API void luaL_unref(lua_State *L, int t, int ref) |
|||
{ |
|||
if (ref >= 0) { |
|||
t = abs_index(L, t); |
|||
lua_rawgeti(L, t, FREELIST_REF); |
|||
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ |
|||
lua_pushinteger(L, ref); |
|||
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ |
|||
} |
|||
} |
|||
|
|||
/* -- Default allocator and panic function -------------------------------- */ |
|||
|
|||
static int panic(lua_State *L) |
|||
{ |
|||
const char *s = lua_tostring(L, -1); |
|||
fputs("PANIC: unprotected error in call to Lua API (", stderr); |
|||
fputs(s ? s : "?", stderr); |
|||
fputc(')', stderr); fputc('\n', stderr); |
|||
fflush(stderr); |
|||
return 0; |
|||
} |
|||
|
|||
#ifdef LUAJIT_USE_SYSMALLOC |
|||
|
|||
#if LJ_64 && !LJ_GC64 && !defined(LUAJIT_USE_VALGRIND) |
|||
#error "Must use builtin allocator for 64 bit target" |
|||
#endif |
|||
|
|||
static void *mem_alloc(void *ud, void *ptr, size_t osize, size_t nsize) |
|||
{ |
|||
(void)ud; |
|||
(void)osize; |
|||
if (nsize == 0) { |
|||
free(ptr); |
|||
return NULL; |
|||
} else { |
|||
return realloc(ptr, nsize); |
|||
} |
|||
} |
|||
|
|||
LUALIB_API lua_State *luaL_newstate(void) |
|||
{ |
|||
lua_State *L = lua_newstate(mem_alloc, NULL); |
|||
if (L) G(L)->panic = panic; |
|||
return L; |
|||
} |
|||
|
|||
#else |
|||
|
|||
LUALIB_API lua_State *luaL_newstate(void) |
|||
{ |
|||
lua_State *L; |
|||
#if LJ_64 && !LJ_GC64 |
|||
L = lj_state_newstate(LJ_ALLOCF_INTERNAL, NULL); |
|||
#else |
|||
L = lua_newstate(LJ_ALLOCF_INTERNAL, NULL); |
|||
#endif |
|||
if (L) G(L)->panic = panic; |
|||
return L; |
|||
} |
|||
|
|||
#if LJ_64 && !LJ_GC64 |
|||
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud) |
|||
{ |
|||
UNUSED(f); UNUSED(ud); |
|||
fputs("Must use luaL_newstate() for 64 bit target\n", stderr); |
|||
return NULL; |
|||
} |
|||
#endif |
|||
|
|||
#endif |
|||
|
@ -0,0 +1,696 @@ |
|||
/*
|
|||
** Base and coroutine library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#include <stdio.h> |
|||
|
|||
#define lib_base_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_debug.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_str.h" |
|||
#include "lj_tab.h" |
|||
#include "lj_meta.h" |
|||
#include "lj_state.h" |
|||
#include "lj_frame.h" |
|||
#if LJ_HASFFI |
|||
#include "lj_ctype.h" |
|||
#include "lj_cconv.h" |
|||
#endif |
|||
#include "lj_bc.h" |
|||
#include "lj_ff.h" |
|||
#include "lj_dispatch.h" |
|||
#include "lj_char.h" |
|||
#include "lj_strscan.h" |
|||
#include "lj_strfmt.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* -- Base library: checks ------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_base |
|||
|
|||
LJLIB_ASM(assert) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checkany(L, 1); |
|||
if (L->top == L->base+1) |
|||
lj_err_caller(L, LJ_ERR_ASSERT); |
|||
else if (tvisstr(L->base+1) || tvisnumber(L->base+1)) |
|||
lj_err_callermsg(L, strdata(lj_lib_checkstr(L, 2))); |
|||
else |
|||
lj_err_run(L); |
|||
return FFH_UNREACHABLE; |
|||
} |
|||
|
|||
/* ORDER LJ_T */ |
|||
LJLIB_PUSH("nil") |
|||
LJLIB_PUSH("boolean") |
|||
LJLIB_PUSH(top-1) /* boolean */ |
|||
LJLIB_PUSH("userdata") |
|||
LJLIB_PUSH("string") |
|||
LJLIB_PUSH("upval") |
|||
LJLIB_PUSH("thread") |
|||
LJLIB_PUSH("proto") |
|||
LJLIB_PUSH("function") |
|||
LJLIB_PUSH("trace") |
|||
LJLIB_PUSH("cdata") |
|||
LJLIB_PUSH("table") |
|||
LJLIB_PUSH(top-9) /* userdata */ |
|||
LJLIB_PUSH("number") |
|||
LJLIB_ASM_(type) LJLIB_REC(.) |
|||
/* Recycle the lj_lib_checkany(L, 1) from assert. */ |
|||
|
|||
/* -- Base library: iterators --------------------------------------------- */ |
|||
|
|||
/* This solves a circular dependency problem -- change FF_next_N as needed. */ |
|||
LJ_STATIC_ASSERT((int)FF_next == FF_next_N); |
|||
|
|||
LJLIB_ASM(next) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checktab(L, 1); |
|||
lj_err_msg(L, LJ_ERR_NEXTIDX); |
|||
return FFH_UNREACHABLE; |
|||
} |
|||
|
|||
#if LJ_52 || LJ_HASFFI |
|||
static int ffh_pairs(lua_State *L, MMS mm) |
|||
{ |
|||
TValue *o = lj_lib_checkany(L, 1); |
|||
cTValue *mo = lj_meta_lookup(L, o, mm); |
|||
if ((LJ_52 || tviscdata(o)) && !tvisnil(mo)) { |
|||
L->top = o+1; /* Only keep one argument. */ |
|||
copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */ |
|||
return FFH_TAILCALL; |
|||
} else { |
|||
if (!tvistab(o)) lj_err_argt(L, 1, LUA_TTABLE); |
|||
if (LJ_FR2) { copyTV(L, o-1, o); o--; } |
|||
setfuncV(L, o-1, funcV(lj_lib_upvalue(L, 1))); |
|||
if (mm == MM_pairs) setnilV(o+1); else setintV(o+1, 0); |
|||
return FFH_RES(3); |
|||
} |
|||
} |
|||
#else |
|||
#define ffh_pairs(L, mm) (lj_lib_checktab(L, 1), FFH_UNREACHABLE) |
|||
#endif |
|||
|
|||
LJLIB_PUSH(lastcl) |
|||
LJLIB_ASM(pairs) LJLIB_REC(xpairs 0) |
|||
{ |
|||
return ffh_pairs(L, MM_pairs); |
|||
} |
|||
|
|||
LJLIB_NOREGUV LJLIB_ASM(ipairs_aux) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checktab(L, 1); |
|||
lj_lib_checkint(L, 2); |
|||
return FFH_UNREACHABLE; |
|||
} |
|||
|
|||
LJLIB_PUSH(lastcl) |
|||
LJLIB_ASM(ipairs) LJLIB_REC(xpairs 1) |
|||
{ |
|||
return ffh_pairs(L, MM_ipairs); |
|||
} |
|||
|
|||
/* -- Base library: getters and setters ----------------------------------- */ |
|||
|
|||
LJLIB_ASM_(getmetatable) LJLIB_REC(.) |
|||
/* Recycle the lj_lib_checkany(L, 1) from assert. */ |
|||
|
|||
LJLIB_ASM(setmetatable) LJLIB_REC(.) |
|||
{ |
|||
GCtab *t = lj_lib_checktab(L, 1); |
|||
GCtab *mt = lj_lib_checktabornil(L, 2); |
|||
if (!tvisnil(lj_meta_lookup(L, L->base, MM_metatable))) |
|||
lj_err_caller(L, LJ_ERR_PROTMT); |
|||
setgcref(t->metatable, obj2gco(mt)); |
|||
if (mt) { lj_gc_objbarriert(L, t, mt); } |
|||
settabV(L, L->base-1-LJ_FR2, t); |
|||
return FFH_RES(1); |
|||
} |
|||
|
|||
LJLIB_CF(getfenv) LJLIB_REC(.) |
|||
{ |
|||
GCfunc *fn; |
|||
cTValue *o = L->base; |
|||
if (!(o < L->top && tvisfunc(o))) { |
|||
int level = lj_lib_optint(L, 1, 1); |
|||
o = lj_debug_frame(L, level, &level); |
|||
if (o == NULL) |
|||
lj_err_arg(L, 1, LJ_ERR_INVLVL); |
|||
if (LJ_FR2) o--; |
|||
} |
|||
fn = &gcval(o)->fn; |
|||
settabV(L, L->top++, isluafunc(fn) ? tabref(fn->l.env) : tabref(L->env)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(setfenv) |
|||
{ |
|||
GCfunc *fn; |
|||
GCtab *t = lj_lib_checktab(L, 2); |
|||
cTValue *o = L->base; |
|||
if (!(o < L->top && tvisfunc(o))) { |
|||
int level = lj_lib_checkint(L, 1); |
|||
if (level == 0) { |
|||
/* NOBARRIER: A thread (i.e. L) is never black. */ |
|||
setgcref(L->env, obj2gco(t)); |
|||
return 0; |
|||
} |
|||
o = lj_debug_frame(L, level, &level); |
|||
if (o == NULL) |
|||
lj_err_arg(L, 1, LJ_ERR_INVLVL); |
|||
if (LJ_FR2) o--; |
|||
} |
|||
fn = &gcval(o)->fn; |
|||
if (!isluafunc(fn)) |
|||
lj_err_caller(L, LJ_ERR_SETFENV); |
|||
setgcref(fn->l.env, obj2gco(t)); |
|||
lj_gc_objbarrier(L, obj2gco(fn), t); |
|||
setfuncV(L, L->top++, fn); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_ASM(rawget) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checktab(L, 1); |
|||
lj_lib_checkany(L, 2); |
|||
return FFH_UNREACHABLE; |
|||
} |
|||
|
|||
LJLIB_CF(rawset) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checktab(L, 1); |
|||
lj_lib_checkany(L, 2); |
|||
L->top = 1+lj_lib_checkany(L, 3); |
|||
lua_rawset(L, 1); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(rawequal) LJLIB_REC(.) |
|||
{ |
|||
cTValue *o1 = lj_lib_checkany(L, 1); |
|||
cTValue *o2 = lj_lib_checkany(L, 2); |
|||
setboolV(L->top-1, lj_obj_equal(o1, o2)); |
|||
return 1; |
|||
} |
|||
|
|||
#if LJ_52 |
|||
LJLIB_CF(rawlen) LJLIB_REC(.) |
|||
{ |
|||
cTValue *o = L->base; |
|||
int32_t len; |
|||
if (L->top > o && tvisstr(o)) |
|||
len = (int32_t)strV(o)->len; |
|||
else |
|||
len = (int32_t)lj_tab_len(lj_lib_checktab(L, 1)); |
|||
setintV(L->top-1, len); |
|||
return 1; |
|||
} |
|||
#endif |
|||
|
|||
LJLIB_CF(unpack) |
|||
{ |
|||
GCtab *t = lj_lib_checktab(L, 1); |
|||
int32_t n, i = lj_lib_optint(L, 2, 1); |
|||
int32_t e = (L->base+3-1 < L->top && !tvisnil(L->base+3-1)) ? |
|||
lj_lib_checkint(L, 3) : (int32_t)lj_tab_len(t); |
|||
uint32_t nu; |
|||
if (i > e) return 0; |
|||
nu = (uint32_t)e - (uint32_t)i; |
|||
n = (int32_t)(nu+1); |
|||
if (nu >= LUAI_MAXCSTACK || !lua_checkstack(L, n)) |
|||
lj_err_caller(L, LJ_ERR_UNPACK); |
|||
do { |
|||
cTValue *tv = lj_tab_getint(t, i); |
|||
if (tv) { |
|||
copyTV(L, L->top++, tv); |
|||
} else { |
|||
setnilV(L->top++); |
|||
} |
|||
} while (i++ < e); |
|||
return n; |
|||
} |
|||
|
|||
LJLIB_CF(select) LJLIB_REC(.) |
|||
{ |
|||
int32_t n = (int32_t)(L->top - L->base); |
|||
if (n >= 1 && tvisstr(L->base) && *strVdata(L->base) == '#') { |
|||
setintV(L->top-1, n-1); |
|||
return 1; |
|||
} else { |
|||
int32_t i = lj_lib_checkint(L, 1); |
|||
if (i < 0) i = n + i; else if (i > n) i = n; |
|||
if (i < 1) |
|||
lj_err_arg(L, 1, LJ_ERR_IDXRNG); |
|||
return n - i; |
|||
} |
|||
} |
|||
|
|||
/* -- Base library: conversions ------------------------------------------- */ |
|||
|
|||
LJLIB_ASM(tonumber) LJLIB_REC(.) |
|||
{ |
|||
int32_t base = lj_lib_optint(L, 2, 10); |
|||
if (base == 10) { |
|||
TValue *o = lj_lib_checkany(L, 1); |
|||
if (lj_strscan_numberobj(o)) { |
|||
copyTV(L, L->base-1-LJ_FR2, o); |
|||
return FFH_RES(1); |
|||
} |
|||
#if LJ_HASFFI |
|||
if (tviscdata(o)) { |
|||
CTState *cts = ctype_cts(L); |
|||
CType *ct = lj_ctype_rawref(cts, cdataV(o)->ctypeid); |
|||
if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); |
|||
if (ctype_isnum(ct->info) || ctype_iscomplex(ct->info)) { |
|||
if (LJ_DUALNUM && ctype_isinteger_or_bool(ct->info) && |
|||
ct->size <= 4 && !(ct->size == 4 && (ct->info & CTF_UNSIGNED))) { |
|||
int32_t i; |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, 0); |
|||
setintV(L->base-1-LJ_FR2, i); |
|||
return FFH_RES(1); |
|||
} |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_DOUBLE), |
|||
(uint8_t *)&(L->base-1-LJ_FR2)->n, o, 0); |
|||
return FFH_RES(1); |
|||
} |
|||
} |
|||
#endif |
|||
} else { |
|||
const char *p = strdata(lj_lib_checkstr(L, 1)); |
|||
char *ep; |
|||
unsigned int neg = 0; |
|||
unsigned long ul; |
|||
if (base < 2 || base > 36) |
|||
lj_err_arg(L, 2, LJ_ERR_BASERNG); |
|||
while (lj_char_isspace((unsigned char)(*p))) p++; |
|||
if (*p == '-') { p++; neg = 1; } else if (*p == '+') { p++; } |
|||
if (lj_char_isalnum((unsigned char)(*p))) { |
|||
ul = strtoul(p, &ep, base); |
|||
if (p != ep) { |
|||
while (lj_char_isspace((unsigned char)(*ep))) ep++; |
|||
if (*ep == '\0') { |
|||
if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u+neg)) { |
|||
if (neg) ul = (unsigned long)-(long)ul; |
|||
setintV(L->base-1-LJ_FR2, (int32_t)ul); |
|||
} else { |
|||
lua_Number n = (lua_Number)ul; |
|||
if (neg) n = -n; |
|||
setnumV(L->base-1-LJ_FR2, n); |
|||
} |
|||
return FFH_RES(1); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
setnilV(L->base-1-LJ_FR2); |
|||
return FFH_RES(1); |
|||
} |
|||
|
|||
LJLIB_ASM(tostring) LJLIB_REC(.) |
|||
{ |
|||
TValue *o = lj_lib_checkany(L, 1); |
|||
cTValue *mo; |
|||
L->top = o+1; /* Only keep one argument. */ |
|||
if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { |
|||
copyTV(L, L->base-1-LJ_FR2, mo); /* Replace callable. */ |
|||
return FFH_TAILCALL; |
|||
} |
|||
lj_gc_check(L); |
|||
setstrV(L, L->base-1-LJ_FR2, lj_strfmt_obj(L, L->base)); |
|||
return FFH_RES(1); |
|||
} |
|||
|
|||
/* -- Base library: throw and catch errors -------------------------------- */ |
|||
|
|||
LJLIB_CF(error) |
|||
{ |
|||
int32_t level = lj_lib_optint(L, 2, 1); |
|||
lua_settop(L, 1); |
|||
if (lua_isstring(L, 1) && level > 0) { |
|||
luaL_where(L, level); |
|||
lua_pushvalue(L, 1); |
|||
lua_concat(L, 2); |
|||
} |
|||
return lua_error(L); |
|||
} |
|||
|
|||
LJLIB_ASM(pcall) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checkany(L, 1); |
|||
lj_lib_checkfunc(L, 2); /* For xpcall only. */ |
|||
return FFH_UNREACHABLE; |
|||
} |
|||
LJLIB_ASM_(xpcall) LJLIB_REC(.) |
|||
|
|||
/* -- Base library: load Lua code ----------------------------------------- */ |
|||
|
|||
static int load_aux(lua_State *L, int status, int envarg) |
|||
{ |
|||
if (status == LUA_OK) { |
|||
if (tvistab(L->base+envarg-1)) { |
|||
GCfunc *fn = funcV(L->top-1); |
|||
GCtab *t = tabV(L->base+envarg-1); |
|||
setgcref(fn->c.env, obj2gco(t)); |
|||
lj_gc_objbarrier(L, fn, t); |
|||
} |
|||
return 1; |
|||
} else { |
|||
setnilV(L->top-2); |
|||
return 2; |
|||
} |
|||
} |
|||
|
|||
LJLIB_CF(loadfile) |
|||
{ |
|||
GCstr *fname = lj_lib_optstr(L, 1); |
|||
GCstr *mode = lj_lib_optstr(L, 2); |
|||
int status; |
|||
lua_settop(L, 3); /* Ensure env arg exists. */ |
|||
status = luaL_loadfilex(L, fname ? strdata(fname) : NULL, |
|||
mode ? strdata(mode) : NULL); |
|||
return load_aux(L, status, 3); |
|||
} |
|||
|
|||
static const char *reader_func(lua_State *L, void *ud, size_t *size) |
|||
{ |
|||
UNUSED(ud); |
|||
luaL_checkstack(L, 2, "too many nested functions"); |
|||
copyTV(L, L->top++, L->base); |
|||
lua_call(L, 0, 1); /* Call user-supplied function. */ |
|||
L->top--; |
|||
if (tvisnil(L->top)) { |
|||
*size = 0; |
|||
return NULL; |
|||
} else if (tvisstr(L->top) || tvisnumber(L->top)) { |
|||
copyTV(L, L->base+4, L->top); /* Anchor string in reserved stack slot. */ |
|||
return lua_tolstring(L, 5, size); |
|||
} else { |
|||
lj_err_caller(L, LJ_ERR_RDRSTR); |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
LJLIB_CF(load) |
|||
{ |
|||
GCstr *name = lj_lib_optstr(L, 2); |
|||
GCstr *mode = lj_lib_optstr(L, 3); |
|||
int status; |
|||
if (L->base < L->top && |
|||
(tvisstr(L->base) || tvisnumber(L->base) || tvisbuf(L->base))) { |
|||
const char *s; |
|||
MSize len; |
|||
if (tvisbuf(L->base)) { |
|||
SBufExt *sbx = bufV(L->base); |
|||
s = sbx->r; |
|||
len = sbufxlen(sbx); |
|||
if (!name) name = &G(L)->strempty; /* Buffers are not NUL-terminated. */ |
|||
} else { |
|||
GCstr *str = lj_lib_checkstr(L, 1); |
|||
s = strdata(str); |
|||
len = str->len; |
|||
} |
|||
lua_settop(L, 4); /* Ensure env arg exists. */ |
|||
status = luaL_loadbufferx(L, s, len, name ? strdata(name) : s, |
|||
mode ? strdata(mode) : NULL); |
|||
} else { |
|||
lj_lib_checkfunc(L, 1); |
|||
lua_settop(L, 5); /* Reserve a slot for the string from the reader. */ |
|||
status = lua_loadx(L, reader_func, NULL, name ? strdata(name) : "=(load)", |
|||
mode ? strdata(mode) : NULL); |
|||
} |
|||
return load_aux(L, status, 4); |
|||
} |
|||
|
|||
LJLIB_CF(loadstring) |
|||
{ |
|||
return lj_cf_load(L); |
|||
} |
|||
|
|||
LJLIB_CF(dofile) |
|||
{ |
|||
GCstr *fname = lj_lib_optstr(L, 1); |
|||
setnilV(L->top); |
|||
L->top = L->base+1; |
|||
if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != LUA_OK) |
|||
lua_error(L); |
|||
lua_call(L, 0, LUA_MULTRET); |
|||
return (int)(L->top - L->base) - 1; |
|||
} |
|||
|
|||
/* -- Base library: GC control -------------------------------------------- */ |
|||
|
|||
LJLIB_CF(gcinfo) |
|||
{ |
|||
setintV(L->top++, (int32_t)(G(L)->gc.total >> 10)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(collectgarbage) |
|||
{ |
|||
int opt = lj_lib_checkopt(L, 1, LUA_GCCOLLECT, /* ORDER LUA_GC* */ |
|||
"\4stop\7restart\7collect\5count\1\377\4step\10setpause\12setstepmul\1\377\11isrunning"); |
|||
int32_t data = lj_lib_optint(L, 2, 0); |
|||
if (opt == LUA_GCCOUNT) { |
|||
setnumV(L->top, (lua_Number)G(L)->gc.total/1024.0); |
|||
} else { |
|||
int res = lua_gc(L, opt, data); |
|||
if (opt == LUA_GCSTEP || opt == LUA_GCISRUNNING) |
|||
setboolV(L->top, res); |
|||
else |
|||
setintV(L->top, res); |
|||
} |
|||
L->top++; |
|||
return 1; |
|||
} |
|||
|
|||
/* -- Base library: miscellaneous functions ------------------------------- */ |
|||
|
|||
LJLIB_PUSH(top-2) /* Upvalue holds weak table. */ |
|||
LJLIB_CF(newproxy) |
|||
{ |
|||
lua_settop(L, 1); |
|||
lua_newuserdata(L, 0); |
|||
if (lua_toboolean(L, 1) == 0) { /* newproxy(): without metatable. */ |
|||
return 1; |
|||
} else if (lua_isboolean(L, 1)) { /* newproxy(true): with metatable. */ |
|||
lua_newtable(L); |
|||
lua_pushvalue(L, -1); |
|||
lua_pushboolean(L, 1); |
|||
lua_rawset(L, lua_upvalueindex(1)); /* Remember mt in weak table. */ |
|||
} else { /* newproxy(proxy): inherit metatable. */ |
|||
int validproxy = 0; |
|||
if (lua_getmetatable(L, 1)) { |
|||
lua_rawget(L, lua_upvalueindex(1)); |
|||
validproxy = lua_toboolean(L, -1); |
|||
lua_pop(L, 1); |
|||
} |
|||
if (!validproxy) |
|||
lj_err_arg(L, 1, LJ_ERR_NOPROXY); |
|||
lua_getmetatable(L, 1); |
|||
} |
|||
lua_setmetatable(L, 2); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH("tostring") |
|||
LJLIB_CF(print) |
|||
{ |
|||
ptrdiff_t i, nargs = L->top - L->base; |
|||
cTValue *tv = lj_tab_getstr(tabref(L->env), strV(lj_lib_upvalue(L, 1))); |
|||
int shortcut; |
|||
if (tv && !tvisnil(tv)) { |
|||
copyTV(L, L->top++, tv); |
|||
} else { |
|||
setstrV(L, L->top++, strV(lj_lib_upvalue(L, 1))); |
|||
lua_gettable(L, LUA_GLOBALSINDEX); |
|||
tv = L->top-1; |
|||
} |
|||
shortcut = (tvisfunc(tv) && funcV(tv)->c.ffid == FF_tostring) && |
|||
!gcrefu(basemt_it(G(L), LJ_TNUMX)); |
|||
for (i = 0; i < nargs; i++) { |
|||
cTValue *o = &L->base[i]; |
|||
const char *str; |
|||
size_t size; |
|||
MSize len; |
|||
if (shortcut && (str = lj_strfmt_wstrnum(L, o, &len)) != NULL) { |
|||
size = len; |
|||
} else { |
|||
copyTV(L, L->top+1, o); |
|||
copyTV(L, L->top, L->top-1); |
|||
L->top += 2; |
|||
lua_call(L, 1, 1); |
|||
str = lua_tolstring(L, -1, &size); |
|||
if (!str) |
|||
lj_err_caller(L, LJ_ERR_PRTOSTR); |
|||
L->top--; |
|||
} |
|||
if (i) |
|||
putchar('\t'); |
|||
fwrite(str, 1, size, stdout); |
|||
} |
|||
putchar('\n'); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-3) |
|||
LJLIB_SET(_VERSION) |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* -- Coroutine library --------------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_coroutine |
|||
|
|||
LJLIB_CF(coroutine_status) |
|||
{ |
|||
const char *s; |
|||
lua_State *co; |
|||
if (!(L->top > L->base && tvisthread(L->base))) |
|||
lj_err_arg(L, 1, LJ_ERR_NOCORO); |
|||
co = threadV(L->base); |
|||
if (co == L) s = "running"; |
|||
else if (co->status == LUA_YIELD) s = "suspended"; |
|||
else if (co->status != LUA_OK) s = "dead"; |
|||
else if (co->base > tvref(co->stack)+1+LJ_FR2) s = "normal"; |
|||
else if (co->top == co->base) s = "dead"; |
|||
else s = "suspended"; |
|||
lua_pushstring(L, s); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(coroutine_running) |
|||
{ |
|||
#if LJ_52 |
|||
int ismain = lua_pushthread(L); |
|||
setboolV(L->top++, ismain); |
|||
return 2; |
|||
#else |
|||
if (lua_pushthread(L)) |
|||
setnilV(L->top++); |
|||
return 1; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_CF(coroutine_isyieldable) |
|||
{ |
|||
setboolV(L->top++, cframe_canyield(L->cframe)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(coroutine_create) |
|||
{ |
|||
lua_State *L1; |
|||
if (!(L->base < L->top && tvisfunc(L->base))) |
|||
lj_err_argt(L, 1, LUA_TFUNCTION); |
|||
L1 = lua_newthread(L); |
|||
setfuncV(L, L1->top++, funcV(L->base)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_ASM(coroutine_yield) |
|||
{ |
|||
lj_err_caller(L, LJ_ERR_CYIELD); |
|||
return FFH_UNREACHABLE; |
|||
} |
|||
|
|||
static int ffh_resume(lua_State *L, lua_State *co, int wrap) |
|||
{ |
|||
if (co->cframe != NULL || co->status > LUA_YIELD || |
|||
(co->status == LUA_OK && co->top == co->base)) { |
|||
ErrMsg em = co->cframe ? LJ_ERR_CORUN : LJ_ERR_CODEAD; |
|||
if (wrap) lj_err_caller(L, em); |
|||
setboolV(L->base-1-LJ_FR2, 0); |
|||
setstrV(L, L->base-LJ_FR2, lj_err_str(L, em)); |
|||
return FFH_RES(2); |
|||
} |
|||
lj_state_growstack(co, (MSize)(L->top - L->base)); |
|||
return FFH_RETRY; |
|||
} |
|||
|
|||
LJLIB_ASM(coroutine_resume) |
|||
{ |
|||
if (!(L->top > L->base && tvisthread(L->base))) |
|||
lj_err_arg(L, 1, LJ_ERR_NOCORO); |
|||
return ffh_resume(L, threadV(L->base), 0); |
|||
} |
|||
|
|||
LJLIB_NOREG LJLIB_ASM(coroutine_wrap_aux) |
|||
{ |
|||
return ffh_resume(L, threadV(lj_lib_upvalue(L, 1)), 1); |
|||
} |
|||
|
|||
/* Inline declarations. */ |
|||
LJ_ASMF void lj_ff_coroutine_wrap_aux(void); |
|||
#if !(LJ_TARGET_MIPS && defined(ljamalg_c)) |
|||
LJ_FUNCA_NORET void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, |
|||
lua_State *co); |
|||
#endif |
|||
|
|||
/* Error handler, called from assembler VM. */ |
|||
void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co) |
|||
{ |
|||
co->top--; copyTV(L, L->top, co->top); L->top++; |
|||
if (tvisstr(L->top-1)) |
|||
lj_err_callermsg(L, strVdata(L->top-1)); |
|||
else |
|||
lj_err_run(L); |
|||
} |
|||
|
|||
/* Forward declaration. */ |
|||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn); |
|||
|
|||
LJLIB_CF(coroutine_wrap) |
|||
{ |
|||
GCfunc *fn; |
|||
lj_cf_coroutine_create(L); |
|||
fn = lj_lib_pushcc(L, lj_ffh_coroutine_wrap_aux, FF_coroutine_wrap_aux, 1); |
|||
setpc_wrap_aux(L, fn); |
|||
return 1; |
|||
} |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* Fix the PC of wrap_aux. Really ugly workaround. */ |
|||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn) |
|||
{ |
|||
setmref(fn->c.pc, &L2GG(L)->bcff[lj_lib_init_coroutine[1]+2]); |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static void newproxy_weaktable(lua_State *L) |
|||
{ |
|||
/* NOBARRIER: The table is new (marked white). */ |
|||
GCtab *t = lj_tab_new(L, 0, 1); |
|||
settabV(L, L->top++, t); |
|||
setgcref(t->metatable, obj2gco(t)); |
|||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")), |
|||
lj_str_newlit(L, "kv")); |
|||
t->nomm = (uint8_t)(~(1u<<MM_mode)); |
|||
} |
|||
|
|||
LUALIB_API int luaopen_base(lua_State *L) |
|||
{ |
|||
/* NOBARRIER: Table and value are the same. */ |
|||
GCtab *env = tabref(L->env); |
|||
settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env); |
|||
lua_pushliteral(L, LUA_VERSION); /* top-3. */ |
|||
newproxy_weaktable(L); /* top-2. */ |
|||
LJ_LIB_REG(L, "_G", base); |
|||
LJ_LIB_REG(L, LUA_COLIBNAME, coroutine); |
|||
return 2; |
|||
} |
|||
|
@ -0,0 +1,180 @@ |
|||
/*
|
|||
** Bit manipulation library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#define lib_bit_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_err.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_strscan.h" |
|||
#include "lj_strfmt.h" |
|||
#if LJ_HASFFI |
|||
#include "lj_ctype.h" |
|||
#include "lj_cdata.h" |
|||
#include "lj_cconv.h" |
|||
#include "lj_carith.h" |
|||
#endif |
|||
#include "lj_ff.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_bit |
|||
|
|||
#if LJ_HASFFI |
|||
static int bit_result64(lua_State *L, CTypeID id, uint64_t x) |
|||
{ |
|||
GCcdata *cd = lj_cdata_new_(L, id, 8); |
|||
*(uint64_t *)cdataptr(cd) = x; |
|||
setcdataV(L, L->base-1-LJ_FR2, cd); |
|||
return FFH_RES(1); |
|||
} |
|||
#else |
|||
static int32_t bit_checkbit(lua_State *L, int narg) |
|||
{ |
|||
TValue *o = L->base + narg-1; |
|||
if (!(o < L->top && lj_strscan_numberobj(o))) |
|||
lj_err_argt(L, narg, LUA_TNUMBER); |
|||
if (LJ_LIKELY(tvisint(o))) { |
|||
return intV(o); |
|||
} else { |
|||
int32_t i = lj_num2bit(numV(o)); |
|||
if (LJ_DUALNUM) setintV(o, i); |
|||
return i; |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
LJLIB_ASM(bit_tobit) LJLIB_REC(bit_tobit) |
|||
{ |
|||
#if LJ_HASFFI |
|||
CTypeID id = 0; |
|||
setintV(L->base-1-LJ_FR2, (int32_t)lj_carith_check64(L, 1, &id)); |
|||
return FFH_RES(1); |
|||
#else |
|||
lj_lib_checknumber(L, 1); |
|||
return FFH_RETRY; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_ASM(bit_bnot) LJLIB_REC(bit_unary IR_BNOT) |
|||
{ |
|||
#if LJ_HASFFI |
|||
CTypeID id = 0; |
|||
uint64_t x = lj_carith_check64(L, 1, &id); |
|||
return id ? bit_result64(L, id, ~x) : FFH_RETRY; |
|||
#else |
|||
lj_lib_checknumber(L, 1); |
|||
return FFH_RETRY; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_ASM(bit_bswap) LJLIB_REC(bit_unary IR_BSWAP) |
|||
{ |
|||
#if LJ_HASFFI |
|||
CTypeID id = 0; |
|||
uint64_t x = lj_carith_check64(L, 1, &id); |
|||
return id ? bit_result64(L, id, lj_bswap64(x)) : FFH_RETRY; |
|||
#else |
|||
lj_lib_checknumber(L, 1); |
|||
return FFH_RETRY; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_ASM(bit_lshift) LJLIB_REC(bit_shift IR_BSHL) |
|||
{ |
|||
#if LJ_HASFFI |
|||
CTypeID id = 0, id2 = 0; |
|||
uint64_t x = lj_carith_check64(L, 1, &id); |
|||
int32_t sh = (int32_t)lj_carith_check64(L, 2, &id2); |
|||
if (id) { |
|||
x = lj_carith_shift64(x, sh, curr_func(L)->c.ffid - (int)FF_bit_lshift); |
|||
return bit_result64(L, id, x); |
|||
} |
|||
if (id2) setintV(L->base+1, sh); |
|||
return FFH_RETRY; |
|||
#else |
|||
lj_lib_checknumber(L, 1); |
|||
bit_checkbit(L, 2); |
|||
return FFH_RETRY; |
|||
#endif |
|||
} |
|||
LJLIB_ASM_(bit_rshift) LJLIB_REC(bit_shift IR_BSHR) |
|||
LJLIB_ASM_(bit_arshift) LJLIB_REC(bit_shift IR_BSAR) |
|||
LJLIB_ASM_(bit_rol) LJLIB_REC(bit_shift IR_BROL) |
|||
LJLIB_ASM_(bit_ror) LJLIB_REC(bit_shift IR_BROR) |
|||
|
|||
LJLIB_ASM(bit_band) LJLIB_REC(bit_nary IR_BAND) |
|||
{ |
|||
#if LJ_HASFFI |
|||
CTypeID id = 0; |
|||
TValue *o = L->base, *top = L->top; |
|||
int i = 0; |
|||
do { lj_carith_check64(L, ++i, &id); } while (++o < top); |
|||
if (id) { |
|||
CTState *cts = ctype_cts(L); |
|||
CType *ct = ctype_get(cts, id); |
|||
int op = curr_func(L)->c.ffid - (int)FF_bit_bor; |
|||
uint64_t x, y = op >= 0 ? 0 : ~(uint64_t)0; |
|||
o = L->base; |
|||
do { |
|||
lj_cconv_ct_tv(cts, ct, (uint8_t *)&x, o, 0); |
|||
if (op < 0) y &= x; else if (op == 0) y |= x; else y ^= x; |
|||
} while (++o < top); |
|||
return bit_result64(L, id, y); |
|||
} |
|||
return FFH_RETRY; |
|||
#else |
|||
int i = 0; |
|||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top); |
|||
return FFH_RETRY; |
|||
#endif |
|||
} |
|||
LJLIB_ASM_(bit_bor) LJLIB_REC(bit_nary IR_BOR) |
|||
LJLIB_ASM_(bit_bxor) LJLIB_REC(bit_nary IR_BXOR) |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
LJLIB_CF(bit_tohex) LJLIB_REC(.) |
|||
{ |
|||
#if LJ_HASFFI |
|||
CTypeID id = 0, id2 = 0; |
|||
uint64_t b = lj_carith_check64(L, 1, &id); |
|||
int32_t n = L->base+1>=L->top ? (id ? 16 : 8) : |
|||
(int32_t)lj_carith_check64(L, 2, &id2); |
|||
#else |
|||
uint32_t b = (uint32_t)bit_checkbit(L, 1); |
|||
int32_t n = L->base+1>=L->top ? 8 : bit_checkbit(L, 2); |
|||
#endif |
|||
SBuf *sb = lj_buf_tmp_(L); |
|||
SFormat sf = (STRFMT_UINT|STRFMT_T_HEX); |
|||
if (n < 0) { n = -n; sf |= STRFMT_F_UPPER; } |
|||
sf |= ((SFormat)((n+1)&255) << STRFMT_SH_PREC); |
|||
#if LJ_HASFFI |
|||
if (n < 16) b &= ((uint64_t)1 << 4*n)-1; |
|||
#else |
|||
if (n < 8) b &= (1u << 4*n)-1; |
|||
#endif |
|||
sb = lj_strfmt_putfxint(sb, sf, b); |
|||
setstrV(L, L->top-1, lj_buf_str(L, sb)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
LUALIB_API int luaopen_bit(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, LUA_BITLIBNAME, bit); |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,358 @@ |
|||
/*
|
|||
** Buffer library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#define lib_buffer_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
|
|||
#if LJ_HASBUFFER |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_str.h" |
|||
#include "lj_tab.h" |
|||
#include "lj_udata.h" |
|||
#include "lj_meta.h" |
|||
#if LJ_HASFFI |
|||
#include "lj_ctype.h" |
|||
#include "lj_cdata.h" |
|||
#include "lj_cconv.h" |
|||
#endif |
|||
#include "lj_strfmt.h" |
|||
#include "lj_serialize.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* -- Helper functions ---------------------------------------------------- */ |
|||
|
|||
/* Check that the first argument is a string buffer. */ |
|||
static SBufExt *buffer_tobuf(lua_State *L) |
|||
{ |
|||
if (!(L->base < L->top && tvisbuf(L->base))) |
|||
lj_err_argtype(L, 1, "buffer"); |
|||
return bufV(L->base); |
|||
} |
|||
|
|||
/* Ditto, but for writers. */ |
|||
static LJ_AINLINE SBufExt *buffer_tobufw(lua_State *L) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
setsbufXL_(sbx, L); |
|||
return sbx; |
|||
} |
|||
|
|||
#define buffer_toudata(sbx) ((GCudata *)(sbx)-1) |
|||
|
|||
/* -- Buffer methods ------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_buffer_method |
|||
|
|||
LJLIB_CF(buffer_method_free) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
lj_bufx_free(L, sbx); |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_reset) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
lj_bufx_reset(sbx); |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_skip) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
MSize n = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF); |
|||
MSize len = sbufxlen(sbx); |
|||
if (n < len) { |
|||
sbx->r += n; |
|||
} else if (sbufiscow(sbx)) { |
|||
sbx->r = sbx->w; |
|||
} else { |
|||
sbx->r = sbx->w = sbx->b; |
|||
} |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_set) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
GCobj *ref; |
|||
const char *p; |
|||
MSize len; |
|||
#if LJ_HASFFI |
|||
if (tviscdata(L->base+1)) { |
|||
CTState *cts = ctype_cts(L); |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, |
|||
L->base+1, CCF_ARG(2)); |
|||
len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF); |
|||
} else |
|||
#endif |
|||
{ |
|||
GCstr *str = lj_lib_checkstrx(L, 2); |
|||
p = strdata(str); |
|||
len = str->len; |
|||
} |
|||
lj_bufx_free(L, sbx); |
|||
lj_bufx_set_cow(L, sbx, p, len); |
|||
ref = gcV(L->base+1); |
|||
setgcref(sbx->cowref, ref); |
|||
lj_gc_objbarrier(L, buffer_toudata(sbx), ref); |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_put) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobufw(L); |
|||
ptrdiff_t arg, narg = L->top - L->base; |
|||
for (arg = 1; arg < narg; arg++) { |
|||
cTValue *o = &L->base[arg], *mo = NULL; |
|||
retry: |
|||
if (tvisstr(o)) { |
|||
lj_buf_putstr((SBuf *)sbx, strV(o)); |
|||
} else if (tvisint(o)) { |
|||
lj_strfmt_putint((SBuf *)sbx, intV(o)); |
|||
} else if (tvisnum(o)) { |
|||
lj_strfmt_putfnum((SBuf *)sbx, STRFMT_G14, numV(o)); |
|||
} else if (tvisbuf(o)) { |
|||
SBufExt *sbx2 = bufV(o); |
|||
if (sbx2 == sbx) lj_err_arg(L, arg+1, LJ_ERR_BUFFER_SELF); |
|||
lj_buf_putmem((SBuf *)sbx, sbx2->r, sbufxlen(sbx2)); |
|||
} else if (!mo && !tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) { |
|||
/* Call __tostring metamethod inline. */ |
|||
copyTV(L, L->top++, mo); |
|||
copyTV(L, L->top++, o); |
|||
lua_call(L, 1, 1); |
|||
o = &L->base[arg]; /* The stack may have been reallocated. */ |
|||
copyTV(L, &L->base[arg], L->top-1); |
|||
L->top = L->base + narg; |
|||
goto retry; /* Retry with the result. */ |
|||
} else { |
|||
lj_err_argtype(L, arg+1, "string/number/__tostring"); |
|||
} |
|||
/* Probably not useful to inline other __tostring MMs, e.g. FFI numbers. */ |
|||
} |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_putf) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobufw(L); |
|||
lj_strfmt_putarg(L, (SBuf *)sbx, 2, 2); |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_get) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
ptrdiff_t arg, narg = L->top - L->base; |
|||
if (narg == 1) { |
|||
narg++; |
|||
setnilV(L->top++); /* get() is the same as get(nil). */ |
|||
} |
|||
for (arg = 1; arg < narg; arg++) { |
|||
TValue *o = &L->base[arg]; |
|||
MSize n = tvisnil(o) ? LJ_MAX_BUF : |
|||
(MSize) lj_lib_checkintrange(L, arg+1, 0, LJ_MAX_BUF); |
|||
MSize len = sbufxlen(sbx); |
|||
if (n > len) n = len; |
|||
setstrV(L, o, lj_str_new(L, sbx->r, n)); |
|||
sbx->r += n; |
|||
} |
|||
if (sbx->r == sbx->w && !sbufiscow(sbx)) sbx->r = sbx->w = sbx->b; |
|||
lj_gc_check(L); |
|||
return narg-1; |
|||
} |
|||
|
|||
#if LJ_HASFFI |
|||
LJLIB_CF(buffer_method_putcdata) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobufw(L); |
|||
const char *p; |
|||
MSize len; |
|||
if (tviscdata(L->base+1)) { |
|||
CTState *cts = ctype_cts(L); |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, |
|||
L->base+1, CCF_ARG(2)); |
|||
} else { |
|||
lj_err_argtype(L, 2, "cdata"); |
|||
} |
|||
len = (MSize)lj_lib_checkintrange(L, 3, 0, LJ_MAX_BUF); |
|||
lj_buf_putmem((SBuf *)sbx, p, len); |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_reserve) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobufw(L); |
|||
MSize sz = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF); |
|||
GCcdata *cd; |
|||
lj_buf_more((SBuf *)sbx, sz); |
|||
ctype_loadffi(L); |
|||
cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR); |
|||
*(void **)cdataptr(cd) = sbx->w; |
|||
setcdataV(L, L->top++, cd); |
|||
setintV(L->top++, sbufleft(sbx)); |
|||
return 2; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_commit) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
MSize len = (MSize)lj_lib_checkintrange(L, 2, 0, LJ_MAX_BUF); |
|||
if (len > sbufleft(sbx)) lj_err_arg(L, 2, LJ_ERR_NUMRNG); |
|||
sbx->w += len; |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_ref) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
GCcdata *cd; |
|||
ctype_loadffi(L); |
|||
cd = lj_cdata_new_(L, CTID_P_UINT8, CTSIZE_PTR); |
|||
*(void **)cdataptr(cd) = sbx->r; |
|||
setcdataV(L, L->top++, cd); |
|||
setintV(L->top++, sbufxlen(sbx)); |
|||
return 2; |
|||
} |
|||
#endif |
|||
|
|||
LJLIB_CF(buffer_method_encode) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobufw(L); |
|||
cTValue *o = lj_lib_checkany(L, 2); |
|||
lj_serialize_put(sbx, o); |
|||
lj_gc_check(L); |
|||
L->top = L->base+1; /* Chain buffer object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method_decode) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobufw(L); |
|||
setnilV(L->top++); |
|||
sbx->r = lj_serialize_get(sbx, L->top-1); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method___gc) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
lj_bufx_free(L, sbx); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method___tostring) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
setstrV(L, L->top-1, lj_str_new(L, sbx->r, sbufxlen(sbx))); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_method___len) LJLIB_REC(.) |
|||
{ |
|||
SBufExt *sbx = buffer_tobuf(L); |
|||
setintV(L->top-1, (int32_t)sbufxlen(sbx)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH("buffer") LJLIB_SET(__metatable) |
|||
LJLIB_PUSH(top-1) LJLIB_SET(__index) |
|||
|
|||
/* -- Buffer library functions -------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_buffer |
|||
|
|||
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */ |
|||
|
|||
LJLIB_CF(buffer_new) |
|||
{ |
|||
MSize sz = 0; |
|||
int targ = 1; |
|||
GCtab *env, *dict_str = NULL, *dict_mt = NULL; |
|||
GCudata *ud; |
|||
SBufExt *sbx; |
|||
if (L->base < L->top && !tvistab(L->base)) { |
|||
targ = 2; |
|||
if (!tvisnil(L->base)) |
|||
sz = (MSize)lj_lib_checkintrange(L, 1, 0, LJ_MAX_BUF); |
|||
} |
|||
if (L->base+targ-1 < L->top) { |
|||
GCtab *options = lj_lib_checktab(L, targ); |
|||
cTValue *opt_dict, *opt_mt; |
|||
opt_dict = lj_tab_getstr(options, lj_str_newlit(L, "dict")); |
|||
if (opt_dict && tvistab(opt_dict)) { |
|||
dict_str = tabV(opt_dict); |
|||
lj_serialize_dict_prep_str(L, dict_str); |
|||
} |
|||
opt_mt = lj_tab_getstr(options, lj_str_newlit(L, "metatable")); |
|||
if (opt_mt && tvistab(opt_mt)) { |
|||
dict_mt = tabV(opt_mt); |
|||
lj_serialize_dict_prep_mt(L, dict_mt); |
|||
} |
|||
} |
|||
env = tabref(curr_func(L)->c.env); |
|||
ud = lj_udata_new(L, sizeof(SBufExt), env); |
|||
ud->udtype = UDTYPE_BUFFER; |
|||
/* NOBARRIER: The GCudata is new (marked white). */ |
|||
setgcref(ud->metatable, obj2gco(env)); |
|||
setudataV(L, L->top++, ud); |
|||
sbx = (SBufExt *)uddata(ud); |
|||
lj_bufx_init(L, sbx); |
|||
setgcref(sbx->dict_str, obj2gco(dict_str)); |
|||
setgcref(sbx->dict_mt, obj2gco(dict_mt)); |
|||
if (sz > 0) lj_buf_need2((SBuf *)sbx, sz); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_encode) LJLIB_REC(.) |
|||
{ |
|||
cTValue *o = lj_lib_checkany(L, 1); |
|||
setstrV(L, L->top++, lj_serialize_encode(L, o)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(buffer_decode) LJLIB_REC(.) |
|||
{ |
|||
GCstr *str = lj_lib_checkstrx(L, 1); |
|||
setnilV(L->top++); |
|||
lj_serialize_decode(L, L->top-1, str); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
int luaopen_string_buffer(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, NULL, buffer_method); |
|||
lua_getfield(L, -1, "__tostring"); |
|||
lua_setfield(L, -2, "tostring"); |
|||
LJ_LIB_REG(L, NULL, buffer); |
|||
return 1; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,406 @@ |
|||
/*
|
|||
** Debug library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#define lib_debug_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_debug.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_debug |
|||
|
|||
LJLIB_CF(debug_getregistry) |
|||
{ |
|||
copyTV(L, L->top++, registry(L)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(debug_getmetatable) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checkany(L, 1); |
|||
if (!lua_getmetatable(L, 1)) { |
|||
setnilV(L->top-1); |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(debug_setmetatable) |
|||
{ |
|||
lj_lib_checktabornil(L, 2); |
|||
L->top = L->base+2; |
|||
lua_setmetatable(L, 1); |
|||
#if !LJ_52 |
|||
setboolV(L->top-1, 1); |
|||
#endif |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(debug_getfenv) |
|||
{ |
|||
lj_lib_checkany(L, 1); |
|||
lua_getfenv(L, 1); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(debug_setfenv) |
|||
{ |
|||
lj_lib_checktab(L, 2); |
|||
L->top = L->base+2; |
|||
if (!lua_setfenv(L, 1)) |
|||
lj_err_caller(L, LJ_ERR_SETFENV); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static void settabss(lua_State *L, const char *i, const char *v) |
|||
{ |
|||
lua_pushstring(L, v); |
|||
lua_setfield(L, -2, i); |
|||
} |
|||
|
|||
static void settabsi(lua_State *L, const char *i, int v) |
|||
{ |
|||
lua_pushinteger(L, v); |
|||
lua_setfield(L, -2, i); |
|||
} |
|||
|
|||
static void settabsb(lua_State *L, const char *i, int v) |
|||
{ |
|||
lua_pushboolean(L, v); |
|||
lua_setfield(L, -2, i); |
|||
} |
|||
|
|||
static lua_State *getthread(lua_State *L, int *arg) |
|||
{ |
|||
if (L->base < L->top && tvisthread(L->base)) { |
|||
*arg = 1; |
|||
return threadV(L->base); |
|||
} else { |
|||
*arg = 0; |
|||
return L; |
|||
} |
|||
} |
|||
|
|||
static void treatstackoption(lua_State *L, lua_State *L1, const char *fname) |
|||
{ |
|||
if (L == L1) { |
|||
lua_pushvalue(L, -2); |
|||
lua_remove(L, -3); |
|||
} |
|||
else |
|||
lua_xmove(L1, L, 1); |
|||
lua_setfield(L, -2, fname); |
|||
} |
|||
|
|||
LJLIB_CF(debug_getinfo) |
|||
{ |
|||
lj_Debug ar; |
|||
int arg, opt_f = 0, opt_L = 0; |
|||
lua_State *L1 = getthread(L, &arg); |
|||
const char *options = luaL_optstring(L, arg+2, "flnSu"); |
|||
if (lua_isnumber(L, arg+1)) { |
|||
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), (lua_Debug *)&ar)) { |
|||
setnilV(L->top-1); |
|||
return 1; |
|||
} |
|||
} else if (L->base+arg < L->top && tvisfunc(L->base+arg)) { |
|||
options = lua_pushfstring(L, ">%s", options); |
|||
setfuncV(L1, L1->top++, funcV(L->base+arg)); |
|||
} else { |
|||
lj_err_arg(L, arg+1, LJ_ERR_NOFUNCL); |
|||
} |
|||
if (!lj_debug_getinfo(L1, options, &ar, 1)) |
|||
lj_err_arg(L, arg+2, LJ_ERR_INVOPT); |
|||
lua_createtable(L, 0, 16); /* Create result table. */ |
|||
for (; *options; options++) { |
|||
switch (*options) { |
|||
case 'S': |
|||
settabss(L, "source", ar.source); |
|||
settabss(L, "short_src", ar.short_src); |
|||
settabsi(L, "linedefined", ar.linedefined); |
|||
settabsi(L, "lastlinedefined", ar.lastlinedefined); |
|||
settabss(L, "what", ar.what); |
|||
break; |
|||
case 'l': |
|||
settabsi(L, "currentline", ar.currentline); |
|||
break; |
|||
case 'u': |
|||
settabsi(L, "nups", ar.nups); |
|||
settabsi(L, "nparams", ar.nparams); |
|||
settabsb(L, "isvararg", ar.isvararg); |
|||
break; |
|||
case 'n': |
|||
settabss(L, "name", ar.name); |
|||
settabss(L, "namewhat", ar.namewhat); |
|||
break; |
|||
case 'f': opt_f = 1; break; |
|||
case 'L': opt_L = 1; break; |
|||
default: break; |
|||
} |
|||
} |
|||
if (opt_L) treatstackoption(L, L1, "activelines"); |
|||
if (opt_f) treatstackoption(L, L1, "func"); |
|||
return 1; /* Return result table. */ |
|||
} |
|||
|
|||
LJLIB_CF(debug_getlocal) |
|||
{ |
|||
int arg; |
|||
lua_State *L1 = getthread(L, &arg); |
|||
lua_Debug ar; |
|||
const char *name; |
|||
int slot = lj_lib_checkint(L, arg+2); |
|||
if (tvisfunc(L->base+arg)) { |
|||
L->top = L->base+arg+1; |
|||
lua_pushstring(L, lua_getlocal(L, NULL, slot)); |
|||
return 1; |
|||
} |
|||
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar)) |
|||
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG); |
|||
name = lua_getlocal(L1, &ar, slot); |
|||
if (name) { |
|||
lua_xmove(L1, L, 1); |
|||
lua_pushstring(L, name); |
|||
lua_pushvalue(L, -2); |
|||
return 2; |
|||
} else { |
|||
setnilV(L->top-1); |
|||
return 1; |
|||
} |
|||
} |
|||
|
|||
LJLIB_CF(debug_setlocal) |
|||
{ |
|||
int arg; |
|||
lua_State *L1 = getthread(L, &arg); |
|||
lua_Debug ar; |
|||
TValue *tv; |
|||
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar)) |
|||
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG); |
|||
tv = lj_lib_checkany(L, arg+3); |
|||
copyTV(L1, L1->top++, tv); |
|||
lua_pushstring(L, lua_setlocal(L1, &ar, lj_lib_checkint(L, arg+2))); |
|||
return 1; |
|||
} |
|||
|
|||
static int debug_getupvalue(lua_State *L, int get) |
|||
{ |
|||
int32_t n = lj_lib_checkint(L, 2); |
|||
const char *name; |
|||
lj_lib_checkfunc(L, 1); |
|||
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); |
|||
if (name) { |
|||
lua_pushstring(L, name); |
|||
if (!get) return 1; |
|||
copyTV(L, L->top, L->top-2); |
|||
L->top++; |
|||
return 2; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(debug_getupvalue) |
|||
{ |
|||
return debug_getupvalue(L, 1); |
|||
} |
|||
|
|||
LJLIB_CF(debug_setupvalue) |
|||
{ |
|||
lj_lib_checkany(L, 3); |
|||
return debug_getupvalue(L, 0); |
|||
} |
|||
|
|||
LJLIB_CF(debug_upvalueid) |
|||
{ |
|||
GCfunc *fn = lj_lib_checkfunc(L, 1); |
|||
int32_t n = lj_lib_checkint(L, 2) - 1; |
|||
if ((uint32_t)n >= fn->l.nupvalues) |
|||
lj_err_arg(L, 2, LJ_ERR_IDXRNG); |
|||
lua_pushlightuserdata(L, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) : |
|||
(void *)&fn->c.upvalue[n]); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(debug_upvaluejoin) |
|||
{ |
|||
GCfunc *fn[2]; |
|||
GCRef *p[2]; |
|||
int i; |
|||
for (i = 0; i < 2; i++) { |
|||
int32_t n; |
|||
fn[i] = lj_lib_checkfunc(L, 2*i+1); |
|||
if (!isluafunc(fn[i])) |
|||
lj_err_arg(L, 2*i+1, LJ_ERR_NOLFUNC); |
|||
n = lj_lib_checkint(L, 2*i+2) - 1; |
|||
if ((uint32_t)n >= fn[i]->l.nupvalues) |
|||
lj_err_arg(L, 2*i+2, LJ_ERR_IDXRNG); |
|||
p[i] = &fn[i]->l.uvptr[n]; |
|||
} |
|||
setgcrefr(*p[0], *p[1]); |
|||
lj_gc_objbarrier(L, fn[0], gcref(*p[1])); |
|||
return 0; |
|||
} |
|||
|
|||
#if LJ_52 |
|||
LJLIB_CF(debug_getuservalue) |
|||
{ |
|||
TValue *o = L->base; |
|||
if (o < L->top && tvisudata(o)) |
|||
settabV(L, o, tabref(udataV(o)->env)); |
|||
else |
|||
setnilV(o); |
|||
L->top = o+1; |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(debug_setuservalue) |
|||
{ |
|||
TValue *o = L->base; |
|||
if (!(o < L->top && tvisudata(o))) |
|||
lj_err_argt(L, 1, LUA_TUSERDATA); |
|||
if (!(o+1 < L->top && tvistab(o+1))) |
|||
lj_err_argt(L, 2, LUA_TTABLE); |
|||
L->top = o+2; |
|||
lua_setfenv(L, 1); |
|||
return 1; |
|||
} |
|||
#endif |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define KEY_HOOK (U64x(80000000,00000000)|'h') |
|||
|
|||
static void hookf(lua_State *L, lua_Debug *ar) |
|||
{ |
|||
static const char *const hooknames[] = |
|||
{"call", "return", "line", "count", "tail return"}; |
|||
(L->top++)->u64 = KEY_HOOK; |
|||
lua_rawget(L, LUA_REGISTRYINDEX); |
|||
if (lua_isfunction(L, -1)) { |
|||
lua_pushstring(L, hooknames[(int)ar->event]); |
|||
if (ar->currentline >= 0) |
|||
lua_pushinteger(L, ar->currentline); |
|||
else lua_pushnil(L); |
|||
lua_call(L, 2, 0); |
|||
} |
|||
} |
|||
|
|||
static int makemask(const char *smask, int count) |
|||
{ |
|||
int mask = 0; |
|||
if (strchr(smask, 'c')) mask |= LUA_MASKCALL; |
|||
if (strchr(smask, 'r')) mask |= LUA_MASKRET; |
|||
if (strchr(smask, 'l')) mask |= LUA_MASKLINE; |
|||
if (count > 0) mask |= LUA_MASKCOUNT; |
|||
return mask; |
|||
} |
|||
|
|||
static char *unmakemask(int mask, char *smask) |
|||
{ |
|||
int i = 0; |
|||
if (mask & LUA_MASKCALL) smask[i++] = 'c'; |
|||
if (mask & LUA_MASKRET) smask[i++] = 'r'; |
|||
if (mask & LUA_MASKLINE) smask[i++] = 'l'; |
|||
smask[i] = '\0'; |
|||
return smask; |
|||
} |
|||
|
|||
LJLIB_CF(debug_sethook) |
|||
{ |
|||
int arg, mask, count; |
|||
lua_Hook func; |
|||
(void)getthread(L, &arg); |
|||
if (lua_isnoneornil(L, arg+1)) { |
|||
lua_settop(L, arg+1); |
|||
func = NULL; mask = 0; count = 0; /* turn off hooks */ |
|||
} else { |
|||
const char *smask = luaL_checkstring(L, arg+2); |
|||
luaL_checktype(L, arg+1, LUA_TFUNCTION); |
|||
count = luaL_optint(L, arg+3, 0); |
|||
func = hookf; mask = makemask(smask, count); |
|||
} |
|||
(L->top++)->u64 = KEY_HOOK; |
|||
lua_pushvalue(L, arg+1); |
|||
lua_rawset(L, LUA_REGISTRYINDEX); |
|||
lua_sethook(L, func, mask, count); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(debug_gethook) |
|||
{ |
|||
char buff[5]; |
|||
int mask = lua_gethookmask(L); |
|||
lua_Hook hook = lua_gethook(L); |
|||
if (hook != NULL && hook != hookf) { /* external hook? */ |
|||
lua_pushliteral(L, "external hook"); |
|||
} else { |
|||
(L->top++)->u64 = KEY_HOOK; |
|||
lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */ |
|||
} |
|||
lua_pushstring(L, unmakemask(mask, buff)); |
|||
lua_pushinteger(L, lua_gethookcount(L)); |
|||
return 3; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
LJLIB_CF(debug_debug) |
|||
{ |
|||
for (;;) { |
|||
char buffer[250]; |
|||
fputs("lua_debug> ", stderr); |
|||
if (fgets(buffer, sizeof(buffer), stdin) == 0 || |
|||
strcmp(buffer, "cont\n") == 0) |
|||
return 0; |
|||
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || |
|||
lua_pcall(L, 0, 0, 0)) { |
|||
const char *s = lua_tostring(L, -1); |
|||
fputs(s ? s : "(error object is not a string)", stderr); |
|||
fputs("\n", stderr); |
|||
} |
|||
lua_settop(L, 0); /* remove eventual returns */ |
|||
} |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LEVELS1 12 /* size of the first part of the stack */ |
|||
#define LEVELS2 10 /* size of the second part of the stack */ |
|||
|
|||
LJLIB_CF(debug_traceback) |
|||
{ |
|||
int arg; |
|||
lua_State *L1 = getthread(L, &arg); |
|||
const char *msg = lua_tostring(L, arg+1); |
|||
if (msg == NULL && L->top > L->base+arg) |
|||
L->top = L->base+arg+1; |
|||
else |
|||
luaL_traceback(L, L1, msg, lj_lib_optint(L, arg+2, (L == L1))); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
LUALIB_API int luaopen_debug(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, LUA_DBLIBNAME, debug); |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,870 @@ |
|||
/*
|
|||
** FFI library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#define lib_ffi_c |
|||
#define LUA_LIB |
|||
|
|||
#include <errno.h> |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
|
|||
#if LJ_HASFFI |
|||
|
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_str.h" |
|||
#include "lj_tab.h" |
|||
#include "lj_meta.h" |
|||
#include "lj_ctype.h" |
|||
#include "lj_cparse.h" |
|||
#include "lj_cdata.h" |
|||
#include "lj_cconv.h" |
|||
#include "lj_carith.h" |
|||
#include "lj_ccall.h" |
|||
#include "lj_ccallback.h" |
|||
#include "lj_clib.h" |
|||
#include "lj_strfmt.h" |
|||
#include "lj_ff.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* -- C type checks ------------------------------------------------------- */ |
|||
|
|||
/* Check first argument for a C type and returns its ID. */ |
|||
static CTypeID ffi_checkctype(lua_State *L, CTState *cts, TValue *param) |
|||
{ |
|||
TValue *o = L->base; |
|||
if (!(o < L->top)) { |
|||
err_argtype: |
|||
lj_err_argtype(L, 1, "C type"); |
|||
} |
|||
if (tvisstr(o)) { /* Parse an abstract C type declaration. */ |
|||
GCstr *s = strV(o); |
|||
CPState cp; |
|||
int errcode; |
|||
cp.L = L; |
|||
cp.cts = cts; |
|||
cp.srcname = strdata(s); |
|||
cp.p = strdata(s); |
|||
cp.param = param; |
|||
cp.mode = CPARSE_MODE_ABSTRACT|CPARSE_MODE_NOIMPLICIT; |
|||
errcode = lj_cparse(&cp); |
|||
if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */ |
|||
return cp.val.id; |
|||
} else { |
|||
GCcdata *cd; |
|||
if (!tviscdata(o)) goto err_argtype; |
|||
if (param && param < L->top) lj_err_arg(L, 1, LJ_ERR_FFI_NUMPARAM); |
|||
cd = cdataV(o); |
|||
return cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) : cd->ctypeid; |
|||
} |
|||
} |
|||
|
|||
/* Check argument for C data and return it. */ |
|||
static GCcdata *ffi_checkcdata(lua_State *L, int narg) |
|||
{ |
|||
TValue *o = L->base + narg-1; |
|||
if (!(o < L->top && tviscdata(o))) |
|||
lj_err_argt(L, narg, LUA_TCDATA); |
|||
return cdataV(o); |
|||
} |
|||
|
|||
/* Convert argument to C pointer. */ |
|||
static void *ffi_checkptr(lua_State *L, int narg, CTypeID id) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
TValue *o = L->base + narg-1; |
|||
void *p; |
|||
if (o >= L->top) |
|||
lj_err_arg(L, narg, LJ_ERR_NOVAL); |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, id), (uint8_t *)&p, o, CCF_ARG(narg)); |
|||
return p; |
|||
} |
|||
|
|||
/* Convert argument to int32_t. */ |
|||
static int32_t ffi_checkint(lua_State *L, int narg) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
TValue *o = L->base + narg-1; |
|||
int32_t i; |
|||
if (o >= L->top) |
|||
lj_err_arg(L, narg, LJ_ERR_NOVAL); |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, |
|||
CCF_ARG(narg)); |
|||
return i; |
|||
} |
|||
|
|||
/* -- C type metamethods -------------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_ffi_meta |
|||
|
|||
/* Handle ctype __index/__newindex metamethods. */ |
|||
static int ffi_index_meta(lua_State *L, CTState *cts, CType *ct, MMS mm) |
|||
{ |
|||
CTypeID id = ctype_typeid(cts, ct); |
|||
cTValue *tv = lj_ctype_meta(cts, id, mm); |
|||
TValue *base = L->base; |
|||
if (!tv) { |
|||
const char *s; |
|||
err_index: |
|||
s = strdata(lj_ctype_repr(L, id, NULL)); |
|||
if (tvisstr(L->base+1)) { |
|||
lj_err_callerv(L, LJ_ERR_FFI_BADMEMBER, s, strVdata(L->base+1)); |
|||
} else { |
|||
const char *key = tviscdata(L->base+1) ? |
|||
strdata(lj_ctype_repr(L, cdataV(L->base+1)->ctypeid, NULL)) : |
|||
lj_typename(L->base+1); |
|||
lj_err_callerv(L, LJ_ERR_FFI_BADIDXW, s, key); |
|||
} |
|||
} |
|||
if (!tvisfunc(tv)) { |
|||
if (mm == MM_index) { |
|||
cTValue *o = lj_meta_tget(L, tv, base+1); |
|||
if (o) { |
|||
if (tvisnil(o)) goto err_index; |
|||
copyTV(L, L->top-1, o); |
|||
return 1; |
|||
} |
|||
} else { |
|||
TValue *o = lj_meta_tset(L, tv, base+1); |
|||
if (o) { |
|||
copyTV(L, o, base+2); |
|||
return 0; |
|||
} |
|||
} |
|||
copyTV(L, base, L->top); |
|||
tv = L->top-1-LJ_FR2; |
|||
} |
|||
return lj_meta_tailcall(L, tv); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTInfo qual = 0; |
|||
CType *ct; |
|||
uint8_t *p; |
|||
TValue *o = L->base; |
|||
if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */ |
|||
lj_err_argt(L, 1, LUA_TCDATA); |
|||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual); |
|||
if ((qual & 1)) |
|||
return ffi_index_meta(L, cts, ct, MM_index); |
|||
if (lj_cdata_get(cts, ct, L->top-1, p)) |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___newindex) LJLIB_REC(cdata_index 1) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTInfo qual = 0; |
|||
CType *ct; |
|||
uint8_t *p; |
|||
TValue *o = L->base; |
|||
if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */ |
|||
lj_err_argt(L, 1, LUA_TCDATA); |
|||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual); |
|||
if ((qual & 1)) { |
|||
if ((qual & CTF_CONST)) |
|||
lj_err_caller(L, LJ_ERR_FFI_WRCONST); |
|||
return ffi_index_meta(L, cts, ct, MM_newindex); |
|||
} |
|||
lj_cdata_set(cts, ct, p, o+2, qual); |
|||
return 0; |
|||
} |
|||
|
|||
/* Common handler for cdata arithmetic. */ |
|||
static int ffi_arith(lua_State *L) |
|||
{ |
|||
MMS mm = (MMS)(curr_func(L)->c.ffid - (int)FF_ffi_meta___eq + (int)MM_eq); |
|||
return lj_carith_op(L, mm); |
|||
} |
|||
|
|||
/* The following functions must be in contiguous ORDER MM. */ |
|||
LJLIB_CF(ffi_meta___eq) LJLIB_REC(cdata_arith MM_eq) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___len) LJLIB_REC(cdata_arith MM_len) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___lt) LJLIB_REC(cdata_arith MM_lt) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___le) LJLIB_REC(cdata_arith MM_le) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___concat) LJLIB_REC(cdata_arith MM_concat) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
/* Forward declaration. */ |
|||
static int lj_cf_ffi_new(lua_State *L); |
|||
|
|||
LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
GCcdata *cd = ffi_checkcdata(L, 1); |
|||
CTypeID id = cd->ctypeid; |
|||
CType *ct; |
|||
cTValue *tv; |
|||
MMS mm = MM_call; |
|||
if (cd->ctypeid == CTID_CTYPEID) { |
|||
id = *(CTypeID *)cdataptr(cd); |
|||
mm = MM_new; |
|||
} else { |
|||
int ret = lj_ccall_func(L, cd); |
|||
if (ret >= 0) |
|||
return ret; |
|||
} |
|||
/* Handle ctype __call/__new metamethod. */ |
|||
ct = ctype_raw(cts, id); |
|||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); |
|||
tv = lj_ctype_meta(cts, id, mm); |
|||
if (tv) |
|||
return lj_meta_tailcall(L, tv); |
|||
else if (mm == MM_call) |
|||
lj_err_callerv(L, LJ_ERR_FFI_BADCALL, strdata(lj_ctype_repr(L, id, NULL))); |
|||
return lj_cf_ffi_new(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___add) LJLIB_REC(cdata_arith MM_add) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___sub) LJLIB_REC(cdata_arith MM_sub) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___mul) LJLIB_REC(cdata_arith MM_mul) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___div) LJLIB_REC(cdata_arith MM_div) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___mod) LJLIB_REC(cdata_arith MM_mod) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___pow) LJLIB_REC(cdata_arith MM_pow) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___unm) LJLIB_REC(cdata_arith MM_unm) |
|||
{ |
|||
return ffi_arith(L); |
|||
} |
|||
/* End of contiguous ORDER MM. */ |
|||
|
|||
LJLIB_CF(ffi_meta___tostring) |
|||
{ |
|||
GCcdata *cd = ffi_checkcdata(L, 1); |
|||
const char *msg = "cdata<%s>: %p"; |
|||
CTypeID id = cd->ctypeid; |
|||
void *p = cdataptr(cd); |
|||
if (id == CTID_CTYPEID) { |
|||
msg = "ctype<%s>"; |
|||
id = *(CTypeID *)p; |
|||
} else { |
|||
CTState *cts = ctype_cts(L); |
|||
CType *ct = ctype_raw(cts, id); |
|||
if (ctype_isref(ct->info)) { |
|||
p = *(void **)p; |
|||
ct = ctype_rawchild(cts, ct); |
|||
} |
|||
if (ctype_iscomplex(ct->info)) { |
|||
setstrV(L, L->top-1, lj_ctype_repr_complex(L, cdataptr(cd), ct->size)); |
|||
goto checkgc; |
|||
} else if (ct->size == 8 && ctype_isinteger(ct->info)) { |
|||
setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd), |
|||
(ct->info & CTF_UNSIGNED))); |
|||
goto checkgc; |
|||
} else if (ctype_isfunc(ct->info)) { |
|||
p = *(void **)p; |
|||
} else if (ctype_isenum(ct->info)) { |
|||
msg = "cdata<%s>: %d"; |
|||
p = (void *)(uintptr_t)*(uint32_t **)p; |
|||
} else { |
|||
if (ctype_isptr(ct->info)) { |
|||
p = cdata_getptr(p, ct->size); |
|||
ct = ctype_rawchild(cts, ct); |
|||
} |
|||
if (ctype_isstruct(ct->info) || ctype_isvector(ct->info)) { |
|||
/* Handle ctype __tostring metamethod. */ |
|||
cTValue *tv = lj_ctype_meta(cts, ctype_typeid(cts, ct), MM_tostring); |
|||
if (tv) |
|||
return lj_meta_tailcall(L, tv); |
|||
} |
|||
} |
|||
} |
|||
lj_strfmt_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), p); |
|||
checkgc: |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
static int ffi_pairs(lua_State *L, MMS mm) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkcdata(L, 1)->ctypeid; |
|||
CType *ct = ctype_raw(cts, id); |
|||
cTValue *tv; |
|||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info); |
|||
tv = lj_ctype_meta(cts, id, mm); |
|||
if (!tv) |
|||
lj_err_callerv(L, LJ_ERR_FFI_BADMM, strdata(lj_ctype_repr(L, id, NULL)), |
|||
strdata(mmname_str(G(L), mm))); |
|||
return lj_meta_tailcall(L, tv); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___pairs) |
|||
{ |
|||
return ffi_pairs(L, MM_pairs); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_meta___ipairs) |
|||
{ |
|||
return ffi_pairs(L, MM_ipairs); |
|||
} |
|||
|
|||
LJLIB_PUSH("ffi") LJLIB_SET(__metatable) |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* -- C library metamethods ----------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_ffi_clib |
|||
|
|||
/* Index C library by a name. */ |
|||
static TValue *ffi_clib_index(lua_State *L) |
|||
{ |
|||
TValue *o = L->base; |
|||
CLibrary *cl; |
|||
if (!(o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB)) |
|||
lj_err_argt(L, 1, LUA_TUSERDATA); |
|||
cl = (CLibrary *)uddata(udataV(o)); |
|||
if (!(o+1 < L->top && tvisstr(o+1))) |
|||
lj_err_argt(L, 2, LUA_TSTRING); |
|||
return lj_clib_index(L, cl, strV(o+1)); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_clib___index) LJLIB_REC(clib_index 1) |
|||
{ |
|||
TValue *tv = ffi_clib_index(L); |
|||
if (tviscdata(tv)) { |
|||
CTState *cts = ctype_cts(L); |
|||
GCcdata *cd = cdataV(tv); |
|||
CType *s = ctype_get(cts, cd->ctypeid); |
|||
if (ctype_isextern(s->info)) { |
|||
CTypeID sid = ctype_cid(s->info); |
|||
void *sp = *(void **)cdataptr(cd); |
|||
CType *ct = ctype_raw(cts, sid); |
|||
if (lj_cconv_tv_ct(cts, ct, sid, L->top-1, sp)) |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
} |
|||
copyTV(L, L->top-1, tv); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_clib___newindex) LJLIB_REC(clib_index 0) |
|||
{ |
|||
TValue *tv = ffi_clib_index(L); |
|||
TValue *o = L->base+2; |
|||
if (o < L->top && tviscdata(tv)) { |
|||
CTState *cts = ctype_cts(L); |
|||
GCcdata *cd = cdataV(tv); |
|||
CType *d = ctype_get(cts, cd->ctypeid); |
|||
if (ctype_isextern(d->info)) { |
|||
CTInfo qual = 0; |
|||
for (;;) { /* Skip attributes and collect qualifiers. */ |
|||
d = ctype_child(cts, d); |
|||
if (!ctype_isattrib(d->info)) break; |
|||
if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size; |
|||
} |
|||
if (!((d->info|qual) & CTF_CONST)) { |
|||
lj_cconv_ct_tv(cts, d, *(void **)cdataptr(cd), o, 0); |
|||
return 0; |
|||
} |
|||
} |
|||
} |
|||
lj_err_caller(L, LJ_ERR_FFI_WRCONST); |
|||
return 0; /* unreachable */ |
|||
} |
|||
|
|||
LJLIB_CF(ffi_clib___gc) |
|||
{ |
|||
TValue *o = L->base; |
|||
if (o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB) |
|||
lj_clib_unload((CLibrary *)uddata(udataV(o))); |
|||
return 0; |
|||
} |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* -- Callback function metamethods --------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_ffi_callback |
|||
|
|||
static int ffi_callback_set(lua_State *L, GCfunc *fn) |
|||
{ |
|||
GCcdata *cd = ffi_checkcdata(L, 1); |
|||
CTState *cts = ctype_cts(L); |
|||
CType *ct = ctype_raw(cts, cd->ctypeid); |
|||
if (ctype_isptr(ct->info) && (LJ_32 || ct->size == 8)) { |
|||
MSize slot = lj_ccallback_ptr2slot(cts, *(void **)cdataptr(cd)); |
|||
if (slot < cts->cb.sizeid && cts->cb.cbid[slot] != 0) { |
|||
GCtab *t = cts->miscmap; |
|||
TValue *tv = lj_tab_setint(L, t, (int32_t)slot); |
|||
if (fn) { |
|||
setfuncV(L, tv, fn); |
|||
lj_gc_anybarriert(L, t); |
|||
} else { |
|||
setnilV(tv); |
|||
cts->cb.cbid[slot] = 0; |
|||
cts->cb.topid = slot < cts->cb.topid ? slot : cts->cb.topid; |
|||
} |
|||
return 0; |
|||
} |
|||
} |
|||
lj_err_caller(L, LJ_ERR_FFI_BADCBACK); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_callback_free) |
|||
{ |
|||
return ffi_callback_set(L, NULL); |
|||
} |
|||
|
|||
LJLIB_CF(ffi_callback_set) |
|||
{ |
|||
GCfunc *fn = lj_lib_checkfunc(L, 2); |
|||
return ffi_callback_set(L, fn); |
|||
} |
|||
|
|||
LJLIB_PUSH(top-1) LJLIB_SET(__index) |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* -- FFI library functions ----------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_ffi |
|||
|
|||
LJLIB_CF(ffi_cdef) |
|||
{ |
|||
GCstr *s = lj_lib_checkstr(L, 1); |
|||
CPState cp; |
|||
int errcode; |
|||
cp.L = L; |
|||
cp.cts = ctype_cts(L); |
|||
cp.srcname = strdata(s); |
|||
cp.p = strdata(s); |
|||
cp.param = L->base+1; |
|||
cp.mode = CPARSE_MODE_MULTI|CPARSE_MODE_DIRECT; |
|||
errcode = lj_cparse(&cp); |
|||
if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */ |
|||
lj_gc_check(L); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_new) LJLIB_REC(.) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, NULL); |
|||
CType *ct = ctype_raw(cts, id); |
|||
CTSize sz; |
|||
CTInfo info = lj_ctype_info(cts, id, &sz); |
|||
TValue *o = L->base+1; |
|||
GCcdata *cd; |
|||
if ((info & CTF_VLA)) { |
|||
o++; |
|||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2)); |
|||
} |
|||
if (sz == CTSIZE_INVALID) |
|||
lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE); |
|||
cd = lj_cdata_newx(cts, id, sz, info); |
|||
setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */ |
|||
lj_cconv_ct_init(cts, ct, sz, cdataptr(cd), |
|||
o, (MSize)(L->top - o)); /* Initialize cdata. */ |
|||
if (ctype_isstruct(ct->info)) { |
|||
/* Handle ctype __gc metamethod. Use the fast lookup here. */ |
|||
cTValue *tv = lj_tab_getinth(cts->miscmap, -(int32_t)id); |
|||
if (tv && tvistab(tv) && (tv = lj_meta_fast(L, tabV(tv), MM_gc))) { |
|||
GCtab *t = cts->finalizer; |
|||
if (gcref(t->metatable)) { |
|||
/* Add to finalizer table, if still enabled. */ |
|||
copyTV(L, lj_tab_set(L, t, o-1), tv); |
|||
lj_gc_anybarriert(L, t); |
|||
cd->marked |= LJ_GC_CDATA_FIN; |
|||
} |
|||
} |
|||
} |
|||
L->top = o; /* Only return the cdata itself. */ |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_cast) LJLIB_REC(ffi_new) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, NULL); |
|||
CType *d = ctype_raw(cts, id); |
|||
TValue *o = lj_lib_checkany(L, 2); |
|||
L->top = o+1; /* Make sure this is the last item on the stack. */ |
|||
if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info))) |
|||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); |
|||
if (!(tviscdata(o) && cdataV(o)->ctypeid == id)) { |
|||
GCcdata *cd = lj_cdata_new(cts, id, d->size); |
|||
lj_cconv_ct_tv(cts, d, cdataptr(cd), o, CCF_CAST); |
|||
setcdataV(L, o, cd); |
|||
lj_gc_check(L); |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_typeof) LJLIB_REC(.) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, L->base+1); |
|||
GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4); |
|||
*(CTypeID *)cdataptr(cd) = id; |
|||
setcdataV(L, L->top-1, cd); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
/* Internal and unsupported API. */ |
|||
LJLIB_CF(ffi_typeinfo) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = (CTypeID)ffi_checkint(L, 1); |
|||
if (id > 0 && id < cts->top) { |
|||
CType *ct = ctype_get(cts, id); |
|||
GCtab *t; |
|||
lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */ |
|||
t = tabV(L->top-1); |
|||
setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "info")), (int32_t)ct->info); |
|||
if (ct->size != CTSIZE_INVALID) |
|||
setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "size")), (int32_t)ct->size); |
|||
if (ct->sib) |
|||
setintV(lj_tab_setstr(L, t, lj_str_newlit(L, "sib")), (int32_t)ct->sib); |
|||
if (gcref(ct->name)) { |
|||
GCstr *s = gco2str(gcref(ct->name)); |
|||
if (isdead(G(L), obj2gco(s))) flipwhite(obj2gco(s)); |
|||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "name")), s); |
|||
} |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_istype) LJLIB_REC(.) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id1 = ffi_checkctype(L, cts, NULL); |
|||
TValue *o = lj_lib_checkany(L, 2); |
|||
int b = 0; |
|||
if (tviscdata(o)) { |
|||
GCcdata *cd = cdataV(o); |
|||
CTypeID id2 = cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) : |
|||
cd->ctypeid; |
|||
CType *ct1 = lj_ctype_rawref(cts, id1); |
|||
CType *ct2 = lj_ctype_rawref(cts, id2); |
|||
if (ct1 == ct2) { |
|||
b = 1; |
|||
} else if (ctype_type(ct1->info) == ctype_type(ct2->info) && |
|||
ct1->size == ct2->size) { |
|||
if (ctype_ispointer(ct1->info)) |
|||
b = lj_cconv_compatptr(cts, ct1, ct2, CCF_IGNQUAL); |
|||
else if (ctype_isnum(ct1->info) || ctype_isvoid(ct1->info)) |
|||
b = (((ct1->info ^ ct2->info) & ~(CTF_QUAL|CTF_LONG)) == 0); |
|||
} else if (ctype_isstruct(ct1->info) && ctype_isptr(ct2->info) && |
|||
ct1 == ctype_rawchild(cts, ct2)) { |
|||
b = 1; |
|||
} |
|||
} |
|||
setboolV(L->top-1, b); |
|||
setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_sizeof) LJLIB_REC(ffi_xof FF_ffi_sizeof) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, NULL); |
|||
CTSize sz; |
|||
if (LJ_UNLIKELY(tviscdata(L->base) && cdataisv(cdataV(L->base)))) { |
|||
sz = cdatavlen(cdataV(L->base)); |
|||
} else { |
|||
CType *ct = lj_ctype_rawref(cts, id); |
|||
if (ctype_isvltype(ct->info)) |
|||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2)); |
|||
else |
|||
sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_INVALID; |
|||
if (LJ_UNLIKELY(sz == CTSIZE_INVALID)) { |
|||
setnilV(L->top-1); |
|||
return 1; |
|||
} |
|||
} |
|||
setintV(L->top-1, (int32_t)sz); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_alignof) LJLIB_REC(ffi_xof FF_ffi_alignof) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, NULL); |
|||
CTSize sz = 0; |
|||
CTInfo info = lj_ctype_info(cts, id, &sz); |
|||
setintV(L->top-1, 1 << ctype_align(info)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_offsetof) LJLIB_REC(ffi_xof FF_ffi_offsetof) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, NULL); |
|||
GCstr *name = lj_lib_checkstr(L, 2); |
|||
CType *ct = lj_ctype_rawref(cts, id); |
|||
CTSize ofs; |
|||
if (ctype_isstruct(ct->info) && ct->size != CTSIZE_INVALID) { |
|||
CType *fct = lj_ctype_getfield(cts, ct, name, &ofs); |
|||
if (fct) { |
|||
setintV(L->top-1, ofs); |
|||
if (ctype_isfield(fct->info)) { |
|||
return 1; |
|||
} else if (ctype_isbitfield(fct->info)) { |
|||
setintV(L->top++, ctype_bitpos(fct->info)); |
|||
setintV(L->top++, ctype_bitbsz(fct->info)); |
|||
return 3; |
|||
} |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_errno) LJLIB_REC(.) |
|||
{ |
|||
int err = errno; |
|||
if (L->top > L->base) |
|||
errno = ffi_checkint(L, 1); |
|||
setintV(L->top++, err); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_string) LJLIB_REC(.) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
TValue *o = lj_lib_checkany(L, 1); |
|||
const char *p; |
|||
size_t len; |
|||
if (o+1 < L->top && !tvisnil(o+1)) { |
|||
len = (size_t)ffi_checkint(L, 2); |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, o, |
|||
CCF_ARG(1)); |
|||
} else { |
|||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CCHAR), (uint8_t *)&p, o, |
|||
CCF_ARG(1)); |
|||
len = strlen(p); |
|||
} |
|||
L->top = o+1; /* Make sure this is the last item on the stack. */ |
|||
setstrV(L, o, lj_str_new(L, p, len)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_copy) LJLIB_REC(.) |
|||
{ |
|||
void *dp = ffi_checkptr(L, 1, CTID_P_VOID); |
|||
void *sp = ffi_checkptr(L, 2, CTID_P_CVOID); |
|||
TValue *o = L->base+1; |
|||
CTSize len; |
|||
if (tvisstr(o) && o+1 >= L->top) |
|||
len = strV(o)->len+1; /* Copy Lua string including trailing '\0'. */ |
|||
else |
|||
len = (CTSize)ffi_checkint(L, 3); |
|||
memcpy(dp, sp, len); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(ffi_fill) LJLIB_REC(.) |
|||
{ |
|||
void *dp = ffi_checkptr(L, 1, CTID_P_VOID); |
|||
CTSize len = (CTSize)ffi_checkint(L, 2); |
|||
int32_t fill = 0; |
|||
if (L->base+2 < L->top && !tvisnil(L->base+2)) fill = ffi_checkint(L, 3); |
|||
memset(dp, fill, len); |
|||
return 0; |
|||
} |
|||
|
|||
/* Test ABI string. */ |
|||
LJLIB_CF(ffi_abi) LJLIB_REC(.) |
|||
{ |
|||
GCstr *s = lj_lib_checkstr(L, 1); |
|||
int b = lj_cparse_case(s, |
|||
#if LJ_64 |
|||
"\00564bit" |
|||
#else |
|||
"\00532bit" |
|||
#endif |
|||
#if LJ_ARCH_HASFPU |
|||
"\003fpu" |
|||
#endif |
|||
#if LJ_ABI_SOFTFP |
|||
"\006softfp" |
|||
#else |
|||
"\006hardfp" |
|||
#endif |
|||
#if LJ_ABI_EABI |
|||
"\004eabi" |
|||
#endif |
|||
#if LJ_ABI_WIN |
|||
"\003win" |
|||
#endif |
|||
#if LJ_TARGET_UWP |
|||
"\003uwp" |
|||
#endif |
|||
#if LJ_LE |
|||
"\002le" |
|||
#else |
|||
"\002be" |
|||
#endif |
|||
#if LJ_GC64 |
|||
"\004gc64" |
|||
#endif |
|||
) >= 0; |
|||
setboolV(L->top-1, b); |
|||
setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-8) LJLIB_SET(!) /* Store reference to miscmap table. */ |
|||
|
|||
LJLIB_CF(ffi_metatype) |
|||
{ |
|||
CTState *cts = ctype_cts(L); |
|||
CTypeID id = ffi_checkctype(L, cts, NULL); |
|||
GCtab *mt = lj_lib_checktab(L, 2); |
|||
GCtab *t = cts->miscmap; |
|||
CType *ct = ctype_get(cts, id); /* Only allow raw types. */ |
|||
TValue *tv; |
|||
GCcdata *cd; |
|||
if (!(ctype_isstruct(ct->info) || ctype_iscomplex(ct->info) || |
|||
ctype_isvector(ct->info))) |
|||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); |
|||
tv = lj_tab_setinth(L, t, -(int32_t)id); |
|||
if (!tvisnil(tv)) |
|||
lj_err_caller(L, LJ_ERR_PROTMT); |
|||
settabV(L, tv, mt); |
|||
lj_gc_anybarriert(L, t); |
|||
cd = lj_cdata_new(cts, CTID_CTYPEID, 4); |
|||
*(CTypeID *)cdataptr(cd) = id; |
|||
setcdataV(L, L->top-1, cd); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to finalizer table. */ |
|||
|
|||
LJLIB_CF(ffi_gc) LJLIB_REC(.) |
|||
{ |
|||
GCcdata *cd = ffi_checkcdata(L, 1); |
|||
TValue *fin = lj_lib_checkany(L, 2); |
|||
CTState *cts = ctype_cts(L); |
|||
CType *ct = ctype_raw(cts, cd->ctypeid); |
|||
if (!(ctype_isptr(ct->info) || ctype_isstruct(ct->info) || |
|||
ctype_isrefarray(ct->info))) |
|||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE); |
|||
lj_cdata_setfin(L, cd, gcval(fin), itype(fin)); |
|||
L->top = L->base+1; /* Pass through the cdata object. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */ |
|||
|
|||
LJLIB_CF(ffi_load) |
|||
{ |
|||
GCstr *name = lj_lib_checkstr(L, 1); |
|||
int global = (L->base+1 < L->top && tvistruecond(L->base+1)); |
|||
lj_clib_load(L, tabref(curr_func(L)->c.env), name, global); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-4) LJLIB_SET(C) |
|||
LJLIB_PUSH(top-3) LJLIB_SET(os) |
|||
LJLIB_PUSH(top-2) LJLIB_SET(arch) |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* Create special weak-keyed finalizer table. */ |
|||
static GCtab *ffi_finalizer(lua_State *L) |
|||
{ |
|||
/* NOBARRIER: The table is new (marked white). */ |
|||
GCtab *t = lj_tab_new(L, 0, 1); |
|||
settabV(L, L->top++, t); |
|||
setgcref(t->metatable, obj2gco(t)); |
|||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")), |
|||
lj_str_newlit(L, "k")); |
|||
t->nomm = (uint8_t)(~(1u<<MM_mode)); |
|||
return t; |
|||
} |
|||
|
|||
/* Register FFI module as loaded. */ |
|||
static void ffi_register_module(lua_State *L) |
|||
{ |
|||
cTValue *tmp = lj_tab_getstr(tabV(registry(L)), lj_str_newlit(L, "_LOADED")); |
|||
if (tmp && tvistab(tmp)) { |
|||
GCtab *t = tabV(tmp); |
|||
copyTV(L, lj_tab_setstr(L, t, lj_str_newlit(L, LUA_FFILIBNAME)), L->top-1); |
|||
lj_gc_anybarriert(L, t); |
|||
} |
|||
} |
|||
|
|||
LUALIB_API int luaopen_ffi(lua_State *L) |
|||
{ |
|||
CTState *cts = lj_ctype_init(L); |
|||
settabV(L, L->top++, (cts->miscmap = lj_tab_new(L, 0, 1))); |
|||
cts->finalizer = ffi_finalizer(L); |
|||
LJ_LIB_REG(L, NULL, ffi_meta); |
|||
/* NOBARRIER: basemt is a GC root. */ |
|||
setgcref(basemt_it(G(L), LJ_TCDATA), obj2gco(tabV(L->top-1))); |
|||
LJ_LIB_REG(L, NULL, ffi_clib); |
|||
LJ_LIB_REG(L, NULL, ffi_callback); |
|||
/* NOBARRIER: the key is new and lj_tab_newkey() handles the barrier. */ |
|||
settabV(L, lj_tab_setstr(L, cts->miscmap, &cts->g->strempty), tabV(L->top-1)); |
|||
L->top--; |
|||
lj_clib_default(L, tabV(L->top-1)); /* Create ffi.C default namespace. */ |
|||
lua_pushliteral(L, LJ_OS_NAME); |
|||
lua_pushliteral(L, LJ_ARCH_NAME); |
|||
LJ_LIB_REG(L, NULL, ffi); /* Note: no global "ffi" created! */ |
|||
ffi_register_module(L); |
|||
return 1; |
|||
} |
|||
|
|||
#endif |
@ -0,0 +1,55 @@ |
|||
/*
|
|||
** Library initialization. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major parts taken verbatim from the Lua interpreter. |
|||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#define lib_init_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_arch.h" |
|||
|
|||
static const luaL_Reg lj_lib_load[] = { |
|||
{ "", luaopen_base }, |
|||
{ LUA_LOADLIBNAME, luaopen_package }, |
|||
{ LUA_TABLIBNAME, luaopen_table }, |
|||
{ LUA_IOLIBNAME, luaopen_io }, |
|||
{ LUA_OSLIBNAME, luaopen_os }, |
|||
{ LUA_STRLIBNAME, luaopen_string }, |
|||
{ LUA_MATHLIBNAME, luaopen_math }, |
|||
{ LUA_DBLIBNAME, luaopen_debug }, |
|||
{ LUA_BITLIBNAME, luaopen_bit }, |
|||
{ LUA_JITLIBNAME, luaopen_jit }, |
|||
{ NULL, NULL } |
|||
}; |
|||
|
|||
static const luaL_Reg lj_lib_preload[] = { |
|||
#if LJ_HASFFI |
|||
{ LUA_FFILIBNAME, luaopen_ffi }, |
|||
#endif |
|||
{ NULL, NULL } |
|||
}; |
|||
|
|||
LUALIB_API void luaL_openlibs(lua_State *L) |
|||
{ |
|||
const luaL_Reg *lib; |
|||
for (lib = lj_lib_load; lib->func; lib++) { |
|||
lua_pushcfunction(L, lib->func); |
|||
lua_pushstring(L, lib->name); |
|||
lua_call(L, 1, 0); |
|||
} |
|||
luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", |
|||
sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1); |
|||
for (lib = lj_lib_preload; lib->func; lib++) { |
|||
lua_pushcfunction(L, lib->func); |
|||
lua_setfield(L, -2, lib->name); |
|||
} |
|||
lua_pop(L, 1); |
|||
} |
|||
|
@ -0,0 +1,551 @@ |
|||
/*
|
|||
** I/O library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#include <errno.h> |
|||
#include <stdio.h> |
|||
|
|||
#define lib_io_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_str.h" |
|||
#include "lj_state.h" |
|||
#include "lj_strfmt.h" |
|||
#include "lj_ff.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* Userdata payload for I/O file. */ |
|||
typedef struct IOFileUD { |
|||
FILE *fp; /* File handle. */ |
|||
uint32_t type; /* File type. */ |
|||
} IOFileUD; |
|||
|
|||
#define IOFILE_TYPE_FILE 0 /* Regular file. */ |
|||
#define IOFILE_TYPE_PIPE 1 /* Pipe. */ |
|||
#define IOFILE_TYPE_STDF 2 /* Standard file handle. */ |
|||
#define IOFILE_TYPE_MASK 3 |
|||
|
|||
#define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */ |
|||
|
|||
#define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud) |
|||
#define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id)))) |
|||
|
|||
/* -- Open/close helpers -------------------------------------------------- */ |
|||
|
|||
static IOFileUD *io_tofilep(lua_State *L) |
|||
{ |
|||
if (!(L->base < L->top && tvisudata(L->base) && |
|||
udataV(L->base)->udtype == UDTYPE_IO_FILE)) |
|||
lj_err_argtype(L, 1, "FILE*"); |
|||
return (IOFileUD *)uddata(udataV(L->base)); |
|||
} |
|||
|
|||
static IOFileUD *io_tofile(lua_State *L) |
|||
{ |
|||
IOFileUD *iof = io_tofilep(L); |
|||
if (iof->fp == NULL) |
|||
lj_err_caller(L, LJ_ERR_IOCLFL); |
|||
return iof; |
|||
} |
|||
|
|||
static IOFileUD *io_stdfile(lua_State *L, ptrdiff_t id) |
|||
{ |
|||
IOFileUD *iof = IOSTDF_IOF(L, id); |
|||
if (iof->fp == NULL) |
|||
lj_err_caller(L, LJ_ERR_IOSTDCL); |
|||
return iof; |
|||
} |
|||
|
|||
static IOFileUD *io_file_new(lua_State *L) |
|||
{ |
|||
IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD)); |
|||
GCudata *ud = udataV(L->top-1); |
|||
ud->udtype = UDTYPE_IO_FILE; |
|||
/* NOBARRIER: The GCudata is new (marked white). */ |
|||
setgcrefr(ud->metatable, curr_func(L)->c.env); |
|||
iof->fp = NULL; |
|||
iof->type = IOFILE_TYPE_FILE; |
|||
return iof; |
|||
} |
|||
|
|||
static IOFileUD *io_file_open(lua_State *L, const char *mode) |
|||
{ |
|||
const char *fname = strdata(lj_lib_checkstr(L, 1)); |
|||
IOFileUD *iof = io_file_new(L); |
|||
iof->fp = fopen(fname, mode); |
|||
if (iof->fp == NULL) |
|||
luaL_argerror(L, 1, lj_strfmt_pushf(L, "%s: %s", fname, strerror(errno))); |
|||
return iof; |
|||
} |
|||
|
|||
static int io_file_close(lua_State *L, IOFileUD *iof) |
|||
{ |
|||
int ok; |
|||
if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) { |
|||
ok = (fclose(iof->fp) == 0); |
|||
} else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) { |
|||
int stat = -1; |
|||
#if LJ_TARGET_POSIX |
|||
stat = pclose(iof->fp); |
|||
#elif LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP |
|||
stat = _pclose(iof->fp); |
|||
#endif |
|||
#if LJ_52 |
|||
iof->fp = NULL; |
|||
return luaL_execresult(L, stat); |
|||
#else |
|||
ok = (stat != -1); |
|||
#endif |
|||
} else { |
|||
lj_assertL((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF, |
|||
"close of unknown FILE* type"); |
|||
setnilV(L->top++); |
|||
lua_pushliteral(L, "cannot close standard file"); |
|||
return 2; |
|||
} |
|||
iof->fp = NULL; |
|||
return luaL_fileresult(L, ok, NULL); |
|||
} |
|||
|
|||
/* -- Read/write helpers -------------------------------------------------- */ |
|||
|
|||
static int io_file_readnum(lua_State *L, FILE *fp) |
|||
{ |
|||
lua_Number d; |
|||
if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) { |
|||
if (LJ_DUALNUM) { |
|||
int32_t i = lj_num2int(d); |
|||
if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) { |
|||
setintV(L->top++, i); |
|||
return 1; |
|||
} |
|||
} |
|||
setnumV(L->top++, d); |
|||
return 1; |
|||
} else { |
|||
setnilV(L->top++); |
|||
return 0; |
|||
} |
|||
} |
|||
|
|||
static int io_file_readline(lua_State *L, FILE *fp, MSize chop) |
|||
{ |
|||
MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0; |
|||
char *buf; |
|||
for (;;) { |
|||
buf = lj_buf_tmp(L, m); |
|||
if (fgets(buf+n, m-n, fp) == NULL) break; |
|||
n += (MSize)strlen(buf+n); |
|||
ok |= n; |
|||
if (n && buf[n-1] == '\n') { n -= chop; break; } |
|||
if (n >= m - 64) m += m; |
|||
} |
|||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); |
|||
lj_gc_check(L); |
|||
return (int)ok; |
|||
} |
|||
|
|||
static void io_file_readall(lua_State *L, FILE *fp) |
|||
{ |
|||
MSize m, n; |
|||
for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) { |
|||
char *buf = lj_buf_tmp(L, m); |
|||
n += (MSize)fread(buf+n, 1, m-n, fp); |
|||
if (n != m) { |
|||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); |
|||
lj_gc_check(L); |
|||
return; |
|||
} |
|||
} |
|||
} |
|||
|
|||
static int io_file_readlen(lua_State *L, FILE *fp, MSize m) |
|||
{ |
|||
if (m) { |
|||
char *buf = lj_buf_tmp(L, m); |
|||
MSize n = (MSize)fread(buf, 1, m, fp); |
|||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n)); |
|||
lj_gc_check(L); |
|||
return n > 0; |
|||
} else { |
|||
int c = getc(fp); |
|||
ungetc(c, fp); |
|||
setstrV(L, L->top++, &G(L)->strempty); |
|||
return (c != EOF); |
|||
} |
|||
} |
|||
|
|||
static int io_file_read(lua_State *L, IOFileUD *iof, int start) |
|||
{ |
|||
FILE *fp = iof->fp; |
|||
int ok, n, nargs = (int)(L->top - L->base) - start; |
|||
clearerr(fp); |
|||
if (nargs == 0) { |
|||
ok = io_file_readline(L, fp, 1); |
|||
n = start+1; /* Return 1 result. */ |
|||
} else { |
|||
/* The results plus the buffers go on top of the args. */ |
|||
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); |
|||
ok = 1; |
|||
for (n = start; nargs-- && ok; n++) { |
|||
if (tvisstr(L->base+n)) { |
|||
const char *p = strVdata(L->base+n); |
|||
if (p[0] == '*') p++; |
|||
if (p[0] == 'n') |
|||
ok = io_file_readnum(L, fp); |
|||
else if ((p[0] & ~0x20) == 'L') |
|||
ok = io_file_readline(L, fp, (p[0] == 'l')); |
|||
else if (p[0] == 'a') |
|||
io_file_readall(L, fp); |
|||
else |
|||
lj_err_arg(L, n+1, LJ_ERR_INVFMT); |
|||
} else if (tvisnumber(L->base+n)) { |
|||
ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1)); |
|||
} else { |
|||
lj_err_arg(L, n+1, LJ_ERR_INVOPT); |
|||
} |
|||
} |
|||
} |
|||
if (ferror(fp)) |
|||
return luaL_fileresult(L, 0, NULL); |
|||
if (!ok) |
|||
setnilV(L->top-1); /* Replace last result with nil. */ |
|||
return n - start; |
|||
} |
|||
|
|||
static int io_file_write(lua_State *L, IOFileUD *iof, int start) |
|||
{ |
|||
FILE *fp = iof->fp; |
|||
cTValue *tv; |
|||
int status = 1; |
|||
for (tv = L->base+start; tv < L->top; tv++) { |
|||
MSize len; |
|||
const char *p = lj_strfmt_wstrnum(L, tv, &len); |
|||
if (!p) |
|||
lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING); |
|||
status = status && (fwrite(p, 1, len, fp) == len); |
|||
} |
|||
if (LJ_52 && status) { |
|||
L->top = L->base+1; |
|||
if (start == 0) |
|||
setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT)); |
|||
return 1; |
|||
} |
|||
return luaL_fileresult(L, status, NULL); |
|||
} |
|||
|
|||
static int io_file_iter(lua_State *L) |
|||
{ |
|||
GCfunc *fn = curr_func(L); |
|||
IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0])); |
|||
int n = fn->c.nupvalues - 1; |
|||
if (iof->fp == NULL) |
|||
lj_err_caller(L, LJ_ERR_IOCLFL); |
|||
L->top = L->base; |
|||
if (n) { /* Copy upvalues with options to stack. */ |
|||
lj_state_checkstack(L, (MSize)n); |
|||
memcpy(L->top, &fn->c.upvalue[1], n*sizeof(TValue)); |
|||
L->top += n; |
|||
} |
|||
n = io_file_read(L, iof, 0); |
|||
if (ferror(iof->fp)) |
|||
lj_err_callermsg(L, strVdata(L->top-2)); |
|||
if (tvisnil(L->base) && (iof->type & IOFILE_FLAG_CLOSE)) { |
|||
io_file_close(L, iof); /* Return values are ignored. */ |
|||
return 0; |
|||
} |
|||
return n; |
|||
} |
|||
|
|||
static int io_file_lines(lua_State *L) |
|||
{ |
|||
int n = (int)(L->top - L->base); |
|||
if (n > LJ_MAX_UPVAL) |
|||
lj_err_caller(L, LJ_ERR_UNPACK); |
|||
lua_pushcclosure(L, io_file_iter, n); |
|||
return 1; |
|||
} |
|||
|
|||
/* -- I/O file methods ---------------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_io_method |
|||
|
|||
LJLIB_CF(io_method_close) |
|||
{ |
|||
IOFileUD *iof; |
|||
if (L->base < L->top) { |
|||
iof = io_tofile(L); |
|||
} else { |
|||
iof = IOSTDF_IOF(L, GCROOT_IO_OUTPUT); |
|||
if (iof->fp == NULL) |
|||
lj_err_caller(L, LJ_ERR_IOCLFL); |
|||
} |
|||
return io_file_close(L, iof); |
|||
} |
|||
|
|||
LJLIB_CF(io_method_read) |
|||
{ |
|||
return io_file_read(L, io_tofile(L), 1); |
|||
} |
|||
|
|||
LJLIB_CF(io_method_write) LJLIB_REC(io_write 0) |
|||
{ |
|||
return io_file_write(L, io_tofile(L), 1); |
|||
} |
|||
|
|||
LJLIB_CF(io_method_flush) LJLIB_REC(io_flush 0) |
|||
{ |
|||
return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL); |
|||
} |
|||
|
|||
#if LJ_32 && defined(__ANDROID__) && __ANDROID_API__ < 24 |
|||
/* The Android NDK is such an unmatched marvel of engineering. */ |
|||
extern int fseeko32(FILE *, long int, int) __asm__("fseeko"); |
|||
extern long int ftello32(FILE *) __asm__("ftello"); |
|||
#define fseeko(fp, pos, whence) (fseeko32((fp), (pos), (whence))) |
|||
#define ftello(fp) (ftello32((fp))) |
|||
#endif |
|||
|
|||
LJLIB_CF(io_method_seek) |
|||
{ |
|||
FILE *fp = io_tofile(L)->fp; |
|||
int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end"); |
|||
int64_t ofs = 0; |
|||
cTValue *o; |
|||
int res; |
|||
if (opt == 0) opt = SEEK_SET; |
|||
else if (opt == 1) opt = SEEK_CUR; |
|||
else if (opt == 2) opt = SEEK_END; |
|||
o = L->base+2; |
|||
if (o < L->top) { |
|||
if (tvisint(o)) |
|||
ofs = (int64_t)intV(o); |
|||
else if (tvisnum(o)) |
|||
ofs = (int64_t)numV(o); |
|||
else if (!tvisnil(o)) |
|||
lj_err_argt(L, 3, LUA_TNUMBER); |
|||
} |
|||
#if LJ_TARGET_POSIX |
|||
res = fseeko(fp, ofs, opt); |
|||
#elif _MSC_VER >= 1400 |
|||
res = _fseeki64(fp, ofs, opt); |
|||
#elif defined(__MINGW32__) |
|||
res = fseeko64(fp, ofs, opt); |
|||
#else |
|||
res = fseek(fp, (long)ofs, opt); |
|||
#endif |
|||
if (res) |
|||
return luaL_fileresult(L, 0, NULL); |
|||
#if LJ_TARGET_POSIX |
|||
ofs = ftello(fp); |
|||
#elif _MSC_VER >= 1400 |
|||
ofs = _ftelli64(fp); |
|||
#elif defined(__MINGW32__) |
|||
ofs = ftello64(fp); |
|||
#else |
|||
ofs = (int64_t)ftell(fp); |
|||
#endif |
|||
setint64V(L->top-1, ofs); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(io_method_setvbuf) |
|||
{ |
|||
FILE *fp = io_tofile(L)->fp; |
|||
int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no"); |
|||
size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE); |
|||
if (opt == 0) opt = _IOFBF; |
|||
else if (opt == 1) opt = _IOLBF; |
|||
else if (opt == 2) opt = _IONBF; |
|||
return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL); |
|||
} |
|||
|
|||
LJLIB_CF(io_method_lines) |
|||
{ |
|||
io_tofile(L); |
|||
return io_file_lines(L); |
|||
} |
|||
|
|||
LJLIB_CF(io_method___gc) |
|||
{ |
|||
IOFileUD *iof = io_tofilep(L); |
|||
if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF) |
|||
io_file_close(L, iof); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(io_method___tostring) |
|||
{ |
|||
IOFileUD *iof = io_tofilep(L); |
|||
if (iof->fp != NULL) |
|||
lua_pushfstring(L, "file (%p)", iof->fp); |
|||
else |
|||
lua_pushliteral(L, "file (closed)"); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-1) LJLIB_SET(__index) |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* -- I/O library functions ----------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_io |
|||
|
|||
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */ |
|||
|
|||
LJLIB_CF(io_open) |
|||
{ |
|||
const char *fname = strdata(lj_lib_checkstr(L, 1)); |
|||
GCstr *s = lj_lib_optstr(L, 2); |
|||
const char *mode = s ? strdata(s) : "r"; |
|||
IOFileUD *iof = io_file_new(L); |
|||
iof->fp = fopen(fname, mode); |
|||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname); |
|||
} |
|||
|
|||
LJLIB_CF(io_popen) |
|||
{ |
|||
#if LJ_TARGET_POSIX || (LJ_TARGET_WINDOWS && !LJ_TARGET_XBOXONE && !LJ_TARGET_UWP) |
|||
const char *fname = strdata(lj_lib_checkstr(L, 1)); |
|||
GCstr *s = lj_lib_optstr(L, 2); |
|||
const char *mode = s ? strdata(s) : "r"; |
|||
IOFileUD *iof = io_file_new(L); |
|||
iof->type = IOFILE_TYPE_PIPE; |
|||
#if LJ_TARGET_POSIX |
|||
fflush(NULL); |
|||
iof->fp = popen(fname, mode); |
|||
#else |
|||
iof->fp = _popen(fname, mode); |
|||
#endif |
|||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname); |
|||
#else |
|||
return luaL_error(L, LUA_QL("popen") " not supported"); |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_CF(io_tmpfile) |
|||
{ |
|||
IOFileUD *iof = io_file_new(L); |
|||
#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA |
|||
iof->fp = NULL; errno = ENOSYS; |
|||
#else |
|||
iof->fp = tmpfile(); |
|||
#endif |
|||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL); |
|||
} |
|||
|
|||
LJLIB_CF(io_close) |
|||
{ |
|||
return lj_cf_io_method_close(L); |
|||
} |
|||
|
|||
LJLIB_CF(io_read) |
|||
{ |
|||
return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0); |
|||
} |
|||
|
|||
LJLIB_CF(io_write) LJLIB_REC(io_write GCROOT_IO_OUTPUT) |
|||
{ |
|||
return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0); |
|||
} |
|||
|
|||
LJLIB_CF(io_flush) LJLIB_REC(io_flush GCROOT_IO_OUTPUT) |
|||
{ |
|||
return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)->fp) == 0, NULL); |
|||
} |
|||
|
|||
static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode) |
|||
{ |
|||
if (L->base < L->top && !tvisnil(L->base)) { |
|||
if (tvisudata(L->base)) { |
|||
io_tofile(L); |
|||
L->top = L->base+1; |
|||
} else { |
|||
io_file_open(L, mode); |
|||
} |
|||
/* NOBARRIER: The standard I/O handles are GC roots. */ |
|||
setgcref(G(L)->gcroot[id], gcV(L->top-1)); |
|||
} else { |
|||
setudataV(L, L->top++, IOSTDF_UD(L, id)); |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(io_input) |
|||
{ |
|||
return io_std_getset(L, GCROOT_IO_INPUT, "r"); |
|||
} |
|||
|
|||
LJLIB_CF(io_output) |
|||
{ |
|||
return io_std_getset(L, GCROOT_IO_OUTPUT, "w"); |
|||
} |
|||
|
|||
LJLIB_CF(io_lines) |
|||
{ |
|||
if (L->base == L->top) setnilV(L->top++); |
|||
if (!tvisnil(L->base)) { /* io.lines(fname) */ |
|||
IOFileUD *iof = io_file_open(L, "r"); |
|||
iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE; |
|||
L->top--; |
|||
setudataV(L, L->base, udataV(L->top)); |
|||
} else { /* io.lines() iterates over stdin. */ |
|||
setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT)); |
|||
} |
|||
return io_file_lines(L); |
|||
} |
|||
|
|||
LJLIB_CF(io_type) |
|||
{ |
|||
cTValue *o = lj_lib_checkany(L, 1); |
|||
if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE)) |
|||
setnilV(L->top++); |
|||
else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL) |
|||
lua_pushliteral(L, "file"); |
|||
else |
|||
lua_pushliteral(L, "closed file"); |
|||
return 1; |
|||
} |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name) |
|||
{ |
|||
IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD)); |
|||
GCudata *ud = udataV(L->top-1); |
|||
ud->udtype = UDTYPE_IO_FILE; |
|||
/* NOBARRIER: The GCudata is new (marked white). */ |
|||
setgcref(ud->metatable, gcV(L->top-3)); |
|||
iof->fp = fp; |
|||
iof->type = IOFILE_TYPE_STDF; |
|||
lua_setfield(L, -2, name); |
|||
return obj2gco(ud); |
|||
} |
|||
|
|||
LUALIB_API int luaopen_io(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, NULL, io_method); |
|||
copyTV(L, L->top, L->top-1); L->top++; |
|||
lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); |
|||
LJ_LIB_REG(L, LUA_IOLIBNAME, io); |
|||
setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin")); |
|||
setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout")); |
|||
io_std_new(L, stderr, "stderr"); |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,761 @@ |
|||
/*
|
|||
** JIT library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#define lib_jit_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_debug.h" |
|||
#include "lj_str.h" |
|||
#include "lj_tab.h" |
|||
#include "lj_state.h" |
|||
#include "lj_bc.h" |
|||
#if LJ_HASFFI |
|||
#include "lj_ctype.h" |
|||
#endif |
|||
#if LJ_HASJIT |
|||
#include "lj_ir.h" |
|||
#include "lj_jit.h" |
|||
#include "lj_ircall.h" |
|||
#include "lj_iropt.h" |
|||
#include "lj_target.h" |
|||
#endif |
|||
#include "lj_trace.h" |
|||
#include "lj_dispatch.h" |
|||
#include "lj_vm.h" |
|||
#include "lj_vmevent.h" |
|||
#include "lj_lib.h" |
|||
|
|||
#include "luajit.h" |
|||
|
|||
/* -- jit.* functions ----------------------------------------------------- */ |
|||
|
|||
#define LJLIB_MODULE_jit |
|||
|
|||
static int setjitmode(lua_State *L, int mode) |
|||
{ |
|||
int idx = 0; |
|||
if (L->base == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */ |
|||
mode |= LUAJIT_MODE_ENGINE; |
|||
} else { |
|||
/* jit.on/off/flush(func|proto, nil|true|false) */ |
|||
if (tvisfunc(L->base) || tvisproto(L->base)) |
|||
idx = 1; |
|||
else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */ |
|||
goto err; |
|||
if (L->base+1 < L->top && tvisbool(L->base+1)) |
|||
mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC; |
|||
else |
|||
mode |= LUAJIT_MODE_FUNC; |
|||
} |
|||
if (luaJIT_setmode(L, idx, mode) != 1) { |
|||
if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE) |
|||
lj_err_caller(L, LJ_ERR_NOJIT); |
|||
err: |
|||
lj_err_argt(L, 1, LUA_TFUNCTION); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(jit_on) |
|||
{ |
|||
return setjitmode(L, LUAJIT_MODE_ON); |
|||
} |
|||
|
|||
LJLIB_CF(jit_off) |
|||
{ |
|||
return setjitmode(L, LUAJIT_MODE_OFF); |
|||
} |
|||
|
|||
LJLIB_CF(jit_flush) |
|||
{ |
|||
#if LJ_HASJIT |
|||
if (L->base < L->top && tvisnumber(L->base)) { |
|||
int traceno = lj_lib_checkint(L, 1); |
|||
luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE); |
|||
return 0; |
|||
} |
|||
#endif |
|||
return setjitmode(L, LUAJIT_MODE_FLUSH); |
|||
} |
|||
|
|||
#if LJ_HASJIT |
|||
/* Push a string for every flag bit that is set. */ |
|||
static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base, |
|||
const char *str) |
|||
{ |
|||
for (; *str; base <<= 1, str += 1+*str) |
|||
if (flags & base) |
|||
setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str)); |
|||
} |
|||
#endif |
|||
|
|||
LJLIB_CF(jit_status) |
|||
{ |
|||
#if LJ_HASJIT |
|||
jit_State *J = L2J(L); |
|||
L->top = L->base; |
|||
setboolV(L->top++, (J->flags & JIT_F_ON) ? 1 : 0); |
|||
flagbits_to_strings(L, J->flags, JIT_F_CPU, JIT_F_CPUSTRING); |
|||
flagbits_to_strings(L, J->flags, JIT_F_OPT, JIT_F_OPTSTRING); |
|||
return (int)(L->top - L->base); |
|||
#else |
|||
setboolV(L->top++, 0); |
|||
return 1; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_CF(jit_security) |
|||
{ |
|||
int idx = lj_lib_checkopt(L, 1, -1, LJ_SECURITY_MODESTRING); |
|||
setintV(L->top++, ((LJ_SECURITY_MODE >> (2*idx)) & 3)); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(jit_attach) |
|||
{ |
|||
#ifdef LUAJIT_DISABLE_VMEVENT |
|||
luaL_error(L, "vmevent API disabled"); |
|||
#else |
|||
GCfunc *fn = lj_lib_checkfunc(L, 1); |
|||
GCstr *s = lj_lib_optstr(L, 2); |
|||
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE); |
|||
if (s) { /* Attach to given event. */ |
|||
const uint8_t *p = (const uint8_t *)strdata(s); |
|||
uint32_t h = s->len; |
|||
while (*p) h = h ^ (lj_rol(h, 6) + *p++); |
|||
lua_pushvalue(L, 1); |
|||
lua_rawseti(L, -2, VMEVENT_HASHIDX(h)); |
|||
G(L)->vmevmask = VMEVENT_NOCACHE; /* Invalidate cache. */ |
|||
} else { /* Detach if no event given. */ |
|||
setnilV(L->top++); |
|||
while (lua_next(L, -2)) { |
|||
L->top--; |
|||
if (tvisfunc(L->top) && funcV(L->top) == fn) { |
|||
setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1)); |
|||
} |
|||
} |
|||
} |
|||
#endif |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_PUSH(top-5) LJLIB_SET(os) |
|||
LJLIB_PUSH(top-4) LJLIB_SET(arch) |
|||
LJLIB_PUSH(top-3) LJLIB_SET(version_num) |
|||
LJLIB_PUSH(top-2) LJLIB_SET(version) |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
/* -- jit.util.* functions ------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_jit_util |
|||
|
|||
/* -- Reflection API for Lua functions ------------------------------------ */ |
|||
|
|||
/* Return prototype of first argument (Lua function or prototype object) */ |
|||
static GCproto *check_Lproto(lua_State *L, int nolua) |
|||
{ |
|||
TValue *o = L->base; |
|||
if (L->top > o) { |
|||
if (tvisproto(o)) { |
|||
return protoV(o); |
|||
} else if (tvisfunc(o)) { |
|||
if (isluafunc(funcV(o))) |
|||
return funcproto(funcV(o)); |
|||
else if (nolua) |
|||
return NULL; |
|||
} |
|||
} |
|||
lj_err_argt(L, 1, LUA_TFUNCTION); |
|||
return NULL; /* unreachable */ |
|||
} |
|||
|
|||
static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val) |
|||
{ |
|||
setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val); |
|||
} |
|||
|
|||
/* local info = jit.util.funcinfo(func [,pc]) */ |
|||
LJLIB_CF(jit_util_funcinfo) |
|||
{ |
|||
GCproto *pt = check_Lproto(L, 1); |
|||
if (pt) { |
|||
BCPos pc = (BCPos)lj_lib_optint(L, 2, 0); |
|||
GCtab *t; |
|||
lua_createtable(L, 0, 16); /* Increment hash size if fields are added. */ |
|||
t = tabV(L->top-1); |
|||
setintfield(L, t, "linedefined", pt->firstline); |
|||
setintfield(L, t, "lastlinedefined", pt->firstline + pt->numline); |
|||
setintfield(L, t, "stackslots", pt->framesize); |
|||
setintfield(L, t, "params", pt->numparams); |
|||
setintfield(L, t, "bytecodes", (int32_t)pt->sizebc); |
|||
setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc); |
|||
setintfield(L, t, "nconsts", (int32_t)pt->sizekn); |
|||
setintfield(L, t, "upvalues", (int32_t)pt->sizeuv); |
|||
if (pc < pt->sizebc) |
|||
setintfield(L, t, "currentline", lj_debug_line(pt, pc)); |
|||
lua_pushboolean(L, (pt->flags & PROTO_VARARG)); |
|||
lua_setfield(L, -2, "isvararg"); |
|||
lua_pushboolean(L, (pt->flags & PROTO_CHILD)); |
|||
lua_setfield(L, -2, "children"); |
|||
setstrV(L, L->top++, proto_chunkname(pt)); |
|||
lua_setfield(L, -2, "source"); |
|||
lj_debug_pushloc(L, pt, pc); |
|||
lua_setfield(L, -2, "loc"); |
|||
setprotoV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "proto")), pt); |
|||
} else { |
|||
GCfunc *fn = funcV(L->base); |
|||
GCtab *t; |
|||
lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */ |
|||
t = tabV(L->top-1); |
|||
if (!iscfunc(fn)) |
|||
setintfield(L, t, "ffid", fn->c.ffid); |
|||
setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")), |
|||
(intptr_t)(void *)fn->c.f); |
|||
setintfield(L, t, "upvalues", fn->c.nupvalues); |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
/* local ins, m = jit.util.funcbc(func, pc) */ |
|||
LJLIB_CF(jit_util_funcbc) |
|||
{ |
|||
GCproto *pt = check_Lproto(L, 0); |
|||
BCPos pc = (BCPos)lj_lib_checkint(L, 2); |
|||
if (pc < pt->sizebc) { |
|||
BCIns ins = proto_bc(pt)[pc]; |
|||
BCOp op = bc_op(ins); |
|||
lj_assertL(op < BC__MAX, "bad bytecode op %d", op); |
|||
setintV(L->top, ins); |
|||
setintV(L->top+1, lj_bc_mode[op]); |
|||
L->top += 2; |
|||
return 2; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local k = jit.util.funck(func, idx) */ |
|||
LJLIB_CF(jit_util_funck) |
|||
{ |
|||
GCproto *pt = check_Lproto(L, 0); |
|||
ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2); |
|||
if (idx >= 0) { |
|||
if (idx < (ptrdiff_t)pt->sizekn) { |
|||
copyTV(L, L->top-1, proto_knumtv(pt, idx)); |
|||
return 1; |
|||
} |
|||
} else { |
|||
if (~idx < (ptrdiff_t)pt->sizekgc) { |
|||
GCobj *gc = proto_kgc(pt, idx); |
|||
setgcV(L, L->top-1, gc, ~gc->gch.gct); |
|||
return 1; |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local name = jit.util.funcuvname(func, idx) */ |
|||
LJLIB_CF(jit_util_funcuvname) |
|||
{ |
|||
GCproto *pt = check_Lproto(L, 0); |
|||
uint32_t idx = (uint32_t)lj_lib_checkint(L, 2); |
|||
if (idx < pt->sizeuv) { |
|||
setstrV(L, L->top-1, lj_str_newz(L, lj_debug_uvname(pt, idx))); |
|||
return 1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* -- Reflection API for traces ------------------------------------------- */ |
|||
|
|||
#if LJ_HASJIT |
|||
|
|||
/* Check trace argument. Must not throw for non-existent trace numbers. */ |
|||
static GCtrace *jit_checktrace(lua_State *L) |
|||
{ |
|||
TraceNo tr = (TraceNo)lj_lib_checkint(L, 1); |
|||
jit_State *J = L2J(L); |
|||
if (tr > 0 && tr < J->sizetrace) |
|||
return traceref(J, tr); |
|||
return NULL; |
|||
} |
|||
|
|||
/* Names of link types. ORDER LJ_TRLINK */ |
|||
static const char *const jit_trlinkname[] = { |
|||
"none", "root", "loop", "tail-recursion", "up-recursion", "down-recursion", |
|||
"interpreter", "return", "stitch" |
|||
}; |
|||
|
|||
/* local info = jit.util.traceinfo(tr) */ |
|||
LJLIB_CF(jit_util_traceinfo) |
|||
{ |
|||
GCtrace *T = jit_checktrace(L); |
|||
if (T) { |
|||
GCtab *t; |
|||
lua_createtable(L, 0, 8); /* Increment hash size if fields are added. */ |
|||
t = tabV(L->top-1); |
|||
setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1); |
|||
setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk); |
|||
setintfield(L, t, "link", T->link); |
|||
setintfield(L, t, "nexit", T->nsnap); |
|||
setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype])); |
|||
lua_setfield(L, -2, "linktype"); |
|||
/* There are many more fields. Add them only when needed. */ |
|||
return 1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */ |
|||
LJLIB_CF(jit_util_traceir) |
|||
{ |
|||
GCtrace *T = jit_checktrace(L); |
|||
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS; |
|||
if (T && ref >= REF_BIAS && ref < T->nins) { |
|||
IRIns *ir = &T->ir[ref]; |
|||
int32_t m = lj_ir_mode[ir->o]; |
|||
setintV(L->top-2, m); |
|||
setintV(L->top-1, ir->ot); |
|||
setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0)); |
|||
setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0)); |
|||
setintV(L->top++, ir->prev); |
|||
return 5; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local k, t [, slot] = jit.util.tracek(tr, idx) */ |
|||
LJLIB_CF(jit_util_tracek) |
|||
{ |
|||
GCtrace *T = jit_checktrace(L); |
|||
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS; |
|||
if (T && ref >= T->nk && ref < REF_BIAS) { |
|||
IRIns *ir = &T->ir[ref]; |
|||
int32_t slot = -1; |
|||
if (ir->o == IR_KSLOT) { |
|||
slot = ir->op2; |
|||
ir = &T->ir[ir->op1]; |
|||
} |
|||
#if LJ_HASFFI |
|||
if (ir->o == IR_KINT64) ctype_loadffi(L); |
|||
#endif |
|||
lj_ir_kvalue(L, L->top-2, ir); |
|||
setintV(L->top-1, (int32_t)irt_type(ir->t)); |
|||
if (slot == -1) |
|||
return 2; |
|||
setintV(L->top++, slot); |
|||
return 3; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local snap = jit.util.tracesnap(tr, sn) */ |
|||
LJLIB_CF(jit_util_tracesnap) |
|||
{ |
|||
GCtrace *T = jit_checktrace(L); |
|||
SnapNo sn = (SnapNo)lj_lib_checkint(L, 2); |
|||
if (T && sn < T->nsnap) { |
|||
SnapShot *snap = &T->snap[sn]; |
|||
SnapEntry *map = &T->snapmap[snap->mapofs]; |
|||
MSize n, nent = snap->nent; |
|||
GCtab *t; |
|||
lua_createtable(L, nent+2, 0); |
|||
t = tabV(L->top-1); |
|||
setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS); |
|||
setintV(lj_tab_setint(L, t, 1), (int32_t)snap->nslots); |
|||
for (n = 0; n < nent; n++) |
|||
setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]); |
|||
setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0)); |
|||
return 1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local mcode, addr, loop = jit.util.tracemc(tr) */ |
|||
LJLIB_CF(jit_util_tracemc) |
|||
{ |
|||
GCtrace *T = jit_checktrace(L); |
|||
if (T && T->mcode != NULL) { |
|||
setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode)); |
|||
setintptrV(L->top++, (intptr_t)(void *)T->mcode); |
|||
setintV(L->top++, T->mcloop); |
|||
return 3; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
/* local addr = jit.util.traceexitstub([tr,] exitno) */ |
|||
LJLIB_CF(jit_util_traceexitstub) |
|||
{ |
|||
#ifdef EXITSTUBS_PER_GROUP |
|||
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1); |
|||
jit_State *J = L2J(L); |
|||
if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) { |
|||
setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno)); |
|||
return 1; |
|||
} |
|||
#else |
|||
if (L->top > L->base+1) { /* Don't throw for one-argument variant. */ |
|||
GCtrace *T = jit_checktrace(L); |
|||
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 2); |
|||
ExitNo maxexit = T->root ? T->nsnap+1 : T->nsnap; |
|||
if (T && T->mcode != NULL && exitno < maxexit) { |
|||
setintptrV(L->top-1, (intptr_t)(void *)exitstub_trace_addr(T, exitno)); |
|||
return 1; |
|||
} |
|||
} |
|||
#endif |
|||
return 0; |
|||
} |
|||
|
|||
/* local addr = jit.util.ircalladdr(idx) */ |
|||
LJLIB_CF(jit_util_ircalladdr) |
|||
{ |
|||
uint32_t idx = (uint32_t)lj_lib_checkint(L, 1); |
|||
if (idx < IRCALL__MAX) { |
|||
setintptrV(L->top-1, (intptr_t)(void *)lj_ir_callinfo[idx].func); |
|||
return 1; |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
#endif |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
static int luaopen_jit_util(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, NULL, jit_util); |
|||
return 1; |
|||
} |
|||
|
|||
/* -- jit.opt module ------------------------------------------------------ */ |
|||
|
|||
#if LJ_HASJIT |
|||
|
|||
#define LJLIB_MODULE_jit_opt |
|||
|
|||
/* Parse optimization level. */ |
|||
static int jitopt_level(jit_State *J, const char *str) |
|||
{ |
|||
if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') { |
|||
uint32_t flags; |
|||
if (str[0] == '0') flags = JIT_F_OPT_0; |
|||
else if (str[0] == '1') flags = JIT_F_OPT_1; |
|||
else if (str[0] == '2') flags = JIT_F_OPT_2; |
|||
else flags = JIT_F_OPT_3; |
|||
J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags; |
|||
return 1; /* Ok. */ |
|||
} |
|||
return 0; /* No match. */ |
|||
} |
|||
|
|||
/* Parse optimization flag. */ |
|||
static int jitopt_flag(jit_State *J, const char *str) |
|||
{ |
|||
const char *lst = JIT_F_OPTSTRING; |
|||
uint32_t opt; |
|||
int set = 1; |
|||
if (str[0] == '+') { |
|||
str++; |
|||
} else if (str[0] == '-') { |
|||
str++; |
|||
set = 0; |
|||
} else if (str[0] == 'n' && str[1] == 'o') { |
|||
str += str[2] == '-' ? 3 : 2; |
|||
set = 0; |
|||
} |
|||
for (opt = JIT_F_OPT; ; opt <<= 1) { |
|||
size_t len = *(const uint8_t *)lst; |
|||
if (len == 0) |
|||
break; |
|||
if (strncmp(str, lst+1, len) == 0 && str[len] == '\0') { |
|||
if (set) J->flags |= opt; else J->flags &= ~opt; |
|||
return 1; /* Ok. */ |
|||
} |
|||
lst += 1+len; |
|||
} |
|||
return 0; /* No match. */ |
|||
} |
|||
|
|||
/* Parse optimization parameter. */ |
|||
static int jitopt_param(jit_State *J, const char *str) |
|||
{ |
|||
const char *lst = JIT_P_STRING; |
|||
int i; |
|||
for (i = 0; i < JIT_P__MAX; i++) { |
|||
size_t len = *(const uint8_t *)lst; |
|||
lj_assertJ(len != 0, "bad JIT_P_STRING"); |
|||
if (strncmp(str, lst+1, len) == 0 && str[len] == '=') { |
|||
int32_t n = 0; |
|||
const char *p = &str[len+1]; |
|||
while (*p >= '0' && *p <= '9') |
|||
n = n*10 + (*p++ - '0'); |
|||
if (*p) return 0; /* Malformed number. */ |
|||
J->param[i] = n; |
|||
if (i == JIT_P_hotloop) |
|||
lj_dispatch_init_hotcount(J2G(J)); |
|||
return 1; /* Ok. */ |
|||
} |
|||
lst += 1+len; |
|||
} |
|||
return 0; /* No match. */ |
|||
} |
|||
|
|||
/* jit.opt.start(flags...) */ |
|||
LJLIB_CF(jit_opt_start) |
|||
{ |
|||
jit_State *J = L2J(L); |
|||
int nargs = (int)(L->top - L->base); |
|||
if (nargs == 0) { |
|||
J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT; |
|||
} else { |
|||
int i; |
|||
for (i = 1; i <= nargs; i++) { |
|||
const char *str = strdata(lj_lib_checkstr(L, i)); |
|||
if (!jitopt_level(J, str) && |
|||
!jitopt_flag(J, str) && |
|||
!jitopt_param(J, str)) |
|||
lj_err_callerv(L, LJ_ERR_JITOPT, str); |
|||
} |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
#endif |
|||
|
|||
/* -- jit.profile module -------------------------------------------------- */ |
|||
|
|||
#if LJ_HASPROFILE |
|||
|
|||
#define LJLIB_MODULE_jit_profile |
|||
|
|||
/* Not loaded by default, use: local profile = require("jit.profile") */ |
|||
|
|||
#define KEY_PROFILE_THREAD (U64x(80000000,00000000)|'t') |
|||
#define KEY_PROFILE_FUNC (U64x(80000000,00000000)|'f') |
|||
|
|||
static void jit_profile_callback(lua_State *L2, lua_State *L, int samples, |
|||
int vmstate) |
|||
{ |
|||
TValue key; |
|||
cTValue *tv; |
|||
key.u64 = KEY_PROFILE_FUNC; |
|||
tv = lj_tab_get(L, tabV(registry(L)), &key); |
|||
if (tvisfunc(tv)) { |
|||
char vmst = (char)vmstate; |
|||
int status; |
|||
setfuncV(L2, L2->top++, funcV(tv)); |
|||
setthreadV(L2, L2->top++, L); |
|||
setintV(L2->top++, samples); |
|||
setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1)); |
|||
status = lua_pcall(L2, 3, 0, 0); /* callback(thread, samples, vmstate) */ |
|||
if (status) { |
|||
if (G(L2)->panic) G(L2)->panic(L2); |
|||
exit(EXIT_FAILURE); |
|||
} |
|||
lj_trace_abort(G(L2)); |
|||
} |
|||
} |
|||
|
|||
/* profile.start(mode, cb) */ |
|||
LJLIB_CF(jit_profile_start) |
|||
{ |
|||
GCtab *registry = tabV(registry(L)); |
|||
GCstr *mode = lj_lib_optstr(L, 1); |
|||
GCfunc *func = lj_lib_checkfunc(L, 2); |
|||
lua_State *L2 = lua_newthread(L); /* Thread that runs profiler callback. */ |
|||
TValue key; |
|||
/* Anchor thread and function in registry. */ |
|||
key.u64 = KEY_PROFILE_THREAD; |
|||
setthreadV(L, lj_tab_set(L, registry, &key), L2); |
|||
key.u64 = KEY_PROFILE_FUNC; |
|||
setfuncV(L, lj_tab_set(L, registry, &key), func); |
|||
lj_gc_anybarriert(L, registry); |
|||
luaJIT_profile_start(L, mode ? strdata(mode) : "", |
|||
(luaJIT_profile_callback)jit_profile_callback, L2); |
|||
return 0; |
|||
} |
|||
|
|||
/* profile.stop() */ |
|||
LJLIB_CF(jit_profile_stop) |
|||
{ |
|||
GCtab *registry; |
|||
TValue key; |
|||
luaJIT_profile_stop(L); |
|||
registry = tabV(registry(L)); |
|||
key.u64 = KEY_PROFILE_THREAD; |
|||
setnilV(lj_tab_set(L, registry, &key)); |
|||
key.u64 = KEY_PROFILE_FUNC; |
|||
setnilV(lj_tab_set(L, registry, &key)); |
|||
lj_gc_anybarriert(L, registry); |
|||
return 0; |
|||
} |
|||
|
|||
/* dump = profile.dumpstack([thread,] fmt, depth) */ |
|||
LJLIB_CF(jit_profile_dumpstack) |
|||
{ |
|||
lua_State *L2 = L; |
|||
int arg = 0; |
|||
size_t len; |
|||
int depth; |
|||
GCstr *fmt; |
|||
const char *p; |
|||
if (L->top > L->base && tvisthread(L->base)) { |
|||
L2 = threadV(L->base); |
|||
arg = 1; |
|||
} |
|||
fmt = lj_lib_checkstr(L, arg+1); |
|||
depth = lj_lib_checkint(L, arg+2); |
|||
p = luaJIT_profile_dumpstack(L2, strdata(fmt), depth, &len); |
|||
lua_pushlstring(L, p, len); |
|||
return 1; |
|||
} |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
static int luaopen_jit_profile(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, NULL, jit_profile); |
|||
return 1; |
|||
} |
|||
|
|||
#endif |
|||
|
|||
/* -- JIT compiler initialization ----------------------------------------- */ |
|||
|
|||
#if LJ_HASJIT |
|||
/* Default values for JIT parameters. */ |
|||
static const int32_t jit_param_default[JIT_P__MAX+1] = { |
|||
#define JIT_PARAMINIT(len, name, value) (value), |
|||
JIT_PARAMDEF(JIT_PARAMINIT) |
|||
#undef JIT_PARAMINIT |
|||
0 |
|||
}; |
|||
|
|||
#if LJ_TARGET_ARM && LJ_TARGET_LINUX |
|||
#include <sys/utsname.h> |
|||
#endif |
|||
|
|||
/* Arch-dependent CPU feature detection. */ |
|||
static uint32_t jit_cpudetect(void) |
|||
{ |
|||
uint32_t flags = 0; |
|||
#if LJ_TARGET_X86ORX64 |
|||
|
|||
uint32_t vendor[4]; |
|||
uint32_t features[4]; |
|||
if (lj_vm_cpuid(0, vendor) && lj_vm_cpuid(1, features)) { |
|||
flags |= ((features[2] >> 0)&1) * JIT_F_SSE3; |
|||
flags |= ((features[2] >> 19)&1) * JIT_F_SSE4_1; |
|||
if (vendor[0] >= 7) { |
|||
uint32_t xfeatures[4]; |
|||
lj_vm_cpuid(7, xfeatures); |
|||
flags |= ((xfeatures[1] >> 8)&1) * JIT_F_BMI2; |
|||
} |
|||
} |
|||
/* Don't bother checking for SSE2 -- the VM will crash before getting here. */ |
|||
|
|||
#elif LJ_TARGET_ARM |
|||
|
|||
int ver = LJ_ARCH_VERSION; /* Compile-time ARM CPU detection. */ |
|||
#if LJ_TARGET_LINUX |
|||
if (ver < 70) { /* Runtime ARM CPU detection. */ |
|||
struct utsname ut; |
|||
uname(&ut); |
|||
if (strncmp(ut.machine, "armv", 4) == 0) { |
|||
if (ut.machine[4] >= '8') ver = 80; |
|||
else if (ut.machine[4] == '7') ver = 70; |
|||
else if (ut.machine[4] == '6') ver = 60; |
|||
} |
|||
} |
|||
#endif |
|||
flags |= ver >= 70 ? JIT_F_ARMV7 : |
|||
ver >= 61 ? JIT_F_ARMV6T2_ : |
|||
ver >= 60 ? JIT_F_ARMV6_ : 0; |
|||
flags |= LJ_ARCH_HASFPU == 0 ? 0 : ver >= 70 ? JIT_F_VFPV3 : JIT_F_VFPV2; |
|||
|
|||
#elif LJ_TARGET_ARM64 |
|||
|
|||
/* No optional CPU features to detect (for now). */ |
|||
|
|||
#elif LJ_TARGET_PPC |
|||
|
|||
#if LJ_ARCH_SQRT |
|||
flags |= JIT_F_SQRT; |
|||
#endif |
|||
#if LJ_ARCH_ROUND |
|||
flags |= JIT_F_ROUND; |
|||
#endif |
|||
|
|||
#elif LJ_TARGET_MIPS |
|||
|
|||
/* Compile-time MIPS CPU detection. */ |
|||
#if LJ_ARCH_VERSION >= 20 |
|||
flags |= JIT_F_MIPSXXR2; |
|||
#endif |
|||
/* Runtime MIPS CPU detection. */ |
|||
#if defined(__GNUC__) |
|||
if (!(flags & JIT_F_MIPSXXR2)) { |
|||
int x; |
|||
#ifdef __mips16 |
|||
x = 0; /* Runtime detection is difficult. Ensure optimal -march flags. */ |
|||
#else |
|||
/* On MIPS32R1 rotr is treated as srl. rotr r2,r2,1 -> srl r2,r2,1. */ |
|||
__asm__("li $2, 1\n\t.long 0x00221042\n\tmove %0, $2" : "=r"(x) : : "$2"); |
|||
#endif |
|||
if (x) flags |= JIT_F_MIPSXXR2; /* Either 0x80000000 (R2) or 0 (R1). */ |
|||
} |
|||
#endif |
|||
|
|||
#else |
|||
#error "Missing CPU detection for this architecture" |
|||
#endif |
|||
return flags; |
|||
} |
|||
|
|||
/* Initialize JIT compiler. */ |
|||
static void jit_init(lua_State *L) |
|||
{ |
|||
jit_State *J = L2J(L); |
|||
J->flags = jit_cpudetect() | JIT_F_ON | JIT_F_OPT_DEFAULT; |
|||
memcpy(J->param, jit_param_default, sizeof(J->param)); |
|||
lj_dispatch_update(G(L)); |
|||
} |
|||
#endif |
|||
|
|||
LUALIB_API int luaopen_jit(lua_State *L) |
|||
{ |
|||
#if LJ_HASJIT |
|||
jit_init(L); |
|||
#endif |
|||
lua_pushliteral(L, LJ_OS_NAME); |
|||
lua_pushliteral(L, LJ_ARCH_NAME); |
|||
lua_pushinteger(L, LUAJIT_VERSION_NUM); |
|||
lua_pushliteral(L, LUAJIT_VERSION); |
|||
LJ_LIB_REG(L, LUA_JITLIBNAME, jit); |
|||
#if LJ_HASPROFILE |
|||
lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile, |
|||
tabref(L->env)); |
|||
#endif |
|||
#ifndef LUAJIT_DISABLE_JITUTIL |
|||
lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env)); |
|||
#endif |
|||
#if LJ_HASJIT |
|||
LJ_LIB_REG(L, "jit.opt", jit_opt); |
|||
#endif |
|||
L->top -= 2; |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,201 @@ |
|||
/*
|
|||
** Math library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
*/ |
|||
|
|||
#include <math.h> |
|||
|
|||
#define lib_math_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_lib.h" |
|||
#include "lj_vm.h" |
|||
#include "lj_prng.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_math |
|||
|
|||
LJLIB_ASM(math_abs) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checknumber(L, 1); |
|||
return FFH_RETRY; |
|||
} |
|||
LJLIB_ASM_(math_floor) LJLIB_REC(math_round IRFPM_FLOOR) |
|||
LJLIB_ASM_(math_ceil) LJLIB_REC(math_round IRFPM_CEIL) |
|||
|
|||
LJLIB_ASM(math_sqrt) LJLIB_REC(math_unary IRFPM_SQRT) |
|||
{ |
|||
lj_lib_checknum(L, 1); |
|||
return FFH_RETRY; |
|||
} |
|||
LJLIB_ASM_(math_log10) LJLIB_REC(math_call IRCALL_log10) |
|||
LJLIB_ASM_(math_exp) LJLIB_REC(math_call IRCALL_exp) |
|||
LJLIB_ASM_(math_sin) LJLIB_REC(math_call IRCALL_sin) |
|||
LJLIB_ASM_(math_cos) LJLIB_REC(math_call IRCALL_cos) |
|||
LJLIB_ASM_(math_tan) LJLIB_REC(math_call IRCALL_tan) |
|||
LJLIB_ASM_(math_asin) LJLIB_REC(math_call IRCALL_asin) |
|||
LJLIB_ASM_(math_acos) LJLIB_REC(math_call IRCALL_acos) |
|||
LJLIB_ASM_(math_atan) LJLIB_REC(math_call IRCALL_atan) |
|||
LJLIB_ASM_(math_sinh) LJLIB_REC(math_call IRCALL_sinh) |
|||
LJLIB_ASM_(math_cosh) LJLIB_REC(math_call IRCALL_cosh) |
|||
LJLIB_ASM_(math_tanh) LJLIB_REC(math_call IRCALL_tanh) |
|||
LJLIB_ASM_(math_frexp) |
|||
LJLIB_ASM_(math_modf) |
|||
|
|||
LJLIB_ASM(math_log) LJLIB_REC(math_log) |
|||
{ |
|||
double x = lj_lib_checknum(L, 1); |
|||
if (L->base+1 < L->top) { |
|||
double y = lj_lib_checknum(L, 2); |
|||
#ifdef LUAJIT_NO_LOG2 |
|||
x = log(x); y = 1.0 / log(y); |
|||
#else |
|||
x = lj_vm_log2(x); y = 1.0 / lj_vm_log2(y); |
|||
#endif |
|||
setnumV(L->base-1-LJ_FR2, x*y); /* Do NOT join the expression to x / y. */ |
|||
return FFH_RES(1); |
|||
} |
|||
return FFH_RETRY; |
|||
} |
|||
|
|||
LJLIB_LUA(math_deg) /* function(x) return x * 57.29577951308232 end */ |
|||
LJLIB_LUA(math_rad) /* function(x) return x * 0.017453292519943295 end */ |
|||
|
|||
LJLIB_ASM(math_atan2) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checknum(L, 1); |
|||
lj_lib_checknum(L, 2); |
|||
return FFH_RETRY; |
|||
} |
|||
LJLIB_ASM_(math_pow) LJLIB_REC(.) |
|||
LJLIB_ASM_(math_fmod) |
|||
|
|||
LJLIB_ASM(math_ldexp) LJLIB_REC(.) |
|||
{ |
|||
lj_lib_checknum(L, 1); |
|||
#if LJ_DUALNUM && !LJ_TARGET_X86ORX64 |
|||
lj_lib_checkint(L, 2); |
|||
#else |
|||
lj_lib_checknum(L, 2); |
|||
#endif |
|||
return FFH_RETRY; |
|||
} |
|||
|
|||
LJLIB_ASM(math_min) LJLIB_REC(math_minmax IR_MIN) |
|||
{ |
|||
int i = 0; |
|||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top); |
|||
return FFH_RETRY; |
|||
} |
|||
LJLIB_ASM_(math_max) LJLIB_REC(math_minmax IR_MAX) |
|||
|
|||
LJLIB_PUSH(3.14159265358979323846) LJLIB_SET(pi) |
|||
LJLIB_PUSH(1e310) LJLIB_SET(huge) |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* This implements a Tausworthe PRNG with period 2^223. Based on:
|
|||
** Tables of maximally-equidistributed combined LFSR generators, |
|||
** Pierre L'Ecuyer, 1991, table 3, 1st entry. |
|||
** Full-period ME-CF generator with L=64, J=4, k=223, N1=49. |
|||
*/ |
|||
|
|||
/* Union needed for bit-pattern conversion between uint64_t and double. */ |
|||
typedef union { uint64_t u64; double d; } U64double; |
|||
|
|||
/* PRNG seeding function. */ |
|||
static void random_seed(PRNGState *rs, double d) |
|||
{ |
|||
uint32_t r = 0x11090601; /* 64-k[i] as four 8 bit constants. */ |
|||
int i; |
|||
for (i = 0; i < 4; i++) { |
|||
U64double u; |
|||
uint32_t m = 1u << (r&255); |
|||
r >>= 8; |
|||
u.d = d = d * 3.14159265358979323846 + 2.7182818284590452354; |
|||
if (u.u64 < m) u.u64 += m; /* Ensure k[i] MSB of u[i] are non-zero. */ |
|||
rs->u[i] = u.u64; |
|||
} |
|||
for (i = 0; i < 10; i++) |
|||
(void)lj_prng_u64(rs); |
|||
} |
|||
|
|||
/* PRNG extract function. */ |
|||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with PRNGState. */ |
|||
LJLIB_CF(math_random) LJLIB_REC(.) |
|||
{ |
|||
int n = (int)(L->top - L->base); |
|||
PRNGState *rs = (PRNGState *)(uddata(udataV(lj_lib_upvalue(L, 1)))); |
|||
U64double u; |
|||
double d; |
|||
u.u64 = lj_prng_u64d(rs); |
|||
d = u.d - 1.0; |
|||
if (n > 0) { |
|||
#if LJ_DUALNUM |
|||
int isint = 1; |
|||
double r1; |
|||
lj_lib_checknumber(L, 1); |
|||
if (tvisint(L->base)) { |
|||
r1 = (lua_Number)intV(L->base); |
|||
} else { |
|||
isint = 0; |
|||
r1 = numV(L->base); |
|||
} |
|||
#else |
|||
double r1 = lj_lib_checknum(L, 1); |
|||
#endif |
|||
if (n == 1) { |
|||
d = lj_vm_floor(d*r1) + 1.0; /* d is an int in range [1, r1] */ |
|||
} else { |
|||
#if LJ_DUALNUM |
|||
double r2; |
|||
lj_lib_checknumber(L, 2); |
|||
if (tvisint(L->base+1)) { |
|||
r2 = (lua_Number)intV(L->base+1); |
|||
} else { |
|||
isint = 0; |
|||
r2 = numV(L->base+1); |
|||
} |
|||
#else |
|||
double r2 = lj_lib_checknum(L, 2); |
|||
#endif |
|||
d = lj_vm_floor(d*(r2-r1+1.0)) + r1; /* d is an int in range [r1, r2] */ |
|||
} |
|||
#if LJ_DUALNUM |
|||
if (isint) { |
|||
setintV(L->top-1, lj_num2int(d)); |
|||
return 1; |
|||
} |
|||
#endif |
|||
} /* else: d is a double in range [0, 1] */ |
|||
setnumV(L->top++, d); |
|||
return 1; |
|||
} |
|||
|
|||
/* PRNG seed function. */ |
|||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with PRNGState. */ |
|||
LJLIB_CF(math_randomseed) |
|||
{ |
|||
PRNGState *rs = (PRNGState *)(uddata(udataV(lj_lib_upvalue(L, 1)))); |
|||
random_seed(rs, lj_lib_checknum(L, 1)); |
|||
return 0; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
LUALIB_API int luaopen_math(lua_State *L) |
|||
{ |
|||
PRNGState *rs = (PRNGState *)lua_newuserdata(L, sizeof(PRNGState)); |
|||
lj_prng_seed_fixed(rs); |
|||
LJ_LIB_REG(L, LUA_MATHLIBNAME, math); |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,292 @@ |
|||
/*
|
|||
** OS library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#include <errno.h> |
|||
#include <time.h> |
|||
|
|||
#define lib_os_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_str.h" |
|||
#include "lj_lib.h" |
|||
|
|||
#if LJ_TARGET_POSIX |
|||
#include <unistd.h> |
|||
#else |
|||
#include <stdio.h> |
|||
#endif |
|||
|
|||
#if !LJ_TARGET_PSVITA |
|||
#include <locale.h> |
|||
#endif |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_os |
|||
|
|||
LJLIB_CF(os_execute) |
|||
{ |
|||
#if LJ_NO_SYSTEM |
|||
#if LJ_52 |
|||
errno = ENOSYS; |
|||
return luaL_fileresult(L, 0, NULL); |
|||
#else |
|||
lua_pushinteger(L, -1); |
|||
return 1; |
|||
#endif |
|||
#else |
|||
const char *cmd = luaL_optstring(L, 1, NULL); |
|||
int stat = system(cmd); |
|||
#if LJ_52 |
|||
if (cmd) |
|||
return luaL_execresult(L, stat); |
|||
setboolV(L->top++, 1); |
|||
#else |
|||
setintV(L->top++, stat); |
|||
#endif |
|||
return 1; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_CF(os_remove) |
|||
{ |
|||
const char *filename = luaL_checkstring(L, 1); |
|||
return luaL_fileresult(L, remove(filename) == 0, filename); |
|||
} |
|||
|
|||
LJLIB_CF(os_rename) |
|||
{ |
|||
const char *fromname = luaL_checkstring(L, 1); |
|||
const char *toname = luaL_checkstring(L, 2); |
|||
return luaL_fileresult(L, rename(fromname, toname) == 0, fromname); |
|||
} |
|||
|
|||
LJLIB_CF(os_tmpname) |
|||
{ |
|||
#if LJ_TARGET_PS3 || LJ_TARGET_PS4 || LJ_TARGET_PS5 || LJ_TARGET_PSVITA |
|||
lj_err_caller(L, LJ_ERR_OSUNIQF); |
|||
return 0; |
|||
#else |
|||
#if LJ_TARGET_POSIX |
|||
char buf[15+1]; |
|||
int fp; |
|||
strcpy(buf, "/tmp/lua_XXXXXX"); |
|||
fp = mkstemp(buf); |
|||
if (fp != -1) |
|||
close(fp); |
|||
else |
|||
lj_err_caller(L, LJ_ERR_OSUNIQF); |
|||
#else |
|||
char buf[L_tmpnam]; |
|||
if (tmpnam(buf) == NULL) |
|||
lj_err_caller(L, LJ_ERR_OSUNIQF); |
|||
#endif |
|||
lua_pushstring(L, buf); |
|||
return 1; |
|||
#endif |
|||
} |
|||
|
|||
LJLIB_CF(os_getenv) |
|||
{ |
|||
#if LJ_TARGET_CONSOLE |
|||
lua_pushnil(L); |
|||
#else |
|||
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ |
|||
#endif |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(os_exit) |
|||
{ |
|||
int status; |
|||
if (L->base < L->top && tvisbool(L->base)) |
|||
status = boolV(L->base) ? EXIT_SUCCESS : EXIT_FAILURE; |
|||
else |
|||
status = lj_lib_optint(L, 1, EXIT_SUCCESS); |
|||
if (L->base+1 < L->top && tvistruecond(L->base+1)) |
|||
lua_close(L); |
|||
exit(status); |
|||
return 0; /* Unreachable. */ |
|||
} |
|||
|
|||
LJLIB_CF(os_clock) |
|||
{ |
|||
setnumV(L->top++, ((lua_Number)clock())*(1.0/(lua_Number)CLOCKS_PER_SEC)); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static void setfield(lua_State *L, const char *key, int value) |
|||
{ |
|||
lua_pushinteger(L, value); |
|||
lua_setfield(L, -2, key); |
|||
} |
|||
|
|||
static void setboolfield(lua_State *L, const char *key, int value) |
|||
{ |
|||
if (value < 0) /* undefined? */ |
|||
return; /* does not set field */ |
|||
lua_pushboolean(L, value); |
|||
lua_setfield(L, -2, key); |
|||
} |
|||
|
|||
static int getboolfield(lua_State *L, const char *key) |
|||
{ |
|||
int res; |
|||
lua_getfield(L, -1, key); |
|||
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); |
|||
lua_pop(L, 1); |
|||
return res; |
|||
} |
|||
|
|||
static int getfield(lua_State *L, const char *key, int d) |
|||
{ |
|||
int res; |
|||
lua_getfield(L, -1, key); |
|||
if (lua_isnumber(L, -1)) { |
|||
res = (int)lua_tointeger(L, -1); |
|||
} else { |
|||
if (d < 0) |
|||
lj_err_callerv(L, LJ_ERR_OSDATEF, key); |
|||
res = d; |
|||
} |
|||
lua_pop(L, 1); |
|||
return res; |
|||
} |
|||
|
|||
LJLIB_CF(os_date) |
|||
{ |
|||
const char *s = luaL_optstring(L, 1, "%c"); |
|||
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); |
|||
struct tm *stm; |
|||
#if LJ_TARGET_POSIX |
|||
struct tm rtm; |
|||
#endif |
|||
if (*s == '!') { /* UTC? */ |
|||
s++; /* Skip '!' */ |
|||
#if LJ_TARGET_POSIX |
|||
stm = gmtime_r(&t, &rtm); |
|||
#else |
|||
stm = gmtime(&t); |
|||
#endif |
|||
} else { |
|||
#if LJ_TARGET_POSIX |
|||
stm = localtime_r(&t, &rtm); |
|||
#else |
|||
stm = localtime(&t); |
|||
#endif |
|||
} |
|||
if (stm == NULL) { /* Invalid date? */ |
|||
setnilV(L->top++); |
|||
} else if (strcmp(s, "*t") == 0) { |
|||
lua_createtable(L, 0, 9); /* 9 = number of fields */ |
|||
setfield(L, "sec", stm->tm_sec); |
|||
setfield(L, "min", stm->tm_min); |
|||
setfield(L, "hour", stm->tm_hour); |
|||
setfield(L, "day", stm->tm_mday); |
|||
setfield(L, "month", stm->tm_mon+1); |
|||
setfield(L, "year", stm->tm_year+1900); |
|||
setfield(L, "wday", stm->tm_wday+1); |
|||
setfield(L, "yday", stm->tm_yday+1); |
|||
setboolfield(L, "isdst", stm->tm_isdst); |
|||
} else if (*s) { |
|||
SBuf *sb = &G(L)->tmpbuf; |
|||
MSize sz = 0, retry = 4; |
|||
const char *q; |
|||
for (q = s; *q; q++) |
|||
sz += (*q == '%') ? 30 : 1; /* Overflow doesn't matter. */ |
|||
setsbufL(sb, L); |
|||
while (retry--) { /* Limit growth for invalid format or empty result. */ |
|||
char *buf = lj_buf_need(sb, sz); |
|||
size_t len = strftime(buf, sbufsz(sb), s, stm); |
|||
if (len) { |
|||
setstrV(L, L->top++, lj_str_new(L, buf, len)); |
|||
lj_gc_check(L); |
|||
break; |
|||
} |
|||
sz += (sz|1); |
|||
} |
|||
} else { |
|||
setstrV(L, L->top++, &G(L)->strempty); |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(os_time) |
|||
{ |
|||
time_t t; |
|||
if (lua_isnoneornil(L, 1)) { /* called without args? */ |
|||
t = time(NULL); /* get current time */ |
|||
} else { |
|||
struct tm ts; |
|||
luaL_checktype(L, 1, LUA_TTABLE); |
|||
lua_settop(L, 1); /* make sure table is at the top */ |
|||
ts.tm_sec = getfield(L, "sec", 0); |
|||
ts.tm_min = getfield(L, "min", 0); |
|||
ts.tm_hour = getfield(L, "hour", 12); |
|||
ts.tm_mday = getfield(L, "day", -1); |
|||
ts.tm_mon = getfield(L, "month", -1) - 1; |
|||
ts.tm_year = getfield(L, "year", -1) - 1900; |
|||
ts.tm_isdst = getboolfield(L, "isdst"); |
|||
t = mktime(&ts); |
|||
} |
|||
if (t == (time_t)(-1)) |
|||
lua_pushnil(L); |
|||
else |
|||
lua_pushnumber(L, (lua_Number)t); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(os_difftime) |
|||
{ |
|||
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), |
|||
(time_t)(luaL_optnumber(L, 2, (lua_Number)0)))); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
LJLIB_CF(os_setlocale) |
|||
{ |
|||
#if LJ_TARGET_PSVITA |
|||
lua_pushliteral(L, "C"); |
|||
#else |
|||
GCstr *s = lj_lib_optstr(L, 1); |
|||
const char *str = s ? strdata(s) : NULL; |
|||
int opt = lj_lib_checkopt(L, 2, 6, |
|||
"\5ctype\7numeric\4time\7collate\10monetary\1\377\3all"); |
|||
if (opt == 0) opt = LC_CTYPE; |
|||
else if (opt == 1) opt = LC_NUMERIC; |
|||
else if (opt == 2) opt = LC_TIME; |
|||
else if (opt == 3) opt = LC_COLLATE; |
|||
else if (opt == 4) opt = LC_MONETARY; |
|||
else if (opt == 6) opt = LC_ALL; |
|||
lua_pushstring(L, setlocale(opt, str)); |
|||
#endif |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
LUALIB_API int luaopen_os(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, LUA_OSLIBNAME, os); |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,628 @@ |
|||
/*
|
|||
** Package library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2012 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#define lib_package_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_err.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* Error codes for ll_loadfunc. */ |
|||
#define PACKAGE_ERR_LIB 1 |
|||
#define PACKAGE_ERR_FUNC 2 |
|||
#define PACKAGE_ERR_LOAD 3 |
|||
|
|||
/* Redefined in platform specific part. */ |
|||
#define PACKAGE_LIB_FAIL "open" |
|||
#define setprogdir(L) ((void)0) |
|||
|
|||
/* Symbol name prefixes. */ |
|||
#define SYMPREFIX_CF "luaopen_%s" |
|||
#define SYMPREFIX_BC "luaJIT_BC_%s" |
|||
|
|||
#if LJ_TARGET_DLOPEN |
|||
|
|||
#include <dlfcn.h> |
|||
|
|||
static void ll_unloadlib(void *lib) |
|||
{ |
|||
dlclose(lib); |
|||
} |
|||
|
|||
static void *ll_load(lua_State *L, const char *path, int gl) |
|||
{ |
|||
void *lib = dlopen(path, RTLD_NOW | (gl ? RTLD_GLOBAL : RTLD_LOCAL)); |
|||
if (lib == NULL) lua_pushstring(L, dlerror()); |
|||
return lib; |
|||
} |
|||
|
|||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) |
|||
{ |
|||
lua_CFunction f = (lua_CFunction)dlsym(lib, sym); |
|||
if (f == NULL) lua_pushstring(L, dlerror()); |
|||
return f; |
|||
} |
|||
|
|||
static const char *ll_bcsym(void *lib, const char *sym) |
|||
{ |
|||
#if defined(RTLD_DEFAULT) |
|||
if (lib == NULL) lib = RTLD_DEFAULT; |
|||
#elif LJ_TARGET_OSX || LJ_TARGET_BSD |
|||
if (lib == NULL) lib = (void *)(intptr_t)-2; |
|||
#endif |
|||
return (const char *)dlsym(lib, sym); |
|||
} |
|||
|
|||
#elif LJ_TARGET_WINDOWS |
|||
|
|||
#define WIN32_LEAN_AND_MEAN |
|||
#include <windows.h> |
|||
|
|||
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
|||
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4 |
|||
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2 |
|||
BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); |
|||
#endif |
|||
|
|||
#if LJ_TARGET_UWP |
|||
void *LJ_WIN_LOADLIBA(const char *path) |
|||
{ |
|||
DWORD err = GetLastError(); |
|||
wchar_t wpath[256]; |
|||
HANDLE lib = NULL; |
|||
if (MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, 256) > 0) { |
|||
lib = LoadPackagedLibrary(wpath, 0); |
|||
} |
|||
SetLastError(err); |
|||
return lib; |
|||
} |
|||
#endif |
|||
|
|||
#undef setprogdir |
|||
|
|||
static void setprogdir(lua_State *L) |
|||
{ |
|||
char buff[MAX_PATH + 1]; |
|||
char *lb; |
|||
DWORD nsize = sizeof(buff); |
|||
DWORD n = GetModuleFileNameA(NULL, buff, nsize); |
|||
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) { |
|||
luaL_error(L, "unable to get ModuleFileName"); |
|||
} else { |
|||
*lb = '\0'; |
|||
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); |
|||
lua_remove(L, -2); /* remove original string */ |
|||
} |
|||
} |
|||
|
|||
static void pusherror(lua_State *L) |
|||
{ |
|||
DWORD error = GetLastError(); |
|||
#if LJ_TARGET_XBOXONE |
|||
wchar_t wbuffer[128]; |
|||
char buffer[128*2]; |
|||
if (FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, |
|||
NULL, error, 0, wbuffer, sizeof(wbuffer)/sizeof(wchar_t), NULL) && |
|||
WideCharToMultiByte(CP_ACP, 0, wbuffer, 128, buffer, 128*2, NULL, NULL)) |
|||
#else |
|||
char buffer[128]; |
|||
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, |
|||
NULL, error, 0, buffer, sizeof(buffer), NULL)) |
|||
#endif |
|||
lua_pushstring(L, buffer); |
|||
else |
|||
lua_pushfstring(L, "system error %d\n", error); |
|||
} |
|||
|
|||
static void ll_unloadlib(void *lib) |
|||
{ |
|||
FreeLibrary((HINSTANCE)lib); |
|||
} |
|||
|
|||
static void *ll_load(lua_State *L, const char *path, int gl) |
|||
{ |
|||
HINSTANCE lib = LJ_WIN_LOADLIBA(path); |
|||
if (lib == NULL) pusherror(L); |
|||
UNUSED(gl); |
|||
return lib; |
|||
} |
|||
|
|||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) |
|||
{ |
|||
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); |
|||
if (f == NULL) pusherror(L); |
|||
return f; |
|||
} |
|||
|
|||
#if LJ_TARGET_UWP |
|||
EXTERN_C IMAGE_DOS_HEADER __ImageBase; |
|||
#endif |
|||
|
|||
static const char *ll_bcsym(void *lib, const char *sym) |
|||
{ |
|||
if (lib) { |
|||
return (const char *)GetProcAddress((HINSTANCE)lib, sym); |
|||
} else { |
|||
#if LJ_TARGET_UWP |
|||
return (const char *)GetProcAddress((HINSTANCE)&__ImageBase, sym); |
|||
#else |
|||
HINSTANCE h = GetModuleHandleA(NULL); |
|||
const char *p = (const char *)GetProcAddress(h, sym); |
|||
if (p == NULL && GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, |
|||
(const char *)ll_bcsym, &h)) |
|||
p = (const char *)GetProcAddress(h, sym); |
|||
return p; |
|||
#endif |
|||
} |
|||
} |
|||
|
|||
#else |
|||
|
|||
#undef PACKAGE_LIB_FAIL |
|||
#define PACKAGE_LIB_FAIL "absent" |
|||
|
|||
#define DLMSG "dynamic libraries not enabled; no support for target OS" |
|||
|
|||
static void ll_unloadlib(void *lib) |
|||
{ |
|||
UNUSED(lib); |
|||
} |
|||
|
|||
static void *ll_load(lua_State *L, const char *path, int gl) |
|||
{ |
|||
UNUSED(path); UNUSED(gl); |
|||
lua_pushliteral(L, DLMSG); |
|||
return NULL; |
|||
} |
|||
|
|||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym) |
|||
{ |
|||
UNUSED(lib); UNUSED(sym); |
|||
lua_pushliteral(L, DLMSG); |
|||
return NULL; |
|||
} |
|||
|
|||
static const char *ll_bcsym(void *lib, const char *sym) |
|||
{ |
|||
UNUSED(lib); UNUSED(sym); |
|||
return NULL; |
|||
} |
|||
|
|||
#endif |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static void **ll_register(lua_State *L, const char *path) |
|||
{ |
|||
void **plib; |
|||
lua_pushfstring(L, "LOADLIB: %s", path); |
|||
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ |
|||
if (!lua_isnil(L, -1)) { /* is there an entry? */ |
|||
plib = (void **)lua_touserdata(L, -1); |
|||
} else { /* no entry yet; create one */ |
|||
lua_pop(L, 1); |
|||
plib = (void **)lua_newuserdata(L, sizeof(void *)); |
|||
*plib = NULL; |
|||
luaL_setmetatable(L, "_LOADLIB"); |
|||
lua_pushfstring(L, "LOADLIB: %s", path); |
|||
lua_pushvalue(L, -2); |
|||
lua_settable(L, LUA_REGISTRYINDEX); |
|||
} |
|||
return plib; |
|||
} |
|||
|
|||
static const char *mksymname(lua_State *L, const char *modname, |
|||
const char *prefix) |
|||
{ |
|||
const char *funcname; |
|||
const char *mark = strchr(modname, *LUA_IGMARK); |
|||
if (mark) modname = mark + 1; |
|||
funcname = luaL_gsub(L, modname, ".", "_"); |
|||
funcname = lua_pushfstring(L, prefix, funcname); |
|||
lua_remove(L, -2); /* remove 'gsub' result */ |
|||
return funcname; |
|||
} |
|||
|
|||
static int ll_loadfunc(lua_State *L, const char *path, const char *name, int r) |
|||
{ |
|||
void **reg; |
|||
if (strlen(path) >= 4096) { |
|||
lua_pushliteral(L, "path too long"); |
|||
return PACKAGE_ERR_LIB; |
|||
} |
|||
reg = ll_register(L, path); |
|||
if (*reg == NULL) *reg = ll_load(L, path, (*name == '*')); |
|||
if (*reg == NULL) { |
|||
return PACKAGE_ERR_LIB; /* Unable to load library. */ |
|||
} else if (*name == '*') { /* Only load library into global namespace. */ |
|||
lua_pushboolean(L, 1); |
|||
return 0; |
|||
} else { |
|||
const char *sym = r ? name : mksymname(L, name, SYMPREFIX_CF); |
|||
lua_CFunction f = ll_sym(L, *reg, sym); |
|||
if (f) { |
|||
lua_pushcfunction(L, f); |
|||
return 0; |
|||
} |
|||
if (!r) { |
|||
const char *bcdata = ll_bcsym(*reg, mksymname(L, name, SYMPREFIX_BC)); |
|||
lua_pop(L, 1); |
|||
if (bcdata) { |
|||
if (luaL_loadbuffer(L, bcdata, ~(size_t)0, name) != 0) |
|||
return PACKAGE_ERR_LOAD; |
|||
return 0; |
|||
} |
|||
} |
|||
return PACKAGE_ERR_FUNC; /* Unable to find function. */ |
|||
} |
|||
} |
|||
|
|||
static int lj_cf_package_loadlib(lua_State *L) |
|||
{ |
|||
const char *path = luaL_checkstring(L, 1); |
|||
const char *init = luaL_checkstring(L, 2); |
|||
int st = ll_loadfunc(L, path, init, 1); |
|||
if (st == 0) { /* no errors? */ |
|||
return 1; /* return the loaded function */ |
|||
} else { /* error; error message is on stack top */ |
|||
lua_pushnil(L); |
|||
lua_insert(L, -2); |
|||
lua_pushstring(L, (st == PACKAGE_ERR_LIB) ? PACKAGE_LIB_FAIL : "init"); |
|||
return 3; /* return nil, error message, and where */ |
|||
} |
|||
} |
|||
|
|||
static int lj_cf_package_unloadlib(lua_State *L) |
|||
{ |
|||
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); |
|||
if (*lib) ll_unloadlib(*lib); |
|||
*lib = NULL; /* mark library as closed */ |
|||
return 0; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static int readable(const char *filename) |
|||
{ |
|||
FILE *f = fopen(filename, "r"); /* try to open file */ |
|||
if (f == NULL) return 0; /* open failed */ |
|||
fclose(f); |
|||
return 1; |
|||
} |
|||
|
|||
static const char *pushnexttemplate(lua_State *L, const char *path) |
|||
{ |
|||
const char *l; |
|||
while (*path == *LUA_PATHSEP) path++; /* skip separators */ |
|||
if (*path == '\0') return NULL; /* no more templates */ |
|||
l = strchr(path, *LUA_PATHSEP); /* find next separator */ |
|||
if (l == NULL) l = path + strlen(path); |
|||
lua_pushlstring(L, path, (size_t)(l - path)); /* template */ |
|||
return l; |
|||
} |
|||
|
|||
static const char *searchpath (lua_State *L, const char *name, |
|||
const char *path, const char *sep, |
|||
const char *dirsep) |
|||
{ |
|||
luaL_Buffer msg; /* to build error message */ |
|||
luaL_buffinit(L, &msg); |
|||
if (*sep != '\0') /* non-empty separator? */ |
|||
name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */ |
|||
while ((path = pushnexttemplate(L, path)) != NULL) { |
|||
const char *filename = luaL_gsub(L, lua_tostring(L, -1), |
|||
LUA_PATH_MARK, name); |
|||
lua_remove(L, -2); /* remove path template */ |
|||
if (readable(filename)) /* does file exist and is readable? */ |
|||
return filename; /* return that file name */ |
|||
lua_pushfstring(L, "\n\tno file " LUA_QS, filename); |
|||
lua_remove(L, -2); /* remove file name */ |
|||
luaL_addvalue(&msg); /* concatenate error msg. entry */ |
|||
} |
|||
luaL_pushresult(&msg); /* create error message */ |
|||
return NULL; /* not found */ |
|||
} |
|||
|
|||
static int lj_cf_package_searchpath(lua_State *L) |
|||
{ |
|||
const char *f = searchpath(L, luaL_checkstring(L, 1), |
|||
luaL_checkstring(L, 2), |
|||
luaL_optstring(L, 3, "."), |
|||
luaL_optstring(L, 4, LUA_DIRSEP)); |
|||
if (f != NULL) { |
|||
return 1; |
|||
} else { /* error message is on top of the stack */ |
|||
lua_pushnil(L); |
|||
lua_insert(L, -2); |
|||
return 2; /* return nil + error message */ |
|||
} |
|||
} |
|||
|
|||
static const char *findfile(lua_State *L, const char *name, |
|||
const char *pname) |
|||
{ |
|||
const char *path; |
|||
lua_getfield(L, LUA_ENVIRONINDEX, pname); |
|||
path = lua_tostring(L, -1); |
|||
if (path == NULL) |
|||
luaL_error(L, LUA_QL("package.%s") " must be a string", pname); |
|||
return searchpath(L, name, path, ".", LUA_DIRSEP); |
|||
} |
|||
|
|||
static void loaderror(lua_State *L, const char *filename) |
|||
{ |
|||
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", |
|||
lua_tostring(L, 1), filename, lua_tostring(L, -1)); |
|||
} |
|||
|
|||
static int lj_cf_package_loader_lua(lua_State *L) |
|||
{ |
|||
const char *filename; |
|||
const char *name = luaL_checkstring(L, 1); |
|||
filename = findfile(L, name, "path"); |
|||
if (filename == NULL) return 1; /* library not found in this path */ |
|||
if (luaL_loadfile(L, filename) != 0) |
|||
loaderror(L, filename); |
|||
return 1; /* library loaded successfully */ |
|||
} |
|||
|
|||
static int lj_cf_package_loader_c(lua_State *L) |
|||
{ |
|||
const char *name = luaL_checkstring(L, 1); |
|||
const char *filename = findfile(L, name, "cpath"); |
|||
if (filename == NULL) return 1; /* library not found in this path */ |
|||
if (ll_loadfunc(L, filename, name, 0) != 0) |
|||
loaderror(L, filename); |
|||
return 1; /* library loaded successfully */ |
|||
} |
|||
|
|||
static int lj_cf_package_loader_croot(lua_State *L) |
|||
{ |
|||
const char *filename; |
|||
const char *name = luaL_checkstring(L, 1); |
|||
const char *p = strchr(name, '.'); |
|||
int st; |
|||
if (p == NULL) return 0; /* is root */ |
|||
lua_pushlstring(L, name, (size_t)(p - name)); |
|||
filename = findfile(L, lua_tostring(L, -1), "cpath"); |
|||
if (filename == NULL) return 1; /* root not found */ |
|||
if ((st = ll_loadfunc(L, filename, name, 0)) != 0) { |
|||
if (st != PACKAGE_ERR_FUNC) loaderror(L, filename); /* real error */ |
|||
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, |
|||
name, filename); |
|||
return 1; /* function not found */ |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
static int lj_cf_package_loader_preload(lua_State *L) |
|||
{ |
|||
const char *name = luaL_checkstring(L, 1); |
|||
lua_getfield(L, LUA_ENVIRONINDEX, "preload"); |
|||
if (!lua_istable(L, -1)) |
|||
luaL_error(L, LUA_QL("package.preload") " must be a table"); |
|||
lua_getfield(L, -1, name); |
|||
if (lua_isnil(L, -1)) { /* Not found? */ |
|||
const char *bcname = mksymname(L, name, SYMPREFIX_BC); |
|||
const char *bcdata = ll_bcsym(NULL, bcname); |
|||
if (bcdata == NULL || luaL_loadbuffer(L, bcdata, ~(size_t)0, name) != 0) |
|||
lua_pushfstring(L, "\n\tno field package.preload['%s']", name); |
|||
} |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define KEY_SENTINEL (U64x(80000000,00000000)|'s') |
|||
|
|||
static int lj_cf_package_require(lua_State *L) |
|||
{ |
|||
const char *name = luaL_checkstring(L, 1); |
|||
int i; |
|||
lua_settop(L, 1); /* _LOADED table will be at index 2 */ |
|||
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); |
|||
lua_getfield(L, 2, name); |
|||
if (lua_toboolean(L, -1)) { /* is it there? */ |
|||
if ((L->top-1)->u64 == KEY_SENTINEL) /* check loops */ |
|||
luaL_error(L, "loop or previous error loading module " LUA_QS, name); |
|||
return 1; /* package is already loaded */ |
|||
} |
|||
/* else must load it; iterate over available loaders */ |
|||
lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); |
|||
if (!lua_istable(L, -1)) |
|||
luaL_error(L, LUA_QL("package.loaders") " must be a table"); |
|||
lua_pushliteral(L, ""); /* error message accumulator */ |
|||
for (i = 1; ; i++) { |
|||
lua_rawgeti(L, -2, i); /* get a loader */ |
|||
if (lua_isnil(L, -1)) |
|||
luaL_error(L, "module " LUA_QS " not found:%s", |
|||
name, lua_tostring(L, -2)); |
|||
lua_pushstring(L, name); |
|||
lua_call(L, 1, 1); /* call it */ |
|||
if (lua_isfunction(L, -1)) /* did it find module? */ |
|||
break; /* module loaded successfully */ |
|||
else if (lua_isstring(L, -1)) /* loader returned error message? */ |
|||
lua_concat(L, 2); /* accumulate it */ |
|||
else |
|||
lua_pop(L, 1); |
|||
} |
|||
(L->top++)->u64 = KEY_SENTINEL; |
|||
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ |
|||
lua_pushstring(L, name); /* pass name as argument to module */ |
|||
lua_call(L, 1, 1); /* run loaded module */ |
|||
if (!lua_isnil(L, -1)) /* non-nil return? */ |
|||
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ |
|||
lua_getfield(L, 2, name); |
|||
if ((L->top-1)->u64 == KEY_SENTINEL) { /* module did not set a value? */ |
|||
lua_pushboolean(L, 1); /* use true as result */ |
|||
lua_pushvalue(L, -1); /* extra copy to be returned */ |
|||
lua_setfield(L, 2, name); /* _LOADED[name] = true */ |
|||
} |
|||
lj_lib_checkfpu(L); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static void setfenv(lua_State *L) |
|||
{ |
|||
lua_Debug ar; |
|||
if (lua_getstack(L, 1, &ar) == 0 || |
|||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */ |
|||
lua_iscfunction(L, -1)) |
|||
luaL_error(L, LUA_QL("module") " not called from a Lua function"); |
|||
lua_pushvalue(L, -2); |
|||
lua_setfenv(L, -2); |
|||
lua_pop(L, 1); |
|||
} |
|||
|
|||
static void dooptions(lua_State *L, int n) |
|||
{ |
|||
int i; |
|||
for (i = 2; i <= n; i++) { |
|||
lua_pushvalue(L, i); /* get option (a function) */ |
|||
lua_pushvalue(L, -2); /* module */ |
|||
lua_call(L, 1, 0); |
|||
} |
|||
} |
|||
|
|||
static void modinit(lua_State *L, const char *modname) |
|||
{ |
|||
const char *dot; |
|||
lua_pushvalue(L, -1); |
|||
lua_setfield(L, -2, "_M"); /* module._M = module */ |
|||
lua_pushstring(L, modname); |
|||
lua_setfield(L, -2, "_NAME"); |
|||
dot = strrchr(modname, '.'); /* look for last dot in module name */ |
|||
if (dot == NULL) dot = modname; else dot++; |
|||
/* set _PACKAGE as package name (full module name minus last part) */ |
|||
lua_pushlstring(L, modname, (size_t)(dot - modname)); |
|||
lua_setfield(L, -2, "_PACKAGE"); |
|||
} |
|||
|
|||
static int lj_cf_package_module(lua_State *L) |
|||
{ |
|||
const char *modname = luaL_checkstring(L, 1); |
|||
int lastarg = (int)(L->top - L->base); |
|||
luaL_pushmodule(L, modname, 1); |
|||
lua_getfield(L, -1, "_NAME"); |
|||
if (!lua_isnil(L, -1)) { /* Module already initialized? */ |
|||
lua_pop(L, 1); |
|||
} else { |
|||
lua_pop(L, 1); |
|||
modinit(L, modname); |
|||
} |
|||
lua_pushvalue(L, -1); |
|||
setfenv(L); |
|||
dooptions(L, lastarg); |
|||
return LJ_52; |
|||
} |
|||
|
|||
static int lj_cf_package_seeall(lua_State *L) |
|||
{ |
|||
luaL_checktype(L, 1, LUA_TTABLE); |
|||
if (!lua_getmetatable(L, 1)) { |
|||
lua_createtable(L, 0, 1); /* create new metatable */ |
|||
lua_pushvalue(L, -1); |
|||
lua_setmetatable(L, 1); |
|||
} |
|||
lua_pushvalue(L, LUA_GLOBALSINDEX); |
|||
lua_setfield(L, -2, "__index"); /* mt.__index = _G */ |
|||
return 0; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define AUXMARK "\1" |
|||
|
|||
static void setpath(lua_State *L, const char *fieldname, const char *envname, |
|||
const char *def, int noenv) |
|||
{ |
|||
#if LJ_TARGET_CONSOLE |
|||
const char *path = NULL; |
|||
UNUSED(envname); |
|||
#else |
|||
const char *path = getenv(envname); |
|||
#endif |
|||
if (path == NULL || noenv) { |
|||
lua_pushstring(L, def); |
|||
} else { |
|||
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, |
|||
LUA_PATHSEP AUXMARK LUA_PATHSEP); |
|||
luaL_gsub(L, path, AUXMARK, def); |
|||
lua_remove(L, -2); |
|||
} |
|||
setprogdir(L); |
|||
lua_setfield(L, -2, fieldname); |
|||
} |
|||
|
|||
static const luaL_Reg package_lib[] = { |
|||
{ "loadlib", lj_cf_package_loadlib }, |
|||
{ "searchpath", lj_cf_package_searchpath }, |
|||
{ "seeall", lj_cf_package_seeall }, |
|||
{ NULL, NULL } |
|||
}; |
|||
|
|||
static const luaL_Reg package_global[] = { |
|||
{ "module", lj_cf_package_module }, |
|||
{ "require", lj_cf_package_require }, |
|||
{ NULL, NULL } |
|||
}; |
|||
|
|||
static const lua_CFunction package_loaders[] = |
|||
{ |
|||
lj_cf_package_loader_preload, |
|||
lj_cf_package_loader_lua, |
|||
lj_cf_package_loader_c, |
|||
lj_cf_package_loader_croot, |
|||
NULL |
|||
}; |
|||
|
|||
LUALIB_API int luaopen_package(lua_State *L) |
|||
{ |
|||
int i; |
|||
int noenv; |
|||
luaL_newmetatable(L, "_LOADLIB"); |
|||
lj_lib_pushcf(L, lj_cf_package_unloadlib, 1); |
|||
lua_setfield(L, -2, "__gc"); |
|||
luaL_register(L, LUA_LOADLIBNAME, package_lib); |
|||
lua_copy(L, -1, LUA_ENVIRONINDEX); |
|||
lua_createtable(L, sizeof(package_loaders)/sizeof(package_loaders[0])-1, 0); |
|||
for (i = 0; package_loaders[i] != NULL; i++) { |
|||
lj_lib_pushcf(L, package_loaders[i], 1); |
|||
lua_rawseti(L, -2, i+1); |
|||
} |
|||
#if LJ_52 |
|||
lua_pushvalue(L, -1); |
|||
lua_setfield(L, -3, "searchers"); |
|||
#endif |
|||
lua_setfield(L, -2, "loaders"); |
|||
lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV"); |
|||
noenv = lua_toboolean(L, -1); |
|||
lua_pop(L, 1); |
|||
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT, noenv); |
|||
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT, noenv); |
|||
lua_pushliteral(L, LUA_PATH_CONFIG); |
|||
lua_setfield(L, -2, "config"); |
|||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16); |
|||
lua_setfield(L, -2, "loaded"); |
|||
luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", 4); |
|||
lua_setfield(L, -2, "preload"); |
|||
lua_pushvalue(L, LUA_GLOBALSINDEX); |
|||
luaL_register(L, NULL, package_global); |
|||
lua_pop(L, 1); |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,676 @@ |
|||
/*
|
|||
** String library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#define lib_string_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_str.h" |
|||
#include "lj_tab.h" |
|||
#include "lj_meta.h" |
|||
#include "lj_state.h" |
|||
#include "lj_ff.h" |
|||
#include "lj_bcdump.h" |
|||
#include "lj_char.h" |
|||
#include "lj_strfmt.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_string |
|||
|
|||
LJLIB_LUA(string_len) /*
|
|||
function(s) |
|||
CHECK_str(s) |
|||
return #s |
|||
end |
|||
*/ |
|||
|
|||
LJLIB_ASM(string_byte) LJLIB_REC(string_range 0) |
|||
{ |
|||
GCstr *s = lj_lib_checkstr(L, 1); |
|||
int32_t len = (int32_t)s->len; |
|||
int32_t start = lj_lib_optint(L, 2, 1); |
|||
int32_t stop = lj_lib_optint(L, 3, start); |
|||
int32_t n, i; |
|||
const unsigned char *p; |
|||
if (stop < 0) stop += len+1; |
|||
if (start < 0) start += len+1; |
|||
if (start <= 0) start = 1; |
|||
if (stop > len) stop = len; |
|||
if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */ |
|||
start--; |
|||
n = stop - start; |
|||
if ((uint32_t)n > LUAI_MAXCSTACK) |
|||
lj_err_caller(L, LJ_ERR_STRSLC); |
|||
lj_state_checkstack(L, (MSize)n); |
|||
p = (const unsigned char *)strdata(s) + start; |
|||
for (i = 0; i < n; i++) |
|||
setintV(L->base + i-1-LJ_FR2, p[i]); |
|||
return FFH_RES(n); |
|||
} |
|||
|
|||
LJLIB_ASM(string_char) LJLIB_REC(.) |
|||
{ |
|||
int i, nargs = (int)(L->top - L->base); |
|||
char *buf = lj_buf_tmp(L, (MSize)nargs); |
|||
for (i = 1; i <= nargs; i++) { |
|||
int32_t k = lj_lib_checkint(L, i); |
|||
if (!checku8(k)) |
|||
lj_err_arg(L, i, LJ_ERR_BADVAL); |
|||
buf[i-1] = (char)k; |
|||
} |
|||
setstrV(L, L->base-1-LJ_FR2, lj_str_new(L, buf, (size_t)nargs)); |
|||
return FFH_RES(1); |
|||
} |
|||
|
|||
LJLIB_ASM(string_sub) LJLIB_REC(string_range 1) |
|||
{ |
|||
lj_lib_checkstr(L, 1); |
|||
lj_lib_checkint(L, 2); |
|||
setintV(L->base+2, lj_lib_optint(L, 3, -1)); |
|||
return FFH_RETRY; |
|||
} |
|||
|
|||
LJLIB_CF(string_rep) LJLIB_REC(.) |
|||
{ |
|||
GCstr *s = lj_lib_checkstr(L, 1); |
|||
int32_t rep = lj_lib_checkint(L, 2); |
|||
GCstr *sep = lj_lib_optstr(L, 3); |
|||
SBuf *sb = lj_buf_tmp_(L); |
|||
if (sep && rep > 1) { |
|||
GCstr *s2 = lj_buf_cat2str(L, sep, s); |
|||
lj_buf_reset(sb); |
|||
lj_buf_putstr(sb, s); |
|||
s = s2; |
|||
rep--; |
|||
} |
|||
sb = lj_buf_putstr_rep(sb, s, rep); |
|||
setstrV(L, L->top-1, lj_buf_str(L, sb)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_ASM(string_reverse) LJLIB_REC(string_op IRCALL_lj_buf_putstr_reverse) |
|||
{ |
|||
lj_lib_checkstr(L, 1); |
|||
return FFH_RETRY; |
|||
} |
|||
LJLIB_ASM_(string_lower) LJLIB_REC(string_op IRCALL_lj_buf_putstr_lower) |
|||
LJLIB_ASM_(string_upper) LJLIB_REC(string_op IRCALL_lj_buf_putstr_upper) |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static int writer_buf(lua_State *L, const void *p, size_t size, void *sb) |
|||
{ |
|||
lj_buf_putmem((SBuf *)sb, p, (MSize)size); |
|||
UNUSED(L); |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_CF(string_dump) |
|||
{ |
|||
GCfunc *fn = lj_lib_checkfunc(L, 1); |
|||
int strip = L->base+1 < L->top && tvistruecond(L->base+1); |
|||
SBuf *sb = lj_buf_tmp_(L); /* Assumes lj_bcwrite() doesn't use tmpbuf. */ |
|||
L->top = L->base+1; |
|||
if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, sb, strip)) |
|||
lj_err_caller(L, LJ_ERR_STRDUMP); |
|||
setstrV(L, L->top-1, lj_buf_str(L, sb)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
/* macro to `unsign' a character */ |
|||
#define uchar(c) ((unsigned char)(c)) |
|||
|
|||
#define CAP_UNFINISHED (-1) |
|||
#define CAP_POSITION (-2) |
|||
|
|||
typedef struct MatchState { |
|||
const char *src_init; /* init of source string */ |
|||
const char *src_end; /* end (`\0') of source string */ |
|||
lua_State *L; |
|||
int level; /* total number of captures (finished or unfinished) */ |
|||
int depth; |
|||
struct { |
|||
const char *init; |
|||
ptrdiff_t len; |
|||
} capture[LUA_MAXCAPTURES]; |
|||
} MatchState; |
|||
|
|||
#define L_ESC '%' |
|||
|
|||
static int check_capture(MatchState *ms, int l) |
|||
{ |
|||
l -= '1'; |
|||
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) |
|||
lj_err_caller(ms->L, LJ_ERR_STRCAPI); |
|||
return l; |
|||
} |
|||
|
|||
static int capture_to_close(MatchState *ms) |
|||
{ |
|||
int level = ms->level; |
|||
for (level--; level>=0; level--) |
|||
if (ms->capture[level].len == CAP_UNFINISHED) return level; |
|||
lj_err_caller(ms->L, LJ_ERR_STRPATC); |
|||
return 0; /* unreachable */ |
|||
} |
|||
|
|||
static const char *classend(MatchState *ms, const char *p) |
|||
{ |
|||
switch (*p++) { |
|||
case L_ESC: |
|||
if (*p == '\0') |
|||
lj_err_caller(ms->L, LJ_ERR_STRPATE); |
|||
return p+1; |
|||
case '[': |
|||
if (*p == '^') p++; |
|||
do { /* look for a `]' */ |
|||
if (*p == '\0') |
|||
lj_err_caller(ms->L, LJ_ERR_STRPATM); |
|||
if (*(p++) == L_ESC && *p != '\0') |
|||
p++; /* skip escapes (e.g. `%]') */ |
|||
} while (*p != ']'); |
|||
return p+1; |
|||
default: |
|||
return p; |
|||
} |
|||
} |
|||
|
|||
static const unsigned char match_class_map[32] = { |
|||
0,LJ_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0, |
|||
LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0, |
|||
LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0 |
|||
}; |
|||
|
|||
static int match_class(int c, int cl) |
|||
{ |
|||
if ((cl & 0xc0) == 0x40) { |
|||
int t = match_class_map[(cl&0x1f)]; |
|||
if (t) { |
|||
t = lj_char_isa(c, t); |
|||
return (cl & 0x20) ? t : !t; |
|||
} |
|||
if (cl == 'z') return c == 0; |
|||
if (cl == 'Z') return c != 0; |
|||
} |
|||
return (cl == c); |
|||
} |
|||
|
|||
static int matchbracketclass(int c, const char *p, const char *ec) |
|||
{ |
|||
int sig = 1; |
|||
if (*(p+1) == '^') { |
|||
sig = 0; |
|||
p++; /* skip the `^' */ |
|||
} |
|||
while (++p < ec) { |
|||
if (*p == L_ESC) { |
|||
p++; |
|||
if (match_class(c, uchar(*p))) |
|||
return sig; |
|||
} |
|||
else if ((*(p+1) == '-') && (p+2 < ec)) { |
|||
p+=2; |
|||
if (uchar(*(p-2)) <= c && c <= uchar(*p)) |
|||
return sig; |
|||
} |
|||
else if (uchar(*p) == c) return sig; |
|||
} |
|||
return !sig; |
|||
} |
|||
|
|||
static int singlematch(int c, const char *p, const char *ep) |
|||
{ |
|||
switch (*p) { |
|||
case '.': return 1; /* matches any char */ |
|||
case L_ESC: return match_class(c, uchar(*(p+1))); |
|||
case '[': return matchbracketclass(c, p, ep-1); |
|||
default: return (uchar(*p) == c); |
|||
} |
|||
} |
|||
|
|||
static const char *match(MatchState *ms, const char *s, const char *p); |
|||
|
|||
static const char *matchbalance(MatchState *ms, const char *s, const char *p) |
|||
{ |
|||
if (*p == 0 || *(p+1) == 0) |
|||
lj_err_caller(ms->L, LJ_ERR_STRPATU); |
|||
if (*s != *p) { |
|||
return NULL; |
|||
} else { |
|||
int b = *p; |
|||
int e = *(p+1); |
|||
int cont = 1; |
|||
while (++s < ms->src_end) { |
|||
if (*s == e) { |
|||
if (--cont == 0) return s+1; |
|||
} else if (*s == b) { |
|||
cont++; |
|||
} |
|||
} |
|||
} |
|||
return NULL; /* string ends out of balance */ |
|||
} |
|||
|
|||
static const char *max_expand(MatchState *ms, const char *s, |
|||
const char *p, const char *ep) |
|||
{ |
|||
ptrdiff_t i = 0; /* counts maximum expand for item */ |
|||
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep)) |
|||
i++; |
|||
/* keeps trying to match with the maximum repetitions */ |
|||
while (i>=0) { |
|||
const char *res = match(ms, (s+i), ep+1); |
|||
if (res) return res; |
|||
i--; /* else didn't match; reduce 1 repetition to try again */ |
|||
} |
|||
return NULL; |
|||
} |
|||
|
|||
static const char *min_expand(MatchState *ms, const char *s, |
|||
const char *p, const char *ep) |
|||
{ |
|||
for (;;) { |
|||
const char *res = match(ms, s, ep+1); |
|||
if (res != NULL) |
|||
return res; |
|||
else if (s<ms->src_end && singlematch(uchar(*s), p, ep)) |
|||
s++; /* try with one more repetition */ |
|||
else |
|||
return NULL; |
|||
} |
|||
} |
|||
|
|||
static const char *start_capture(MatchState *ms, const char *s, |
|||
const char *p, int what) |
|||
{ |
|||
const char *res; |
|||
int level = ms->level; |
|||
if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN); |
|||
ms->capture[level].init = s; |
|||
ms->capture[level].len = what; |
|||
ms->level = level+1; |
|||
if ((res=match(ms, s, p)) == NULL) /* match failed? */ |
|||
ms->level--; /* undo capture */ |
|||
return res; |
|||
} |
|||
|
|||
static const char *end_capture(MatchState *ms, const char *s, |
|||
const char *p) |
|||
{ |
|||
int l = capture_to_close(ms); |
|||
const char *res; |
|||
ms->capture[l].len = s - ms->capture[l].init; /* close capture */ |
|||
if ((res = match(ms, s, p)) == NULL) /* match failed? */ |
|||
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ |
|||
return res; |
|||
} |
|||
|
|||
static const char *match_capture(MatchState *ms, const char *s, int l) |
|||
{ |
|||
size_t len; |
|||
l = check_capture(ms, l); |
|||
len = (size_t)ms->capture[l].len; |
|||
if ((size_t)(ms->src_end-s) >= len && |
|||
memcmp(ms->capture[l].init, s, len) == 0) |
|||
return s+len; |
|||
else |
|||
return NULL; |
|||
} |
|||
|
|||
static const char *match(MatchState *ms, const char *s, const char *p) |
|||
{ |
|||
if (++ms->depth > LJ_MAX_XLEVEL) |
|||
lj_err_caller(ms->L, LJ_ERR_STRPATX); |
|||
init: /* using goto's to optimize tail recursion */ |
|||
switch (*p) { |
|||
case '(': /* start capture */ |
|||
if (*(p+1) == ')') /* position capture? */ |
|||
s = start_capture(ms, s, p+2, CAP_POSITION); |
|||
else |
|||
s = start_capture(ms, s, p+1, CAP_UNFINISHED); |
|||
break; |
|||
case ')': /* end capture */ |
|||
s = end_capture(ms, s, p+1); |
|||
break; |
|||
case L_ESC: |
|||
switch (*(p+1)) { |
|||
case 'b': /* balanced string? */ |
|||
s = matchbalance(ms, s, p+2); |
|||
if (s == NULL) break; |
|||
p+=4; |
|||
goto init; /* else s = match(ms, s, p+4); */ |
|||
case 'f': { /* frontier? */ |
|||
const char *ep; char previous; |
|||
p += 2; |
|||
if (*p != '[') |
|||
lj_err_caller(ms->L, LJ_ERR_STRPATB); |
|||
ep = classend(ms, p); /* points to what is next */ |
|||
previous = (s == ms->src_init) ? '\0' : *(s-1); |
|||
if (matchbracketclass(uchar(previous), p, ep-1) || |
|||
!matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; } |
|||
p=ep; |
|||
goto init; /* else s = match(ms, s, ep); */ |
|||
} |
|||
default: |
|||
if (lj_char_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ |
|||
s = match_capture(ms, s, uchar(*(p+1))); |
|||
if (s == NULL) break; |
|||
p+=2; |
|||
goto init; /* else s = match(ms, s, p+2) */ |
|||
} |
|||
goto dflt; /* case default */ |
|||
} |
|||
break; |
|||
case '\0': /* end of pattern */ |
|||
break; /* match succeeded */ |
|||
case '$': |
|||
/* is the `$' the last char in pattern? */ |
|||
if (*(p+1) != '\0') goto dflt; |
|||
if (s != ms->src_end) s = NULL; /* check end of string */ |
|||
break; |
|||
default: dflt: { /* it is a pattern item */ |
|||
const char *ep = classend(ms, p); /* points to what is next */ |
|||
int m = s<ms->src_end && singlematch(uchar(*s), p, ep); |
|||
switch (*ep) { |
|||
case '?': { /* optional */ |
|||
const char *res; |
|||
if (m && ((res=match(ms, s+1, ep+1)) != NULL)) { |
|||
s = res; |
|||
break; |
|||
} |
|||
p=ep+1; |
|||
goto init; /* else s = match(ms, s, ep+1); */ |
|||
} |
|||
case '*': /* 0 or more repetitions */ |
|||
s = max_expand(ms, s, p, ep); |
|||
break; |
|||
case '+': /* 1 or more repetitions */ |
|||
s = (m ? max_expand(ms, s+1, p, ep) : NULL); |
|||
break; |
|||
case '-': /* 0 or more repetitions (minimum) */ |
|||
s = min_expand(ms, s, p, ep); |
|||
break; |
|||
default: |
|||
if (m) { s++; p=ep; goto init; } /* else s = match(ms, s+1, ep); */ |
|||
s = NULL; |
|||
break; |
|||
} |
|||
break; |
|||
} |
|||
} |
|||
ms->depth--; |
|||
return s; |
|||
} |
|||
|
|||
static void push_onecapture(MatchState *ms, int i, const char *s, const char *e) |
|||
{ |
|||
if (i >= ms->level) { |
|||
if (i == 0) /* ms->level == 0, too */ |
|||
lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */ |
|||
else |
|||
lj_err_caller(ms->L, LJ_ERR_STRCAPI); |
|||
} else { |
|||
ptrdiff_t l = ms->capture[i].len; |
|||
if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU); |
|||
if (l == CAP_POSITION) |
|||
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); |
|||
else |
|||
lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l); |
|||
} |
|||
} |
|||
|
|||
static int push_captures(MatchState *ms, const char *s, const char *e) |
|||
{ |
|||
int i; |
|||
int nlevels = (ms->level == 0 && s) ? 1 : ms->level; |
|||
luaL_checkstack(ms->L, nlevels, "too many captures"); |
|||
for (i = 0; i < nlevels; i++) |
|||
push_onecapture(ms, i, s, e); |
|||
return nlevels; /* number of strings pushed */ |
|||
} |
|||
|
|||
static int str_find_aux(lua_State *L, int find) |
|||
{ |
|||
GCstr *s = lj_lib_checkstr(L, 1); |
|||
GCstr *p = lj_lib_checkstr(L, 2); |
|||
int32_t start = lj_lib_optint(L, 3, 1); |
|||
MSize st; |
|||
if (start < 0) start += (int32_t)s->len; else start--; |
|||
if (start < 0) start = 0; |
|||
st = (MSize)start; |
|||
if (st > s->len) { |
|||
#if LJ_52 |
|||
setnilV(L->top-1); |
|||
return 1; |
|||
#else |
|||
st = s->len; |
|||
#endif |
|||
} |
|||
if (find && ((L->base+3 < L->top && tvistruecond(L->base+3)) || |
|||
!lj_str_haspattern(p))) { /* Search for fixed string. */ |
|||
const char *q = lj_str_find(strdata(s)+st, strdata(p), s->len-st, p->len); |
|||
if (q) { |
|||
setintV(L->top-2, (int32_t)(q-strdata(s)) + 1); |
|||
setintV(L->top-1, (int32_t)(q-strdata(s)) + (int32_t)p->len); |
|||
return 2; |
|||
} |
|||
} else { /* Search for pattern. */ |
|||
MatchState ms; |
|||
const char *pstr = strdata(p); |
|||
const char *sstr = strdata(s) + st; |
|||
int anchor = 0; |
|||
if (*pstr == '^') { pstr++; anchor = 1; } |
|||
ms.L = L; |
|||
ms.src_init = strdata(s); |
|||
ms.src_end = strdata(s) + s->len; |
|||
do { /* Loop through string and try to match the pattern. */ |
|||
const char *q; |
|||
ms.level = ms.depth = 0; |
|||
q = match(&ms, sstr, pstr); |
|||
if (q) { |
|||
if (find) { |
|||
setintV(L->top++, (int32_t)(sstr-(strdata(s)-1))); |
|||
setintV(L->top++, (int32_t)(q-strdata(s))); |
|||
return push_captures(&ms, NULL, NULL) + 2; |
|||
} else { |
|||
return push_captures(&ms, sstr, q); |
|||
} |
|||
} |
|||
} while (sstr++ < ms.src_end && !anchor); |
|||
} |
|||
setnilV(L->top-1); /* Not found. */ |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(string_find) LJLIB_REC(.) |
|||
{ |
|||
return str_find_aux(L, 1); |
|||
} |
|||
|
|||
LJLIB_CF(string_match) |
|||
{ |
|||
return str_find_aux(L, 0); |
|||
} |
|||
|
|||
LJLIB_NOREG LJLIB_CF(string_gmatch_aux) |
|||
{ |
|||
const char *p = strVdata(lj_lib_upvalue(L, 2)); |
|||
GCstr *str = strV(lj_lib_upvalue(L, 1)); |
|||
const char *s = strdata(str); |
|||
TValue *tvpos = lj_lib_upvalue(L, 3); |
|||
const char *src = s + tvpos->u32.lo; |
|||
MatchState ms; |
|||
ms.L = L; |
|||
ms.src_init = s; |
|||
ms.src_end = s + str->len; |
|||
for (; src <= ms.src_end; src++) { |
|||
const char *e; |
|||
ms.level = ms.depth = 0; |
|||
if ((e = match(&ms, src, p)) != NULL) { |
|||
int32_t pos = (int32_t)(e - s); |
|||
if (e == src) pos++; /* Ensure progress for empty match. */ |
|||
tvpos->u32.lo = (uint32_t)pos; |
|||
return push_captures(&ms, src, e); |
|||
} |
|||
} |
|||
return 0; /* not found */ |
|||
} |
|||
|
|||
LJLIB_CF(string_gmatch) |
|||
{ |
|||
lj_lib_checkstr(L, 1); |
|||
lj_lib_checkstr(L, 2); |
|||
L->top = L->base+3; |
|||
(L->top-1)->u64 = 0; |
|||
lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3); |
|||
return 1; |
|||
} |
|||
|
|||
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e) |
|||
{ |
|||
size_t l, i; |
|||
const char *news = lua_tolstring(ms->L, 3, &l); |
|||
for (i = 0; i < l; i++) { |
|||
if (news[i] != L_ESC) { |
|||
luaL_addchar(b, news[i]); |
|||
} else { |
|||
i++; /* skip ESC */ |
|||
if (!lj_char_isdigit(uchar(news[i]))) { |
|||
luaL_addchar(b, news[i]); |
|||
} else if (news[i] == '0') { |
|||
luaL_addlstring(b, s, (size_t)(e - s)); |
|||
} else { |
|||
push_onecapture(ms, news[i] - '1', s, e); |
|||
luaL_addvalue(b); /* add capture to accumulated result */ |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
static void add_value(MatchState *ms, luaL_Buffer *b, |
|||
const char *s, const char *e) |
|||
{ |
|||
lua_State *L = ms->L; |
|||
switch (lua_type(L, 3)) { |
|||
case LUA_TNUMBER: |
|||
case LUA_TSTRING: { |
|||
add_s(ms, b, s, e); |
|||
return; |
|||
} |
|||
case LUA_TFUNCTION: { |
|||
int n; |
|||
lua_pushvalue(L, 3); |
|||
n = push_captures(ms, s, e); |
|||
lua_call(L, n, 1); |
|||
break; |
|||
} |
|||
case LUA_TTABLE: { |
|||
push_onecapture(ms, 0, s, e); |
|||
lua_gettable(L, 3); |
|||
break; |
|||
} |
|||
} |
|||
if (!lua_toboolean(L, -1)) { /* nil or false? */ |
|||
lua_pop(L, 1); |
|||
lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */ |
|||
} else if (!lua_isstring(L, -1)) { |
|||
lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1)); |
|||
} |
|||
luaL_addvalue(b); /* add result to accumulator */ |
|||
} |
|||
|
|||
LJLIB_CF(string_gsub) |
|||
{ |
|||
size_t srcl; |
|||
const char *src = luaL_checklstring(L, 1, &srcl); |
|||
const char *p = luaL_checkstring(L, 2); |
|||
int tr = lua_type(L, 3); |
|||
int max_s = luaL_optint(L, 4, (int)(srcl+1)); |
|||
int anchor = (*p == '^') ? (p++, 1) : 0; |
|||
int n = 0; |
|||
MatchState ms; |
|||
luaL_Buffer b; |
|||
if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING || |
|||
tr == LUA_TFUNCTION || tr == LUA_TTABLE)) |
|||
lj_err_arg(L, 3, LJ_ERR_NOSFT); |
|||
luaL_buffinit(L, &b); |
|||
ms.L = L; |
|||
ms.src_init = src; |
|||
ms.src_end = src+srcl; |
|||
while (n < max_s) { |
|||
const char *e; |
|||
ms.level = ms.depth = 0; |
|||
e = match(&ms, src, p); |
|||
if (e) { |
|||
n++; |
|||
add_value(&ms, &b, src, e); |
|||
} |
|||
if (e && e>src) /* non empty match? */ |
|||
src = e; /* skip it */ |
|||
else if (src < ms.src_end) |
|||
luaL_addchar(&b, *src++); |
|||
else |
|||
break; |
|||
if (anchor) |
|||
break; |
|||
} |
|||
luaL_addlstring(&b, src, (size_t)(ms.src_end-src)); |
|||
luaL_pushresult(&b); |
|||
lua_pushinteger(L, n); /* number of substitutions */ |
|||
return 2; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
LJLIB_CF(string_format) LJLIB_REC(.) |
|||
{ |
|||
int retry = 0; |
|||
SBuf *sb; |
|||
do { |
|||
sb = lj_buf_tmp_(L); |
|||
retry = lj_strfmt_putarg(L, sb, 1, -retry); |
|||
} while (retry > 0); |
|||
setstrV(L, L->top-1, lj_buf_str(L, sb)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
LUALIB_API int luaopen_string(lua_State *L) |
|||
{ |
|||
GCtab *mt; |
|||
global_State *g; |
|||
LJ_LIB_REG(L, LUA_STRLIBNAME, string); |
|||
mt = lj_tab_new(L, 0, 1); |
|||
/* NOBARRIER: basemt is a GC root. */ |
|||
g = G(L); |
|||
setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt)); |
|||
settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1)); |
|||
mt->nomm = (uint8_t)(~(1u<<MM_index)); |
|||
#if LJ_HASBUFFER |
|||
lj_lib_prereg(L, LUA_STRLIBNAME ".buffer", luaopen_string_buffer, tabV(L->top-1)); |
|||
#endif |
|||
return 1; |
|||
} |
|||
|
@ -0,0 +1,327 @@ |
|||
/*
|
|||
** Table library. |
|||
** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h |
|||
** |
|||
** Major portions taken verbatim or adapted from the Lua interpreter. |
|||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h |
|||
*/ |
|||
|
|||
#define lib_table_c |
|||
#define LUA_LIB |
|||
|
|||
#include "lua.h" |
|||
#include "lauxlib.h" |
|||
#include "lualib.h" |
|||
|
|||
#include "lj_obj.h" |
|||
#include "lj_gc.h" |
|||
#include "lj_err.h" |
|||
#include "lj_buf.h" |
|||
#include "lj_tab.h" |
|||
#include "lj_ff.h" |
|||
#include "lj_lib.h" |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#define LJLIB_MODULE_table |
|||
|
|||
LJLIB_LUA(table_foreachi) /*
|
|||
function(t, f) |
|||
CHECK_tab(t) |
|||
CHECK_func(f) |
|||
for i=1,#t do |
|||
local r = f(i, t[i]) |
|||
if r ~= nil then return r end |
|||
end |
|||
end |
|||
*/ |
|||
|
|||
LJLIB_LUA(table_foreach) /*
|
|||
function(t, f) |
|||
CHECK_tab(t) |
|||
CHECK_func(f) |
|||
for k, v in PAIRS(t) do |
|||
local r = f(k, v) |
|||
if r ~= nil then return r end |
|||
end |
|||
end |
|||
*/ |
|||
|
|||
LJLIB_LUA(table_getn) /*
|
|||
function(t) |
|||
CHECK_tab(t) |
|||
return #t |
|||
end |
|||
*/ |
|||
|
|||
LJLIB_CF(table_maxn) |
|||
{ |
|||
GCtab *t = lj_lib_checktab(L, 1); |
|||
TValue *array = tvref(t->array); |
|||
Node *node; |
|||
lua_Number m = 0; |
|||
ptrdiff_t i; |
|||
for (i = (ptrdiff_t)t->asize - 1; i >= 0; i--) |
|||
if (!tvisnil(&array[i])) { |
|||
m = (lua_Number)(int32_t)i; |
|||
break; |
|||
} |
|||
node = noderef(t->node); |
|||
for (i = (ptrdiff_t)t->hmask; i >= 0; i--) |
|||
if (!tvisnil(&node[i].val) && tvisnumber(&node[i].key)) { |
|||
lua_Number n = numberVnum(&node[i].key); |
|||
if (n > m) m = n; |
|||
} |
|||
setnumV(L->top-1, m); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_CF(table_insert) LJLIB_REC(.) |
|||
{ |
|||
GCtab *t = lj_lib_checktab(L, 1); |
|||
int32_t n, i = (int32_t)lj_tab_len(t) + 1; |
|||
int nargs = (int)((char *)L->top - (char *)L->base); |
|||
if (nargs != 2*sizeof(TValue)) { |
|||
if (nargs != 3*sizeof(TValue)) |
|||
lj_err_caller(L, LJ_ERR_TABINS); |
|||
/* NOBARRIER: This just moves existing elements around. */ |
|||
for (n = lj_lib_checkint(L, 2); i > n; i--) { |
|||
/* The set may invalidate the get pointer, so need to do it first! */ |
|||
TValue *dst = lj_tab_setint(L, t, i); |
|||
cTValue *src = lj_tab_getint(t, i-1); |
|||
if (src) { |
|||
copyTV(L, dst, src); |
|||
} else { |
|||
setnilV(dst); |
|||
} |
|||
} |
|||
i = n; |
|||
} |
|||
{ |
|||
TValue *dst = lj_tab_setint(L, t, i); |
|||
copyTV(L, dst, L->top-1); /* Set new value. */ |
|||
lj_gc_barriert(L, t, dst); |
|||
} |
|||
return 0; |
|||
} |
|||
|
|||
LJLIB_LUA(table_remove) /*
|
|||
function(t, pos) |
|||
CHECK_tab(t) |
|||
local len = #t |
|||
if pos == nil then |
|||
if len ~= 0 then |
|||
local old = t[len] |
|||
t[len] = nil |
|||
return old |
|||
end |
|||
else |
|||
CHECK_int(pos) |
|||
if pos >= 1 and pos <= len then |
|||
local old = t[pos] |
|||
for i=pos+1,len do |
|||
t[i-1] = t[i] |
|||
end |
|||
t[len] = nil |
|||
return old |
|||
end |
|||
end |
|||
end |
|||
*/ |
|||
|
|||
LJLIB_LUA(table_move) /*
|
|||
function(a1, f, e, t, a2) |
|||
CHECK_tab(a1) |
|||
CHECK_int(f) |
|||
CHECK_int(e) |
|||
CHECK_int(t) |
|||
if a2 == nil then a2 = a1 end |
|||
CHECK_tab(a2) |
|||
if e >= f then |
|||
local d = t - f |
|||
if t > e or t <= f or a2 ~= a1 then |
|||
for i=f,e do a2[i+d] = a1[i] end |
|||
else |
|||
for i=e,f,-1 do a2[i+d] = a1[i] end |
|||
end |
|||
end |
|||
return a2 |
|||
end |
|||
*/ |
|||
|
|||
LJLIB_CF(table_concat) LJLIB_REC(.) |
|||
{ |
|||
GCtab *t = lj_lib_checktab(L, 1); |
|||
GCstr *sep = lj_lib_optstr(L, 2); |
|||
int32_t i = lj_lib_optint(L, 3, 1); |
|||
int32_t e = (L->base+3 < L->top && !tvisnil(L->base+3)) ? |
|||
lj_lib_checkint(L, 4) : (int32_t)lj_tab_len(t); |
|||
SBuf *sb = lj_buf_tmp_(L); |
|||
SBuf *sbx = lj_buf_puttab(sb, t, sep, i, e); |
|||
if (LJ_UNLIKELY(!sbx)) { /* Error: bad element type. */ |
|||
int32_t idx = (int32_t)(intptr_t)sb->w; |
|||
cTValue *o = lj_tab_getint(t, idx); |
|||
lj_err_callerv(L, LJ_ERR_TABCAT, |
|||
lj_obj_itypename[o ? itypemap(o) : ~LJ_TNIL], idx); |
|||
} |
|||
setstrV(L, L->top-1, lj_buf_str(L, sbx)); |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
static void set2(lua_State *L, int i, int j) |
|||
{ |
|||
lua_rawseti(L, 1, i); |
|||
lua_rawseti(L, 1, j); |
|||
} |
|||
|
|||
static int sort_comp(lua_State *L, int a, int b) |
|||
{ |
|||
if (!lua_isnil(L, 2)) { /* function? */ |
|||
int res; |
|||
lua_pushvalue(L, 2); |
|||
lua_pushvalue(L, a-1); /* -1 to compensate function */ |
|||
lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */ |
|||
lua_call(L, 2, 1); |
|||
res = lua_toboolean(L, -1); |
|||
lua_pop(L, 1); |
|||
return res; |
|||
} else { /* a < b? */ |
|||
return lua_lessthan(L, a, b); |
|||
} |
|||
} |
|||
|
|||
static void auxsort(lua_State *L, int l, int u) |
|||
{ |
|||
while (l < u) { /* for tail recursion */ |
|||
int i, j; |
|||
/* sort elements a[l], a[(l+u)/2] and a[u] */ |
|||
lua_rawgeti(L, 1, l); |
|||
lua_rawgeti(L, 1, u); |
|||
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ |
|||
set2(L, l, u); /* swap a[l] - a[u] */ |
|||
else |
|||
lua_pop(L, 2); |
|||
if (u-l == 1) break; /* only 2 elements */ |
|||
i = (l+u)/2; |
|||
lua_rawgeti(L, 1, i); |
|||
lua_rawgeti(L, 1, l); |
|||
if (sort_comp(L, -2, -1)) { /* a[i]<a[l]? */ |
|||
set2(L, i, l); |
|||
} else { |
|||
lua_pop(L, 1); /* remove a[l] */ |
|||
lua_rawgeti(L, 1, u); |
|||
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */ |
|||
set2(L, i, u); |
|||
else |
|||
lua_pop(L, 2); |
|||
} |
|||
if (u-l == 2) break; /* only 3 elements */ |
|||
lua_rawgeti(L, 1, i); /* Pivot */ |
|||
lua_pushvalue(L, -1); |
|||
lua_rawgeti(L, 1, u-1); |
|||
set2(L, i, u-1); |
|||
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ |
|||
i = l; j = u-1; |
|||
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */ |
|||
/* repeat ++i until a[i] >= P */ |
|||
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { |
|||
if (i>=u) lj_err_caller(L, LJ_ERR_TABSORT); |
|||
lua_pop(L, 1); /* remove a[i] */ |
|||
} |
|||
/* repeat --j until a[j] <= P */ |
|||
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { |
|||
if (j<=l) lj_err_caller(L, LJ_ERR_TABSORT); |
|||
lua_pop(L, 1); /* remove a[j] */ |
|||
} |
|||
if (j<i) { |
|||
lua_pop(L, 3); /* pop pivot, a[i], a[j] */ |
|||
break; |
|||
} |
|||
set2(L, i, j); |
|||
} |
|||
lua_rawgeti(L, 1, u-1); |
|||
lua_rawgeti(L, 1, i); |
|||
set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */ |
|||
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */ |
|||
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */ |
|||
if (i-l < u-i) { |
|||
j=l; i=i-1; l=i+2; |
|||
} else { |
|||
j=i+1; i=u; u=j-2; |
|||
} |
|||
auxsort(L, j, i); /* call recursively the smaller one */ |
|||
} /* repeat the routine for the larger one */ |
|||
} |
|||
|
|||
LJLIB_CF(table_sort) |
|||
{ |
|||
GCtab *t = lj_lib_checktab(L, 1); |
|||
int32_t n = (int32_t)lj_tab_len(t); |
|||
lua_settop(L, 2); |
|||
if (!tvisnil(L->base+1)) |
|||
lj_lib_checkfunc(L, 2); |
|||
auxsort(L, 1, n); |
|||
return 0; |
|||
} |
|||
|
|||
#if LJ_52 |
|||
LJLIB_PUSH("n") |
|||
LJLIB_CF(table_pack) |
|||
{ |
|||
TValue *array, *base = L->base; |
|||
MSize i, n = (uint32_t)(L->top - base); |
|||
GCtab *t = lj_tab_new(L, n ? n+1 : 0, 1); |
|||
/* NOBARRIER: The table is new (marked white). */ |
|||
setintV(lj_tab_setstr(L, t, strV(lj_lib_upvalue(L, 1))), (int32_t)n); |
|||
for (array = tvref(t->array) + 1, i = 0; i < n; i++) |
|||
copyTV(L, &array[i], &base[i]); |
|||
settabV(L, base, t); |
|||
L->top = base+1; |
|||
lj_gc_check(L); |
|||
return 1; |
|||
} |
|||
#endif |
|||
|
|||
LJLIB_NOREG LJLIB_CF(table_new) LJLIB_REC(.) |
|||
{ |
|||
int32_t a = lj_lib_checkint(L, 1); |
|||
int32_t h = lj_lib_checkint(L, 2); |
|||
lua_createtable(L, a, h); |
|||
return 1; |
|||
} |
|||
|
|||
LJLIB_NOREG LJLIB_CF(table_clear) LJLIB_REC(.) |
|||
{ |
|||
lj_tab_clear(lj_lib_checktab(L, 1)); |
|||
return 0; |
|||
} |
|||
|
|||
static int luaopen_table_new(lua_State *L) |
|||
{ |
|||
return lj_lib_postreg(L, lj_cf_table_new, FF_table_new, "new"); |
|||
} |
|||
|
|||
static int luaopen_table_clear(lua_State *L) |
|||
{ |
|||
return lj_lib_postreg(L, lj_cf_table_clear, FF_table_clear, "clear"); |
|||
} |
|||
|
|||
/* ------------------------------------------------------------------------ */ |
|||
|
|||
#include "lj_libdef.h" |
|||
|
|||
LUALIB_API int luaopen_table(lua_State *L) |
|||
{ |
|||
LJ_LIB_REG(L, LUA_TABLIBNAME, table); |
|||
#if LJ_52 |
|||
lua_getglobal(L, "unpack"); |
|||
lua_setfield(L, -2, "unpack"); |
|||
#endif |
|||
lj_lib_prereg(L, LUA_TABLIBNAME ".new", luaopen_table_new, tabV(L->top-1)); |
|||
lj_lib_prereg(L, LUA_TABLIBNAME ".clear", luaopen_table_clear, tabV(L->top-1)); |
|||
return 1; |
|||
} |
|||
|
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue