mirror of
https://github.com/mirror/tinycc.git
synced 2026-08-24 11:33:28 +08:00
Compare commits
285 Commits
initial
...
release_0_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44cea6aee9 | ||
|
|
77a19bf50e | ||
|
|
b7f12dfbdf | ||
|
|
9890ea1233 | ||
|
|
feed3262c9 | ||
|
|
bef8703f77 | ||
|
|
46169b92d9 | ||
|
|
d369a77647 | ||
|
|
c5ce5eaeda | ||
|
|
9c1106d854 | ||
|
|
f8d0241764 | ||
|
|
c5959b77b4 | ||
|
|
7b940fcb1b | ||
|
|
21d2d99bdc | ||
|
|
277e01cd64 | ||
|
|
9d95be78c8 | ||
|
|
370f5e571c | ||
|
|
d0476a0e2e | ||
|
|
4e6edcdcdd | ||
|
|
98713a6be3 | ||
|
|
fe3b68b087 | ||
|
|
770ef5ed1f | ||
|
|
7b05ed6b81 | ||
|
|
8f5e44a439 | ||
|
|
813acdc691 | ||
|
|
638e666c42 | ||
|
|
e58bda52cd | ||
|
|
f0d5a74bcd | ||
|
|
6cdf6f88d7 | ||
|
|
5908725ad5 | ||
|
|
edbfa7715f | ||
|
|
c4e6b2e9be | ||
|
|
71f119fe25 | ||
|
|
68c800981e | ||
|
|
a901c5f204 | ||
|
|
2b64f2f570 | ||
|
|
7828683e60 | ||
|
|
60efcfe27c | ||
|
|
356fa0c3bb | ||
|
|
9791bfaf48 | ||
|
|
0eef235475 | ||
|
|
410fc57ba0 | ||
|
|
066d5e7a84 | ||
|
|
214ccccea7 | ||
|
|
0d6f8021ee | ||
|
|
c5ab452d64 | ||
|
|
4149fedd7b | ||
|
|
6e197e3d5d | ||
|
|
85fa15e64c | ||
|
|
3bbcd30776 | ||
|
|
fe7bcdef6b | ||
|
|
6104629dc2 | ||
|
|
40987541dc | ||
|
|
dd56ab8c77 | ||
|
|
5647993e79 | ||
|
|
d579dc4dbf | ||
|
|
cb96022ab7 | ||
|
|
f46a02a360 | ||
|
|
34b7d96fec | ||
|
|
cab6018913 | ||
|
|
0c618f4b7f | ||
|
|
c2e6daafc1 | ||
|
|
d6819c2c36 | ||
|
|
73acbe2413 | ||
|
|
60b9a1aa38 | ||
|
|
eed54d6bfb | ||
|
|
3605c50b36 | ||
|
|
d575137648 | ||
|
|
0da3613ac3 | ||
|
|
65092b7d85 | ||
|
|
50f0ae610f | ||
|
|
7893a9ecdf | ||
|
|
8e40dd90bb | ||
|
|
75e743d23e | ||
|
|
7808b4046e | ||
|
|
53196cd475 | ||
|
|
fa6ac83ec5 | ||
|
|
4df76253ad | ||
|
|
b5259da4f2 | ||
|
|
a6c5e2f9ca | ||
|
|
dc685c6b7d | ||
|
|
abf909e1ac | ||
|
|
2ec23ab858 | ||
|
|
5fac37b0a2 | ||
|
|
d1e7e264a5 | ||
|
|
122198a8c2 | ||
|
|
b9d9425993 | ||
|
|
7b2721ff07 | ||
|
|
0841db04c5 | ||
|
|
ab9973c843 | ||
|
|
0adc5a8921 | ||
|
|
a9f08655ac | ||
|
|
9e180474f0 | ||
|
|
8901fbeef1 | ||
|
|
b81d4ba6b3 | ||
|
|
cdcfed9737 | ||
|
|
2956bd85cd | ||
|
|
e926c359bb | ||
|
|
2f219d8702 | ||
|
|
63965fc585 | ||
|
|
5286d3d84c | ||
|
|
9620fd18e4 | ||
|
|
6cd2d5d2c8 | ||
|
|
923627f030 | ||
|
|
804d0c8d41 | ||
|
|
fde824b7cd | ||
|
|
bde35a81f3 | ||
|
|
b31d34fa0b | ||
|
|
b8772fc99a | ||
|
|
43f30315f9 | ||
|
|
c0180c4e5f | ||
|
|
973eb60e01 | ||
|
|
05a919e29f | ||
|
|
e4c7dd5e60 | ||
|
|
c62792e6aa | ||
|
|
d8ef07df04 | ||
|
|
10f5d44f43 | ||
|
|
3748975feb | ||
|
|
1cb355747a | ||
|
|
8ecc9cd789 | ||
|
|
ab4da04aa9 | ||
|
|
509fa60368 | ||
|
|
405c88106d | ||
|
|
f1418e836f | ||
|
|
bc427f5bec | ||
|
|
540d31895c | ||
|
|
5c81896e06 | ||
|
|
44b19defc2 | ||
|
|
431d648096 | ||
|
|
6dd6a026c3 | ||
|
|
d2115bfb27 | ||
|
|
46803f05e0 | ||
|
|
41391fbb20 | ||
|
|
5e85ad260d | ||
|
|
5370be48e2 | ||
|
|
f946dffd33 | ||
|
|
524be938fd | ||
|
|
2eddd13c10 | ||
|
|
cdf51a7a2a | ||
|
|
4264267299 | ||
|
|
59b27e85d9 | ||
|
|
796866862f | ||
|
|
f5b9220b60 | ||
|
|
53834650fb | ||
|
|
4891761bc9 | ||
|
|
acba7d10a2 | ||
|
|
3a58c53eaf | ||
|
|
4c9c0ab114 | ||
|
|
7ac43f6d19 | ||
|
|
18e40e1a25 | ||
|
|
519ec30bcf | ||
|
|
8264c257e7 | ||
|
|
a42941b1f0 | ||
|
|
9b48237795 | ||
|
|
540cd6fde7 | ||
|
|
20f4085145 | ||
|
|
840d197e87 | ||
|
|
e9315f499b | ||
|
|
b2834b35b5 | ||
|
|
3f13ae4961 | ||
|
|
58af3a6cd2 | ||
|
|
b12e91aa01 | ||
|
|
6cdecbe4e6 | ||
|
|
3d902af1a9 | ||
|
|
9051cc21af | ||
|
|
be44fce9cd | ||
|
|
7b54a53e08 | ||
|
|
f75e0c6d62 | ||
|
|
a50e58e5ba | ||
|
|
61a1599052 | ||
|
|
82695ae971 | ||
|
|
3cd4a7c4f9 | ||
|
|
9a8976d5eb | ||
|
|
6cd36b1285 | ||
|
|
7ab1ac48bb | ||
|
|
12f3f3592c | ||
|
|
17cbb388d3 | ||
|
|
264dbcfed2 | ||
|
|
30766d3cd1 | ||
|
|
3d3e2372c5 | ||
|
|
4e5a85292b | ||
|
|
dd35b8ccd1 | ||
|
|
14fb0c49d8 | ||
|
|
8f64c13c40 | ||
|
|
1e14c90c7a | ||
|
|
7feafd2bba | ||
|
|
db9fd02062 | ||
|
|
9a69c067c3 | ||
|
|
7f4fcf1216 | ||
|
|
22ce5f248a | ||
|
|
4226681d36 | ||
|
|
6933ac641f | ||
|
|
77db7175ca | ||
|
|
e5de65fcec | ||
|
|
048e710e15 | ||
|
|
5a50e73980 | ||
|
|
421911f921 | ||
|
|
308017d5e0 | ||
|
|
5bf9559e9e | ||
|
|
2d948c7610 | ||
|
|
dbb999f8bd | ||
|
|
6e2d1a809a | ||
|
|
d2c9157388 | ||
|
|
621b3fc3b5 | ||
|
|
0b8f352cb7 | ||
|
|
9c06595a1e | ||
|
|
bbd541d760 | ||
|
|
cc10b240cb | ||
|
|
2bd31e4ea6 | ||
|
|
1f15c98ab9 | ||
|
|
8d19107149 | ||
|
|
872eed648c | ||
|
|
ebe9e87ccf | ||
|
|
fd20e74b2f | ||
|
|
4a922fa9ed | ||
|
|
3d22a2aa6d | ||
|
|
04ae07f03e | ||
|
|
d85bf50bfe | ||
|
|
b7fed2f2d4 | ||
|
|
7e51b6607d | ||
|
|
5f85585b77 | ||
|
|
ad28d4c5b0 | ||
|
|
9eab1030f7 | ||
|
|
4124f1a1c1 | ||
|
|
60f781c443 | ||
|
|
14799da838 | ||
|
|
8b41bc57e1 | ||
|
|
1465899342 | ||
|
|
21c35b9443 | ||
|
|
498551188e | ||
|
|
f7181903bb | ||
|
|
f50d0b4488 | ||
|
|
b0b2d5d2e9 | ||
|
|
2694c10547 | ||
|
|
5ee1584493 | ||
|
|
fbc51a39f9 | ||
|
|
fa5161061e | ||
|
|
cb44835e94 | ||
|
|
25618c0430 | ||
|
|
3917389449 | ||
|
|
9cebd0617f | ||
|
|
3d1bdcf70e | ||
|
|
cb693e5318 | ||
|
|
9b3c96c88e | ||
|
|
52ca29cd4f | ||
|
|
3c9ab729ae | ||
|
|
5d7f7bb5ef | ||
|
|
711075fe3b | ||
|
|
d7e05e1b8f | ||
|
|
8523baf61d | ||
|
|
18a8013fe7 | ||
|
|
a88b855866 | ||
|
|
ebdec645a8 | ||
|
|
cb1c8c892c | ||
|
|
f22a32342b | ||
|
|
df9c047082 | ||
|
|
4298fccb0c | ||
|
|
0c6b2dcdfe | ||
|
|
34a14a08a3 | ||
|
|
b9455a7484 | ||
|
|
44b5c9742a | ||
|
|
ef6e8589d1 | ||
|
|
4dceee3677 | ||
|
|
eb0e3e70dc | ||
|
|
605a028bf4 | ||
|
|
d261452977 | ||
|
|
b536ac3068 | ||
|
|
507a221f7e | ||
|
|
cd526480b8 | ||
|
|
cdf6247518 | ||
|
|
a254461560 | ||
|
|
b0bdbb14ef | ||
|
|
aca442913c | ||
|
|
9736493149 | ||
|
|
946b600ddd | ||
|
|
b15f269fbe | ||
|
|
4e92bac070 | ||
|
|
704d61357c | ||
|
|
9c1b84560e | ||
|
|
58f3296a6e | ||
|
|
927c4afc0c | ||
|
|
9a8dfaac35 | ||
|
|
586b05f7e6 | ||
|
|
3115a8a38b | ||
|
|
881aa9077a |
51
.cvsignore
Normal file
51
.cvsignore
Normal file
@@ -0,0 +1,51 @@
|
||||
tcc_g
|
||||
tcc
|
||||
tc2.c
|
||||
ex3
|
||||
otcc1.c
|
||||
doc
|
||||
tc3s.c
|
||||
p3.c
|
||||
tc1.c
|
||||
error.c
|
||||
i386-gen1.c
|
||||
test.out2
|
||||
test.out3
|
||||
web.sh
|
||||
memdebug.c
|
||||
bench
|
||||
tcc-newparse.c
|
||||
p1.c
|
||||
Makefile.uClibc
|
||||
boundtest
|
||||
p4.c
|
||||
p5.c
|
||||
prog.ref
|
||||
test.ref
|
||||
a.c
|
||||
p3
|
||||
test.out
|
||||
elf.h
|
||||
stab.h
|
||||
stab.def
|
||||
p2
|
||||
tcc-doc.html
|
||||
ideas
|
||||
tcctest.ref
|
||||
test
|
||||
linux.tcc
|
||||
wintest.c
|
||||
ldtest
|
||||
libtcc_test
|
||||
instr.S
|
||||
p.c
|
||||
p2.c
|
||||
tcctest[1234]
|
||||
test[1234].out
|
||||
.gdb_history
|
||||
tcc.1
|
||||
tcc.pod
|
||||
config.h
|
||||
config.mak
|
||||
config.texi
|
||||
gcctestsuite.sh
|
||||
504
COPYING
Normal file
504
COPYING
Normal file
@@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
||||
242
Changelog
Normal file
242
Changelog
Normal file
@@ -0,0 +1,242 @@
|
||||
version 0.9.20:
|
||||
|
||||
- added '-w' option
|
||||
- added '.gnu.linkonce' ELF sections support
|
||||
- fixed libc linking when running in memory (avoid 'stat' function
|
||||
errors).
|
||||
- extended '-run' option to be able to give several arguments to a C
|
||||
script.
|
||||
|
||||
version 0.9.19:
|
||||
|
||||
- "alacarte" linking (Dave Long)
|
||||
- simpler function call
|
||||
- more strict type checks
|
||||
- added 'const' and 'volatile' support and associated warnings
|
||||
- added -Werror, -Wunsupported, -Wwrite-strings, -Wall.
|
||||
- added __builtin_types_compatible_p() and __builtin_constant_p()
|
||||
- chars support in assembler (Dave Long)
|
||||
- .string, .globl, .section, .text, .data and .bss asm directive
|
||||
support (Dave Long)
|
||||
- man page generated from tcc-doc.texi
|
||||
- fixed macro argument substitution
|
||||
- fixed zero argument macro parsing
|
||||
- changed license to LGPL
|
||||
- added -rdynamic option support
|
||||
|
||||
version 0.9.18:
|
||||
|
||||
- header fix (time.h)
|
||||
- fixed inline asm without operand case
|
||||
- fixed 'default:' or 'case x:' with '}' after (incorrect C construct accepted
|
||||
by gcc)
|
||||
- added 'A' inline asm constraint.
|
||||
|
||||
version 0.9.17:
|
||||
|
||||
- PLT generation fix
|
||||
- tcc doc fixes (Peter Lund)
|
||||
- struct parse fix (signaled by Pedro A. Aranda Gutierrez)
|
||||
- better _Bool lvalue support (signaled by Alex Measday)
|
||||
- function parameters must be converted to pointers (signaled by Neil Brown)
|
||||
- sanitized string and character constant parsing
|
||||
- fixed comment parse (signaled by Damian M Gryski)
|
||||
- fixed macro function bug (signaled by Philippe Ribet)
|
||||
- added configure (initial patch by Mitchell N Charity)
|
||||
- added '-run' and '-v' options (initial patch by vlindos)
|
||||
- added real date report in __DATE__ and __TIME__ macros
|
||||
|
||||
version 0.9.16:
|
||||
|
||||
- added assembler language support
|
||||
- added GCC inline asm() support
|
||||
- fixed multiple variable definitions : uninitialized variables are
|
||||
created as COMMON symbols.
|
||||
- optimized macro processing
|
||||
- added GCC statement expressions support
|
||||
- added GCC local labels support
|
||||
- fixed array declaration in old style function parameters
|
||||
- support casts in static structure initializations
|
||||
- added various __xxx[__] keywords for GCC compatibility
|
||||
- ignore __extension__ GCC in an expression or in a type (still not perfect)
|
||||
- added '? :' GCC extension support
|
||||
|
||||
version 0.9.15:
|
||||
|
||||
- compilation fixes for glibc 2.2, gcc 2.95.3 and gcc 3.2.
|
||||
- FreeBSD compile fixes. Makefile patches still missing (Carl Drougge).
|
||||
- fixed file type guessing if '.' is in the path.
|
||||
- fixed tcc_compile_string()
|
||||
- add a dummy page in ELF files to fix RX/RW accesses (pageexec at
|
||||
freemail dot hu).
|
||||
|
||||
version 0.9.14:
|
||||
|
||||
- added #warning. error message if invalid preprocessing directive.
|
||||
- added CType structure to ease typing (faster parse).
|
||||
- suppressed secondary hash tables (faster parse).
|
||||
- rewrote parser by optimizing common cases (faster parse).
|
||||
- fixed signed long long comparisons.
|
||||
- fixed 'int a(), b();' declaration case.
|
||||
- fixed structure init without '{}'.
|
||||
- correct alignment support in structures.
|
||||
- empty structures support.
|
||||
- gcc testsuite now supported.
|
||||
- output only warning if implicit integer/pointer conversions.
|
||||
- added static bitfield init.
|
||||
|
||||
version 0.9.13:
|
||||
|
||||
- correct preprocessing token pasting (## operator) in all cases (added
|
||||
preprocessing number token).
|
||||
- fixed long long register spill.
|
||||
- fixed signed long long '>>'.
|
||||
- removed memory leaks.
|
||||
- better error handling : processing can continue on link errors. A
|
||||
custom callback can be added to display error messages. Most
|
||||
errors do not call exit() now.
|
||||
- ignore -O, -W, -m and -f options
|
||||
- added old style function declarations
|
||||
- added GCC __alignof__ support.
|
||||
- added GCC typeof support.
|
||||
- added GCC computed gotos support.
|
||||
- added stack backtrace in runtime error message. Improved runtime
|
||||
error position display.
|
||||
|
||||
version 0.9.12:
|
||||
|
||||
- more fixes for || and && handling.
|
||||
- improved '? :' type handling.
|
||||
- fixed bound checking generation with structures
|
||||
- force '#endif' to be in same file as matching '#if'
|
||||
- #include file optimization with '#ifndef #endif' construct detection
|
||||
- macro handling optimization
|
||||
- added tcc_relocate() and tcc_get_symbol() in libtcc.
|
||||
|
||||
version 0.9.11:
|
||||
|
||||
- stdarg.h fix for double type (thanks to Philippe Ribet).
|
||||
- correct white space characters and added MSDOS newline support.
|
||||
- fixed invalid implicit function call type declaration.
|
||||
- special macros such as __LINE__ are defined if tested with defined().
|
||||
- fixed '!' operator with relocated address.
|
||||
- added symbol + offset relocation (fixes some static variable initializers)
|
||||
- '-l' option can be specified anywhere. '-c' option yields default
|
||||
output name. added '-r' option for relocatable output.
|
||||
- fixed '\nnn' octal parsing.
|
||||
- fixed local extern variables declarations.
|
||||
|
||||
version 0.9.10:
|
||||
|
||||
- fixed lvalue type when saved in local stack.
|
||||
- fixed '#include' syntax when using macros.
|
||||
- fixed '#line' bug.
|
||||
- removed size limit on strings. Unified string constants handling
|
||||
with variable declarations.
|
||||
- added correct support for '\xX' in wchar_t strings.
|
||||
- added support for bound checking in generated executables
|
||||
- fixed -I include order.
|
||||
- fixed incorrect function displayed in runtime error.
|
||||
|
||||
version 0.9.9:
|
||||
|
||||
- fixed preprocessor expression parsing for #if/#elif.
|
||||
- relocated debug info (.stab section).
|
||||
- relocated bounds info (.bounds section).
|
||||
- fixed cast to char of char constants ('\377' is -1 instead of 255)
|
||||
- fixed implicit cast for unary plus.
|
||||
- strings and '__func__' have now 'char[]' type instead of 'char *'
|
||||
(fixes sizeof() return value).
|
||||
- added __start_xxx and __stop_xxx symbols in linker.
|
||||
- better DLL creation support (option -shared begins to work).
|
||||
- ELF sections and hash tables are resized dynamically.
|
||||
- executables and DLLs are stripped by default.
|
||||
|
||||
version 0.9.8:
|
||||
|
||||
- First version of full ELF linking support (generate objects, static
|
||||
executable, dynamic executable, dynamic libraries). Dynamic library
|
||||
support is not finished (need PIC support in compiler and some
|
||||
patches in symbol exporting).
|
||||
- First version of ELF loader for object (.o) and archive (.a) files.
|
||||
- Support of simple GNU ld scripts (GROUP and FILE commands)
|
||||
- Separated runtime library and bound check code from TCC (smaller
|
||||
compiler core).
|
||||
- fixed register reload in float compare.
|
||||
- fixed implicit char/short to int casting.
|
||||
- allow array type for address of ('&') operator.
|
||||
- fixed unused || or && result.
|
||||
- added GCC style variadic macro support.
|
||||
- optimized bound checking code for array access.
|
||||
- tcc includes are now in $(prefix)/lib/tcc/include.
|
||||
- more command line options - more consistent handling of multiple
|
||||
input files.
|
||||
- added tcc man page (thanks to Cyril Bouthors).
|
||||
- uClibc Makefile update
|
||||
- converted documentation to texinfo format.
|
||||
- added developper's guide in documentation.
|
||||
|
||||
version 0.9.7:
|
||||
|
||||
- added library API for easy dynamic compilation (see libtcc.h - first
|
||||
draft).
|
||||
- fixed long long register spill bug.
|
||||
- fixed '? :' register spill bug.
|
||||
|
||||
version 0.9.6:
|
||||
|
||||
- added floating point constant propagation (fixes negative floating
|
||||
point constants bug).
|
||||
|
||||
version 0.9.5:
|
||||
|
||||
- uClibc patches (submitted by Alfonso Martone).
|
||||
- error reporting fix
|
||||
- added CONFIG_TCC_BCHECK to get smaller code if needed.
|
||||
|
||||
version 0.9.4:
|
||||
|
||||
- windows port (currently cannot use -g, -b and dll functions).
|
||||
- faster and simpler I/O handling.
|
||||
- '-D' option works in all cases.
|
||||
- preprocessor fixes (#elif and empty macro args)
|
||||
- floating point fixes
|
||||
- first code for CIL generation (does not work yet)
|
||||
|
||||
version 0.9.3:
|
||||
|
||||
- better and smaller code generator.
|
||||
- full ISOC99 64 bit 'long long' support.
|
||||
- full 32 bit 'float', 64 bit 'double' and 96 bit 'long double' support.
|
||||
- added '-U' option.
|
||||
- added assembly sections support.
|
||||
- even faster startup time by mmaping sections instead of mallocing them.
|
||||
- added GNUC __attribute__ keyword support (currently supports
|
||||
'section' and 'aligned' attributes).
|
||||
- added ELF file output (only usable for debugging now)
|
||||
- added debug symbol generation (STAB format).
|
||||
- added integrated runtime error analysis ('-g' option: print clear
|
||||
run time error messages instead of "Segmentation fault").
|
||||
- added first version of tiny memory and bound checker ('-b' option).
|
||||
|
||||
version 0.9.2:
|
||||
|
||||
- even faster parsing.
|
||||
- various syntax parsing fixes.
|
||||
- fixed external relocation handling for variables or functions pointers.
|
||||
- better function pointers type handling.
|
||||
- can compile multiple files (-i option).
|
||||
- ANSI C bit fields are supported.
|
||||
- beginning of float/double/long double support.
|
||||
- beginning of long long support.
|
||||
|
||||
version 0.9.1:
|
||||
|
||||
- full ISOC99 initializers handling.
|
||||
- compound literals.
|
||||
- structures handle in assignments and as function param or return value.
|
||||
- wide chars and strings.
|
||||
- macro bug fix
|
||||
|
||||
version 0.9:
|
||||
- initial version.
|
||||
252
Makefile
252
Makefile
@@ -1,52 +1,234 @@
|
||||
all: test cvt
|
||||
#
|
||||
# Tiny C Compiler Makefile
|
||||
#
|
||||
include config.mak
|
||||
|
||||
test: prog.bin
|
||||
cmp -l prog.bin prog.bin.ref
|
||||
CFLAGS=-O2 -g -Wall
|
||||
LIBS=-ldl
|
||||
CFLAGS_P=$(CFLAGS) -pg -static -DCONFIG_TCC_STATIC
|
||||
LIBS_P=
|
||||
|
||||
run: tc prog.c
|
||||
./tc prog.c
|
||||
CFLAGS+=-mpreferred-stack-boundary=2
|
||||
ifeq ($(GCC_MAJOR),2)
|
||||
CFLAGS+=-m386 -malign-functions=0
|
||||
else
|
||||
CFLAGS+=-march=i386 -falign-functions=0
|
||||
endif
|
||||
|
||||
run2: tc tc1.c prog.c
|
||||
./tc tc1.c prog.c
|
||||
DISAS=objdump -d
|
||||
INSTALL=install
|
||||
|
||||
run3: tc tc1.c prog.c
|
||||
./tc tc1.c tc1.c prog.c
|
||||
# run local version of tcc with local libraries and includes
|
||||
TCC=./tcc -B. -I.
|
||||
|
||||
prog.bin: prog.c tct
|
||||
./tct prog.c $@
|
||||
ndisasm -b 32 $@
|
||||
all: tcc libtcc1.a bcheck.o tcc-doc.html tcc.1 libtcc.a libtcc_test
|
||||
|
||||
p2.bin: p2.c tct
|
||||
./tct $< $@
|
||||
ndisasm -b 32 $@
|
||||
Makefile: config.mak
|
||||
|
||||
tct: tc.c
|
||||
gcc -DTEST -O2 -g -o $@ $< -ldl
|
||||
# auto test
|
||||
|
||||
tc: tc.c Makefile
|
||||
gcc -O2 -Wall -g -o $@ $< -ldl
|
||||
test: test.ref test.out
|
||||
@if diff -u test.ref test.out ; then echo "Auto Test OK"; fi
|
||||
|
||||
tc1: tc1.c
|
||||
gcc -O2 -Wall -g -o $@ $<
|
||||
tcctest.ref: tcctest.c
|
||||
$(CC) $(CFLAGS) -I. -o $@ $<
|
||||
|
||||
cvt: cvt.c
|
||||
gcc -O2 -Wall -g -o $@ $<
|
||||
test.ref: tcctest.ref
|
||||
./tcctest.ref > $@
|
||||
|
||||
test.out: tcc tcctest.c
|
||||
$(TCC) -run tcctest.c > $@
|
||||
|
||||
run: tcc tcctest.c
|
||||
$(TCC) -run tcctest.c
|
||||
|
||||
# iterated test2 (compile tcc then compile tcctest.c !)
|
||||
test2: tcc tcc.c tcctest.c test.ref
|
||||
$(TCC) -run tcc.c -B. -I. -run tcctest.c > test.out2
|
||||
@if diff -u test.ref test.out2 ; then echo "Auto Test2 OK"; fi
|
||||
|
||||
# iterated test3 (compile tcc then compile tcc then compile tcctest.c !)
|
||||
test3: tcc tcc.c tcctest.c test.ref
|
||||
$(TCC) -run tcc.c -B. -I. -run tcc.c -B. -I. -run tcctest.c > test.out3
|
||||
@if diff -u test.ref test.out3 ; then echo "Auto Test3 OK"; fi
|
||||
|
||||
# binary output test
|
||||
test4: tcc test.ref
|
||||
# dynamic output
|
||||
$(TCC) -o tcctest1 tcctest.c
|
||||
./tcctest1 > test1.out
|
||||
@if diff -u test.ref test1.out ; then echo "Dynamic Auto Test OK"; fi
|
||||
# static output
|
||||
$(TCC) -static -o tcctest2 tcctest.c
|
||||
./tcctest2 > test2.out
|
||||
@if diff -u test.ref test2.out ; then echo "Static Auto Test OK"; fi
|
||||
# object + link output
|
||||
$(TCC) -c -o tcctest3.o tcctest.c
|
||||
$(TCC) -o tcctest3 tcctest3.o
|
||||
./tcctest3 > test3.out
|
||||
@if diff -u test.ref test3.out ; then echo "Object Auto Test OK"; fi
|
||||
# dynamic output + bound check
|
||||
$(TCC) -b -o tcctest4 tcctest.c
|
||||
./tcctest4 > test4.out
|
||||
@if diff -u test.ref test4.out ; then echo "BCheck Auto Test OK"; fi
|
||||
|
||||
# memory and bound check auto test
|
||||
BOUNDS_OK = 1 4 8 10
|
||||
BOUNDS_FAIL= 2 5 7 9 11 12 13
|
||||
|
||||
btest: boundtest.c tcc
|
||||
@for i in $(BOUNDS_OK); do \
|
||||
if $(TCC) -b -run boundtest.c $$i ; then \
|
||||
/bin/true ; \
|
||||
else\
|
||||
echo Failed positive test $$i ; exit 1 ; \
|
||||
fi ;\
|
||||
done ;\
|
||||
for i in $(BOUNDS_FAIL); do \
|
||||
if $(TCC) -b -run boundtest.c $$i ; then \
|
||||
echo Failed negative test $$i ; exit 1 ;\
|
||||
else\
|
||||
/bin/true ; \
|
||||
fi\
|
||||
done ;\
|
||||
echo Bound test OK
|
||||
|
||||
# speed test
|
||||
speed: tcc ex2 ex3
|
||||
time ./ex2 1238 2 3 4 10 13 4
|
||||
time ./tcc -I. ./ex2.c 1238 2 3 4 10 13 4
|
||||
time ./ex3 35
|
||||
time ./tcc -I. ./ex3.c 35
|
||||
|
||||
ex2: ex2.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
ex3: ex3.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# Native Tiny C Compiler
|
||||
|
||||
tcc_g: tcc.c i386-gen.c tccelf.c tccasm.c i386-asm.c tcctok.h libtcc.h i386-asm.h Makefile
|
||||
$(CC) $(CFLAGS) -o $@ $< $(LIBS)
|
||||
|
||||
tcc: tcc_g Makefile
|
||||
strip -s -R .comment -R .note -o $@ $<
|
||||
|
||||
# TinyCC runtime libraries
|
||||
libtcc1.o: libtcc1.c
|
||||
$(CC) -O2 -Wall -c -o $@ $<
|
||||
|
||||
libtcc1.a: libtcc1.o
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
bcheck.o: bcheck.c
|
||||
$(CC) -O2 -Wall -c -o $@ $<
|
||||
|
||||
install: tcc_install libinstall
|
||||
|
||||
tcc_install: tcc tcc.1 libtcc1.a bcheck.o
|
||||
$(INSTALL) -m755 tcc $(bindir)
|
||||
$(INSTALL) tcc.1 $(mandir)/man1
|
||||
mkdir -p $(libdir)/tcc
|
||||
mkdir -p $(libdir)/tcc/include
|
||||
$(INSTALL) -m644 libtcc1.a bcheck.o $(libdir)/tcc
|
||||
$(INSTALL) -m644 stdarg.h stddef.h stdbool.h float.h varargs.h \
|
||||
tcclib.h $(libdir)/tcc/include
|
||||
|
||||
clean:
|
||||
rm -f *~ *.o tcc tcc1 tcct tcc_g tcctest.ref *.bin *.i ex2 \
|
||||
core gmon.out test.out test.ref a.out tcc_p \
|
||||
*.exe tcc-doc.html tcc.pod tcc.1 libtcc.a libtcc_test \
|
||||
tcctest[1234] test[1234].out
|
||||
|
||||
distclean: clean
|
||||
rm -f config.h config.mak config.texi
|
||||
|
||||
# win32 TCC
|
||||
tcc_g.exe: tcc.c i386-gen.c bcheck.c Makefile
|
||||
i386-mingw32msvc-gcc $(CFLAGS) -DCONFIG_TCC_STATIC -o $@ $<
|
||||
|
||||
tcc.exe: tcc_g.exe
|
||||
i386-mingw32msvc-strip -o $@ $<
|
||||
|
||||
# profiling version
|
||||
tcc_p: tcc.c Makefile
|
||||
$(CC) $(CFLAGS_P) -o $@ $< $(LIBS_P)
|
||||
|
||||
# libtcc generation and example
|
||||
libinstall: libtcc.a
|
||||
$(INSTALL) -m644 libtcc.a $(libdir)
|
||||
$(INSTALL) -m644 libtcc.h $(includedir)
|
||||
|
||||
libtcc.o: tcc.c i386-gen.c bcheck.c Makefile
|
||||
$(CC) $(CFLAGS) -DLIBTCC -c -o $@ $<
|
||||
|
||||
libtcc.a: libtcc.o
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
libtcc_test: libtcc_test.c libtcc.a
|
||||
$(CC) $(CFLAGS) -I. -o $@ $< -L. -ltcc -ldl
|
||||
|
||||
libtest: libtcc_test
|
||||
./libtcc_test
|
||||
|
||||
# targets for development
|
||||
|
||||
%.bin: %.c tcc
|
||||
$(TCC) -g -o $@ $<
|
||||
$(DISAS) $@
|
||||
|
||||
instr: instr.o
|
||||
objdump -d instr.o
|
||||
|
||||
# tiny assembler testing
|
||||
|
||||
asmtest.ref: asmtest.S
|
||||
$(CC) -c -o asmtest.ref.o asmtest.S
|
||||
objdump -D asmtest.ref.o > $@
|
||||
|
||||
# XXX: we compute tcc.c to go faster during development !
|
||||
asmtest.out: asmtest.S tcc
|
||||
# ./tcc tcc.c -c asmtest.S
|
||||
#asmtest.out: asmtest.S tcc
|
||||
./tcc -c asmtest.S
|
||||
objdump -D asmtest.o > $@
|
||||
|
||||
asmtest: asmtest.out asmtest.ref
|
||||
@if diff -u --ignore-matching-lines="file format" asmtest.ref asmtest.out ; then echo "ASM Auto Test OK"; fi
|
||||
|
||||
instr.o: instr.S
|
||||
gcc -O2 -Wall -g -c -o $@ $<
|
||||
$(CC) -O2 -Wall -g -c -o $@ $<
|
||||
|
||||
tc.i: tc.c Makefile
|
||||
gcc -E -P -DTINY -o $@ tc.c
|
||||
cache: tcc_g
|
||||
cachegrind ./tcc_g -o /tmp/linpack -lm bench/linpack.c
|
||||
vg_annotate tcc.c > /tmp/linpack.cache.log
|
||||
|
||||
tc1.c: tc.i cvt Makefile
|
||||
./cvt $< $@
|
||||
@ls -l $@
|
||||
# documentation and man page
|
||||
tcc-doc.html: tcc-doc.texi
|
||||
texi2html -monolithic -number $<
|
||||
|
||||
test2: tct tc1.c
|
||||
./tct tc1.c tc2
|
||||
ndisasm -b 32 tc2
|
||||
tcc.1: tcc-doc.texi
|
||||
./texi2pod.pl $< tcc.pod
|
||||
pod2man --section=1 --center=" " --release=" " tcc.pod > $@
|
||||
|
||||
tc2: tc
|
||||
./tct < tc1.c > tc2
|
||||
ndisasm -b 32 tc2
|
||||
FILES= Makefile Makefile.uClibc configure VERSION \
|
||||
README TODO COPYING \
|
||||
Changelog tcc-doc.texi tcc-doc.html \
|
||||
tcc.1 \
|
||||
tcc.c i386-gen.c tccelf.c tcctok.h tccasm.c i386-asm.c i386-asm.h\
|
||||
bcheck.c libtcc1.c \
|
||||
elf.h stab.h stab.def \
|
||||
stddef.h stdarg.h stdbool.h float.h varargs.h \
|
||||
tcclib.h libtcc.h libtcc_test.c \
|
||||
ex1.c ex2.c ex3.c ex4.c ex5.c \
|
||||
tcctest.c boundtest.c gcctestsuite.sh texi2pod.pl
|
||||
|
||||
FILE=tcc-$(VERSION)
|
||||
|
||||
tar:
|
||||
rm -rf /tmp/$(FILE)
|
||||
mkdir -p /tmp/$(FILE)
|
||||
cp -P $(FILES) /tmp/$(FILE)
|
||||
( cd /tmp ; tar zcvf ~/$(FILE).tar.gz $(FILE) )
|
||||
rm -rf /tmp/$(FILE)
|
||||
|
||||
90
README
Normal file
90
README
Normal file
@@ -0,0 +1,90 @@
|
||||
Tiny C Compiler - C Scripting Everywhere - The Smallest ANSI C compiler
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Features:
|
||||
--------
|
||||
|
||||
- SMALL! You can compile and execute C code everywhere, for example on
|
||||
rescue disks.
|
||||
|
||||
- FAST! tcc generates optimized x86 code. No byte code
|
||||
overhead. Compile, assemble and link about 7 times faster than 'gcc
|
||||
-O0'.
|
||||
|
||||
- UNLIMITED! Any C dynamic library can be used directly. TCC is
|
||||
heading torward full ISOC99 compliance. TCC can of course compile
|
||||
itself.
|
||||
|
||||
- SAFE! tcc includes an optional memory and bound checker. Bound
|
||||
checked code can be mixed freely with standard code.
|
||||
|
||||
- Compile and execute C source directly. No linking or assembly
|
||||
necessary. Full C preprocessor included.
|
||||
|
||||
- C script supported : just add '#!/usr/local/bin/tcc -run' at the first
|
||||
line of your C source, and execute it directly from the command
|
||||
line.
|
||||
|
||||
Documentation:
|
||||
-------------
|
||||
|
||||
1) Installation
|
||||
|
||||
*** TCC currently only works on Linux x86 with glibc >= 2.1 ***.
|
||||
|
||||
./configure
|
||||
make
|
||||
make test
|
||||
make install
|
||||
|
||||
By default, tcc is installed in /usr/local/bin.
|
||||
./configure --help shows configuration options.
|
||||
|
||||
|
||||
2) Introduction
|
||||
|
||||
We assume here that you know ANSI C. Look at the example ex1.c to know
|
||||
what the programs look like.
|
||||
|
||||
The include file <tcclib.h> can be used if you want a small basic libc
|
||||
include support (especially useful for floppy disks). Of course, you
|
||||
can also use standard headers, although they are slower to compile.
|
||||
|
||||
You can begin your C script with '#!/usr/local/bin/tcc -run' on the first
|
||||
line and set its execute bits (chmod a+x your_script). Then, you can
|
||||
launch the C code as a shell or perl script :-) The command line
|
||||
arguments are put in 'argc' and 'argv' of the main functions, as in
|
||||
ANSI C.
|
||||
|
||||
3) Examples
|
||||
|
||||
ex1.c: simplest example (hello world). Can also be launched directly
|
||||
as a script: './ex1.c'.
|
||||
|
||||
ex2.c: more complicated example: find a number with the four
|
||||
operations given a list of numbers (benchmark).
|
||||
|
||||
ex3.c: compute fibonacci numbers (benchmark).
|
||||
|
||||
ex4.c: more complicated: X11 program. Very complicated test in fact
|
||||
because standard headers are being used !
|
||||
|
||||
ex5.c: 'hello world' with standard glibc headers.
|
||||
|
||||
tcc.c: TCC can of course compile itself. Used to check the code
|
||||
generator.
|
||||
|
||||
tcctest.c: auto test for TCC which tests many subtle possible bugs. Used
|
||||
when doing 'make test'.
|
||||
|
||||
4) Full Documentation
|
||||
|
||||
Please read tcc-doc.html to have all the features of TCC.
|
||||
|
||||
License:
|
||||
-------
|
||||
|
||||
TCC is distributed under the GNU Lesser General Public License (see
|
||||
COPYING file).
|
||||
|
||||
Fabrice Bellard.
|
||||
79
TODO
79
TODO
@@ -1,6 +1,75 @@
|
||||
TODO list:
|
||||
|
||||
- cast bug (Peter Wang)
|
||||
- define incomplete type if defined several times (Peter Wang).
|
||||
- long long constant evaluation
|
||||
- configure --cc=tcc (still one bug in libtcc1.c)
|
||||
- disable-asm and disable-bcheck options
|
||||
- test binutils/gcc compile
|
||||
- add alloca(), __builtin_expect()
|
||||
- gcc '-E' option.
|
||||
- optimize VT_LOCAL + const
|
||||
- tci patch + argument.
|
||||
- '-b' bug.
|
||||
- atexit (Nigel Horne)
|
||||
- see -lxxx bug (Michael Charity).
|
||||
- handle inline functions as macros.
|
||||
- see transparent union pb in /urs/include/sys/socket.h
|
||||
- precise behaviour of typeof with arrays ? (__put_user macro)
|
||||
- #include_next support for /usr/include/limits ?
|
||||
but should suffice for most cases)
|
||||
- handle '? x, y : z' in unsized variable initialization (',' is
|
||||
considered incorrectly as separator in preparser)
|
||||
- function pointers/lvalues in ? : (linux kernel net/core/dev.c)
|
||||
- transform functions to function pointers in function parameters (net/ipv4/ip_output.c)
|
||||
- fix function pointer type display
|
||||
- fix bound exit on RedHat 7.3
|
||||
- check lcc test suite -> fix bitfield binary operations
|
||||
- check section alignment in C
|
||||
- fix invalid cast in comparison 'if (v == (int8_t)v)'
|
||||
- packed attribute
|
||||
- finish varargs.h support (gcc 3.2 testsuite issue)
|
||||
- fix static functions declared inside block
|
||||
- C99: add variable size arrays (gcc 3.2 testsuite issue)
|
||||
- C99: add complex types (gcc 3.2 testsuite issue)
|
||||
- postfix compound literals (see 20010124-1.c)
|
||||
- fix multiple unions init
|
||||
- setjmp is not supported properly in bound checking.
|
||||
- better local variables handling (needed for other targets)
|
||||
- fix bound check code with '&' on local variables (currently done
|
||||
only for local arrays).
|
||||
- sizeof, alignof, typeof can still generate code in some cases.
|
||||
- bound checking and float/long long/struct copy code. bound
|
||||
checking and symbol + offset optimization
|
||||
- Fix the remaining libtcc memory leaks.
|
||||
- make libtcc fully reentrant (except for the compilation stage itself).
|
||||
- '-MD' option
|
||||
|
||||
Optimizations:
|
||||
|
||||
- suppress specific anonymous symbol handling
|
||||
- more parse optimizations (=even faster compilation)
|
||||
- memory alloc optimizations (=even faster compilation)
|
||||
|
||||
Not critical:
|
||||
|
||||
- C99: fix multiple compound literals inits in blocks (ISOC99
|
||||
normative example - only relevant when using gotos! -> must add
|
||||
boolean variable to tell if compound literal was already
|
||||
initialized).
|
||||
- add PowerPC or ARM code generator and improve codegen for RISC (need
|
||||
to suppress VT_LOCAL and use a base register instead).
|
||||
- interactive mode / integrated debugger
|
||||
- fix preprocessor symbol redefinition
|
||||
- better constant opt (&&, ||, ?:)
|
||||
- add portable byte code generator and interpreter for other
|
||||
unsupported architectures.
|
||||
- C++: variable declaration in for, minimal 'class' support.
|
||||
- win32: add __stdcall, __intxx. use resolve for bchecked malloc et
|
||||
al. check GetModuleHandle for dlls. check exception code (exception
|
||||
filter func).
|
||||
- handle void (__attribute__() *ptr)()
|
||||
|
||||
|
||||
|
||||
|
||||
- 'x'=
|
||||
- shifts
|
||||
- conditionnal ?:
|
||||
- constant propagation for '+', '-' and [].
|
||||
- verify function types
|
||||
|
||||
542
asmtest.S
Normal file
542
asmtest.S
Normal file
@@ -0,0 +1,542 @@
|
||||
|
||||
/* some directive tests */
|
||||
|
||||
.byte 0xff
|
||||
.byte 1, 2, 3
|
||||
.short 1, 2, 3
|
||||
.word 1, 2, 3
|
||||
.long 1, 2, 3
|
||||
.int 1, 2, 3
|
||||
.align 8
|
||||
.byte 1
|
||||
.align 16, 0x90
|
||||
.skip 3
|
||||
.skip 15, 0x90
|
||||
.string "hello\0world"
|
||||
|
||||
/* some label tests */
|
||||
|
||||
movl %eax, %ebx
|
||||
L1:
|
||||
movl %eax, %ebx
|
||||
mov 0x10000, %eax
|
||||
L2:
|
||||
movl $L2 - L1, %ecx
|
||||
var1:
|
||||
nop ; nop ; nop ; nop
|
||||
|
||||
mov var1, %eax
|
||||
|
||||
/* instruction tests */
|
||||
movl %eax, %ebx
|
||||
mov 0x10000, %eax
|
||||
mov 0x10000, %ax
|
||||
mov 0x10000, %al
|
||||
mov %al, 0x10000
|
||||
|
||||
mov $1, %edx
|
||||
mov $1, %dx
|
||||
mov $1, %dl
|
||||
movb $2, 0x100(%ebx,%edx,2)
|
||||
movw $2, 0x100(%ebx,%edx,2)
|
||||
movl $2, 0x100(%ebx,%edx,2)
|
||||
movl %eax, 0x100(%ebx,%edx,2)
|
||||
movl 0x100(%ebx,%edx,2), %edx
|
||||
movw %ax, 0x100(%ebx,%edx,2)
|
||||
|
||||
mov %cr3, %edx
|
||||
mov %ecx, %cr3
|
||||
movl %cr3, %eax
|
||||
movl %tr3, %eax
|
||||
movl %db3, %ebx
|
||||
movl %dr6, %eax
|
||||
movl %fs, %ecx
|
||||
movl %ebx, %fs
|
||||
|
||||
movsbl 0x1000, %eax
|
||||
movsbw 0x1000, %ax
|
||||
movswl 0x1000, %eax
|
||||
|
||||
movzbl 0x1000, %eax
|
||||
movzbw 0x1000, %ax
|
||||
movzwl 0x1000, %eax
|
||||
|
||||
movzb 0x1000, %eax
|
||||
movzb 0x1000, %ax
|
||||
|
||||
|
||||
pushl %eax
|
||||
pushw %ax
|
||||
push %eax
|
||||
push %cs
|
||||
push %gs
|
||||
push $1
|
||||
push $100
|
||||
|
||||
popl %eax
|
||||
popw %ax
|
||||
pop %eax
|
||||
pop %ds
|
||||
pop %fs
|
||||
|
||||
xchg %eax, %ecx
|
||||
xchg %edx, %eax
|
||||
xchg %bx, 0x10000
|
||||
xchg 0x10000, %ebx
|
||||
xchg 0x10000, %dl
|
||||
|
||||
in $100, %al
|
||||
in $100, %ax
|
||||
in $100, %eax
|
||||
in %dx, %al
|
||||
in %dx, %ax
|
||||
in %dx, %eax
|
||||
inb %dx
|
||||
inw %dx
|
||||
inl %dx
|
||||
|
||||
out %al, $100
|
||||
out %ax, $100
|
||||
out %eax, $100
|
||||
|
||||
/* NOTE: gas is bugged here, so size must be added */
|
||||
outb %al, %dx
|
||||
outw %ax, %dx
|
||||
outl %eax, %dx
|
||||
|
||||
leal 0x1000(%ebx), %ecx
|
||||
lea 0x1000(%ebx), %ecx
|
||||
|
||||
les 0x2000, %eax
|
||||
lds 0x2000, %ebx
|
||||
lfs 0x2000, %ecx
|
||||
lgs 0x2000, %edx
|
||||
lss 0x2000, %edx
|
||||
|
||||
addl $0x123, %eax
|
||||
add $0x123, %ebx
|
||||
addl $0x123, 0x100
|
||||
addl $0x123, 0x100(%ebx)
|
||||
addl $0x123, 0x100(%ebx,%edx,2)
|
||||
addl $0x123, 0x100(%esp)
|
||||
addl $0x123, (%ebp)
|
||||
addl $0x123, (%esp)
|
||||
cmpl $0x123, (%esp)
|
||||
|
||||
add %eax, (%ebx)
|
||||
add (%ebx), %eax
|
||||
|
||||
or %dx, (%ebx)
|
||||
or (%ebx), %si
|
||||
|
||||
add %cl, (%ebx)
|
||||
add (%ebx), %dl
|
||||
|
||||
inc %edx
|
||||
incl 0x10000
|
||||
incb 0x10000
|
||||
dec %dx
|
||||
|
||||
test $1, %al
|
||||
test $1, %cl
|
||||
|
||||
testl $1, 0x1000
|
||||
testb $1, 0x1000
|
||||
testw $1, 0x1000
|
||||
test %eax, %ebx
|
||||
test %eax, 0x1000
|
||||
test 0x1000, %edx
|
||||
|
||||
not %edx
|
||||
notw 0x10000
|
||||
notl 0x10000
|
||||
notb 0x10000
|
||||
|
||||
neg %edx
|
||||
negw 0x10000
|
||||
negl 0x10000
|
||||
negb 0x10000
|
||||
|
||||
imul %ecx
|
||||
mul %edx
|
||||
mulb %cl
|
||||
|
||||
imul %eax, %ecx
|
||||
imul 0x1000, %cx
|
||||
imul $10, %eax, %ecx
|
||||
imul $10, %ax, %cx
|
||||
imul $10, %eax
|
||||
imul $0x1100000, %eax
|
||||
imul $1, %eax
|
||||
|
||||
idivw 0x1000
|
||||
div %ecx
|
||||
div %bl
|
||||
div %ecx, %eax
|
||||
|
||||
|
||||
shl %edx
|
||||
shl $10, %edx
|
||||
shl %cl, %edx
|
||||
|
||||
shld $1, %eax, %edx
|
||||
shld %cl, %eax, %edx
|
||||
shld %eax, %edx
|
||||
|
||||
shrd $1, %eax, %edx
|
||||
shrd %cl, %eax, %edx
|
||||
shrd %eax, %edx
|
||||
|
||||
L4:
|
||||
call 0x1000
|
||||
call L4
|
||||
call *%eax
|
||||
call *0x1000
|
||||
call func1
|
||||
|
||||
lcall $0x100, $0x1000
|
||||
|
||||
jmp 0x1000
|
||||
jmp *%eax
|
||||
jmp *0x1000
|
||||
|
||||
ljmp $0x100, $0x1000
|
||||
|
||||
ret
|
||||
|
||||
ret $10
|
||||
|
||||
lret
|
||||
|
||||
lret $10
|
||||
|
||||
enter $1234, $10
|
||||
|
||||
L3:
|
||||
jo 0x1000
|
||||
jnp 0x1001
|
||||
jne 0x1002
|
||||
jg 0x1003
|
||||
|
||||
jo L3
|
||||
jnp L3
|
||||
jne L3
|
||||
jg L3
|
||||
|
||||
loopne L3
|
||||
loopnz L3
|
||||
loope L3
|
||||
loopz L3
|
||||
loop L3
|
||||
jecxz L3
|
||||
|
||||
|
||||
seto %al
|
||||
setnp 0x1000
|
||||
setl 0xaaaa
|
||||
setg %dl
|
||||
|
||||
fadd
|
||||
fadd %st(1), %st
|
||||
fadd %st(3)
|
||||
|
||||
faddp %st(5)
|
||||
faddp
|
||||
faddp %st(1), %st
|
||||
|
||||
fadds 0x1000
|
||||
fiadds 0x1002
|
||||
faddl 0x1004
|
||||
fiaddl 0x1006
|
||||
|
||||
fmul
|
||||
fmul %st(1), %st
|
||||
fmul %st(3)
|
||||
|
||||
fmulp %st(5)
|
||||
fmulp
|
||||
fmulp %st(1), %st
|
||||
|
||||
fmuls 0x1000
|
||||
fimuls 0x1002
|
||||
fmull 0x1004
|
||||
fimull 0x1006
|
||||
|
||||
fsub
|
||||
fsub %st(1), %st
|
||||
fsub %st(3)
|
||||
|
||||
fsubp %st(5)
|
||||
fsubp
|
||||
fsubp %st(1), %st
|
||||
|
||||
fsubs 0x1000
|
||||
fisubs 0x1002
|
||||
fsubl 0x1004
|
||||
fisubl 0x1006
|
||||
|
||||
fsubr
|
||||
fsubr %st(1), %st
|
||||
fsubr %st(3)
|
||||
|
||||
fsubrp %st(5)
|
||||
fsubrp
|
||||
fsubrp %st(1), %st
|
||||
|
||||
fsubrs 0x1000
|
||||
fisubrs 0x1002
|
||||
fsubrl 0x1004
|
||||
fisubrl 0x1006
|
||||
|
||||
fdiv
|
||||
fdiv %st(1), %st
|
||||
fdiv %st(3)
|
||||
|
||||
fdivp %st(5)
|
||||
fdivp
|
||||
fdivp %st(1), %st
|
||||
|
||||
fdivs 0x1000
|
||||
fidivs 0x1002
|
||||
fdivl 0x1004
|
||||
fidivl 0x1006
|
||||
|
||||
fcom %st(3)
|
||||
|
||||
fcoms 0x1000
|
||||
ficoms 0x1002
|
||||
fcoml 0x1004
|
||||
ficoml 0x1006
|
||||
|
||||
fcomp %st(5)
|
||||
fcomp
|
||||
fcompp
|
||||
|
||||
fcomps 0x1000
|
||||
ficomps 0x1002
|
||||
fcompl 0x1004
|
||||
ficompl 0x1006
|
||||
|
||||
fld %st(5)
|
||||
fldl 0x1000
|
||||
flds 0x1002
|
||||
fildl 0x1004
|
||||
fst %st(4)
|
||||
fstp %st(6)
|
||||
fstpt 0x1006
|
||||
fbstp 0x1008
|
||||
|
||||
fxch
|
||||
fxch %st(4)
|
||||
|
||||
fucom %st(6)
|
||||
fucomp %st(3)
|
||||
fucompp
|
||||
|
||||
finit
|
||||
fninit
|
||||
fldcw 0x1000
|
||||
fnstcw 0x1002
|
||||
fstcw 0x1002
|
||||
fnstsw 0x1004
|
||||
fnstsw %eax
|
||||
fstsw 0x1004
|
||||
fstsw %eax
|
||||
fnclex
|
||||
fclex
|
||||
fnstenv 0x1000
|
||||
fstenv 0x1000
|
||||
fldenv 0x1000
|
||||
fnsave 0x1002
|
||||
fsave 0x1000
|
||||
frstor 0x1000
|
||||
ffree %st(7)
|
||||
ffreep %st(6)
|
||||
|
||||
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
|
||||
fchs
|
||||
fabs
|
||||
fnop
|
||||
fwait
|
||||
|
||||
bswap %edx
|
||||
xadd %ecx, %edx
|
||||
xaddb %dl, 0x1000
|
||||
xaddw %ax, 0x1000
|
||||
xaddl %eax, 0x1000
|
||||
cmpxchg %ecx, %edx
|
||||
cmpxchgb %dl, 0x1000
|
||||
cmpxchgw %ax, 0x1000
|
||||
cmpxchgl %eax, 0x1000
|
||||
invlpg 0x1000
|
||||
cmpxchg8b 0x1002
|
||||
|
||||
fcmovb %st(5), %st
|
||||
fcmove %st(5), %st
|
||||
fcmovbe %st(5), %st
|
||||
fcmovu %st(5), %st
|
||||
fcmovnb %st(5), %st
|
||||
fcmovne %st(5), %st
|
||||
fcmovnbe %st(5), %st
|
||||
fcmovnu %st(5), %st
|
||||
fcomi %st(5), %st
|
||||
fucomi %st(5), %st
|
||||
fcomip %st(5), %st
|
||||
fucomip %st(5), %st
|
||||
|
||||
|
||||
|
||||
cmovo 0x1000, %eax
|
||||
cmovs 0x1000, %eax
|
||||
cmovns %edx, %edi
|
||||
|
||||
int $3
|
||||
int $0x10
|
||||
|
||||
pusha
|
||||
popa
|
||||
clc
|
||||
cld
|
||||
cli
|
||||
clts
|
||||
cmc
|
||||
lahf
|
||||
sahf
|
||||
pushfl
|
||||
popfl
|
||||
pushf
|
||||
popf
|
||||
stc
|
||||
std
|
||||
sti
|
||||
aaa
|
||||
aas
|
||||
daa
|
||||
das
|
||||
aad
|
||||
aam
|
||||
cbw
|
||||
cwd
|
||||
cwde
|
||||
cdq
|
||||
cbtw
|
||||
cwtd
|
||||
cwtl
|
||||
cltd
|
||||
leave
|
||||
int3
|
||||
into
|
||||
iret
|
||||
rsm
|
||||
hlt
|
||||
wait
|
||||
nop
|
||||
|
||||
/* XXX: handle prefixes */
|
||||
#if 0
|
||||
aword
|
||||
addr16
|
||||
#endif
|
||||
lock
|
||||
rep
|
||||
repe
|
||||
repz
|
||||
repne
|
||||
repnz
|
||||
|
||||
invd
|
||||
wbinvd
|
||||
cpuid
|
||||
wrmsr
|
||||
rdtsc
|
||||
rdmsr
|
||||
rdpmc
|
||||
ud2
|
||||
|
||||
emms
|
||||
movd %edx, %mm3
|
||||
movd 0x1000, %mm2
|
||||
movd %mm4, %ecx
|
||||
movd %mm5, 0x1000
|
||||
|
||||
movq 0x1000, %mm2
|
||||
movq %mm4, 0x1000
|
||||
|
||||
pand 0x1000, %mm3
|
||||
pand %mm4, %mm5
|
||||
|
||||
psllw $1, %mm6
|
||||
psllw 0x1000, %mm7
|
||||
psllw %mm2, %mm7
|
||||
|
||||
xlat
|
||||
cmpsb
|
||||
scmpw
|
||||
insl
|
||||
outsw
|
||||
lodsb
|
||||
slodl
|
||||
movsb
|
||||
movsl
|
||||
smovb
|
||||
scasb
|
||||
sscaw
|
||||
stosw
|
||||
sstol
|
||||
|
||||
bsf 0x1000, %ebx
|
||||
bsr 0x1000, %ebx
|
||||
bt %edx, 0x1000
|
||||
btl $2, 0x1000
|
||||
btc %edx, 0x1000
|
||||
btcl $2, 0x1000
|
||||
btr %edx, 0x1000
|
||||
btrl $2, 0x1000
|
||||
bts %edx, 0x1000
|
||||
btsl $2, 0x1000
|
||||
|
||||
boundl %edx, 0x10000
|
||||
boundw %bx, 0x1000
|
||||
|
||||
arpl %bx, 0x1000
|
||||
lar 0x1000, %eax
|
||||
lgdt 0x1000
|
||||
lidt 0x1000
|
||||
lldt 0x1000
|
||||
lmsw 0x1000
|
||||
lsl 0x1000, %ecx
|
||||
ltr 0x1000
|
||||
|
||||
sgdt 0x1000
|
||||
sidt 0x1000
|
||||
sldt 0x1000
|
||||
smsw 0x1000
|
||||
str 0x1000
|
||||
|
||||
verr 0x1000
|
||||
verw 0x1000
|
||||
879
bcheck.c
Normal file
879
bcheck.c
Normal file
@@ -0,0 +1,879 @@
|
||||
/*
|
||||
* Tiny C Memory and bounds checker
|
||||
*
|
||||
* Copyright (c) 2002 Fabrice Bellard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#ifndef __FreeBSD__
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
//#define BOUND_DEBUG
|
||||
|
||||
/* define so that bound array is static (faster, but use memory if
|
||||
bound checking not used) */
|
||||
//#define BOUND_STATIC
|
||||
|
||||
/* use malloc hooks. Currently the code cannot be reliable if no hooks */
|
||||
#define CONFIG_TCC_MALLOC_HOOKS
|
||||
|
||||
#define HAVE_MEMALIGN
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#warning Bound checking not fully supported on FreeBSD
|
||||
#undef CONFIG_TCC_MALLOC_HOOKS
|
||||
#undef HAVE_MEMALIGN
|
||||
#endif
|
||||
|
||||
#define BOUND_T1_BITS 13
|
||||
#define BOUND_T2_BITS 11
|
||||
#define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS)
|
||||
|
||||
#define BOUND_T1_SIZE (1 << BOUND_T1_BITS)
|
||||
#define BOUND_T2_SIZE (1 << BOUND_T2_BITS)
|
||||
#define BOUND_T3_SIZE (1 << BOUND_T3_BITS)
|
||||
#define BOUND_E_BITS 4
|
||||
|
||||
#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS)
|
||||
#define BOUND_T23_SIZE (1 << BOUND_T23_BITS)
|
||||
|
||||
|
||||
/* this pointer is generated when bound check is incorrect */
|
||||
#define INVALID_POINTER ((void *)(-2))
|
||||
/* size of an empty region */
|
||||
#define EMPTY_SIZE 0xffffffff
|
||||
/* size of an invalid region */
|
||||
#define INVALID_SIZE 0
|
||||
|
||||
typedef struct BoundEntry {
|
||||
unsigned long start;
|
||||
unsigned long size;
|
||||
struct BoundEntry *next;
|
||||
unsigned long is_invalid; /* true if pointers outside region are invalid */
|
||||
} BoundEntry;
|
||||
|
||||
/* external interface */
|
||||
void __bound_init(void);
|
||||
void __bound_new_region(void *p, unsigned long size);
|
||||
int __bound_delete_region(void *p);
|
||||
|
||||
/* currently, tcc cannot compile that because we use unsupported GNU C
|
||||
extensions */
|
||||
#if !defined(__TINYC__)
|
||||
void *__bound_ptr_add(void *p, int offset) __attribute__((regparm(2)));
|
||||
void *__bound_ptr_indir1(void *p, int offset) __attribute__((regparm(2)));
|
||||
void *__bound_ptr_indir2(void *p, int offset) __attribute__((regparm(2)));
|
||||
void *__bound_ptr_indir4(void *p, int offset) __attribute__((regparm(2)));
|
||||
void *__bound_ptr_indir8(void *p, int offset) __attribute__((regparm(2)));
|
||||
void *__bound_ptr_indir12(void *p, int offset) __attribute__((regparm(2)));
|
||||
void *__bound_ptr_indir16(void *p, int offset) __attribute__((regparm(2)));
|
||||
void __bound_local_new(void *p) __attribute__((regparm(1)));
|
||||
void __bound_local_delete(void *p) __attribute__((regparm(1)));
|
||||
#endif
|
||||
|
||||
void *__bound_malloc(size_t size, const void *caller);
|
||||
void *__bound_memalign(size_t size, size_t align, const void *caller);
|
||||
void __bound_free(void *ptr, const void *caller);
|
||||
void *__bound_realloc(void *ptr, size_t size, const void *caller);
|
||||
static void *libc_malloc(size_t size);
|
||||
static void libc_free(void *ptr);
|
||||
static void install_malloc_hooks(void);
|
||||
static void restore_malloc_hooks(void);
|
||||
|
||||
#ifdef CONFIG_TCC_MALLOC_HOOKS
|
||||
static void *saved_malloc_hook;
|
||||
static void *saved_free_hook;
|
||||
static void *saved_realloc_hook;
|
||||
static void *saved_memalign_hook;
|
||||
#endif
|
||||
|
||||
/* linker definitions */
|
||||
extern char _end;
|
||||
|
||||
/* TCC definitions */
|
||||
extern char __bounds_start; /* start of static bounds table */
|
||||
/* error message, just for TCC */
|
||||
const char *__bound_error_msg;
|
||||
|
||||
/* runtime error output */
|
||||
extern void rt_error(unsigned long pc, const char *fmt, ...);
|
||||
|
||||
#ifdef BOUND_STATIC
|
||||
static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */
|
||||
#else
|
||||
static BoundEntry **__bound_t1; /* page table */
|
||||
#endif
|
||||
static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */
|
||||
static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */
|
||||
|
||||
static BoundEntry *__bound_find_region(BoundEntry *e1, void *p)
|
||||
{
|
||||
unsigned long addr, tmp;
|
||||
BoundEntry *e;
|
||||
|
||||
e = e1;
|
||||
while (e != NULL) {
|
||||
addr = (unsigned long)p;
|
||||
addr -= e->start;
|
||||
if (addr <= e->size) {
|
||||
/* put region at the head */
|
||||
tmp = e1->start;
|
||||
e1->start = e->start;
|
||||
e->start = tmp;
|
||||
tmp = e1->size;
|
||||
e1->size = e->size;
|
||||
e->size = tmp;
|
||||
return e1;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
/* no entry found: return empty entry or invalid entry */
|
||||
if (e1->is_invalid)
|
||||
return __bound_invalid_t2;
|
||||
else
|
||||
return __bound_empty_t2;
|
||||
}
|
||||
|
||||
/* print a bound error message */
|
||||
static void bound_error(const char *fmt, ...)
|
||||
{
|
||||
__bound_error_msg = fmt;
|
||||
*(int *)0 = 0; /* force a runtime error */
|
||||
}
|
||||
|
||||
static void bound_alloc_error(void)
|
||||
{
|
||||
bound_error("not enough memory for bound checking code");
|
||||
}
|
||||
|
||||
/* currently, tcc cannot compile that because we use GNUC extensions */
|
||||
#if !defined(__TINYC__)
|
||||
|
||||
/* return '(p + offset)' for pointer arithmetic (a pointer can reach
|
||||
the end of a region in this case */
|
||||
void *__bound_ptr_add(void *p, int offset)
|
||||
{
|
||||
unsigned long addr = (unsigned long)p;
|
||||
BoundEntry *e;
|
||||
#if defined(BOUND_DEBUG)
|
||||
printf("add: 0x%x %d\n", (int)p, offset);
|
||||
#endif
|
||||
|
||||
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
|
||||
e = (BoundEntry *)((char *)e +
|
||||
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
|
||||
addr -= e->start;
|
||||
if (addr > e->size) {
|
||||
e = __bound_find_region(e, p);
|
||||
addr = (unsigned long)p - e->start;
|
||||
}
|
||||
addr += offset;
|
||||
if (addr > e->size)
|
||||
return INVALID_POINTER; /* return an invalid pointer */
|
||||
return p + offset;
|
||||
}
|
||||
|
||||
/* return '(p + offset)' for pointer indirection (the resulting must
|
||||
be strictly inside the region */
|
||||
#define BOUND_PTR_INDIR(dsize) \
|
||||
void *__bound_ptr_indir ## dsize (void *p, int offset) \
|
||||
{ \
|
||||
unsigned long addr = (unsigned long)p; \
|
||||
BoundEntry *e; \
|
||||
\
|
||||
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \
|
||||
e = (BoundEntry *)((char *)e + \
|
||||
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \
|
||||
addr -= e->start; \
|
||||
if (addr > e->size) { \
|
||||
e = __bound_find_region(e, p); \
|
||||
addr = (unsigned long)p - e->start; \
|
||||
} \
|
||||
addr += offset + dsize; \
|
||||
if (addr > e->size) \
|
||||
return INVALID_POINTER; /* return an invalid pointer */ \
|
||||
return p + offset; \
|
||||
}
|
||||
|
||||
#ifdef __i386__
|
||||
/* return the frame pointer of the caller */
|
||||
#define GET_CALLER_FP(fp)\
|
||||
{\
|
||||
unsigned long *fp1;\
|
||||
__asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\
|
||||
fp = fp1[0];\
|
||||
}
|
||||
#else
|
||||
#error put code to extract the calling frame pointer
|
||||
#endif
|
||||
|
||||
/* called when entering a function to add all the local regions */
|
||||
void __bound_local_new(void *p1)
|
||||
{
|
||||
unsigned long addr, size, fp, *p = p1;
|
||||
GET_CALLER_FP(fp);
|
||||
for(;;) {
|
||||
addr = p[0];
|
||||
if (addr == 0)
|
||||
break;
|
||||
addr += fp;
|
||||
size = p[1];
|
||||
p += 2;
|
||||
__bound_new_region((void *)addr, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* called when leaving a function to delete all the local regions */
|
||||
void __bound_local_delete(void *p1)
|
||||
{
|
||||
unsigned long addr, fp, *p = p1;
|
||||
GET_CALLER_FP(fp);
|
||||
for(;;) {
|
||||
addr = p[0];
|
||||
if (addr == 0)
|
||||
break;
|
||||
addr += fp;
|
||||
p += 2;
|
||||
__bound_delete_region((void *)addr);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void __bound_local_new(void *p)
|
||||
{
|
||||
}
|
||||
void __bound_local_delete(void *p)
|
||||
{
|
||||
}
|
||||
|
||||
void *__bound_ptr_add(void *p, int offset)
|
||||
{
|
||||
return p + offset;
|
||||
}
|
||||
|
||||
#define BOUND_PTR_INDIR(dsize) \
|
||||
void *__bound_ptr_indir ## dsize (void *p, int offset) \
|
||||
{ \
|
||||
return p + offset; \
|
||||
}
|
||||
#endif
|
||||
|
||||
BOUND_PTR_INDIR(1)
|
||||
BOUND_PTR_INDIR(2)
|
||||
BOUND_PTR_INDIR(4)
|
||||
BOUND_PTR_INDIR(8)
|
||||
BOUND_PTR_INDIR(12)
|
||||
BOUND_PTR_INDIR(16)
|
||||
|
||||
static BoundEntry *__bound_new_page(void)
|
||||
{
|
||||
BoundEntry *page;
|
||||
int i;
|
||||
|
||||
page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE);
|
||||
if (!page)
|
||||
bound_alloc_error();
|
||||
for(i=0;i<BOUND_T2_SIZE;i++) {
|
||||
/* put empty entries */
|
||||
page[i].start = 0;
|
||||
page[i].size = EMPTY_SIZE;
|
||||
page[i].next = NULL;
|
||||
page[i].is_invalid = 0;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
/* currently we use malloc(). Should use bound_new_page() */
|
||||
static BoundEntry *bound_new_entry(void)
|
||||
{
|
||||
BoundEntry *e;
|
||||
e = libc_malloc(sizeof(BoundEntry));
|
||||
return e;
|
||||
}
|
||||
|
||||
static void bound_free_entry(BoundEntry *e)
|
||||
{
|
||||
libc_free(e);
|
||||
}
|
||||
|
||||
static inline BoundEntry *get_page(int index)
|
||||
{
|
||||
BoundEntry *page;
|
||||
page = __bound_t1[index];
|
||||
if (page == __bound_empty_t2 || page == __bound_invalid_t2) {
|
||||
/* create a new page if necessary */
|
||||
page = __bound_new_page();
|
||||
__bound_t1[index] = page;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
/* mark a region as being invalid (can only be used during init) */
|
||||
static void mark_invalid(unsigned long addr, unsigned long size)
|
||||
{
|
||||
unsigned long start, end;
|
||||
BoundEntry *page;
|
||||
int t1_start, t1_end, i, j, t2_start, t2_end;
|
||||
|
||||
start = addr;
|
||||
end = addr + size;
|
||||
|
||||
t2_start = (start + BOUND_T3_SIZE - 1) >> BOUND_T3_BITS;
|
||||
if (end != 0)
|
||||
t2_end = end >> BOUND_T3_BITS;
|
||||
else
|
||||
t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS);
|
||||
|
||||
#if 0
|
||||
printf("mark_invalid: start = %x %x\n", t2_start, t2_end);
|
||||
#endif
|
||||
|
||||
/* first we handle full pages */
|
||||
t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS;
|
||||
t1_end = t2_end >> BOUND_T2_BITS;
|
||||
|
||||
i = t2_start & (BOUND_T2_SIZE - 1);
|
||||
j = t2_end & (BOUND_T2_SIZE - 1);
|
||||
|
||||
if (t1_start == t1_end) {
|
||||
page = get_page(t2_start >> BOUND_T2_BITS);
|
||||
for(; i < j; i++) {
|
||||
page[i].size = INVALID_SIZE;
|
||||
page[i].is_invalid = 1;
|
||||
}
|
||||
} else {
|
||||
if (i > 0) {
|
||||
page = get_page(t2_start >> BOUND_T2_BITS);
|
||||
for(; i < BOUND_T2_SIZE; i++) {
|
||||
page[i].size = INVALID_SIZE;
|
||||
page[i].is_invalid = 1;
|
||||
}
|
||||
}
|
||||
for(i = t1_start; i < t1_end; i++) {
|
||||
__bound_t1[i] = __bound_invalid_t2;
|
||||
}
|
||||
if (j != 0) {
|
||||
page = get_page(t1_end);
|
||||
for(i = 0; i < j; i++) {
|
||||
page[i].size = INVALID_SIZE;
|
||||
page[i].is_invalid = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __bound_init(void)
|
||||
{
|
||||
int i;
|
||||
BoundEntry *page;
|
||||
unsigned long start, size;
|
||||
int *p;
|
||||
|
||||
/* save malloc hooks and install bound check hooks */
|
||||
install_malloc_hooks();
|
||||
|
||||
#ifndef BOUND_STATIC
|
||||
__bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *));
|
||||
if (!__bound_t1)
|
||||
bound_alloc_error();
|
||||
#endif
|
||||
__bound_empty_t2 = __bound_new_page();
|
||||
for(i=0;i<BOUND_T1_SIZE;i++) {
|
||||
__bound_t1[i] = __bound_empty_t2;
|
||||
}
|
||||
|
||||
page = __bound_new_page();
|
||||
for(i=0;i<BOUND_T2_SIZE;i++) {
|
||||
/* put invalid entries */
|
||||
page[i].start = 0;
|
||||
page[i].size = INVALID_SIZE;
|
||||
page[i].next = NULL;
|
||||
page[i].is_invalid = 1;
|
||||
}
|
||||
__bound_invalid_t2 = page;
|
||||
|
||||
/* invalid pointer zone */
|
||||
start = (unsigned long)INVALID_POINTER & ~(BOUND_T23_SIZE - 1);
|
||||
size = BOUND_T23_SIZE;
|
||||
mark_invalid(start, size);
|
||||
|
||||
#if !defined(__TINYC__) && defined(CONFIG_TCC_MALLOC_HOOKS)
|
||||
/* malloc zone is also marked invalid. can only use that with
|
||||
hooks because all libs should use the same malloc. The solution
|
||||
would be to build a new malloc for tcc. */
|
||||
start = (unsigned long)&_end;
|
||||
size = 128 * 0x100000;
|
||||
mark_invalid(start, size);
|
||||
#endif
|
||||
|
||||
/* add all static bound check values */
|
||||
p = (int *)&__bounds_start;
|
||||
while (p[0] != 0) {
|
||||
__bound_new_region((void *)p[0], p[1]);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void add_region(BoundEntry *e,
|
||||
unsigned long start, unsigned long size)
|
||||
{
|
||||
BoundEntry *e1;
|
||||
if (e->start == 0) {
|
||||
/* no region : add it */
|
||||
e->start = start;
|
||||
e->size = size;
|
||||
} else {
|
||||
/* already regions in the list: add it at the head */
|
||||
e1 = bound_new_entry();
|
||||
e1->start = e->start;
|
||||
e1->size = e->size;
|
||||
e1->next = e->next;
|
||||
e->start = start;
|
||||
e->size = size;
|
||||
e->next = e1;
|
||||
}
|
||||
}
|
||||
|
||||
/* create a new region. It should not already exist in the region list */
|
||||
void __bound_new_region(void *p, unsigned long size)
|
||||
{
|
||||
unsigned long start, end;
|
||||
BoundEntry *page, *e, *e2;
|
||||
int t1_start, t1_end, i, t2_start, t2_end;
|
||||
|
||||
start = (unsigned long)p;
|
||||
end = start + size;
|
||||
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
|
||||
t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
|
||||
|
||||
/* start */
|
||||
page = get_page(t1_start);
|
||||
t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) &
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
|
||||
t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) &
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
|
||||
#ifdef BOUND_DEBUG
|
||||
printf("new %lx %lx %x %x %x %x\n",
|
||||
start, end, t1_start, t1_end, t2_start, t2_end);
|
||||
#endif
|
||||
|
||||
e = (BoundEntry *)((char *)page + t2_start);
|
||||
add_region(e, start, size);
|
||||
|
||||
if (t1_end == t1_start) {
|
||||
/* same ending page */
|
||||
e2 = (BoundEntry *)((char *)page + t2_end);
|
||||
if (e2 > e) {
|
||||
e++;
|
||||
for(;e<e2;e++) {
|
||||
e->start = start;
|
||||
e->size = size;
|
||||
}
|
||||
add_region(e, start, size);
|
||||
}
|
||||
} else {
|
||||
/* mark until end of page */
|
||||
e2 = page + BOUND_T2_SIZE;
|
||||
e++;
|
||||
for(;e<e2;e++) {
|
||||
e->start = start;
|
||||
e->size = size;
|
||||
}
|
||||
/* mark intermediate pages, if any */
|
||||
for(i=t1_start+1;i<t1_end;i++) {
|
||||
page = get_page(i);
|
||||
e2 = page + BOUND_T2_SIZE;
|
||||
for(e=page;e<e2;e++) {
|
||||
e->start = start;
|
||||
e->size = size;
|
||||
}
|
||||
}
|
||||
/* last page */
|
||||
page = get_page(t1_end);
|
||||
e2 = (BoundEntry *)((char *)page + t2_end);
|
||||
for(e=page;e<e2;e++) {
|
||||
e->start = start;
|
||||
e->size = size;
|
||||
}
|
||||
add_region(e, start, size);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete a region */
|
||||
static inline void delete_region(BoundEntry *e,
|
||||
void *p, unsigned long empty_size)
|
||||
{
|
||||
unsigned long addr;
|
||||
BoundEntry *e1;
|
||||
|
||||
addr = (unsigned long)p;
|
||||
addr -= e->start;
|
||||
if (addr <= e->size) {
|
||||
/* region found is first one */
|
||||
e1 = e->next;
|
||||
if (e1 == NULL) {
|
||||
/* no more region: mark it empty */
|
||||
e->start = 0;
|
||||
e->size = empty_size;
|
||||
} else {
|
||||
/* copy next region in head */
|
||||
e->start = e1->start;
|
||||
e->size = e1->size;
|
||||
e->next = e1->next;
|
||||
bound_free_entry(e1);
|
||||
}
|
||||
} else {
|
||||
/* find the matching region */
|
||||
for(;;) {
|
||||
e1 = e;
|
||||
e = e->next;
|
||||
/* region not found: do nothing */
|
||||
if (e == NULL)
|
||||
break;
|
||||
addr = (unsigned long)p - e->start;
|
||||
if (addr <= e->size) {
|
||||
/* found: remove entry */
|
||||
e1->next = e->next;
|
||||
bound_free_entry(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* WARNING: 'p' must be the starting point of the region. */
|
||||
/* return non zero if error */
|
||||
int __bound_delete_region(void *p)
|
||||
{
|
||||
unsigned long start, end, addr, size, empty_size;
|
||||
BoundEntry *page, *e, *e2;
|
||||
int t1_start, t1_end, t2_start, t2_end, i;
|
||||
|
||||
start = (unsigned long)p;
|
||||
t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS);
|
||||
t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) &
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
|
||||
|
||||
/* find region size */
|
||||
page = __bound_t1[t1_start];
|
||||
e = (BoundEntry *)((char *)page + t2_start);
|
||||
addr = start - e->start;
|
||||
if (addr > e->size)
|
||||
e = __bound_find_region(e, p);
|
||||
/* test if invalid region */
|
||||
if (e->size == EMPTY_SIZE || (unsigned long)p != e->start)
|
||||
return -1;
|
||||
/* compute the size we put in invalid regions */
|
||||
if (e->is_invalid)
|
||||
empty_size = INVALID_SIZE;
|
||||
else
|
||||
empty_size = EMPTY_SIZE;
|
||||
size = e->size;
|
||||
end = start + size;
|
||||
|
||||
/* now we can free each entry */
|
||||
t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS);
|
||||
t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) &
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS);
|
||||
|
||||
delete_region(e, p, empty_size);
|
||||
if (t1_end == t1_start) {
|
||||
/* same ending page */
|
||||
e2 = (BoundEntry *)((char *)page + t2_end);
|
||||
if (e2 > e) {
|
||||
e++;
|
||||
for(;e<e2;e++) {
|
||||
e->start = 0;
|
||||
e->size = empty_size;
|
||||
}
|
||||
delete_region(e, p, empty_size);
|
||||
}
|
||||
} else {
|
||||
/* mark until end of page */
|
||||
e2 = page + BOUND_T2_SIZE;
|
||||
e++;
|
||||
for(;e<e2;e++) {
|
||||
e->start = 0;
|
||||
e->size = empty_size;
|
||||
}
|
||||
/* mark intermediate pages, if any */
|
||||
/* XXX: should free them */
|
||||
for(i=t1_start+1;i<t1_end;i++) {
|
||||
page = get_page(i);
|
||||
e2 = page + BOUND_T2_SIZE;
|
||||
for(e=page;e<e2;e++) {
|
||||
e->start = 0;
|
||||
e->size = empty_size;
|
||||
}
|
||||
}
|
||||
/* last page */
|
||||
page = get_page(t2_end);
|
||||
e2 = (BoundEntry *)((char *)page + t2_end);
|
||||
for(e=page;e<e2;e++) {
|
||||
e->start = 0;
|
||||
e->size = empty_size;
|
||||
}
|
||||
delete_region(e, p, empty_size);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* return the size of the region starting at p, or EMPTY_SIZE if non
|
||||
existant region. */
|
||||
static unsigned long get_region_size(void *p)
|
||||
{
|
||||
unsigned long addr = (unsigned long)p;
|
||||
BoundEntry *e;
|
||||
|
||||
e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)];
|
||||
e = (BoundEntry *)((char *)e +
|
||||
((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) &
|
||||
((BOUND_T2_SIZE - 1) << BOUND_E_BITS)));
|
||||
addr -= e->start;
|
||||
if (addr > e->size)
|
||||
e = __bound_find_region(e, p);
|
||||
if (e->start != (unsigned long)p)
|
||||
return EMPTY_SIZE;
|
||||
return e->size;
|
||||
}
|
||||
|
||||
/* patched memory functions */
|
||||
|
||||
static void install_malloc_hooks(void)
|
||||
{
|
||||
#ifdef CONFIG_TCC_MALLOC_HOOKS
|
||||
saved_malloc_hook = __malloc_hook;
|
||||
saved_free_hook = __free_hook;
|
||||
saved_realloc_hook = __realloc_hook;
|
||||
saved_memalign_hook = __memalign_hook;
|
||||
__malloc_hook = __bound_malloc;
|
||||
__free_hook = __bound_free;
|
||||
__realloc_hook = __bound_realloc;
|
||||
__memalign_hook = __bound_memalign;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void restore_malloc_hooks(void)
|
||||
{
|
||||
#ifdef CONFIG_TCC_MALLOC_HOOKS
|
||||
__malloc_hook = saved_malloc_hook;
|
||||
__free_hook = saved_free_hook;
|
||||
__realloc_hook = saved_realloc_hook;
|
||||
__memalign_hook = saved_memalign_hook;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void *libc_malloc(size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
restore_malloc_hooks();
|
||||
ptr = malloc(size);
|
||||
install_malloc_hooks();
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void libc_free(void *ptr)
|
||||
{
|
||||
restore_malloc_hooks();
|
||||
free(ptr);
|
||||
install_malloc_hooks();
|
||||
}
|
||||
|
||||
/* XXX: we should use a malloc which ensure that it is unlikely that
|
||||
two malloc'ed data have the same address if 'free' are made in
|
||||
between. */
|
||||
void *__bound_malloc(size_t size, const void *caller)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
/* we allocate one more byte to ensure the regions will be
|
||||
separated by at least one byte. With the glibc malloc, it may
|
||||
be in fact not necessary */
|
||||
ptr = libc_malloc(size + 1);
|
||||
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
__bound_new_region(ptr, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *__bound_memalign(size_t size, size_t align, const void *caller)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
restore_malloc_hooks();
|
||||
|
||||
#ifndef HAVE_MEMALIGN
|
||||
if (align > 4) {
|
||||
/* XXX: handle it ? */
|
||||
ptr = NULL;
|
||||
} else {
|
||||
/* we suppose that malloc aligns to at least four bytes */
|
||||
ptr = malloc(size + 1);
|
||||
}
|
||||
#else
|
||||
/* we allocate one more byte to ensure the regions will be
|
||||
separated by at least one byte. With the glibc malloc, it may
|
||||
be in fact not necessary */
|
||||
ptr = memalign(size + 1, align);
|
||||
#endif
|
||||
|
||||
install_malloc_hooks();
|
||||
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
__bound_new_region(ptr, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void __bound_free(void *ptr, const void *caller)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
if (__bound_delete_region(ptr) != 0)
|
||||
bound_error("freeing invalid region");
|
||||
|
||||
libc_free(ptr);
|
||||
}
|
||||
|
||||
void *__bound_realloc(void *ptr, size_t size, const void *caller)
|
||||
{
|
||||
void *ptr1;
|
||||
int old_size;
|
||||
|
||||
if (size == 0) {
|
||||
__bound_free(ptr, caller);
|
||||
return NULL;
|
||||
} else {
|
||||
ptr1 = __bound_malloc(size, caller);
|
||||
if (ptr == NULL || ptr1 == NULL)
|
||||
return ptr1;
|
||||
old_size = get_region_size(ptr);
|
||||
if (old_size == EMPTY_SIZE)
|
||||
bound_error("realloc'ing invalid pointer");
|
||||
memcpy(ptr1, ptr, old_size);
|
||||
__bound_free(ptr, caller);
|
||||
return ptr1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_TCC_MALLOC_HOOKS
|
||||
void *__bound_calloc(size_t nmemb, size_t size)
|
||||
{
|
||||
void *ptr;
|
||||
size = size * nmemb;
|
||||
ptr = __bound_malloc(size);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
static void bound_dump(void)
|
||||
{
|
||||
BoundEntry *page, *e;
|
||||
int i, j;
|
||||
|
||||
printf("region dump:\n");
|
||||
for(i=0;i<BOUND_T1_SIZE;i++) {
|
||||
page = __bound_t1[i];
|
||||
for(j=0;j<BOUND_T2_SIZE;j++) {
|
||||
e = page + j;
|
||||
/* do not print invalid or empty entries */
|
||||
if (e->size != EMPTY_SIZE && e->start != 0) {
|
||||
printf("%08x:",
|
||||
(i << (BOUND_T2_BITS + BOUND_T3_BITS)) +
|
||||
(j << BOUND_T3_BITS));
|
||||
do {
|
||||
printf(" %08lx:%08lx", e->start, e->start + e->size);
|
||||
e = e->next;
|
||||
} while (e != NULL);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* some useful checked functions */
|
||||
|
||||
/* check that (p ... p + size - 1) lies inside 'p' region, if any */
|
||||
static void __bound_check(const void *p, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
p = __bound_ptr_add((void *)p, size);
|
||||
if (p == INVALID_POINTER)
|
||||
bound_error("invalid pointer");
|
||||
}
|
||||
|
||||
void *__bound_memcpy(void *dst, const void *src, size_t size)
|
||||
{
|
||||
__bound_check(dst, size);
|
||||
__bound_check(src, size);
|
||||
/* check also region overlap */
|
||||
if (src >= dst && src < dst + size)
|
||||
bound_error("overlapping regions in memcpy()");
|
||||
return memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
void *__bound_memmove(void *dst, const void *src, size_t size)
|
||||
{
|
||||
__bound_check(dst, size);
|
||||
__bound_check(src, size);
|
||||
return memmove(dst, src, size);
|
||||
}
|
||||
|
||||
void *__bound_memset(void *dst, int c, size_t size)
|
||||
{
|
||||
__bound_check(dst, size);
|
||||
return memset(dst, c, size);
|
||||
}
|
||||
|
||||
/* XXX: could be optimized */
|
||||
int __bound_strlen(const char *s)
|
||||
{
|
||||
const char *p;
|
||||
int len;
|
||||
|
||||
len = 0;
|
||||
for(;;) {
|
||||
p = __bound_ptr_indir1((char *)s, len);
|
||||
if (p == INVALID_POINTER)
|
||||
bound_error("bad pointer in strlen()");
|
||||
if (*p == '\0')
|
||||
break;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
char *__bound_strcpy(char *dst, const char *src)
|
||||
{
|
||||
int len;
|
||||
len = __bound_strlen(src);
|
||||
return __bound_memcpy(dst, src, len + 1);
|
||||
}
|
||||
|
||||
214
boundtest.c
Normal file
214
boundtest.c
Normal file
@@ -0,0 +1,214 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define NB_ITS 1000000
|
||||
//#define NB_ITS 1
|
||||
#define TAB_SIZE 100
|
||||
|
||||
int tab[TAB_SIZE];
|
||||
int ret_sum;
|
||||
char tab3[256];
|
||||
|
||||
int test1(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
for(i=0;i<TAB_SIZE;i++) {
|
||||
sum += tab[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test2(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
for(i=0;i<TAB_SIZE + 1;i++) {
|
||||
sum += tab[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* actually, profiling test */
|
||||
int test3(void)
|
||||
{
|
||||
int sum;
|
||||
int i, it;
|
||||
|
||||
sum = 0;
|
||||
for(it=0;it<NB_ITS;it++) {
|
||||
for(i=0;i<TAB_SIZE;i++) {
|
||||
sum += tab[i];
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* ok */
|
||||
int test4(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
int *tab4;
|
||||
|
||||
tab4 = malloc(20 * sizeof(int));
|
||||
for(i=0;i<20;i++) {
|
||||
sum += tab4[i];
|
||||
}
|
||||
free(tab4);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test5(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
int *tab4;
|
||||
|
||||
tab4 = malloc(20 * sizeof(int));
|
||||
for(i=0;i<21;i++) {
|
||||
sum += tab4[i];
|
||||
}
|
||||
free(tab4);
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* error */
|
||||
/* XXX: currently: bug */
|
||||
int test6(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
int *tab4;
|
||||
|
||||
tab4 = malloc(20 * sizeof(int));
|
||||
free(tab4);
|
||||
for(i=0;i<21;i++) {
|
||||
sum += tab4[i];
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test7(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
int *p;
|
||||
|
||||
for(i=0;i<TAB_SIZE + 1;i++) {
|
||||
p = &tab[i];
|
||||
if (i == TAB_SIZE)
|
||||
printf("i=%d %x\n", i, p);
|
||||
sum += *p;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* ok */
|
||||
int test8(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
int tab[10];
|
||||
|
||||
for(i=0;i<10;i++) {
|
||||
sum += tab[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test9(void)
|
||||
{
|
||||
int i, sum = 0;
|
||||
char tab[10];
|
||||
|
||||
for(i=0;i<11;i++) {
|
||||
sum += tab[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/* ok */
|
||||
int test10(void)
|
||||
{
|
||||
char tab[10];
|
||||
char tab1[10];
|
||||
|
||||
memset(tab, 0, 10);
|
||||
memcpy(tab, tab1, 10);
|
||||
memmove(tab, tab1, 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test11(void)
|
||||
{
|
||||
char tab[10];
|
||||
|
||||
memset(tab, 0, 11);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test12(void)
|
||||
{
|
||||
void *ptr;
|
||||
ptr = malloc(10);
|
||||
free(ptr);
|
||||
free(ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* error */
|
||||
int test13(void)
|
||||
{
|
||||
char pad1 = 0;
|
||||
char tab[10];
|
||||
char pad2 = 0;
|
||||
memset(tab, 'a', sizeof(tab));
|
||||
return strlen(tab);
|
||||
}
|
||||
|
||||
int (*table_test[])(void) = {
|
||||
test1,
|
||||
test1,
|
||||
test2,
|
||||
test3,
|
||||
test4,
|
||||
test5,
|
||||
test6,
|
||||
test7,
|
||||
test8,
|
||||
test9,
|
||||
test10,
|
||||
test11,
|
||||
test12,
|
||||
test13,
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int index;
|
||||
int (*ftest)(void);
|
||||
|
||||
if (argc < 2) {
|
||||
printf("usage: boundtest n\n"
|
||||
"test TCC bound checking system\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
index = 0;
|
||||
if (argc >= 2)
|
||||
index = atoi(argv[1]);
|
||||
/* well, we also use bounds on this ! */
|
||||
ftest = table_test[index];
|
||||
ftest();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* without bound 0.77 s
|
||||
* with bounds 4.73
|
||||
*/
|
||||
293
configure
vendored
Executable file
293
configure
vendored
Executable file
@@ -0,0 +1,293 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# tcc configure script (c) 2003 Fabrice Bellard
|
||||
#
|
||||
# set temporary file name
|
||||
if test ! -z "$TMPDIR" ; then
|
||||
TMPDIR1="${TMPDIR}"
|
||||
elif test ! -z "$TEMPDIR" ; then
|
||||
TMPDIR1="${TEMPDIR}"
|
||||
else
|
||||
TMPDIR1="/tmp"
|
||||
fi
|
||||
|
||||
TMPC="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.c"
|
||||
TMPO="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.o"
|
||||
TMPE="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}"
|
||||
TMPS="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.S"
|
||||
TMPH="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.h"
|
||||
|
||||
# default parameters
|
||||
prefix="/usr/local"
|
||||
execprefix=""
|
||||
bindir=""
|
||||
libdir=""
|
||||
includedir=""
|
||||
mandir=""
|
||||
cross_prefix=""
|
||||
cc="gcc"
|
||||
host_cc="gcc"
|
||||
ar="ar"
|
||||
make="make"
|
||||
strip="strip"
|
||||
cpu=`uname -m`
|
||||
case "$cpu" in
|
||||
i386|i486|i586|i686|i86pc|BePC)
|
||||
cpu="x86"
|
||||
;;
|
||||
armv4l)
|
||||
cpu="armv4l"
|
||||
;;
|
||||
alpha)
|
||||
cpu="alpha"
|
||||
;;
|
||||
"Power Macintosh"|ppc|ppc64)
|
||||
cpu="powerpc"
|
||||
;;
|
||||
mips)
|
||||
cpu="mips"
|
||||
;;
|
||||
s390)
|
||||
cpu="s390"
|
||||
;;
|
||||
*)
|
||||
cpu="unknown"
|
||||
;;
|
||||
esac
|
||||
gprof="no"
|
||||
bigendian="no"
|
||||
|
||||
# OS specific
|
||||
targetos=`uname -s`
|
||||
case $targetos in
|
||||
*) ;;
|
||||
esac
|
||||
|
||||
# find source path
|
||||
# XXX: we assume an absolute path is given when launching configure,
|
||||
# except in './configure' case.
|
||||
source_path=${0%configure}
|
||||
source_path=${source_path%/}
|
||||
source_path_used="yes"
|
||||
if test -z "$source_path" -o "$source_path" = "." ; then
|
||||
source_path=`pwd`
|
||||
source_path_used="no"
|
||||
fi
|
||||
|
||||
for opt do
|
||||
case "$opt" in
|
||||
--prefix=*) prefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--exec-prefix=*) execprefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--bindir=*) bindir=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--libdir=*) libdir=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--includedir=*) includedir=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--mandir=*) mandir=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--source-path=*) source_path=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--cc=*) cc=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--make=*) make=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--extra-cflags=*) CFLAGS="${opt#--extra-cflags=}"
|
||||
;;
|
||||
--extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}"
|
||||
;;
|
||||
--extra-libs=*) extralibs=${opt#--extra-libs=}
|
||||
;;
|
||||
--cpu=*) cpu=`echo $opt | cut -d '=' -f 2`
|
||||
;;
|
||||
--enable-gprof) gprof="yes"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Checking for CFLAGS
|
||||
if test -z "$CFLAGS"; then
|
||||
CFLAGS="-O2"
|
||||
fi
|
||||
|
||||
cc="${cross_prefix}${cc}"
|
||||
ar="${cross_prefix}${ar}"
|
||||
strip="${cross_prefix}${strip}"
|
||||
|
||||
if test -z "$cross_prefix" ; then
|
||||
|
||||
# ---
|
||||
# big/little endian test
|
||||
cat > $TMPC << EOF
|
||||
#include <inttypes.h>
|
||||
int main(int argc, char ** argv){
|
||||
volatile uint32_t i=0x01234567;
|
||||
return (*((uint8_t*)(&i))) == 0x67;
|
||||
}
|
||||
EOF
|
||||
|
||||
if $cc -o $TMPE $TMPC 2>/dev/null ; then
|
||||
$TMPE && bigendian="yes"
|
||||
else
|
||||
echo big/little test failed
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
# if cross compiling, cannot launch a program, so make a static guess
|
||||
if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" ; then
|
||||
bigendian="yes"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# check gcc version
|
||||
cat > $TMPC <<EOF
|
||||
int main(void) {
|
||||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)
|
||||
return 0;
|
||||
#else
|
||||
#error gcc < 3.2
|
||||
#endif
|
||||
}
|
||||
EOF
|
||||
|
||||
gcc_major="2"
|
||||
if $cc -o $TMPO $TMPC 2> /dev/null ; then
|
||||
gcc_major="3"
|
||||
fi
|
||||
|
||||
if test x"$1" = x"-h" -o x"$1" = x"--help" ; then
|
||||
cat << EOF
|
||||
|
||||
Usage: configure [options]
|
||||
Options: [defaults in brackets after descriptions]
|
||||
|
||||
EOF
|
||||
echo "Standard options:"
|
||||
echo " --help print this message"
|
||||
echo " --prefix=PREFIX install in PREFIX [$prefix]"
|
||||
echo " --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX"
|
||||
echo " [same as prefix]"
|
||||
echo " --bindir=DIR user executables in DIR [EPREFIX/bin]"
|
||||
echo " --libdir=DIR object code libraries in DIR [EPREFIX/lib]"
|
||||
echo " --includedir=DIR C header files in DIR [PREFIX/include]"
|
||||
echo " --mandir=DIR man documentation in DIR [PREFIX/man]"
|
||||
echo ""
|
||||
echo "Advanced options (experts only):"
|
||||
echo " --source-path=PATH path of source code [$source_path]"
|
||||
echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]"
|
||||
echo " --cc=CC use C compiler CC [$cc]"
|
||||
echo " --make=MAKE use specified make [$make]"
|
||||
echo ""
|
||||
#echo "NOTE: The object files are build at the place where configure is launched"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test x"$execprefix" = x""; then
|
||||
execprefix="${prefix}"
|
||||
fi
|
||||
if test x"$bindir" = x""; then
|
||||
bindir="${execprefix}/bin"
|
||||
fi
|
||||
if test x"$libdir" = x""; then
|
||||
libdir="${execprefix}/lib"
|
||||
fi
|
||||
if test x"$includedir" = x""; then
|
||||
includedir="${prefix}/include"
|
||||
fi
|
||||
if test x"$mandir" = x""; then
|
||||
mandir="${prefix}/man"
|
||||
fi
|
||||
|
||||
echo "Binary directory $bindir"
|
||||
echo "Library directory $libdir"
|
||||
echo "Include directory $includedir"
|
||||
echo "Manual directory $mandir"
|
||||
echo "Source path $source_path"
|
||||
echo "C compiler $cc"
|
||||
echo "make $make"
|
||||
echo "CPU $cpu"
|
||||
echo "Big Endian $bigendian"
|
||||
echo "gprof enabled $gprof"
|
||||
|
||||
echo "Creating config.mak and config.h"
|
||||
|
||||
echo "# Automatically generated by configure - do not modify" > config.mak
|
||||
echo "/* Automatically generated by configure - do not modify */" > $TMPH
|
||||
|
||||
echo "prefix=$prefix" >> config.mak
|
||||
echo "bindir=$bindir" >> config.mak
|
||||
echo "libdir=$libdir" >> config.mak
|
||||
echo "includedir=$includedir" >> config.mak
|
||||
echo "mandir=$mandir" >> config.mak
|
||||
echo "#define CONFIG_TCC_LIBDIR \"$libdir\"" >> $TMPH
|
||||
echo "MAKE=$make" >> config.mak
|
||||
echo "CC=$cc" >> config.mak
|
||||
echo "GCC_MAJOR=$gcc_major" >> config.mak
|
||||
echo "#define GCC_MAJOR $gcc_major" >> $TMPH
|
||||
echo "HOST_CC=$host_cc" >> config.mak
|
||||
echo "AR=$ar" >> config.mak
|
||||
echo "STRIP=$strip -s -R .comment -R .note" >> config.mak
|
||||
echo "CFLAGS=$CFLAGS" >> config.mak
|
||||
echo "LDFLAGS=$LDFLAGS" >> config.mak
|
||||
if test "$cpu" = "x86" ; then
|
||||
echo "ARCH=i386" >> config.mak
|
||||
echo "#define HOST_I386 1" >> $TMPH
|
||||
elif test "$cpu" = "armv4l" ; then
|
||||
echo "ARCH=arm" >> config.mak
|
||||
echo "#define HOST_ARM 1" >> $TMPH
|
||||
elif test "$cpu" = "powerpc" ; then
|
||||
echo "ARCH=ppc" >> config.mak
|
||||
echo "#define HOST_PPC 1" >> $TMPH
|
||||
elif test "$cpu" = "mips" ; then
|
||||
echo "ARCH=mips" >> config.mak
|
||||
echo "#define HOST_MIPS 1" >> $TMPH
|
||||
elif test "$cpu" = "s390" ; then
|
||||
echo "ARCH=s390" >> config.mak
|
||||
echo "#define HOST_S390 1" >> $TMPH
|
||||
elif test "$cpu" = "alpha" ; then
|
||||
echo "ARCH=alpha" >> config.mak
|
||||
echo "#define HOST_ALPHA 1" >> $TMPH
|
||||
else
|
||||
echo "Unsupported CPU"
|
||||
exit 1
|
||||
fi
|
||||
if test "$bigendian" = "yes" ; then
|
||||
echo "WORDS_BIGENDIAN=yes" >> config.mak
|
||||
echo "#define WORDS_BIGENDIAN 1" >> $TMPH
|
||||
fi
|
||||
if test "$gprof" = "yes" ; then
|
||||
echo "TARGET_GPROF=yes" >> config.mak
|
||||
echo "#define HAVE_GPROF 1" >> $TMPH
|
||||
fi
|
||||
version=`head $source_path/VERSION`
|
||||
echo "VERSION=$version" >>config.mak
|
||||
echo "#define TCC_VERSION \"$version\"" >> $TMPH
|
||||
echo "@set VERSION $version" > config.texi
|
||||
|
||||
# build tree in object directory if source path is different from current one
|
||||
if test "$source_path_used" = "yes" ; then
|
||||
DIRS="tests"
|
||||
FILES="Makefile tests/Makefile"
|
||||
for dir in $DIRS ; do
|
||||
mkdir -p $dir
|
||||
done
|
||||
for f in $FILES ; do
|
||||
ln -sf $source_path/$f $f
|
||||
done
|
||||
fi
|
||||
echo "SRC_PATH=$source_path" >> config.mak
|
||||
|
||||
diff $TMPH config.h >/dev/null 2>&1
|
||||
if test $? -ne 0 ; then
|
||||
mv -f $TMPH config.h
|
||||
else
|
||||
echo "config.h is unchanged"
|
||||
fi
|
||||
|
||||
rm -f $TMPO $TMPC $TMPE $TMPS $TMPH
|
||||
8
ex1.c
Executable file
8
ex1.c
Executable file
@@ -0,0 +1,8 @@
|
||||
#! /usr/local/bin/tcc -run
|
||||
#include <tcclib.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello World\n");
|
||||
return 0;
|
||||
}
|
||||
97
ex2.c
Normal file
97
ex2.c
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "tcclib.h"
|
||||
|
||||
#define N 20
|
||||
|
||||
int nb_num;
|
||||
int tab[N];
|
||||
int stack_ptr;
|
||||
int stack_op[N];
|
||||
int stack_res[60];
|
||||
int result;
|
||||
|
||||
int find(int n, int i1, int a, int b, int op)
|
||||
{
|
||||
int i, j;
|
||||
int c;
|
||||
|
||||
if (stack_ptr >= 0) {
|
||||
stack_res[3*stack_ptr] = a;
|
||||
stack_op[stack_ptr] = op;
|
||||
stack_res[3*stack_ptr+1] = b;
|
||||
stack_res[3*stack_ptr+2] = n;
|
||||
if (n == result)
|
||||
return 1;
|
||||
tab[i1] = n;
|
||||
}
|
||||
|
||||
for(i=0;i<nb_num;i++) {
|
||||
for(j=i+1;j<nb_num;j++) {
|
||||
a = tab[i];
|
||||
b = tab[j];
|
||||
if (a != 0 && b != 0) {
|
||||
|
||||
tab[j] = 0;
|
||||
stack_ptr++;
|
||||
|
||||
if (find(a + b, i, a, b, '+'))
|
||||
return 1;
|
||||
if (find(a - b, i, a, b, '-'))
|
||||
return 1;
|
||||
if (find(b - a, i, b, a, '-'))
|
||||
return 1;
|
||||
if (find(a * b, i, a, b, '*'))
|
||||
return 1;
|
||||
if (b != 0) {
|
||||
c = a / b;
|
||||
if (find(c, i, a, b, '/'))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (a != 0) {
|
||||
c = b / a;
|
||||
if (find(c, i, b, a, '/'))
|
||||
return 1;
|
||||
}
|
||||
|
||||
stack_ptr--;
|
||||
tab[i] = a;
|
||||
tab[j] = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, res, p;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("usage: %s: result numbers...\n"
|
||||
"Try to find result from numbers with the 4 basic operations.\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
p = 1;
|
||||
result = atoi(argv[p]);
|
||||
printf("result=%d\n", result);
|
||||
nb_num = 0;
|
||||
for(i=p+1;i<argc;i++) {
|
||||
tab[nb_num++] = atoi(argv[i]);
|
||||
}
|
||||
|
||||
stack_ptr = -1;
|
||||
res = find(0, 0, 0, 0, ' ');
|
||||
if (res) {
|
||||
for(i=0;i<=stack_ptr;i++) {
|
||||
printf("%d %c %d = %d\n",
|
||||
stack_res[3*i], stack_op[i],
|
||||
stack_res[3*i+1], stack_res[3*i+2]);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
printf("Impossible\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
23
ex3.c
Normal file
23
ex3.c
Normal file
@@ -0,0 +1,23 @@
|
||||
#include <tcclib.h>
|
||||
|
||||
int fib(n)
|
||||
{
|
||||
if (n <= 2)
|
||||
return 1;
|
||||
else
|
||||
return fib(n-1) + fib(n-2);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
if (argc < 2) {
|
||||
printf("usage: fib n\n"
|
||||
"Compute nth Fibonacci number\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
n = atoi(argv[1]);
|
||||
printf("fib(%d) = %d\n", n, fib(n, 2));
|
||||
return 0;
|
||||
}
|
||||
25
ex4.c
Executable file
25
ex4.c
Executable file
@@ -0,0 +1,25 @@
|
||||
#!./tcc -run -L/usr/X11R6/lib -lX11
|
||||
#include <stdlib.h>
|
||||
/* Yes, TCC can use X11 too ! */
|
||||
#include <stdio.h>
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
Display *display;
|
||||
Screen *screen;
|
||||
|
||||
display = XOpenDisplay("");
|
||||
if (!display) {
|
||||
fprintf(stderr, "Could not open X11 display\n");
|
||||
exit(1);
|
||||
}
|
||||
printf("X11 display opened.\n");
|
||||
screen = XScreenOfDisplay(display, 0);
|
||||
printf("width = %d\nheight = %d\ndepth = %d\n",
|
||||
screen->width,
|
||||
screen->height,
|
||||
screen->root_depth);
|
||||
XCloseDisplay(display);
|
||||
return 0;
|
||||
}
|
||||
8
ex5.c
Normal file
8
ex5.c
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
printf("Hello World\n");
|
||||
return 0;
|
||||
}
|
||||
57
float.h
Normal file
57
float.h
Normal file
@@ -0,0 +1,57 @@
|
||||
#ifndef _FLOAT_H_
|
||||
#define _FLOAT_H_
|
||||
|
||||
#define FLT_RADIX 2
|
||||
|
||||
/* IEEE float */
|
||||
#define FLT_MANT_DIG 24
|
||||
#define FLT_DIG 6
|
||||
#define FLT_ROUNDS 1
|
||||
#define FLT_EPSILON 1.19209290e-07F
|
||||
#define FLT_MIN_EXP (-125)
|
||||
#define FLT_MIN 1.17549435e-38F
|
||||
#define FLT_MIN_10_EXP (-37)
|
||||
#define FLT_MAX_EXP 128
|
||||
#define FLT_MAX 3.40282347e+38F
|
||||
#define FLT_MAX_10_EXP 38
|
||||
|
||||
/* IEEE double */
|
||||
#define DBL_MANT_DIG 53
|
||||
#define DBL_DIG 15
|
||||
#define DBL_EPSILON 2.2204460492503131e-16
|
||||
#define DBL_MIN_EXP (-1021)
|
||||
#define DBL_MIN 2.2250738585072014e-308
|
||||
#define DBL_MIN_10_EXP (-307)
|
||||
#define DBL_MAX_EXP 1024
|
||||
#define DBL_MAX 1.7976931348623157e+308
|
||||
#define DBL_MAX_10_EXP 308
|
||||
|
||||
/* horrible intel long double */
|
||||
#ifdef __i386__
|
||||
|
||||
#define LDBL_MANT_DIG 64
|
||||
#define LDBL_DIG 18
|
||||
#define LDBL_EPSILON 1.08420217248550443401e-19L
|
||||
#define LDBL_MIN_EXP (-16381)
|
||||
#define LDBL_MIN 3.36210314311209350626e-4932L
|
||||
#define LDBL_MIN_10_EXP (-4931)
|
||||
#define LDBL_MAX_EXP 16384
|
||||
#define LDBL_MAX 1.18973149535723176502e+4932L
|
||||
#define LDBL_MAX_10_EXP 4932
|
||||
|
||||
#else
|
||||
|
||||
/* same as IEEE double */
|
||||
#define LDBL_MANT_DIG 53
|
||||
#define LDBL_DIG 15
|
||||
#define LDBL_EPSILON 2.2204460492503131e-16
|
||||
#define LDBL_MIN_EXP (-1021)
|
||||
#define LDBL_MIN 2.2250738585072014e-308
|
||||
#define LDBL_MIN_10_EXP (-307)
|
||||
#define LDBL_MAX_EXP 1024
|
||||
#define LDBL_MAX 1.7976931348623157e+308
|
||||
#define LDBL_MAX_10_EXP 308
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* _FLOAT_H_ */
|
||||
33
gcctestsuite.sh
Executable file
33
gcctestsuite.sh
Executable file
@@ -0,0 +1,33 @@
|
||||
#!/bin/sh
|
||||
|
||||
TESTSUITE_PATH=$HOME/gcc/gcc-3.2/gcc/testsuite/gcc.c-torture
|
||||
TCC="./tcc -B. -I. -DNO_TRAMPOLINES"
|
||||
rm -f tcc.sum tcc.log
|
||||
nb_failed="0"
|
||||
|
||||
for src in $TESTSUITE_PATH/compile/*.c ; do
|
||||
echo $TCC -o /tmp/test.o -c $src
|
||||
$TCC -o /tmp/test.o -c $src >> tcc.log 2>&1
|
||||
if [ "$?" == "0" ] ; then
|
||||
result="PASS"
|
||||
else
|
||||
result="FAIL"
|
||||
nb_failed=$[ $nb_failed + 1 ]
|
||||
fi
|
||||
echo "$result: $src" >> tcc.sum
|
||||
done
|
||||
|
||||
for src in $TESTSUITE_PATH/execute/*.c ; do
|
||||
echo $TCC $src
|
||||
$TCC $src >> tcc.log 2>&1
|
||||
if [ "$?" == "0" ] ; then
|
||||
result="PASS"
|
||||
else
|
||||
result="FAIL"
|
||||
nb_failed=$[ $nb_failed + 1 ]
|
||||
fi
|
||||
echo "$result: $src" >> tcc.sum
|
||||
done
|
||||
|
||||
echo "$nb_failed test(s) failed." >> tcc.sum
|
||||
echo "$nb_failed test(s) failed."
|
||||
1108
i386-asm.c
Normal file
1108
i386-asm.c
Normal file
File diff suppressed because it is too large
Load Diff
444
i386-asm.h
Normal file
444
i386-asm.h
Normal file
@@ -0,0 +1,444 @@
|
||||
DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */
|
||||
DEF_ASM_OP0(popa, 0x61)
|
||||
DEF_ASM_OP0(clc, 0xf8)
|
||||
DEF_ASM_OP0(cld, 0xfc)
|
||||
DEF_ASM_OP0(cli, 0xfa)
|
||||
DEF_ASM_OP0(clts, 0x0f06)
|
||||
DEF_ASM_OP0(cmc, 0xf5)
|
||||
DEF_ASM_OP0(lahf, 0x9f)
|
||||
DEF_ASM_OP0(sahf, 0x9e)
|
||||
DEF_ASM_OP0(pushfl, 0x9c)
|
||||
DEF_ASM_OP0(popfl, 0x9d)
|
||||
DEF_ASM_OP0(pushf, 0x9c)
|
||||
DEF_ASM_OP0(popf, 0x9d)
|
||||
DEF_ASM_OP0(stc, 0xf9)
|
||||
DEF_ASM_OP0(std, 0xfd)
|
||||
DEF_ASM_OP0(sti, 0xfb)
|
||||
DEF_ASM_OP0(aaa, 0x37)
|
||||
DEF_ASM_OP0(aas, 0x3f)
|
||||
DEF_ASM_OP0(daa, 0x27)
|
||||
DEF_ASM_OP0(das, 0x2f)
|
||||
DEF_ASM_OP0(aad, 0xd50a)
|
||||
DEF_ASM_OP0(aam, 0xd40a)
|
||||
DEF_ASM_OP0(cbw, 0x6698)
|
||||
DEF_ASM_OP0(cwd, 0x6699)
|
||||
DEF_ASM_OP0(cwde, 0x98)
|
||||
DEF_ASM_OP0(cdq, 0x99)
|
||||
DEF_ASM_OP0(cbtw, 0x6698)
|
||||
DEF_ASM_OP0(cwtl, 0x98)
|
||||
DEF_ASM_OP0(cwtd, 0x6699)
|
||||
DEF_ASM_OP0(cltd, 0x99)
|
||||
DEF_ASM_OP0(int3, 0xcc)
|
||||
DEF_ASM_OP0(into, 0xce)
|
||||
DEF_ASM_OP0(iret, 0xcf)
|
||||
DEF_ASM_OP0(rsm, 0x0faa)
|
||||
DEF_ASM_OP0(hlt, 0xf4)
|
||||
DEF_ASM_OP0(wait, 0x9b)
|
||||
DEF_ASM_OP0(nop, 0x90)
|
||||
DEF_ASM_OP0(xlat, 0xd7)
|
||||
|
||||
/* strings */
|
||||
ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
|
||||
ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
|
||||
|
||||
ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
|
||||
ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
|
||||
|
||||
ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
|
||||
ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
|
||||
|
||||
ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
|
||||
ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
|
||||
|
||||
ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
|
||||
ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
|
||||
|
||||
ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
|
||||
ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
|
||||
|
||||
/* bits */
|
||||
|
||||
ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||||
ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
|
||||
|
||||
ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||||
ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||||
ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||||
ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
|
||||
ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
|
||||
|
||||
/* prefixes */
|
||||
DEF_ASM_OP0(aword, 0x67)
|
||||
DEF_ASM_OP0(addr16, 0x67)
|
||||
DEF_ASM_OP0(word, 0x66)
|
||||
DEF_ASM_OP0(data16, 0x66)
|
||||
DEF_ASM_OP0(lock, 0xf0)
|
||||
DEF_ASM_OP0(rep, 0xf3)
|
||||
DEF_ASM_OP0(repe, 0xf3)
|
||||
DEF_ASM_OP0(repz, 0xf3)
|
||||
DEF_ASM_OP0(repne, 0xf2)
|
||||
DEF_ASM_OP0(repnz, 0xf2)
|
||||
|
||||
DEF_ASM_OP0(invd, 0x0f08)
|
||||
DEF_ASM_OP0(wbinvd, 0x0f09)
|
||||
DEF_ASM_OP0(cpuid, 0x0fa2)
|
||||
DEF_ASM_OP0(wrmsr, 0x0f30)
|
||||
DEF_ASM_OP0(rdtsc, 0x0f31)
|
||||
DEF_ASM_OP0(rdmsr, 0x0f32)
|
||||
DEF_ASM_OP0(rdpmc, 0x0f33)
|
||||
DEF_ASM_OP0(ud2, 0x0f0b)
|
||||
|
||||
/* NOTE: we took the same order as gas opcode definition order */
|
||||
ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
|
||||
ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
|
||||
ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||||
ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
|
||||
ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
|
||||
|
||||
ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
|
||||
ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
|
||||
ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
|
||||
|
||||
ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
|
||||
ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
|
||||
ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
|
||||
|
||||
ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||||
ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||||
ALT(DEF_ASM_OP1(push, 0x6a, 0, 0, OPT_IM8S))
|
||||
ALT(DEF_ASM_OP1(push, 0x68, 0, 0, OPT_IM32))
|
||||
ALT(DEF_ASM_OP1(push, 0x06, 0, 0, OPT_SEG))
|
||||
|
||||
ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||||
ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
|
||||
ALT(DEF_ASM_OP1(pop, 0x07, 0, 0, OPT_SEG))
|
||||
|
||||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
|
||||
ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
|
||||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||||
|
||||
ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
|
||||
ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
|
||||
ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
|
||||
ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
|
||||
|
||||
ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
|
||||
ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
|
||||
ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
|
||||
ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
|
||||
|
||||
ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
|
||||
|
||||
ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||||
ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
|
||||
|
||||
/* arith */
|
||||
ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
|
||||
ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||||
ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
|
||||
ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
|
||||
|
||||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
|
||||
ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
|
||||
ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
|
||||
|
||||
ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||||
ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
|
||||
ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
|
||||
ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
|
||||
ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
|
||||
ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
|
||||
ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
|
||||
|
||||
ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||||
ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
|
||||
ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
|
||||
|
||||
/* shifts */
|
||||
ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
|
||||
ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
|
||||
|
||||
ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||||
ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||||
ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||||
ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
|
||||
ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
|
||||
ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
|
||||
|
||||
ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
|
||||
ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
|
||||
ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
|
||||
ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
|
||||
|
||||
ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
|
||||
ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
|
||||
ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
|
||||
ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
|
||||
|
||||
ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
|
||||
ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
|
||||
DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
|
||||
DEF_ASM_OP0(leave, 0xc9)
|
||||
DEF_ASM_OP0(ret, 0xc3)
|
||||
ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
|
||||
DEF_ASM_OP0(lret, 0xcb)
|
||||
ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
|
||||
|
||||
ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
|
||||
DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||
DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||
DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||
DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||
DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||
DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
|
||||
|
||||
/* float */
|
||||
/* specific fcomp handling */
|
||||
ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
|
||||
|
||||
ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||||
ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||||
ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
|
||||
ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
|
||||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
|
||||
ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
|
||||
ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
|
||||
ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||||
ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||||
ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||||
ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
|
||||
|
||||
DEF_ASM_OP0(fucompp, 0xdae9)
|
||||
DEF_ASM_OP0(ftst, 0xd9e4)
|
||||
DEF_ASM_OP0(fxam, 0xd9e5)
|
||||
DEF_ASM_OP0(fld1, 0xd9e8)
|
||||
DEF_ASM_OP0(fldl2t, 0xd9e9)
|
||||
DEF_ASM_OP0(fldl2e, 0xd9ea)
|
||||
DEF_ASM_OP0(fldpi, 0xd9eb)
|
||||
DEF_ASM_OP0(fldlg2, 0xd9ec)
|
||||
DEF_ASM_OP0(fldln2, 0xd9ed)
|
||||
DEF_ASM_OP0(fldz, 0xd9ee)
|
||||
|
||||
DEF_ASM_OP0(f2xm1, 0xd9f0)
|
||||
DEF_ASM_OP0(fyl2x, 0xd9f1)
|
||||
DEF_ASM_OP0(fptan, 0xd9f2)
|
||||
DEF_ASM_OP0(fpatan, 0xd9f3)
|
||||
DEF_ASM_OP0(fxtract, 0xd9f4)
|
||||
DEF_ASM_OP0(fprem1, 0xd9f5)
|
||||
DEF_ASM_OP0(fdecstp, 0xd9f6)
|
||||
DEF_ASM_OP0(fincstp, 0xd9f7)
|
||||
DEF_ASM_OP0(fprem, 0xd9f8)
|
||||
DEF_ASM_OP0(fyl2xp1, 0xd9f9)
|
||||
DEF_ASM_OP0(fsqrt, 0xd9fa)
|
||||
DEF_ASM_OP0(fsincos, 0xd9fb)
|
||||
DEF_ASM_OP0(frndint, 0xd9fc)
|
||||
DEF_ASM_OP0(fscale, 0xd9fd)
|
||||
DEF_ASM_OP0(fsin, 0xd9fe)
|
||||
DEF_ASM_OP0(fcos, 0xd9ff)
|
||||
DEF_ASM_OP0(fchs, 0xd9e0)
|
||||
DEF_ASM_OP0(fabs, 0xd9e1)
|
||||
DEF_ASM_OP0(fninit, 0xdbe3)
|
||||
DEF_ASM_OP0(fnclex, 0xdbe2)
|
||||
DEF_ASM_OP0(fnop, 0xd9d0)
|
||||
DEF_ASM_OP0(fwait, 0x9b)
|
||||
|
||||
/* fp load */
|
||||
DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||||
DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
|
||||
DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
|
||||
ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
|
||||
DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
|
||||
DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
|
||||
|
||||
/* fp store */
|
||||
DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
|
||||
DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
|
||||
DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
|
||||
ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
|
||||
DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
|
||||
|
||||
DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
|
||||
DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
|
||||
|
||||
/* exchange */
|
||||
DEF_ASM_OP0(fxch, 0xd9c9)
|
||||
ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
|
||||
|
||||
/* misc FPU */
|
||||
DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
|
||||
DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
|
||||
|
||||
DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
|
||||
DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
|
||||
DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
|
||||
DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||||
DEF_ASM_OP0(fnstsw, 0xdfe0)
|
||||
ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
|
||||
ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
|
||||
DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
|
||||
ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
|
||||
ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
|
||||
DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
|
||||
DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
|
||||
DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||||
DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
|
||||
DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
|
||||
DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
|
||||
DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
|
||||
DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
|
||||
DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
|
||||
|
||||
/* segments */
|
||||
DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
|
||||
DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
|
||||
DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
|
||||
DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
|
||||
ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
|
||||
DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
|
||||
DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
|
||||
DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
|
||||
DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||||
DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
|
||||
DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
|
||||
DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
|
||||
|
||||
/* 486 */
|
||||
DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
|
||||
ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||||
ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
|
||||
DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
|
||||
|
||||
DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
|
||||
DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
|
||||
|
||||
/* pentium */
|
||||
DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
|
||||
|
||||
/* pentium pro */
|
||||
ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
|
||||
|
||||
DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
|
||||
DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
|
||||
|
||||
/* mmx */
|
||||
DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
|
||||
DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
|
||||
DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
|
||||
DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
|
||||
DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
|
||||
|
||||
#undef ALT
|
||||
#undef DEF_ASM_OP0
|
||||
#undef DEF_ASM_OP0L
|
||||
#undef DEF_ASM_OP1
|
||||
#undef DEF_ASM_OP2
|
||||
#undef DEF_ASM_OP3
|
||||
942
i386-gen.c
Normal file
942
i386-gen.c
Normal file
@@ -0,0 +1,942 @@
|
||||
/*
|
||||
* X86 code generator for TCC
|
||||
*
|
||||
* Copyright (c) 2001, 2002, 2003 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* number of available registers */
|
||||
#define NB_REGS 4
|
||||
|
||||
/* a register can belong to several classes. The classes must be
|
||||
sorted from more general to more precise (see gv2() code which does
|
||||
assumptions on it). */
|
||||
#define RC_INT 0x0001 /* generic integer register */
|
||||
#define RC_FLOAT 0x0002 /* generic float register */
|
||||
#define RC_EAX 0x0004
|
||||
#define RC_ST0 0x0008
|
||||
#define RC_ECX 0x0010
|
||||
#define RC_EDX 0x0020
|
||||
#define RC_IRET RC_EAX /* function return: integer register */
|
||||
#define RC_LRET RC_EDX /* function return: second integer register */
|
||||
#define RC_FRET RC_ST0 /* function return: float register */
|
||||
|
||||
/* pretty names for the registers */
|
||||
enum {
|
||||
TREG_EAX = 0,
|
||||
TREG_ECX,
|
||||
TREG_EDX,
|
||||
TREG_ST0,
|
||||
};
|
||||
|
||||
int reg_classes[NB_REGS] = {
|
||||
/* eax */ RC_INT | RC_EAX,
|
||||
/* ecx */ RC_INT | RC_ECX,
|
||||
/* edx */ RC_INT | RC_EDX,
|
||||
/* st0 */ RC_FLOAT | RC_ST0,
|
||||
};
|
||||
|
||||
/* return registers for function */
|
||||
#define REG_IRET TREG_EAX /* single word int return register */
|
||||
#define REG_LRET TREG_EDX /* second word return register (for long long) */
|
||||
#define REG_FRET TREG_ST0 /* float return register */
|
||||
|
||||
/* defined if function parameters must be evaluated in reverse order */
|
||||
#define INVERT_FUNC_PARAMS
|
||||
|
||||
/* defined if structures are passed as pointers. Otherwise structures
|
||||
are directly pushed on stack. */
|
||||
//#define FUNC_STRUCT_PARAM_AS_PTR
|
||||
|
||||
/* pointer size, in bytes */
|
||||
#define PTR_SIZE 4
|
||||
|
||||
/* long double size and alignment, in bytes */
|
||||
#define LDOUBLE_SIZE 12
|
||||
#define LDOUBLE_ALIGN 4
|
||||
/* maximum alignment (for aligned attribute support) */
|
||||
#define MAX_ALIGN 8
|
||||
|
||||
/* relocation type for 32 bit data relocation */
|
||||
#define R_DATA_32 R_386_32
|
||||
|
||||
/******************************************************/
|
||||
|
||||
static unsigned long func_sub_sp_offset;
|
||||
static unsigned long func_bound_offset;
|
||||
static int func_ret_sub;
|
||||
|
||||
/* XXX: make it faster ? */
|
||||
void g(int c)
|
||||
{
|
||||
int ind1;
|
||||
ind1 = ind + 1;
|
||||
if (ind1 > cur_text_section->data_allocated)
|
||||
section_realloc(cur_text_section, ind1);
|
||||
cur_text_section->data[ind] = c;
|
||||
ind = ind1;
|
||||
}
|
||||
|
||||
void o(int c)
|
||||
{
|
||||
while (c) {
|
||||
g(c);
|
||||
c = c / 256;
|
||||
}
|
||||
}
|
||||
|
||||
void gen_le32(int c)
|
||||
{
|
||||
g(c);
|
||||
g(c >> 8);
|
||||
g(c >> 16);
|
||||
g(c >> 24);
|
||||
}
|
||||
|
||||
/* output a symbol and patch all calls to it */
|
||||
void gsym_addr(int t, int a)
|
||||
{
|
||||
int n, *ptr;
|
||||
while (t) {
|
||||
ptr = (int *)(cur_text_section->data + t);
|
||||
n = *ptr; /* next value */
|
||||
*ptr = a - t - 4;
|
||||
t = n;
|
||||
}
|
||||
}
|
||||
|
||||
void gsym(int t)
|
||||
{
|
||||
gsym_addr(t, ind);
|
||||
}
|
||||
|
||||
/* psym is used to put an instruction with a data field which is a
|
||||
reference to a symbol. It is in fact the same as oad ! */
|
||||
#define psym oad
|
||||
|
||||
/* instruction + 4 bytes data. Return the address of the data */
|
||||
static int oad(int c, int s)
|
||||
{
|
||||
int ind1;
|
||||
|
||||
o(c);
|
||||
ind1 = ind + 4;
|
||||
if (ind1 > cur_text_section->data_allocated)
|
||||
section_realloc(cur_text_section, ind1);
|
||||
*(int *)(cur_text_section->data + ind) = s;
|
||||
s = ind;
|
||||
ind = ind1;
|
||||
return s;
|
||||
}
|
||||
|
||||
/* output constant with relocation if 'r & VT_SYM' is true */
|
||||
static void gen_addr32(int r, Sym *sym, int c)
|
||||
{
|
||||
if (r & VT_SYM)
|
||||
greloc(cur_text_section, sym, ind, R_386_32);
|
||||
gen_le32(c);
|
||||
}
|
||||
|
||||
/* generate a modrm reference. 'op_reg' contains the addtionnal 3
|
||||
opcode bits */
|
||||
static void gen_modrm(int op_reg, int r, Sym *sym, int c)
|
||||
{
|
||||
op_reg = op_reg << 3;
|
||||
if ((r & VT_VALMASK) == VT_CONST) {
|
||||
/* constant memory reference */
|
||||
o(0x05 | op_reg);
|
||||
gen_addr32(r, sym, c);
|
||||
} else if ((r & VT_VALMASK) == VT_LOCAL) {
|
||||
/* currently, we use only ebp as base */
|
||||
if (c == (char)c) {
|
||||
/* short reference */
|
||||
o(0x45 | op_reg);
|
||||
g(c);
|
||||
} else {
|
||||
oad(0x85 | op_reg, c);
|
||||
}
|
||||
} else {
|
||||
g(0x00 | op_reg | (r & VT_VALMASK));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* load 'r' from value 'sv' */
|
||||
void load(int r, SValue *sv)
|
||||
{
|
||||
int v, t, ft, fc, fr;
|
||||
SValue v1;
|
||||
|
||||
fr = sv->r;
|
||||
ft = sv->type.t;
|
||||
fc = sv->c.ul;
|
||||
|
||||
v = fr & VT_VALMASK;
|
||||
if (fr & VT_LVAL) {
|
||||
if (v == VT_LLOCAL) {
|
||||
v1.type.t = VT_INT;
|
||||
v1.r = VT_LOCAL | VT_LVAL;
|
||||
v1.c.ul = fc;
|
||||
load(r, &v1);
|
||||
fr = r;
|
||||
}
|
||||
if ((ft & VT_BTYPE) == VT_FLOAT) {
|
||||
o(0xd9); /* flds */
|
||||
r = 0;
|
||||
} else if ((ft & VT_BTYPE) == VT_DOUBLE) {
|
||||
o(0xdd); /* fldl */
|
||||
r = 0;
|
||||
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||||
o(0xdb); /* fldt */
|
||||
r = 5;
|
||||
} else if ((ft & VT_TYPE) == VT_BYTE) {
|
||||
o(0xbe0f); /* movsbl */
|
||||
} else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
|
||||
o(0xb60f); /* movzbl */
|
||||
} else if ((ft & VT_TYPE) == VT_SHORT) {
|
||||
o(0xbf0f); /* movswl */
|
||||
} else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
|
||||
o(0xb70f); /* movzwl */
|
||||
} else {
|
||||
o(0x8b); /* movl */
|
||||
}
|
||||
gen_modrm(r, fr, sv->sym, fc);
|
||||
} else {
|
||||
if (v == VT_CONST) {
|
||||
o(0xb8 + r); /* mov $xx, r */
|
||||
gen_addr32(fr, sv->sym, fc);
|
||||
} else if (v == VT_LOCAL) {
|
||||
o(0x8d); /* lea xxx(%ebp), r */
|
||||
gen_modrm(r, VT_LOCAL, sv->sym, fc);
|
||||
} else if (v == VT_CMP) {
|
||||
oad(0xb8 + r, 0); /* mov $0, r */
|
||||
o(0x0f); /* setxx %br */
|
||||
o(fc);
|
||||
o(0xc0 + r);
|
||||
} else if (v == VT_JMP || v == VT_JMPI) {
|
||||
t = v & 1;
|
||||
oad(0xb8 + r, t); /* mov $1, r */
|
||||
o(0x05eb); /* jmp after */
|
||||
gsym(fc);
|
||||
oad(0xb8 + r, t ^ 1); /* mov $0, r */
|
||||
} else if (v != r) {
|
||||
o(0x89);
|
||||
o(0xc0 + r + v * 8); /* mov v, r */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* store register 'r' in lvalue 'v' */
|
||||
void store(int r, SValue *v)
|
||||
{
|
||||
int fr, bt, ft, fc;
|
||||
|
||||
ft = v->type.t;
|
||||
fc = v->c.ul;
|
||||
fr = v->r & VT_VALMASK;
|
||||
bt = ft & VT_BTYPE;
|
||||
/* XXX: incorrect if float reg to reg */
|
||||
if (bt == VT_FLOAT) {
|
||||
o(0xd9); /* fsts */
|
||||
r = 2;
|
||||
} else if (bt == VT_DOUBLE) {
|
||||
o(0xdd); /* fstpl */
|
||||
r = 2;
|
||||
} else if (bt == VT_LDOUBLE) {
|
||||
o(0xc0d9); /* fld %st(0) */
|
||||
o(0xdb); /* fstpt */
|
||||
r = 7;
|
||||
} else {
|
||||
if (bt == VT_SHORT)
|
||||
o(0x66);
|
||||
if (bt == VT_BYTE)
|
||||
o(0x88);
|
||||
else
|
||||
o(0x89);
|
||||
}
|
||||
if (fr == VT_CONST ||
|
||||
fr == VT_LOCAL ||
|
||||
(v->r & VT_LVAL)) {
|
||||
gen_modrm(r, v->r, v->sym, fc);
|
||||
} else if (fr != r) {
|
||||
o(0xc0 + fr + r * 8); /* mov r, fr */
|
||||
}
|
||||
}
|
||||
|
||||
static void gadd_sp(int val)
|
||||
{
|
||||
if (val == (char)val) {
|
||||
o(0xc483);
|
||||
g(val);
|
||||
} else {
|
||||
oad(0xc481, val); /* add $xxx, %esp */
|
||||
}
|
||||
}
|
||||
|
||||
/* 'is_jmp' is '1' if it is a jump */
|
||||
static void gcall_or_jmp(int is_jmp)
|
||||
{
|
||||
int r;
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||||
/* constant case */
|
||||
if (vtop->r & VT_SYM) {
|
||||
/* relocation case */
|
||||
greloc(cur_text_section, vtop->sym,
|
||||
ind + 1, R_386_PC32);
|
||||
} else {
|
||||
/* put an empty PC32 relocation */
|
||||
put_elf_reloc(symtab_section, cur_text_section,
|
||||
ind + 1, R_386_PC32, 0);
|
||||
}
|
||||
oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */
|
||||
} else {
|
||||
/* otherwise, indirect call */
|
||||
r = gv(RC_INT);
|
||||
o(0xff); /* call/jmp *r */
|
||||
o(0xd0 + r + (is_jmp << 4));
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate function call. The function address is pushed first, then
|
||||
all the parameters in call order. This functions pops all the
|
||||
parameters and the function address. */
|
||||
void gfunc_call(int nb_args)
|
||||
{
|
||||
int size, align, r, args_size, i;
|
||||
Sym *func_sym;
|
||||
|
||||
args_size = 0;
|
||||
for(i = 0;i < nb_args; i++) {
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
|
||||
size = type_size(&vtop->type, &align);
|
||||
/* align to stack align size */
|
||||
size = (size + 3) & ~3;
|
||||
/* allocate the necessary size on stack */
|
||||
oad(0xec81, size); /* sub $xxx, %esp */
|
||||
/* generate structure store */
|
||||
r = get_reg(RC_INT);
|
||||
o(0x89); /* mov %esp, r */
|
||||
o(0xe0 + r);
|
||||
vset(&vtop->type, r | VT_LVAL, 0);
|
||||
vswap();
|
||||
vstore();
|
||||
args_size += size;
|
||||
} else if (is_float(vtop->type.t)) {
|
||||
gv(RC_FLOAT); /* only one float register */
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
|
||||
size = 4;
|
||||
else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
|
||||
size = 8;
|
||||
else
|
||||
size = 12;
|
||||
oad(0xec81, size); /* sub $xxx, %esp */
|
||||
if (size == 12)
|
||||
o(0x7cdb);
|
||||
else
|
||||
o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
|
||||
g(0x24);
|
||||
g(0x00);
|
||||
args_size += size;
|
||||
} else {
|
||||
/* simple type (currently always same size) */
|
||||
/* XXX: implicit cast ? */
|
||||
r = gv(RC_INT);
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
|
||||
size = 8;
|
||||
o(0x50 + vtop->r2); /* push r */
|
||||
} else {
|
||||
size = 4;
|
||||
}
|
||||
o(0x50 + r); /* push r */
|
||||
args_size += size;
|
||||
}
|
||||
vtop--;
|
||||
}
|
||||
save_regs(0); /* save used temporary registers */
|
||||
func_sym = vtop->type.ref;
|
||||
gcall_or_jmp(0);
|
||||
if (args_size && func_sym->r == FUNC_CDECL)
|
||||
gadd_sp(args_size);
|
||||
vtop--;
|
||||
}
|
||||
|
||||
/* generate function prolog of type 't' */
|
||||
void gfunc_prolog(CType *func_type)
|
||||
{
|
||||
int addr, align, size, func_call;
|
||||
Sym *sym;
|
||||
CType *type;
|
||||
|
||||
sym = func_type->ref;
|
||||
func_call = sym->r;
|
||||
addr = 8;
|
||||
/* if the function returns a structure, then add an
|
||||
implicit pointer parameter */
|
||||
func_vt = sym->type;
|
||||
if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
|
||||
func_vc = addr;
|
||||
addr += 4;
|
||||
}
|
||||
/* define parameters */
|
||||
while ((sym = sym->next) != NULL) {
|
||||
type = &sym->type;
|
||||
sym_push(sym->v & ~SYM_FIELD, type,
|
||||
VT_LOCAL | VT_LVAL, addr);
|
||||
size = type_size(type, &align);
|
||||
size = (size + 3) & ~3;
|
||||
#ifdef FUNC_STRUCT_PARAM_AS_PTR
|
||||
/* structs are passed as pointer */
|
||||
if ((type->t & VT_BTYPE) == VT_STRUCT) {
|
||||
size = 4;
|
||||
}
|
||||
#endif
|
||||
addr += size;
|
||||
}
|
||||
func_ret_sub = 0;
|
||||
/* pascal type call ? */
|
||||
if (func_call == FUNC_STDCALL)
|
||||
func_ret_sub = addr - 8;
|
||||
o(0xe58955); /* push %ebp, mov %esp, %ebp */
|
||||
func_sub_sp_offset = oad(0xec81, 0); /* sub $xxx, %esp */
|
||||
/* leave some room for bound checking code */
|
||||
if (do_bounds_check) {
|
||||
oad(0xb8, 0); /* lbound section pointer */
|
||||
oad(0xb8, 0); /* call to function */
|
||||
func_bound_offset = lbounds_section->data_offset;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate function epilog */
|
||||
void gfunc_epilog(void)
|
||||
{
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
if (do_bounds_check && func_bound_offset != lbounds_section->data_offset) {
|
||||
int saved_ind;
|
||||
int *bounds_ptr;
|
||||
Sym *sym, *sym_data;
|
||||
/* add end of table info */
|
||||
bounds_ptr = section_ptr_add(lbounds_section, sizeof(int));
|
||||
*bounds_ptr = 0;
|
||||
/* generate bound local allocation */
|
||||
saved_ind = ind;
|
||||
ind = func_sub_sp_offset + 4;
|
||||
sym_data = get_sym_ref(&char_pointer_type, lbounds_section,
|
||||
func_bound_offset, lbounds_section->data_offset);
|
||||
greloc(cur_text_section, sym_data,
|
||||
ind + 1, R_386_32);
|
||||
oad(0xb8, 0); /* mov %eax, xxx */
|
||||
sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0);
|
||||
greloc(cur_text_section, sym,
|
||||
ind + 1, R_386_PC32);
|
||||
oad(0xe8, -4);
|
||||
ind = saved_ind;
|
||||
/* generate bound check local freeing */
|
||||
o(0x5250); /* save returned value, if any */
|
||||
greloc(cur_text_section, sym_data,
|
||||
ind + 1, R_386_32);
|
||||
oad(0xb8, 0); /* mov %eax, xxx */
|
||||
sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0);
|
||||
greloc(cur_text_section, sym,
|
||||
ind + 1, R_386_PC32);
|
||||
oad(0xe8, -4);
|
||||
o(0x585a); /* restore returned value, if any */
|
||||
}
|
||||
#endif
|
||||
o(0xc9); /* leave */
|
||||
if (func_ret_sub == 0) {
|
||||
o(0xc3); /* ret */
|
||||
} else {
|
||||
o(0xc2); /* ret n */
|
||||
g(func_ret_sub);
|
||||
g(func_ret_sub >> 8);
|
||||
}
|
||||
/* align local size to word & save local variables */
|
||||
*(int *)(cur_text_section->data + func_sub_sp_offset) = (-loc + 3) & -4;
|
||||
}
|
||||
|
||||
/* generate a jump to a label */
|
||||
int gjmp(int t)
|
||||
{
|
||||
return psym(0xe9, t);
|
||||
}
|
||||
|
||||
/* generate a jump to a fixed address */
|
||||
void gjmp_addr(int a)
|
||||
{
|
||||
int r;
|
||||
r = a - ind - 2;
|
||||
if (r == (char)r) {
|
||||
g(0xeb);
|
||||
g(r);
|
||||
} else {
|
||||
oad(0xe9, a - ind - 5);
|
||||
}
|
||||
}
|
||||
|
||||
/* generate a test. set 'inv' to invert test. Stack entry is popped */
|
||||
int gtst(int inv, int t)
|
||||
{
|
||||
int v, *p;
|
||||
|
||||
v = vtop->r & VT_VALMASK;
|
||||
if (v == VT_CMP) {
|
||||
/* fast case : can jump directly since flags are set */
|
||||
g(0x0f);
|
||||
t = psym((vtop->c.i - 16) ^ inv, t);
|
||||
} else if (v == VT_JMP || v == VT_JMPI) {
|
||||
/* && or || optimization */
|
||||
if ((v & 1) == inv) {
|
||||
/* insert vtop->c jump list in t */
|
||||
p = &vtop->c.i;
|
||||
while (*p != 0)
|
||||
p = (int *)(cur_text_section->data + *p);
|
||||
*p = t;
|
||||
t = vtop->c.i;
|
||||
} else {
|
||||
t = gjmp(t);
|
||||
gsym(vtop->c.i);
|
||||
}
|
||||
} else {
|
||||
if (is_float(vtop->type.t)) {
|
||||
vpushi(0);
|
||||
gen_op(TOK_NE);
|
||||
}
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||||
/* constant jmp optimization */
|
||||
if ((vtop->c.i != 0) != inv)
|
||||
t = gjmp(t);
|
||||
} else {
|
||||
v = gv(RC_INT);
|
||||
o(0x85);
|
||||
o(0xc0 + v * 9);
|
||||
g(0x0f);
|
||||
t = psym(0x85 ^ inv, t);
|
||||
}
|
||||
}
|
||||
vtop--;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* generate an integer binary operation */
|
||||
void gen_opi(int op)
|
||||
{
|
||||
int r, fr, opc, c;
|
||||
|
||||
switch(op) {
|
||||
case '+':
|
||||
case TOK_ADDC1: /* add with carry generation */
|
||||
opc = 0;
|
||||
gen_op8:
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||||
/* constant case */
|
||||
vswap();
|
||||
r = gv(RC_INT);
|
||||
vswap();
|
||||
c = vtop->c.i;
|
||||
if (c == (char)c) {
|
||||
/* XXX: generate inc and dec for smaller code ? */
|
||||
o(0x83);
|
||||
o(0xc0 | (opc << 3) | r);
|
||||
g(c);
|
||||
} else {
|
||||
o(0x81);
|
||||
oad(0xc0 | (opc << 3) | r, c);
|
||||
}
|
||||
} else {
|
||||
gv2(RC_INT, RC_INT);
|
||||
r = vtop[-1].r;
|
||||
fr = vtop[0].r;
|
||||
o((opc << 3) | 0x01);
|
||||
o(0xc0 + r + fr * 8);
|
||||
}
|
||||
vtop--;
|
||||
if (op >= TOK_ULT && op <= TOK_GT) {
|
||||
vtop->r = VT_CMP;
|
||||
vtop->c.i = op;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
case TOK_SUBC1: /* sub with carry generation */
|
||||
opc = 5;
|
||||
goto gen_op8;
|
||||
case TOK_ADDC2: /* add with carry use */
|
||||
opc = 2;
|
||||
goto gen_op8;
|
||||
case TOK_SUBC2: /* sub with carry use */
|
||||
opc = 3;
|
||||
goto gen_op8;
|
||||
case '&':
|
||||
opc = 4;
|
||||
goto gen_op8;
|
||||
case '^':
|
||||
opc = 6;
|
||||
goto gen_op8;
|
||||
case '|':
|
||||
opc = 1;
|
||||
goto gen_op8;
|
||||
case '*':
|
||||
gv2(RC_INT, RC_INT);
|
||||
r = vtop[-1].r;
|
||||
fr = vtop[0].r;
|
||||
vtop--;
|
||||
o(0xaf0f); /* imul fr, r */
|
||||
o(0xc0 + fr + r * 8);
|
||||
break;
|
||||
case TOK_SHL:
|
||||
opc = 4;
|
||||
goto gen_shift;
|
||||
case TOK_SHR:
|
||||
opc = 5;
|
||||
goto gen_shift;
|
||||
case TOK_SAR:
|
||||
opc = 7;
|
||||
gen_shift:
|
||||
opc = 0xc0 | (opc << 3);
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
|
||||
/* constant case */
|
||||
vswap();
|
||||
r = gv(RC_INT);
|
||||
vswap();
|
||||
c = vtop->c.i & 0x1f;
|
||||
o(0xc1); /* shl/shr/sar $xxx, r */
|
||||
o(opc | r);
|
||||
g(c);
|
||||
} else {
|
||||
/* we generate the shift in ecx */
|
||||
gv2(RC_INT, RC_ECX);
|
||||
r = vtop[-1].r;
|
||||
o(0xd3); /* shl/shr/sar %cl, r */
|
||||
o(opc | r);
|
||||
}
|
||||
vtop--;
|
||||
break;
|
||||
case '/':
|
||||
case TOK_UDIV:
|
||||
case TOK_PDIV:
|
||||
case '%':
|
||||
case TOK_UMOD:
|
||||
case TOK_UMULL:
|
||||
/* first operand must be in eax */
|
||||
/* XXX: need better constraint for second operand */
|
||||
gv2(RC_EAX, RC_ECX);
|
||||
r = vtop[-1].r;
|
||||
fr = vtop[0].r;
|
||||
vtop--;
|
||||
save_reg(TREG_EDX);
|
||||
if (op == TOK_UMULL) {
|
||||
o(0xf7); /* mul fr */
|
||||
o(0xe0 + fr);
|
||||
vtop->r2 = TREG_EDX;
|
||||
r = TREG_EAX;
|
||||
} else {
|
||||
if (op == TOK_UDIV || op == TOK_UMOD) {
|
||||
o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
|
||||
o(0xf0 + fr);
|
||||
} else {
|
||||
o(0xf799); /* cltd, idiv fr, %eax */
|
||||
o(0xf8 + fr);
|
||||
}
|
||||
if (op == '%' || op == TOK_UMOD)
|
||||
r = TREG_EDX;
|
||||
else
|
||||
r = TREG_EAX;
|
||||
}
|
||||
vtop->r = r;
|
||||
break;
|
||||
default:
|
||||
opc = 7;
|
||||
goto gen_op8;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate a floating point operation 'v = t1 op t2' instruction. The
|
||||
two operands are guaranted to have the same floating point type */
|
||||
/* XXX: need to use ST1 too */
|
||||
void gen_opf(int op)
|
||||
{
|
||||
int a, ft, fc, swapped, r;
|
||||
|
||||
/* convert constants to memory references */
|
||||
if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||||
vswap();
|
||||
gv(RC_FLOAT);
|
||||
vswap();
|
||||
}
|
||||
if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
|
||||
gv(RC_FLOAT);
|
||||
|
||||
/* must put at least one value in the floating point register */
|
||||
if ((vtop[-1].r & VT_LVAL) &&
|
||||
(vtop[0].r & VT_LVAL)) {
|
||||
vswap();
|
||||
gv(RC_FLOAT);
|
||||
vswap();
|
||||
}
|
||||
swapped = 0;
|
||||
/* swap the stack if needed so that t1 is the register and t2 is
|
||||
the memory reference */
|
||||
if (vtop[-1].r & VT_LVAL) {
|
||||
vswap();
|
||||
swapped = 1;
|
||||
}
|
||||
if (op >= TOK_ULT && op <= TOK_GT) {
|
||||
/* load on stack second operand */
|
||||
load(TREG_ST0, vtop);
|
||||
save_reg(TREG_EAX); /* eax is used by FP comparison code */
|
||||
if (op == TOK_GE || op == TOK_GT)
|
||||
swapped = !swapped;
|
||||
else if (op == TOK_EQ || op == TOK_NE)
|
||||
swapped = 0;
|
||||
if (swapped)
|
||||
o(0xc9d9); /* fxch %st(1) */
|
||||
o(0xe9da); /* fucompp */
|
||||
o(0xe0df); /* fnstsw %ax */
|
||||
if (op == TOK_EQ) {
|
||||
o(0x45e480); /* and $0x45, %ah */
|
||||
o(0x40fC80); /* cmp $0x40, %ah */
|
||||
} else if (op == TOK_NE) {
|
||||
o(0x45e480); /* and $0x45, %ah */
|
||||
o(0x40f480); /* xor $0x40, %ah */
|
||||
op = TOK_NE;
|
||||
} else if (op == TOK_GE || op == TOK_LE) {
|
||||
o(0x05c4f6); /* test $0x05, %ah */
|
||||
op = TOK_EQ;
|
||||
} else {
|
||||
o(0x45c4f6); /* test $0x45, %ah */
|
||||
op = TOK_EQ;
|
||||
}
|
||||
vtop--;
|
||||
vtop->r = VT_CMP;
|
||||
vtop->c.i = op;
|
||||
} else {
|
||||
/* no memory reference possible for long double operations */
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
|
||||
load(TREG_ST0, vtop);
|
||||
swapped = !swapped;
|
||||
}
|
||||
|
||||
switch(op) {
|
||||
default:
|
||||
case '+':
|
||||
a = 0;
|
||||
break;
|
||||
case '-':
|
||||
a = 4;
|
||||
if (swapped)
|
||||
a++;
|
||||
break;
|
||||
case '*':
|
||||
a = 1;
|
||||
break;
|
||||
case '/':
|
||||
a = 6;
|
||||
if (swapped)
|
||||
a++;
|
||||
break;
|
||||
}
|
||||
ft = vtop->type.t;
|
||||
fc = vtop->c.ul;
|
||||
if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||||
o(0xde); /* fxxxp %st, %st(1) */
|
||||
o(0xc1 + (a << 3));
|
||||
} else {
|
||||
/* if saved lvalue, then we must reload it */
|
||||
r = vtop->r;
|
||||
if ((r & VT_VALMASK) == VT_LLOCAL) {
|
||||
SValue v1;
|
||||
r = get_reg(RC_INT);
|
||||
v1.type.t = VT_INT;
|
||||
v1.r = VT_LOCAL | VT_LVAL;
|
||||
v1.c.ul = fc;
|
||||
load(r, &v1);
|
||||
fc = 0;
|
||||
}
|
||||
|
||||
if ((ft & VT_BTYPE) == VT_DOUBLE)
|
||||
o(0xdc);
|
||||
else
|
||||
o(0xd8);
|
||||
gen_modrm(a, r, vtop->sym, fc);
|
||||
}
|
||||
vtop--;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
|
||||
and 'long long' cases. */
|
||||
void gen_cvt_itof(int t)
|
||||
{
|
||||
save_reg(TREG_ST0);
|
||||
gv(RC_INT);
|
||||
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
|
||||
/* signed long long to float/double/long double (unsigned case
|
||||
is handled generically) */
|
||||
o(0x50 + vtop->r2); /* push r2 */
|
||||
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
|
||||
o(0x242cdf); /* fildll (%esp) */
|
||||
o(0x08c483); /* add $8, %esp */
|
||||
} else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
|
||||
(VT_INT | VT_UNSIGNED)) {
|
||||
/* unsigned int to float/double/long double */
|
||||
o(0x6a); /* push $0 */
|
||||
g(0x00);
|
||||
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
|
||||
o(0x242cdf); /* fildll (%esp) */
|
||||
o(0x08c483); /* add $8, %esp */
|
||||
} else {
|
||||
/* int to float/double/long double */
|
||||
o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
|
||||
o(0x2404db); /* fildl (%esp) */
|
||||
o(0x04c483); /* add $4, %esp */
|
||||
}
|
||||
vtop->r = TREG_ST0;
|
||||
}
|
||||
|
||||
/* convert fp to int 't' type */
|
||||
/* XXX: handle long long case */
|
||||
void gen_cvt_ftoi(int t)
|
||||
{
|
||||
int r, r2, size;
|
||||
Sym *sym;
|
||||
CType ushort_type;
|
||||
|
||||
ushort_type.t = VT_SHORT | VT_UNSIGNED;
|
||||
|
||||
gv(RC_FLOAT);
|
||||
if (t != VT_INT)
|
||||
size = 8;
|
||||
else
|
||||
size = 4;
|
||||
|
||||
o(0x2dd9); /* ldcw xxx */
|
||||
sym = external_global_sym(TOK___tcc_int_fpu_control,
|
||||
&ushort_type, VT_LVAL);
|
||||
greloc(cur_text_section, sym,
|
||||
ind, R_386_32);
|
||||
gen_le32(0);
|
||||
|
||||
oad(0xec81, size); /* sub $xxx, %esp */
|
||||
if (size == 4)
|
||||
o(0x1cdb); /* fistpl */
|
||||
else
|
||||
o(0x3cdf); /* fistpll */
|
||||
o(0x24);
|
||||
o(0x2dd9); /* ldcw xxx */
|
||||
sym = external_global_sym(TOK___tcc_fpu_control,
|
||||
&ushort_type, VT_LVAL);
|
||||
greloc(cur_text_section, sym,
|
||||
ind, R_386_32);
|
||||
gen_le32(0);
|
||||
|
||||
r = get_reg(RC_INT);
|
||||
o(0x58 + r); /* pop r */
|
||||
if (size == 8) {
|
||||
if (t == VT_LLONG) {
|
||||
vtop->r = r; /* mark reg as used */
|
||||
r2 = get_reg(RC_INT);
|
||||
o(0x58 + r2); /* pop r2 */
|
||||
vtop->r2 = r2;
|
||||
} else {
|
||||
o(0x04c483); /* add $4, %esp */
|
||||
}
|
||||
}
|
||||
vtop->r = r;
|
||||
}
|
||||
|
||||
/* convert from one floating point type to another */
|
||||
void gen_cvt_ftof(int t)
|
||||
{
|
||||
/* all we have to do on i386 is to put the float in a register */
|
||||
gv(RC_FLOAT);
|
||||
}
|
||||
|
||||
/* computed goto support */
|
||||
void ggoto(void)
|
||||
{
|
||||
gcall_or_jmp(1);
|
||||
vtop--;
|
||||
}
|
||||
|
||||
/* bound check support functions */
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
|
||||
/* generate a bounded pointer addition */
|
||||
void gen_bounded_ptr_add(void)
|
||||
{
|
||||
Sym *sym;
|
||||
|
||||
/* prepare fast i386 function call (args in eax and edx) */
|
||||
gv2(RC_EAX, RC_EDX);
|
||||
/* save all temporary registers */
|
||||
vtop -= 2;
|
||||
save_regs(0);
|
||||
/* do a fast function call */
|
||||
sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0);
|
||||
greloc(cur_text_section, sym,
|
||||
ind + 1, R_386_PC32);
|
||||
oad(0xe8, -4);
|
||||
/* returned pointer is in eax */
|
||||
vtop++;
|
||||
vtop->r = TREG_EAX | VT_BOUNDED;
|
||||
/* address of bounding function call point */
|
||||
vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
|
||||
}
|
||||
|
||||
/* patch pointer addition in vtop so that pointer dereferencing is
|
||||
also tested */
|
||||
void gen_bounded_ptr_deref(void)
|
||||
{
|
||||
int func;
|
||||
int size, align;
|
||||
Elf32_Rel *rel;
|
||||
Sym *sym;
|
||||
|
||||
size = 0;
|
||||
/* XXX: put that code in generic part of tcc */
|
||||
if (!is_float(vtop->type.t)) {
|
||||
if (vtop->r & VT_LVAL_BYTE)
|
||||
size = 1;
|
||||
else if (vtop->r & VT_LVAL_SHORT)
|
||||
size = 2;
|
||||
}
|
||||
if (!size)
|
||||
size = type_size(&vtop->type, &align);
|
||||
switch(size) {
|
||||
case 1: func = TOK___bound_ptr_indir1; break;
|
||||
case 2: func = TOK___bound_ptr_indir2; break;
|
||||
case 4: func = TOK___bound_ptr_indir4; break;
|
||||
case 8: func = TOK___bound_ptr_indir8; break;
|
||||
case 12: func = TOK___bound_ptr_indir12; break;
|
||||
case 16: func = TOK___bound_ptr_indir16; break;
|
||||
default:
|
||||
error("unhandled size when derefencing bounded pointer");
|
||||
func = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* patch relocation */
|
||||
/* XXX: find a better solution ? */
|
||||
rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul);
|
||||
sym = external_global_sym(func, &func_old_type, 0);
|
||||
if (!sym->c)
|
||||
put_extern_sym(sym, NULL, 0, 0);
|
||||
rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* end of X86 code generator */
|
||||
/*************************************************************/
|
||||
|
||||
667
il-gen.c
Normal file
667
il-gen.c
Normal file
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
* CIL code generator for TCC
|
||||
*
|
||||
* Copyright (c) 2002 Fabrice Bellard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* number of available registers */
|
||||
#define NB_REGS 3
|
||||
|
||||
/* a register can belong to several classes. The classes must be
|
||||
sorted from more general to more precise (see gv2() code which does
|
||||
assumptions on it). */
|
||||
#define RC_ST 0x0001 /* any stack entry */
|
||||
#define RC_ST0 0x0002 /* top of stack */
|
||||
#define RC_ST1 0x0004 /* top - 1 */
|
||||
|
||||
#define RC_INT RC_ST
|
||||
#define RC_FLOAT RC_ST
|
||||
#define RC_IRET RC_ST0 /* function return: integer register */
|
||||
#define RC_LRET RC_ST0 /* function return: second integer register */
|
||||
#define RC_FRET RC_ST0 /* function return: float register */
|
||||
|
||||
/* pretty names for the registers */
|
||||
enum {
|
||||
REG_ST0 = 0,
|
||||
REG_ST1,
|
||||
REG_ST2,
|
||||
};
|
||||
|
||||
int reg_classes[NB_REGS] = {
|
||||
/* ST0 */ RC_ST | RC_ST0,
|
||||
/* ST1 */ RC_ST | RC_ST1,
|
||||
/* ST2 */ RC_ST,
|
||||
};
|
||||
|
||||
/* return registers for function */
|
||||
#define REG_IRET REG_ST0 /* single word int return register */
|
||||
#define REG_LRET REG_ST0 /* second word return register (for long long) */
|
||||
#define REG_FRET REG_ST0 /* float return register */
|
||||
|
||||
/* defined if function parameters must be evaluated in reverse order */
|
||||
//#define INVERT_FUNC_PARAMS
|
||||
|
||||
/* defined if structures are passed as pointers. Otherwise structures
|
||||
are directly pushed on stack. */
|
||||
//#define FUNC_STRUCT_PARAM_AS_PTR
|
||||
|
||||
/* pointer size, in bytes */
|
||||
#define PTR_SIZE 4
|
||||
|
||||
/* long double size and alignment, in bytes */
|
||||
#define LDOUBLE_SIZE 8
|
||||
#define LDOUBLE_ALIGN 8
|
||||
|
||||
/* function call context */
|
||||
typedef struct GFuncContext {
|
||||
int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */
|
||||
} GFuncContext;
|
||||
|
||||
/******************************************************/
|
||||
/* opcode definitions */
|
||||
|
||||
#define IL_OP_PREFIX 0xFE
|
||||
|
||||
enum ILOPCodes {
|
||||
#define OP(name, str, n) IL_OP_ ## name = n,
|
||||
#include "il-opcodes.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
char *il_opcodes_str[] = {
|
||||
#define OP(name, str, n) [n] = str,
|
||||
#include "il-opcodes.h"
|
||||
#undef OP
|
||||
};
|
||||
|
||||
/******************************************************/
|
||||
|
||||
/* arguments variable numbers start from there */
|
||||
#define ARG_BASE 0x70000000
|
||||
|
||||
static FILE *il_outfile;
|
||||
|
||||
static void out_byte(int c)
|
||||
{
|
||||
*(char *)ind++ = c;
|
||||
}
|
||||
|
||||
static void out_le32(int c)
|
||||
{
|
||||
out_byte(c);
|
||||
out_byte(c >> 8);
|
||||
out_byte(c >> 16);
|
||||
out_byte(c >> 24);
|
||||
}
|
||||
|
||||
static void init_outfile(void)
|
||||
{
|
||||
if (!il_outfile) {
|
||||
il_outfile = stdout;
|
||||
fprintf(il_outfile,
|
||||
".assembly extern mscorlib\n"
|
||||
"{\n"
|
||||
".ver 1:0:2411:0\n"
|
||||
"}\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void out_op1(int op)
|
||||
{
|
||||
if (op & 0x100)
|
||||
out_byte(IL_OP_PREFIX);
|
||||
out_byte(op & 0xff);
|
||||
}
|
||||
|
||||
/* output an opcode with prefix */
|
||||
static void out_op(int op)
|
||||
{
|
||||
out_op1(op);
|
||||
fprintf(il_outfile, " %s\n", il_opcodes_str[op]);
|
||||
}
|
||||
|
||||
static void out_opb(int op, int c)
|
||||
{
|
||||
out_op1(op);
|
||||
out_byte(c);
|
||||
fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c);
|
||||
}
|
||||
|
||||
static void out_opi(int op, int c)
|
||||
{
|
||||
out_op1(op);
|
||||
out_le32(c);
|
||||
fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c);
|
||||
}
|
||||
|
||||
/* XXX: not complete */
|
||||
static void il_type_to_str(char *buf, int buf_size,
|
||||
int t, const char *varstr)
|
||||
{
|
||||
int bt;
|
||||
Sym *s, *sa;
|
||||
char buf1[256];
|
||||
const char *tstr;
|
||||
|
||||
t = t & VT_TYPE;
|
||||
bt = t & VT_BTYPE;
|
||||
buf[0] = '\0';
|
||||
if (t & VT_UNSIGNED)
|
||||
pstrcat(buf, buf_size, "unsigned ");
|
||||
switch(bt) {
|
||||
case VT_VOID:
|
||||
tstr = "void";
|
||||
goto add_tstr;
|
||||
case VT_BOOL:
|
||||
tstr = "bool";
|
||||
goto add_tstr;
|
||||
case VT_BYTE:
|
||||
tstr = "int8";
|
||||
goto add_tstr;
|
||||
case VT_SHORT:
|
||||
tstr = "int16";
|
||||
goto add_tstr;
|
||||
case VT_ENUM:
|
||||
case VT_INT:
|
||||
case VT_LONG:
|
||||
tstr = "int32";
|
||||
goto add_tstr;
|
||||
case VT_LLONG:
|
||||
tstr = "int64";
|
||||
goto add_tstr;
|
||||
case VT_FLOAT:
|
||||
tstr = "float32";
|
||||
goto add_tstr;
|
||||
case VT_DOUBLE:
|
||||
case VT_LDOUBLE:
|
||||
tstr = "float64";
|
||||
add_tstr:
|
||||
pstrcat(buf, buf_size, tstr);
|
||||
break;
|
||||
case VT_STRUCT:
|
||||
error("structures not handled yet");
|
||||
break;
|
||||
case VT_FUNC:
|
||||
s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
|
||||
il_type_to_str(buf, buf_size, s->t, varstr);
|
||||
pstrcat(buf, buf_size, "(");
|
||||
sa = s->next;
|
||||
while (sa != NULL) {
|
||||
il_type_to_str(buf1, sizeof(buf1), sa->t, NULL);
|
||||
pstrcat(buf, buf_size, buf1);
|
||||
sa = sa->next;
|
||||
if (sa)
|
||||
pstrcat(buf, buf_size, ", ");
|
||||
}
|
||||
pstrcat(buf, buf_size, ")");
|
||||
goto no_var;
|
||||
case VT_PTR:
|
||||
s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
|
||||
pstrcpy(buf1, sizeof(buf1), "*");
|
||||
if (varstr)
|
||||
pstrcat(buf1, sizeof(buf1), varstr);
|
||||
il_type_to_str(buf, buf_size, s->t, buf1);
|
||||
goto no_var;
|
||||
}
|
||||
if (varstr) {
|
||||
pstrcat(buf, buf_size, " ");
|
||||
pstrcat(buf, buf_size, varstr);
|
||||
}
|
||||
no_var: ;
|
||||
}
|
||||
|
||||
|
||||
/* patch relocation entry with value 'val' */
|
||||
void greloc_patch1(Reloc *p, int val)
|
||||
{
|
||||
}
|
||||
|
||||
/* output a symbol and patch all calls to it */
|
||||
void gsym_addr(t, a)
|
||||
{
|
||||
}
|
||||
|
||||
/* output jump and return symbol */
|
||||
static int out_opj(int op, int c)
|
||||
{
|
||||
out_op1(op);
|
||||
out_le32(0);
|
||||
if (c == 0) {
|
||||
c = ind - (int)cur_text_section->data;
|
||||
}
|
||||
fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c);
|
||||
return c;
|
||||
}
|
||||
|
||||
void gsym(int t)
|
||||
{
|
||||
fprintf(il_outfile, "L%d:\n", t);
|
||||
}
|
||||
|
||||
/* load 'r' from value 'sv' */
|
||||
void load(int r, SValue *sv)
|
||||
{
|
||||
int v, fc, ft;
|
||||
|
||||
v = sv->r & VT_VALMASK;
|
||||
fc = sv->c.i;
|
||||
ft = sv->t;
|
||||
|
||||
if (sv->r & VT_LVAL) {
|
||||
if (v == VT_LOCAL) {
|
||||
if (fc >= ARG_BASE) {
|
||||
fc -= ARG_BASE;
|
||||
if (fc >= 0 && fc <= 4) {
|
||||
out_op(IL_OP_LDARG_0 + fc);
|
||||
} else if (fc <= 0xff) {
|
||||
out_opb(IL_OP_LDARG_S, fc);
|
||||
} else {
|
||||
out_opi(IL_OP_LDARG, fc);
|
||||
}
|
||||
} else {
|
||||
if (fc >= 0 && fc <= 4) {
|
||||
out_op(IL_OP_LDLOC_0 + fc);
|
||||
} else if (fc <= 0xff) {
|
||||
out_opb(IL_OP_LDLOC_S, fc);
|
||||
} else {
|
||||
out_opi(IL_OP_LDLOC, fc);
|
||||
}
|
||||
}
|
||||
} else if (v == VT_CONST) {
|
||||
/* XXX: handle globals */
|
||||
out_opi(IL_OP_LDSFLD, 0);
|
||||
} else {
|
||||
if ((ft & VT_BTYPE) == VT_FLOAT) {
|
||||
out_op(IL_OP_LDIND_R4);
|
||||
} else if ((ft & VT_BTYPE) == VT_DOUBLE) {
|
||||
out_op(IL_OP_LDIND_R8);
|
||||
} else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
|
||||
out_op(IL_OP_LDIND_R8);
|
||||
} else if ((ft & VT_TYPE) == VT_BYTE)
|
||||
out_op(IL_OP_LDIND_I1);
|
||||
else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED))
|
||||
out_op(IL_OP_LDIND_U1);
|
||||
else if ((ft & VT_TYPE) == VT_SHORT)
|
||||
out_op(IL_OP_LDIND_I2);
|
||||
else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED))
|
||||
out_op(IL_OP_LDIND_U2);
|
||||
else
|
||||
out_op(IL_OP_LDIND_I4);
|
||||
}
|
||||
} else {
|
||||
if (v == VT_CONST) {
|
||||
/* XXX: handle globals */
|
||||
if (fc >= -1 && fc <= 8) {
|
||||
out_op(IL_OP_LDC_I4_M1 + fc + 1);
|
||||
} else {
|
||||
out_opi(IL_OP_LDC_I4, fc);
|
||||
}
|
||||
} else if (v == VT_LOCAL) {
|
||||
if (fc >= ARG_BASE) {
|
||||
fc -= ARG_BASE;
|
||||
if (fc <= 0xff) {
|
||||
out_opb(IL_OP_LDARGA_S, fc);
|
||||
} else {
|
||||
out_opi(IL_OP_LDARGA, fc);
|
||||
}
|
||||
} else {
|
||||
if (fc <= 0xff) {
|
||||
out_opb(IL_OP_LDLOCA_S, fc);
|
||||
} else {
|
||||
out_opi(IL_OP_LDLOCA, fc);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* XXX: do it */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* store register 'r' in lvalue 'v' */
|
||||
void store(int r, SValue *sv)
|
||||
{
|
||||
int v, fc, ft;
|
||||
|
||||
v = sv->r & VT_VALMASK;
|
||||
fc = sv->c.i;
|
||||
ft = sv->t;
|
||||
if (v == VT_LOCAL) {
|
||||
if (fc >= ARG_BASE) {
|
||||
fc -= ARG_BASE;
|
||||
/* XXX: check IL arg store semantics */
|
||||
if (fc <= 0xff) {
|
||||
out_opb(IL_OP_STARG_S, fc);
|
||||
} else {
|
||||
out_opi(IL_OP_STARG, fc);
|
||||
}
|
||||
} else {
|
||||
if (fc >= 0 && fc <= 4) {
|
||||
out_op(IL_OP_STLOC_0 + fc);
|
||||
} else if (fc <= 0xff) {
|
||||
out_opb(IL_OP_STLOC_S, fc);
|
||||
} else {
|
||||
out_opi(IL_OP_STLOC, fc);
|
||||
}
|
||||
}
|
||||
} else if (v == VT_CONST) {
|
||||
/* XXX: handle globals */
|
||||
out_opi(IL_OP_STSFLD, 0);
|
||||
} else {
|
||||
if ((ft & VT_BTYPE) == VT_FLOAT)
|
||||
out_op(IL_OP_STIND_R4);
|
||||
else if ((ft & VT_BTYPE) == VT_DOUBLE)
|
||||
out_op(IL_OP_STIND_R8);
|
||||
else if ((ft & VT_BTYPE) == VT_LDOUBLE)
|
||||
out_op(IL_OP_STIND_R8);
|
||||
else if ((ft & VT_BTYPE) == VT_BYTE)
|
||||
out_op(IL_OP_STIND_I1);
|
||||
else if ((ft & VT_BTYPE) == VT_SHORT)
|
||||
out_op(IL_OP_STIND_I2);
|
||||
else
|
||||
out_op(IL_OP_STIND_I4);
|
||||
}
|
||||
}
|
||||
|
||||
/* start function call and return function call context */
|
||||
void gfunc_start(GFuncContext *c, int func_call)
|
||||
{
|
||||
c->func_call = func_call;
|
||||
}
|
||||
|
||||
/* push function parameter which is in (vtop->t, vtop->c). Stack entry
|
||||
is then popped. */
|
||||
void gfunc_param(GFuncContext *c)
|
||||
{
|
||||
if ((vtop->t & VT_BTYPE) == VT_STRUCT) {
|
||||
error("structures passed as value not handled yet");
|
||||
} else {
|
||||
/* simply push on stack */
|
||||
gv(RC_ST0);
|
||||
}
|
||||
vtop--;
|
||||
}
|
||||
|
||||
/* generate function call with address in (vtop->t, vtop->c) and free function
|
||||
context. Stack entry is popped */
|
||||
void gfunc_call(GFuncContext *c)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
|
||||
/* XXX: more info needed from tcc */
|
||||
il_type_to_str(buf, sizeof(buf), vtop->t, "xxx");
|
||||
fprintf(il_outfile, " call %s\n", buf);
|
||||
} else {
|
||||
/* indirect call */
|
||||
gv(RC_INT);
|
||||
il_type_to_str(buf, sizeof(buf), vtop->t, NULL);
|
||||
fprintf(il_outfile, " calli %s\n", buf);
|
||||
}
|
||||
vtop--;
|
||||
}
|
||||
|
||||
/* generate function prolog of type 't' */
|
||||
void gfunc_prolog(int t)
|
||||
{
|
||||
int addr, u, func_call;
|
||||
Sym *sym;
|
||||
char buf[1024];
|
||||
|
||||
init_outfile();
|
||||
|
||||
/* XXX: pass function name to gfunc_prolog */
|
||||
il_type_to_str(buf, sizeof(buf), t, funcname);
|
||||
fprintf(il_outfile, ".method static %s il managed\n", buf);
|
||||
fprintf(il_outfile, "{\n");
|
||||
/* XXX: cannot do better now */
|
||||
fprintf(il_outfile, " .maxstack %d\n", NB_REGS);
|
||||
fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n");
|
||||
|
||||
if (!strcmp(funcname, "main"))
|
||||
fprintf(il_outfile, " .entrypoint\n");
|
||||
|
||||
sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
|
||||
func_call = sym->r;
|
||||
|
||||
addr = ARG_BASE;
|
||||
/* if the function returns a structure, then add an
|
||||
implicit pointer parameter */
|
||||
func_vt = sym->t;
|
||||
if ((func_vt & VT_BTYPE) == VT_STRUCT) {
|
||||
func_vc = addr;
|
||||
addr++;
|
||||
}
|
||||
/* define parameters */
|
||||
while ((sym = sym->next) != NULL) {
|
||||
u = sym->t;
|
||||
sym_push(sym->v & ~SYM_FIELD, u,
|
||||
VT_LOCAL | VT_LVAL, addr);
|
||||
addr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate function epilog */
|
||||
void gfunc_epilog(void)
|
||||
{
|
||||
out_op(IL_OP_RET);
|
||||
fprintf(il_outfile, "}\n\n");
|
||||
}
|
||||
|
||||
/* generate a jump to a label */
|
||||
int gjmp(int t)
|
||||
{
|
||||
return out_opj(IL_OP_BR, t);
|
||||
}
|
||||
|
||||
/* generate a jump to a fixed address */
|
||||
void gjmp_addr(int a)
|
||||
{
|
||||
/* XXX: handle syms */
|
||||
out_opi(IL_OP_BR, a);
|
||||
}
|
||||
|
||||
/* generate a test. set 'inv' to invert test. Stack entry is popped */
|
||||
int gtst(int inv, int t)
|
||||
{
|
||||
int v, *p, c;
|
||||
|
||||
v = vtop->r & VT_VALMASK;
|
||||
if (v == VT_CMP) {
|
||||
c = vtop->c.i ^ inv;
|
||||
switch(c) {
|
||||
case TOK_EQ:
|
||||
c = IL_OP_BEQ;
|
||||
break;
|
||||
case TOK_NE:
|
||||
c = IL_OP_BNE_UN;
|
||||
break;
|
||||
case TOK_LT:
|
||||
c = IL_OP_BLT;
|
||||
break;
|
||||
case TOK_LE:
|
||||
c = IL_OP_BLE;
|
||||
break;
|
||||
case TOK_GT:
|
||||
c = IL_OP_BGT;
|
||||
break;
|
||||
case TOK_GE:
|
||||
c = IL_OP_BGE;
|
||||
break;
|
||||
case TOK_ULT:
|
||||
c = IL_OP_BLT_UN;
|
||||
break;
|
||||
case TOK_ULE:
|
||||
c = IL_OP_BLE_UN;
|
||||
break;
|
||||
case TOK_UGT:
|
||||
c = IL_OP_BGT_UN;
|
||||
break;
|
||||
case TOK_UGE:
|
||||
c = IL_OP_BGE_UN;
|
||||
break;
|
||||
}
|
||||
t = out_opj(c, t);
|
||||
} else if (v == VT_JMP || v == VT_JMPI) {
|
||||
/* && or || optimization */
|
||||
if ((v & 1) == inv) {
|
||||
/* insert vtop->c jump list in t */
|
||||
p = &vtop->c.i;
|
||||
while (*p != 0)
|
||||
p = (int *)*p;
|
||||
*p = t;
|
||||
t = vtop->c.i;
|
||||
} else {
|
||||
t = gjmp(t);
|
||||
gsym(vtop->c.i);
|
||||
}
|
||||
} else {
|
||||
if (is_float(vtop->t)) {
|
||||
vpushi(0);
|
||||
gen_op(TOK_NE);
|
||||
}
|
||||
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) {
|
||||
/* constant jmp optimization */
|
||||
if ((vtop->c.i != 0) != inv)
|
||||
t = gjmp(t);
|
||||
} else {
|
||||
v = gv(RC_INT);
|
||||
t = out_opj(IL_OP_BRTRUE - inv, t);
|
||||
}
|
||||
}
|
||||
vtop--;
|
||||
return t;
|
||||
}
|
||||
|
||||
/* generate an integer binary operation */
|
||||
void gen_opi(int op)
|
||||
{
|
||||
gv2(RC_ST1, RC_ST0);
|
||||
switch(op) {
|
||||
case '+':
|
||||
out_op(IL_OP_ADD);
|
||||
goto std_op;
|
||||
case '-':
|
||||
out_op(IL_OP_SUB);
|
||||
goto std_op;
|
||||
case '&':
|
||||
out_op(IL_OP_AND);
|
||||
goto std_op;
|
||||
case '^':
|
||||
out_op(IL_OP_XOR);
|
||||
goto std_op;
|
||||
case '|':
|
||||
out_op(IL_OP_OR);
|
||||
goto std_op;
|
||||
case '*':
|
||||
out_op(IL_OP_MUL);
|
||||
goto std_op;
|
||||
case TOK_SHL:
|
||||
out_op(IL_OP_SHL);
|
||||
goto std_op;
|
||||
case TOK_SHR:
|
||||
out_op(IL_OP_SHR_UN);
|
||||
goto std_op;
|
||||
case TOK_SAR:
|
||||
out_op(IL_OP_SHR);
|
||||
goto std_op;
|
||||
case '/':
|
||||
case TOK_PDIV:
|
||||
out_op(IL_OP_DIV);
|
||||
goto std_op;
|
||||
case TOK_UDIV:
|
||||
out_op(IL_OP_DIV_UN);
|
||||
goto std_op;
|
||||
case '%':
|
||||
out_op(IL_OP_REM);
|
||||
goto std_op;
|
||||
case TOK_UMOD:
|
||||
out_op(IL_OP_REM_UN);
|
||||
std_op:
|
||||
vtop--;
|
||||
vtop[0].r = REG_ST0;
|
||||
break;
|
||||
case TOK_EQ:
|
||||
case TOK_NE:
|
||||
case TOK_LT:
|
||||
case TOK_LE:
|
||||
case TOK_GT:
|
||||
case TOK_GE:
|
||||
case TOK_ULT:
|
||||
case TOK_ULE:
|
||||
case TOK_UGT:
|
||||
case TOK_UGE:
|
||||
vtop--;
|
||||
vtop[0].r = VT_CMP;
|
||||
vtop[0].c.i = op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* generate a floating point operation 'v = t1 op t2' instruction. The
|
||||
two operands are guaranted to have the same floating point type */
|
||||
void gen_opf(int op)
|
||||
{
|
||||
/* same as integer */
|
||||
gen_opi(op);
|
||||
}
|
||||
|
||||
/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
|
||||
and 'long long' cases. */
|
||||
void gen_cvt_itof(int t)
|
||||
{
|
||||
gv(RC_ST0);
|
||||
if (t == VT_FLOAT)
|
||||
out_op(IL_OP_CONV_R4);
|
||||
else
|
||||
out_op(IL_OP_CONV_R8);
|
||||
}
|
||||
|
||||
/* convert fp to int 't' type */
|
||||
/* XXX: handle long long case */
|
||||
void gen_cvt_ftoi(int t)
|
||||
{
|
||||
gv(RC_ST0);
|
||||
switch(t) {
|
||||
case VT_INT | VT_UNSIGNED:
|
||||
out_op(IL_OP_CONV_U4);
|
||||
break;
|
||||
case VT_LLONG:
|
||||
out_op(IL_OP_CONV_I8);
|
||||
break;
|
||||
case VT_LLONG | VT_UNSIGNED:
|
||||
out_op(IL_OP_CONV_U8);
|
||||
break;
|
||||
default:
|
||||
out_op(IL_OP_CONV_I4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* convert from one floating point type to another */
|
||||
void gen_cvt_ftof(int t)
|
||||
{
|
||||
gv(RC_ST0);
|
||||
if (t == VT_FLOAT) {
|
||||
out_op(IL_OP_CONV_R4);
|
||||
} else {
|
||||
out_op(IL_OP_CONV_R8);
|
||||
}
|
||||
}
|
||||
|
||||
/* end of CIL code generator */
|
||||
/*************************************************************/
|
||||
|
||||
251
il-opcodes.h
Normal file
251
il-opcodes.h
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* CIL opcode definition
|
||||
*
|
||||
* Copyright (c) 2002 Fabrice Bellard
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
OP(NOP, "nop", 0x00)
|
||||
OP(BREAK, "break", 0x01)
|
||||
OP(LDARG_0, "ldarg.0", 0x02)
|
||||
OP(LDARG_1, "ldarg.1", 0x03)
|
||||
OP(LDARG_2, "ldarg.2", 0x04)
|
||||
OP(LDARG_3, "ldarg.3", 0x05)
|
||||
OP(LDLOC_0, "ldloc.0", 0x06)
|
||||
OP(LDLOC_1, "ldloc.1", 0x07)
|
||||
OP(LDLOC_2, "ldloc.2", 0x08)
|
||||
OP(LDLOC_3, "ldloc.3", 0x09)
|
||||
OP(STLOC_0, "stloc.0", 0x0a)
|
||||
OP(STLOC_1, "stloc.1", 0x0b)
|
||||
OP(STLOC_2, "stloc.2", 0x0c)
|
||||
OP(STLOC_3, "stloc.3", 0x0d)
|
||||
OP(LDARG_S, "ldarg.s", 0x0e)
|
||||
OP(LDARGA_S, "ldarga.s", 0x0f)
|
||||
OP(STARG_S, "starg.s", 0x10)
|
||||
OP(LDLOC_S, "ldloc.s", 0x11)
|
||||
OP(LDLOCA_S, "ldloca.s", 0x12)
|
||||
OP(STLOC_S, "stloc.s", 0x13)
|
||||
OP(LDNULL, "ldnull", 0x14)
|
||||
OP(LDC_I4_M1, "ldc.i4.m1", 0x15)
|
||||
OP(LDC_I4_0, "ldc.i4.0", 0x16)
|
||||
OP(LDC_I4_1, "ldc.i4.1", 0x17)
|
||||
OP(LDC_I4_2, "ldc.i4.2", 0x18)
|
||||
OP(LDC_I4_3, "ldc.i4.3", 0x19)
|
||||
OP(LDC_I4_4, "ldc.i4.4", 0x1a)
|
||||
OP(LDC_I4_5, "ldc.i4.5", 0x1b)
|
||||
OP(LDC_I4_6, "ldc.i4.6", 0x1c)
|
||||
OP(LDC_I4_7, "ldc.i4.7", 0x1d)
|
||||
OP(LDC_I4_8, "ldc.i4.8", 0x1e)
|
||||
OP(LDC_I4_S, "ldc.i4.s", 0x1f)
|
||||
OP(LDC_I4, "ldc.i4", 0x20)
|
||||
OP(LDC_I8, "ldc.i8", 0x21)
|
||||
OP(LDC_R4, "ldc.r4", 0x22)
|
||||
OP(LDC_R8, "ldc.r8", 0x23)
|
||||
OP(LDPTR, "ldptr", 0x24)
|
||||
OP(DUP, "dup", 0x25)
|
||||
OP(POP, "pop", 0x26)
|
||||
OP(JMP, "jmp", 0x27)
|
||||
OP(CALL, "call", 0x28)
|
||||
OP(CALLI, "calli", 0x29)
|
||||
OP(RET, "ret", 0x2a)
|
||||
OP(BR_S, "br.s", 0x2b)
|
||||
OP(BRFALSE_S, "brfalse.s", 0x2c)
|
||||
OP(BRTRUE_S, "brtrue.s", 0x2d)
|
||||
OP(BEQ_S, "beq.s", 0x2e)
|
||||
OP(BGE_S, "bge.s", 0x2f)
|
||||
OP(BGT_S, "bgt.s", 0x30)
|
||||
OP(BLE_S, "ble.s", 0x31)
|
||||
OP(BLT_S, "blt.s", 0x32)
|
||||
OP(BNE_UN_S, "bne.un.s", 0x33)
|
||||
OP(BGE_UN_S, "bge.un.s", 0x34)
|
||||
OP(BGT_UN_S, "bgt.un.s", 0x35)
|
||||
OP(BLE_UN_S, "ble.un.s", 0x36)
|
||||
OP(BLT_UN_S, "blt.un.s", 0x37)
|
||||
OP(BR, "br", 0x38)
|
||||
OP(BRFALSE, "brfalse", 0x39)
|
||||
OP(BRTRUE, "brtrue", 0x3a)
|
||||
OP(BEQ, "beq", 0x3b)
|
||||
OP(BGE, "bge", 0x3c)
|
||||
OP(BGT, "bgt", 0x3d)
|
||||
OP(BLE, "ble", 0x3e)
|
||||
OP(BLT, "blt", 0x3f)
|
||||
OP(BNE_UN, "bne.un", 0x40)
|
||||
OP(BGE_UN, "bge.un", 0x41)
|
||||
OP(BGT_UN, "bgt.un", 0x42)
|
||||
OP(BLE_UN, "ble.un", 0x43)
|
||||
OP(BLT_UN, "blt.un", 0x44)
|
||||
OP(SWITCH, "switch", 0x45)
|
||||
OP(LDIND_I1, "ldind.i1", 0x46)
|
||||
OP(LDIND_U1, "ldind.u1", 0x47)
|
||||
OP(LDIND_I2, "ldind.i2", 0x48)
|
||||
OP(LDIND_U2, "ldind.u2", 0x49)
|
||||
OP(LDIND_I4, "ldind.i4", 0x4a)
|
||||
OP(LDIND_U4, "ldind.u4", 0x4b)
|
||||
OP(LDIND_I8, "ldind.i8", 0x4c)
|
||||
OP(LDIND_I, "ldind.i", 0x4d)
|
||||
OP(LDIND_R4, "ldind.r4", 0x4e)
|
||||
OP(LDIND_R8, "ldind.r8", 0x4f)
|
||||
OP(LDIND_REF, "ldind.ref", 0x50)
|
||||
OP(STIND_REF, "stind.ref", 0x51)
|
||||
OP(STIND_I1, "stind.i1", 0x52)
|
||||
OP(STIND_I2, "stind.i2", 0x53)
|
||||
OP(STIND_I4, "stind.i4", 0x54)
|
||||
OP(STIND_I8, "stind.i8", 0x55)
|
||||
OP(STIND_R4, "stind.r4", 0x56)
|
||||
OP(STIND_R8, "stind.r8", 0x57)
|
||||
OP(ADD, "add", 0x58)
|
||||
OP(SUB, "sub", 0x59)
|
||||
OP(MUL, "mul", 0x5a)
|
||||
OP(DIV, "div", 0x5b)
|
||||
OP(DIV_UN, "div.un", 0x5c)
|
||||
OP(REM, "rem", 0x5d)
|
||||
OP(REM_UN, "rem.un", 0x5e)
|
||||
OP(AND, "and", 0x5f)
|
||||
OP(OR, "or", 0x60)
|
||||
OP(XOR, "xor", 0x61)
|
||||
OP(SHL, "shl", 0x62)
|
||||
OP(SHR, "shr", 0x63)
|
||||
OP(SHR_UN, "shr.un", 0x64)
|
||||
OP(NEG, "neg", 0x65)
|
||||
OP(NOT, "not", 0x66)
|
||||
OP(CONV_I1, "conv.i1", 0x67)
|
||||
OP(CONV_I2, "conv.i2", 0x68)
|
||||
OP(CONV_I4, "conv.i4", 0x69)
|
||||
OP(CONV_I8, "conv.i8", 0x6a)
|
||||
OP(CONV_R4, "conv.r4", 0x6b)
|
||||
OP(CONV_R8, "conv.r8", 0x6c)
|
||||
OP(CONV_U4, "conv.u4", 0x6d)
|
||||
OP(CONV_U8, "conv.u8", 0x6e)
|
||||
OP(CALLVIRT, "callvirt", 0x6f)
|
||||
OP(CPOBJ, "cpobj", 0x70)
|
||||
OP(LDOBJ, "ldobj", 0x71)
|
||||
OP(LDSTR, "ldstr", 0x72)
|
||||
OP(NEWOBJ, "newobj", 0x73)
|
||||
OP(CASTCLASS, "castclass", 0x74)
|
||||
OP(ISINST, "isinst", 0x75)
|
||||
OP(CONV_R_UN, "conv.r.un", 0x76)
|
||||
OP(ANN_DATA_S, "ann.data.s", 0x77)
|
||||
OP(UNBOX, "unbox", 0x79)
|
||||
OP(THROW, "throw", 0x7a)
|
||||
OP(LDFLD, "ldfld", 0x7b)
|
||||
OP(LDFLDA, "ldflda", 0x7c)
|
||||
OP(STFLD, "stfld", 0x7d)
|
||||
OP(LDSFLD, "ldsfld", 0x7e)
|
||||
OP(LDSFLDA, "ldsflda", 0x7f)
|
||||
OP(STSFLD, "stsfld", 0x80)
|
||||
OP(STOBJ, "stobj", 0x81)
|
||||
OP(CONV_OVF_I1_UN, "conv.ovf.i1.un", 0x82)
|
||||
OP(CONV_OVF_I2_UN, "conv.ovf.i2.un", 0x83)
|
||||
OP(CONV_OVF_I4_UN, "conv.ovf.i4.un", 0x84)
|
||||
OP(CONV_OVF_I8_UN, "conv.ovf.i8.un", 0x85)
|
||||
OP(CONV_OVF_U1_UN, "conv.ovf.u1.un", 0x86)
|
||||
OP(CONV_OVF_U2_UN, "conv.ovf.u2.un", 0x87)
|
||||
OP(CONV_OVF_U4_UN, "conv.ovf.u4.un", 0x88)
|
||||
OP(CONV_OVF_U8_UN, "conv.ovf.u8.un", 0x89)
|
||||
OP(CONV_OVF_I_UN, "conv.ovf.i.un", 0x8a)
|
||||
OP(CONV_OVF_U_UN, "conv.ovf.u.un", 0x8b)
|
||||
OP(BOX, "box", 0x8c)
|
||||
OP(NEWARR, "newarr", 0x8d)
|
||||
OP(LDLEN, "ldlen", 0x8e)
|
||||
OP(LDELEMA, "ldelema", 0x8f)
|
||||
OP(LDELEM_I1, "ldelem.i1", 0x90)
|
||||
OP(LDELEM_U1, "ldelem.u1", 0x91)
|
||||
OP(LDELEM_I2, "ldelem.i2", 0x92)
|
||||
OP(LDELEM_U2, "ldelem.u2", 0x93)
|
||||
OP(LDELEM_I4, "ldelem.i4", 0x94)
|
||||
OP(LDELEM_U4, "ldelem.u4", 0x95)
|
||||
OP(LDELEM_I8, "ldelem.i8", 0x96)
|
||||
OP(LDELEM_I, "ldelem.i", 0x97)
|
||||
OP(LDELEM_R4, "ldelem.r4", 0x98)
|
||||
OP(LDELEM_R8, "ldelem.r8", 0x99)
|
||||
OP(LDELEM_REF, "ldelem.ref", 0x9a)
|
||||
OP(STELEM_I, "stelem.i", 0x9b)
|
||||
OP(STELEM_I1, "stelem.i1", 0x9c)
|
||||
OP(STELEM_I2, "stelem.i2", 0x9d)
|
||||
OP(STELEM_I4, "stelem.i4", 0x9e)
|
||||
OP(STELEM_I8, "stelem.i8", 0x9f)
|
||||
OP(STELEM_R4, "stelem.r4", 0xa0)
|
||||
OP(STELEM_R8, "stelem.r8", 0xa1)
|
||||
OP(STELEM_REF, "stelem.ref", 0xa2)
|
||||
OP(CONV_OVF_I1, "conv.ovf.i1", 0xb3)
|
||||
OP(CONV_OVF_U1, "conv.ovf.u1", 0xb4)
|
||||
OP(CONV_OVF_I2, "conv.ovf.i2", 0xb5)
|
||||
OP(CONV_OVF_U2, "conv.ovf.u2", 0xb6)
|
||||
OP(CONV_OVF_I4, "conv.ovf.i4", 0xb7)
|
||||
OP(CONV_OVF_U4, "conv.ovf.u4", 0xb8)
|
||||
OP(CONV_OVF_I8, "conv.ovf.i8", 0xb9)
|
||||
OP(CONV_OVF_U8, "conv.ovf.u8", 0xba)
|
||||
OP(REFANYVAL, "refanyval", 0xc2)
|
||||
OP(CKFINITE, "ckfinite", 0xc3)
|
||||
OP(MKREFANY, "mkrefany", 0xc6)
|
||||
OP(ANN_CALL, "ann.call", 0xc7)
|
||||
OP(ANN_CATCH, "ann.catch", 0xc8)
|
||||
OP(ANN_DEAD, "ann.dead", 0xc9)
|
||||
OP(ANN_HOISTED, "ann.hoisted", 0xca)
|
||||
OP(ANN_HOISTED_CALL, "ann.hoisted.call", 0xcb)
|
||||
OP(ANN_LAB, "ann.lab", 0xcc)
|
||||
OP(ANN_DEF, "ann.def", 0xcd)
|
||||
OP(ANN_REF_S, "ann.ref.s", 0xce)
|
||||
OP(ANN_PHI, "ann.phi", 0xcf)
|
||||
OP(LDTOKEN, "ldtoken", 0xd0)
|
||||
OP(CONV_U2, "conv.u2", 0xd1)
|
||||
OP(CONV_U1, "conv.u1", 0xd2)
|
||||
OP(CONV_I, "conv.i", 0xd3)
|
||||
OP(CONV_OVF_I, "conv.ovf.i", 0xd4)
|
||||
OP(CONV_OVF_U, "conv.ovf.u", 0xd5)
|
||||
OP(ADD_OVF, "add.ovf", 0xd6)
|
||||
OP(ADD_OVF_UN, "add.ovf.un", 0xd7)
|
||||
OP(MUL_OVF, "mul.ovf", 0xd8)
|
||||
OP(MUL_OVF_UN, "mul.ovf.un", 0xd9)
|
||||
OP(SUB_OVF, "sub.ovf", 0xda)
|
||||
OP(SUB_OVF_UN, "sub.ovf.un", 0xdb)
|
||||
OP(ENDFINALLY, "endfinally", 0xdc)
|
||||
OP(LEAVE, "leave", 0xdd)
|
||||
OP(LEAVE_S, "leave.s", 0xde)
|
||||
OP(STIND_I, "stind.i", 0xdf)
|
||||
OP(CONV_U, "conv.u", 0xe0)
|
||||
|
||||
/* prefix instructions. we use an opcode >= 256 to ease coding */
|
||||
|
||||
OP(ARGLIST, "arglist", 0x100)
|
||||
OP(CEQ, "ceq", 0x101)
|
||||
OP(CGT, "cgt", 0x102)
|
||||
OP(CGT_UN, "cgt.un", 0x103)
|
||||
OP(CLT, "clt", 0x104)
|
||||
OP(CLT_UN, "clt.un", 0x105)
|
||||
OP(LDFTN, "ldftn", 0x106)
|
||||
OP(LDVIRTFTN, "ldvirtftn", 0x107)
|
||||
OP(JMPI, "jmpi", 0x108)
|
||||
OP(LDARG, "ldarg", 0x109)
|
||||
OP(LDARGA, "ldarga", 0x10a)
|
||||
OP(STARG, "starg", 0x10b)
|
||||
OP(LDLOC, "ldloc", 0x10c)
|
||||
OP(LDLOCA, "ldloca", 0x10d)
|
||||
OP(STLOC, "stloc", 0x10e)
|
||||
OP(LOCALLOC, "localloc", 0x10f)
|
||||
OP(ENDFILTER, "endfilter", 0x111)
|
||||
OP(UNALIGNED, "unaligned", 0x112)
|
||||
OP(VOLATILE, "volatile", 0x113)
|
||||
OP(TAIL, "tail", 0x114)
|
||||
OP(INITOBJ, "initobj", 0x115)
|
||||
OP(ANN_LIVE, "ann.live", 0x116)
|
||||
OP(CPBLK, "cpblk", 0x117)
|
||||
OP(INITBLK, "initblk", 0x118)
|
||||
OP(ANN_REF, "ann.ref", 0x119)
|
||||
OP(RETHROW, "rethrow", 0x11a)
|
||||
OP(SIZEOF, "sizeof", 0x11c)
|
||||
OP(REFANYTYPE, "refanytype", 0x11d)
|
||||
OP(ANN_DATA, "ann.data", 0x122)
|
||||
OP(ANN_ARG, "ann.arg", 0x123)
|
||||
93
libtcc.h
Normal file
93
libtcc.h
Normal file
@@ -0,0 +1,93 @@
|
||||
#ifndef LIBTCC_H
|
||||
#define LIBTCC_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct TCCState;
|
||||
|
||||
typedef struct TCCState TCCState;
|
||||
|
||||
/* create a new TCC compilation context */
|
||||
TCCState *tcc_new(void);
|
||||
|
||||
/* free a TCC compilation context */
|
||||
void tcc_delete(TCCState *s);
|
||||
|
||||
/* add debug information in the generated code */
|
||||
void tcc_enable_debug(TCCState *s);
|
||||
|
||||
/* set error/warning display callback */
|
||||
void tcc_set_error_func(TCCState *s, void *error_opaque,
|
||||
void (*error_func)(void *opaque, const char *msg));
|
||||
|
||||
/* set/reset a warning */
|
||||
int tcc_set_warning(TCCState *s, const char *warning_name, int value);
|
||||
|
||||
/*****************************/
|
||||
/* preprocessor */
|
||||
|
||||
/* add include path */
|
||||
int tcc_add_include_path(TCCState *s, const char *pathname);
|
||||
|
||||
/* add in system include path */
|
||||
int tcc_add_sysinclude_path(TCCState *s, const char *pathname);
|
||||
|
||||
/* define preprocessor symbol 'sym'. Can put optional value */
|
||||
void tcc_define_symbol(TCCState *s, const char *sym, const char *value);
|
||||
|
||||
/* undefine preprocess symbol 'sym' */
|
||||
void tcc_undefine_symbol(TCCState *s, const char *sym);
|
||||
|
||||
/*****************************/
|
||||
/* compiling */
|
||||
|
||||
/* add a file (either a C file, dll, an object, a library or an ld
|
||||
script). Return -1 if error. */
|
||||
int tcc_add_file(TCCState *s, const char *filename);
|
||||
|
||||
/* compile a string containing a C source. Return non zero if
|
||||
error. */
|
||||
int tcc_compile_string(TCCState *s, const char *buf);
|
||||
|
||||
/*****************************/
|
||||
/* linking commands */
|
||||
|
||||
/* set output type. MUST BE CALLED before any compilation */
|
||||
#define TCC_OUTPUT_MEMORY 0 /* output will be ran in memory (no
|
||||
output file) (default) */
|
||||
#define TCC_OUTPUT_EXE 1 /* executable file */
|
||||
#define TCC_OUTPUT_DLL 2 /* dynamic library */
|
||||
#define TCC_OUTPUT_OBJ 3 /* object file */
|
||||
int tcc_set_output_type(TCCState *s, int output_type);
|
||||
|
||||
/* equivalent to -Lpath option */
|
||||
int tcc_add_library_path(TCCState *s, const char *pathname);
|
||||
|
||||
/* the library name is the same as the argument of the '-l' option */
|
||||
int tcc_add_library(TCCState *s, const char *libraryname);
|
||||
|
||||
/* add a symbol to the compiled program */
|
||||
int tcc_add_symbol(TCCState *s, const char *name, unsigned long val);
|
||||
|
||||
/* output an executable, library or object file. DO NOT call
|
||||
tcc_relocate() before. */
|
||||
int tcc_output_file(TCCState *s, const char *filename);
|
||||
|
||||
/* link and run main() function and return its value. DO NOT call
|
||||
tcc_relocate() before. */
|
||||
int tcc_run(TCCState *s, int argc, char **argv);
|
||||
|
||||
/* do all relocations (needed before using tcc_get_symbol()). Return
|
||||
non zero if link error. */
|
||||
int tcc_relocate(TCCState *s);
|
||||
|
||||
/* return symbol value. return 0 if OK, -1 if symbol not found */
|
||||
int tcc_get_symbol(TCCState *s, unsigned long *pval, const char *name);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
602
libtcc1.c
Normal file
602
libtcc1.c
Normal file
@@ -0,0 +1,602 @@
|
||||
/* TCC runtime library.
|
||||
Parts of this code are (c) 2002 Fabrice Bellard
|
||||
|
||||
Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc.
|
||||
|
||||
This file is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by the
|
||||
Free Software Foundation; either version 2, or (at your option) any
|
||||
later version.
|
||||
|
||||
In addition to the permissions in the GNU General Public License, the
|
||||
Free Software Foundation gives you unlimited permission to link the
|
||||
compiled version of this file into combinations with other programs,
|
||||
and to distribute those combinations without any restriction coming
|
||||
from the use of this file. (The General Public License restrictions
|
||||
do apply in other respects; for example, they cover modification of
|
||||
the file, and distribution when not linked into a combine
|
||||
executable.)
|
||||
|
||||
This file is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; see the file COPYING. If not, write to
|
||||
the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#define W_TYPE_SIZE 32
|
||||
#define BITS_PER_UNIT 8
|
||||
|
||||
typedef int Wtype;
|
||||
typedef unsigned int UWtype;
|
||||
typedef unsigned int USItype;
|
||||
typedef long long DWtype;
|
||||
typedef unsigned long long UDWtype;
|
||||
|
||||
struct DWstruct {
|
||||
Wtype low, high;
|
||||
};
|
||||
|
||||
typedef union
|
||||
{
|
||||
struct DWstruct s;
|
||||
DWtype ll;
|
||||
} DWunion;
|
||||
|
||||
typedef long double XFtype;
|
||||
#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT)
|
||||
#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE)
|
||||
|
||||
/* the following deal with IEEE single-precision numbers */
|
||||
#define EXCESS 126
|
||||
#define SIGNBIT 0x80000000
|
||||
#define HIDDEN (1 << 23)
|
||||
#define SIGN(fp) ((fp) & SIGNBIT)
|
||||
#define EXP(fp) (((fp) >> 23) & 0xFF)
|
||||
#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN)
|
||||
#define PACK(s,e,m) ((s) | ((e) << 23) | (m))
|
||||
|
||||
/* the following deal with IEEE double-precision numbers */
|
||||
#define EXCESSD 1022
|
||||
#define HIDDEND (1 << 20)
|
||||
#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF)
|
||||
#define SIGND(fp) ((fp.l.upper) & SIGNBIT)
|
||||
#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \
|
||||
(fp.l.lower >> 22))
|
||||
#define HIDDEND_LL ((long long)1 << 52)
|
||||
#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL)
|
||||
#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m))
|
||||
|
||||
/* the following deal with x86 long double-precision numbers */
|
||||
#define EXCESSLD 16382
|
||||
#define EXPLD(fp) (fp.l.upper & 0x7fff)
|
||||
#define SIGNLD(fp) ((fp.l.upper) & 0x8000)
|
||||
|
||||
/* only for x86 */
|
||||
union ldouble_long {
|
||||
long double ld;
|
||||
struct {
|
||||
unsigned long long lower;
|
||||
unsigned short upper;
|
||||
} l;
|
||||
};
|
||||
|
||||
union double_long {
|
||||
double d;
|
||||
#if 1
|
||||
struct {
|
||||
unsigned long lower;
|
||||
long upper;
|
||||
} l;
|
||||
#else
|
||||
struct {
|
||||
long upper;
|
||||
unsigned long lower;
|
||||
} l;
|
||||
#endif
|
||||
long long ll;
|
||||
};
|
||||
|
||||
union float_long {
|
||||
float f;
|
||||
long l;
|
||||
};
|
||||
|
||||
/* XXX: use gcc/tcc intrinsic ? */
|
||||
#if defined(__i386__)
|
||||
#define sub_ddmmss(sh, sl, ah, al, bh, bl) \
|
||||
__asm__ ("subl %5,%1\n\tsbbl %3,%0" \
|
||||
: "=r" ((USItype) (sh)), \
|
||||
"=&r" ((USItype) (sl)) \
|
||||
: "0" ((USItype) (ah)), \
|
||||
"g" ((USItype) (bh)), \
|
||||
"1" ((USItype) (al)), \
|
||||
"g" ((USItype) (bl)))
|
||||
#define umul_ppmm(w1, w0, u, v) \
|
||||
__asm__ ("mull %3" \
|
||||
: "=a" ((USItype) (w0)), \
|
||||
"=d" ((USItype) (w1)) \
|
||||
: "%0" ((USItype) (u)), \
|
||||
"rm" ((USItype) (v)))
|
||||
#define udiv_qrnnd(q, r, n1, n0, dv) \
|
||||
__asm__ ("divl %4" \
|
||||
: "=a" ((USItype) (q)), \
|
||||
"=d" ((USItype) (r)) \
|
||||
: "0" ((USItype) (n0)), \
|
||||
"1" ((USItype) (n1)), \
|
||||
"rm" ((USItype) (dv)))
|
||||
#define count_leading_zeros(count, x) \
|
||||
do { \
|
||||
USItype __cbtmp; \
|
||||
__asm__ ("bsrl %1,%0" \
|
||||
: "=r" (__cbtmp) : "rm" ((USItype) (x))); \
|
||||
(count) = __cbtmp ^ 31; \
|
||||
} while (0)
|
||||
#else
|
||||
#error unsupported CPU type
|
||||
#endif
|
||||
|
||||
/* most of this code is taken from libgcc2.c from gcc */
|
||||
|
||||
static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp)
|
||||
{
|
||||
DWunion ww;
|
||||
DWunion nn, dd;
|
||||
DWunion rr;
|
||||
UWtype d0, d1, n0, n1, n2;
|
||||
UWtype q0, q1;
|
||||
UWtype b, bm;
|
||||
|
||||
nn.ll = n;
|
||||
dd.ll = d;
|
||||
|
||||
d0 = dd.s.low;
|
||||
d1 = dd.s.high;
|
||||
n0 = nn.s.low;
|
||||
n1 = nn.s.high;
|
||||
|
||||
#if !UDIV_NEEDS_NORMALIZATION
|
||||
if (d1 == 0)
|
||||
{
|
||||
if (d0 > n1)
|
||||
{
|
||||
/* 0q = nn / 0D */
|
||||
|
||||
udiv_qrnnd (q0, n0, n1, n0, d0);
|
||||
q1 = 0;
|
||||
|
||||
/* Remainder in n0. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* qq = NN / 0d */
|
||||
|
||||
if (d0 == 0)
|
||||
d0 = 1 / d0; /* Divide intentionally by zero. */
|
||||
|
||||
udiv_qrnnd (q1, n1, 0, n1, d0);
|
||||
udiv_qrnnd (q0, n0, n1, n0, d0);
|
||||
|
||||
/* Remainder in n0. */
|
||||
}
|
||||
|
||||
if (rp != 0)
|
||||
{
|
||||
rr.s.low = n0;
|
||||
rr.s.high = 0;
|
||||
*rp = rr.ll;
|
||||
}
|
||||
}
|
||||
|
||||
#else /* UDIV_NEEDS_NORMALIZATION */
|
||||
|
||||
if (d1 == 0)
|
||||
{
|
||||
if (d0 > n1)
|
||||
{
|
||||
/* 0q = nn / 0D */
|
||||
|
||||
count_leading_zeros (bm, d0);
|
||||
|
||||
if (bm != 0)
|
||||
{
|
||||
/* Normalize, i.e. make the most significant bit of the
|
||||
denominator set. */
|
||||
|
||||
d0 = d0 << bm;
|
||||
n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm));
|
||||
n0 = n0 << bm;
|
||||
}
|
||||
|
||||
udiv_qrnnd (q0, n0, n1, n0, d0);
|
||||
q1 = 0;
|
||||
|
||||
/* Remainder in n0 >> bm. */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* qq = NN / 0d */
|
||||
|
||||
if (d0 == 0)
|
||||
d0 = 1 / d0; /* Divide intentionally by zero. */
|
||||
|
||||
count_leading_zeros (bm, d0);
|
||||
|
||||
if (bm == 0)
|
||||
{
|
||||
/* From (n1 >= d0) /\ (the most significant bit of d0 is set),
|
||||
conclude (the most significant bit of n1 is set) /\ (the
|
||||
leading quotient digit q1 = 1).
|
||||
|
||||
This special case is necessary, not an optimization.
|
||||
(Shifts counts of W_TYPE_SIZE are undefined.) */
|
||||
|
||||
n1 -= d0;
|
||||
q1 = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Normalize. */
|
||||
|
||||
b = W_TYPE_SIZE - bm;
|
||||
|
||||
d0 = d0 << bm;
|
||||
n2 = n1 >> b;
|
||||
n1 = (n1 << bm) | (n0 >> b);
|
||||
n0 = n0 << bm;
|
||||
|
||||
udiv_qrnnd (q1, n1, n2, n1, d0);
|
||||
}
|
||||
|
||||
/* n1 != d0... */
|
||||
|
||||
udiv_qrnnd (q0, n0, n1, n0, d0);
|
||||
|
||||
/* Remainder in n0 >> bm. */
|
||||
}
|
||||
|
||||
if (rp != 0)
|
||||
{
|
||||
rr.s.low = n0 >> bm;
|
||||
rr.s.high = 0;
|
||||
*rp = rr.ll;
|
||||
}
|
||||
}
|
||||
#endif /* UDIV_NEEDS_NORMALIZATION */
|
||||
|
||||
else
|
||||
{
|
||||
if (d1 > n1)
|
||||
{
|
||||
/* 00 = nn / DD */
|
||||
|
||||
q0 = 0;
|
||||
q1 = 0;
|
||||
|
||||
/* Remainder in n1n0. */
|
||||
if (rp != 0)
|
||||
{
|
||||
rr.s.low = n0;
|
||||
rr.s.high = n1;
|
||||
*rp = rr.ll;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 0q = NN / dd */
|
||||
|
||||
count_leading_zeros (bm, d1);
|
||||
if (bm == 0)
|
||||
{
|
||||
/* From (n1 >= d1) /\ (the most significant bit of d1 is set),
|
||||
conclude (the most significant bit of n1 is set) /\ (the
|
||||
quotient digit q0 = 0 or 1).
|
||||
|
||||
This special case is necessary, not an optimization. */
|
||||
|
||||
/* The condition on the next line takes advantage of that
|
||||
n1 >= d1 (true due to program flow). */
|
||||
if (n1 > d1 || n0 >= d0)
|
||||
{
|
||||
q0 = 1;
|
||||
sub_ddmmss (n1, n0, n1, n0, d1, d0);
|
||||
}
|
||||
else
|
||||
q0 = 0;
|
||||
|
||||
q1 = 0;
|
||||
|
||||
if (rp != 0)
|
||||
{
|
||||
rr.s.low = n0;
|
||||
rr.s.high = n1;
|
||||
*rp = rr.ll;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UWtype m1, m0;
|
||||
/* Normalize. */
|
||||
|
||||
b = W_TYPE_SIZE - bm;
|
||||
|
||||
d1 = (d1 << bm) | (d0 >> b);
|
||||
d0 = d0 << bm;
|
||||
n2 = n1 >> b;
|
||||
n1 = (n1 << bm) | (n0 >> b);
|
||||
n0 = n0 << bm;
|
||||
|
||||
udiv_qrnnd (q0, n1, n2, n1, d1);
|
||||
umul_ppmm (m1, m0, q0, d0);
|
||||
|
||||
if (m1 > n1 || (m1 == n1 && m0 > n0))
|
||||
{
|
||||
q0--;
|
||||
sub_ddmmss (m1, m0, m1, m0, d1, d0);
|
||||
}
|
||||
|
||||
q1 = 0;
|
||||
|
||||
/* Remainder in (n1n0 - m1m0) >> bm. */
|
||||
if (rp != 0)
|
||||
{
|
||||
sub_ddmmss (n1, n0, n1, n0, m1, m0);
|
||||
rr.s.low = (n1 << b) | (n0 >> bm);
|
||||
rr.s.high = n1 >> bm;
|
||||
*rp = rr.ll;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ww.s.low = q0;
|
||||
ww.s.high = q1;
|
||||
return ww.ll;
|
||||
}
|
||||
|
||||
#define __negdi2(a) (-(a))
|
||||
|
||||
long long __divdi3(long long u, long long v)
|
||||
{
|
||||
int c = 0;
|
||||
DWunion uu, vv;
|
||||
DWtype w;
|
||||
|
||||
uu.ll = u;
|
||||
vv.ll = v;
|
||||
|
||||
if (uu.s.high < 0) {
|
||||
c = ~c;
|
||||
uu.ll = __negdi2 (uu.ll);
|
||||
}
|
||||
if (vv.s.high < 0) {
|
||||
c = ~c;
|
||||
vv.ll = __negdi2 (vv.ll);
|
||||
}
|
||||
w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0);
|
||||
if (c)
|
||||
w = __negdi2 (w);
|
||||
return w;
|
||||
}
|
||||
|
||||
long long __moddi3(long long u, long long v)
|
||||
{
|
||||
int c = 0;
|
||||
DWunion uu, vv;
|
||||
DWtype w;
|
||||
|
||||
uu.ll = u;
|
||||
vv.ll = v;
|
||||
|
||||
if (uu.s.high < 0) {
|
||||
c = ~c;
|
||||
uu.ll = __negdi2 (uu.ll);
|
||||
}
|
||||
if (vv.s.high < 0)
|
||||
vv.ll = __negdi2 (vv.ll);
|
||||
|
||||
__udivmoddi4 (uu.ll, vv.ll, &w);
|
||||
if (c)
|
||||
w = __negdi2 (w);
|
||||
return w;
|
||||
}
|
||||
|
||||
unsigned long long __udivdi3(unsigned long long u, unsigned long long v)
|
||||
{
|
||||
return __udivmoddi4 (u, v, (UDWtype *) 0);
|
||||
}
|
||||
|
||||
unsigned long long __umoddi3(unsigned long long u, unsigned long long v)
|
||||
{
|
||||
UDWtype w;
|
||||
|
||||
__udivmoddi4 (u, v, &w);
|
||||
return w;
|
||||
}
|
||||
|
||||
/* XXX: fix tcc's code generator to do this instead */
|
||||
long long __sardi3(long long a, int b)
|
||||
{
|
||||
#ifdef __TINYC__
|
||||
DWunion u;
|
||||
u.ll = a;
|
||||
if (b >= 32) {
|
||||
u.s.low = u.s.high >> (b - 32);
|
||||
u.s.high = u.s.high >> 31;
|
||||
} else if (b != 0) {
|
||||
u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
|
||||
u.s.high = u.s.high >> b;
|
||||
}
|
||||
return u.ll;
|
||||
#else
|
||||
return a >> b;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* XXX: fix tcc's code generator to do this instead */
|
||||
unsigned long long __shrdi3(unsigned long long a, int b)
|
||||
{
|
||||
#ifdef __TINYC__
|
||||
DWunion u;
|
||||
u.ll = a;
|
||||
if (b >= 32) {
|
||||
u.s.low = (unsigned)u.s.high >> (b - 32);
|
||||
u.s.high = 0;
|
||||
} else if (b != 0) {
|
||||
u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b));
|
||||
u.s.high = (unsigned)u.s.high >> b;
|
||||
}
|
||||
return u.ll;
|
||||
#else
|
||||
return a >> b;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* XXX: fix tcc's code generator to do this instead */
|
||||
long long __shldi3(long long a, int b)
|
||||
{
|
||||
#ifdef __TINYC__
|
||||
DWunion u;
|
||||
u.ll = a;
|
||||
if (b >= 32) {
|
||||
u.s.high = (unsigned)u.s.low << (b - 32);
|
||||
u.s.low = 0;
|
||||
} else if (b != 0) {
|
||||
u.s.high = ((unsigned)u.s.high << b) | (u.s.low >> (32 - b));
|
||||
u.s.low = (unsigned)u.s.low << b;
|
||||
}
|
||||
return u.ll;
|
||||
#else
|
||||
return a << b;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__i386__)
|
||||
/* FPU control word for rounding to nearest mode */
|
||||
unsigned short __tcc_fpu_control = 0x137f;
|
||||
/* FPU control word for round to zero mode for int conversion */
|
||||
unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00;
|
||||
#endif
|
||||
|
||||
/* XXX: fix tcc's code generator to do this instead */
|
||||
float __ulltof(unsigned long long a)
|
||||
{
|
||||
DWunion uu;
|
||||
XFtype r;
|
||||
|
||||
uu.ll = a;
|
||||
if (uu.s.high >= 0) {
|
||||
return (float)uu.ll;
|
||||
} else {
|
||||
r = (XFtype)uu.ll;
|
||||
r += 18446744073709551616.0;
|
||||
return (float)r;
|
||||
}
|
||||
}
|
||||
|
||||
double __ulltod(unsigned long long a)
|
||||
{
|
||||
DWunion uu;
|
||||
XFtype r;
|
||||
|
||||
uu.ll = a;
|
||||
if (uu.s.high >= 0) {
|
||||
return (double)uu.ll;
|
||||
} else {
|
||||
r = (XFtype)uu.ll;
|
||||
r += 18446744073709551616.0;
|
||||
return (double)r;
|
||||
}
|
||||
}
|
||||
|
||||
long double __ulltold(unsigned long long a)
|
||||
{
|
||||
DWunion uu;
|
||||
XFtype r;
|
||||
|
||||
uu.ll = a;
|
||||
if (uu.s.high >= 0) {
|
||||
return (long double)uu.ll;
|
||||
} else {
|
||||
r = (XFtype)uu.ll;
|
||||
r += 18446744073709551616.0;
|
||||
return (long double)r;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long __fixunssfdi (float a1)
|
||||
{
|
||||
register union float_long fl1;
|
||||
register int exp;
|
||||
register unsigned long l;
|
||||
|
||||
fl1.f = a1;
|
||||
|
||||
if (fl1.l == 0)
|
||||
return (0);
|
||||
|
||||
exp = EXP (fl1.l) - EXCESS - 24;
|
||||
|
||||
l = MANT(fl1.l);
|
||||
if (exp >= 41)
|
||||
return (unsigned long long)-1;
|
||||
else if (exp >= 0)
|
||||
return (unsigned long long)l << exp;
|
||||
else if (exp >= -23)
|
||||
return l >> -exp;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long __fixunsdfdi (double a1)
|
||||
{
|
||||
register union double_long dl1;
|
||||
register int exp;
|
||||
register unsigned long long l;
|
||||
|
||||
dl1.d = a1;
|
||||
|
||||
if (dl1.ll == 0)
|
||||
return (0);
|
||||
|
||||
exp = EXPD (dl1) - EXCESSD - 53;
|
||||
|
||||
l = MANTD_LL(dl1);
|
||||
|
||||
if (exp >= 12)
|
||||
return (unsigned long long)-1;
|
||||
else if (exp >= 0)
|
||||
return l << exp;
|
||||
else if (exp >= -52)
|
||||
return l >> -exp;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned long long __fixunsxfdi (long double a1)
|
||||
{
|
||||
register union ldouble_long dl1;
|
||||
register int exp;
|
||||
register unsigned long long l;
|
||||
|
||||
dl1.ld = a1;
|
||||
|
||||
if (dl1.l.lower == 0 && dl1.l.upper == 0)
|
||||
return (0);
|
||||
|
||||
exp = EXPLD (dl1) - EXCESSLD - 64;
|
||||
|
||||
l = dl1.l.lower;
|
||||
|
||||
if (exp > 0)
|
||||
return (unsigned long long)-1;
|
||||
else if (exp >= -63)
|
||||
return l >> -exp;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
65
libtcc_test.c
Normal file
65
libtcc_test.c
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Simple Test program for libtcc
|
||||
*
|
||||
* libtcc can be useful to use tcc as a "backend" for a code generator.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libtcc.h>
|
||||
|
||||
/* this function is called by the generated code */
|
||||
int add(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
char my_program[] =
|
||||
"int fib(int n)\n"
|
||||
"{\n"
|
||||
" if (n <= 2)\n"
|
||||
" return 1;\n"
|
||||
" else\n"
|
||||
" return fib(n-1) + fib(n-2);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"int foo(int n)\n"
|
||||
"{\n"
|
||||
" printf(\"Hello World!\\n\");\n"
|
||||
" printf(\"fib(%d) = %d\\n\", n, fib(n));\n"
|
||||
" printf(\"add(%d, %d) = %d\\n\", n, 2 * n, add(n, 2 * n));\n"
|
||||
" return 0;\n"
|
||||
"}\n";
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
TCCState *s;
|
||||
int (*func)(int);
|
||||
unsigned long val;
|
||||
|
||||
s = tcc_new();
|
||||
if (!s) {
|
||||
fprintf(stderr, "Could not create tcc state\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* MUST BE CALLED before any compilation or file loading */
|
||||
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
|
||||
|
||||
tcc_compile_string(s, my_program);
|
||||
|
||||
/* as a test, we add a symbol that the compiled program can be
|
||||
linked with. You can have a similar result by opening a dll
|
||||
with tcc_add_dll(() and using its symbols directly. */
|
||||
tcc_add_symbol(s, "add", (unsigned long)&add);
|
||||
|
||||
tcc_relocate(s);
|
||||
|
||||
tcc_get_symbol(s, &val, "foo");
|
||||
func = (void *)val;
|
||||
|
||||
func(32);
|
||||
|
||||
tcc_delete(s);
|
||||
return 0;
|
||||
}
|
||||
234
stab.def
Normal file
234
stab.def
Normal file
@@ -0,0 +1,234 @@
|
||||
/* Table of DBX symbol codes for the GNU system.
|
||||
Copyright (C) 1988, 1997 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public License as
|
||||
published by the Free Software Foundation; either version 2 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* This contains contribution from Cygnus Support. */
|
||||
|
||||
/* Global variable. Only the name is significant.
|
||||
To find the address, look in the corresponding external symbol. */
|
||||
__define_stab (N_GSYM, 0x20, "GSYM")
|
||||
|
||||
/* Function name for BSD Fortran. Only the name is significant.
|
||||
To find the address, look in the corresponding external symbol. */
|
||||
__define_stab (N_FNAME, 0x22, "FNAME")
|
||||
|
||||
/* Function name or text-segment variable for C. Value is its address.
|
||||
Desc is supposedly starting line number, but GCC doesn't set it
|
||||
and DBX seems not to miss it. */
|
||||
__define_stab (N_FUN, 0x24, "FUN")
|
||||
|
||||
/* Data-segment variable with internal linkage. Value is its address.
|
||||
"Static Sym". */
|
||||
__define_stab (N_STSYM, 0x26, "STSYM")
|
||||
|
||||
/* BSS-segment variable with internal linkage. Value is its address. */
|
||||
__define_stab (N_LCSYM, 0x28, "LCSYM")
|
||||
|
||||
/* Name of main routine. Only the name is significant.
|
||||
This is not used in C. */
|
||||
__define_stab (N_MAIN, 0x2a, "MAIN")
|
||||
|
||||
/* Global symbol in Pascal.
|
||||
Supposedly the value is its line number; I'm skeptical. */
|
||||
__define_stab (N_PC, 0x30, "PC")
|
||||
|
||||
/* Number of symbols: 0, files,,funcs,lines according to Ultrix V4.0. */
|
||||
__define_stab (N_NSYMS, 0x32, "NSYMS")
|
||||
|
||||
/* "No DST map for sym: name, ,0,type,ignored" according to Ultrix V4.0. */
|
||||
__define_stab (N_NOMAP, 0x34, "NOMAP")
|
||||
|
||||
/* New stab from Solaris. I don't know what it means, but it
|
||||
don't seem to contain useful information. */
|
||||
__define_stab (N_OBJ, 0x38, "OBJ")
|
||||
|
||||
/* New stab from Solaris. I don't know what it means, but it
|
||||
don't seem to contain useful information. Possibly related to the
|
||||
optimization flags used in this module. */
|
||||
__define_stab (N_OPT, 0x3c, "OPT")
|
||||
|
||||
/* Register variable. Value is number of register. */
|
||||
__define_stab (N_RSYM, 0x40, "RSYM")
|
||||
|
||||
/* Modula-2 compilation unit. Can someone say what info it contains? */
|
||||
__define_stab (N_M2C, 0x42, "M2C")
|
||||
|
||||
/* Line number in text segment. Desc is the line number;
|
||||
value is corresponding address. */
|
||||
__define_stab (N_SLINE, 0x44, "SLINE")
|
||||
|
||||
/* Similar, for data segment. */
|
||||
__define_stab (N_DSLINE, 0x46, "DSLINE")
|
||||
|
||||
/* Similar, for bss segment. */
|
||||
__define_stab (N_BSLINE, 0x48, "BSLINE")
|
||||
|
||||
/* Sun's source-code browser stabs. ?? Don't know what the fields are.
|
||||
Supposedly the field is "path to associated .cb file". THIS VALUE
|
||||
OVERLAPS WITH N_BSLINE! */
|
||||
__define_stab (N_BROWS, 0x48, "BROWS")
|
||||
|
||||
/* GNU Modula-2 definition module dependency. Value is the modification time
|
||||
of the definition file. Other is non-zero if it is imported with the
|
||||
GNU M2 keyword %INITIALIZE. Perhaps N_M2C can be used if there
|
||||
are enough empty fields? */
|
||||
__define_stab(N_DEFD, 0x4a, "DEFD")
|
||||
|
||||
/* THE FOLLOWING TWO STAB VALUES CONFLICT. Happily, one is for Modula-2
|
||||
and one is for C++. Still,... */
|
||||
/* GNU C++ exception variable. Name is variable name. */
|
||||
__define_stab (N_EHDECL, 0x50, "EHDECL")
|
||||
/* Modula2 info "for imc": name,,0,0,0 according to Ultrix V4.0. */
|
||||
__define_stab (N_MOD2, 0x50, "MOD2")
|
||||
|
||||
/* GNU C++ `catch' clause. Value is its address. Desc is nonzero if
|
||||
this entry is immediately followed by a CAUGHT stab saying what exception
|
||||
was caught. Multiple CAUGHT stabs means that multiple exceptions
|
||||
can be caught here. If Desc is 0, it means all exceptions are caught
|
||||
here. */
|
||||
__define_stab (N_CATCH, 0x54, "CATCH")
|
||||
|
||||
/* Structure or union element. Value is offset in the structure. */
|
||||
__define_stab (N_SSYM, 0x60, "SSYM")
|
||||
|
||||
/* Name of main source file.
|
||||
Value is starting text address of the compilation. */
|
||||
__define_stab (N_SO, 0x64, "SO")
|
||||
|
||||
/* Automatic variable in the stack. Value is offset from frame pointer.
|
||||
Also used for type descriptions. */
|
||||
__define_stab (N_LSYM, 0x80, "LSYM")
|
||||
|
||||
/* Beginning of an include file. Only Sun uses this.
|
||||
In an object file, only the name is significant.
|
||||
The Sun linker puts data into some of the other fields. */
|
||||
__define_stab (N_BINCL, 0x82, "BINCL")
|
||||
|
||||
/* Name of sub-source file (#include file).
|
||||
Value is starting text address of the compilation. */
|
||||
__define_stab (N_SOL, 0x84, "SOL")
|
||||
|
||||
/* Parameter variable. Value is offset from argument pointer.
|
||||
(On most machines the argument pointer is the same as the frame pointer. */
|
||||
__define_stab (N_PSYM, 0xa0, "PSYM")
|
||||
|
||||
/* End of an include file. No name.
|
||||
This and N_BINCL act as brackets around the file's output.
|
||||
In an object file, there is no significant data in this entry.
|
||||
The Sun linker puts data into some of the fields. */
|
||||
__define_stab (N_EINCL, 0xa2, "EINCL")
|
||||
|
||||
/* Alternate entry point. Value is its address. */
|
||||
__define_stab (N_ENTRY, 0xa4, "ENTRY")
|
||||
|
||||
/* Beginning of lexical block.
|
||||
The desc is the nesting level in lexical blocks.
|
||||
The value is the address of the start of the text for the block.
|
||||
The variables declared inside the block *precede* the N_LBRAC symbol. */
|
||||
__define_stab (N_LBRAC, 0xc0, "LBRAC")
|
||||
|
||||
/* Place holder for deleted include file. Replaces a N_BINCL and everything
|
||||
up to the corresponding N_EINCL. The Sun linker generates these when
|
||||
it finds multiple identical copies of the symbols from an include file.
|
||||
This appears only in output from the Sun linker. */
|
||||
__define_stab (N_EXCL, 0xc2, "EXCL")
|
||||
|
||||
/* Modula-2 scope information. Can someone say what info it contains? */
|
||||
__define_stab (N_SCOPE, 0xc4, "SCOPE")
|
||||
|
||||
/* End of a lexical block. Desc matches the N_LBRAC's desc.
|
||||
The value is the address of the end of the text for the block. */
|
||||
__define_stab (N_RBRAC, 0xe0, "RBRAC")
|
||||
|
||||
/* Begin named common block. Only the name is significant. */
|
||||
__define_stab (N_BCOMM, 0xe2, "BCOMM")
|
||||
|
||||
/* End named common block. Only the name is significant
|
||||
(and it should match the N_BCOMM). */
|
||||
__define_stab (N_ECOMM, 0xe4, "ECOMM")
|
||||
|
||||
/* End common (local name): value is address.
|
||||
I'm not sure how this is used. */
|
||||
__define_stab (N_ECOML, 0xe8, "ECOML")
|
||||
|
||||
/* These STAB's are used on Gould systems for Non-Base register symbols
|
||||
or something like that. FIXME. I have assigned the values at random
|
||||
since I don't have a Gould here. Fixups from Gould folk welcome... */
|
||||
__define_stab (N_NBTEXT, 0xF0, "NBTEXT")
|
||||
__define_stab (N_NBDATA, 0xF2, "NBDATA")
|
||||
__define_stab (N_NBBSS, 0xF4, "NBBSS")
|
||||
__define_stab (N_NBSTS, 0xF6, "NBSTS")
|
||||
__define_stab (N_NBLCS, 0xF8, "NBLCS")
|
||||
|
||||
/* Second symbol entry containing a length-value for the preceding entry.
|
||||
The value is the length. */
|
||||
__define_stab (N_LENG, 0xfe, "LENG")
|
||||
|
||||
/* The above information, in matrix format.
|
||||
|
||||
STAB MATRIX
|
||||
_________________________________________________
|
||||
| 00 - 1F are not dbx stab symbols |
|
||||
| In most cases, the low bit is the EXTernal bit|
|
||||
|
||||
| 00 UNDEF | 02 ABS | 04 TEXT | 06 DATA |
|
||||
| 01 |EXT | 03 |EXT | 05 |EXT | 07 |EXT |
|
||||
|
||||
| 08 BSS | 0A INDR | 0C FN_SEQ | 0E |
|
||||
| 09 |EXT | 0B | 0D | 0F |
|
||||
|
||||
| 10 | 12 COMM | 14 SETA | 16 SETT |
|
||||
| 11 | 13 | 15 | 17 |
|
||||
|
||||
| 18 SETD | 1A SETB | 1C SETV | 1E WARNING|
|
||||
| 19 | 1B | 1D | 1F FN |
|
||||
|
||||
|_______________________________________________|
|
||||
| Debug entries with bit 01 set are unused. |
|
||||
| 20 GSYM | 22 FNAME | 24 FUN | 26 STSYM |
|
||||
| 28 LCSYM | 2A MAIN | 2C | 2E |
|
||||
| 30 PC | 32 NSYMS | 34 NOMAP | 36 |
|
||||
| 38 OBJ | 3A | 3C OPT | 3E |
|
||||
| 40 RSYM | 42 M2C | 44 SLINE | 46 DSLINE |
|
||||
| 48 BSLINE*| 4A DEFD | 4C | 4E |
|
||||
| 50 EHDECL*| 52 | 54 CATCH | 56 |
|
||||
| 58 | 5A | 5C | 5E |
|
||||
| 60 SSYM | 62 | 64 SO | 66 |
|
||||
| 68 | 6A | 6C | 6E |
|
||||
| 70 | 72 | 74 | 76 |
|
||||
| 78 | 7A | 7C | 7E |
|
||||
| 80 LSYM | 82 BINCL | 84 SOL | 86 |
|
||||
| 88 | 8A | 8C | 8E |
|
||||
| 90 | 92 | 94 | 96 |
|
||||
| 98 | 9A | 9C | 9E |
|
||||
| A0 PSYM | A2 EINCL | A4 ENTRY | A6 |
|
||||
| A8 | AA | AC | AE |
|
||||
| B0 | B2 | B4 | B6 |
|
||||
| B8 | BA | BC | BE |
|
||||
| C0 LBRAC | C2 EXCL | C4 SCOPE | C6 |
|
||||
| C8 | CA | CC | CE |
|
||||
| D0 | D2 | D4 | D6 |
|
||||
| D8 | DA | DC | DE |
|
||||
| E0 RBRAC | E2 BCOMM | E4 ECOMM | E6 |
|
||||
| E8 ECOML | EA | EC | EE |
|
||||
| F0 | F2 | F4 | F6 |
|
||||
| F8 | FA | FC | FE LENG |
|
||||
+-----------------------------------------------+
|
||||
* 50 EHDECL is also MOD2.
|
||||
* 48 BSLINE is also BROWS.
|
||||
*/
|
||||
17
stab.h
Normal file
17
stab.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef __GNU_STAB__
|
||||
|
||||
/* Indicate the GNU stab.h is in use. */
|
||||
|
||||
#define __GNU_STAB__
|
||||
|
||||
#define __define_stab(NAME, CODE, STRING) NAME=CODE,
|
||||
|
||||
enum __stab_debug_code
|
||||
{
|
||||
#include "stab.def"
|
||||
LAST_UNUSED_STAB_CODE
|
||||
};
|
||||
|
||||
#undef __define_stab
|
||||
|
||||
#endif /* __GNU_STAB_ */
|
||||
15
stdarg.h
Normal file
15
stdarg.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef _STDARG_H
|
||||
#define _STDARG_H
|
||||
|
||||
typedef char *va_list;
|
||||
|
||||
/* only correct for i386 */
|
||||
#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3)
|
||||
#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3)))
|
||||
#define va_end(ap)
|
||||
|
||||
/* fix a buggy dependency on GCC in libio.h */
|
||||
typedef va_list __gnuc_va_list;
|
||||
#define _VA_LIST_DEFINED
|
||||
|
||||
#endif
|
||||
10
stdbool.h
Normal file
10
stdbool.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef _STDBOOL_H
|
||||
#define _STDBOOL_H
|
||||
|
||||
/* ISOC99 boolean */
|
||||
|
||||
#define bool _Bool
|
||||
#define true 1
|
||||
#define false 0
|
||||
|
||||
#endif /* _STDBOOL_H */
|
||||
21
stddef.h
Normal file
21
stddef.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _STDDEF_H
|
||||
#define _STDDEF_H
|
||||
|
||||
#define NULL ((void *)0)
|
||||
typedef __SIZE_TYPE__ size_t;
|
||||
typedef __WCHAR_TYPE__ wchar_t;
|
||||
typedef __PTRDIFF_TYPE__ ptrdiff_t;
|
||||
#define offsetof(type, field) ((size_t) &((type *)0)->field)
|
||||
|
||||
/* need to do that because of glibc 2.1 bug (should have a way to test
|
||||
presence of 'long long' without __GNUC__, or TCC should define
|
||||
__GNUC__ ? */
|
||||
#ifndef __int8_t_defined
|
||||
#define __int8_t_defined
|
||||
typedef char int8_t;
|
||||
typedef short int int16_t;
|
||||
typedef int int32_t;
|
||||
typedef long long int int64_t;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1128
tcc-doc.texi
Normal file
1128
tcc-doc.texi
Normal file
File diff suppressed because it is too large
Load Diff
825
tccasm.c
Normal file
825
tccasm.c
Normal file
@@ -0,0 +1,825 @@
|
||||
/*
|
||||
* GAS like assembler for TCC
|
||||
*
|
||||
* Copyright (c) 2001, 2002 Fabrice Bellard
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
static int asm_get_local_label_name(TCCState *s1, unsigned int n)
|
||||
{
|
||||
char buf[64];
|
||||
TokenSym *ts;
|
||||
|
||||
snprintf(buf, sizeof(buf), "L..%u", n);
|
||||
ts = tok_alloc(buf, strlen(buf));
|
||||
return ts->tok;
|
||||
}
|
||||
|
||||
/* We do not use the C expression parser to handle symbols. Maybe the
|
||||
C expression parser could be tweaked to do so. */
|
||||
|
||||
static void asm_expr_unary(TCCState *s1, ExprValue *pe)
|
||||
{
|
||||
Sym *sym;
|
||||
int op, n, label;
|
||||
const char *p;
|
||||
|
||||
switch(tok) {
|
||||
case TOK_PPNUM:
|
||||
p = tokc.cstr->data;
|
||||
n = strtoul(p, (char **)&p, 0);
|
||||
if (*p == 'b' || *p == 'f') {
|
||||
/* backward or forward label */
|
||||
label = asm_get_local_label_name(s1, n);
|
||||
sym = label_find(label);
|
||||
if (*p == 'b') {
|
||||
/* backward : find the last corresponding defined label */
|
||||
if (sym && sym->r == 0)
|
||||
sym = sym->prev_tok;
|
||||
if (!sym)
|
||||
error("local label '%d' not found backward", n);
|
||||
} else {
|
||||
/* forward */
|
||||
if (!sym || sym->r) {
|
||||
/* if the last label is defined, then define a new one */
|
||||
sym = label_push(&s1->asm_labels, label, 0);
|
||||
sym->type.t = VT_STATIC | VT_VOID;
|
||||
}
|
||||
}
|
||||
pe->v = 0;
|
||||
pe->sym = sym;
|
||||
} else if (*p == '\0') {
|
||||
pe->v = n;
|
||||
pe->sym = NULL;
|
||||
} else {
|
||||
error("invalid number syntax");
|
||||
}
|
||||
next();
|
||||
break;
|
||||
case '+':
|
||||
next();
|
||||
asm_expr_unary(s1, pe);
|
||||
break;
|
||||
case '-':
|
||||
case '~':
|
||||
op = tok;
|
||||
next();
|
||||
asm_expr_unary(s1, pe);
|
||||
if (pe->sym)
|
||||
error("invalid operation with label");
|
||||
if (op == '-')
|
||||
pe->v = -pe->v;
|
||||
else
|
||||
pe->v = ~pe->v;
|
||||
break;
|
||||
case TOK_CCHAR:
|
||||
case TOK_LCHAR:
|
||||
pe->v = tokc.i;
|
||||
pe->sym = NULL;
|
||||
next();
|
||||
break;
|
||||
default:
|
||||
if (tok >= TOK_IDENT) {
|
||||
/* label case : if the label was not found, add one */
|
||||
sym = label_find(tok);
|
||||
if (!sym) {
|
||||
sym = label_push(&s1->asm_labels, tok, 0);
|
||||
/* NOTE: by default, the symbol is global */
|
||||
sym->type.t = VT_VOID;
|
||||
}
|
||||
pe->v = 0;
|
||||
pe->sym = sym;
|
||||
next();
|
||||
} else {
|
||||
error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void asm_expr_prod(TCCState *s1, ExprValue *pe)
|
||||
{
|
||||
int op;
|
||||
ExprValue e2;
|
||||
|
||||
asm_expr_unary(s1, pe);
|
||||
for(;;) {
|
||||
op = tok;
|
||||
if (op != '*' && op != '/' && op != '%' &&
|
||||
op != TOK_SHL && op != TOK_SAR)
|
||||
break;
|
||||
next();
|
||||
asm_expr_unary(s1, &e2);
|
||||
if (pe->sym || e2.sym)
|
||||
error("invalid operation with label");
|
||||
switch(op) {
|
||||
case '*':
|
||||
pe->v *= e2.v;
|
||||
break;
|
||||
case '/':
|
||||
if (e2.v == 0) {
|
||||
div_error:
|
||||
error("division by zero");
|
||||
}
|
||||
pe->v /= e2.v;
|
||||
break;
|
||||
case '%':
|
||||
if (e2.v == 0)
|
||||
goto div_error;
|
||||
pe->v %= e2.v;
|
||||
break;
|
||||
case TOK_SHL:
|
||||
pe->v <<= e2.v;
|
||||
break;
|
||||
default:
|
||||
case TOK_SAR:
|
||||
pe->v >>= e2.v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void asm_expr_logic(TCCState *s1, ExprValue *pe)
|
||||
{
|
||||
int op;
|
||||
ExprValue e2;
|
||||
|
||||
asm_expr_prod(s1, pe);
|
||||
for(;;) {
|
||||
op = tok;
|
||||
if (op != '&' && op != '|' && op != '^')
|
||||
break;
|
||||
next();
|
||||
asm_expr_prod(s1, &e2);
|
||||
if (pe->sym || e2.sym)
|
||||
error("invalid operation with label");
|
||||
switch(op) {
|
||||
case '&':
|
||||
pe->v &= e2.v;
|
||||
break;
|
||||
case '|':
|
||||
pe->v |= e2.v;
|
||||
break;
|
||||
default:
|
||||
case '^':
|
||||
pe->v ^= e2.v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
|
||||
{
|
||||
int op;
|
||||
ExprValue e2;
|
||||
|
||||
asm_expr_logic(s1, pe);
|
||||
for(;;) {
|
||||
op = tok;
|
||||
if (op != '+' && op != '-')
|
||||
break;
|
||||
next();
|
||||
asm_expr_logic(s1, &e2);
|
||||
if (op == '+') {
|
||||
if (pe->sym != NULL && e2.sym != NULL)
|
||||
goto cannot_relocate;
|
||||
pe->v += e2.v;
|
||||
if (pe->sym == NULL && e2.sym != NULL)
|
||||
pe->sym = e2.sym;
|
||||
} else {
|
||||
pe->v -= e2.v;
|
||||
/* NOTE: we are less powerful than gas in that case
|
||||
because we store only one symbol in the expression */
|
||||
if (!pe->sym && !e2.sym) {
|
||||
/* OK */
|
||||
} else if (pe->sym && !e2.sym) {
|
||||
/* OK */
|
||||
} else if (pe->sym && e2.sym) {
|
||||
if (pe->sym == e2.sym) {
|
||||
/* OK */
|
||||
} else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
|
||||
/* we also accept defined symbols in the same section */
|
||||
pe->v += (long)pe->sym->next - (long)e2.sym->next;
|
||||
} else {
|
||||
goto cannot_relocate;
|
||||
}
|
||||
pe->sym = NULL; /* same symbols can be substracted to NULL */
|
||||
} else {
|
||||
cannot_relocate:
|
||||
error("invalid operation with label");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void asm_expr(TCCState *s1, ExprValue *pe)
|
||||
{
|
||||
asm_expr_sum(s1, pe);
|
||||
}
|
||||
|
||||
static int asm_int_expr(TCCState *s1)
|
||||
{
|
||||
ExprValue e;
|
||||
asm_expr(s1, &e);
|
||||
if (e.sym)
|
||||
expect("constant");
|
||||
return e.v;
|
||||
}
|
||||
|
||||
/* NOTE: the same name space as C labels is used to avoid using too
|
||||
much memory when storing labels in TokenStrings */
|
||||
static void asm_new_label(TCCState *s1, int label, int is_local)
|
||||
{
|
||||
Sym *sym;
|
||||
|
||||
sym = label_find(label);
|
||||
if (sym) {
|
||||
if (sym->r) {
|
||||
/* the label is already defined */
|
||||
if (!is_local) {
|
||||
error("assembler label '%s' already defined",
|
||||
get_tok_str(label, NULL));
|
||||
} else {
|
||||
/* redefinition of local labels is possible */
|
||||
goto new_label;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
new_label:
|
||||
sym = label_push(&s1->asm_labels, label, 0);
|
||||
sym->type.t = VT_STATIC | VT_VOID;
|
||||
}
|
||||
sym->r = cur_text_section->sh_num;
|
||||
sym->next = (void *)ind;
|
||||
}
|
||||
|
||||
static void asm_free_labels(TCCState *st)
|
||||
{
|
||||
Sym *s, *s1;
|
||||
for(s = st->asm_labels; s != NULL; s = s1) {
|
||||
s1 = s->prev;
|
||||
/* define symbol value in object file */
|
||||
if (s->r) {
|
||||
put_extern_sym(s, st->sections[s->r], (long)s->next, 0);
|
||||
}
|
||||
/* remove label */
|
||||
table_ident[s->v - TOK_IDENT]->sym_label = NULL;
|
||||
tcc_free(s);
|
||||
}
|
||||
st->asm_labels = NULL;
|
||||
}
|
||||
|
||||
static void use_section(TCCState *s1, const char *name)
|
||||
{
|
||||
Section *sec;
|
||||
sec = find_section(s1, name);
|
||||
cur_text_section->data_offset = ind;
|
||||
cur_text_section = sec;
|
||||
ind = cur_text_section->data_offset;
|
||||
}
|
||||
|
||||
static void asm_parse_directive(TCCState *s1)
|
||||
{
|
||||
int n, offset, v, size, tok1;
|
||||
Section *sec;
|
||||
uint8_t *ptr;
|
||||
|
||||
/* assembler directive */
|
||||
next();
|
||||
sec = cur_text_section;
|
||||
switch(tok) {
|
||||
case TOK_ASM_align:
|
||||
case TOK_ASM_skip:
|
||||
case TOK_ASM_space:
|
||||
tok1 = tok;
|
||||
next();
|
||||
n = asm_int_expr(s1);
|
||||
if (tok1 == TOK_ASM_align) {
|
||||
if (n < 0 || (n & (n-1)) != 0)
|
||||
error("alignment must be a positive power of two");
|
||||
offset = (ind + n - 1) & -n;
|
||||
size = offset - ind;
|
||||
} else {
|
||||
size = n;
|
||||
}
|
||||
v = 0;
|
||||
if (tok == ',') {
|
||||
next();
|
||||
v = asm_int_expr(s1);
|
||||
}
|
||||
if (sec->sh_type != SHT_NOBITS) {
|
||||
sec->data_offset = ind;
|
||||
ptr = section_ptr_add(sec, size);
|
||||
memset(ptr, v, size);
|
||||
}
|
||||
ind += size;
|
||||
break;
|
||||
case TOK_ASM_byte:
|
||||
size = 1;
|
||||
goto asm_data;
|
||||
case TOK_ASM_word:
|
||||
case TOK_SHORT:
|
||||
size = 2;
|
||||
goto asm_data;
|
||||
case TOK_LONG:
|
||||
case TOK_INT:
|
||||
size = 4;
|
||||
asm_data:
|
||||
next();
|
||||
for(;;) {
|
||||
ExprValue e;
|
||||
asm_expr(s1, &e);
|
||||
if (sec->sh_type != SHT_NOBITS) {
|
||||
if (size == 4) {
|
||||
gen_expr32(&e);
|
||||
} else {
|
||||
if (e.sym)
|
||||
expect("constant");
|
||||
if (size == 1)
|
||||
g(e.v);
|
||||
else
|
||||
gen_le16(e.v);
|
||||
}
|
||||
} else {
|
||||
ind += size;
|
||||
}
|
||||
if (tok != ',')
|
||||
break;
|
||||
next();
|
||||
}
|
||||
break;
|
||||
case TOK_ASM_globl:
|
||||
{
|
||||
Sym *sym;
|
||||
|
||||
next();
|
||||
sym = label_find(tok);
|
||||
if (!sym) {
|
||||
sym = label_push(&s1->asm_labels, tok, 0);
|
||||
sym->type.t = VT_VOID;
|
||||
}
|
||||
sym->type.t &= ~VT_STATIC;
|
||||
next();
|
||||
}
|
||||
break;
|
||||
case TOK_ASM_string:
|
||||
{
|
||||
const uint8_t *p;
|
||||
int i;
|
||||
|
||||
next();
|
||||
if (tok != TOK_STR)
|
||||
expect("string constant");
|
||||
p = tokc.cstr->data;
|
||||
for(i = 0; i < tokc.cstr->size; i++)
|
||||
g(p[i]);
|
||||
next();
|
||||
}
|
||||
break;
|
||||
case TOK_ASM_text:
|
||||
case TOK_ASM_data:
|
||||
case TOK_ASM_bss:
|
||||
{
|
||||
char sname[64];
|
||||
tok1 = tok;
|
||||
n = 0;
|
||||
next();
|
||||
if (tok != ';' && tok != TOK_LINEFEED) {
|
||||
n = asm_int_expr(s1);
|
||||
next();
|
||||
}
|
||||
sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
|
||||
use_section(s1, sname);
|
||||
}
|
||||
break;
|
||||
case TOK_SECTION1:
|
||||
{
|
||||
char sname[256];
|
||||
|
||||
/* XXX: support more options */
|
||||
next();
|
||||
sname[0] = '\0';
|
||||
while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
|
||||
if (tok == TOK_STR)
|
||||
pstrcat(sname, sizeof(sname), tokc.cstr->data);
|
||||
else
|
||||
pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
|
||||
next();
|
||||
}
|
||||
use_section(s1, sname);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* assemble a file */
|
||||
static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
|
||||
{
|
||||
int opcode;
|
||||
|
||||
#if 0
|
||||
/* print stats about opcodes */
|
||||
{
|
||||
const ASMInstr *pa;
|
||||
int freq[4];
|
||||
int op_vals[500];
|
||||
int nb_op_vals, i, j;
|
||||
|
||||
nb_op_vals = 0;
|
||||
memset(freq, 0, sizeof(freq));
|
||||
for(pa = asm_instrs; pa->sym != 0; pa++) {
|
||||
freq[pa->nb_ops]++;
|
||||
for(i=0;i<pa->nb_ops;i++) {
|
||||
for(j=0;j<nb_op_vals;j++) {
|
||||
if (pa->op_type[i] == op_vals[j])
|
||||
goto found;
|
||||
}
|
||||
op_vals[nb_op_vals++] = pa->op_type[i];
|
||||
found: ;
|
||||
}
|
||||
}
|
||||
for(i=0;i<nb_op_vals;i++) {
|
||||
int v = op_vals[i];
|
||||
if ((v & (v - 1)) != 0)
|
||||
printf("%3d: %08x\n", i, v);
|
||||
}
|
||||
printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
|
||||
sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
|
||||
freq[0], freq[1], freq[2], freq[3]);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* XXX: undefine C labels */
|
||||
|
||||
ch = file->buf_ptr[0];
|
||||
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
|
||||
parse_flags = 0;
|
||||
if (do_preprocess)
|
||||
parse_flags |= PARSE_FLAG_PREPROCESS;
|
||||
next();
|
||||
for(;;) {
|
||||
if (tok == TOK_EOF)
|
||||
break;
|
||||
parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
|
||||
redo:
|
||||
if (tok == '#') {
|
||||
/* horrible gas comment */
|
||||
while (tok != TOK_LINEFEED)
|
||||
next();
|
||||
} else if (tok == '.') {
|
||||
asm_parse_directive(s1);
|
||||
} else if (tok == TOK_PPNUM) {
|
||||
const char *p;
|
||||
int n;
|
||||
p = tokc.cstr->data;
|
||||
n = strtoul(p, (char **)&p, 10);
|
||||
if (*p != '\0')
|
||||
expect("':'");
|
||||
/* new local label */
|
||||
asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
|
||||
next();
|
||||
skip(':');
|
||||
goto redo;
|
||||
} else if (tok >= TOK_IDENT) {
|
||||
/* instruction or label */
|
||||
opcode = tok;
|
||||
next();
|
||||
if (tok == ':') {
|
||||
/* new label */
|
||||
asm_new_label(s1, opcode, 0);
|
||||
next();
|
||||
goto redo;
|
||||
} else {
|
||||
asm_opcode(s1, opcode);
|
||||
}
|
||||
}
|
||||
/* end of line */
|
||||
if (tok != ';' && tok != TOK_LINEFEED){
|
||||
expect("end of line");
|
||||
}
|
||||
parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
|
||||
next();
|
||||
}
|
||||
|
||||
asm_free_labels(s1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Assemble the current file */
|
||||
static int tcc_assemble(TCCState *s1, int do_preprocess)
|
||||
{
|
||||
int ret;
|
||||
|
||||
preprocess_init(s1);
|
||||
|
||||
/* default section is text */
|
||||
cur_text_section = text_section;
|
||||
ind = cur_text_section->data_offset;
|
||||
|
||||
ret = tcc_assemble_internal(s1, do_preprocess);
|
||||
|
||||
cur_text_section->data_offset = ind;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/********************************************************************/
|
||||
/* GCC inline asm support */
|
||||
|
||||
/* assemble the string 'str' in the current C compilation unit without
|
||||
C preprocessing. NOTE: str is modified by modifying the '\0' at the
|
||||
end */
|
||||
static void tcc_assemble_inline(TCCState *s1, char *str, int len)
|
||||
{
|
||||
BufferedFile *bf, *saved_file;
|
||||
int saved_parse_flags, *saved_macro_ptr;
|
||||
|
||||
bf = tcc_malloc(sizeof(BufferedFile));
|
||||
memset(bf, 0, sizeof(BufferedFile));
|
||||
bf->fd = -1;
|
||||
bf->buf_ptr = str;
|
||||
bf->buf_end = str + len;
|
||||
str[len] = CH_EOB;
|
||||
/* same name as current file so that errors are correctly
|
||||
reported */
|
||||
pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
|
||||
bf->line_num = file->line_num;
|
||||
saved_file = file;
|
||||
file = bf;
|
||||
saved_parse_flags = parse_flags;
|
||||
saved_macro_ptr = macro_ptr;
|
||||
macro_ptr = NULL;
|
||||
|
||||
tcc_assemble_internal(s1, 0);
|
||||
|
||||
parse_flags = saved_parse_flags;
|
||||
macro_ptr = saved_macro_ptr;
|
||||
file = saved_file;
|
||||
tcc_free(bf);
|
||||
}
|
||||
|
||||
/* find a constraint by its number or id (gcc 3 extended
|
||||
syntax). return -1 if not found. Return in *pp in char after the
|
||||
constraint */
|
||||
static int find_constraint(ASMOperand *operands, int nb_operands,
|
||||
const char *name, const char **pp)
|
||||
{
|
||||
int index;
|
||||
TokenSym *ts;
|
||||
const char *p;
|
||||
|
||||
if (isnum(*name)) {
|
||||
index = 0;
|
||||
while (isnum(*name)) {
|
||||
index = (index * 10) + (*name) - '0';
|
||||
name++;
|
||||
}
|
||||
if ((unsigned)index >= nb_operands)
|
||||
index = -1;
|
||||
} else if (*name == '[') {
|
||||
name++;
|
||||
p = strchr(name, ']');
|
||||
if (p) {
|
||||
ts = tok_alloc(name, p - name);
|
||||
for(index = 0; index < nb_operands; index++) {
|
||||
if (operands[index].id == ts->tok)
|
||||
goto found;
|
||||
}
|
||||
index = -1;
|
||||
found:
|
||||
name = p + 1;
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
} else {
|
||||
index = -1;
|
||||
}
|
||||
if (pp)
|
||||
*pp = name;
|
||||
return index;
|
||||
}
|
||||
|
||||
static void subst_asm_operands(ASMOperand *operands, int nb_operands,
|
||||
int nb_outputs,
|
||||
CString *out_str, CString *in_str)
|
||||
{
|
||||
int c, index, modifier;
|
||||
const char *str;
|
||||
ASMOperand *op;
|
||||
SValue sv;
|
||||
|
||||
cstr_new(out_str);
|
||||
str = in_str->data;
|
||||
for(;;) {
|
||||
c = *str++;
|
||||
if (c == '%') {
|
||||
if (*str == '%') {
|
||||
str++;
|
||||
goto add_char;
|
||||
}
|
||||
modifier = 0;
|
||||
if (*str == 'c' || *str == 'n' ||
|
||||
*str == 'b' || *str == 'w' || *str == 'h')
|
||||
modifier = *str++;
|
||||
index = find_constraint(operands, nb_operands, str, &str);
|
||||
if (index < 0)
|
||||
error("invalid operand reference after %%");
|
||||
op = &operands[index];
|
||||
sv = *op->vt;
|
||||
if (op->reg >= 0) {
|
||||
sv.r = op->reg;
|
||||
if ((op->vt->r & VT_VALMASK) == VT_LLOCAL)
|
||||
sv.r |= VT_LVAL;
|
||||
}
|
||||
subst_asm_operand(out_str, &sv, modifier);
|
||||
} else {
|
||||
add_char:
|
||||
cstr_ccat(out_str, c);
|
||||
if (c == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
|
||||
int is_output)
|
||||
{
|
||||
ASMOperand *op;
|
||||
int nb_operands;
|
||||
|
||||
if (tok != ':') {
|
||||
nb_operands = *nb_operands_ptr;
|
||||
for(;;) {
|
||||
if (nb_operands >= MAX_ASM_OPERANDS)
|
||||
error("too many asm operands");
|
||||
op = &operands[nb_operands++];
|
||||
op->id = 0;
|
||||
if (tok == '[') {
|
||||
next();
|
||||
if (tok < TOK_IDENT)
|
||||
expect("identifier");
|
||||
op->id = tok;
|
||||
next();
|
||||
skip(']');
|
||||
}
|
||||
if (tok != TOK_STR)
|
||||
expect("string constant");
|
||||
op->constraint = tcc_malloc(tokc.cstr->size);
|
||||
strcpy(op->constraint, tokc.cstr->data);
|
||||
next();
|
||||
skip('(');
|
||||
gexpr();
|
||||
if (is_output) {
|
||||
test_lvalue();
|
||||
} else {
|
||||
/* we want to avoid LLOCAL case. note that it may come
|
||||
from register storage, so we need to convert (reg)
|
||||
case */
|
||||
if ((vtop->r & VT_LVAL) &&
|
||||
((vtop->r & VT_VALMASK) == VT_LLOCAL ||
|
||||
(vtop->r & VT_VALMASK) < VT_CONST)) {
|
||||
gv(RC_INT);
|
||||
}
|
||||
}
|
||||
op->vt = vtop;
|
||||
skip(')');
|
||||
if (tok == ',') {
|
||||
next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*nb_operands_ptr = nb_operands;
|
||||
}
|
||||
}
|
||||
|
||||
/* parse the GCC asm() instruction */
|
||||
static void asm_instr(void)
|
||||
{
|
||||
CString astr, astr1;
|
||||
ASMOperand operands[MAX_ASM_OPERANDS];
|
||||
int nb_inputs, nb_outputs, nb_operands, i;
|
||||
uint8_t input_regs_allocated[NB_ASM_REGS];
|
||||
uint8_t output_regs_allocated[NB_ASM_REGS];
|
||||
uint8_t clobber_regs[NB_ASM_REGS];
|
||||
|
||||
next();
|
||||
/* since we always generate the asm() instruction, we can ignore
|
||||
volatile */
|
||||
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
|
||||
next();
|
||||
}
|
||||
skip('(');
|
||||
/* read the string */
|
||||
if (tok != TOK_STR)
|
||||
expect("string constant");
|
||||
cstr_new(&astr);
|
||||
while (tok == TOK_STR) {
|
||||
/* XXX: add \0 handling too ? */
|
||||
cstr_cat(&astr, tokc.cstr->data);
|
||||
next();
|
||||
}
|
||||
cstr_ccat(&astr, '\0');
|
||||
nb_operands = 0;
|
||||
nb_outputs = 0;
|
||||
memset(clobber_regs, 0, sizeof(clobber_regs));
|
||||
if (tok == ':') {
|
||||
next();
|
||||
/* output args */
|
||||
parse_asm_operands(operands, &nb_operands, 1);
|
||||
nb_outputs = nb_operands;
|
||||
if (tok == ':') {
|
||||
next();
|
||||
/* input args */
|
||||
parse_asm_operands(operands, &nb_operands, 0);
|
||||
if (tok == ':') {
|
||||
/* clobber list */
|
||||
/* XXX: handle registers */
|
||||
next();
|
||||
for(;;) {
|
||||
if (tok != TOK_STR)
|
||||
expect("string constant");
|
||||
asm_clobber(clobber_regs, tokc.cstr->data);
|
||||
next();
|
||||
if (tok == ',') {
|
||||
next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
skip(')');
|
||||
/* NOTE: we do not eat the ';' so that we can restore the current
|
||||
token after the assembler parsing */
|
||||
if (tok != ';')
|
||||
expect("';'");
|
||||
nb_inputs = nb_operands - nb_outputs;
|
||||
|
||||
/* save all values in the memory */
|
||||
save_regs(0);
|
||||
|
||||
/* compute constraints */
|
||||
asm_compute_constraints(input_regs_allocated,
|
||||
operands, nb_operands, nb_outputs, 0,
|
||||
NULL);
|
||||
asm_compute_constraints(output_regs_allocated,
|
||||
operands, nb_operands, nb_outputs, 1,
|
||||
input_regs_allocated);
|
||||
|
||||
/* substitute the operands in the asm string. No substitution is
|
||||
done if no operands (GCC behaviour) */
|
||||
#ifdef ASM_DEBUG
|
||||
printf("asm: \"%s\"\n", (char *)astr.data);
|
||||
#endif
|
||||
if (nb_operands > 0) {
|
||||
subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
|
||||
cstr_free(&astr);
|
||||
} else {
|
||||
astr1 = astr;
|
||||
}
|
||||
#ifdef ASM_DEBUG
|
||||
printf("subst_asm: \"%s\"\n", (char *)astr1.data);
|
||||
#endif
|
||||
|
||||
/* generate loads */
|
||||
asm_gen_code(operands, nb_operands, nb_outputs, 0, clobber_regs);
|
||||
|
||||
/* assemble the string with tcc internal assembler */
|
||||
tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
|
||||
|
||||
/* restore the current C token */
|
||||
next();
|
||||
|
||||
/* store the output values if needed */
|
||||
asm_gen_code(operands, nb_operands, nb_outputs, 1, clobber_regs);
|
||||
|
||||
/* free everything */
|
||||
for(i=0;i<nb_operands;i++) {
|
||||
ASMOperand *op;
|
||||
op = &operands[i];
|
||||
tcc_free(op->constraint);
|
||||
vpop();
|
||||
}
|
||||
cstr_free(&astr1);
|
||||
}
|
||||
|
||||
77
tcclib.h
Normal file
77
tcclib.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* Simple libc header for TCC
|
||||
*
|
||||
* Add any function you want from the libc there. This file is here
|
||||
* only for your convenience so that you do not need to put the whole
|
||||
* glibc include files on your floppy disk
|
||||
*/
|
||||
#ifndef _TCCLIB_H
|
||||
#define _TCCLIB_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* stdlib.h */
|
||||
void *calloc(size_t nmemb, size_t size);
|
||||
void *malloc(size_t size);
|
||||
void free(void *ptr);
|
||||
void *realloc(void *ptr, size_t size);
|
||||
int atoi(const char *nptr);
|
||||
long int strtol(const char *nptr, char **endptr, int base);
|
||||
unsigned long int strtoul(const char *nptr, char **endptr, int base);
|
||||
|
||||
/* stdio.h */
|
||||
typedef struct __FILE FILE;
|
||||
#define EOF (-1)
|
||||
extern FILE *stdin;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stderr;
|
||||
FILE *fopen(const char *path, const char *mode);
|
||||
FILE *fdopen(int fildes, const char *mode);
|
||||
FILE *freopen(const char *path, const char *mode, FILE *stream);
|
||||
int fclose(FILE *stream);
|
||||
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
|
||||
int fgetc(FILE *stream);
|
||||
char *fgets(char *s, int size, FILE *stream);
|
||||
int getc(FILE *stream);
|
||||
int getchar(void);
|
||||
char *gets(char *s);
|
||||
int ungetc(int c, FILE *stream);
|
||||
int fflush(FILE *stream);
|
||||
|
||||
int printf(const char *format, ...);
|
||||
int fprintf(FILE *stream, const char *format, ...);
|
||||
int sprintf(char *str, const char *format, ...);
|
||||
int snprintf(char *str, size_t size, const char *format, ...);
|
||||
int asprintf(char **strp, const char *format, ...);
|
||||
int dprintf(int fd, const char *format, ...);
|
||||
int vprintf(const char *format, va_list ap);
|
||||
int vfprintf(FILE *stream, const char *format, va_list ap);
|
||||
int vsprintf(char *str, const char *format, va_list ap);
|
||||
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
|
||||
int vasprintf(char **strp, const char *format, va_list ap);
|
||||
int vdprintf(int fd, const char *format, va_list ap);
|
||||
|
||||
void perror(const char *s);
|
||||
|
||||
/* string.h */
|
||||
char *strcat(char *dest, const char *src);
|
||||
char *strchr(const char *s, int c);
|
||||
char *strrchr(const char *s, int c);
|
||||
char *strcpy(char *dest, const char *src);
|
||||
void *memcpy(void *dest, const void *src, size_t n);
|
||||
void *memmove(void *dest, const void *src, size_t n);
|
||||
void *memset(void *s, int c, size_t n);
|
||||
char *strdup(const char *s);
|
||||
|
||||
/* dlfcn.h */
|
||||
#define RTLD_LAZY 0x001
|
||||
#define RTLD_NOW 0x002
|
||||
#define RTLD_GLOBAL 0x100
|
||||
|
||||
void *dlopen(const char *filename, int flag);
|
||||
const char *dlerror(void);
|
||||
void *dlsym(void *handle, char *symbol);
|
||||
int dlclose(void *handle);
|
||||
|
||||
#endif /* _TCCLIB_H */
|
||||
375
tcctok.h
Normal file
375
tcctok.h
Normal file
@@ -0,0 +1,375 @@
|
||||
/* keywords */
|
||||
DEF(TOK_INT, "int")
|
||||
DEF(TOK_VOID, "void")
|
||||
DEF(TOK_CHAR, "char")
|
||||
DEF(TOK_IF, "if")
|
||||
DEF(TOK_ELSE, "else")
|
||||
DEF(TOK_WHILE, "while")
|
||||
DEF(TOK_BREAK, "break")
|
||||
DEF(TOK_RETURN, "return")
|
||||
DEF(TOK_FOR, "for")
|
||||
DEF(TOK_EXTERN, "extern")
|
||||
DEF(TOK_STATIC, "static")
|
||||
DEF(TOK_UNSIGNED, "unsigned")
|
||||
DEF(TOK_GOTO, "goto")
|
||||
DEF(TOK_DO, "do")
|
||||
DEF(TOK_CONTINUE, "continue")
|
||||
DEF(TOK_SWITCH, "switch")
|
||||
DEF(TOK_CASE, "case")
|
||||
|
||||
DEF(TOK_CONST1, "const")
|
||||
DEF(TOK_CONST2, "__const") /* gcc keyword */
|
||||
DEF(TOK_CONST3, "__const__") /* gcc keyword */
|
||||
DEF(TOK_VOLATILE1, "volatile")
|
||||
DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
|
||||
DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
|
||||
DEF(TOK_LONG, "long")
|
||||
DEF(TOK_REGISTER, "register")
|
||||
DEF(TOK_SIGNED1, "signed")
|
||||
DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
|
||||
DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
|
||||
DEF(TOK_AUTO, "auto")
|
||||
DEF(TOK_INLINE1, "inline")
|
||||
DEF(TOK_INLINE2, "__inline") /* gcc keyword */
|
||||
DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
|
||||
DEF(TOK_RESTRICT1, "restrict")
|
||||
DEF(TOK_RESTRICT2, "__restrict")
|
||||
DEF(TOK_RESTRICT3, "__restrict__")
|
||||
DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
|
||||
|
||||
DEF(TOK_FLOAT, "float")
|
||||
DEF(TOK_DOUBLE, "double")
|
||||
DEF(TOK_BOOL, "_Bool")
|
||||
DEF(TOK_SHORT, "short")
|
||||
DEF(TOK_STRUCT, "struct")
|
||||
DEF(TOK_UNION, "union")
|
||||
DEF(TOK_TYPEDEF, "typedef")
|
||||
DEF(TOK_DEFAULT, "default")
|
||||
DEF(TOK_ENUM, "enum")
|
||||
DEF(TOK_SIZEOF, "sizeof")
|
||||
DEF(TOK_ATTRIBUTE1, "__attribute")
|
||||
DEF(TOK_ATTRIBUTE2, "__attribute__")
|
||||
DEF(TOK_ALIGNOF1, "__alignof")
|
||||
DEF(TOK_ALIGNOF2, "__alignof__")
|
||||
DEF(TOK_TYPEOF1, "typeof")
|
||||
DEF(TOK_TYPEOF2, "__typeof")
|
||||
DEF(TOK_TYPEOF3, "__typeof__")
|
||||
DEF(TOK_LABEL, "__label__")
|
||||
DEF(TOK_ASM1, "asm")
|
||||
DEF(TOK_ASM2, "__asm")
|
||||
DEF(TOK_ASM3, "__asm__")
|
||||
|
||||
/*********************************************************************/
|
||||
/* the following are not keywords. They are included to ease parsing */
|
||||
/* preprocessor only */
|
||||
DEF(TOK_DEFINE, "define")
|
||||
DEF(TOK_INCLUDE, "include")
|
||||
DEF(TOK_IFDEF, "ifdef")
|
||||
DEF(TOK_IFNDEF, "ifndef")
|
||||
DEF(TOK_ELIF, "elif")
|
||||
DEF(TOK_ENDIF, "endif")
|
||||
DEF(TOK_DEFINED, "defined")
|
||||
DEF(TOK_UNDEF, "undef")
|
||||
DEF(TOK_ERROR, "error")
|
||||
DEF(TOK_WARNING, "warning")
|
||||
DEF(TOK_LINE, "line")
|
||||
DEF(TOK_PRAGMA, "pragma")
|
||||
DEF(TOK___LINE__, "__LINE__")
|
||||
DEF(TOK___FILE__, "__FILE__")
|
||||
DEF(TOK___DATE__, "__DATE__")
|
||||
DEF(TOK___TIME__, "__TIME__")
|
||||
DEF(TOK___FUNCTION__, "__FUNCTION__")
|
||||
DEF(TOK___VA_ARGS__, "__VA_ARGS__")
|
||||
|
||||
/* special identifiers */
|
||||
DEF(TOK___FUNC__, "__func__")
|
||||
|
||||
/* attribute identifiers */
|
||||
/* XXX: handle all tokens generically since speed is not critical */
|
||||
DEF(TOK_SECTION1, "section")
|
||||
DEF(TOK_SECTION2, "__section__")
|
||||
DEF(TOK_ALIGNED1, "aligned")
|
||||
DEF(TOK_ALIGNED2, "__aligned__")
|
||||
DEF(TOK_UNUSED1, "unused")
|
||||
DEF(TOK_UNUSED2, "__unused__")
|
||||
DEF(TOK_CDECL1, "cdecl")
|
||||
DEF(TOK_CDECL2, "__cdecl")
|
||||
DEF(TOK_CDECL3, "__cdecl__")
|
||||
DEF(TOK_STDCALL1, "stdcall")
|
||||
DEF(TOK_STDCALL2, "__stdcall")
|
||||
DEF(TOK_STDCALL3, "__stdcall__")
|
||||
DEF(TOK_NORETURN1, "noreturn")
|
||||
DEF(TOK_NORETURN2, "__noreturn__")
|
||||
DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
|
||||
DEF(TOK_builtin_constant_p, "__builtin_constant_p")
|
||||
|
||||
/* builtin functions or variables */
|
||||
DEF(TOK_memcpy, "memcpy")
|
||||
DEF(TOK_memset, "memset")
|
||||
DEF(TOK_alloca, "alloca")
|
||||
DEF(TOK___divdi3, "__divdi3")
|
||||
DEF(TOK___moddi3, "__moddi3")
|
||||
DEF(TOK___udivdi3, "__udivdi3")
|
||||
DEF(TOK___umoddi3, "__umoddi3")
|
||||
DEF(TOK___sardi3, "__sardi3")
|
||||
DEF(TOK___shrdi3, "__shrdi3")
|
||||
DEF(TOK___shldi3, "__shldi3")
|
||||
DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control")
|
||||
DEF(TOK___tcc_fpu_control, "__tcc_fpu_control")
|
||||
DEF(TOK___ulltof, "__ulltof")
|
||||
DEF(TOK___ulltod, "__ulltod")
|
||||
DEF(TOK___ulltold, "__ulltold")
|
||||
DEF(TOK___fixunssfdi, "__fixunssfdi")
|
||||
DEF(TOK___fixunsdfdi, "__fixunsdfdi")
|
||||
DEF(TOK___fixunsxfdi, "__fixunsxfdi")
|
||||
|
||||
/* bound checking symbols */
|
||||
#ifdef CONFIG_TCC_BCHECK
|
||||
DEF(TOK___bound_ptr_add, "__bound_ptr_add")
|
||||
DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
|
||||
DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
|
||||
DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
|
||||
DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
|
||||
DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
|
||||
DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
|
||||
DEF(TOK___bound_local_new, "__bound_local_new")
|
||||
DEF(TOK___bound_local_delete, "__bound_local_delete")
|
||||
DEF(TOK_malloc, "malloc")
|
||||
DEF(TOK_free, "free")
|
||||
DEF(TOK_realloc, "realloc")
|
||||
DEF(TOK_memalign, "memalign")
|
||||
DEF(TOK_calloc, "calloc")
|
||||
DEF(TOK_memmove, "memmove")
|
||||
DEF(TOK_strlen, "strlen")
|
||||
DEF(TOK_strcpy, "strcpy")
|
||||
#endif
|
||||
|
||||
/* Tiny Assembler */
|
||||
|
||||
DEF_ASM(byte)
|
||||
DEF_ASM(align)
|
||||
DEF_ASM(skip)
|
||||
DEF_ASM(space)
|
||||
DEF_ASM(string)
|
||||
DEF_ASM(globl)
|
||||
DEF_ASM(text)
|
||||
DEF_ASM(data)
|
||||
DEF_ASM(bss)
|
||||
|
||||
#ifdef TCC_TARGET_I386
|
||||
|
||||
/* WARNING: relative order of tokens is important. */
|
||||
DEF_ASM(al)
|
||||
DEF_ASM(cl)
|
||||
DEF_ASM(dl)
|
||||
DEF_ASM(bl)
|
||||
DEF_ASM(ah)
|
||||
DEF_ASM(ch)
|
||||
DEF_ASM(dh)
|
||||
DEF_ASM(bh)
|
||||
DEF_ASM(ax)
|
||||
DEF_ASM(cx)
|
||||
DEF_ASM(dx)
|
||||
DEF_ASM(bx)
|
||||
DEF_ASM(sp)
|
||||
DEF_ASM(bp)
|
||||
DEF_ASM(si)
|
||||
DEF_ASM(di)
|
||||
DEF_ASM(eax)
|
||||
DEF_ASM(ecx)
|
||||
DEF_ASM(edx)
|
||||
DEF_ASM(ebx)
|
||||
DEF_ASM(esp)
|
||||
DEF_ASM(ebp)
|
||||
DEF_ASM(esi)
|
||||
DEF_ASM(edi)
|
||||
DEF_ASM(mm0)
|
||||
DEF_ASM(mm1)
|
||||
DEF_ASM(mm2)
|
||||
DEF_ASM(mm3)
|
||||
DEF_ASM(mm4)
|
||||
DEF_ASM(mm5)
|
||||
DEF_ASM(mm6)
|
||||
DEF_ASM(mm7)
|
||||
DEF_ASM(xmm0)
|
||||
DEF_ASM(xmm1)
|
||||
DEF_ASM(xmm2)
|
||||
DEF_ASM(xmm3)
|
||||
DEF_ASM(xmm4)
|
||||
DEF_ASM(xmm5)
|
||||
DEF_ASM(xmm6)
|
||||
DEF_ASM(xmm7)
|
||||
DEF_ASM(cr0)
|
||||
DEF_ASM(cr1)
|
||||
DEF_ASM(cr2)
|
||||
DEF_ASM(cr3)
|
||||
DEF_ASM(cr4)
|
||||
DEF_ASM(cr5)
|
||||
DEF_ASM(cr6)
|
||||
DEF_ASM(cr7)
|
||||
DEF_ASM(tr0)
|
||||
DEF_ASM(tr1)
|
||||
DEF_ASM(tr2)
|
||||
DEF_ASM(tr3)
|
||||
DEF_ASM(tr4)
|
||||
DEF_ASM(tr5)
|
||||
DEF_ASM(tr6)
|
||||
DEF_ASM(tr7)
|
||||
DEF_ASM(db0)
|
||||
DEF_ASM(db1)
|
||||
DEF_ASM(db2)
|
||||
DEF_ASM(db3)
|
||||
DEF_ASM(db4)
|
||||
DEF_ASM(db5)
|
||||
DEF_ASM(db6)
|
||||
DEF_ASM(db7)
|
||||
DEF_ASM(dr0)
|
||||
DEF_ASM(dr1)
|
||||
DEF_ASM(dr2)
|
||||
DEF_ASM(dr3)
|
||||
DEF_ASM(dr4)
|
||||
DEF_ASM(dr5)
|
||||
DEF_ASM(dr6)
|
||||
DEF_ASM(dr7)
|
||||
DEF_ASM(es)
|
||||
DEF_ASM(cs)
|
||||
DEF_ASM(ss)
|
||||
DEF_ASM(ds)
|
||||
DEF_ASM(fs)
|
||||
DEF_ASM(gs)
|
||||
DEF_ASM(st)
|
||||
|
||||
DEF_BWL(mov)
|
||||
|
||||
/* generic two operands */
|
||||
DEF_BWL(add)
|
||||
DEF_BWL(or)
|
||||
DEF_BWL(adc)
|
||||
DEF_BWL(sbb)
|
||||
DEF_BWL(and)
|
||||
DEF_BWL(sub)
|
||||
DEF_BWL(xor)
|
||||
DEF_BWL(cmp)
|
||||
|
||||
/* unary ops */
|
||||
DEF_BWL(inc)
|
||||
DEF_BWL(dec)
|
||||
DEF_BWL(not)
|
||||
DEF_BWL(neg)
|
||||
DEF_BWL(mul)
|
||||
DEF_BWL(imul)
|
||||
DEF_BWL(div)
|
||||
DEF_BWL(idiv)
|
||||
|
||||
DEF_BWL(xchg)
|
||||
DEF_BWL(test)
|
||||
|
||||
/* shifts */
|
||||
DEF_BWL(rol)
|
||||
DEF_BWL(ror)
|
||||
DEF_BWL(rcl)
|
||||
DEF_BWL(rcr)
|
||||
DEF_BWL(shl)
|
||||
DEF_BWL(shr)
|
||||
DEF_BWL(sar)
|
||||
|
||||
DEF_ASM(shldw)
|
||||
DEF_ASM(shldl)
|
||||
DEF_ASM(shld)
|
||||
DEF_ASM(shrdw)
|
||||
DEF_ASM(shrdl)
|
||||
DEF_ASM(shrd)
|
||||
|
||||
DEF_ASM(pushw)
|
||||
DEF_ASM(pushl)
|
||||
DEF_ASM(push)
|
||||
DEF_ASM(popw)
|
||||
DEF_ASM(popl)
|
||||
DEF_ASM(pop)
|
||||
DEF_BWL(in)
|
||||
DEF_BWL(out)
|
||||
|
||||
DEF_WL(movzb)
|
||||
|
||||
DEF_ASM(movzwl)
|
||||
DEF_ASM(movsbw)
|
||||
DEF_ASM(movsbl)
|
||||
DEF_ASM(movswl)
|
||||
|
||||
DEF_WL(lea)
|
||||
|
||||
DEF_ASM(les)
|
||||
DEF_ASM(lds)
|
||||
DEF_ASM(lss)
|
||||
DEF_ASM(lfs)
|
||||
DEF_ASM(lgs)
|
||||
|
||||
DEF_ASM(call)
|
||||
DEF_ASM(jmp)
|
||||
DEF_ASM(lcall)
|
||||
DEF_ASM(ljmp)
|
||||
|
||||
DEF_ASMTEST(j)
|
||||
|
||||
DEF_ASMTEST(set)
|
||||
DEF_ASMTEST(cmov)
|
||||
|
||||
DEF_WL(bsf)
|
||||
DEF_WL(bsr)
|
||||
DEF_WL(bt)
|
||||
DEF_WL(bts)
|
||||
DEF_WL(btr)
|
||||
DEF_WL(btc)
|
||||
|
||||
DEF_WL(lsl)
|
||||
|
||||
/* generic FP ops */
|
||||
DEF_FP(add)
|
||||
DEF_FP(mul)
|
||||
|
||||
DEF_ASM(fcom)
|
||||
DEF_ASM(fcom_1) /* non existant op, just to have a regular table */
|
||||
DEF_FP1(com)
|
||||
|
||||
DEF_FP(comp)
|
||||
DEF_FP(sub)
|
||||
DEF_FP(subr)
|
||||
DEF_FP(div)
|
||||
DEF_FP(divr)
|
||||
|
||||
DEF_BWL(xadd)
|
||||
DEF_BWL(cmpxchg)
|
||||
|
||||
/* string ops */
|
||||
DEF_BWL(cmps)
|
||||
DEF_BWL(scmp)
|
||||
DEF_BWL(ins)
|
||||
DEF_BWL(outs)
|
||||
DEF_BWL(lods)
|
||||
DEF_BWL(slod)
|
||||
DEF_BWL(movs)
|
||||
DEF_BWL(smov)
|
||||
DEF_BWL(scas)
|
||||
DEF_BWL(ssca)
|
||||
DEF_BWL(stos)
|
||||
DEF_BWL(ssto)
|
||||
|
||||
/* generic asm ops */
|
||||
|
||||
#define ALT(x)
|
||||
#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
|
||||
#define DEF_ASM_OP0L(name, opcode, group, instr_type)
|
||||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
|
||||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
|
||||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
|
||||
#include "i386-asm.h"
|
||||
|
||||
#define ALT(x)
|
||||
#define DEF_ASM_OP0(name, opcode)
|
||||
#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
|
||||
#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
|
||||
#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
|
||||
#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
|
||||
#include "i386-asm.h"
|
||||
|
||||
#endif
|
||||
427
texi2pod.pl
Executable file
427
texi2pod.pl
Executable file
@@ -0,0 +1,427 @@
|
||||
#! /usr/bin/perl -w
|
||||
|
||||
# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
|
||||
|
||||
# This file is part of GNU CC.
|
||||
|
||||
# GNU CC is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
|
||||
# GNU CC is distributed in the hope that it will be 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 GNU CC; see the file COPYING. If not, write to
|
||||
# the Free Software Foundation, 59 Temple Place - Suite 330,
|
||||
# Boston MA 02111-1307, USA.
|
||||
|
||||
# This does trivial (and I mean _trivial_) conversion of Texinfo
|
||||
# markup to Perl POD format. It's intended to be used to extract
|
||||
# something suitable for a manpage from a Texinfo document.
|
||||
|
||||
$output = 0;
|
||||
$skipping = 0;
|
||||
%sects = ();
|
||||
$section = "";
|
||||
@icstack = ();
|
||||
@endwstack = ();
|
||||
@skstack = ();
|
||||
@instack = ();
|
||||
$shift = "";
|
||||
%defs = ();
|
||||
$fnno = 1;
|
||||
$inf = "";
|
||||
$ibase = "";
|
||||
|
||||
while ($_ = shift) {
|
||||
if (/^-D(.*)$/) {
|
||||
if ($1 ne "") {
|
||||
$flag = $1;
|
||||
} else {
|
||||
$flag = shift;
|
||||
}
|
||||
$value = "";
|
||||
($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
|
||||
die "no flag specified for -D\n"
|
||||
unless $flag ne "";
|
||||
die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
|
||||
unless $flag =~ /^[a-zA-Z0-9_-]+$/;
|
||||
$defs{$flag} = $value;
|
||||
} elsif (/^-/) {
|
||||
usage();
|
||||
} else {
|
||||
$in = $_, next unless defined $in;
|
||||
$out = $_, next unless defined $out;
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $in) {
|
||||
$inf = gensym();
|
||||
open($inf, "<$in") or die "opening \"$in\": $!\n";
|
||||
$ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
|
||||
} else {
|
||||
$inf = \*STDIN;
|
||||
}
|
||||
|
||||
if (defined $out) {
|
||||
open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
|
||||
}
|
||||
|
||||
while(defined $inf) {
|
||||
while(<$inf>) {
|
||||
# Certain commands are discarded without further processing.
|
||||
/^\@(?:
|
||||
[a-z]+index # @*index: useful only in complete manual
|
||||
|need # @need: useful only in printed manual
|
||||
|(?:end\s+)?group # @group .. @end group: ditto
|
||||
|page # @page: ditto
|
||||
|node # @node: useful only in .info file
|
||||
|(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents
|
||||
)\b/x and next;
|
||||
|
||||
chomp;
|
||||
|
||||
# Look for filename and title markers.
|
||||
/^\@setfilename\s+([^.]+)/ and $fn = $1, next;
|
||||
/^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
|
||||
|
||||
# Identify a man title but keep only the one we are interested in.
|
||||
/^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
|
||||
if (exists $defs{$1}) {
|
||||
$fn = $1;
|
||||
$tl = postprocess($2);
|
||||
}
|
||||
next;
|
||||
};
|
||||
|
||||
# Look for blocks surrounded by @c man begin SECTION ... @c man end.
|
||||
# This really oughta be @ifman ... @end ifman and the like, but such
|
||||
# would require rev'ing all other Texinfo translators.
|
||||
/^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
|
||||
$output = 1 if exists $defs{$2};
|
||||
$sect = $1;
|
||||
next;
|
||||
};
|
||||
/^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
|
||||
/^\@c\s+man\s+end/ and do {
|
||||
$sects{$sect} = "" unless exists $sects{$sect};
|
||||
$sects{$sect} .= postprocess($section);
|
||||
$section = "";
|
||||
$output = 0;
|
||||
next;
|
||||
};
|
||||
|
||||
# handle variables
|
||||
/^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
|
||||
$defs{$1} = $2;
|
||||
next;
|
||||
};
|
||||
/^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
|
||||
delete $defs{$1};
|
||||
next;
|
||||
};
|
||||
|
||||
next unless $output;
|
||||
|
||||
# Discard comments. (Can't do it above, because then we'd never see
|
||||
# @c man lines.)
|
||||
/^\@c\b/ and next;
|
||||
|
||||
# End-block handler goes up here because it needs to operate even
|
||||
# if we are skipping.
|
||||
/^\@end\s+([a-z]+)/ and do {
|
||||
# Ignore @end foo, where foo is not an operation which may
|
||||
# cause us to skip, if we are presently skipping.
|
||||
my $ended = $1;
|
||||
next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/;
|
||||
|
||||
die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
|
||||
die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
|
||||
|
||||
$endw = pop @endwstack;
|
||||
|
||||
if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
|
||||
$skipping = pop @skstack;
|
||||
next;
|
||||
} elsif ($ended =~ /^(?:example|smallexample|display)$/) {
|
||||
$shift = "";
|
||||
$_ = ""; # need a paragraph break
|
||||
} elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
|
||||
$_ = "\n=back\n";
|
||||
$ic = pop @icstack;
|
||||
} else {
|
||||
die "unknown command \@end $ended at line $.\n";
|
||||
}
|
||||
};
|
||||
|
||||
# We must handle commands which can cause skipping even while we
|
||||
# are skipping, otherwise we will not process nested conditionals
|
||||
# correctly.
|
||||
/^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
|
||||
push @endwstack, $endw;
|
||||
push @skstack, $skipping;
|
||||
$endw = "ifset";
|
||||
$skipping = 1 unless exists $defs{$1};
|
||||
next;
|
||||
};
|
||||
|
||||
/^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
|
||||
push @endwstack, $endw;
|
||||
push @skstack, $skipping;
|
||||
$endw = "ifclear";
|
||||
$skipping = 1 if exists $defs{$1};
|
||||
next;
|
||||
};
|
||||
|
||||
/^\@(ignore|menu|iftex)\b/ and do {
|
||||
push @endwstack, $endw;
|
||||
push @skstack, $skipping;
|
||||
$endw = $1;
|
||||
$skipping = 1;
|
||||
next;
|
||||
};
|
||||
|
||||
next if $skipping;
|
||||
|
||||
# Character entities. First the ones that can be replaced by raw text
|
||||
# or discarded outright:
|
||||
s/\@copyright\{\}/(c)/g;
|
||||
s/\@dots\{\}/.../g;
|
||||
s/\@enddots\{\}/..../g;
|
||||
s/\@([.!? ])/$1/g;
|
||||
s/\@[:-]//g;
|
||||
s/\@bullet(?:\{\})?/*/g;
|
||||
s/\@TeX\{\}/TeX/g;
|
||||
s/\@pounds\{\}/\#/g;
|
||||
s/\@minus(?:\{\})?/-/g;
|
||||
s/\\,/,/g;
|
||||
|
||||
# Now the ones that have to be replaced by special escapes
|
||||
# (which will be turned back into text by unmunge())
|
||||
s/&/&/g;
|
||||
s/\@\{/{/g;
|
||||
s/\@\}/}/g;
|
||||
s/\@\@/&at;/g;
|
||||
|
||||
# Inside a verbatim block, handle @var specially.
|
||||
if ($shift ne "") {
|
||||
s/\@var\{([^\}]*)\}/<$1>/g;
|
||||
}
|
||||
|
||||
# POD doesn't interpret E<> inside a verbatim block.
|
||||
if ($shift eq "") {
|
||||
s/</</g;
|
||||
s/>/>/g;
|
||||
} else {
|
||||
s/</</g;
|
||||
s/>/>/g;
|
||||
}
|
||||
|
||||
# Single line command handlers.
|
||||
|
||||
/^\@include\s+(.+)$/ and do {
|
||||
push @instack, $inf;
|
||||
$inf = gensym();
|
||||
|
||||
# Try cwd and $ibase.
|
||||
open($inf, "<" . $1)
|
||||
or open($inf, "<" . $ibase . "/" . $1)
|
||||
or die "cannot open $1 or $ibase/$1: $!\n";
|
||||
next;
|
||||
};
|
||||
|
||||
/^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
|
||||
and $_ = "\n=head2 $1\n";
|
||||
/^\@subsection\s+(.+)$/
|
||||
and $_ = "\n=head3 $1\n";
|
||||
|
||||
# Block command handlers:
|
||||
/^\@itemize\s+(\@[a-z]+|\*|-)/ and do {
|
||||
push @endwstack, $endw;
|
||||
push @icstack, $ic;
|
||||
$ic = $1;
|
||||
$_ = "\n=over 4\n";
|
||||
$endw = "itemize";
|
||||
};
|
||||
|
||||
/^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
|
||||
push @endwstack, $endw;
|
||||
push @icstack, $ic;
|
||||
if (defined $1) {
|
||||
$ic = $1 . ".";
|
||||
} else {
|
||||
$ic = "1.";
|
||||
}
|
||||
$_ = "\n=over 4\n";
|
||||
$endw = "enumerate";
|
||||
};
|
||||
|
||||
/^\@([fv]?table)\s+(\@[a-z]+)/ and do {
|
||||
push @endwstack, $endw;
|
||||
push @icstack, $ic;
|
||||
$endw = $1;
|
||||
$ic = $2;
|
||||
$ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/;
|
||||
$ic =~ s/\@(?:code|kbd)/C/;
|
||||
$ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
|
||||
$ic =~ s/\@(?:file)/F/;
|
||||
$_ = "\n=over 4\n";
|
||||
};
|
||||
|
||||
/^\@((?:small)?example|display)/ and do {
|
||||
push @endwstack, $endw;
|
||||
$endw = $1;
|
||||
$shift = "\t";
|
||||
$_ = ""; # need a paragraph break
|
||||
};
|
||||
|
||||
/^\@itemx?\s*(.+)?$/ and do {
|
||||
if (defined $1) {
|
||||
# Entity escapes prevent munging by the <> processing below.
|
||||
$_ = "\n=item $ic\<$1\>\n";
|
||||
} else {
|
||||
$_ = "\n=item $ic\n";
|
||||
$ic =~ y/A-Ya-y/B-Zb-z/;
|
||||
$ic =~ s/(\d+)/$1 + 1/eg;
|
||||
}
|
||||
};
|
||||
|
||||
$section .= $shift.$_."\n";
|
||||
}
|
||||
# End of current file.
|
||||
close($inf);
|
||||
$inf = pop @instack;
|
||||
}
|
||||
|
||||
die "No filename or title\n" unless defined $fn && defined $tl;
|
||||
|
||||
$sects{NAME} = "$fn \- $tl\n";
|
||||
$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
|
||||
|
||||
for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
|
||||
BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
|
||||
if(exists $sects{$sect}) {
|
||||
$head = $sect;
|
||||
$head =~ s/SEEALSO/SEE ALSO/;
|
||||
print "=head1 $head\n\n";
|
||||
print scalar unmunge ($sects{$sect});
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub usage
|
||||
{
|
||||
die "usage: $0 [-D toggle...] [infile [outfile]]\n";
|
||||
}
|
||||
|
||||
sub postprocess
|
||||
{
|
||||
local $_ = $_[0];
|
||||
|
||||
# @value{foo} is replaced by whatever 'foo' is defined as.
|
||||
while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
|
||||
if (! exists $defs{$2}) {
|
||||
print STDERR "Option $2 not defined\n";
|
||||
s/\Q$1\E//;
|
||||
} else {
|
||||
$value = $defs{$2};
|
||||
s/\Q$1\E/$value/;
|
||||
}
|
||||
}
|
||||
|
||||
# Formatting commands.
|
||||
# Temporary escape for @r.
|
||||
s/\@r\{([^\}]*)\}/R<$1>/g;
|
||||
s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
|
||||
s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
|
||||
s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
|
||||
s/\@sc\{([^\}]*)\}/\U$1/g;
|
||||
s/\@file\{([^\}]*)\}/F<$1>/g;
|
||||
s/\@w\{([^\}]*)\}/S<$1>/g;
|
||||
s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
|
||||
|
||||
# Cross references are thrown away, as are @noindent and @refill.
|
||||
# (@noindent is impossible in .pod, and @refill is unnecessary.)
|
||||
# @* is also impossible in .pod; we discard it and any newline that
|
||||
# follows it. Similarly, our macro @gol must be discarded.
|
||||
|
||||
s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
|
||||
s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
|
||||
s/;\s+\@pxref\{(?:[^\}]*)\}//g;
|
||||
s/\@noindent\s*//g;
|
||||
s/\@refill//g;
|
||||
s/\@gol//g;
|
||||
s/\@\*\s*\n?//g;
|
||||
|
||||
# @uref can take one, two, or three arguments, with different
|
||||
# semantics each time. @url and @email are just like @uref with
|
||||
# one argument, for our purposes.
|
||||
s/\@(?:uref|url|email)\{([^\},]*)\}/<B<$1>>/g;
|
||||
s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
|
||||
s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
|
||||
|
||||
# Turn B<blah I<blah> blah> into B<blah> I<blah> B<blah> to
|
||||
# match Texinfo semantics of @emph inside @samp. Also handle @r
|
||||
# inside bold.
|
||||
s/</</g;
|
||||
s/>/>/g;
|
||||
1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B</g;
|
||||
1 while (s/B<([^<>]*)I<([^>]+)>/B<$1>I<$2>B</g);
|
||||
1 while (s/I<([^<>]*)B<([^>]+)>/I<$1>B<$2>I</g);
|
||||
s/[BI]<>//g;
|
||||
s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
|
||||
s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
|
||||
|
||||
# Extract footnotes. This has to be done after all other
|
||||
# processing because otherwise the regexp will choke on formatting
|
||||
# inside @footnote.
|
||||
while (/\@footnote/g) {
|
||||
s/\@footnote\{([^\}]+)\}/[$fnno]/;
|
||||
add_footnote($1, $fnno);
|
||||
$fnno++;
|
||||
}
|
||||
|
||||
return $_;
|
||||
}
|
||||
|
||||
sub unmunge
|
||||
{
|
||||
# Replace escaped symbols with their equivalents.
|
||||
local $_ = $_[0];
|
||||
|
||||
s/</E<lt>/g;
|
||||
s/>/E<gt>/g;
|
||||
s/{/\{/g;
|
||||
s/}/\}/g;
|
||||
s/&at;/\@/g;
|
||||
s/&/&/g;
|
||||
return $_;
|
||||
}
|
||||
|
||||
sub add_footnote
|
||||
{
|
||||
unless (exists $sects{FOOTNOTES}) {
|
||||
$sects{FOOTNOTES} = "\n=over 4\n\n";
|
||||
}
|
||||
|
||||
$sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
|
||||
$sects{FOOTNOTES} .= $_[0];
|
||||
$sects{FOOTNOTES} .= "\n\n";
|
||||
}
|
||||
|
||||
# stolen from Symbol.pm
|
||||
{
|
||||
my $genseq = 0;
|
||||
sub gensym
|
||||
{
|
||||
my $name = "GEN" . $genseq++;
|
||||
my $ref = \*{$name};
|
||||
delete $::{$name};
|
||||
return $ref;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user