RELEASE LuaJIT-2.0.0-beta1

This commit is contained in:
Mike Pall 2009-12-08 19:46:35 +01:00
commit 55b1695971
122 changed files with 42143 additions and 0 deletions

11
.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
*.[oa]
*.so
*.obj
*.lib
*.exp
*.dll
*.exe
*.manifest
*.dmp
*.swp
.tags

84
Makefile Normal file
View File

@ -0,0 +1,84 @@
##############################################################################
# LuaJIT top level Makefile for installation. Requires GNU Make.
#
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
# Note: src/Makefile has many more configurable options.
#
# ##### This Makefile is NOT useful for installation on Windows! #####
# For MSVC, please follow the instructions given in src/msvcbuild.bat.
# For MinGW and Cygwin, cd to src and run make with the Makefile there.
# NYI: add wininstall.bat
#
# Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
##############################################################################
BASEVER= 2.0.0
VERSION= 2.0.0-beta1
##############################################################################
#
# Change the installation path as needed and modify src/luaconf.h accordingly.
# Note: PREFIX must be an absolute path!
#
PREFIX= /usr/local
##############################################################################
INSTALL_BIN= $(PREFIX)/bin
INSTALL_NAME= luajit-$(VERSION)
INSTALL_T= $(INSTALL_BIN)/$(INSTALL_NAME)
INSTALL_TSYM= $(INSTALL_BIN)/luajit
INSTALL_INC= $(PREFIX)/include/luajit-$(BASEVER)
INSTALL_JITLIB= $(PREFIX)/share/luajit-$(VERSION)/jit
MKDIR= mkdir -p
SYMLINK= ln -f -s
INSTALL_X= install -m 0755
INSTALL_F= install -m 0644
FILES_T= luajit
FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h
FILES_JITLIB= bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua vmdef.lua
##############################################################################
INSTALL_DEP= src/luajit
all $(INSTALL_DEP):
@echo "==== Building LuaJIT $(VERSION) ===="
$(MAKE) -C src
@echo "==== Successfully built LuaJIT $(VERSION) ===="
install: $(INSTALL_DEP)
@echo "==== Installing LuaJIT $(VERSION) to $(PREFIX) ===="
$(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_JITLIB)
cd src && $(INSTALL_X) $(FILES_T) $(INSTALL_T)
cd src && $(INSTALL_F) $(FILES_INC) $(INSTALL_INC)
cd lib && $(INSTALL_F) $(FILES_JITLIB) $(INSTALL_JITLIB)
@echo "==== Successfully installed LuaJIT $(VERSION) to $(PREFIX) ===="
@echo ""
@echo "Note: the beta releases deliberately do NOT install a symlink for luajit"
@echo "You can do this now by running this command (with sudo):"
@echo ""
@echo " $(SYMLINK) $(INSTALL_NAME) $(INSTALL_TSYM)"
@echo ""
##############################################################################
amalg:
@echo "Building LuaJIT $(VERSION)"
$(MAKE) -C src amalg
clean:
$(MAKE) -C src clean
cleaner:
$(MAKE) -C src cleaner
distclean:
$(MAKE) -C src distclean
SUB_TARGETS= amalg clean cleaner distclean
.PHONY: all install $(SUB_TARGETS)
##############################################################################

16
README Normal file
View File

@ -0,0 +1,16 @@
README for LuaJIT 2.0.0-beta1
-----------------------------
LuaJIT is a Just-In-Time (JIT) compiler for the Lua programming language.
Project Homepage: http://luajit.org/
LuaJIT is Copyright (C) 2005-2009 Mike Pall.
LuaJIT is free software, released under the MIT/X license.
See full Copyright Notice in src/luajit.h
Documentation for LuaJIT is available in HTML format.
Please point your favorite browser to:
doc/luajit.html

203
doc/api.html Normal file
View File

@ -0,0 +1,203 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>API Extensions</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>API Extensions</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a class="current" href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
LuaJIT is fully upwards-compatible with Lua 5.1. It supports all
<a href="http://www.lua.org/manual/5.1/manual.html#5"><span class="ext">&raquo;</span>&nbsp;standard Lua
library functions</a> and the full set of
<a href="http://www.lua.org/manual/5.1/manual.html#3"><span class="ext">&raquo;</span>&nbsp;Lua/C API
functions</a>.
</p>
<p>
LuaJIT is also fully ABI-compatible to Lua 5.1 at the linker/dynamic
loader level. This means you can compile a C&nbsp;module against the
standard Lua headers and load the same shared library from either Lua
or LuaJIT.
</p>
<h2 id="bit"><tt>bit.*</tt> &mdash; Bitwise Operations</h2>
<p>
LuaJIT supports all bitwise operations as defined by
<a href="http://bitop.luajit.org"><span class="ext">&raquo;</span>&nbsp;Lua BitOp</a>:
</p>
<pre class="code">
bit.tobit bit.tohex bit.bnot bit.band bit.bor bit.bxor
bit.lshift bit.rshift bit.arshift bit.rol bit.ror bit.bswap
</pre>
<p>
This module is a LuaJIT built-in &mdash; you don't need to download or
install Lua BitOp. The Lua BitOp site has full documentation for all
<a href="http://bitop.luajit.org/api.html"><span class="ext">&raquo;</span>&nbsp;Lua BitOp API functions</a>.
</p>
<p>
Please make sure to <tt>require</tt> the module before using any of
its functions:
</p>
<pre class="code">
local bit = require("bit")
</pre>
<p>
An already installed Lua BitOp module is ignored by LuaJIT.
This way you can use bit operations from both Lua and LuaJIT on a
shared installation.
</p>
<h2 id="jit"><tt>jit.*</tt> &mdash; JIT compiler control</h2>
<p>
The functions in this built-in module control the behavior
of the JIT compiler engine.
</p>
<h3 id="jit_onoff"><tt>jit.on()<br>
jit.off()</tt></h3>
<p>
Turns the whole JIT compiler on (default) or off.
</p>
<p>
These functions are typically used with the command line options
<tt>-j on</tt> or <tt>-j off</tt>.
</p>
<h3 id="jit_flush"><tt>jit.flush()</tt></h3>
<p>
Flushes the whole cache of compiled code.
</p>
<h3 id="jit_flush_tr"><tt>jit.flush(tr)</tt></h3>
<p>
Flushes the code for the specified root trace and all of its
side traces from the cache.
</p>
<h3 id="jit_onoff_func"><tt>jit.on(func|true [,true|false])<br>
jit.off(func|true [,true|false])<br>
jit.flush(func|true [,true|false])</tt></h3>
<p>
<tt>jit.on</tt> enables JIT compilation for a Lua function (this is
the default).
</p>
<p>
<tt>jit.off</tt> disables JIT compilation for a Lua function and
flushes any already compiled code from the code cache.
</p>
<p>
<tt>jit.flush</tt> flushes the code, but doesn't affect the
enable/disable status.
</p>
<p>
The current function, i.e. the Lua function calling this library
function, can also be specified by passing <tt>true</tt> as the first
argument.
</p>
<p>
If the second argument is <tt>true</tt>, JIT compilation is also
enabled, disabled or flushed recursively for all subfunctions of a
function. With <tt>false</tt> only the subfunctions are affected.
</p>
<p>
The <tt>jit.on</tt> and <tt>jit.off</tt> functions only set a flag
which is checked when the function is about to be compiled. They do
not trigger immediate compilation.
</p>
<p>
Typical usage is <tt>jit.off(true, true)</tt> in the main chunk
of a module to turn off JIT compilation for the whole module for
debugging purposes.
</p>
<h3 id="jit_version"><tt>jit.version</tt></h3>
<p>
Contains the LuaJIT version string.
</p>
<h3 id="jit_version_num"><tt>jit.version_num</tt></h3>
<p>
Contains the version number of the LuaJIT core. Version xx.yy.zz
is represented by the decimal number xxyyzz.
</p>
<h3 id="jit_arch"><tt>jit.arch</tt></h3>
<p>
Contains the target architecture name (CPU and optional ABI).
</p>
<h2 id="jit_opt"><tt>jit.opt.*</tt> &mdash; JIT compiler optimization control</h2>
<p>
This module provides the backend for the <tt>-O</tt> command line
option.
</p>
<p>
You can also use it programmatically, e.g.:
</p>
<pre class="code">
jit.opt.start(2) -- same as -O2
jit.opt.start("-dce")
jit.opt.start("hotloop=10", "hotexit=2")
</pre>
<p>
Unlike in LuaJIT 1.x, the module is built-in and
<b>optimization is turned on by default!</b>
It's no longer necessary to run <tt>require("jit.opt").start()</tt>,
which was one of the ways to enable optimization.
</p>
<h2 id="jit_util"><tt>jit.util.*</tt> &mdash; JIT compiler introspection</h2>
<p>
This module holds functions to introspect the bytecode, generated
traces, the IR and the generated machine code. The functionality
provided by this module is still in flux and therefore undocumented.
</p>
<p>
The debug modules <tt>-jbc</tt>, <tt>-jv</tt> and <tt>-jdump</tt> make
extensive use of these functions. Please check out their source code,
if you want to know more.
</p>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

166
doc/bluequad-print.css Normal file
View File

@ -0,0 +1,166 @@
/* Copyright (C) 2004-2009 Mike Pall.
*
* You are welcome to use the general ideas of this design for your own sites.
* But please do not steal the stylesheet, the layout or the color scheme.
*/
body {
font-family: serif;
font-size: 11pt;
margin: 0 3em;
padding: 0;
border: none;
}
a:link, a:visited, a:hover, a:active {
text-decoration: none;
background: transparent;
color: #0000ff;
}
h1, h2, h3 {
font-family: sans-serif;
font-weight: bold;
text-align: left;
margin: 0.5em 0;
padding: 0;
}
h1 {
font-size: 200%;
}
h2 {
font-size: 150%;
}
h3 {
font-size: 125%;
}
p {
margin: 0 0 0.5em 0;
padding: 0;
}
ul, ol {
margin: 0.5em 0;
padding: 0 0 0 2em;
}
ul {
list-style: outside square;
}
ol {
list-style: outside decimal;
}
li {
margin: 0;
padding: 0;
}
dl {
margin: 1em 0;
padding: 1em;
border: 1px solid black;
}
dt {
font-weight: bold;
margin: 0;
padding: 0;
}
dt sup {
float: right;
margin-left: 1em;
}
dd {
margin: 0.5em 0 0 2em;
padding: 0;
}
table {
table-layout: fixed;
width: 100%;
margin: 1em 0;
padding: 0;
border: 1px solid black;
border-spacing: 0;
border-collapse: collapse;
}
tr {
margin: 0;
padding: 0;
border: none;
}
td {
text-align: left;
margin: 0;
padding: 0.2em 0.5em;
border-top: 1px solid black;
border-bottom: 1px solid black;
}
tr.separate td {
border-top: double;
}
tt, pre, code, kbd, samp {
font-family: monospace;
font-size: 75%;
}
kbd {
font-weight: bolder;
}
blockquote, pre {
margin: 1em 2em;
padding: 0;
}
img {
border: none;
vertical-align: baseline;
margin: 0;
padding: 0;
}
img.left {
float: left;
margin: 0.5em 1em 0.5em 0;
}
img.right {
float: right;
margin: 0.5em 0 0.5em 1em;
}
.flush {
clear: both;
visibility: hidden;
}
.hide, .noprint, #nav {
display: none !important;
}
.pagebreak {
page-break-before: always;
}
#site {
text-align: right;
font-family: sans-serif;
font-weight: bold;
margin: 0 1em;
border-bottom: 1pt solid black;
}
#site a {
font-size: 1.2em;
}
#site a:link, #site a:visited {
text-decoration: none;
font-weight: bold;
background: transparent;
color: #ffffff;
}
#logo {
color: #ff8000;
}
#head {
clear: both;
margin: 0 1em;
}
#main {
line-height: 1.3;
text-align: justify;
margin: 1em;
}
#foot {
clear: both;
font-size: 80%;
text-align: center;
margin: 0 1.25em;
padding: 0.5em 0 0 0;
border-top: 1pt solid black;
page-break-before: avoid;
page-break-after: avoid;
}

303
doc/bluequad.css Normal file
View File

@ -0,0 +1,303 @@
/* Copyright (C) 2004-2009 Mike Pall.
*
* You are welcome to use the general ideas of this design for your own sites.
* But please do not steal the stylesheet, the layout or the color scheme.
*/
/* colorscheme:
*
* site | head #4162bf/white | #6078bf/#e6ecff
* ------+------ ----------------+-------------------
* nav | main #bfcfff | #e6ecff/black
*
* nav: hiback loback #c5d5ff #b9c9f9
* hiborder loborder #e6ecff #97a7d7
* link hover #2142bf #ff0000
*
* link: link visited hover #2142bf #8122bf #ff0000
*
* main: boxback boxborder #f0f4ff #bfcfff
*/
body {
font-family: Verdana, Arial, Helvetica, sans-serif;
font-size: 10pt;
margin: 0;
padding: 0;
border: none;
background: #e0e0e0;
color: #000000;
}
a:link {
text-decoration: none;
background: transparent;
color: #2142bf;
}
a:visited {
text-decoration: none;
background: transparent;
color: #8122bf;
}
a:hover, a:active {
text-decoration: underline;
background: transparent;
color: #ff0000;
}
h1, h2, h3 {
font-weight: bold;
text-align: left;
margin: 0.5em 0;
padding: 0;
background: transparent;
}
h1 {
font-size: 200%;
line-height: 3em; /* really 6em relative to body, match #site span */
margin: 0;
}
h2 {
font-size: 150%;
color: #606060;
}
h3 {
font-size: 125%;
color: #404040;
}
p {
max-width: 600px;
margin: 0 0 0.5em 0;
padding: 0;
}
b {
color: #404040;
}
ul, ol {
max-width: 600px;
margin: 0.5em 0;
padding: 0 0 0 2em;
}
ul {
list-style: outside square;
}
ol {
list-style: outside decimal;
}
li {
margin: 0;
padding: 0;
}
dl {
max-width: 600px;
margin: 1em 0;
padding: 1em;
border: 1px solid #bfcfff;
background: #f0f4ff;
}
dt {
font-weight: bold;
margin: 0;
padding: 0;
}
dt sup {
float: right;
margin-left: 1em;
color: #808080;
}
dt a:visited {
text-decoration: none;
color: #2142bf;
}
dt a:hover, dt a:active {
text-decoration: none;
color: #ff0000;
}
dd {
margin: 0.5em 0 0 2em;
padding: 0;
}
div.tablewrap { /* for IE *sigh* */
max-width: 600px;
}
table {
table-layout: fixed;
border-spacing: 0;
border-collapse: collapse;
max-width: 600px;
width: 100%;
margin: 1em 0;
padding: 0;
border: 1px solid #bfcfff;
}
tr {
margin: 0;
padding: 0;
border: none;
}
tr.odd {
background: #f0f4ff;
}
tr.separate td {
border-top: 1px solid #bfcfff;
}
td {
text-align: left;
margin: 0;
padding: 0.2em 0.5em;
border: none;
}
tt, code, kbd, samp {
font-family: Courier New, Courier, monospace;
line-height: 1.2;
font-size: 110%;
}
kbd {
font-weight: bolder;
}
blockquote, pre {
max-width: 600px;
margin: 1em 2em;
padding: 0;
}
pre {
line-height: 1.1;
}
pre.code {
line-height: 1.4;
margin: 0.5em 0 1em 0.5em;
padding: 0.5em 1em;
border: 1px solid #bfcfff;
background: #f0f4ff;
}
img {
border: none;
vertical-align: baseline;
margin: 0;
padding: 0;
}
img.left {
float: left;
margin: 0.5em 1em 0.5em 0;
}
img.right {
float: right;
margin: 0.5em 0 0.5em 1em;
}
.indent {
padding-left: 1em;
}
.flush {
clear: both;
visibility: hidden;
}
.hide, .noscreen {
display: none !important;
}
.ext {
color: #ff8000;
}
#site {
clear: both;
float: left;
width: 13em;
text-align: center;
font-weight: bold;
margin: 0;
padding: 0;
background: transparent;
color: #ffffff;
}
#site a {
font-size: 200%;
}
#site a:link, #site a:visited {
text-decoration: none;
font-weight: bold;
background: transparent;
color: #ffffff;
}
#site span {
line-height: 3em; /* really 6em relative to body, match h1 */
}
#logo {
color: #ffb380;
}
#head {
margin: 0;
padding: 0 0 0 2em;
border-left: solid 13em #4162bf;
border-right: solid 3em #6078bf;
background: #6078bf;
color: #e6ecff;
}
#nav {
clear: both;
float: left;
overflow: hidden;
text-align: left;
line-height: 1.5;
width: 13em;
padding-top: 1em;
background: transparent;
}
#nav ul {
list-style: none outside;
margin: 0;
padding: 0;
}
#nav li {
margin: 0;
padding: 0;
}
#nav a {
display: block;
text-decoration: none;
font-weight: bold;
margin: 0;
padding: 2px 1em;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
background: transparent;
color: #2142bf;
}
#nav a:hover, #nav a:active {
text-decoration: none;
border-top: 1px solid #97a7d7;
border-bottom: 1px solid #e6ecff;
background: #b9c9f9;
color: #ff0000;
}
#nav a.current, #nav a.current:hover, #nav a.current:active {
border-top: 1px solid #e6ecff;
border-bottom: 1px solid #97a7d7;
background: #c5d5ff;
color: #2142bf;
}
#nav ul ul a {
padding: 0 1em 0 2em;
}
#main {
line-height: 1.5;
text-align: left;
margin: 0;
padding: 1em 2em;
border-left: solid 13em #bfcfff;
border-right: solid 3em #e6ecff;
background: #e6ecff;
}
#foot {
clear: both;
font-size: 80%;
text-align: center;
margin: 0;
padding: 0.5em;
background: #6078bf;
color: #ffffff;
}
#foot a:link, #foot a:visited {
text-decoration: underline;
background: transparent;
color: #ffffff;
}
#foot a:hover, #foot a:active {
text-decoration: underline;
background: transparent;
color: #bfcfff;
}

281
doc/changes.html Normal file
View File

@ -0,0 +1,281 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>LuaJIT Change History</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
<style type="text/css">
div.major { max-width: 600px; padding: 1em; margin: 1em 0 1em 0; }
</style>
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>LuaJIT Change History</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a class="current" href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
This is a list of changes between the released versions of LuaJIT.<br>
The current <span style="color: #c00000;">development version</span> is <strong>LuaJIT&nbsp;2.0.0-beta1</strong>.<br>
The current <span style="color: #0000c0;">stable version</span> is <strong>LuaJIT&nbsp;1.1.5</strong>.
</p>
<p>
Please check the
<a href="http://luajit.org/luajit_changes.html"><span class="ext">&raquo;</span>&nbsp;Online Change History</a>
to see whether newer versions are available.
</p>
<div class="major" style="background: #ffd0d0;">
<h2 id="LuaJIT-2.0.0-beta1">LuaJIT 2.0.0-beta1 &mdash; 2009-10-31</h2>
<ul>
<li>This is the first public release of LuaJIT 2.0.</li>
<li>The whole VM has been rewritten from the ground up, so there's
no point in listing differences over earlier versions.</li>
</ul>
</div>
<div class="major" style="background: #d0d0ff;">
<h2 id="LuaJIT-1.1.5">LuaJIT 1.1.5 &mdash; 2008-10-25</h2>
<ul>
<li>Merged with Lua 5.1.4. Fixes all
<a href="http://www.lua.org/bugs.html#5.1.3"><span class="ext">&raquo;</span>&nbsp;known bugs in Lua 5.1.3</a>.</li>
</ul>
<h2 id="LuaJIT-1.1.4">LuaJIT 1.1.4 &mdash; 2008-02-05</h2>
<ul>
<li>Merged with Lua 5.1.3. Fixes all
<a href="http://www.lua.org/bugs.html#5.1.2"><span class="ext">&raquo;</span>&nbsp;known bugs in Lua 5.1.2</a>.</li>
<li>Fixed possible (but unlikely) stack corruption while compiling
<tt>k^x</tt> expressions.</li>
<li>Fixed DynASM template for cmpss instruction.</li>
</ul>
<h2 id="LuaJIT-1.1.3">LuaJIT 1.1.3 &mdash; 2007-05-24</h2>
<ul>
<li>Merged with Lua 5.1.2. Fixes all
<a href="http://www.lua.org/bugs.html#5.1.1"><span class="ext">&raquo;</span>&nbsp;known bugs in Lua 5.1.1</a>.</li>
<li>Merged pending Lua 5.1.x fixes: "return -nil" bug, spurious count hook call.</li>
<li>Remove a (sometimes) wrong assertion in <tt>luaJIT_findpc()</tt>.</li>
<li>DynASM now allows labels for displacements and <tt>.aword</tt>.</li>
<li>Fix some compiler warnings for DynASM glue (internal API change).</li>
<li>Correct naming for SSSE3 (temporarily known as SSE4) in DynASM and x86 disassembler.</li>
<li>The loadable debug modules now handle redirection to stdout
(e.g. <tt>-j&nbsp;trace=-</tt>).</li>
</ul>
<h2 id="LuaJIT-1.1.2">LuaJIT 1.1.2 &mdash; 2006-06-24</h2>
<ul>
<li>Fix MSVC inline assembly: use only local variables with
<tt>lua_number2int()</tt>.</li>
<li>Fix "attempt to call a thread value" bug on Mac OS X:
make values of consts used as lightuserdata keys unique
to avoid joining by the compiler/linker.</li>
</ul>
<h2 id="LuaJIT-1.1.1">LuaJIT 1.1.1 &mdash; 2006-06-20</h2>
<ul>
<li>Merged with Lua 5.1.1. Fixes all
<a href="http://www.lua.org/bugs.html#5.1"><span class="ext">&raquo;</span>&nbsp;known bugs in Lua 5.1</a>.</li>
<li>Enforce (dynamic) linker error for EXE/DLL version mismatches.</li>
<li>Minor changes to DynASM: faster preprocessing, smaller encoding
for some immediates.</li>
</ul>
<p>
This release is in sync with Coco 1.1.1 (see the
<a href="http://coco.luajit.org/changes.html"><span class="ext">&raquo;</span>&nbsp;Coco Change History</a>).
</p>
<h2 id="LuaJIT-1.1.0">LuaJIT 1.1.0 &mdash; 2006-03-13</h2>
<ul>
<li>Merged with Lua 5.1 (final).</li>
<li>New JIT call frame setup:
<ul>
<li>The C stack is kept 16 byte aligned (faster).
Mandatory for Mac OS X on Intel, too.</li>
<li>Faster calling conventions for internal C helper functions.</li>
<li>Better instruction scheduling for function prologue, OP_CALL and
OP_RETURN.</li>
</ul></li>
<li>Miscellaneous optimizations:
<ul>
<li>Faster loads of FP constants. Remove narrow-to-wide store-to-load
forwarding stalls.</li>
<li>Use (scalar) SSE2 ops (if the CPU supports it) to speed up slot moves
and FP to integer conversions.</li>
<li>Optimized the two-argument form of <tt>OP_CONCAT</tt> (<tt>a..b</tt>).</li>
<li>Inlined <tt>OP_MOD</tt> (<tt>a%b</tt>).
With better accuracy than the C variant, too.</li>
<li>Inlined <tt>OP_POW</tt> (<tt>a^b</tt>). Unroll <tt>x^k</tt> or
use <tt>k^x = 2^(log2(k)*x)</tt> or call <tt>pow()</tt>.</li>
</ul></li>
<li>Changes in the optimizer:
<ul>
<li>Improved hinting for table keys derived from table values
(<tt>t1[t2[x]]</tt>).</li>
<li>Lookup hinting now works with arbitrary object types and
supports index chains, too.</li>
<li>Generate type hints for arithmetic and comparison operators,
OP_LEN, OP_CONCAT and OP_FORPREP.</li>
<li>Remove several hint definitions in favour of a generic COMBINE hint.</li>
<li>Complete rewrite of <tt>jit.opt_inline</tt> module
(ex <tt>jit.opt_lib</tt>).</li>
</ul></li>
<li>Use adaptive deoptimization:
<ul>
<li>If runtime verification of a contract fails, the affected
instruction is recompiled and patched on-the-fly.
Regular programs will trigger deoptimization only occasionally.</li>
<li>This avoids generating code for uncommon fallback cases
most of the time. Generated code is up to 30% smaller compared to
LuaJIT&nbsp;1.0.3.</li>
<li>Deoptimization is used for many opcodes and contracts:
<ul>
<li>OP_CALL, OP_TAILCALL: type mismatch for callable.</li>
<li>Inlined calls: closure mismatch, parameter number and type mismatches.</li>
<li>OP_GETTABLE, OP_SETTABLE: table or key type and range mismatches.</li>
<li>All arithmetic and comparison operators, OP_LEN, OP_CONCAT,
OP_FORPREP: operand type and range mismatches.</li>
</ul></li>
<li>Complete redesign of the debug and traceback info
(bytecode &harr; mcode) to support deoptimization.
Much more flexible and needs only 50% of the space.</li>
<li>The modules <tt>jit.trace</tt>, <tt>jit.dumphints</tt> and
<tt>jit.dump</tt> handle deoptimization.</li>
</ul></li>
<li>Inlined many popular library functions
(for commonly used arguments only):
<ul>
<li>Most <tt>math.*</tt> functions (the 18 most used ones)
[2x-10x faster].</li>
<li><tt>string.len</tt>, <tt>string.sub</tt> and <tt>string.char</tt>
[2x-10x faster].</li>
<li><tt>table.insert</tt>, <tt>table.remove</tt> and <tt>table.getn</tt>
[3x-5x faster].</li>
<li><tt>coroutine.yield</tt> and <tt>coroutine.resume</tt>
[3x-5x faster].</li>
<li><tt>pairs</tt>, <tt>ipairs</tt> and the corresponding iterators
[8x-15x faster].</li>
</ul></li>
<li>Changes in the core and loadable modules and the stand-alone executable:
<ul>
<li>Added <tt>jit.version</tt>, <tt>jit.version_num</tt>
and <tt>jit.arch</tt>.</li>
<li>Reorganized some internal API functions (<tt>jit.util.*mcode*</tt>).</li>
<li>The <tt>-j dump</tt> output now shows JSUB names, too.</li>
<li>New x86 disassembler module written in pure Lua. No dependency
on ndisasm anymore. Flexible API, very compact (500 lines)
and complete (x87, MMX, SSE, SSE2, SSE3, SSSE3, privileged instructions).</li>
<li><tt>luajit -v</tt> prints the LuaJIT version and copyright
on a separate line.</li>
</ul></li>
<li>Added SSE, SSE2, SSE3 and SSSE3 support to DynASM.</li>
<li>Miscellaneous doc changes. Added a section about
<a href="luajit_install.html#embedding">embedding LuaJIT</a>.</li>
</ul>
<p>
This release is in sync with Coco 1.1.0 (see the
<a href="http://coco.luajit.org/changes.html"><span class="ext">&raquo;</span>&nbsp;Coco Change History</a>).
</p>
</div>
<div class="major" style="background: #ffffd0;">
<h2 id="LuaJIT-1.0.3">LuaJIT 1.0.3 &mdash; 2005-09-08</h2>
<ul>
<li>Even more docs.</li>
<li>Unified closure checks in <tt>jit.*</tt>.</li>
<li>Fixed some range checks in <tt>jit.util.*</tt>.</li>
<li>Fixed __newindex call originating from <tt>jit_settable_str()</tt>.</li>
<li>Merged with Lua 5.1 alpha (including early bugfixes).</li>
</ul>
<p>
This is the first public release of LuaJIT.
</p>
<h2 id="LuaJIT-1.0.2">LuaJIT 1.0.2 &mdash; 2005-09-02</h2>
<ul>
<li>Add support for flushing the Valgrind translation cache <br>
(<tt>MYCFLAGS= -DUSE_VALGRIND</tt>).</li>
<li>Add support for freeing executable mcode memory to the <tt>mmap()</tt>-based
variant for POSIX systems.</li>
<li>Reorganized the C&nbsp;function signature handling in
<tt>jit.opt_lib</tt>.</li>
<li>Changed to index-based hints for inlining C&nbsp;functions.
Still no support in the backend for inlining.</li>
<li>Hardcode <tt>HEAP_CREATE_ENABLE_EXECUTE</tt> value if undefined.</li>
<li>Misc. changes to the <tt>jit.*</tt> modules.</li>
<li>Misc. changes to the Makefiles.</li>
<li>Lots of new docs.</li>
<li>Complete doc reorg.</li>
</ul>
<p>
Not released because Lua 5.1 alpha came out today.
</p>
<h2 id="LuaJIT-1.0.1">LuaJIT 1.0.1 &mdash; 2005-08-31</h2>
<ul>
<li>Missing GC step in <tt>OP_CONCAT</tt>.</li>
<li>Fix result handling for C &ndash;> JIT calls.</li>
<li>Detect CPU feature bits.</li>
<li>Encode conditional moves (<tt>fucomip</tt>) only when supported.</li>
<li>Add fallback instructions for FP compares.</li>
<li>Add support for <tt>LUA_COMPAT_VARARG</tt>. Still disabled by default.</li>
<li>MSVC needs a specific place for the <tt>CALLBACK</tt> attribute
(David Burgess).</li>
<li>Misc. doc updates.</li>
</ul>
<p>
Interim non-public release.
Special thanks to Adam D. Moss for reporting most of the bugs.
</p>
<h2 id="LuaJIT-1.0.0">LuaJIT 1.0.0 &mdash; 2005-08-29</h2>
<p>
This is the initial non-public release of LuaJIT.
</p>
</div>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

84
doc/contact.html Normal file
View File

@ -0,0 +1,84 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Contact</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>Contact</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
Please send general questions to the
<a href="http://www.lua.org/lua-l.html"><span class="ext">&raquo;</span>&nbsp;Lua mailing list</a>.
You can also send any questions you have directly to me:
</p>
<script type="text/javascript">
<!--
var xS="@-: .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZa<b>cdefghijklmnopqrstuvwxyz"
function xD(s)
{var len=s.length;var r="";for(var i=0;i<len;i++)
{var c=s.charAt(i);var n=xS.indexOf(c);if(n!=-1)
c=xS.charAt(66-n);r+=c;}
document.write("<"+"p>"+r+"<"+"/p>\n");}
//-->
</script>
<script type="text/javascript">
<!--
xD("ewYKA7vu-EIwslx7 K9A.t41C")
//--></script>
<noscript>
<p><img src="img/contact.png" alt="Contact info in image" width="170" height="13">
</p>
</noscript>
<h2>Copyright</h2>
<p>
All documentation is
Copyright &copy; 2005-2009 Mike Pall.
</p>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

141
doc/faq.html Normal file
View File

@ -0,0 +1,141 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Frequently Asked Questions (FAQ)</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
<style type="text/css">
dd { margin-left: 1.5em; }
</style>
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>Frequently Asked Questions (FAQ)</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a class="current" href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<dl>
<dt>Q: Where can I learn more about Lua and LuaJIT?</dt>
<dd>
<ul style="padding: 0;">
<li>The <a href="http://lua.org"><span class="ext">&raquo;</span>&nbsp;main Lua.org site</a> has complete
<a href="http://www.lua.org/docs.html"><span class="ext">&raquo;</span>&nbsp;documentation</a> of the language
and links to books and papers about Lua.</li>
<li>The community-managed <a href="http://lua-users.org/wiki/"><span class="ext">&raquo;</span>&nbsp;Lua Wiki</a>
has information about diverse topics.</li>
<li>The primary source of information for the latest developments surrounding
Lua is the <a href="http://www.lua.org/lua-l.html"><span class="ext">&raquo;</span>&nbsp;Lua mailing list</a>.
You can check out the <a href="http://lua-users.org/lists/lua-l/"><span class="ext">&raquo;</span>&nbsp;mailing
list archive</a> or
<a href="http://bazar2.conectiva.com.br/mailman/listinfo/lua"><span class="ext">&raquo;</span>&nbsp;subscribe</a>
to the list (you need to be subscribed before posting).<br>
This is also the place where announcements and discussions about LuaJIT
take place.</li>
</ul>
</dl>
<dl>
<dt>Q: Where can I learn more about the compiler technology used by LuaJIT?</dt>
<dd>
I'm planning to write more documentation about the internals of LuaJIT.
In the meantime, please use the following Google Scholar searches
to find relevant papers:<br>
Search for: <a href="http://scholar.google.com/scholar?q=Trace+Compiler"><span class="ext">&raquo;</span>&nbsp;Trace Compiler</a><br>
Search for: <a href="http://scholar.google.com/scholar?q=JIT+Compiler"><span class="ext">&raquo;</span>&nbsp;JIT Compiler</a><br>
Search for: <a href="http://scholar.google.com/scholar?q=Dynamic+Language+Optimizations"><span class="ext">&raquo;</span>&nbsp;Dynamic Language Optimizations</a><br>
Search for: <a href="http://scholar.google.com/scholar?q=SSA+Form"><span class="ext">&raquo;</span>&nbsp;SSA Form</a><br>
Search for: <a href="http://scholar.google.com/scholar?q=Linear+Scan+Register+Allocation"><span class="ext">&raquo;</span>&nbsp;Linear Scan Register Allocation</a><br>
And, you know, reading the source is of course the only way to enlightenment. :-)
</dd>
</dl>
<dl>
<dt>Q: Why do I get this error: "attempt to index global 'arg' (a nil value)"?<br>
Q: My vararg functions fail after switching to LuaJIT!</dt>
<dd>LuaJIT is compatible to the Lua 5.1 language standard. It doesn't
support the implicit <tt>arg</tt> parameter for old-style vararg
functions from Lua 5.0.<br>Please convert your code to the
<a href="http://www.lua.org/manual/5.1/manual.html#2.5.9"><span class="ext">&raquo;</span>&nbsp;Lua 5.1
vararg syntax</a>.</dd>
</dl>
<dl>
<dt>Q: Sometimes Ctrl-C fails to stop my Lua program. Why?</dt>
<dd>The interrupt signal handler sets a Lua debug hook. But this is
currently ignored by compiled code (this will eventually be fixed). If
your program is running in a tight loop and never falls back to the
interpreter, the debug hook never runs and can't throw the
"interrupted!" error.<br> In the meantime you have to press Ctrl-C
twice to get stop your program. That's similar to when it's stuck
running inside a C function under the Lua interpreter.</dd>
</dl>
<dl>
<dt>Q: Why doesn't my favorite power-patch for Lua apply against LuaJIT?</dt>
<dd>Because it's a completely redesigned VM and has very little code
in common with Lua anymore. Also, if the patch introduces changes to
the Lua semantics, this would need to be reflected everywhere in the
VM, from the interpreter up to all stages of the compiler.<br> Please
use only standard Lua language constructs. For many common needs you
can use source transformations or use wrapper or proxy functions.
The compiler will happily optimize away such indirections.</dd>
</dl>
<dl>
<dt>Q: Lua runs everywhere. Why doesn't LuaJIT support my CPU?</dt>
<dd>Because it's a compiler &mdash; it needs to generate native
machine code. This means the code generator must be ported to each
architecture. And the fast interpreter is written in assembler and
must be ported, too. This is quite an undertaking.<br> Currently only
x86 CPUs are supported. x64 support is in the works. Other
architectures will follow with sufficient demand and/or
sponsoring.</dd>
</dl>
<dl>
<dt>Q: When will feature X be added? When will the next version be released?</dt>
<dd>When it's ready.<br>
C'mon, it's open source &mdash; I'm doing it on my own time and you're
getting it for free. You can either contribute a patch or sponsor
the development of certain features, if they are important to you.
</dd>
</dl>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

BIN
doc/img/contact.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

216
doc/install.html Normal file
View File

@ -0,0 +1,216 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Installation</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>Installation</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a class="current" href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
LuaJIT is only distributed as a source package. This page explains
how to build and install LuaJIT with different operating systems
and C&nbsp;compilers.
</p>
<p>
For the impatient (on POSIX systems):
</p>
<pre class="code">
make &amp;&amp; sudo make install
</pre>
<p>
LuaJIT currently builds out-of-the box on all popular x86 systems
(Linux, Windows, OSX etc.). It builds and runs fine as a 32&nbsp;bit
application under x64-based systems, too.
</p>
<h2>Configuring LuaJIT</h2>
<p>
The standard configuration should work fine for most installations.
Usually there is no need to tweak the settings, except when you want to
install to a non-standard path. The following three files hold all
user-configurable settings:
</p>
<ul>
<li><tt>src/luaconf.h</tt> sets some configuration variables, in
particular the default paths for loading modules.</li>
<li><tt>Makefile</tt> has settings for installing LuaJIT (POSIX
only).</li>
<li><tt>src/Makefile</tt> has settings for compiling LuaJIT under POSIX,
MinGW and Cygwin.</li>
<li><tt>src/msvcbuild.bat</tt> has settings for compiling LuaJIT with
MSVC.</li>
</ul>
<p>
Please read the instructions given in these files, before changing
any settings.
</p>
<h2 id="posix">POSIX Systems (Linux, OSX, *BSD etc.)</h2>
<h3>Prerequisites</h3>
<p>
Depending on your distribution, you may need to install a package for
GCC (GCC 3.4 or later required), the development headers and/or a
complete SDK.
</p>
<p>
E.g. on a current Debian/Ubuntu, install <tt>libc6-dev</tt>
with the package manager. Currently LuaJIT only builds as a 32&nbsp;bit
application, so you actually need to install <tt>libc6-dev-i386</tt>
when building on an x64 OS.
</p>
<p>
Download the current source package (pick the .tar.gz), if you haven't
already done so. Move it to a directory of your choice, open a
terminal window and change to this directory. Now unpack the archive
and change to the newly created directory:
</p>
<pre class="code">
tar zxf LuaJIT-2.0.0-beta1.tar.gz
cd LuaJIT-2.0.0-beta1
</pre>
<h3>Building LuaJIT</h3>
<p>
The supplied Makefiles try to auto-detect the settings needed for your
operating system and your compiler. They need to be run with GNU Make,
which is probably the default on your system, anyway. Simply run:
</p>
<pre class="code">
make
</pre>
<h3>Installing LuaJIT</h3>
<p>
The top-level Makefile installs LuaJIT by default under
<tt>/usr/local</tt>, i.e. the executable ends up in
<tt>/usr/local/bin</tt> and so on. You need to have root privileges
to write to this path. So, assuming sudo is installed on your system,
run the following command and enter your sudo password:
</p>
<pre class="code">
sudo make install
</pre>
<p>
Otherwise specify the directory prefix as an absolute path, e.g.:
</p>
<pre class="code">
sudo make install PREFIX=/opt/lj2
</pre>
<p>
But note that the installation prefix and the prefix for the module paths
(configured in <tt>src/luaconf.h</tt>) must match.
</p>
<p style="color: #c00000;">
Note: to avoid overwriting a previous version, the beta test releases
only install the LuaJIT executable under the versioned name (i.e.
<tt>luajit-2.0.0-beta1</tt>). You probably want to create a symlink
for convenience, with a command like this:
</p>
<pre class="code" style="color: #c00000;">
sudo ln -sf luajit-2.0.0-beta1 /usr/local/bin/luajit
</pre>
<h2 id="windows">Windows Systems</h2>
<h3>Prerequisites</h3>
<p>
Either install one of the open source SDKs
(<a href="http://mingw.org/"><span class="ext">&raquo;</span>&nbsp;MinGW</a> or
<a href="http://www.cygwin.com/"><span class="ext">&raquo;</span>&nbsp;Cygwin</a>) which come with modified
versions of GCC plus the required development headers.
</p>
<p>
Or install Microsoft's Visual C++ (MSVC) &mdash; the freely downloadable
<a href="http://www.microsoft.com/Express/VC/"><span class="ext">&raquo;</span>&nbsp;Express Edition</a>
works just fine.
</p>
<p>
Next, download the source package and unpack it using an archive manager
(e.g. the Windows Explorer) to a directory of your choice.
</p>
<h3>Building with MSVC</h3>
<p>
Open a "Visual Studio .NET Command Prompt" and <tt>cd</tt> to the
directory where you've unpacked the sources. Then run this command:
</p>
<pre class="code">
cd src
msvcbuild
</pre>
<p>
Then follow the installation instructions below.
</p>
<h3>Building with MinGW or Cygwin</h3>
<p>
Open a command prompt window and make sure the MinGW or Cygwin programs
are in your path. Then <tt>cd</tt> to the directory where
you've unpacked the sources and run this command for MinGW:
</p>
<pre class="code">
cd src
mingw32-make
</pre>
<p>
Or this command for Cygwin:
</p>
<pre class="code">
cd src
make
</pre>
<p>
Then follow the installation instructions below.
</p>
<h3>Installing LuaJIT</h3>
<p>
Copy <tt>luajit.exe</tt> and <tt>lua51.dll</tt>
to a newly created directory (any location is ok). Add <tt>lua</tt>
and <tt>lua\jit</tt> directories below it and copy all Lua files
from the <tt>lib</tt> directory of the distribution to the latter directory.
</p>
<p>
There are no hardcoded
absolute path names &mdash; all modules are loaded relative to the
directory where <tt>luajit.exe</tt> is installed
(see <tt>src/luaconf.h</tt>).
</p>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

120
doc/luajit.html Normal file
View File

@ -0,0 +1,120 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>LuaJIT</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>LuaJIT</h1>
</div>
<div id="nav">
<ul><li>
<a class="current" href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
LuaJIT is a <b>Just-In-Time Compiler</b> for the Lua<sup>*</sup>
programming language.
</p>
<p>
LuaJIT is Copyright &copy; 2005-2008 Mike Pall.
LuaJIT is open source software, released under the
<a href="http://www.opensource.org/licenses/mit-license.php"><span class="ext">&raquo;</span>&nbsp;MIT/X license</a>.
</p>
<p class="indent" style="color: #606060;">
* Lua is a powerful, dynamic and light-weight programming language
designed for extending applications. Lua is also frequently used as a
general-purpose, stand-alone language. More information about
Lua can be found at: <a href="http://www.lua.org/"><span class="ext">&raquo;</span>&nbsp;http://www.lua.org/</a>
</p>
<h2>Compatibility</h2>
<p>
LuaJIT implements the full set of language features defined by Lua 5.1.
The virtual machine (VM) is <b>API- and ABI-compatible</b> to the
standard Lua interpreter and can be deployed as a drop-in replacement.
</p>
<p>
LuaJIT offers more performance, at the expense of portability. It
currently runs on all popular operating systems based on <b>x86 CPUs</b>
(Linux, Windows, OSX etc.). It will be ported to x64 CPUs and other
platforms in the future, based on user demand and sponsoring.
</p>
<h2>Overview</h2>
<p>
LuaJIT has been successfully used as a <b>scripting middleware</b> in
games, 3D modellers, numerical simulations, trading platforms and many
other specialty applications. It combines high flexibility with high
performance and an unmatched <b>low memory footprint</b>: less than
<b>120K</b> for the VM plus less than <b>80K</b> for the JIT compiler.
</p>
<p>
LuaJIT has been in continuous development since 2005. It's widely
considered to be <b>one of the fastest dynamic language
implementations</b>. It has outperfomed other dynamic languages on many
cross-language benchmarks since its first release &mdash; often by a
substantial margin. Only now, in 2009, other dynamic language VMs are
starting to catch up with the performance of LuaJIT 1.x &hellip;
</p>
<p>
2009 also marks the first release of the long-awaited <b>LuaJIT 2.0</b>.
The whole VM has been rewritten from the ground up and relentlessly
optimized for performance. It combines a high-speed interpreter,
written in assembler, with a state-of-the-art JIT compiler.
</p>
<p>
An innovative <b>trace compiler</b> is integrated with advanced,
SSA-based optimizations and a highly tuned code generation backend. This
allows a substantial reduction of the overhead associated with dynamic
language features. It's destined to break into the performance range
traditionally reserved for offline, static language compilers.
</p>
<h2>More ...</h2>
<p>
Click on the LuaJIT sub-topics in the navigation bar to learn more
about LuaJIT.
</p>
<p><p>
Click on the Logo in the upper left corner to visit
the LuaJIT project page on the web. All other links to online
resources are marked with a '<span class="ext">&raquo;</span>'.
</p>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

233
doc/running.html Normal file
View File

@ -0,0 +1,233 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Running LuaJIT</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
<style type="text/css">
table.opt {
line-height: 1.2;
}
tr.opthead td {
font-weight: bold;
}
td.flag_name {
width: 4em;
}
td.flag_level {
width: 2em;
text-align: center;
}
td.param_name {
width: 6em;
}
td.param_default {
width: 4em;
text-align: right;
}
</style>
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>Running LuaJIT</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a class="current" href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
LuaJIT has only a single stand-alone executable, called <tt>luajit</tt> on
POSIX systems or <tt>luajit.exe</tt> on Windows. It can be used to run simple
Lua statements or whole Lua applications from the command line. It has an
interactive mode, too.
</p>
<p class="indent" style="color: #c00000;">
Note: the beta test releases only install under the versioned name on
POSIX systems (to avoid overwriting a previous version). You either need
to type <tt>luajit-2.0.0-beta1</tt> to start it or create a symlink
with a command like this:
</p>
<pre class="code" style="color: #c00000;">
sudo ln -sf luajit-2.0.0-beta1 /usr/local/bin/luajit
</pre>
<p>
Unlike previous versions <b>optimization is turned on by default</b> in
LuaJIT 2.0!<br>It's no longer necessary to use <tt>luajit&nbsp;-O</tt>.
</p>
<h2 id="options">Command Line Options</h2>
<p>
The <tt>luajit</tt> stand-alone executable is just a slightly modified
version of the regular <tt>lua</tt> stand-alone executable.
It supports the same basic options, too. <tt>luajit&nbsp;-h</tt>
prints a short list of the available options. Please have a look at the
<a href="http://www.lua.org/manual/5.1/manual.html#6"><span class="ext">&raquo;</span>&nbsp;Lua manual</a>
for details.
</p>
<p>
Two additional options control the behavior of LuaJIT:
</p>
<h3 id="opt_j"><tt>-j cmd[=arg[,arg...]]</tt></h3>
<p>
This option performs a LuaJIT control command or activates one of the
loadable extension modules. The command is first looked up in the
<tt>jit.*</tt> library. If no matching function is found, a module
named <tt>jit.&lt;cmd&gt;</tt> is loaded and the <tt>start()</tt>
function of the module is called with the specified arguments (if
any). The space between <tt>-j</tt> and <tt>cmd</tt> is optional.
</p>
<p>
Here are the available LuaJIT control commands:
</p>
<ul>
<li id="j_on"><tt>-jon</tt> &mdash; Turns the JIT compiler on (default).</li>
<li id="j_off"><tt>-joff</tt> &mdash; Turns the JIT compiler off (only use the interpreter).</li>
<li id="j_flush"><tt>-jflush</tt> &mdash; Flushes the whole cache of compiled code.</li>
<li id="j_v"><tt>-jv</tt> &mdash; Shows verbose information about the progress of the JIT compiler.</li>
<li id="j_dump"><tt>-jdump</tt> &mdash; Dumps the code and structures used in various compiler stages.</li>
</ul>
<p>
The <tt>-jv</tt> and <tt>-jdump</tt> commands are extension modules
written in Lua. They are mainly used for debugging the JIT compiler
itself. For a description of their options and output format, please
read the comment block at the start of their source.
They can be found in the <tt>lib</tt> directory of the source
distribution or installed under the <tt>jit</tt> directory. By default
this is <tt>/usr/local/share/luajit-2.0.0-beta1/jit</tt> on POSIX
systems.
</p>
<h3 id="opt_O"><tt>-O[level]</tt><br>
<tt>-O[+]flag</tt> <tt>-O-flag</tt><br>
<tt>-Oparam=value</tt></h3>
<p>
This options allows fine-tuned control of the optimizations used by
the JIT compiler. This is mainly intended for debugging LuaJIT itself.
Please note that the JIT compiler is extremly fast (we are talking
about the microsecond to millisecond range). Disabling optimizations
doesn't have any visible impact on its overhead, but usually generates
code that runs slower.
</p>
<p>
The first form sets an optimization level &mdash; this enables a
specific mix of optimization flags. <tt>-O0</tt> turns off all
optimizations and higher numbers enable more optimizations. Omitting
the level (i.e. just <tt>-O</tt>) sets the default optimization level,
which is <tt>-O3</tt> in the current version.
</p>
<p>
The second form adds or removes individual optimization flags.
The third form sets a parameter for the VM or the JIT compiler
to a specific value.
</p>
<p>
You can either use this option multiple times (like <tt>-Ocse
-O-dce -Ohotloop=10</tt>) or separate several settings with a comma
(like <tt>-O+cse,-dce,hotloop=10</tt>). The settings are applied from
left to right and later settings override earlier ones. You can freely
mix the three forms, but note that setting an optimization level
overrides all earlier flags.
</p>
<p>
Here are the available flags and at what optimization levels they
are enabled:
</p>
<table class="opt">
<tr class="opthead">
<td class="flag_name">Flag</td>
<td class="flag_level">-O1</td>
<td class="flag_level">-O2</td>
<td class="flag_level">-O3</td>
<td class="flag_desc">&nbsp;</td>
</tr>
<tr class="odd separate">
<td class="flag_name">fold</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_desc">Constant Folding, Simplifications and Reassociation</td></tr>
<tr class="even">
<td class="flag_name">cse</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_desc">Common-Subexpression Elimination</td></tr>
<tr class="odd">
<td class="flag_name">dce</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_desc">Dead-Code Elimination</td></tr>
<tr class="even">
<td class="flag_name">narrow</td><td class="flag_level">&nbsp;</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_desc">Narrowing of numbers to integers</td></tr>
<tr class="odd">
<td class="flag_name">loop</td><td class="flag_level">&nbsp;</td><td class="flag_level">&bull;</td><td class="flag_level">&bull;</td><td class="flag_desc">Loop Optimizations (code hoisting)</td></tr>
<tr class="even">
<td class="flag_name">fwd</td><td class="flag_level">&nbsp;</td><td class="flag_level">&nbsp;</td><td class="flag_level">&bull;</td><td class="flag_desc">Load Forwarding (L2L) and Store Forwarding (S2L)</td></tr>
<tr class="odd">
<td class="flag_name">dse</td><td class="flag_level">&nbsp;</td><td class="flag_level">&nbsp;</td><td class="flag_level">&bull;</td><td class="flag_desc">Dead-Store Elimination</td></tr>
<tr class="even">
<td class="flag_name">fuse</td><td class="flag_level">&nbsp;</td><td class="flag_level">&nbsp;</td><td class="flag_level">&bull;</td><td class="flag_desc">Fusion of operands into instructions</td></tr>
</table>
<p>
Here are the parameters and their default settings:
</p>
<table class="opt">
<tr class="opthead">
<td class="param_name">Parameter</td>
<td class="param_default">Default</td>
<td class="param_desc">&nbsp;</td>
</tr>
<tr class="odd separate">
<td class="param_name">maxtrace</td><td class="param_default">1000</td><td class="param_desc">Max. number of traces in the cache</td></tr>
<tr class="even">
<td class="param_name">maxrecord</td><td class="param_default">2000</td><td class="param_desc">Max. number of recorded IR instructions</td></tr>
<tr class="odd">
<td class="param_name">maxirconst</td><td class="param_default">500</td><td class="param_desc">Max. number of IR constants of a trace</td></tr>
<tr class="even">
<td class="param_name">maxside</td><td class="param_default">100</td><td class="param_desc">Max. number of side traces of a root trace</td></tr>
<tr class="odd">
<td class="param_name">maxsnap</td><td class="param_default">100</td><td class="param_desc">Max. number of snapshots for a trace</td></tr>
<tr class="even separate">
<td class="param_name">hotloop</td><td class="param_default">57</td><td class="param_desc">Number of iterations to detect a hot loop</td></tr>
<tr class="odd">
<td class="param_name">hotexit</td><td class="param_default">10</td><td class="param_desc">Number of taken exits to start a side trace</td></tr>
<tr class="even">
<td class="param_name">tryside</td><td class="param_default">4</td><td class="param_desc">Number of attempts to compile a side trace</td></tr>
<tr class="odd separate">
<td class="param_name">instunroll</td><td class="param_default">4</td><td class="param_desc">Max. unroll factor for instable loops</td></tr>
<tr class="even">
<td class="param_name">loopunroll</td><td class="param_default">7</td><td class="param_desc">Max. unroll factor for loop ops in side traces</td></tr>
<tr class="odd">
<td class="param_name">callunroll</td><td class="param_default">3</td><td class="param_desc">Max. unroll factor for pseudo-recursive calls</td></tr>
<tr class="even separate">
<td class="param_name">sizemcode</td><td class="param_default">32</td><td class="param_desc">Size of each machine code area in KBytes (Windows: 64K)</td></tr>
<tr class="odd">
<td class="param_name">maxmcode</td><td class="param_default">512</td><td class="param_desc">Max. total size of all machine code areas in KBytes</td></tr>
</table>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

235
doc/status.html Normal file
View File

@ -0,0 +1,235 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Status &amp; Roadmap</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="Author" content="Mike Pall">
<meta name="Copyright" content="Copyright (C) 2005-2009, Mike Pall">
<meta name="Language" content="en">
<link rel="stylesheet" type="text/css" href="bluequad.css" media="screen">
<link rel="stylesheet" type="text/css" href="bluequad-print.css" media="print">
<style type="text/css">
ul li { padding-bottom: 0.3em; }
</style>
</head>
<body>
<div id="site">
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
</div>
<div id="head">
<h1>Status &amp; Roadmap</h1>
</div>
<div id="nav">
<ul><li>
<a href="luajit.html">LuaJIT</a>
<ul><li>
<a href="install.html">Installation</a>
</li><li>
<a href="running.html">Running</a>
</li><li>
<a href="api.html">API Extensions</a>
</li></ul>
</li><li>
<a class="current" href="status.html">Status</a>
<ul><li>
<a href="changes.html">Changes</a>
</li></ul>
</li><li>
<a href="faq.html">FAQ</a>
</li><li>
<a href="http://luajit.org/download.html">Download <span class="ext">&raquo;</span></a>
</li></ul>
</div>
<div id="main">
<p>
The <span style="color: #0000c0;">LuaJIT 1.x</span> series represents
the current <span style="color: #0000c0;">stable branch</span>. As of
this writing there have been no open bugs since about a year. So, if
you need a rock-solid VM, you are encouraged to fetch the latest
release of LuaJIT 1.x from the <a href="http://luajit.org/download.html"><span class="ext">&raquo;</span>&nbsp;Download</a>
page.
</p>
<p>
<span style="color: #c00000;">LuaJIT 2.0</span> is the currently active
<span style="color: #c00000;">development branch</span>.
It has <b>Beta Test</b> status and is still undergoing
substantial changes. It's expected to quickly mature within the next
months. You should definitely start to evaluate it for new projects
right now. But deploying it in production environments is not yet
recommended.
</p>
<h2>Current Status</h2>
<p>
This is a list of the things you should know about the LuaJIT 2.0 beta test:
</p>
<ul>
<li>
The JIT compiler can only generate code for CPUs with <b>SSE2</b> at the
moment. I.e. you need at least a P4, Core 2/i5/i7 or K8/K10 to use it. I
plan to fix this during the beta phase and add support for emitting x87
instructions to the backend.
</li>
<li>
Obviously there will be many <b>bugs</b> in a VM which has been
rewritten from the ground up. Please report your findings together with
the circumstances needed to reproduce the bug. If possible reduce the
problem down to a simple test cases.<br>
There is no formal bug tracker at the moment. The best place for
discussion is the
<a href="http://www.lua.org/lua-l.html"><span class="ext">&raquo;</span>&nbsp;Lua mailing list</a>. Of course
you may also send your bug report directly to me, especially when they
contains lengthy debug output. Please check the
<a href="contact.html">Contact</a> page for details.
</li>
<li>
The VM is complete in the sense that it <b>should</b> run all Lua code
just fine. It's considered a serious bug if the VM crashes or produces
unexpected results &mdash; please report it. There are only very few
known incompatibilities with standard Lua:
<ul>
<li>
The Lua <b>debug API</b> is missing a couple of features (call/return
hooks) and shows slightly different behavior (no per-coroutine hooks).
</li>
<li>
Most other issues you're likely to find (e.g. with the existing test
suites) are differences in the <b>implementation-defined</b> behavior.
These either have a good reason (like early tail call resolving which
may cause differences in error reporting), are arbitrary design choices
or are due to quirks in the VM. The latter cases may get fixed if a
demonstrable need is shown.
</li>
</ul>
</li>
<li>
The <b>JIT compiler</b> is not complete (yet) and falls back to the
interpreter in some cases. All of this works transparently, so unless
you use -jv, you'll probably never notice (the interpreter is quite
fast, too). Here are the known issues:
<ul>
<li>
Many known issues cause a <b>NYI</b> (not yet implemented) trace abort
message. E.g. for calls to vararg functions or many string library
functions. Reporting these is only mildly useful, except if you have good
example code that shows the problem. Obviously, reports accompanied with
a patch to fix the issue are more than welcome. But please check back
with me, before writing major improvements, to avoid duplication of
effort.
</li>
<li>
<b>Recursion</b> is not traced yet. Often no trace will be generated at
all or some unroll limit will catch it and aborts the trace.
</li>
<li>
The trace compiler currently does not back off specialization for
function call dispatch. It should really fall back to specializing on
the prototype, not the closure identity. This can lead to the so-called
"trace explosion" problem with <b>closure-heavy programming</b>. The
trace linking heuristics prevent this, but in the worst case this
means the code always falls back to the interpreter.
</li>
<li>
<b>Trace management</b> needs more tuning: better blacklisting of aborted
traces, less drastic countermeasures against trace explosion and better
heuristics in general.
</li>
<li>
Some checks are missing in the JIT-compiled code for obscure situations
with <b>open upvalues aliasing</b> one of the SSA slots later on (or
vice versa). Bonus points, if you can find a real world test case for
this.
</li>
</ul>
</li>
</ul>
<h2>Roadmap</h2>
<p>
Rather than stating exact release dates (I'm well known for making
spectacularly wrong guesses), this roadmap lists the general project
plan, sorted by priority, as well as ideas for the future:
</p>
<ul>
<li>
The main goal right now is to stabilize LuaJIT 2.0 and get it out of
beta test. <b>Correctness</b> has priority over completeness. This
implies the first stable release will certainly NOT compile every
library function call and will fall back to the interpreter from time
to time. This is perfectly ok, since it still executes all Lua code,
just not at the highest possible speed.
</li>
<li>
The next step is to get it to compile more library functions and handle
more cases where the compiler currently bails out. This doesn't mean it
will compile every corner case. It's much more important that it
performs well in a majority of use cases. Every compiler has to make
these trade-offs &mdash; <b>completeness</b> just cannot be the
overriding goal for a low-footprint, low-overhead JIT compiler.
</li>
<li>
More <b>optimizations</b> will be added in parallel to the last step on
an as-needed basis. Array-bounds-check (ABC) removal, sinking of stores
to aggregates and sinking of allocations are high on the list. Faster
handling of NEWREF and better alias analysis are desirable, too. More
complex optimizations with less pay-off, such as value-range-propagation
(VRP) will have to wait.
</li>
<li>
LuaJIT 2.0 has been designed with <b>portability</b> in mind.
Nonetheless, it compiles to native code and needs to be adapted to each
architecture. Porting the compiler backend is probably the easier task,
but a key element of its design is the fast interpreter, written in
machine-specific assembler.<br>
The code base and the internal structures are already prepared for
easier porting to 64 bit architectures. The most likely next target is a
port to <b>x64</b>, but this will have to wait until the x86 port
stabilizes. Other ports will follow &mdash; companies which are
interested in sponsoring a port to a particular architecture, please
<a href="contact.html">contact me</a>.
</li>
<li>
There are some planned <b>structural improvements</b> to the compiler,
like compressed snapshot maps or generic handling of calls to helper
methods. These are of lesser importance, unless other developments
elevate their priority.
</li>
<li>
<b>Documentation</b> about the <b>internals</b> of LuaJIT is still sorely
missing. Although the source code is included and is IMHO well
commented, many basic design decisions are in need of an explanation.
The rather un-traditional compiler architecture and the many highly
optimized data structures are a barrier for outside participation in
the development. Alas, as I've repeatedly stated, I'm better at
writing code than papers and I'm not in need of any academical merits.
Someday I will find the time for it. :-)
</li>
<li>
Producing good code for unbiased branches is a key problem for trace
compilers. This is the main cause for "trace explosion".
<b>Hyperblock scheduling</b> promises to solve this nicely at the
price of a major redesign of the compiler. This would also pave the
way for emitting predicated instructions, which is a prerequisite
for efficient <b>vectorization</b>.
</li>
<li>
Currently Lua is missing a standard library for access to <b>structured
binary data</b> and <b>arrays/buffers</b> holding low-level data types.
Allowing calls to arbitrary C functions (<b>FFI</b>) would obviate the
need to write manual bindings. A variety of extension modules is floating
around, with different scope and capabilities. Alas, none of them has been
designed with a JIT compiler in mind.
</li>
</ul>
<br class="flush">
</div>
<div id="foot">
<hr class="hide">
Copyright &copy; 2005-2009 Mike Pall
<span class="noprint">
&middot;
<a href="contact.html">Contact</a>
</span>
</div>
</body>
</html>

69
dynasm/dasm_proto.h Normal file
View File

@ -0,0 +1,69 @@
/*
** DynASM encoding engine prototypes.
** Copyright (C) 2005-2009 Mike Pall. All rights reserved.
** Released under the MIT/X license. See dynasm.lua for full copyright notice.
*/
#ifndef _DASM_PROTO_H
#define _DASM_PROTO_H
#include <stddef.h>
#include <stdarg.h>
#define DASM_IDENT "DynASM 1.2.1"
#define DASM_VERSION 10201 /* 1.2.1 */
#ifndef Dst_DECL
#define Dst_DECL dasm_State *Dst
#endif
#ifndef Dst_GET
#define Dst_GET (Dst)
#endif
#ifndef DASM_FDEF
#define DASM_FDEF extern
#endif
/* Internal DynASM encoder state. */
typedef struct dasm_State dasm_State;
/* Action list type. */
typedef const unsigned char *dasm_ActList;
/* Initialize and free DynASM state. */
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
DASM_FDEF void dasm_free(Dst_DECL);
/* Setup global array. Must be called before dasm_setup(). */
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
/* Grow PC label array. Can be called after dasm_setup(), too. */
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
/* Setup encoder. */
DASM_FDEF void dasm_setup(Dst_DECL, dasm_ActList actionlist);
/* Feed encoder with actions. Calls are generated by pre-processor. */
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
/* Link sections and return the resulting size. */
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
/* Encode sections into buffer. */
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
/* Get PC label offset. */
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
#ifdef DASM_CHECKS
/* Optional sanity checker to call between isolated encoding steps. */
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
#else
#define dasm_checkstep(a, b) 0
#endif
#endif /* _DASM_PROTO_H */

467
dynasm/dasm_x86.h Normal file
View File

@ -0,0 +1,467 @@
/*
** DynASM x86 encoding engine.
** Copyright (C) 2005-2009 Mike Pall. All rights reserved.
** Released under the MIT/X license. See dynasm.lua for full copyright notice.
*/
#include <stddef.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#define DASM_ARCH "x86"
#ifndef DASM_EXTERN
#define DASM_EXTERN(a,b,c,d) 0
#endif
/* Action definitions. DASM_STOP must be 255. */
enum {
DASM_DISP = 233,
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
};
/* Maximum number of section buffer positions for a single dasm_put() call. */
#define DASM_MAXSECPOS 25
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
#define DASM_S_OK 0x00000000
#define DASM_S_NOMEM 0x01000000
#define DASM_S_PHASE 0x02000000
#define DASM_S_MATCH_SEC 0x03000000
#define DASM_S_RANGE_I 0x11000000
#define DASM_S_RANGE_SEC 0x12000000
#define DASM_S_RANGE_LG 0x13000000
#define DASM_S_RANGE_PC 0x14000000
#define DASM_S_RANGE_VREG 0x15000000
#define DASM_S_UNDEF_L 0x21000000
#define DASM_S_UNDEF_PC 0x22000000
/* Macros to convert positions (8 bit section + 24 bit index). */
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
#define DASM_SEC2POS(sec) ((sec)<<24)
#define DASM_POS2SEC(pos) ((pos)>>24)
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
/* Per-section structure. */
typedef struct dasm_Section {
int *rbuf; /* Biased buffer pointer (negative section bias). */
int *buf; /* True buffer pointer. */
size_t bsize; /* Buffer size in bytes. */
int pos; /* Biased buffer position. */
int epos; /* End of biased buffer position - max single put. */
int ofs; /* Byte offset into section. */
} dasm_Section;
/* Core structure holding the DynASM encoding state. */
struct dasm_State {
size_t psize; /* Allocated size of this structure. */
dasm_ActList actionlist; /* Current actionlist pointer. */
int *lglabels; /* Local/global chain/pos ptrs. */
size_t lgsize;
int *pclabels; /* PC label chains/pos ptrs. */
size_t pcsize;
void **globals; /* Array of globals (bias -10). */
dasm_Section *section; /* Pointer to active section. */
size_t codesize; /* Total size of all code sections. */
int maxsection; /* 0 <= sectionidx < maxsection. */
int status; /* Status code. */
dasm_Section sections[1]; /* All sections. Alloc-extended. */
};
/* The size of the core structure depends on the max. number of sections. */
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
/* Initialize DynASM state. */
void dasm_init(Dst_DECL, int maxsection)
{
dasm_State *D;
size_t psz = 0;
int i;
Dst_REF = NULL;
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
D = Dst_REF;
D->psize = psz;
D->lglabels = NULL;
D->lgsize = 0;
D->pclabels = NULL;
D->pcsize = 0;
D->globals = NULL;
D->maxsection = maxsection;
for (i = 0; i < maxsection; i++) {
D->sections[i].buf = NULL; /* Need this for pass3. */
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
D->sections[i].bsize = 0;
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
}
}
/* Free DynASM state. */
void dasm_free(Dst_DECL)
{
dasm_State *D = Dst_REF;
int i;
for (i = 0; i < D->maxsection; i++)
if (D->sections[i].buf)
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
DASM_M_FREE(Dst, D, D->psize);
}
/* Setup global label array. Must be called before dasm_setup(). */
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
{
dasm_State *D = Dst_REF;
D->globals = gl - 10; /* Negative bias to compensate for locals. */
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
}
/* Grow PC label array. Can be called after dasm_setup(), too. */
void dasm_growpc(Dst_DECL, unsigned int maxpc)
{
dasm_State *D = Dst_REF;
size_t osz = D->pcsize;
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
}
/* Setup encoder. */
void dasm_setup(Dst_DECL, dasm_ActList actionlist)
{
dasm_State *D = Dst_REF;
int i;
D->actionlist = actionlist;
D->status = DASM_S_OK;
D->section = &D->sections[0];
memset((void *)D->lglabels, 0, D->lgsize);
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
for (i = 0; i < D->maxsection; i++) {
D->sections[i].pos = DASM_SEC2POS(i);
D->sections[i].ofs = 0;
}
}
#ifdef DASM_CHECKS
#define CK(x, st) \
do { if (!(x)) { \
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
#define CKPL(kind, st) \
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
#else
#define CK(x, st) ((void)0)
#define CKPL(kind, st) ((void)0)
#endif
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
void dasm_put(Dst_DECL, int start, ...)
{
va_list ap;
dasm_State *D = Dst_REF;
dasm_ActList p = D->actionlist + start;
dasm_Section *sec = D->section;
int pos = sec->pos, ofs = sec->ofs, mrm = 4;
int *b;
if (pos >= sec->epos) {
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
}
b = sec->rbuf;
b[pos++] = start;
va_start(ap, start);
while (1) {
int action = *p++;
if (action < DASM_DISP) {
ofs++;
} else if (action <= DASM_REL_A) {
int n = va_arg(ap, int);
b[pos++] = n;
switch (action) {
case DASM_DISP:
if (n == 0) { if ((mrm&7) == 4) mrm = p[-2]; if ((mrm&7) != 5) break; }
case DASM_IMM_DB: if (((n+128)&-256) == 0) goto ob;
case DASM_REL_A: /* Assumes ptrdiff_t is int. !x64 */
case DASM_IMM_D: ofs += 4; break;
case DASM_IMM_S: CK(((n+128)&-256) == 0, RANGE_I); goto ob;
case DASM_IMM_B: CK((n&-256) == 0, RANGE_I); ob: ofs++; break;
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto ob;
case DASM_IMM_W: CK((n&-65536) == 0, RANGE_I); ofs += 2; break;
case DASM_SPACE: p++; ofs += n; break;
case DASM_SETLABEL: b[pos-2] = -0x40000000; break; /* Neg. label ofs. */
case DASM_VREG: CK((n&-8) == 0 && (n != 4 || (*p&1) == 0), RANGE_VREG);
if (*p++ == 1 && *p == DASM_DISP) mrm = n; continue;
}
mrm = 4;
} else {
int *pl, n;
switch (action) {
case DASM_REL_LG:
case DASM_IMM_LG:
n = *p++; pl = D->lglabels + n;
if (n <= 246) { CKPL(lg, LG); goto putrel; } /* Bkwd rel or global. */
pl -= 246; n = *pl;
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
goto linkrel;
case DASM_REL_PC:
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
putrel:
n = *pl;
if (n < 0) { /* Label exists. Get label pos and store it. */
b[pos] = -n;
} else {
linkrel:
b[pos] = n; /* Else link to rel chain, anchored at label. */
*pl = pos;
}
pos++;
ofs += 4; /* Maximum offset needed. */
if (action == DASM_REL_LG || action == DASM_REL_PC)
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_LABEL_LG: pl = D->lglabels + *p++; CKPL(lg, LG); goto putlabel;
case DASM_LABEL_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
putlabel:
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos; }
*pl = -pos; /* Label exists now. */
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_ALIGN:
ofs += *p++; /* Maximum alignment needed (arg is 2**n-1). */
b[pos++] = ofs; /* Store pass1 offset estimate. */
break;
case DASM_EXTERN: p += 2; ofs += 4; break;
case DASM_ESC: p++; ofs++; break;
case DASM_MARK: mrm = p[-2]; break;
case DASM_SECTION:
n = *p; CK(n < D->maxsection, RANGE_SEC); D->section = &D->sections[n];
case DASM_STOP: goto stop;
}
}
}
stop:
va_end(ap);
sec->pos = pos;
sec->ofs = ofs;
}
#undef CK
/* Pass 2: Link sections, shrink branches/aligns, fix label offsets. */
int dasm_link(Dst_DECL, size_t *szp)
{
dasm_State *D = Dst_REF;
int secnum;
int ofs = 0;
#ifdef DASM_CHECKS
*szp = 0;
if (D->status != DASM_S_OK) return D->status;
{
int pc;
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
}
#endif
{ /* Handle globals not defined in this translation unit. */
int idx;
for (idx = 10; idx*sizeof(int) < D->lgsize; idx++) {
int n = D->lglabels[idx];
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
}
}
/* Combine all code sections. No support for data sections (yet). */
for (secnum = 0; secnum < D->maxsection; secnum++) {
dasm_Section *sec = D->sections + secnum;
int *b = sec->rbuf;
int pos = DASM_SEC2POS(secnum);
int lastpos = sec->pos;
while (pos != lastpos) {
dasm_ActList p = D->actionlist + b[pos++];
while (1) {
int op, action = *p++;
switch (action) {
case DASM_REL_LG: p++; op = p[-3]; goto rel_pc;
case DASM_REL_PC: op = p[-2]; rel_pc: {
int shrink = op == 0xe9 ? 3 : ((op&0xf0) == 0x80 ? 4 : 0);
if (shrink) { /* Shrinkable branch opcode? */
int lofs, lpos = b[pos];
if (lpos < 0) goto noshrink; /* Ext global? */
lofs = *DASM_POS2PTR(D, lpos);
if (lpos > pos) { /* Fwd label: add cumulative section offsets. */
int i;
for (i = secnum; i < DASM_POS2SEC(lpos); i++)
lofs += D->sections[i].ofs;
} else {
lofs -= ofs; /* Bkwd label: unfix offset. */
}
lofs -= b[pos+1]; /* Short branch ok? */
if (lofs >= -128-shrink && lofs <= 127) ofs -= shrink; /* Yes. */
else { noshrink: shrink = 0; } /* No, cannot shrink op. */
}
b[pos+1] = shrink;
pos += 2;
break;
}
case DASM_SPACE: case DASM_IMM_LG: case DASM_VREG: p++;
case DASM_DISP: case DASM_IMM_S: case DASM_IMM_B: case DASM_IMM_W:
case DASM_IMM_D: case DASM_IMM_WB: case DASM_IMM_DB:
case DASM_SETLABEL: case DASM_REL_A: case DASM_IMM_PC: pos++; break;
case DASM_LABEL_LG: p++;
case DASM_LABEL_PC: b[pos++] += ofs; break; /* Fix label offset. */
case DASM_ALIGN: ofs -= (b[pos++]+ofs)&*p++; break; /* Adjust ofs. */
case DASM_EXTERN: p += 2; break;
case DASM_ESC: p++; break;
case DASM_MARK: break;
case DASM_SECTION: case DASM_STOP: goto stop;
}
}
stop: (void)0;
}
ofs += sec->ofs; /* Next section starts right after current section. */
}
D->codesize = ofs; /* Total size of all code sections */
*szp = ofs;
return DASM_S_OK;
}
#define dasmb(x) *cp++ = (unsigned char)(x)
#ifndef DASM_ALIGNED_WRITES
#define dasmw(x) \
do { *((unsigned short *)cp) = (unsigned short)(x); cp+=2; } while (0)
#define dasmd(x) \
do { *((unsigned int *)cp) = (unsigned int)(x); cp+=4; } while (0)
#else
#define dasmw(x) do { dasmb(x); dasmb((x)>>8); } while (0)
#define dasmd(x) do { dasmw(x); dasmw((x)>>16); } while (0)
#endif
/* Pass 3: Encode sections. */
int dasm_encode(Dst_DECL, void *buffer)
{
dasm_State *D = Dst_REF;
unsigned char *base = (unsigned char *)buffer;
unsigned char *cp = base;
int secnum;
/* Encode all code sections. No support for data sections (yet). */
for (secnum = 0; secnum < D->maxsection; secnum++) {
dasm_Section *sec = D->sections + secnum;
int *b = sec->buf;
int *endb = sec->rbuf + sec->pos;
while (b != endb) {
dasm_ActList p = D->actionlist + *b++;
unsigned char *mark = NULL;
while (1) {
int action = *p++;
int n = (action >= DASM_DISP && action <= DASM_ALIGN) ? *b++ : 0;
switch (action) {
case DASM_DISP: if (!mark) mark = cp; {
unsigned char *mm = mark;
if (*p != DASM_IMM_DB && *p != DASM_IMM_WB) mark = NULL;
if (n == 0) { int mrm = mm[-1]&7; if (mrm == 4) mrm = mm[0]&7;
if (mrm != 5) { mm[-1] -= 0x80; break; } }
if (((n+128) & -256) != 0) goto wd; else mm[-1] -= 0x40;
}
case DASM_IMM_S: case DASM_IMM_B: wb: dasmb(n); break;
case DASM_IMM_DB: if (((n+128)&-256) == 0) {
db: if (!mark) mark = cp; mark[-2] += 2; mark = NULL; goto wb;
} else mark = NULL;
case DASM_IMM_D: wd: dasmd(n); break;
case DASM_IMM_WB: if (((n+128)&-256) == 0) goto db; else mark = NULL;
case DASM_IMM_W: dasmw(n); break;
case DASM_VREG: { int t = *p++; if (t >= 2) n<<=3; cp[-1] |= n; break; }
case DASM_REL_LG: p++; if (n >= 0) goto rel_pc;
b++; n = (int)(ptrdiff_t)D->globals[-n];
case DASM_REL_A: rel_a: n -= (int)(ptrdiff_t)(cp+4); goto wd; /* !x64 */
case DASM_REL_PC: rel_pc: {
int shrink = *b++;
int *pb = DASM_POS2PTR(D, n); if (*pb < 0) { n = pb[1]; goto rel_a; }
n = *pb - ((int)(cp-base) + 4-shrink);
if (shrink == 0) goto wd;
if (shrink == 4) { cp--; cp[-1] = *cp-0x10; } else cp[-1] = 0xeb;
goto wb;
}
case DASM_IMM_LG:
p++; if (n < 0) { n = (int)(ptrdiff_t)D->globals[-n]; goto wd; }
case DASM_IMM_PC: {
int *pb = DASM_POS2PTR(D, n);
n = *pb < 0 ? pb[1] : (*pb + (int)(ptrdiff_t)base);
goto wd;
}
case DASM_LABEL_LG: {
int idx = *p++;
if (idx >= 10)
D->globals[idx] = (void *)(base + (*p == DASM_SETLABEL ? *b : n));
break;
}
case DASM_LABEL_PC: case DASM_SETLABEL: break;
case DASM_SPACE: { int fill = *p++; while (n--) *cp++ = fill; break; }
case DASM_ALIGN:
n = *p++;
while (((cp-base) & n)) *cp++ = 0x90; /* nop */
break;
case DASM_EXTERN: n = DASM_EXTERN(Dst, cp, p[1], *p); p += 2; goto wd;
case DASM_MARK: mark = cp; break;
case DASM_ESC: action = *p++;
default: *cp++ = action; break;
case DASM_SECTION: case DASM_STOP: goto stop;
}
}
stop: (void)0;
}
}
if (base + D->codesize != cp) /* Check for phase errors. */
return DASM_S_PHASE;
return DASM_S_OK;
}
/* Get PC label offset. */
int dasm_getpclabel(Dst_DECL, unsigned int pc)
{
dasm_State *D = Dst_REF;
if (pc*sizeof(int) < D->pcsize) {
int pos = D->pclabels[pc];
if (pos < 0) return *DASM_POS2PTR(D, -pos);
if (pos > 0) return -1; /* Undefined. */
}
return -2; /* Unused or out of range. */
}
#ifdef DASM_CHECKS
/* Optional sanity checker to call between isolated encoding steps. */
int dasm_checkstep(Dst_DECL, int secmatch)
{
dasm_State *D = Dst_REF;
if (D->status == DASM_S_OK) {
int i;
for (i = 1; i <= 9; i++) {
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_L|i; break; }
D->lglabels[i] = 0;
}
}
if (D->status == DASM_S_OK && secmatch >= 0 &&
D->section != &D->sections[secmatch])
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
return D->status;
}
#endif

1799
dynasm/dasm_x86.lua Normal file

File diff suppressed because it is too large Load Diff

1070
dynasm/dynasm.lua Normal file

File diff suppressed because it is too large Load Diff

41
etc/strict.lua Normal file
View File

@ -0,0 +1,41 @@
--
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
--
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function (t, n)
if not mt.__declared[n] and what() ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
return rawget(t, n)
end

1
lib/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vmdef.lua

182
lib/bc.lua Normal file
View File

@ -0,0 +1,182 @@
----------------------------------------------------------------------------
-- LuaJIT bytecode listing module.
--
-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
-- it hooks into the parser and lists all functions of a chunk as they
-- are parsed.
--
-- Example usage:
--
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
-- luajit -jbc=- foo.lua
-- luajit -jbc=foo.list foo.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
-- is started.
--
-- This module can also be used programmatically:
--
-- local bc = require("jit.bc")
--
-- local function foo() print("hello") end
--
-- bc.dump(foo) --> -- BYTECODE -- [...]
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
--
-- local out = {
-- -- Do something wich each line:
-- write = function(t, ...) io.write(...) end,
-- close = function(t) end,
-- flush = function(t) end,
-- }
-- bc.dump(foo, out)
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local bit = require("bit")
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, band, shr = string.byte, bit.band, bit.rshift
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
local funcuvname = jutil.funcuvname
local bcnames = vmdef.bcnames
local stdout, stderr = io.stdout, io.stderr
------------------------------------------------------------------------------
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
elseif c == "\r" then return "\\r"
else return format("\\%03d", byte(c))
end
end
-- Return one bytecode line.
local function bcline(func, pc, prefix)
local ins, m = funcbc(func, pc)
if not ins then return end
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
local a = band(shr(ins, 8), 0xff)
local oidx = 6*band(ins, 0xff)
local s = format("%04d %s %-6s %3s ",
pc, prefix or " ", sub(bcnames, oidx+1, oidx+6), ma == 0 and "" or a)
local d = shr(ins, 16)
if mc == 13*128 then -- BCMjump
if ma == 0 then
return format("%s=> %04d\n", sub(s, 1, -3), pc+d-0x7fff)
end
return format("%s=> %04d\n", s, pc+d-0x7fff)
end
if mb ~= 0 then d = band(d, 0xff) end
local kc
if mc == 10*128 then -- BCMstr
kc = funck(func, -d-1)
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
elseif mc == 9*128 then -- BCMnum
kc = funck(func, d)
elseif mc == 12*128 then -- BCMfunc
local fi = funcinfo(funck(func, -d-1))
if fi.ffid then
kc = vmdef.ffnames[fi.ffid]
else
kc = fi.loc
end
elseif mc == 5*128 then -- BCMuv
kc = funcuvname(func, d)
end
if ma == 5 then -- BCMuv
local ka = funcuvname(func, a)
if kc then kc = ka.." ; "..kc else kc = ka end
end
if mb ~= 0 then
local b = shr(ins, 24)
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
return format("%s%3d %3d\n", s, b, d)
end
if kc then return format("%s%3d ; %s\n", s, d, kc) end
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
return format("%s%3d\n", s, d)
end
-- Collect branch targets of a function.
local function bctargets(func)
local target = {}
for pc=1,1000000000 do
local ins, m = funcbc(func, pc)
if not ins then break end
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
end
return target
end
-- Dump bytecode instructions of a function.
local function bcdump(func, out)
if not out then out = stdout end
local fi = funcinfo(func)
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
local target = bctargets(func)
for pc=1,1000000000 do
local s = bcline(func, pc, target[pc] and "=>")
if not s then break end
out:write(s)
end
out:write("\n")
out:flush()
end
------------------------------------------------------------------------------
-- Active flag and output file handle.
local active, out
-- List handler.
local function h_list(func)
return bcdump(func, out)
end
-- Detach list handler.
local function bclistoff()
if active then
active = false
jit.attach(h_list)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach list handler.
local function bcliston(outfile)
if active then bclistoff() end
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(h_list, "bc")
active = true
end
-- Public module functions.
module(...)
line = bcline
dump = bcdump
targets = bctargets
on = bcliston
off = bclistoff
start = bcliston -- For -j command line option.

19
lib/dis_x64.lua Normal file
View File

@ -0,0 +1,19 @@
----------------------------------------------------------------------------
-- LuaJIT x64 disassembler wrapper module.
--
-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This module just exports the 64 bit functions from the combined
-- x86/x64 disassembler module. All the interesting stuff is there.
------------------------------------------------------------------------------
local require = require
module(...)
local dis_x86 = require(_PACKAGE.."dis_x86")
create = dis_x86.create64
disass = dis_x86.disass64

824
lib/dis_x86.lua Normal file
View File

@ -0,0 +1,824 @@
----------------------------------------------------------------------------
-- LuaJIT x86/x64 disassembler module.
--
-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
-- This is a helper module used by the LuaJIT machine code dumper module.
--
-- Sending small code snippets to an external disassembler and mixing the
-- output with our own stuff was too fragile. So I had to bite the bullet
-- and write yet another x86 disassembler. Oh well ...
--
-- The output format is very similar to what ndisasm generates. But it has
-- been developed independently by looking at the opcode tables from the
-- Intel and AMD manuals. The supported instruction set is quite extensive
-- and reflects what a current generation Intel or AMD CPU implements in
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
-- SSE4.1, SSE4.2, SSE4a and even privileged and hypervisor (VMX/SVM)
-- instructions.
--
-- Notes:
-- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
-- * No attempt at optimization has been made -- it's fast enough for my needs.
-- * The public API may change when more architectures are added.
------------------------------------------------------------------------------
local type = type
local sub, byte, format = string.sub, string.byte, string.format
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
local lower, rep = string.lower, string.rep
-- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
local map_opc1_32 = {
--0x
[0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
"orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
--1x
"adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
"sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
--2x
"andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
"subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
--3x
"xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
"cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
--4x
"incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
"decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
--5x
"pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
"popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
--6x
"sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
"fs:seg","gs:seg","o16:","a16",
"pushUi","imulVrmi","pushBs","imulVrms",
"insb","insVS","outsb","outsVS",
--7x
"joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
"jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
--8x
"arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
"testBmr","testVmr","xchgBrm","xchgVrm",
"movBmr","movVmr","movBrm","movVrm",
"movVmg","leaVrm","movWgm","popUm",
--9x
"nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
"xchgVaR","xchgVaR","xchgVaR","xchgVaR",
"sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
"sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
--Ax
"movBao","movVao","movBoa","movVoa",
"movsb","movsVS","cmpsb","cmpsVS",
"testBai","testVai","stosb","stosVS",
"lodsb","lodsVS","scasb","scasVS",
--Bx
"movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
"movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
--Cx
"shift!Bmu","shift!Vmu","retBw","ret","$lesVrm","$ldsVrm","movBmi","movVmi",
"enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
--Dx
"shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
"fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
--Ex
"loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
"inBau","inVau","outBua","outVua",
"callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
--Fx
"lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
"clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
}
assert(#map_opc1_32 == 255)
-- Map for 1st opcode byte in 64 bit mode (overrides only).
local map_opc1_64 = setmetatable({
[0x06]=false, [0x07]=false, [0x0e]=false,
[0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
[0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
[0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
[0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
[0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
[0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
[0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
[0x82]=false, [0x9a]=false, [0xc4]=false, [0xc5]=false, [0xce]=false,
[0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
}, { __index = map_opc1_32 })
-- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
-- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
local map_opc2 = {
--0x
[0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
"invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
--1x
"movupsXrm|movssXrm|movupdXrm|movsdXrm",
"movupsXmr|movssXmr|movupdXmr|movsdXmr",
"movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
"movlpsXmr||movlpdXmr",
"unpcklpsXrm||unpcklpdXrm",
"unpckhpsXrm||unpckhpdXrm",
"movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
"movhpsXmr||movhpdXmr",
"$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
"hintnopVm","hintnopVm","hintnopVm","hintnopVm",
--2x
"movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
"movapsXrm||movapdXrm",
"movapsXmr||movapdXmr",
"cvtpi2psXrMm|cvtsi2ssXrVm|cvtpi2pdXrMm|cvtsi2sdXrVm",
"movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
"cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
"cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
"ucomissXrm||ucomisdXrm",
"comissXrm||comisdXrm",
--3x
"wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
"opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
--4x
"cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
"cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
"cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
"cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
--5x
"movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
"rsqrtpsXrm|rsqrtssXrm","rcppsXrm|rcpssXrm",
"andpsXrm||andpdXrm","andnpsXrm||andnpdXrm",
"orpsXrm||orpdXrm","xorpsXrm||xorpdXrm",
"addpsXrm|addssXrm|addpdXrm|addsdXrm","mulpsXrm|mulssXrm|mulpdXrm|mulsdXrm",
"cvtps2pdXrm|cvtss2sdXrm|cvtpd2psXrm|cvtsd2ssXrm",
"cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
"subpsXrm|subssXrm|subpdXrm|subsdXrm","minpsXrm|minssXrm|minpdXrm|minsdXrm",
"divpsXrm|divssXrm|divpdXrm|divsdXrm","maxpsXrm|maxssXrm|maxpdXrm|maxsdXrm",
--6x
"punpcklbwPrm","punpcklwdPrm","punpckldqPrm","packsswbPrm",
"pcmpgtbPrm","pcmpgtwPrm","pcmpgtdPrm","packuswbPrm",
"punpckhbwPrm","punpckhwdPrm","punpckhdqPrm","packssdwPrm",
"||punpcklqdqXrm","||punpckhqdqXrm",
"movPrVSm","movqMrm|movdquXrm|movdqaXrm",
--7x
"pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pmu",
"pshiftd!Pmu","pshiftq!Mmu||pshiftdq!Xmu",
"pcmpeqbPrm","pcmpeqwPrm","pcmpeqdPrm","emms|",
"vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
nil,nil,
"||haddpdXrm|haddpsXrm","||hsubpdXrm|hsubpsXrm",
"movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
--8x
"joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
"jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
--9x
"setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
"setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
--Ax
"push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
"push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
--Bx
"cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
"$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
"|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
"bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
--Cx
"xaddBmr","xaddVmr",
"cmppsXrmu|cmpssXrmu|cmppdXrmu|cmpsdXrmu","$movntiVmr|",
"pinsrwPrWmu","pextrwDrPmu",
"shufpsXrmu||shufpdXrmu","$cmpxchg!Qmp",
"bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
--Dx
"||addsubpdXrm|addsubpsXrm","psrlwPrm","psrldPrm","psrlqPrm",
"paddqPrm","pmullwPrm",
"|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
"psubusbPrm","psubuswPrm","pminubPrm","pandPrm",
"paddusbPrm","padduswPrm","pmaxubPrm","pandnPrm",
--Ex
"pavgbPrm","psrawPrm","psradPrm","pavgwPrm",
"pmulhuwPrm","pmulhwPrm",
"|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
"psubsbPrm","psubswPrm","pminswPrm","porPrm",
"paddsbPrm","paddswPrm","pmaxswPrm","pxorPrm",
--Fx
"|||lddquXrm","psllwPrm","pslldPrm","psllqPrm",
"pmuludqPrm","pmaddwdPrm","psadbwPrm","maskmovqMrm||maskmovdquXrm$",
"psubbPrm","psubwPrm","psubdPrm","psubqPrm",
"paddbPrm","paddwPrm","padddPrm","ud",
}
assert(map_opc2[255] == "ud")
-- Map for three-byte opcodes. Can't wait for their next invention.
local map_opc3 = {
["38"] = { -- [66] 0f 38 xx
--0x
[0]="pshufbPrm","phaddwPrm","phadddPrm","phaddswPrm",
"pmaddubswPrm","phsubwPrm","phsubdPrm","phsubswPrm",
"psignbPrm","psignwPrm","psigndPrm","pmulhrswPrm",
nil,nil,nil,nil,
--1x
"||pblendvbXrma",nil,nil,nil,
"||blendvpsXrma","||blendvpdXrma",nil,"||ptestXrm",
nil,nil,nil,nil,
"pabsbPrm","pabswPrm","pabsdPrm",nil,
--2x
"||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
"||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
"||pmuldqXrm","||pcmpeqqXrm","||$movntdqaXrm","||packusdwXrm",
nil,nil,nil,nil,
--3x
"||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
"||pmovzxwqXrm","||pmovzxdqXrm",nil,"||pcmpgtqXrm",
"||pminsbXrm","||pminsdXrm","||pminuwXrm","||pminudXrm",
"||pmaxsbXrm","||pmaxsdXrm","||pmaxuwXrm","||pmaxudXrm",
--4x
"||pmulddXrm","||phminposuwXrm",
--Fx
[0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
},
["3a"] = { -- [66] 0f 3a xx
--0x
[0x00]=nil,nil,nil,nil,nil,nil,nil,nil,
"||roundpsXrmu","||roundpdXrmu","||roundssXrmu","||roundsdXrmu",
"||blendpsXrmu","||blendpdXrmu","||pblendwXrmu","palignrPrmu",
--1x
nil,nil,nil,nil,
"||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
nil,nil,nil,nil,nil,nil,nil,nil,
--2x
"||pinsrbXrVmu","||insertpsXrmu","||pinsrXrVmuS",nil,
--4x
[0x40] = "||dppsXrmu",
[0x41] = "||dppdXrmu",
[0x42] = "||mpsadbwXrmu",
--6x
[0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
[0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
},
}
-- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
local map_opcvm = {
[0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
[0xc8]="monitor",[0xc9]="mwait",
[0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
[0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
[0xf8]="swapgs",[0xf9]="rdtscp",
}
-- Map for FP opcodes. And you thought stack machines are simple?
local map_opcfp = {
-- D8-DF 00-BF: opcodes with a memory operand.
-- D8
[0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
"fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
-- DA
"fiaddDm","fimulDm","ficomDm","ficompDm",
"fisubDm","fisubrDm","fidivDm","fidivrDm",
-- DB
"fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
-- DC
"faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
-- DD
"fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
-- DE
"fiaddWm","fimulWm","ficomWm","ficompWm",
"fisubWm","fisubrWm","fidivWm","fidivrWm",
-- DF
"fildWm","fisttpWm","fistWm","fistpWm",
"fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
-- xx C0-FF: opcodes with a pseudo-register operand.
-- D8
"faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
-- D9
"fldFf","fxchFf",{"fnop"},nil,
{"fchs","fabs",nil,nil,"ftst","fxam"},
{"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
{"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
{"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
-- DA
"fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
-- DB
"fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
{nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
-- DC
"fadd toFf","fmul toFf",nil,nil,
"fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
-- DD
"ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
-- DE
"faddpFf","fmulpFf",nil,{nil,"fcompp"},
"fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
-- DF
nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
}
assert(map_opcfp[126] == "fcomipFf")
-- Map for opcode groups. The subkey is sp from the ModRM byte.
local map_opcgroup = {
arith = { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
shift = { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
testb = { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
testv = { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
incb = { "inc", "dec" },
incd = { "inc", "dec", "callDmp", "$call farDmp",
"jmpDmp", "$jmp farDmp", "pushUm" },
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
"smsw", nil, "lmsw", "vm*$invlpg" },
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
prefetch = { "prefetch", "prefetchw" },
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
}
------------------------------------------------------------------------------
-- Maps for register names.
local map_regs = {
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
}
local map_segregs = { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
-- Maps for size names.
local map_sz2n = {
B = 1, W = 2, D = 4, Q = 8, M = 8, X = 16,
}
local map_sz2prefix = {
B = "byte", W = "word", D = "dword",
Q = "qword",
M = "qword", X = "xword",
F = "dword", G = "qword", -- No need for sizes/register names for these two.
}
------------------------------------------------------------------------------
-- Output a nicely formatted line with an opcode and operands.
local function putop(ctx, text, operands)
local code, pos, hex = ctx.code, ctx.pos, ""
local hmax = ctx.hexdump
if hmax > 0 then
for i=ctx.start,pos-1 do
hex = hex..format("%02X", byte(code, i, i))
end
if #hex > hmax then hex = sub(hex, 1, hmax)..". "
else hex = hex..rep(" ", hmax-#hex+2) end
end
if operands then text = text.." "..operands end
if ctx.o16 then text = "o16 "..text; ctx.o16 = false end
if ctx.a32 then text = "a32 "..text; ctx.a32 = false end
if ctx.rep then text = ctx.rep.." "..text; ctx.rep = false end
if ctx.rex then
local t = (ctx.rexw and "w" or "")..(ctx.rexr and "r" or "")..
(ctx.rexx and "x" or "")..(ctx.rexb and "b" or "")
if t ~= "" then text = "rex."..t.." "..text end
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false
end
if ctx.seg then
local text2, n = gsub(text, "%[", "["..ctx.seg..":")
if n == 0 then text = ctx.seg.." "..text else text = text2 end
ctx.seg = false
end
if ctx.lock then text = "lock "..text; ctx.lock = false end
local imm = ctx.imm
if imm then
local sym = ctx.symtab[imm]
if sym then text = text.."\t->"..sym end
end
ctx.out(format("%08x %s%s\n", ctx.addr+ctx.start, hex, text))
ctx.mrm = false
ctx.start = pos
ctx.imm = nil
end
-- Clear all prefix flags.
local function clearprefixes(ctx)
ctx.o16 = false; ctx.seg = false; ctx.lock = false; ctx.rep = false
ctx.rexw = false; ctx.rexr = false; ctx.rexx = false; ctx.rexb = false
ctx.rex = false; ctx.a32 = false
end
-- Fallback for incomplete opcodes at the end.
local function incomplete(ctx)
ctx.pos = ctx.stop+1
clearprefixes(ctx)
return putop(ctx, "(incomplete)")
end
-- Fallback for unknown opcodes.
local function unknown(ctx)
clearprefixes(ctx)
return putop(ctx, "(unknown)")
end
-- Return an immediate of the specified size.
local function getimm(ctx, pos, n)
if pos+n-1 > ctx.stop then return incomplete(ctx) end
local code = ctx.code
if n == 1 then
local b1 = byte(code, pos, pos)
return b1
elseif n == 2 then
local b1, b2 = byte(code, pos, pos+1)
return b1+b2*256
else
local b1, b2, b3, b4 = byte(code, pos, pos+3)
local imm = b1+b2*256+b3*65536+b4*16777216
ctx.imm = imm
return imm
end
end
-- Process pattern string and generate the operands.
local function putpat(ctx, name, pat)
local operands, regs, sz, mode, sp, rm, sc, rx, sdisp
local code, pos, stop = ctx.code, ctx.pos, ctx.stop
-- Chars used: 1DFGIMPQRSTUVWXacdfgijmoprstuwxyz
for p in gmatch(pat, ".") do
local x = nil
if p == "V" or p == "U" then
if ctx.rexw then sz = "Q"; ctx.rexw = false
elseif ctx.o16 then sz = "W"; ctx.o16 = false
elseif p == "U" and ctx.x64 then sz = "Q"
else sz = "D" end
regs = map_regs[sz]
elseif p == "T" then
if ctx.rexw then sz = "Q"; ctx.rexw = false else sz = "D" end
regs = map_regs[sz]
elseif p == "B" then
sz = "B"
regs = ctx.rex and map_regs.B64 or map_regs.B
elseif match(p, "[WDQMXFG]") then
sz = p
regs = map_regs[sz]
elseif p == "P" then
sz = ctx.o16 and "X" or "M"; ctx.o16 = false
regs = map_regs[sz]
elseif p == "S" then
name = name..lower(sz)
elseif p == "s" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = imm <= 127 and format("+0x%02x", imm)
or format("-0x%02x", 256-imm)
pos = pos+1
elseif p == "u" then
local imm = getimm(ctx, pos, 1); if not imm then return end
x = format("0x%02x", imm)
pos = pos+1
elseif p == "w" then
local imm = getimm(ctx, pos, 2); if not imm then return end
x = format("0x%x", imm)
pos = pos+2
elseif p == "o" then -- [offset]
if ctx.x64 then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("[0x%08x%08x]", imm2, imm1)
pos = pos+8
else
local imm = getimm(ctx, pos, 4); if not imm then return end
x = format("[0x%08x]", imm)
pos = pos+4
end
elseif p == "i" or p == "I" then
local n = map_sz2n[sz]
if n == 8 and ctx.x64 and p == "I" then
local imm1 = getimm(ctx, pos, 4); if not imm1 then return end
local imm2 = getimm(ctx, pos+4, 4); if not imm2 then return end
x = format("0x%08x%08x", imm2, imm1)
else
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "Q" and (imm < 0 or imm > 0x7fffffff) then
imm = (0xffffffff+1)-imm
x = format(imm > 65535 and "-0x%08x" or "-0x%x", imm)
else
x = format(imm > 65535 and "0x%08x" or "0x%x", imm)
end
end
pos = pos+n
elseif p == "j" then
local n = map_sz2n[sz]
if n == 8 then n = 4 end
local imm = getimm(ctx, pos, n); if not imm then return end
if sz == "B" and imm > 127 then imm = imm-256
elseif imm > 2147483647 then imm = imm-4294967296 end
pos = pos+n
imm = imm + pos + ctx.addr
if imm > 4294967295 and not ctx.x64 then imm = imm-4294967296 end
ctx.imm = imm
if sz == "W" then
x = format("word 0x%04x", imm%65536)
elseif ctx.x64 then
local lo = imm % 0x1000000
x = format("0x%02x%06x", (imm-lo) / 0x1000000, lo)
else
x = format("0x%08x", imm)
end
elseif p == "R" then
local r = byte(code, pos-1, pos-1)%8
if ctx.rexb then r = r + 8; ctx.rexb = false end
x = regs[r+1]
elseif p == "a" then x = regs[1]
elseif p == "c" then x = "cl"
elseif p == "d" then x = "dx"
elseif p == "1" then x = "1"
else
if not mode then
mode = ctx.mrm
if not mode then
if pos > stop then return incomplete(ctx) end
mode = byte(code, pos, pos)
pos = pos+1
end
rm = mode%8; mode = (mode-rm)/8
sp = mode%8; mode = (mode-sp)/8
sdisp = ""
if mode < 3 then
if rm == 4 then
if pos > stop then return incomplete(ctx) end
sc = byte(code, pos, pos)
pos = pos+1
rm = sc%8; sc = (sc-rm)/8
rx = sc%8; sc = (sc-rx)/8
if ctx.rexx then rx = rx + 8; ctx.rexx = false end
if rx == 4 then rx = nil end
end
if mode > 0 or rm == 5 then
local dsz = mode
if dsz ~= 1 then dsz = 4 end
local disp = getimm(ctx, pos, dsz); if not disp then return end
if mode == 0 then rm = nil end
if rm or rx or (not sc and ctx.x64 and not ctx.a32) then
if dsz == 1 and disp > 127 then
sdisp = format("-0x%x", 256-disp)
elseif disp >= 0 and disp <= 0x7fffffff then
sdisp = format("+0x%x", disp)
else
sdisp = format("-0x%x", (0xffffffff+1)-disp)
end
else
sdisp = format(ctx.x64 and not ctx.a32 and
not (disp >= 0 and disp <= 0x7fffffff)
and "0xffffffff%08x" or "0x%08x", disp)
end
pos = pos+dsz
end
end
if rm and ctx.rexb then rm = rm + 8; ctx.rexb = false end
if ctx.rexr then sp = sp + 8; ctx.rexr = false end
end
if p == "m" then
if mode == 3 then x = regs[rm+1]
else
local aregs = ctx.a32 and map_regs.D or ctx.aregs
local srm, srx = "", ""
if rm then srm = aregs[rm+1]
elseif not sc and ctx.x64 and not ctx.a32 then srm = "rip" end
ctx.a32 = false
if rx then
if rm then srm = srm.."+" end
srx = aregs[rx+1]
if sc > 0 then srx = srx.."*"..(2^sc) end
end
x = format("[%s%s%s]", srm, srx, sdisp)
end
if mode < 3 and
(not match(pat, "[aRrgp]") or match(pat, "t")) then -- Yuck.
x = map_sz2prefix[sz].." "..x
end
elseif p == "r" then x = regs[sp+1]
elseif p == "g" then x = map_segregs[sp+1]
elseif p == "p" then -- Suppress prefix.
elseif p == "f" then x = "st"..rm
elseif p == "x" then
if sp == 0 and ctx.lock and not ctx.x64 then
x = "CR8"; ctx.lock = false
else
x = "CR"..sp
end
elseif p == "y" then x = "DR"..sp
elseif p == "z" then x = "TR"..sp
elseif p == "t" then
else
error("bad pattern `"..pat.."'")
end
end
if x then operands = operands and operands..", "..x or x end
end
ctx.pos = pos
return putop(ctx, name, operands)
end
-- Forward declaration.
local map_act
-- Fetch and cache MRM byte.
local function getmrm(ctx)
local mrm = ctx.mrm
if not mrm then
local pos = ctx.pos
if pos > ctx.stop then return nil end
mrm = byte(ctx.code, pos, pos)
ctx.pos = pos+1
ctx.mrm = mrm
end
return mrm
end
-- Dispatch to handler depending on pattern.
local function dispatch(ctx, opat, patgrp)
if not opat then return unknown(ctx) end
if match(opat, "%|") then -- MMX/SSE variants depending on prefix.
local p
if ctx.rep then
p = ctx.rep=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
ctx.rep = false
elseif ctx.o16 then p = "%|[^%|]*%|([^%|]*)"; ctx.o16 = false
else p = "^[^%|]*" end
opat = match(opat, p)
if not opat then return unknown(ctx) end
-- ctx.rep = false; ctx.o16 = false
--XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
--XXX remove in branches?
end
if match(opat, "%$") then -- reg$mem variants.
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
opat = match(opat, mrm >= 192 and "^[^%$]*" or "%$(.*)")
if opat == "" then return unknown(ctx) end
end
if opat == "" then return unknown(ctx) end
local name, pat = match(opat, "^([a-z0-9 ]*)(.*)")
if pat == "" and patgrp then pat = patgrp end
return map_act[sub(pat, 1, 1)](ctx, name, pat)
end
-- Get a pattern from an opcode map and dispatch to handler.
local function dispatchmap(ctx, opcmap)
local pos = ctx.pos
local opat = opcmap[byte(ctx.code, pos, pos)]
pos = pos + 1
ctx.pos = pos
return dispatch(ctx, opat)
end
-- Map for action codes. The key is the first char after the name.
map_act = {
-- Simple opcodes without operands.
[""] = function(ctx, name, pat)
return putop(ctx, name)
end,
-- Operand size chars fall right through.
B = putpat, W = putpat, D = putpat, Q = putpat,
V = putpat, U = putpat, T = putpat,
M = putpat, X = putpat, P = putpat,
F = putpat, G = putpat,
-- Collect prefixes.
[":"] = function(ctx, name, pat)
ctx[pat == ":" and name or sub(pat, 2)] = name
if ctx.pos - ctx.start > 5 then return unknown(ctx) end -- Limit #prefixes.
end,
-- Chain to special handler specified by name.
["*"] = function(ctx, name, pat)
return map_act[name](ctx, name, sub(pat, 2))
end,
-- Use named subtable for opcode group.
["!"] = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
return dispatch(ctx, map_opcgroup[name][((mrm-(mrm%8))/8)%8+1], sub(pat, 2))
end,
-- o16,o32[,o64] variants.
sz = function(ctx, name, pat)
if ctx.o16 then ctx.o16 = false
else
pat = match(pat, ",(.*)")
if ctx.rexw then
local p = match(pat, ",(.*)")
if p then pat = p; ctx.rexw = false end
end
end
pat = match(pat, "^[^,]*")
return dispatch(ctx, pat)
end,
-- Two-byte opcode dispatch.
opc2 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc2)
end,
-- Three-byte opcode dispatch.
opc3 = function(ctx, name, pat)
return dispatchmap(ctx, map_opc3[pat])
end,
-- VMX/SVM dispatch.
vm = function(ctx, name, pat)
return dispatch(ctx, map_opcvm[ctx.mrm])
end,
-- Floating point opcode dispatch.
fp = function(ctx, name, pat)
local mrm = getmrm(ctx); if not mrm then return incomplete(ctx) end
local rm = mrm%8
local idx = pat*8 + ((mrm-rm)/8)%8
if mrm >= 192 then idx = idx + 64 end
local opat = map_opcfp[idx]
if type(opat) == "table" then opat = opat[rm+1] end
return dispatch(ctx, opat)
end,
-- REX prefix.
rex = function(ctx, name, pat)
if ctx.rex then return unknown(ctx) end -- Only 1 REX prefix allowed.
for p in gmatch(pat, ".") do ctx["rex"..p] = true end
ctx.rex = true
end,
-- Special case for nop with REX prefix.
nop = function(ctx, name, pat)
return dispatch(ctx, ctx.rex and pat or "nop")
end,
}
------------------------------------------------------------------------------
-- Disassemble a block of code.
local function disass_block(ctx, ofs, len)
if not ofs then ofs = 0 end
local stop = len and ofs+len or #ctx.code
ofs = ofs + 1
ctx.start = ofs
ctx.pos = ofs
ctx.stop = stop
ctx.imm = nil
ctx.mrm = false
clearprefixes(ctx)
while ctx.pos <= stop do dispatchmap(ctx, ctx.map1) end
if ctx.pos ~= ctx.start then incomplete(ctx) end
end
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
local function create_(code, addr, out)
local ctx = {}
ctx.code = code
ctx.addr = (addr or 0) - 1
ctx.out = out or io.write
ctx.symtab = {}
ctx.disass = disass_block
ctx.hexdump = 16
ctx.x64 = false
ctx.map1 = map_opc1_32
ctx.aregs = map_regs.D
return ctx
end
local function create64_(code, addr, out)
local ctx = create_(code, addr, out)
ctx.x64 = true
ctx.map1 = map_opc1_64
ctx.aregs = map_regs.Q
return ctx
end
-- Simple API: disassemble code (a string) at address and output via out.
local function disass_(code, addr, out)
create_(code, addr, out):disass()
end
local function disass64_(code, addr, out)
create64_(code, addr, out):disass()
end
-- Public module functions.
module(...)
create = create_
create64 = create64_
disass = disass_
disass64 = disass64_

567
lib/dump.lua Normal file
View File

@ -0,0 +1,567 @@
----------------------------------------------------------------------------
-- LuaJIT compiler dump module.
--
-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module can be used to debug the JIT compiler itself. It dumps the
-- code representations and structures used in various compiler stages.
--
-- Example usage:
--
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
-- luajit -jdump=is myapp.lua | less -R
-- luajit -jdump=-b myapp.lua
-- luajit -jdump=+aH,myapp.html myapp.lua
-- luajit -jdump=ixT,myapp.dump myapp.lua
--
-- The first argument specifies the dump mode. The second argument gives
-- the output file name. Default output is to stdout, unless the environment
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
-- module is started.
--
-- Different features can be turned on or off with the dump mode. If the
-- mode starts with a '+', the following features are added to the default
-- set of features; a '-' removes them. Otherwise the features are replaced.
--
-- The following dump features are available (* marks the default):
--
-- * t Print a line for each started, ended or aborted trace (see also -jv).
-- * b Dump the traced bytecode.
-- * i Dump the IR (intermediate representation).
-- r Augment the IR with register/stack slots.
-- s Dump the snapshot map.
-- * m Dump the generated machine code.
-- x Print each taken trace exit.
-- X Print each taken trace exit and the contents of all registers.
--
-- The output format can be set with the following characters:
--
-- T Plain text output.
-- A ANSI-colored text output
-- H Colorized HTML + CSS output.
--
-- The default output format is plain text. It's set to ANSI-colored text
-- if the COLORTERM variable is set. Note: this is independent of any output
-- redirection, which is actually considered a feature.
--
-- You probably want to use less -R to enjoy viewing ANSI-colored text from
-- a pipe or a file. Add this to your ~/.bashrc: export LESS="-R"
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
local tracemc, traceexitstub = jutil.tracemc, jutil.traceexitstub
local tracesnap = jutil.tracesnap
local bit = require("bit")
local band, shl, shr = bit.band, bit.lshift, bit.rshift
local sub, gsub, format = string.sub, string.gsub, string.format
local byte, char, rep = string.byte, string.char, string.rep
local type, tostring = type, tostring
local stdout, stderr = io.stdout, io.stderr
-- Load other modules on-demand.
local bcline, discreate
-- Active flag, output file handle and dump mode.
local active, out, dumpmode
------------------------------------------------------------------------------
local symtab = {}
local nexitsym = 0
-- Fill symbol table with trace exit addresses.
local function fillsymtab(nexit)
local t = symtab
if nexit > nexitsym then
for i=nexitsym,nexit-1 do t[traceexitstub(i)] = tostring(i) end
nexitsym = nexit
end
return t
end
local function dumpwrite(s)
out:write(s)
end
-- Disassemble machine code.
local function dump_mcode(tr)
local info = traceinfo(tr)
if not info then return end
local mcode, addr, loop = tracemc(tr)
if not mcode then return end
if not discreate then
discreate = require("jit.dis_"..jit.arch).create
end
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
local ctx = discreate(mcode, addr, dumpwrite)
ctx.hexdump = 0
ctx.symtab = fillsymtab(info.nexit)
if loop ~= 0 then
symtab[addr+loop] = "LOOP"
ctx:disass(0, loop)
out:write("->LOOP:\n")
ctx:disass(loop, #mcode-loop)
symtab[addr+loop] = nil
else
ctx:disass(0, #mcode)
end
end
------------------------------------------------------------------------------
local irtype_text = {
[0] = "nil",
"fal",
"tru",
"lud",
"str",
"ptr",
"thr",
"pro",
"fun",
"t09",
"tab",
"udt",
"num",
"int",
"i8 ",
"u8 ",
"i16",
"u16",
}
local colortype_ansi = {
[0] = "%s",
"%s",
"%s",
"%s",
"\027[32m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[1m%s\027[m",
"%s",
"\027[31m%s\027[m",
"\027[36m%s\027[m",
"\027[34m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
"\027[35m%s\027[m",
}
local function colorize_text(s, t)
return s
end
local function colorize_ansi(s, t)
return format(colortype_ansi[t], s)
end
local irtype_ansi = setmetatable({},
{ __index = function(tab, t)
local s = colorize_ansi(irtype_text[t], t); tab[t] = s; return s; end })
local html_escape = { ["<"] = "&lt;", [">"] = "&gt;", ["&"] = "&amp;", }
local function colorize_html(s, t)
s = gsub(s, "[<>&]", html_escape)
return format('<span class="irt_%s">%s</span>', irtype_text[t], s)
end
local irtype_html = setmetatable({},
{ __index = function(tab, t)
local s = colorize_html(irtype_text[t], t); tab[t] = s; return s; end })
local header_html = [[
<style type="text/css">
background { background: #ffffff; color: #000000; }
pre.ljdump {
font-size: 10pt;
background: #f0f4ff;
color: #000000;
border: 1px solid #bfcfff;
padding: 0.5em;
margin-left: 2em;
margin-right: 2em;
}
span.irt_str { color: #00a000; }
span.irt_thr, span.irt_fun { color: #404040; font-weight: bold; }
span.irt_tab { color: #c00000; }
span.irt_udt { color: #00c0c0; }
span.irt_num { color: #0000c0; }
span.irt_int { color: #c000c0; }
</style>
]]
local colorize, irtype
-- Lookup table to convert some literals into names.
local litname = {
["SLOAD "] = { [0] = "", "I", "R", "RI", "P", "PI", "PR", "PRI", },
["XLOAD "] = { [0] = "", "unaligned", },
["TOINT "] = { [0] = "check", "index", "", },
["FLOAD "] = vmdef.irfield,
["FREF "] = vmdef.irfield,
["FPMATH"] = vmdef.irfpm,
}
local function ctlsub(c)
if c == "\n" then return "\\n"
elseif c == "\r" then return "\\r"
elseif c == "\t" then return "\\t"
elseif c == "\r" then return "\\r"
else return format("\\%03d", byte(c))
end
end
local function formatk(tr, idx)
local k, t, slot = tracek(tr, idx)
local tn = type(k)
local s
if tn == "number" then
if k == 2^52+2^51 then
s = "bias"
else
s = format("%+.14g", k)
end
elseif tn == "string" then
s = format(#k > 20 and '"%.20s"~' or '"%s"', gsub(k, "%c", ctlsub))
elseif tn == "function" then
local fi = funcinfo(k)
if fi.ffid then
s = vmdef.ffnames[fi.ffid]
else
s = fi.loc
end
elseif tn == "table" then
s = format("{%p}", k)
elseif tn == "userdata" then
if t == 11 then
s = format("userdata:%p", k)
else
s = format("[%p]", k)
if s == "[0x00000000]" then s = "NULL" end
end
else
s = tostring(k) -- For primitives.
end
s = colorize(format("%-4s", s), t)
if slot then
s = format("%s @%d", s, slot)
end
return s
end
local function printsnap(tr, snap)
for i=1,#snap do
local ref = snap[i]
if not ref then
out:write("---- ")
elseif ref < 0 then
out:write(formatk(tr, ref), " ")
else
local m, ot, op1, op2 = traceir(tr, ref)
local t = band(ot, 15)
local sep = " "
if t == 8 then
local oidx = 6*shr(ot, 8)
local op = sub(vmdef.irnames, oidx+1, oidx+6)
if op == "FRAME " then
sep = "|"
end
end
out:write(colorize(format("%04d", ref), t), sep)
end
end
out:write("]\n")
end
-- Dump snapshots (not interleaved with IR).
local function dump_snap(tr)
out:write("---- TRACE ", tr, " snapshots\n")
for i=0,1000000000 do
local snap = tracesnap(tr, i)
if not snap then break end
out:write(format("#%-3d %04d [ ", i, snap[0]))
printsnap(tr, snap)
end
end
-- NYI: should really get the register map from the disassembler.
local reg_map = {
[0] = "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
}
-- Return a register name or stack slot for a rid/sp location.
local function ridsp_name(ridsp)
local rid = band(ridsp, 0xff)
if ridsp > 255 then return format("[%x]", shr(ridsp, 8)*4) end
if rid < 128 then return reg_map[rid] end
return ""
end
-- Dump IR and interleaved snapshots.
local function dump_ir(tr, dumpsnap, dumpreg)
local info = traceinfo(tr)
if not info then return end
local nins = info.nins
out:write("---- TRACE ", tr, " IR\n")
local irnames = vmdef.irnames
local snapref = 65536
local snap, snapno
if dumpsnap then
snap = tracesnap(tr, 0)
snapref = snap[0]
snapno = 0
end
for ins=1,nins do
if ins >= snapref then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
snapno = snapno + 1
snap = tracesnap(tr, snapno)
snapref = snap and snap[0] or 65536
end
local m, ot, op1, op2, ridsp = traceir(tr, ins)
local oidx, t = 6*shr(ot, 8), band(ot, 31)
local op = sub(irnames, oidx+1, oidx+6)
if op == "LOOP " then
if dumpreg then
out:write(format("%04d ------------ LOOP ------------\n", ins))
else
out:write(format("%04d ------ LOOP ------------\n", ins))
end
elseif op ~= "NOP " and (dumpreg or op ~= "RENAME") then
if dumpreg then
out:write(format("%04d %-5s ", ins, ridsp_name(ridsp)))
else
out:write(format("%04d ", ins))
end
out:write(format("%s%s %s %s ",
band(ot, 64) == 0 and " " or ">",
band(ot, 128) == 0 and " " or "+",
irtype[t], op))
local m1 = band(m, 3)
if m1 ~= 3 then -- op1 != IRMnone
if op1 < 0 then
out:write(formatk(tr, op1))
else
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
end
local m2 = band(m, 3*4)
if m2 ~= 3*4 then -- op2 != IRMnone
if m2 == 1*4 then -- op2 == IRMlit
local litn = litname[op]
if litn and litn[op2] then
out:write(" ", litn[op2])
else
out:write(format(" #%-3d", op2))
end
elseif op2 < 0 then
out:write(" ", formatk(tr, op2))
else
out:write(format(" %04d", op2))
end
end
end
out:write("\n")
end
end
if snap then
if dumpreg then
out:write(format(".... SNAP #%-3d [ ", snapno))
else
out:write(format(".... SNAP #%-3d [ ", snapno))
end
printsnap(tr, snap)
end
end
------------------------------------------------------------------------------
local recprefix = ""
local recdepth = 0
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then
local fi = funcinfo(info)
if fi.ffid then
info = vmdef.ffnames[fi.ffid]
else
info = fi.loc
end
end
err = format(vmdef.traceerr[err], info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "stop" or (what == "abort" and dumpmode.a) then
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
elseif dumpmode.s then dump_snap(tr) end
if dumpmode.m then dump_mcode(tr) end
end
if what == "start" then
if dumpmode.H then out:write('<pre class="ljdump">\n') end
out:write("---- TRACE ", tr, " ", what)
if otr then out:write(" ", otr, "/", oex) end
local fi = funcinfo(func, pc)
out:write(" ", fi.loc, "\n")
recprefix = ""
reclevel = 0
elseif what == "stop" or what == "abort" then
out:write("---- TRACE ", tr, " ", what)
recprefix = nil
if what == "abort" then
local fi = funcinfo(func, pc)
out:write(" ", fi.loc, " -- ", fmterr(otr, oex), "\n")
else
local link = traceinfo(tr).link
if link == tr then
link = "loop"
elseif link == 0 then
link = "interpreter"
end
out:write(" -> ", link, "\n")
end
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
else
out:write("---- TRACE ", what, "\n\n")
end
out:flush()
end
-- Dump recorded bytecode.
local function dump_record(tr, func, pc, depth, callee)
if depth ~= recdepth then
recdepth = depth
recprefix = rep(" .", depth)
end
local line = bcline(func, pc, recprefix)
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
if type(callee) == "function" then
local fi = funcinfo(callee)
if fi.ffid then
out:write(sub(line, 1, -2), " ; ", vmdef.ffnames[fi.ffid], "\n")
else
out:write(sub(line, 1, -2), " ; ", fi.loc, "\n")
end
else
out:write(line)
end
if band(funcbc(func, pc), 0xff) < 16 then -- Write JMP for cond. ORDER BC
out:write(bcline(func, pc+1, recprefix))
end
end
------------------------------------------------------------------------------
-- Dump taken trace exits.
local function dump_texit(tr, ex, ngpr, nfpr, ...)
out:write("---- TRACE ", tr, " exit ", ex, "\n")
if dumpmode.X then
local regs = {...}
for i=1,ngpr do
out:write(format(" %08x", regs[i]))
if i % 8 == 0 then out:write("\n") end
end
for i=1,nfpr do
out:write(format(" %+17.14g", regs[ngpr+i]))
if i % 4 == 0 then out:write("\n") end
end
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_texit)
jit.attach(dump_record)
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(opt, outfile)
if active then dumpoff() end
local colormode = os.getenv("COLORTERM") and "A" or "T"
if opt then
opt = gsub(opt, "[TAH]", function(mode) colormode = mode; return ""; end)
end
local m = { t=true, b=true, i=true, m=true, }
if opt and opt ~= "" then
local o = sub(opt, 1, 1)
if o ~= "+" and o ~= "-" then m = {} end
for i=1,#opt do m[sub(opt, i, i)] = (o ~= "-") end
end
dumpmode = m
if m.t or m.b or m.i or m.s or m.m then
jit.attach(dump_trace, "trace")
end
if m.b then
jit.attach(dump_record, "record")
if not bcline then bcline = require("jit.bc").line end
end
if m.x or m.X then
jit.attach(dump_texit, "texit")
end
if not outfile then outfile = os.getenv("LUAJIT_DUMPFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stdout
end
m[colormode] = true
if colormode == "A" then
colorize = colorize_ansi
irtype = irtype_ansi
elseif colormode == "H" then
colorize = colorize_html
irtype = irtype_html
out:write(header_html)
else
colorize = colorize_text
irtype = irtype_text
end
active = true
end
-- Public module functions.
module(...)
on = dumpon
off = dumpoff
start = dumpon -- For -j command line option.

156
lib/v.lua Normal file
View File

@ -0,0 +1,156 @@
----------------------------------------------------------------------------
-- Verbose mode of the LuaJIT compiler.
--
-- Copyright (C) 2005-2009 Mike Pall. All rights reserved.
-- Released under the MIT/X license. See Copyright Notice in luajit.h
----------------------------------------------------------------------------
--
-- This module shows verbose information about the progress of the
-- JIT compiler. It prints one line for each generated trace. This module
-- is useful to see which code has been compiled or where the compiler
-- punts and falls back to the interpreter.
--
-- Example usage:
--
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
-- luajit -jv=myapp.out myapp.lua
--
-- Default output is to stderr. To redirect the output to a file, pass a
-- filename as an argument (use '-' for stdout) or set the environment
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
-- module is started.
--
-- The output from the first example should look like this:
--
-- [TRACE 1 (command line):1]
-- [TRACE 2 (1/3) (command line):1 -> 1]
--
-- The first number in each line is the internal trace number. Next are
-- the file name ('(command line)') and the line number (':1') where the
-- trace has started. Side traces also show the parent trace number and
-- the exit number where they are attached to in parentheses ('(1/3)').
-- An arrow at the end shows where the trace links to ('-> 1'), unless
-- it loops to itself.
--
-- In this case the inner loop gets hot and is traced first, generating
-- a root trace. Then the last exit from the 1st trace gets hot, too,
-- and triggers generation of the 2nd trace. The side trace follows the
-- path along the outer loop and *around* the inner loop, back to its
-- start, and then links to the 1st trace. Yes, this may seem unusual,
-- if you know how traditional compilers work. Trace compilers are full
-- of surprises like this -- have fun! :-)
--
-- Aborted traces are shown like this:
--
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
--
-- Don't worry -- trace aborts are quite common, even in programs which
-- can be fully compiled. The compiler may retry several times until it
-- finds a suitable trace.
--
-- Of course this doesn't work with features that are not-yet-implemented
-- (NYI error messages). The VM simply falls back to the interpreter. This
-- may not matter at all if the particular trace is not very high up in
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
--
-- Also check out the -jdump module, which prints all the gory details.
--
------------------------------------------------------------------------------
-- Cache some library functions and objects.
local jit = require("jit")
assert(jit.version_num == 20000, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef")
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
local type, format = type, string.format
local stdout, stderr = io.stdout, io.stderr
-- Active flag and output file handle.
local active, out
------------------------------------------------------------------------------
local startloc, startex
-- Format trace error message.
local function fmterr(err, info)
if type(err) == "number" then
if type(info) == "function" then
local fi = funcinfo(info)
if fi.ffid then
info = vmdef.ffnames[fi.ffid]
else
info = fi.loc
end
end
err = format(vmdef.traceerr[err], info)
end
return err
end
-- Dump trace states.
local function dump_trace(what, tr, func, pc, otr, oex)
if what == "start" then
startloc = funcinfo(func, pc).loc
startex = otr and "("..otr.."/"..oex..") " or ""
else
if what == "abort" then
local loc = funcinfo(func, pc).loc
if loc ~= startloc then
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
startex, startloc, fmterr(otr, oex), loc))
else
out:write(format("[TRACE --- %s%s -- %s]\n",
startex, startloc, fmterr(otr, oex)))
end
elseif what == "stop" then
local link = traceinfo(tr).link
if link == 0 then
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
tr, startex, startloc))
elseif link == tr then
out:write(format("[TRACE %3s %s%s]\n", tr, startex, startloc))
else
out:write(format("[TRACE %3s %s%s -> %d]\n",
tr, startex, startloc, link))
end
else
out:write(format("[TRACE %s]\n", what))
end
out:flush()
end
end
------------------------------------------------------------------------------
-- Detach dump handlers.
local function dumpoff()
if active then
active = false
jit.attach(dump_trace)
if out and out ~= stdout and out ~= stderr then out:close() end
out = nil
end
end
-- Open the output file and attach dump handlers.
local function dumpon(outfile)
if active then dumpoff() end
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
if outfile then
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
else
out = stderr
end
jit.attach(dump_trace, "trace")
active = true
end
-- Public module functions.
module(...)
on = dumpon
off = dumpoff
start = dumpon -- For -j command line option.

8
src/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
luajit
buildvm
buildvm_*.h
lj_ffdef.h
lj_libdef.h
lj_recdef.h
lj_folddef.h
lj_vm.s

326
src/Makefile Normal file
View File

@ -0,0 +1,326 @@
##############################################################################
# LuaJIT Makefile. Requires GNU Make.
#
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
# Also works with MinGW and Cygwin on Windows.
# Please check msvcbuild.bat for building with MSVC on Windows.
#
# Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
##############################################################################
##############################################################################
# Compiler options: change them as needed. This mainly affects the speed of
# the JIT compiler itself, not the speed of the JIT compiled code.
# Turn any of the optional settings on by removing the '#' in front of them.
#
# Note: LuaJIT can only be compiled for x86, and not for x64 (yet)!
# In the meantime, the x86 binary runs fine under a x64 OS.
#
# It's recommended to compile at least for i686. By default the assembler part
# of the interpreter makes use of CMOV/FCOMI*/FUCOMI* instructions, anyway.
CC= gcc -m32 -march=i686
# Use this for GCC 4.2 or higher if you don't intend to distribute the
# binaries to a different machine:
#CC= gcc -m32 -march=native
#
# Since the assembler part does NOT maintain a frame pointer, it's pointless
# to slow down the C part by not omitting it. Debugging and tracebacks are
# not affected -- the assembler part has frame unwind information and GCC
# emits it with -g (see CCDEBUG below).
CCOPT= -O2 -fomit-frame-pointer
# Use this if you want to generate a smaller binary (but it's slower):
#CCOPT= -Os -fomit-frame-pointer
# Note: it's no longer recommended to use -O3 with GCC 4.x.
# The I-Cache bloat usually outweighs the benefits from aggressive inlining.
#
CCDEBUG=
# Uncomment the next line to generate debug information:
#CCDEBUG= -g
#
CCWARN= -Wall
# Uncomment the next line to enable more warnings:
#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith
#
##############################################################################
##############################################################################
# Compile time definitions: change them as needed, but make sure you force
# a full recompile with "make clean", followed by "make".
# Note that most of these are NOT suitable for benchmarking or release mode!
XCFLAGS=
#
# Disable the use of CMOV and FCOMI*/FUCOMI* instructions in the interpreter.
# This is only necessary if you intend to run the code on REALLY ANCIENT CPUs
# (before Pentium Pro, or on the VIA C3). This generally slows down the
# interpreter. Don't bother if your OS wouldn't run on them, anyway.
#XCFLAGS+= -DLUAJIT_CPU_NOCMOV
#
# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter:
#XCFLAGS+= -DLUAJIT_DISABLE_JIT
#
# Use the system provided memory allocator (realloc) instead of the
# bundled memory allocator. This is slower, but sometimes helpful for
# debugging. It's mandatory for Valgrind's memcheck tool, too.
#XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
#
# This define is required to run LuaJIT under Valgrind. The Valgrind
# header files must be installed. You should enable debug information, too.
#XCFLAGS+= -DLUAJIT_USE_VALGRIND
#
# This is the client for the GDB JIT API. GDB 7.0 or higher is required
# to make use of it. See lj_gdbjit.c for details. Enabling this causes
# a non-negligible overhead, even when not running under GDB.
#XCFLAGS+= -DLUAJIT_USE_GDBJIT
#
# Turn on assertions for the Lua/C API to debug problems with lua_* calls.
# This is rather slow -- use only while developing C libraries/embeddings.
#XCFLAGS+= -DLUA_USE_APICHECK
#
# Turn on assertions for the whole LuaJIT VM. This significantly slows down
# everything. Use only if you suspect a problem with LuaJIT itself.
#XCFLAGS+= -DLUA_USE_ASSERT
#
##############################################################################
# You probably don't need to change anything below this line.
##############################################################################
CCOPTIONS= $(CCDEBUG) $(CCOPT) $(CCWARN) $(CFLAGS) $(XCFLAGS)
LDOPTIONS= $(CCDEBUG) $(LDFLAGS)
HOST_CC= $(CC)
HOST_RM= rm -f
HOST_XCFLAGS=
HOST_XLDFLAGS=
HOST_XLIBS=
TARGET_CC= $(CC)
TARGET_STRIP= strip
TARGET_XCFLAGS= -D_FILE_OFFSET_BITS=64
TARGET_XLDFLAGS=
TARGET_XSHLDFLAGS= -shared
TARGET_XLIBS=
TARGET_ARCH= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET))
TARGET_DISABLE= -U_FORTIFY_SOURCE
ifneq (,$(findstring stack-protector,$(shell $(CC) -dumpspecs)))
TARGET_DISABLE+= -fno-stack-protector
endif
ifneq (,$(findstring Windows,$(OS)))
TARGET_SYS= Windows
else
TARGET_SYS:= $(shell uname -s)
ifneq (,$(findstring CYGWIN,$(TARGET_SYS)))
TARGET_SYS= Windows
endif
endif
ifeq (Linux,$(TARGET_SYS))
TARGET_XLIBS= -ldl
TARGET_XLDFLAGS= -Wl,-E
else
ifeq (Windows,$(TARGET_SYS))
HOST_RM= del
TARGET_STRIP= strip --strip-unneeded
else
ifeq (Darwin,$(TARGET_SYS))
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup
TARGET_STRIP= strip -x
export MACOSX_DEPLOYMENT_TARGET=10.3
else
TARGET_XLDFLAGS= -Wl,-E
endif
endif
endif
# NOTE: The LuaJIT distribution comes with a pre-generated buildvm_*.h.
# You DO NOT NEED an installed copy of (plain) Lua 5.1 to run DynASM unless
# you want to MODIFY the corresponding *.dasc file. You can also use LuaJIT
# itself (bootstrapped from the pre-generated file) to run DynASM of course.
DASM_LUA= lua
Q= @
E= @echo
#Q=
#E= @:
##############################################################################
TARGET_CFLAGS= $(CCOPTIONS) $(TARGET_DISABLE) $(TARGET_XCFLAGS)
TARGET_LDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS)
TARGET_SHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS)
TARGET_LIBS= -lm $(TARGET_XLIBS)
ifneq (,$(CCDEBUG))
TARGET_STRIP= @:
endif
HOST_CFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH)
HOST_LDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS)
HOST_LIBS= $(HOST_XLIBS)
DASM_DIR= ../dynasm
DASM= $(DASM_LUA) $(DASM_DIR)/dynasm.lua
DASM_FLAGS=
DASM_DISTFLAGS= -LN
BUILDVM_O= buildvm.o buildvm_asm.o buildvm_peobj.o buildvm_lib.o buildvm_fold.o
BUILDVM_T= buildvm
HOST_O= $(BUILDVM_O)
HOST_T= $(BUILDVM_T)
LJVM_S= lj_vm.s
LJVM_O= lj_vm.o
LJVM_BOUT= $(LJVM_S)
LJVM_MODE= asm
LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \
lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o
LJLIB_C= $(LJLIB_O:.o=.c)
LJCORE_O= lj_gc.o lj_err.o lj_ctype.o lj_bc.o lj_obj.o \
lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o \
lj_state.o lj_dispatch.o lj_vmevent.o lj_api.o \
lj_lex.o lj_parse.o \
lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
lj_opt_dce.o lj_opt_loop.o \
lj_mcode.o lj_snap.o lj_record.o lj_asm.o lj_trace.o lj_gdbjit.o \
lj_lib.o lj_alloc.o lib_aux.o \
$(LJLIB_O) lib_init.o
LJVMCORE_O= $(LJVM_O) $(LJCORE_O)
# NYI: Need complete support for building as a shared library on POSIX.
# This is currently *only* suitable for MinGW and Cygwin, see below.
LUAJIT_O= luajit.o
LUAJIT_SO= luajit.so
LUAJIT_T= luajit
LIB_VMDEF= ../lib/vmdef.lua
TARGET_DEP= $(LIB_VMDEF)
TARGET_O= $(LJVMCORE_O) $(LUAJIT_O)
TARGET_T= $(LUAJIT_T)
ALL_GEN= $(LJVM_S) lj_ffdef.h lj_libdef.h lj_recdef.h $(LIB_VMDEF) lj_folddef.h
ALL_DYNGEN= buildvm_x86.h
WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest
ALL_RM= $(LUAJIT_T) $(LUAJIT_SO) $(HOST_T) $(ALL_GEN) *.o $(WIN_RM)
ifeq (Windows,$(TARGET_SYS))
LJVM_BOUT= $(LJVM_O)
LJVM_MODE= peobj
LIB_VMDEF= ..\lib\vmdef.lua
# Imported symbols are bound to a specific DLL name under Windows.
LUAJIT_SO= lua51.dll
LUAJIT_T= luajit.exe
BUILDVM_T= buildvm.exe
#
# You can comment out the following two lines to build a static executable.
# But then you won't be able to dynamically load any C modules, because
# they bind to lua51.dll.
#
TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL
TARGET_O= $(LUAJIT_SO) $(LUAJIT_O)
endif
##############################################################################
default: $(TARGET_T)
all: $(TARGET_T)
amalg:
@grep "^[+|]" ljamalg.c
$(MAKE) all "LJCORE_O=ljamalg.o"
MAKE_TARGETS= amalg
##############################################################################
buildvm_x86.h: buildvm_x86.dasc
$(E) "DYNASM $@"
$(Q)$(DASM) $(DASM_FLAGS) -o $@ buildvm_x86.dasc
$(BUILDVM_T): $(BUILDVM_O)
$(E) "HOSTLINK $@"
$(Q)$(HOST_CC) $(HOST_LDFLAGS) -o $@ $(BUILDVM_O) $(HOST_LIBS)
$(LJVM_BOUT): $(BUILDVM_T)
$(E) "BUILDVM $@"
$(Q)./$(BUILDVM_T) -m $(LJVM_MODE) -o $@
lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C)
$(E) "BUILDVM $@"
$(Q)./$(BUILDVM_T) -m ffdef -o $@ $(LJLIB_C)
lj_libdef.h: $(BUILDVM_T) $(LJLIB_C)
$(E) "BUILDVM $@"
$(Q)./$(BUILDVM_T) -m libdef -o $@ $(LJLIB_C)
lj_recdef.h: $(BUILDVM_T) $(LJLIB_C)
$(E) "BUILDVM $@"
$(Q)./$(BUILDVM_T) -m recdef -o $@ $(LJLIB_C)
$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C)
$(E) "BUILDVM $@"
$(Q)./$(BUILDVM_T) -m vmdef -o $@ $(LJLIB_C)
lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c
$(E) "BUILDVM $@"
$(Q)./$(BUILDVM_T) -m folddef -o $@ lj_opt_fold.c
$(LUAJIT_SO): $(LJVMCORE_O)
$(E) "LINK $@"
$(Q)$(TARGET_CC) $(TARGET_SHLDFLAGS) -o $@ $(LJVMCORE_O) $(TARGET_LIBS)
$(Q)$(TARGET_STRIP) $@
$(LUAJIT_T): $(TARGET_O) $(TARGET_DEP)
$(E) "LINK $@"
$(Q)$(TARGET_CC) $(TARGET_LDFLAGS) -o $@ $(TARGET_O) $(TARGET_LIBS)
$(Q)$(TARGET_STRIP) $@
$(E) "OK Successfully built LuaJIT"
##############################################################################
%.o: %.c
$(E) "CC $@"
$(Q)$(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ $<
%.o: %.s
$(E) "ASM $@"
$(Q)$(TARGET_CC) $(TARGET_CFLAGS) -c -o $@ $<
$(HOST_O): %.o: %.c
$(E) "HOSTCC $@"
$(Q)$(HOST_CC) $(HOST_CFLAGS) -c -o $@ $<
include Makefile.dep
##############################################################################
clean:
$(HOST_RM) $(ALL_RM)
cleaner: clean
$(HOST_RM) $(ALL_DYNGEN)
distclean: clean
$(E) "DYNASM $@"
$(Q)$(DASM) $(DASM_DISTFLAGS) -o buildvm_x86.h buildvm_x86.dasc
depend:
@test -f lj_ffdef.h || touch lj_ffdef.h
@test -f lj_libdef.h || touch lj_libdef.h
@test -f lj_recdef.h || touch lj_recdef.h
@test -f lj_folddef.h || touch lj_folddef.h
@test -f buildvm_x86.h || touch buildvm_x86.h
@$(HOST_CC) $(HOST_CFLAGS) -MM *.c | sed "s|$(DASM_DIR)|\$$(DASM_DIR)|g" >Makefile.dep
@test -s lj_ffdef.h || $(HOST_RM) lj_ffdef.h
@test -s lj_libdef.h || $(HOST_RM) lj_libdef.h
@test -s lj_recdef.h || $(HOST_RM) lj_recdef.h
@test -s lj_folddef.h || $(HOST_RM) lj_folddef.h
@test -s buildvm_x86.h || $(HOST_RM) buildvm_x86.h
.PHONY: default all $(MAKE_TARGETS) clean cleaner distclean depend
##############################################################################

139
src/Makefile.dep Normal file
View File

@ -0,0 +1,139 @@
buildvm.o: buildvm.c lua.h luaconf.h luajit.h lj_obj.h lj_def.h lj_arch.h \
lj_gc.h lj_bc.h lj_ir.h lj_frame.h lj_dispatch.h lj_jit.h lj_target.h \
lj_target_x86.h buildvm.h $(DASM_DIR)/dasm_proto.h $(DASM_DIR)/dasm_x86.h \
buildvm_x86.h lj_traceerr.h
buildvm_asm.o: buildvm_asm.c buildvm.h lj_def.h lua.h luaconf.h lj_arch.h \
lj_bc.h
buildvm_fold.o: buildvm_fold.c lj_obj.h lua.h luaconf.h lj_def.h \
lj_arch.h lj_ir.h buildvm.h
buildvm_lib.o: buildvm_lib.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_lib.h buildvm.h
buildvm_peobj.o: buildvm_peobj.c buildvm.h lj_def.h lua.h luaconf.h \
lj_arch.h lj_bc.h
lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_alloc.h
lib_base.o: lib_base.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h \
lj_meta.h lj_state.h lj_ff.h lj_ffdef.h lj_ctype.h lj_lib.h lj_libdef.h
lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_lib.h lj_libdef.h
lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h
lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h
lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
lj_arch.h lj_err.h lj_errmsg.h lj_gc.h lj_ff.h lj_ffdef.h lj_lib.h \
lj_libdef.h
lib_jit.o: lib_jit.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h \
lj_obj.h lj_def.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ir.h \
lj_jit.h lj_iropt.h lj_dispatch.h lj_bc.h lj_vm.h lj_vmevent.h lj_lib.h \
luajit.h lj_libdef.h
lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_lib.h lj_libdef.h
lib_os.o: lib_os.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h
lib_package.o: lib_package.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h
lib_string.o: lib_string.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_state.h \
lj_ff.h lj_ffdef.h lj_ctype.h lj_lib.h lj_libdef.h
lib_table.o: lib_table.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_lib.h \
lj_libdef.h
lj_alloc.o: lj_alloc.c lj_def.h lua.h luaconf.h lj_arch.h lj_alloc.h
lj_api.o: lj_api.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h \
lj_state.h lj_frame.h lj_bc.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
lj_traceerr.h lj_vm.h lj_lex.h lj_parse.h
lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_str.h lj_tab.h lj_ir.h lj_jit.h lj_iropt.h lj_mcode.h lj_trace.h \
lj_dispatch.h lj_bc.h lj_traceerr.h lj_snap.h lj_asm.h lj_vm.h \
lj_target.h lj_target_x86.h
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h
lj_ctype.o: lj_ctype.c lj_ctype.h lj_def.h lua.h luaconf.h
lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_err.h lj_errmsg.h lj_state.h lj_frame.h lj_bc.h lj_jit.h lj_ir.h \
lj_trace.h lj_dispatch.h lj_traceerr.h lj_vm.h luajit.h
lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \
lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_state.h lj_frame.h lj_bc.h \
lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h
lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
lj_traceerr.h lj_vm.h
lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h \
lj_state.h lj_frame.h lj_bc.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
lj_traceerr.h lj_vm.h
lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_frame.h lj_bc.h lj_jit.h \
lj_ir.h lj_dispatch.h
lj_ir.o: lj_ir.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_str.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h lj_bc.h \
lj_traceerr.h
lj_lex.o: lj_lex.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_str.h lj_lex.h lj_parse.h lj_ctype.h
lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_vm.h \
lj_lib.h
lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h lj_dispatch.h lj_bc.h \
lj_traceerr.h
lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_bc.h lj_vm.h
lj_obj.o: lj_obj.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h
lj_opt_dce.o: lj_opt_dce.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_ir.h lj_jit.h lj_iropt.h
lj_opt_fold.o: lj_opt_fold.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_str.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h lj_bc.h \
lj_traceerr.h lj_vm.h lj_folddef.h
lj_opt_loop.o: lj_opt_loop.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ir.h lj_jit.h lj_iropt.h \
lj_trace.h lj_dispatch.h lj_bc.h lj_traceerr.h lj_snap.h lj_vm.h
lj_opt_mem.o: lj_opt_mem.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_tab.h lj_ir.h lj_jit.h lj_iropt.h
lj_opt_narrow.o: lj_opt_narrow.c lj_obj.h lua.h luaconf.h lj_def.h \
lj_arch.h lj_str.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \
lj_dispatch.h lj_traceerr.h
lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_state.h \
lj_bc.h lj_lex.h lj_parse.h lj_vm.h lj_vmevent.h
lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_state.h lj_frame.h \
lj_bc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \
lj_dispatch.h lj_traceerr.h lj_record.h lj_snap.h lj_asm.h lj_vm.h \
lj_recdef.h
lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h \
lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h lj_target_x86.h
lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_meta.h \
lj_state.h lj_frame.h lj_bc.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h
lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_str.h lj_state.h lj_ctype.h
lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_err.h lj_errmsg.h lj_tab.h
lj_trace.o: lj_trace.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_frame.h lj_bc.h lj_state.h \
lj_ir.h lj_jit.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h \
lj_traceerr.h lj_snap.h lj_gdbjit.h lj_record.h lj_asm.h lj_vm.h \
lj_vmevent.h lj_target.h lj_target_x86.h
lj_udata.o: lj_udata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_udata.h
lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_str.h lj_tab.h lj_state.h lj_dispatch.h lj_bc.h lj_jit.h lj_ir.h \
lj_vm.h lj_vmevent.h
ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h \
lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_trace.h lj_jit.h \
lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_err.c lj_ctype.c \
lj_ctype.h lj_bc.c lj_obj.c lj_str.c lj_tab.c lj_func.c lj_udata.c \
lj_meta.c lj_state.c lj_lex.h lj_alloc.h lj_dispatch.c luajit.h \
lj_vmevent.c lj_vmevent.h lj_api.c lj_parse.h lj_lex.c lj_parse.c \
lj_lib.c lj_lib.h lj_ir.c lj_iropt.h lj_opt_mem.c lj_opt_fold.c \
lj_folddef.h lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h \
lj_mcode.c lj_mcode.h lj_snap.c lj_target.h lj_target_x86.h lj_record.c \
lj_ff.h lj_ffdef.h lj_record.h lj_asm.h lj_recdef.h lj_asm.c lj_trace.c \
lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c lib_base.c lualib.h \
lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c lib_os.c \
lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_init.c
luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h

438
src/buildvm.c Normal file
View File

@ -0,0 +1,438 @@
/*
** LuaJIT VM builder.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** This is a tool to build the hand-tuned assembler code required for
** LuaJIT's bytecode interpreter. It supports a variety of output formats
** to feed different toolchains (see usage() below).
**
** This tool is not particularly optimized because it's only used while
** _building_ LuaJIT. There's no point in distributing or installing it.
** Only the object code generated by this tool is linked into LuaJIT.
**
** Caveat: some memory is not free'd, error handling is lazy.
** It's a one-shot tool -- any effort fixing this would be wasted.
*/
#include "lua.h"
#include "luajit.h"
#ifdef LUA_USE_WIN
#include <fcntl.h>
#include <io.h>
#endif
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_bc.h"
#include "lj_ir.h"
#include "lj_frame.h"
#include "lj_dispatch.h"
#include "lj_target.h"
#include "buildvm.h"
/* ------------------------------------------------------------------------ */
/* DynASM glue definitions. */
#define Dst ctx
#define Dst_DECL BuildCtx *ctx
#define Dst_REF (ctx->D)
#include "../dynasm/dasm_proto.h"
/* Glue macros for DynASM. */
#define DASM_M_GROW(ctx, t, p, sz, need) \
do { \
size_t _sz = (sz), _need = (need); \
if (_sz < _need) { \
if (_sz < 16) _sz = 16; \
while (_sz < _need) _sz += _sz; \
(p) = (t *)realloc((p), _sz); \
if ((p) == NULL) exit(1); \
(sz) = _sz; \
} \
} while(0)
#define DASM_M_FREE(ctx, p, sz) free(p)
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type);
#define DASM_EXTERN(ctx, addr, idx, type) \
collect_reloc(ctx, addr, idx, type)
/* ------------------------------------------------------------------------ */
/* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */
#define DASM_ALIGNED_WRITES 1
/* Embed architecture-specific DynASM encoder and backend. */
#if LJ_TARGET_X86
#include "../dynasm/dasm_x86.h"
#include "buildvm_x86.h"
#else
#error "No support for this architecture (yet)"
#endif
/* ------------------------------------------------------------------------ */
void owrite(BuildCtx *ctx, const void *ptr, size_t sz)
{
if (fwrite(ptr, 1, sz, ctx->fp) != sz) {
fprintf(stderr, "Error: cannot write to output file: %s\n",
strerror(errno));
exit(1);
}
}
/* ------------------------------------------------------------------------ */
/* Emit code as raw bytes. Only used for DynASM debugging. */
static void emit_raw(BuildCtx *ctx)
{
owrite(ctx, ctx->code, ctx->codesz);
}
/* -- Build machine code -------------------------------------------------- */
/* Collect external relocations. */
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type)
{
if (ctx->nreloc >= BUILD_MAX_RELOC) {
fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n");
exit(1);
}
ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code);
ctx->reloc[ctx->nreloc].sym = idx;
ctx->reloc[ctx->nreloc].type = type;
ctx->nreloc++;
return 0; /* Encode symbol offset of 0. */
}
/* Naive insertion sort. Performance doesn't matter here. */
static void perm_insert(int *perm, int32_t *ofs, int i)
{
perm[i] = i;
while (i > 0) {
int a = perm[i-1];
int b = perm[i];
if (ofs[a] <= ofs[b]) break;
perm[i] = a;
perm[i-1] = b;
i--;
}
}
/* Build the machine code. */
static int build_code(BuildCtx *ctx)
{
int status;
int i, j;
/* Initialize DynASM structures. */
ctx->nglob = GLOB__MAX;
ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *));
memset(ctx->glob, 0, ctx->nglob*sizeof(void *));
ctx->nreloc = 0;
ctx->extnames = extnames;
ctx->globnames = globnames;
ctx->dasm_ident = DASM_IDENT;
ctx->dasm_arch = DASM_ARCH;
dasm_init(Dst, DASM_MAXSECTION);
dasm_setupglobal(Dst, ctx->glob, ctx->nglob);
dasm_setup(Dst, build_actionlist);
/* Call arch-specific backend to emit the code. */
ctx->npc = build_backend(ctx);
/* Finalize the code. */
(void)dasm_checkstep(Dst, DASM_SECTION_CODE);
if ((status = dasm_link(Dst, &ctx->codesz))) return status;
ctx->code = (uint8_t *)malloc(ctx->codesz);
if ((status = dasm_encode(Dst, (void *)ctx->code))) return status;
/* Allocate the symbol offset and permutation tables. */
ctx->nsym = ctx->npc + ctx->nglob;
ctx->perm = (int *)malloc((ctx->nsym+1)*sizeof(int *));
ctx->sym_ofs = (int32_t *)malloc((ctx->nsym+1)*sizeof(int32_t));
/* Collect the opcodes (PC labels). */
for (i = 0; i < ctx->npc; i++) {
int32_t n = dasm_getpclabel(Dst, i);
if (n < 0) return 0x22000000|i;
ctx->sym_ofs[i] = n;
perm_insert(ctx->perm, ctx->sym_ofs, i);
}
/* Collect the globals (named labels). */
for (j = 0; j < ctx->nglob; j++, i++) {
const char *gl = globnames[j];
int len = (int)strlen(gl);
if (!ctx->glob[j]) {
fprintf(stderr, "Error: undefined global %s\n", gl);
exit(2);
}
if (len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z')
ctx->sym_ofs[i] = -1; /* Skip the _Z symbols. */
else
ctx->sym_ofs[i] = (int32_t)((uint8_t *)(ctx->glob[j]) - ctx->code);
perm_insert(ctx->perm, ctx->sym_ofs, i);
}
/* Close the address range. */
ctx->sym_ofs[i] = (int32_t)ctx->codesz;
perm_insert(ctx->perm, ctx->sym_ofs, i);
dasm_free(Dst);
return 0;
}
/* -- Generate VM enums --------------------------------------------------- */
const char *const bc_names[] = {
#define BCNAME(name, ma, mb, mc, mt) #name,
BCDEF(BCNAME)
#undef BCNAME
NULL
};
const char *const ir_names[] = {
#define IRNAME(name, m, m1, m2) #name,
IRDEF(IRNAME)
#undef IRNAME
NULL
};
const char *const irfpm_names[] = {
#define FPMNAME(name) #name,
IRFPMDEF(FPMNAME)
#undef FPMNAME
NULL
};
const char *const irfield_names[] = {
#define FLNAME(name, type, field) #name,
IRFLDEF(FLNAME)
#undef FLNAME
NULL
};
static const char *const trace_errors[] = {
#define TREDEF(name, msg) msg,
#include "lj_traceerr.h"
NULL
};
static const char *lower(char *buf, const char *s)
{
char *p = buf;
while (*s) {
*p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s;
s++;
}
*p = '\0';
return buf;
}
/* Emit VM definitions as Lua code for debug modules. */
static void emit_vmdef(BuildCtx *ctx)
{
char buf[80];
int i;
fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n");
fprintf(ctx->fp, "module(...)\n\n");
fprintf(ctx->fp, "bcnames = \"");
for (i = 0; bc_names[i]; i++) fprintf(ctx->fp, "%-6s", bc_names[i]);
fprintf(ctx->fp, "\"\n\n");
fprintf(ctx->fp, "irnames = \"");
for (i = 0; ir_names[i]; i++) fprintf(ctx->fp, "%-6s", ir_names[i]);
fprintf(ctx->fp, "\"\n\n");
fprintf(ctx->fp, "irfpm = { [0]=");
for (i = 0; irfpm_names[i]; i++)
fprintf(ctx->fp, "\"%s\", ", lower(buf, irfpm_names[i]));
fprintf(ctx->fp, "}\n\n");
fprintf(ctx->fp, "irfield = { [0]=");
for (i = 0; irfield_names[i]; i++) {
char *p;
lower(buf, irfield_names[i]);
p = strchr(buf, '_');
if (p) *p = '.';
fprintf(ctx->fp, "\"%s\", ", buf);
}
fprintf(ctx->fp, "}\n\n");
fprintf(ctx->fp, "traceerr = {\n[0]=");
for (i = 0; trace_errors[i]; i++)
fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]);
fprintf(ctx->fp, "}\n\n");
}
/* -- Argument parsing ---------------------------------------------------- */
/* Build mode names. */
static const char *const modenames[] = {
#define BUILDNAME(name) #name,
BUILDDEF(BUILDNAME)
#undef BUILDNAME
NULL
};
/* Print usage information and exit. */
static void usage(void)
{
int i;
fprintf(stderr, LUAJIT_VERSION " VM builder.\n");
fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n");
fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n");
fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n");
fprintf(stderr, "Available modes:\n");
for (i = 0; i < BUILD__MAX; i++)
fprintf(stderr, " %s\n", modenames[i]);
exit(1);
}
/* Parse the output mode name. */
static BuildMode parsemode(const char *mode)
{
int i;
for (i = 0; modenames[i]; i++)
if (!strcmp(mode, modenames[i]))
return (BuildMode)i;
usage();
return (BuildMode)-1;
}
/* Parse arguments. */
static void parseargs(BuildCtx *ctx, char **argv)
{
const char *a;
int i;
ctx->mode = (BuildMode)-1;
ctx->outname = "-";
for (i = 1; (a = argv[i]) != NULL; i++) {
if (a[0] != '-')
break;
switch (a[1]) {
case '-':
if (a[2]) goto err;
i++;
goto ok;
case '\0':
goto ok;
case 'm':
i++;
if (a[2] || argv[i] == NULL) goto err;
ctx->mode = parsemode(argv[i]);
break;
case 'o':
i++;
if (a[2] || argv[i] == NULL) goto err;
ctx->outname = argv[i];
break;
default: err:
usage();
break;
}
}
ok:
ctx->args = argv+i;
if (ctx->mode == (BuildMode)-1) goto err;
}
int main(int argc, char **argv)
{
BuildCtx ctx_;
BuildCtx *ctx = &ctx_;
int status, binmode;
UNUSED(argc);
parseargs(ctx, argv);
if ((status = build_code(ctx))) {
fprintf(stderr,"Error: DASM error %08x\n", status);
return 1;
}
switch (ctx->mode) {
#if LJ_TARGET_X86ORX64
case BUILD_peobj:
#endif
case BUILD_raw:
binmode = 1;
break;
default:
binmode = 0;
break;
}
if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') {
ctx->fp = stdout;
#ifdef LUA_USE_WIN
if (binmode)
_setmode(_fileno(stdout), _O_BINARY); /* Yuck. */
#endif
} else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) {
fprintf(stderr, "Error: cannot open output file '%s': %s\n",
ctx->outname, strerror(errno));
exit(1);
}
switch (ctx->mode) {
case BUILD_asm:
#if defined(__ELF__)
ctx->mode = BUILD_elfasm;
#elif defined(__MACH__)
ctx->mode = BUILD_machasm;
#else
fprintf(stderr,"Error: auto-guessing the system assembler failed\n");
return 1;
#endif
/* fallthrough */
case BUILD_elfasm:
case BUILD_coffasm:
case BUILD_machasm:
emit_asm(ctx);
emit_asm_debug(ctx);
break;
#if LJ_TARGET_X86ORX64
case BUILD_peobj:
emit_peobj(ctx);
break;
#endif
case BUILD_raw:
emit_raw(ctx);
break;
case BUILD_vmdef:
emit_vmdef(ctx);
/* fallthrough */
case BUILD_ffdef:
case BUILD_libdef:
case BUILD_recdef:
emit_lib(ctx);
break;
case BUILD_folddef:
emit_fold(ctx);
break;
default:
break;
}
fflush(ctx->fp);
if (ferror(ctx->fp)) {
fprintf(stderr, "Error: cannot write to output file: %s\n",
strerror(errno));
exit(1);
}
fclose(ctx->fp);
return 0;
}

106
src/buildvm.h Normal file
View File

@ -0,0 +1,106 @@
/*
** LuaJIT VM builder.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _BUILDVM_H
#define _BUILDVM_H
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "lj_def.h"
#include "lj_arch.h"
/* Hardcoded limits. Increase as needed. */
#define BUILD_MAX_RELOC 100 /* Max. number of relocations. */
#define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */
/* Prefix for scanned library definitions. */
#define LIBDEF_PREFIX "LJLIB_"
/* Prefix for scanned fold definitions. */
#define FOLDDEF_PREFIX "LJFOLD"
/* Prefixes for generated labels. */
#define LABEL_PREFIX "lj_"
#define LABEL_PREFIX_BC LABEL_PREFIX "BC_"
#define LABEL_PREFIX_FF LABEL_PREFIX "ff_"
#define LABEL_PREFIX_CF LABEL_PREFIX "cf_"
#define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_"
#define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_"
#define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_"
/* Extra labels. */
#define LABEL_ASM_BEGIN LABEL_PREFIX "vm_asm_begin"
#define LABEL_OP_OFS LABEL_PREFIX "vm_op_ofs"
/* Forward declaration. */
struct dasm_State;
/* Build modes. */
#if LJ_TARGET_X86ORX64
#define BUILDDEFX(_) _(peobj)
#else
#define BUILDDEFX(_)
#endif
#define BUILDDEF(_) \
_(asm) _(elfasm) _(coffasm) _(machasm) BUILDDEFX(_) _(raw) \
_(ffdef) _(libdef) _(recdef) _(vmdef) \
_(folddef)
typedef enum {
#define BUILDENUM(name) BUILD_##name,
BUILDDEF(BUILDENUM)
#undef BUILDENUM
BUILD__MAX
} BuildMode;
/* Code relocation. */
typedef struct BuildReloc {
int32_t ofs;
int sym;
int type;
} BuildReloc;
/* Build context structure. */
typedef struct BuildCtx {
/* DynASM state pointer. Should be first member. */
struct dasm_State *D;
/* Parsed command line. */
BuildMode mode;
FILE *fp;
const char *outname;
char **args;
/* Code and symbols generated by DynASM. */
uint8_t *code;
size_t codesz;
int npc, nglob, nsym, nreloc;
void **glob;
int *perm;
int32_t *sym_ofs;
/* Strings generated by DynASM. */
const char *const *extnames;
const char *const *globnames;
const char *dasm_ident;
const char *dasm_arch;
/* Relocations. */
BuildReloc reloc[BUILD_MAX_RELOC];
} BuildCtx;
extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz);
extern void emit_asm(BuildCtx *ctx);
extern void emit_peobj(BuildCtx *ctx);
extern void emit_lib(BuildCtx *ctx);
extern void emit_fold(BuildCtx *ctx);
extern const char *const bc_names[];
extern const char *const ir_names[];
extern const char *const irfpm_names[];
extern const char *const irfield_names[];
#endif

220
src/buildvm_asm.c Normal file
View File

@ -0,0 +1,220 @@
/*
** LuaJIT VM builder: Assembler source code emitter.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#include "buildvm.h"
#include "lj_bc.h"
/* ------------------------------------------------------------------------ */
/* Emit bytes piecewise as assembler text. */
static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n)
{
int i;
for (i = 0; i < n; i++) {
if ((i & 15) == 0)
fprintf(ctx->fp, "\t.byte %d", p[i]);
else
fprintf(ctx->fp, ",%d", p[i]);
if ((i & 15) == 15) putc('\n', ctx->fp);
}
if ((n & 15) != 0) putc('\n', ctx->fp);
}
/* Emit relocation */
static void emit_asm_reloc(BuildCtx *ctx, BuildReloc *r)
{
const char *sym = ctx->extnames[r->sym];
switch (ctx->mode) {
case BUILD_elfasm:
if (r->type)
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
else
fprintf(ctx->fp, "\t.long %s\n", sym);
break;
case BUILD_coffasm:
fprintf(ctx->fp, "\t.def _%s; .scl 3; .type 32; .endef\n", sym);
if (r->type)
fprintf(ctx->fp, "\t.long _%s-.-4\n", sym);
else
fprintf(ctx->fp, "\t.long _%s\n", sym);
break;
default: /* BUILD_machasm for relative relocations handled below. */
fprintf(ctx->fp, "\t.long _%s\n", sym);
break;
}
}
static const char *const jccnames[] = {
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja",
"js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg"
};
/* Emit relocation for the incredibly stupid OSX assembler. */
static void emit_asm_reloc_mach(BuildCtx *ctx, uint8_t *cp, int n,
const char *sym)
{
const char *opname = NULL;
if (--n < 0) goto err;
if (cp[n] == 0xe8) {
opname = "call";
} else if (cp[n] == 0xe9) {
opname = "jmp";
} else if (cp[n] >= 0x80 && cp[n] <= 0x8f && n > 0 && cp[n-1] == 0x0f) {
opname = jccnames[cp[n]-0x80];
n--;
} else {
err:
fprintf(stderr, "Error: unsupported opcode for %s symbol relocation.\n",
sym);
exit(1);
}
emit_asm_bytes(ctx, cp, n);
if (!strncmp(sym, LABEL_PREFIX, sizeof(LABEL_PREFIX)-1))
fprintf(ctx->fp, "\t%s _%s\n", opname, sym);
else
fprintf(ctx->fp, "\t%s _" LABEL_PREFIX "wrapper_%s\n", opname, sym);
}
/* Emit an assembler label. */
static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc)
{
switch (ctx->mode) {
case BUILD_elfasm:
fprintf(ctx->fp,
"\n\t.globl %s\n"
"\t.hidden %s\n"
"\t.type %s, @%s\n"
"\t.size %s, %d\n"
"%s:\n",
name, name, name, isfunc ? "function" : "object", name, size, name);
break;
case BUILD_coffasm:
fprintf(ctx->fp, "\n\t.globl _%s\n", name);
if (isfunc)
fprintf(ctx->fp, "\t.def _%s; .scl 3; .type 32; .endef\n", name);
fprintf(ctx->fp, "_%s:\n", name);
break;
case BUILD_machasm:
fprintf(ctx->fp,
"\n\t.private_extern _%s\n"
"_%s:\n", name, name);
break;
default:
break;
}
}
/* Emit alignment. */
static void emit_asm_align(BuildCtx *ctx, int bits)
{
switch (ctx->mode) {
case BUILD_elfasm:
case BUILD_coffasm:
fprintf(ctx->fp, "\t.p2align %d\n", bits);
break;
case BUILD_machasm:
fprintf(ctx->fp, "\t.align %d\n", bits);
break;
default:
break;
}
}
/* ------------------------------------------------------------------------ */
/* Emit assembler source code. */
void emit_asm(BuildCtx *ctx)
{
char name[80];
int32_t prev;
int i, pi, rel;
fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch);
fprintf(ctx->fp, "\t.text\n");
emit_asm_align(ctx, 4);
emit_asm_label(ctx, LABEL_ASM_BEGIN, 0, 1);
if (ctx->mode == BUILD_elfasm)
fprintf(ctx->fp, ".Lbegin:\n");
i = 0;
do {
pi = ctx->perm[i++];
prev = ctx->sym_ofs[pi];
} while (prev < 0); /* Skip the _Z symbols. */
for (rel = 0; i <= ctx->nsym; i++) {
int ni = ctx->perm[i];
int32_t next = ctx->sym_ofs[ni];
int size = (int)(next - prev);
int32_t stop = next;
if (pi >= ctx->npc) {
sprintf(name, LABEL_PREFIX "%s", ctx->globnames[pi-ctx->npc]);
emit_asm_label(ctx, name, size, 1);
#if LJ_HASJIT
} else {
#else
} else if (!(pi == BC_JFORI || pi == BC_JFORL || pi == BC_JITERL ||
pi == BC_JLOOP || pi == BC_IFORL || pi == BC_IITERL ||
pi == BC_ILOOP)) {
#endif
sprintf(name, LABEL_PREFIX_BC "%s", bc_names[pi]);
emit_asm_label(ctx, name, size, 1);
}
while (rel < ctx->nreloc && ctx->reloc[rel].ofs < stop) {
int n = ctx->reloc[rel].ofs - prev;
if (ctx->mode == BUILD_machasm && ctx->reloc[rel].type != 0) {
emit_asm_reloc_mach(ctx, ctx->code+prev, n,
ctx->extnames[ctx->reloc[rel].sym]);
} else {
emit_asm_bytes(ctx, ctx->code+prev, n);
emit_asm_reloc(ctx, &ctx->reloc[rel]);
}
prev += n+4;
rel++;
}
emit_asm_bytes(ctx, ctx->code+prev, stop-prev);
prev = next;
pi = ni;
}
switch (ctx->mode) {
case BUILD_elfasm:
fprintf(ctx->fp, "\n\t.section .rodata\n");
break;
case BUILD_coffasm:
fprintf(ctx->fp, "\n\t.section .rdata,\"dr\"\n");
break;
case BUILD_machasm:
fprintf(ctx->fp, "\n\t.const\n");
break;
default:
break;
}
emit_asm_align(ctx, 5);
emit_asm_label(ctx, LABEL_OP_OFS, 2*ctx->npc, 0);
for (i = 0; i < ctx->npc; i++)
fprintf(ctx->fp, "\t.short %d\n", ctx->sym_ofs[i]);
fprintf(ctx->fp, "\n");
switch (ctx->mode) {
case BUILD_elfasm:
fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\",@progbits\n");
/* fallthrough */
case BUILD_coffasm:
fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident);
break;
case BUILD_machasm:
fprintf(ctx->fp,
"\t.cstring\n"
"\t.ascii \"%s\\0\"\n", ctx->dasm_ident);
break;
default:
break;
}
fprintf(ctx->fp, "\n");
}

206
src/buildvm_fold.c Normal file
View File

@ -0,0 +1,206 @@
/*
** LuaJIT VM builder: IR folding hash table generator.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#include "lj_obj.h"
#include "lj_ir.h"
#include "buildvm.h"
/* Context for the folding hash table generator. */
static int lineno;
static int funcidx;
static uint32_t foldkeys[BUILD_MAX_FOLD];
static uint32_t nkeys;
/* Try to fill the hash table with keys using the hash parameters. */
static int tryhash(uint32_t *htab, uint32_t sz, uint32_t r, int dorol)
{
uint32_t i;
if (dorol && ((r & 31) == 0 || (r>>5) == 0))
return 0; /* Avoid zero rotates. */
memset(htab, 0xff, (sz+1)*sizeof(uint32_t));
for (i = 0; i < nkeys; i++) {
uint32_t key = foldkeys[i];
uint32_t k = key & 0xffffff;
uint32_t h = (dorol ? lj_rol(lj_rol(k, r>>5) - k, r&31) :
(((k << (r>>5)) - k) << (r&31))) % sz;
if (htab[h] != 0xffffffff) { /* Collision on primary slot. */
if (htab[h+1] != 0xffffffff) { /* Collision on secondary slot. */
/* Try to move the colliding key, if possible. */
if (h < sz-1 && htab[h+2] == 0xffffffff) {
uint32_t k2 = htab[h+1] & 0xffffff;
uint32_t h2 = (dorol ? lj_rol(lj_rol(k2, r>>5) - k2, r&31) :
(((k2 << (r>>5)) - k2) << (r&31))) % sz;
if (h2 != h+1) return 0; /* Cannot resolve collision. */
htab[h+2] = htab[h+1]; /* Move colliding key to secondary slot. */
} else {
return 0; /* Collision. */
}
}
htab[h+1] = key;
} else {
htab[h] = key;
}
}
return 1; /* Success, all keys could be stored. */
}
/* Print the generated hash table. */
static void printhash(BuildCtx *ctx, uint32_t *htab, uint32_t sz)
{
uint32_t i;
fprintf(ctx->fp, "static const uint32_t fold_hash[%d] = {\n0x%08x",
sz+1, htab[0]);
for (i = 1; i < sz+1; i++)
fprintf(ctx->fp, ",\n0x%08x", htab[i]);
fprintf(ctx->fp, "\n};\n\n");
}
/* Exhaustive search for the shortest semi-perfect hash table. */
static void makehash(BuildCtx *ctx)
{
uint32_t htab[BUILD_MAX_FOLD*2+1];
uint32_t sz, r;
/* Search for the smallest hash table with an odd size. */
for (sz = (nkeys|1); sz < BUILD_MAX_FOLD*2; sz += 2) {
/* First try all shift hash combinations. */
for (r = 0; r < 32*32; r++) {
if (tryhash(htab, sz, r, 0)) {
printhash(ctx, htab, sz);
fprintf(ctx->fp,
"#define fold_hashkey(k)\t(((((k)<<%u)-(k))<<%u)%%%u)\n\n",
r>>5, r&31, sz);
return;
}
}
/* Then try all rotate hash combinations. */
for (r = 0; r < 32*32; r++) {
if (tryhash(htab, sz, r, 1)) {
printhash(ctx, htab, sz);
fprintf(ctx->fp,
"#define fold_hashkey(k)\t(lj_rol(lj_rol((k),%u)-(k),%u)%%%u)\n\n",
r>>5, r&31, sz);
return;
}
}
}
fprintf(stderr, "Error: search for perfect hash failed\n");
exit(1);
}
/* Parse one token of a fold rule. */
static uint32_t nexttoken(char **pp, int allowlit, int allowany)
{
char *p = *pp;
if (p) {
uint32_t i;
char *q = strchr(p, ' ');
if (q) *q++ = '\0';
*pp = q;
if (allowlit && !strncmp(p, "IRFPM_", 6)) {
for (i = 0; irfpm_names[i]; i++)
if (!strcmp(irfpm_names[i], p+6))
return i;
} else if (allowlit && !strncmp(p, "IRFL_", 5)) {
for (i = 0; irfield_names[i]; i++)
if (!strcmp(irfield_names[i], p+5))
return i;
} else if (allowany && !strcmp("any", p)) {
return 0xff;
} else {
for (i = 0; ir_names[i]; i++)
if (!strcmp(ir_names[i], p))
return i;
}
fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno);
exit(1);
}
return 0;
}
/* Parse a fold rule. */
static void foldrule(char *p)
{
uint32_t op = nexttoken(&p, 0, 0);
uint32_t left = nexttoken(&p, 0, 1);
uint32_t right = nexttoken(&p, 1, 1);
uint32_t key = (funcidx << 24) | (op << 16) | (left << 8) | right;
uint32_t i;
if (nkeys >= BUILD_MAX_FOLD) {
fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n");
exit(1);
}
/* Simple insertion sort to detect duplicates. */
for (i = nkeys; i > 0; i--) {
if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff))
break;
if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) {
fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno);
exit(1);
}
foldkeys[i] = foldkeys[i-1];
}
foldkeys[i] = key;
nkeys++;
}
/* Emit C source code for IR folding hash table. */
void emit_fold(BuildCtx *ctx)
{
char buf[256]; /* We don't care about analyzing lines longer than that. */
const char *fname = ctx->args[0];
FILE *fp;
if (fname == NULL) {
fprintf(stderr, "Error: missing input filename\n");
exit(1);
}
if (fname[0] == '-' && fname[1] == '\0') {
fp = stdin;
} else {
fp = fopen(fname, "r");
if (!fp) {
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
fname, strerror(errno));
exit(1);
}
}
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n");
lineno = 0;
funcidx = 0;
nkeys = 0;
while (fgets(buf, sizeof(buf), fp) != NULL) {
lineno++;
/* The prefix must be at the start of a line, otherwise it's ignored. */
if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) {
char *p = buf+sizeof(FOLDDEF_PREFIX)-1;
char *q = strchr(p, ')');
if (p[0] == '(' && q) {
p++;
*q = '\0';
foldrule(p);
} else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) {
p += 2;
*q = '\0';
fprintf(ctx->fp, funcidx ? ",\n %s" : " %s", p);
funcidx++;
} else {
buf[strlen(buf)-1] = '\0';
fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n",
FOLDDEF_PREFIX, p, lineno);
exit(1);
}
}
}
fclose(fp);
fprintf(ctx->fp, "\n};\n\n");
makehash(ctx);
}

365
src/buildvm_lib.c Normal file
View File

@ -0,0 +1,365 @@
/*
** LuaJIT VM builder: library definition compiler.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#include "lj_obj.h"
#include "lj_lib.h"
#include "buildvm.h"
/* Context for library definitions. */
static uint8_t obuf[8192];
static uint8_t *optr;
static char modname[80];
static size_t modnamelen;
static char funcname[80];
static int modstate, regfunc;
static int ffid, recffid;
enum {
REGFUNC_OK,
REGFUNC_NOREG,
REGFUNC_NOREGUV
};
static void libdef_name(char *p, int kind)
{
size_t n = strlen(p);
if (kind != LIBINIT_STRING) {
if (n > modnamelen && p[modnamelen] == '_' &&
!strncmp(p, modname, modnamelen)) {
p += modnamelen+1;
n -= modnamelen+1;
}
}
if (n > LIBINIT_MAXSTR) {
fprintf(stderr, "Error: string too long: '%s'\n", p);
exit(1);
}
if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */
fprintf(stderr, "Error: output buffer overflow\n");
exit(1);
}
*optr++ = (uint8_t)(n | kind);
memcpy(optr, p, n);
optr += n;
}
static void libdef_endmodule(BuildCtx *ctx)
{
if (modstate != 0) {
char line[80];
const uint8_t *p;
int n;
if (modstate == 1)
fprintf(ctx->fp, " (lua_CFunction)0");
fprintf(ctx->fp, "\n};\n");
fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n",
LABEL_PREFIX_LIBINIT, modname);
line[0] = '\0';
for (n = 0, p = obuf; p < optr; p++) {
n += sprintf(line+n, "%d,", *p);
if (n >= 75) {
fprintf(ctx->fp, "%s\n", line);
n = 0;
line[0] = '\0';
}
}
fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END);
}
}
static void libdef_module(BuildCtx *ctx, char *p, int arg)
{
UNUSED(arg);
if (ctx->mode == BUILD_libdef) {
libdef_endmodule(ctx);
optr = obuf;
*optr++ = (uint8_t)ffid;
*optr++ = 0;
modstate = 1;
fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p);
fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p);
fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n",
LABEL_PREFIX_LIBCF, p);
}
modnamelen = strlen(p);
if (modnamelen > sizeof(modname)-1) {
fprintf(stderr, "Error: module name too long: '%s'\n", p);
exit(1);
}
strcpy(modname, p);
}
static int find_ffofs(BuildCtx *ctx, const char *name)
{
int i;
for (i = 0; i < ctx->nglob; i++) {
const char *gl = ctx->globnames[i];
if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) {
return (int)((uint8_t *)ctx->glob[i] - ctx->code);
}
}
fprintf(stderr, "Error: undefined fast function %s%s\n",
LABEL_PREFIX_FF, name);
exit(1);
}
static void libdef_func(BuildCtx *ctx, char *p, int arg)
{
if (ctx->mode == BUILD_libdef) {
int ofs = arg != LIBINIT_CF ? find_ffofs(ctx, p) : 0;
if (modstate == 0) {
fprintf(stderr, "Error: no module for function definition %s\n", p);
exit(1);
}
if (regfunc == REGFUNC_NOREG) {
if (optr+1 > obuf+sizeof(obuf)) {
fprintf(stderr, "Error: output buffer overflow\n");
exit(1);
}
*optr++ = LIBINIT_FFID;
} else {
if (arg != LIBINIT_ASM_) {
if (modstate != 1) fprintf(ctx->fp, ",\n");
modstate = 2;
fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p);
}
if (regfunc != REGFUNC_NOREGUV) obuf[1]++; /* Bump hash table size. */
libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg);
if (arg) {
*optr++ = (uint8_t)ofs;
*optr++ = (uint8_t)(ofs >> 8);
}
}
} else if (ctx->mode == BUILD_ffdef) {
fprintf(ctx->fp, "FFDEF(%s)\n", p);
} else if (ctx->mode == BUILD_recdef) {
if (strlen(p) > sizeof(funcname)-1) {
fprintf(stderr, "Error: function name too long: '%s'\n", p);
exit(1);
}
strcpy(funcname, p);
} else if (ctx->mode == BUILD_vmdef) {
int i;
for (i = 1; p[i] && modname[i-1]; i++)
if (p[i] == '_') p[i] = '.';
fprintf(ctx->fp, "\"%s\",\n", p);
}
ffid++;
regfunc = REGFUNC_OK;
}
static uint32_t find_rec(char *name)
{
char *p = (char *)obuf;
uint32_t n;
for (n = 2; *p; n++) {
if (strcmp(p, name) == 0)
return n;
p += strlen(p)+1;
}
if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) {
fprintf(stderr, "Error: output buffer overflow\n");
exit(1);
}
strcpy(p, name);
return n;
}
static void libdef_rec(BuildCtx *ctx, char *p, int arg)
{
UNUSED(arg);
if (ctx->mode == BUILD_recdef) {
char *q;
uint32_t n;
for (; recffid+1 < ffid; recffid++)
fprintf(ctx->fp, ",\n0");
recffid = ffid;
if (*p == '.') p = funcname;
q = strchr(p, ' ');
if (q) *q++ = '\0';
n = find_rec(p);
if (q)
fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q);
else
fprintf(ctx->fp, ",\n0x%02x00", n);
}
}
static void memcpy_endian(void *dst, void *src, size_t n)
{
union { uint8_t b; uint32_t u; } host_endian;
host_endian.u = 1;
if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) {
memcpy(dst, src, n);
} else {
size_t i;
for (i = 0; i < n; i++)
((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i];
}
}
static void libdef_push(BuildCtx *ctx, char *p, int arg)
{
UNUSED(arg);
if (ctx->mode == BUILD_libdef) {
int len = (int)strlen(p);
if (*p == '"') {
if (len > 1 && p[len-1] == '"') {
p[len-1] = '\0';
libdef_name(p+1, LIBINIT_STRING);
return;
}
} else if (*p >= '0' && *p <= '9') {
char *ep;
double d = strtod(p, &ep);
if (*ep == '\0') {
if (optr+1+sizeof(double) > obuf+sizeof(obuf)) {
fprintf(stderr, "Error: output buffer overflow\n");
exit(1);
}
*optr++ = LIBINIT_NUMBER;
memcpy_endian(optr, &d, sizeof(double));
optr += sizeof(double);
return;
}
} else if (!strcmp(p, "lastcl")) {
if (optr+1 > obuf+sizeof(obuf)) {
fprintf(stderr, "Error: output buffer overflow\n");
exit(1);
}
*optr++ = LIBINIT_LASTCL;
return;
} else if (len > 4 && !strncmp(p, "top-", 4)) {
if (optr+2 > obuf+sizeof(obuf)) {
fprintf(stderr, "Error: output buffer overflow\n");
exit(1);
}
*optr++ = LIBINIT_COPY;
*optr++ = (uint8_t)atoi(p+4);
return;
}
fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p);
exit(1);
}
}
static void libdef_set(BuildCtx *ctx, char *p, int arg)
{
UNUSED(arg);
if (ctx->mode == BUILD_libdef) {
if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */
libdef_name(p, LIBINIT_STRING);
*optr++ = LIBINIT_SET;
obuf[1]++; /* Bump hash table size. */
}
}
static void libdef_regfunc(BuildCtx *ctx, char *p, int arg)
{
UNUSED(ctx); UNUSED(p);
regfunc = arg;
}
typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg);
typedef struct LibDefHandler {
const char *suffix;
const char *stop;
const LibDefFunc func;
const int arg;
} LibDefHandler;
static const LibDefHandler libdef_handlers[] = {
{ "MODULE_", " \t\r\n", libdef_module, 0 },
{ "CF(", ")", libdef_func, LIBINIT_CF },
{ "ASM(", ")", libdef_func, LIBINIT_ASM },
{ "ASM_(", ")", libdef_func, LIBINIT_ASM_ },
{ "REC(", ")", libdef_rec, 0 },
{ "PUSH(", ")", libdef_push, 0 },
{ "SET(", ")", libdef_set, 0 },
{ "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV },
{ "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG },
{ NULL, NULL, (LibDefFunc)0, 0 }
};
/* Emit C source code for library function definitions. */
void emit_lib(BuildCtx *ctx)
{
const char *fname;
if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef ||
ctx->mode == BUILD_recdef)
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
else if (ctx->mode == BUILD_vmdef)
fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n");
if (ctx->mode == BUILD_recdef)
fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100");
recffid = ffid = FF_C+1;
while ((fname = *ctx->args++)) {
char buf[256]; /* We don't care about analyzing lines longer than that. */
FILE *fp;
if (fname[0] == '-' && fname[1] == '\0') {
fp = stdin;
} else {
fp = fopen(fname, "r");
if (!fp) {
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
fname, strerror(errno));
exit(1);
}
}
modstate = 0;
regfunc = REGFUNC_OK;
while (fgets(buf, sizeof(buf), fp) != NULL) {
char *p;
for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) {
const LibDefHandler *ldh;
p += sizeof(LIBDEF_PREFIX)-1;
for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
size_t n, len = strlen(ldh->suffix);
if (!strncmp(p, ldh->suffix, len)) {
p += len;
n = ldh->stop ? strcspn(p, ldh->stop) : 0;
if (!p[n]) break;
p[n] = '\0';
ldh->func(ctx, p, ldh->arg);
p += n+1;
break;
}
}
if (ldh->suffix == NULL) {
buf[strlen(buf)-1] = '\0';
fprintf(stderr, "Error: unknown library definition tag %s%s\n",
LIBDEF_PREFIX, p);
exit(1);
}
}
}
fclose(fp);
if (ctx->mode == BUILD_libdef) {
libdef_endmodule(ctx);
}
}
if (ctx->mode == BUILD_ffdef) {
fprintf(ctx->fp, "\n#undef FFDEF\n\n");
} else if (ctx->mode == BUILD_vmdef) {
fprintf(ctx->fp, "}\n\n");
} else if (ctx->mode == BUILD_recdef) {
char *p = (char *)obuf;
fprintf(ctx->fp, "\n};\n\n");
fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n"
"recff_nyi,\n"
"recff_c");
while (*p) {
fprintf(ctx->fp, ",\nrecff_%s", p);
p += strlen(p)+1;
}
fprintf(ctx->fp, "\n};\n\n");
}
}

303
src/buildvm_peobj.c Normal file
View File

@ -0,0 +1,303 @@
/*
** LuaJIT VM builder: PE object emitter.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Only used for building on Windows, since we cannot assume the presence
** of a suitable assembler. The host and target byte order must match.
*/
#include "buildvm.h"
#include "lj_bc.h"
#if LJ_TARGET_X86ORX64
/* Context for PE object emitter. */
static char *strtab;
static size_t strtabofs;
/* -- PE object definitions ----------------------------------------------- */
/* PE header. */
typedef struct PEheader {
uint16_t arch;
uint16_t nsects;
uint32_t time;
uint32_t symtabofs;
uint32_t nsyms;
uint16_t opthdrsz;
uint16_t flags;
} PEheader;
/* PE section. */
typedef struct PEsection {
char name[8];
uint32_t vsize;
uint32_t vaddr;
uint32_t size;
uint32_t ofs;
uint32_t relocofs;
uint32_t lineofs;
uint16_t nreloc;
uint16_t nline;
uint32_t flags;
} PEsection;
/* PE relocation. */
typedef struct PEreloc {
uint32_t vaddr;
uint32_t symidx;
uint16_t type;
} PEreloc;
/* Cannot use sizeof, because it pads up to the max. alignment. */
#define PEOBJ_RELOC_SIZE (4+4+2)
/* PE symbol table entry. */
typedef struct PEsym {
union {
char name[8];
uint32_t nameref[2];
} n;
uint32_t value;
int16_t sect;
uint16_t type;
uint8_t scl;
uint8_t naux;
} PEsym;
/* PE symbol table auxiliary entry for a section. */
typedef struct PEsymaux {
uint32_t size;
uint16_t nreloc;
uint16_t nline;
uint32_t cksum;
uint16_t assoc;
uint8_t comdatsel;
uint8_t unused[3];
} PEsymaux;
/* Cannot use sizeof, because it pads up to the max. alignment. */
#define PEOBJ_SYM_SIZE (8+4+2+2+1+1)
/* PE object CPU specific defines. */
#if LJ_TARGET_X86
#define PEOBJ_ARCH_TARGET 0x014c
#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */
#define PEOBJ_RELOC_DIR32 0x06
#define PEOBJ_SYM_PREFIX "_"
#elif LJ_TARGET_X64
#define PEOBJ_ARCH_TARGET 0x8664
#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
#define PEOBJ_RELOC_DIR32 0x02
#define PEOBJ_SYM_PREFIX ""
#endif
/* Section numbers (0-based). */
enum {
PEOBJ_SECT_ABS = -2,
PEOBJ_SECT_UNDEF = -1,
PEOBJ_SECT_TEXT,
/* TODO: add .pdata/.xdata for x64. */
PEOBJ_SECT_RDATA,
PEOBJ_SECT_RDATA_Z,
PEOBJ_NSECTIONS
};
/* Symbol types. */
#define PEOBJ_TYPE_NULL 0
#define PEOBJ_TYPE_FUNC 0x20
/* Symbol storage class. */
#define PEOBJ_SCL_EXTERN 2
#define PEOBJ_SCL_STATIC 3
/* -- PE object emitter --------------------------------------------------- */
/* Emit PE object symbol. */
static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
int sect, int type, int scl)
{
PEsym sym;
size_t len = strlen(name);
if (!strtab) { /* Pass 1: only calculate string table length. */
if (len > 8) strtabofs += len+1;
return;
}
if (len <= 8) {
memcpy(sym.n.name, name, len);
memset(sym.n.name+len, 0, 8-len);
} else {
sym.n.nameref[0] = 0;
sym.n.nameref[1] = strtabofs;
memcpy(strtab + strtabofs, name, len);
strtab[strtabofs+len] = 0;
strtabofs += len+1;
}
sym.value = value;
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
sym.type = (uint16_t)type;
sym.scl = (uint8_t)scl;
sym.naux = 0;
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
}
/* Emit PE object section symbol. */
static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
{
PEsym sym;
PEsymaux aux;
if (!strtab) return; /* Pass 1: no output. */
memcpy(sym.n.name, pesect[sect].name, 8);
sym.value = 0;
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
sym.type = PEOBJ_TYPE_NULL;
sym.scl = PEOBJ_SCL_STATIC;
sym.naux = 1;
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
memset(&aux, 0, sizeof(PEsymaux));
aux.size = pesect[sect].size;
aux.nreloc = pesect[sect].nreloc;
owrite(ctx, &aux, PEOBJ_SYM_SIZE);
}
#define emit_peobj_sym_func(ctx, name, ofs) \
emit_peobj_sym(ctx, name, (uint32_t)(ofs), \
PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN)
#define emit_peobj_sym_rdata(ctx, name, ofs) \
emit_peobj_sym(ctx, name, (uint32_t)(ofs), \
PEOBJ_SECT_RDATA, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN)
/* Emit Windows PE object file. */
void emit_peobj(BuildCtx *ctx)
{
PEheader pehdr;
PEsection pesect[PEOBJ_NSECTIONS];
int nzsym, relocsyms;
uint32_t sofs;
int i;
union { uint8_t b; uint32_t u; } host_endian;
host_endian.u = 1;
if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
fprintf(stderr, "Error: different byte order for host and target\n");
exit(1);
}
sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
/* Fill in PE sections. */
memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
pesect[PEOBJ_SECT_TEXT].ofs = sofs;
sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
/* Flags: 60 = read+execute, 50 = align16, 20 = code. */
pesect[PEOBJ_SECT_TEXT].flags = 0x60500020;
memcpy(pesect[PEOBJ_SECT_RDATA].name, ".rdata", sizeof(".rdata")-1);
pesect[PEOBJ_SECT_RDATA].ofs = sofs;
sofs += (pesect[PEOBJ_SECT_RDATA].size = ctx->npc*sizeof(uint16_t));
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
pesect[PEOBJ_SECT_RDATA].flags = 0x40300040;
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
/* Fill in PE header. */
pehdr.arch = PEOBJ_ARCH_TARGET;
pehdr.nsects = PEOBJ_NSECTIONS;
pehdr.time = 0; /* Timestamp is optional. */
pehdr.symtabofs = sofs;
pehdr.opthdrsz = 0;
pehdr.flags = 0;
/* Compute the size of the symbol table:
** @feat.00 + nsections*2
** + asm_start + (nsyms-nzsym) + op_ofs
** + relocsyms
*/
/* Skip _Z syms. */
for (nzsym = 0; ctx->sym_ofs[ctx->perm[nzsym]] < 0; nzsym++) ;
for (relocsyms = 0; ctx->extnames[relocsyms]; relocsyms++) ;
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+(ctx->nsym-nzsym)+1 + relocsyms;
/* Write PE object header and all sections. */
owrite(ctx, &pehdr, sizeof(PEheader));
owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
/* Write .text section. */
owrite(ctx, ctx->code, ctx->codesz);
for (i = 0; i < ctx->nreloc; i++) {
PEreloc reloc;
reloc.vaddr = (uint32_t)ctx->reloc[i].ofs;
reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
}
/* Write .rdata section. */
for (i = 0; i < ctx->npc; i++) {
uint16_t pcofs = (uint16_t)ctx->sym_ofs[i];
owrite(ctx, &pcofs, 2);
}
/* Write .rdata$Z section. */
owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
/* Write symbol table. */
strtab = NULL; /* 1st pass: collect string sizes. */
for (;;) {
char name[80];
strtabofs = 4;
/* Mark as SafeSEH compliant. */
emit_peobj_sym(ctx, "@feat.00", 1,
PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
for (i = 0; ctx->extnames[i]; i++) {
sprintf(name, PEOBJ_SYM_PREFIX "%s", ctx->extnames[i]);
emit_peobj_sym(ctx, name, 0,
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
}
emit_peobj_sym_func(ctx, PEOBJ_SYM_PREFIX LABEL_ASM_BEGIN, 0);
for (i = nzsym; i < ctx->nsym; i++) {
int pi = ctx->perm[i];
if (pi >= ctx->npc) {
sprintf(name, PEOBJ_SYM_PREFIX LABEL_PREFIX "%s",
ctx->globnames[pi-ctx->npc]);
emit_peobj_sym_func(ctx, name, ctx->sym_ofs[pi]);
#if LJ_HASJIT
} else {
#else
} else if (!(pi == BC_JFORI || pi == BC_JFORL || pi == BC_JITERL ||
pi == BC_JLOOP || pi == BC_IFORL || pi == BC_IITERL ||
pi == BC_ILOOP)) {
#endif
sprintf(name, PEOBJ_SYM_PREFIX LABEL_PREFIX_BC "%s",
bc_names[pi]);
emit_peobj_sym_func(ctx, name, ctx->sym_ofs[pi]);
}
}
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA);
emit_peobj_sym_rdata(ctx, PEOBJ_SYM_PREFIX LABEL_OP_OFS, 0);
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
if (strtab)
break;
/* 2nd pass: alloc strtab, write syms and copy strings. */
strtab = (char *)malloc(strtabofs);
*(uint32_t *)strtab = strtabofs;
}
/* Write string table. */
owrite(ctx, strtab, strtabofs);
}
#endif

3592
src/buildvm_x86.dasc Normal file

File diff suppressed because it is too large Load Diff

159
src/lauxlib.h Normal file
View File

@ -0,0 +1,159 @@
/*
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#ifndef lauxlib_h
#define lauxlib_h
#include <stddef.h>
#include <stdio.h>
#include "lua.h"
#define luaL_getn(L,i) ((int)lua_objlen(L, i))
#define luaL_setn(L,i,j) ((void)0) /* no op! */
/* extra error code for `luaL_load' */
#define LUA_ERRFILE (LUA_ERRERR+1)
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname,
const luaL_Reg *l, int nup);
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
const luaL_Reg *l);
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
const char *def, size_t *l);
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
lua_Integer def);
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
const char *const lst[]);
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
const char *name);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
const char *r);
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
const char *fname, int szhint);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define luaL_argcheck(L, cond,numarg,extramsg) \
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
typedef struct luaL_Buffer {
char *p; /* current position in buffer */
int lvl; /* number of strings in the stack (level) */
lua_State *L;
char buffer[LUAL_BUFFERSIZE];
} luaL_Buffer;
#define luaL_addchar(B,c) \
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
(*(B)->p++ = (char)(c)))
/* compatibility only */
#define luaL_putchar(B,c) luaL_addchar(B,c)
#define luaL_addsize(B,n) ((B)->p += (n))
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* }====================================================== */
/* compatibility with ref system */
/* pre-defined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
(lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
#define luaL_reg luaL_Reg
#endif

438
src/lib_aux.c Normal file
View File

@ -0,0 +1,438 @@
/*
** Auxiliary library for the Lua/C API.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major parts taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#define lib_aux_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_lib.h"
/* convert a stack index to positive */
#define abs_index(L, i) \
((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
/* -- Type checks --------------------------------------------------------- */
LUALIB_API void luaL_checkstack(lua_State *L, int size, const char *msg)
{
if (!lua_checkstack(L, size))
lj_err_callerv(L, LJ_ERR_STKOVM, msg);
}
LUALIB_API void luaL_checktype(lua_State *L, int narg, int tt)
{
if (lua_type(L, narg) != tt)
lj_err_argt(L, narg, tt);
}
LUALIB_API void luaL_checkany(lua_State *L, int narg)
{
lj_lib_checkany(L, narg);
}
LUALIB_API const char *luaL_checklstring(lua_State *L, int narg, size_t *len)
{
GCstr *s = lj_lib_checkstr(L, narg);
if (len != NULL) *len = s->len;
return strdata(s);
}
LUALIB_API const char *luaL_optlstring(lua_State *L, int narg,
const char *def, size_t *len)
{
GCstr *s = lj_lib_optstr(L, narg);
if (s) {
if (len != NULL) *len = s->len;
return strdata(s);
}
if (len != NULL) *len = def ? strlen(def) : 0;
return def;
}
LUALIB_API lua_Number luaL_checknumber(lua_State *L, int narg)
{
return lj_lib_checknum(L, narg);
}
LUALIB_API lua_Number luaL_optnumber(lua_State *L, int narg, lua_Number def)
{
lj_lib_opt(L, narg,
return lj_lib_checknum(L, narg);
,
return def;
)
}
LUALIB_API lua_Integer luaL_checkinteger(lua_State *L, int narg)
{
#if LJ_64
return (lua_Integer)lj_lib_checknum(L, narg);
#else
return lj_lib_checkint(L, narg);
#endif
}
LUALIB_API lua_Integer luaL_optinteger(lua_State *L, int narg, lua_Integer def)
{
#if LJ_64
lj_lib_opt(L, narg,
return (lua_Integer)lj_lib_checknum(L, narg);
,
return def;
)
#else
return lj_lib_optint(L, narg, def);
#endif
}
LUALIB_API int luaL_checkoption(lua_State *L, int narg, const char *def,
const char *const lst[])
{
GCstr *s = lj_lib_optstr(L, narg);
const char *opt = s ? strdata(s) : def;
uint32_t i;
if (!opt) lj_err_argt(L, narg, LUA_TSTRING);
for (i = 0; lst[i]; i++)
if (strcmp(lst[i], opt) == 0)
return (int)i;
lj_err_argv(L, narg, LJ_ERR_INVOPTM, opt);
}
/* -- Module registration ------------------------------------------------- */
LUALIB_API const char *luaL_findtable(lua_State *L, int idx,
const char *fname, int szhint)
{
const char *e;
lua_pushvalue(L, idx);
do {
e = strchr(fname, '.');
if (e == NULL) e = fname + strlen(fname);
lua_pushlstring(L, fname, (size_t)(e - fname));
lua_rawget(L, -2);
if (lua_isnil(L, -1)) { /* no such field? */
lua_pop(L, 1); /* remove this nil */
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
lua_pushlstring(L, fname, (size_t)(e - fname));
lua_pushvalue(L, -2);
lua_settable(L, -4); /* set new table into field */
} else if (!lua_istable(L, -1)) { /* field has a non-table value? */
lua_pop(L, 2); /* remove table and value */
return fname; /* return problematic part of the name */
}
lua_remove(L, -2); /* remove previous table */
fname = e + 1;
} while (*e == '.');
return NULL;
}
static int libsize(const luaL_Reg *l)
{
int size = 0;
for (; l->name; l++) size++;
return size;
}
LUALIB_API void luaL_openlib(lua_State *L, const char *libname,
const luaL_Reg *l, int nup)
{
if (libname) {
int size = libsize(l);
/* check whether lib already exists */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
lua_getfield(L, -1, libname); /* get _LOADED[libname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
lj_err_callerv(L, LJ_ERR_BADMODN, libname);
lua_pushvalue(L, -1);
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
}
lua_remove(L, -2); /* remove _LOADED table */
lua_insert(L, -(nup+1)); /* move library table to below upvalues */
}
for (; l->name; l++) {
int i;
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup);
lua_setfield(L, -(nup+2), l->name);
}
lua_pop(L, nup); /* remove upvalues */
}
LUALIB_API void luaL_register(lua_State *L, const char *libname,
const luaL_Reg *l)
{
luaL_openlib(L, libname, l, 0);
}
LUALIB_API const char *luaL_gsub(lua_State *L, const char *s,
const char *p, const char *r)
{
const char *wild;
size_t l = strlen(p);
luaL_Buffer b;
luaL_buffinit(L, &b);
while ((wild = strstr(s, p)) != NULL) {
luaL_addlstring(&b, s, (size_t)(wild - s)); /* push prefix */
luaL_addstring(&b, r); /* push replacement in place of pattern */
s = wild + l; /* continue after `p' */
}
luaL_addstring(&b, s); /* push last suffix */
luaL_pushresult(&b);
return lua_tostring(L, -1);
}
/* -- Buffer handling ----------------------------------------------------- */
#define bufflen(B) ((size_t)((B)->p - (B)->buffer))
#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
static int emptybuffer(luaL_Buffer *B)
{
size_t l = bufflen(B);
if (l == 0)
return 0; /* put nothing on stack */
lua_pushlstring(B->L, B->buffer, l);
B->p = B->buffer;
B->lvl++;
return 1;
}
static void adjuststack(luaL_Buffer *B)
{
if (B->lvl > 1) {
lua_State *L = B->L;
int toget = 1; /* number of levels to concat */
size_t toplen = lua_strlen(L, -1);
do {
size_t l = lua_strlen(L, -(toget+1));
if (!(B->lvl - toget + 1 >= LUA_MINSTACK/2 || toplen > l))
break;
toplen += l;
toget++;
} while (toget < B->lvl);
lua_concat(L, toget);
B->lvl = B->lvl - toget + 1;
}
}
LUALIB_API char *luaL_prepbuffer(luaL_Buffer *B)
{
if (emptybuffer(B))
adjuststack(B);
return B->buffer;
}
LUALIB_API void luaL_addlstring(luaL_Buffer *B, const char *s, size_t l)
{
while (l--)
luaL_addchar(B, *s++);
}
LUALIB_API void luaL_addstring(luaL_Buffer *B, const char *s)
{
luaL_addlstring(B, s, strlen(s));
}
LUALIB_API void luaL_pushresult(luaL_Buffer *B)
{
emptybuffer(B);
lua_concat(B->L, B->lvl);
B->lvl = 1;
}
LUALIB_API void luaL_addvalue(luaL_Buffer *B)
{
lua_State *L = B->L;
size_t vl;
const char *s = lua_tolstring(L, -1, &vl);
if (vl <= bufffree(B)) { /* fit into buffer? */
memcpy(B->p, s, vl); /* put it there */
B->p += vl;
lua_pop(L, 1); /* remove from stack */
} else {
if (emptybuffer(B))
lua_insert(L, -2); /* put buffer before new value */
B->lvl++; /* add new value into B stack */
adjuststack(B);
}
}
LUALIB_API void luaL_buffinit(lua_State *L, luaL_Buffer *B)
{
B->L = L;
B->p = B->buffer;
B->lvl = 0;
}
/* -- Reference management ------------------------------------------------ */
#define FREELIST_REF 0
LUALIB_API int luaL_ref(lua_State *L, int t)
{
int ref;
t = abs_index(L, t);
if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* remove from stack */
return LUA_REFNIL; /* `nil' has a unique fixed reference */
}
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
lua_pop(L, 1); /* remove it from stack */
if (ref != 0) { /* any free element? */
lua_rawgeti(L, t, ref); /* remove it from list */
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
} else { /* no free elements */
ref = (int)lua_objlen(L, t);
ref++; /* create new reference */
}
lua_rawseti(L, t, ref);
return ref;
}
LUALIB_API void luaL_unref(lua_State *L, int t, int ref)
{
if (ref >= 0) {
t = abs_index(L, t);
lua_rawgeti(L, t, FREELIST_REF);
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
lua_pushinteger(L, ref);
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
}
}
/* -- Load Lua code ------------------------------------------------------- */
typedef struct FileReaderCtx {
FILE *fp;
char buf[LUAL_BUFFERSIZE];
} FileReaderCtx;
static const char *reader_file(lua_State *L, void *ud, size_t *size)
{
FileReaderCtx *ctx = (FileReaderCtx *)ud;
UNUSED(L);
if (feof(ctx->fp)) return NULL;
*size = fread(ctx->buf, 1, sizeof(ctx->buf), ctx->fp);
return *size > 0 ? ctx->buf : NULL;
}
LUALIB_API int luaL_loadfile(lua_State *L, const char *filename)
{
FileReaderCtx ctx;
int status;
const char *chunkname;
if (filename) {
ctx.fp = fopen(filename, "r");
if (ctx.fp == NULL) {
lua_pushfstring(L, "cannot open %s: %s", filename, strerror(errno));
return LUA_ERRFILE;
}
chunkname = lua_pushfstring(L, "@%s", filename);
} else {
ctx.fp = stdin;
chunkname = "=stdin";
}
status = lua_load(L, reader_file, &ctx, chunkname);
if (ferror(ctx.fp)) {
L->top -= filename ? 2 : 1;
lua_pushfstring(L, "cannot read %s: %s", chunkname+1, strerror(errno));
if (filename)
fclose(ctx.fp);
return LUA_ERRFILE;
}
if (filename) {
L->top--;
copyTV(L, L->top-1, L->top);
fclose(ctx.fp);
}
return status;
}
typedef struct StringReaderCtx {
const char *str;
size_t size;
} StringReaderCtx;
static const char *reader_string(lua_State *L, void *ud, size_t *size)
{
StringReaderCtx *ctx = (StringReaderCtx *)ud;
UNUSED(L);
if (ctx->size == 0) return NULL;
*size = ctx->size;
ctx->size = 0;
return ctx->str;
}
LUALIB_API int luaL_loadbuffer(lua_State *L, const char *buf, size_t size,
const char *name)
{
StringReaderCtx ctx;
ctx.str = buf;
ctx.size = size;
return lua_load(L, reader_string, &ctx, name);
}
LUALIB_API int luaL_loadstring(lua_State *L, const char *s)
{
return luaL_loadbuffer(L, s, strlen(s), s);
}
/* -- Default allocator and panic function -------------------------------- */
#ifdef LUAJIT_USE_SYSMALLOC
static void *mem_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
{
(void)ud;
(void)osize;
if (nsize == 0) {
free(ptr);
return NULL;
} else {
return realloc(ptr, nsize);
}
}
#define mem_create() NULL
#else
#include "lj_alloc.h"
#define mem_alloc lj_alloc_f
#define mem_create lj_alloc_create
#endif
static int panic(lua_State *L)
{
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1));
return 0;
}
LUALIB_API lua_State *luaL_newstate(void)
{
lua_State *L = lua_newstate(mem_alloc, mem_create());
if (L) G(L)->panic = panic;
return L;
}

560
src/lib_base.c Normal file
View File

@ -0,0 +1,560 @@
/*
** Base and coroutine library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <stdio.h>
#define lib_base_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_meta.h"
#include "lj_state.h"
#include "lj_ff.h"
#include "lj_ctype.h"
#include "lj_lib.h"
/* -- Base library: checks ------------------------------------------------ */
#define LJLIB_MODULE_base
LJLIB_ASM(assert) LJLIB_REC(.)
{
GCstr *s;
lj_lib_checkany(L, 1);
s = lj_lib_optstr(L, 2);
if (s)
lj_err_callermsg(L, strdata(s));
else
lj_err_caller(L, LJ_ERR_ASSERT);
return FFH_UNREACHABLE;
}
/* ORDER LJ_T */
LJLIB_PUSH("nil")
LJLIB_PUSH("boolean")
LJLIB_PUSH(top-1) /* boolean */
LJLIB_PUSH("userdata")
LJLIB_PUSH("string")
LJLIB_PUSH("upval")
LJLIB_PUSH("thread")
LJLIB_PUSH("proto")
LJLIB_PUSH("function")
LJLIB_PUSH("deadkey")
LJLIB_PUSH("table")
LJLIB_PUSH(top-8) /* userdata */
LJLIB_PUSH("number")
LJLIB_ASM_(type) LJLIB_REC(.)
/* Recycle the lj_lib_checkany(L, 1) from assert. */
/* -- Base library: getters and setters ----------------------------------- */
LJLIB_ASM_(getmetatable) LJLIB_REC(.)
/* Recycle the lj_lib_checkany(L, 1) from assert. */
LJLIB_ASM(setmetatable) LJLIB_REC(.)
{
GCtab *t = lj_lib_checktab(L, 1);
GCtab *mt = lj_lib_checktabornil(L, 2);
if (!tvisnil(lj_meta_lookup(L, L->base, MM_metatable)))
lj_err_caller(L, LJ_ERR_PROTMT);
setgcref(t->metatable, obj2gco(mt));
if (mt) { lj_gc_objbarriert(L, t, mt); }
settabV(L, L->base-1, t);
return FFH_RES(1);
}
LJLIB_CF(getfenv)
{
GCfunc *fn;
cTValue *o = L->base;
if (!(o < L->top && tvisfunc(o))) {
int level = lj_lib_optint(L, 1, 1);
o = lj_err_getframe(L, level, &level);
if (o == NULL)
lj_err_arg(L, 1, LJ_ERR_INVLVL);
}
fn = &gcval(o)->fn;
settabV(L, L->top++, isluafunc(fn) ? tabref(fn->l.env) : tabref(L->env));
return 1;
}
LJLIB_CF(setfenv)
{
GCfunc *fn;
GCtab *t = lj_lib_checktab(L, 2);
cTValue *o = L->base;
if (!(o < L->top && tvisfunc(o))) {
int level = lj_lib_checkint(L, 1);
if (level == 0) {
/* NOBARRIER: A thread (i.e. L) is never black. */
setgcref(L->env, obj2gco(t));
return 0;
}
o = lj_err_getframe(L, level, &level);
if (o == NULL)
lj_err_arg(L, 1, LJ_ERR_INVLVL);
}
fn = &gcval(o)->fn;
if (!isluafunc(fn))
lj_err_caller(L, LJ_ERR_SETFENV);
setgcref(fn->l.env, obj2gco(t));
lj_gc_objbarrier(L, obj2gco(fn), t);
setfuncV(L, L->top++, fn);
return 1;
}
LJLIB_ASM(rawget) LJLIB_REC(.)
{
lj_lib_checktab(L, 1);
lj_lib_checkany(L, 2);
return FFH_UNREACHABLE;
}
LJLIB_CF(rawset) LJLIB_REC(.)
{
lj_lib_checktab(L, 1);
lj_lib_checkany(L, 2);
L->top = 1+lj_lib_checkany(L, 3);
lua_rawset(L, 1);
return 1;
}
LJLIB_CF(rawequal) LJLIB_REC(.)
{
cTValue *o1 = lj_lib_checkany(L, 1);
cTValue *o2 = lj_lib_checkany(L, 2);
setboolV(L->top-1, lj_obj_equal(o1, o2));
return 1;
}
LJLIB_CF(unpack)
{
GCtab *t = lj_lib_checktab(L, 1);
int32_t n, i = lj_lib_optint(L, 2, 1);
int32_t e = (L->base+3-1 < L->top && !tvisnil(L->base+3-1)) ?
lj_lib_checkint(L, 3) : (int32_t)lj_tab_len(t);
if (i > e) return 0;
n = e - i + 1;
if (n <= 0 || !lua_checkstack(L, n))
lj_err_caller(L, LJ_ERR_UNPACK);
do {
cTValue *tv = lj_tab_getint(t, i);
if (tv) {
copyTV(L, L->top++, tv);
} else {
setnilV(L->top++);
}
} while (i++ < e);
return n;
}
LJLIB_CF(select)
{
int32_t n = (int32_t)(L->top - L->base);
if (n >= 1 && tvisstr(L->base) && *strVdata(L->base) == '#') {
setintV(L->top-1, n-1);
return 1;
} else {
int32_t i = lj_lib_checkint(L, 1);
if (i < 0) i = n + i; else if (i > n) i = n;
if (i < 1)
lj_err_arg(L, 1, LJ_ERR_IDXRNG);
return n - i;
}
}
/* -- Base library: conversions ------------------------------------------- */
LJLIB_ASM(tonumber) LJLIB_REC(.)
{
int32_t base = lj_lib_optint(L, 2, 10);
if (base == 10) {
TValue *o = lj_lib_checkany(L, 1);
if (tvisnum(o) || (tvisstr(o) && lj_str_numconv(strVdata(o), o))) {
setnumV(L->base-1, numV(o));
return FFH_RES(1);
}
} else {
const char *p = strdata(lj_lib_checkstr(L, 1));
char *ep;
unsigned long ul;
if (base < 2 || base > 36)
lj_err_arg(L, 2, LJ_ERR_BASERNG);
ul = strtoul(p, &ep, base);
if (p != ep) {
while (lj_ctype_isspace((unsigned char)(*ep))) ep++;
if (*ep == '\0') {
setnumV(L->base-1, cast_num(ul));
return FFH_RES(1);
}
}
}
setnilV(L->base-1);
return FFH_RES(1);
}
LJLIB_ASM(tostring) LJLIB_REC(.)
{
TValue *o = lj_lib_checkany(L, 1);
cTValue *mo;
L->top = o+1; /* Only keep one argument. */
if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
copyTV(L, L->base-1, mo); /* Replace callable. */
return FFH_RETRY;
} else {
GCstr *s;
if (tvisnum(o)) {
s = lj_str_fromnum(L, &o->n);
} else if (tvisnil(o)) {
s = lj_str_newlit(L, "nil");
} else if (tvisfalse(o)) {
s = lj_str_newlit(L, "false");
} else if (tvistrue(o)) {
s = lj_str_newlit(L, "true");
} else {
if (tvisfunc(o) && isffunc(funcV(o)))
lua_pushfstring(L, "function: fast#%d", funcV(o)->c.ffid);
else
lua_pushfstring(L, "%s: %p", typename(o), lua_topointer(L, 1));
/* Note: lua_pushfstring calls the GC which may invalidate o. */
s = strV(L->top-1);
}
setstrV(L, L->base-1, s);
return FFH_RES(1);
}
}
/* -- Base library: iterators --------------------------------------------- */
LJLIB_ASM(next)
{
lj_lib_checktab(L, 1);
lj_lib_checknum(L, 2); /* For ipairs_aux. */
return FFH_UNREACHABLE;
}
LJLIB_PUSH(lastcl)
LJLIB_ASM_(pairs)
LJLIB_NOREGUV LJLIB_ASM_(ipairs_aux) LJLIB_REC(.)
LJLIB_PUSH(lastcl)
LJLIB_ASM_(ipairs) LJLIB_REC(.)
/* -- Base library: throw and catch errors -------------------------------- */
LJLIB_CF(error)
{
int32_t level = lj_lib_optint(L, 2, 1);
lua_settop(L, 1);
if (lua_isstring(L, 1) && level > 0) {
luaL_where(L, level);
lua_pushvalue(L, 1);
lua_concat(L, 2);
}
return lua_error(L);
}
LJLIB_ASM(pcall) LJLIB_REC(.)
{
lj_lib_checkany(L, 1);
lj_lib_checkfunc(L, 2); /* For xpcall only. */
return FFH_UNREACHABLE;
}
LJLIB_ASM_(xpcall) LJLIB_REC(.)
/* -- Base library: load Lua code ----------------------------------------- */
static int load_aux(lua_State *L, int status)
{
if (status == 0)
return 1;
copyTV(L, L->top, L->top-1);
setnilV(L->top-1);
L->top++;
return 2;
}
LJLIB_CF(loadstring)
{
GCstr *s = lj_lib_checkstr(L, 1);
GCstr *name = lj_lib_optstr(L, 2);
return load_aux(L,
luaL_loadbuffer(L, strdata(s), s->len, strdata(name ? name : s)));
}
LJLIB_CF(loadfile)
{
GCstr *fname = lj_lib_optstr(L, 1);
return load_aux(L, luaL_loadfile(L, fname ? strdata(fname) : NULL));
}
static const char *reader_func(lua_State *L, void *ud, size_t *size)
{
UNUSED(ud);
luaL_checkstack(L, 2, "too many nested functions");
copyTV(L, L->top++, L->base);
lua_call(L, 0, 1); /* Call user-supplied function. */
L->top--;
if (tvisnil(L->top)) {
*size = 0;
return NULL;
} else if (tvisstr(L->top) || tvisnum(L->top)) {
copyTV(L, L->base+2, L->top); /* Anchor string in reserved stack slot. */
return lua_tolstring(L, 3, size);
} else {
lj_err_caller(L, LJ_ERR_RDRSTR);
return NULL;
}
}
LJLIB_CF(load)
{
GCstr *name = lj_lib_optstr(L, 2);
lj_lib_checkfunc(L, 1);
lua_settop(L, 3); /* Reserve a slot for the string from the reader. */
return load_aux(L,
lua_load(L, reader_func, NULL, name ? strdata(name) : "=(load)"));
}
LJLIB_CF(dofile)
{
GCstr *fname = lj_lib_optstr(L, 1);
setnilV(L->top);
L->top = L->base+1;
if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != 0)
lua_error(L);
lua_call(L, 0, LUA_MULTRET);
return (L->top - L->base) - 1;
}
/* -- Base library: GC control -------------------------------------------- */
LJLIB_CF(gcinfo)
{
setintV(L->top++, (G(L)->gc.total >> 10));
return 1;
}
LJLIB_CF(collectgarbage)
{
int opt = lj_lib_checkopt(L, 1, LUA_GCCOLLECT, /* ORDER LUA_GC* */
"\4stop\7restart\7collect\5count\1\377\4step\10setpause\12setstepmul");
int32_t data = lj_lib_optint(L, 2, 0);
if (opt == LUA_GCCOUNT) {
setnumV(L->top-1, cast_num((int32_t)G(L)->gc.total)/1024.0);
} else {
int res = lua_gc(L, opt, data);
if (opt == LUA_GCSTEP)
setboolV(L->top-1, res);
else
setintV(L->top-1, res);
}
return 1;
}
/* -- Base library: miscellaneous functions ------------------------------- */
LJLIB_PUSH(top-2) /* Upvalue holds weak table. */
LJLIB_CF(newproxy)
{
lua_settop(L, 1);
lua_newuserdata(L, 0);
if (lua_toboolean(L, 1) == 0) { /* newproxy(): without metatable. */
return 1;
} else if (lua_isboolean(L, 1)) { /* newproxy(true): with metatable. */
lua_newtable(L);
lua_pushvalue(L, -1);
lua_pushboolean(L, 1);
lua_rawset(L, lua_upvalueindex(1)); /* Remember mt in weak table. */
} else { /* newproxy(proxy): inherit metatable. */
int validproxy = 0;
if (lua_getmetatable(L, 1)) {
lua_rawget(L, lua_upvalueindex(1));
validproxy = lua_toboolean(L, -1);
lua_pop(L, 1);
}
if (!validproxy)
lj_err_arg(L, 1, LJ_ERR_NOPROXY);
lua_getmetatable(L, 1);
}
lua_setmetatable(L, 2);
return 1;
}
LJLIB_PUSH("tostring")
LJLIB_CF(print)
{
ptrdiff_t i, nargs = L->top - L->base;
cTValue *tv = lj_tab_getstr(tabref(L->env), strV(lj_lib_upvalue(L, 1)));
int shortcut = (tv && tvisfunc(tv) && funcV(tv)->c.ffid == FF_tostring);
copyTV(L, L->top++, tv ? tv : niltv(L));
for (i = 0; i < nargs; i++) {
const char *str;
size_t size;
cTValue *o = &L->base[i];
if (shortcut && tvisstr(o)) {
str = strVdata(o);
size = strV(o)->len;
} else if (shortcut && tvisnum(o)) {
char buf[LUAI_MAXNUMBER2STR];
lua_Number n = numV(o);
size = (size_t)lua_number2str(buf, n);
str = buf;
} else {
copyTV(L, L->top+1, o);
copyTV(L, L->top, L->top-1);
L->top += 2;
lua_call(L, 1, 1);
str = lua_tolstring(L, -1, &size);
if (!str)
lj_err_caller(L, LJ_ERR_PRTOSTR);
L->top--;
}
if (i)
putchar('\t');
fwrite(str, 1, size, stdout);
}
putchar('\n');
return 0;
}
LJLIB_PUSH(top-3)
LJLIB_SET(_VERSION)
#include "lj_libdef.h"
/* -- Coroutine library --------------------------------------------------- */
#define LJLIB_MODULE_coroutine
LJLIB_CF(coroutine_status)
{
const char *s;
lua_State *co;
if (!(L->top > L->base && tvisthread(L->base)))
lj_err_arg(L, 1, LJ_ERR_NOCORO);
co = threadV(L->base);
if (co == L) s = "running";
else if (co->status == LUA_YIELD) s = "suspended";
else if (co->status != 0) s = "dead";
else if (co->base > co->stack+1) s = "normal";
else if (co->top == co->base) s = "dead";
else s = "suspended";
lua_pushstring(L, s);
return 1;
}
LJLIB_CF(coroutine_running)
{
if (lua_pushthread(L))
setnilV(L->top++);
return 1;
}
LJLIB_CF(coroutine_create)
{
lua_State *L1 = lua_newthread(L);
if (!(L->top > L->base && tvisfunc(L->base) && isluafunc(funcV(L->base))))
lj_err_arg(L, 1, LJ_ERR_NOLFUNC);
setfuncV(L, L1->top++, funcV(L->base));
return 1;
}
LJLIB_ASM(coroutine_yield)
{
lj_err_caller(L, LJ_ERR_CYIELD);
return FFH_UNREACHABLE;
}
static int ffh_resume(lua_State *L, lua_State *co, int wrap)
{
if (co->cframe != NULL || co->status > LUA_YIELD ||
(co->status == 0 && co->top == co->base)) {
ErrMsg em = co->cframe ? LJ_ERR_CORUN : LJ_ERR_CODEAD;
if (wrap) lj_err_caller(L, em);
setboolV(L->base-1, 0);
setstrV(L, L->base, lj_err_str(L, em));
return FFH_RES(2);
}
lj_state_growstack(co, (MSize)(L->top - L->base - 1));
return FFH_RETRY;
}
LJLIB_ASM(coroutine_resume)
{
if (!(L->top > L->base && tvisthread(L->base)))
lj_err_arg(L, 1, LJ_ERR_NOCORO);
return ffh_resume(L, threadV(L->base), 0);
}
LJLIB_NOREG LJLIB_ASM(coroutine_wrap_aux)
{
return ffh_resume(L, threadV(lj_lib_upvalue(L, 1)), 1);
}
/* Inline declarations. */
LJ_ASMF void lj_ff_coroutine_wrap_aux(void);
LJ_FUNCA_NORET void lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co);
/* Error handler, called from assembler VM. */
void lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co)
{
co->top--; copyTV(L, L->top, co->top); L->top++;
if (tvisstr(L->top-1))
lj_err_callermsg(L, strVdata(L->top-1));
else
lj_err_run(L);
}
LJLIB_CF(coroutine_wrap)
{
GCfunc *fn;
lj_cf_coroutine_create(L);
lua_pushcclosure(L, lj_ffh_coroutine_wrap_aux, 1);
fn = funcV(L->top-1);
fn->c.gate = lj_ff_coroutine_wrap_aux;
fn->c.ffid = FF_coroutine_wrap_aux;
return 1;
}
#include "lj_libdef.h"
/* ------------------------------------------------------------------------ */
static void newproxy_weaktable(lua_State *L)
{
/* NOBARRIER: The table is new (marked white). */
GCtab *t = lj_tab_new(L, 0, 1);
settabV(L, L->top++, t);
setgcref(t->metatable, obj2gco(t));
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")),
lj_str_newlit(L, "kv"));
t->nomm = cast_byte(~(1u<<MM_mode));
}
LUALIB_API int luaopen_base(lua_State *L)
{
/* NOBARRIER: Table and value are the same. */
GCtab *env = tabref(L->env);
settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env);
lua_pushliteral(L, LUA_VERSION); /* top-3. */
newproxy_weaktable(L); /* top-2. */
LJ_LIB_REG_(L, "_G", base);
LJ_LIB_REG(L, coroutine);
return 2;
}

74
src/lib_bit.c Normal file
View File

@ -0,0 +1,74 @@
/*
** Bit manipulation library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lib_bit_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
#define LJLIB_MODULE_bit
LJLIB_ASM(bit_tobit) LJLIB_REC(bit_unary IR_TOBIT)
{
lj_lib_checknum(L, 1);
return FFH_RETRY;
}
LJLIB_ASM_(bit_bnot) LJLIB_REC(bit_unary IR_BNOT)
LJLIB_ASM_(bit_bswap) LJLIB_REC(bit_unary IR_BSWAP)
LJLIB_ASM(bit_lshift) LJLIB_REC(bit_shift IR_BSHL)
{
lj_lib_checknum(L, 1);
lj_lib_checknum(L, 2);
return FFH_RETRY;
}
LJLIB_ASM_(bit_rshift) LJLIB_REC(bit_shift IR_BSHR)
LJLIB_ASM_(bit_arshift) LJLIB_REC(bit_shift IR_BSAR)
LJLIB_ASM_(bit_rol) LJLIB_REC(bit_shift IR_BROL)
LJLIB_ASM_(bit_ror) LJLIB_REC(bit_shift IR_BROR)
LJLIB_ASM(bit_band) LJLIB_REC(bit_nary IR_BAND)
{
int i = 0;
do { lj_lib_checknum(L, ++i); } while (L->base+i < L->top);
return FFH_RETRY;
}
LJLIB_ASM_(bit_bor) LJLIB_REC(bit_nary IR_BOR)
LJLIB_ASM_(bit_bxor) LJLIB_REC(bit_nary IR_BXOR)
/* ------------------------------------------------------------------------ */
LJLIB_CF(bit_tohex)
{
uint32_t b = (uint32_t)lj_num2bit(lj_lib_checknum(L, 1));
int32_t i, n = L->base+1 >= L->top ? 8 : lj_num2bit(lj_lib_checknum(L, 2));
const char *hexdigits = "0123456789abcdef";
char buf[8];
if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
if (n > 8) n = 8;
for (i = n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
lua_pushlstring(L, buf, (size_t)n);
return 1;
}
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
LUALIB_API int luaopen_bit(lua_State *L)
{
LJ_LIB_REG(L, bit);
return 1;
}

366
src/lib_debug.c Normal file
View File

@ -0,0 +1,366 @@
/*
** Debug library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lib_debug_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
#define LJLIB_MODULE_debug
LJLIB_CF(debug_getregistry)
{
copyTV(L, L->top++, registry(L));
return 1;
}
LJLIB_CF(debug_getmetatable)
{
lj_lib_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
setnilV(L->top-1);
}
return 1;
}
LJLIB_CF(debug_setmetatable)
{
lj_lib_checktabornil(L, 2);
L->top = L->base+2;
lua_setmetatable(L, 1);
setboolV(L->top-1, 1);
return 1;
}
LJLIB_CF(debug_getfenv)
{
lj_lib_checkany(L, 1);
lua_getfenv(L, 1);
return 1;
}
LJLIB_CF(debug_setfenv)
{
lj_lib_checktab(L, 2);
L->top = L->base+2;
if (!lua_setfenv(L, 1))
lj_err_caller(L, LJ_ERR_SETFENV);
return 1;
}
/* ------------------------------------------------------------------------ */
static void settabss(lua_State *L, const char *i, const char *v)
{
lua_pushstring(L, v);
lua_setfield(L, -2, i);
}
static void settabsi(lua_State *L, const char *i, int v)
{
lua_pushinteger(L, v);
lua_setfield(L, -2, i);
}
static lua_State *getthread(lua_State *L, int *arg)
{
if (L->base < L->top && tvisthread(L->base)) {
*arg = 1;
return threadV(L->base);
} else {
*arg = 0;
return L;
}
}
static void treatstackoption(lua_State *L, lua_State *L1, const char *fname)
{
if (L == L1) {
lua_pushvalue(L, -2);
lua_remove(L, -3);
}
else
lua_xmove(L1, L, 1);
lua_setfield(L, -2, fname);
}
LJLIB_CF(debug_getinfo)
{
lua_Debug ar;
int arg;
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSu");
if (lua_isnumber(L, arg+1)) {
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
setnilV(L->top-1);
return 1;
}
} else if (L->base+arg < L->top && tvisfunc(L->base+arg)) {
options = lua_pushfstring(L, ">%s", options);
setfuncV(L1, L1->top++, funcV(L->base+arg));
} else {
lj_err_arg(L, arg+1, LJ_ERR_NOFUNCL);
}
if (!lua_getinfo(L1, options, &ar))
lj_err_arg(L, arg+2, LJ_ERR_INVOPT);
lua_createtable(L, 0, 16);
if (strchr(options, 'S')) {
settabss(L, "source", ar.source);
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
settabss(L, "what", ar.what);
}
if (strchr(options, 'l'))
settabsi(L, "currentline", ar.currentline);
if (strchr(options, 'u'))
settabsi(L, "nups", ar.nups);
if (strchr(options, 'n')) {
settabss(L, "name", ar.name);
settabss(L, "namewhat", ar.namewhat);
}
if (strchr(options, 'L'))
treatstackoption(L, L1, "activelines");
if (strchr(options, 'f'))
treatstackoption(L, L1, "func");
return 1; /* return table */
}
LJLIB_CF(debug_getlocal)
{
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
const char *name;
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar))
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG);
name = lua_getlocal(L1, &ar, lj_lib_checkint(L, arg+2));
if (name) {
lua_xmove(L1, L, 1);
lua_pushstring(L, name);
lua_pushvalue(L, -2);
return 2;
} else {
setnilV(L->top-1);
return 1;
}
}
LJLIB_CF(debug_setlocal)
{
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
TValue *tv;
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar))
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG);
tv = lj_lib_checkany(L, arg+3);
copyTV(L1, L1->top++, tv);
lua_pushstring(L, lua_setlocal(L1, &ar, lj_lib_checkint(L, arg+2)));
return 1;
}
static int debug_getupvalue(lua_State *L, int get)
{
int32_t n = lj_lib_checkint(L, 2);
if (isluafunc(lj_lib_checkfunc(L, 1))) {
const char *name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
if (name) {
lua_pushstring(L, name);
if (!get) return 1;
copyTV(L, L->top, L->top-2);
L->top++;
return 2;
}
}
return 0;
}
LJLIB_CF(debug_getupvalue)
{
return debug_getupvalue(L, 1);
}
LJLIB_CF(debug_setupvalue)
{
lj_lib_checkany(L, 3);
return debug_getupvalue(L, 0);
}
/* ------------------------------------------------------------------------ */
static const char KEY_HOOK = 'h';
static void hookf(lua_State *L, lua_Debug *ar)
{
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail return"};
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline);
else lua_pushnil(L);
lua_call(L, 2, 0);
}
}
static int makemask(const char *smask, int count)
{
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
static char *unmakemask(int mask, char *smask)
{
int i = 0;
if (mask & LUA_MASKCALL) smask[i++] = 'c';
if (mask & LUA_MASKRET) smask[i++] = 'r';
if (mask & LUA_MASKLINE) smask[i++] = 'l';
smask[i] = '\0';
return smask;
}
LJLIB_CF(debug_sethook)
{
int arg, mask, count;
lua_Hook func;
(void)getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) {
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
} else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_pushvalue(L, arg+1);
lua_rawset(L, LUA_REGISTRYINDEX);
lua_sethook(L, func, mask, count);
return 0;
}
LJLIB_CF(debug_gethook)
{
char buff[5];
int mask = lua_gethookmask(L);
lua_Hook hook = lua_gethook(L);
if (hook != NULL && hook != hookf) { /* external hook? */
lua_pushliteral(L, "external hook");
} else {
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX); /* get hook */
}
lua_pushstring(L, unmakemask(mask, buff));
lua_pushinteger(L, lua_gethookcount(L));
return 3;
}
/* ------------------------------------------------------------------------ */
LJLIB_CF(debug_debug)
{
for (;;) {
char buffer[250];
fputs("lua_debug> ", stderr);
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) {
fputs(lua_tostring(L, -1), stderr);
fputs("\n", stderr);
}
lua_settop(L, 0); /* remove eventual returns */
}
}
/* ------------------------------------------------------------------------ */
#define LEVELS1 12 /* size of the first part of the stack */
#define LEVELS2 10 /* size of the second part of the stack */
LJLIB_CF(debug_traceback)
{
int level;
int firstpart = 1; /* still before eventual `...' */
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (lua_isnumber(L, arg+2)) {
level = (int)lua_tointeger(L, arg+2);
lua_pop(L, 1);
}
else
level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
if (lua_gettop(L) == arg)
lua_pushliteral(L, "");
else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
else lua_pushliteral(L, "\n");
lua_pushliteral(L, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
if (level > LEVELS1 && firstpart) {
/* no more than `LEVELS2' more levels? */
if (!lua_getstack(L1, level+LEVELS2, &ar)) {
level--; /* keep going */
} else {
lua_pushliteral(L, "\n\t..."); /* too many levels */
/* This only works with LuaJIT 2.x. Avoids O(n^2) behaviour. */
lua_getstack(L1, -10, &ar);
level = ar.i_ci - LEVELS2;
}
firstpart = 0;
continue;
}
lua_pushliteral(L, "\n\t");
lua_getinfo(L1, "Snl", &ar);
lua_pushfstring(L, "%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
if (*ar.namewhat != '\0') { /* is there a name? */
lua_pushfstring(L, " in function " LUA_QS, ar.name);
} else {
if (*ar.what == 'm') /* main? */
lua_pushfstring(L, " in main chunk");
else if (*ar.what == 'C' || *ar.what == 't')
lua_pushliteral(L, " ?"); /* C function or tail call */
else
lua_pushfstring(L, " in function <%s:%d>",
ar.short_src, ar.linedefined);
}
lua_concat(L, lua_gettop(L) - arg);
}
lua_concat(L, lua_gettop(L) - arg);
return 1;
}
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
LUALIB_API int luaopen_debug(lua_State *L)
{
LJ_LIB_REG(L, debug);
return 1;
}

37
src/lib_init.c Normal file
View File

@ -0,0 +1,37 @@
/*
** Library initialization.
** Major parts taken verbatim from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lib_init_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static const luaL_Reg lualibs[] = {
{ "", luaopen_base },
{ LUA_LOADLIBNAME, luaopen_package },
{ LUA_TABLIBNAME, luaopen_table },
{ LUA_IOLIBNAME, luaopen_io },
{ LUA_OSLIBNAME, luaopen_os },
{ LUA_STRLIBNAME, luaopen_string },
{ LUA_MATHLIBNAME, luaopen_math },
{ LUA_DBLIBNAME, luaopen_debug },
{ LUA_BITLIBNAME, luaopen_bit },
{ LUA_JITLIBNAME, luaopen_jit },
{ NULL, NULL }
};
LUALIB_API void luaL_openlibs(lua_State *L)
{
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}

538
src/lib_io.c Normal file
View File

@ -0,0 +1,538 @@
/*
** I/O library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdio.h>
#define lib_io_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_gc.h"
#include "lj_ff.h"
#include "lj_lib.h"
/* Index of standard handles in function environment. */
#define IO_INPUT 1
#define IO_OUTPUT 2
/* -- Error handling ------------------------------------------------------ */
static int io_pushresult(lua_State *L, int ok, const char *fname)
{
if (ok) {
setboolV(L->top++, 1);
return 1;
} else {
int en = errno; /* Lua API calls may change this value. */
lua_pushnil(L);
if (fname)
lua_pushfstring(L, "%s: %s", fname, strerror(en));
else
lua_pushfstring(L, "%s", strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static void io_file_error(lua_State *L, int arg, const char *fname)
{
lua_pushfstring(L, "%s: %s", fname, strerror(errno));
luaL_argerror(L, arg, lua_tostring(L, -1));
}
/* -- Open helpers -------------------------------------------------------- */
#define io_tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
static FILE *io_tofile(lua_State *L)
{
FILE **f = io_tofilep(L);
if (*f == NULL)
lj_err_caller(L, LJ_ERR_IOCLFL);
return *f;
}
static FILE **io_file_new(lua_State *L)
{
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
*pf = NULL;
luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2);
return pf;
}
/* -- Close helpers ------------------------------------------------------- */
static int lj_cf_io_std_close(lua_State *L)
{
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
static int lj_cf_io_pipe_close(lua_State *L)
{
FILE **p = io_tofilep(L);
#if defined(LUA_USE_POSIX)
int ok = (pclose(*p) != -1);
#elif defined(LUA_USE_WIN)
int ok = (_pclose(*p) != -1);
#else
int ok = 0;
#endif
*p = NULL;
return io_pushresult(L, ok, NULL);
}
static int lj_cf_io_file_close(lua_State *L)
{
FILE **p = io_tofilep(L);
int ok = (fclose(*p) == 0);
*p = NULL;
return io_pushresult(L, ok, NULL);
}
static int io_file_close(lua_State *L)
{
lua_getfenv(L, 1);
lua_getfield(L, -1, "__close");
return (lua_tocfunction(L, -1))(L);
}
/* -- Read/write helpers -------------------------------------------------- */
static int io_file_readnum(lua_State *L, FILE *fp)
{
lua_Number d;
if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) {
lua_pushnumber(L, d);
return 1;
} else {
return 0; /* read fails */
}
}
static int test_eof(lua_State *L, FILE *fp)
{
int c = getc(fp);
ungetc(c, fp);
lua_pushlstring(L, NULL, 0);
return (c != EOF);
}
static int io_file_readline(lua_State *L, FILE *fp)
{
luaL_Buffer b;
luaL_buffinit(L, &b);
for (;;) {
size_t len;
char *p = luaL_prepbuffer(&b);
if (fgets(p, LUAL_BUFFERSIZE, fp) == NULL) { /* EOF? */
luaL_pushresult(&b);
return (strV(L->top-1)->len > 0); /* Anything read? */
}
len = strlen(p);
if (len == 0 || p[len-1] != '\n') { /* Partial line? */
luaL_addsize(&b, len);
} else {
luaL_addsize(&b, len - 1); /* Don't include EOL. */
luaL_pushresult(&b);
return 1; /* Got at least an EOL. */
}
}
}
static int io_file_readchars(lua_State *L, FILE *fp, size_t n)
{
size_t rlen; /* how much to read */
size_t nr; /* number of chars actually read */
luaL_Buffer b;
luaL_buffinit(L, &b);
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
do {
char *p = luaL_prepbuffer(&b);
if (rlen > n) rlen = n; /* cannot read more than asked */
nr = fread(p, 1, rlen, fp);
luaL_addsize(&b, nr);
n -= nr; /* still have to read `n' chars */
} while (n > 0 && nr == rlen); /* until end of count or eof */
luaL_pushresult(&b); /* close buffer */
return (n == 0 || lua_objlen(L, -1) > 0);
}
static int io_file_read(lua_State *L, FILE *fp, int start)
{
int ok, n, nargs = (L->top - L->base) - start;
clearerr(fp);
if (nargs == 0) {
ok = io_file_readline(L, fp);
n = start+1; /* Return 1 result. */
} else {
/* The results plus the buffers go on top of the args. */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
ok = 1;
for (n = start; nargs-- && ok; n++) {
if (tvisstr(L->base+n)) {
const char *p = strVdata(L->base+n);
if (p[0] != '*')
lj_err_arg(L, n+1, LJ_ERR_INVOPT);
if (p[1] == 'n')
ok = io_file_readnum(L, fp);
else if (p[1] == 'l')
ok = io_file_readline(L, fp);
else if (p[1] == 'a')
io_file_readchars(L, fp, ~((size_t)0));
else
lj_err_arg(L, n+1, LJ_ERR_INVFMT);
} else if (tvisnum(L->base+n)) {
size_t len = (size_t)lj_lib_checkint(L, n+1);
ok = len ? io_file_readchars(L, fp, len) : test_eof(L, fp);
} else {
lj_err_arg(L, n+1, LJ_ERR_INVOPT);
}
}
}
if (ferror(fp))
return io_pushresult(L, 0, NULL);
if (!ok)
setnilV(L->top-1); /* Replace last result with nil. */
return n - start;
}
static int io_file_write(lua_State *L, FILE *fp, int start)
{
cTValue *tv;
int status = 1;
for (tv = L->base+start; tv < L->top; tv++) {
if (tvisstr(tv)) {
MSize len = strV(tv)->len;
status = status && (fwrite(strVdata(tv), 1, len, fp) == len);
} else if (tvisnum(tv)) {
status = status && (fprintf(fp, LUA_NUMBER_FMT, numV(tv)) > 0);
} else {
lj_lib_checkstr(L, tv-L->base+1);
}
}
return io_pushresult(L, status, NULL);
}
/* -- I/O file methods ---------------------------------------------------- */
#define LJLIB_MODULE_io_method
LJLIB_CF(io_method_close)
{
if (lua_isnone(L, 1))
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
io_tofile(L);
return io_file_close(L);
}
LJLIB_CF(io_method_read)
{
return io_file_read(L, io_tofile(L), 1);
}
LJLIB_CF(io_method_write)
{
return io_file_write(L, io_tofile(L), 1);
}
LJLIB_CF(io_method_flush)
{
return io_pushresult(L, fflush(io_tofile(L)) == 0, NULL);
}
LJLIB_CF(io_method_seek)
{
FILE *fp = io_tofile(L);
int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
lua_Number ofs;
int res;
if (opt == 0) opt = SEEK_SET;
else if (opt == 1) opt = SEEK_CUR;
else if (opt == 2) opt = SEEK_END;
lj_lib_opt(L, 3,
ofs = lj_lib_checknum(L, 3);
,
ofs = 0;
)
#if defined(LUA_USE_POSIX)
res = fseeko(fp, (int64_t)ofs, opt);
#elif _MSC_VER >= 1400
res = _fseeki64(fp, (int64_t)ofs, opt);
#elif defined(__MINGW32__)
res = fseeko64(fp, (int64_t)ofs, opt);
#else
res = fseek(fp, (long)ofs, opt);
#endif
if (res)
return io_pushresult(L, 0, NULL);
#if defined(LUA_USE_POSIX)
ofs = cast_num(ftello(fp));
#elif _MSC_VER >= 1400
ofs = cast_num(_ftelli64(fp));
#elif defined(__MINGW32__)
ofs = cast_num(ftello64(fp));
#else
ofs = cast_num(ftell(fp));
#endif
setnumV(L->top-1, ofs);
return 1;
}
LJLIB_CF(io_method_setvbuf)
{
FILE *fp = io_tofile(L);
int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no");
size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE);
if (opt == 0) opt = _IOFBF;
else if (opt == 1) opt = _IOLBF;
else if (opt == 2) opt = _IONBF;
return io_pushresult(L, (setvbuf(fp, NULL, opt, sz) == 0), NULL);
}
/* Forward declaration. */
static void io_file_lines(lua_State *L, int idx, int toclose);
LJLIB_CF(io_method_lines)
{
io_tofile(L);
io_file_lines(L, 1, 0);
return 1;
}
LJLIB_CF(io_method___gc)
{
FILE *fp = *io_tofilep(L);
if (fp != NULL) io_file_close(L);
return 0;
}
LJLIB_CF(io_method___tostring)
{
FILE *fp = *io_tofilep(L);
if (fp == NULL)
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", fp);
return 1;
}
LJLIB_PUSH(top-1) LJLIB_SET(__index)
#include "lj_libdef.h"
/* -- I/O library functions ----------------------------------------------- */
#define LJLIB_MODULE_io
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
static FILE *io_file_get(lua_State *L, int findex)
{
GCtab *fenv = tabref(curr_func(L)->c.env);
GCudata *ud = udataV(&tvref(fenv->array)[findex]);
FILE *fp = *(FILE **)uddata(ud);
if (fp == NULL)
lj_err_caller(L, LJ_ERR_IOSTDCL);
return fp;
}
LJLIB_CF(io_open)
{
const char *fname = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = io_file_new(L);
*pf = fopen(fname, mode);
return (*pf == NULL) ? io_pushresult(L, 0, fname) : 1;
}
LJLIB_CF(io_tmpfile)
{
FILE **pf = io_file_new(L);
*pf = tmpfile();
return (*pf == NULL) ? io_pushresult(L, 0, NULL) : 1;
}
LJLIB_CF(io_close)
{
return lj_cf_io_method_close(L);
}
LJLIB_CF(io_read)
{
return io_file_read(L, io_file_get(L, IO_INPUT), 0);
}
LJLIB_CF(io_write)
{
return io_file_write(L, io_file_get(L, IO_OUTPUT), 0);
}
LJLIB_CF(io_flush)
{
return io_pushresult(L, fflush(io_file_get(L, IO_OUTPUT)) == 0, NULL);
}
LJLIB_NOREG LJLIB_CF(io_lines_iter)
{
FILE *fp = *(FILE **)uddata(udataV(lj_lib_upvalue(L, 1)));
int ok;
if (fp == NULL)
lj_err_caller(L, LJ_ERR_IOCLFL);
ok = io_file_readline(L, fp);
if (ferror(fp))
return luaL_error(L, "%s", strerror(errno));
if (ok)
return 1;
if (tvistrue(lj_lib_upvalue(L, 2))) { /* Need to close file? */
L->top = L->base+1;
setudataV(L, L->base, udataV(lj_lib_upvalue(L, 1)));
io_file_close(L);
}
return 0;
}
static void io_file_lines(lua_State *L, int idx, int toclose)
{
lua_pushvalue(L, idx);
lua_pushboolean(L, toclose);
lua_pushcclosure(L, lj_cf_io_lines_iter, 2);
funcV(L->top-1)->c.ffid = FF_io_lines_iter;
}
LJLIB_CF(io_lines)
{
if (lua_isnoneornil(L, 1)) { /* no arguments? */
/* will iterate over default input */
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
return lj_cf_io_method_lines(L);
} else {
const char *fname = luaL_checkstring(L, 1);
FILE **pf = io_file_new(L);
*pf = fopen(fname, "r");
if (*pf == NULL)
io_file_error(L, 1, fname);
io_file_lines(L, lua_gettop(L), 1);
return 1;
}
}
static int io_std_get(lua_State *L, int fp, const char *mode)
{
if (!lua_isnoneornil(L, 1)) {
const char *fname = lua_tostring(L, 1);
if (fname) {
FILE **pf = io_file_new(L);
*pf = fopen(fname, mode);
if (*pf == NULL)
io_file_error(L, 1, fname);
} else {
io_tofile(L); /* check that it's a valid file handle */
lua_pushvalue(L, 1);
}
lua_rawseti(L, LUA_ENVIRONINDEX, fp);
}
/* return current value */
lua_rawgeti(L, LUA_ENVIRONINDEX, fp);
return 1;
}
LJLIB_CF(io_input)
{
return io_std_get(L, IO_INPUT, "r");
}
LJLIB_CF(io_output)
{
return io_std_get(L, IO_OUTPUT, "w");
}
LJLIB_CF(io_type)
{
void *ud;
luaL_checkany(L, 1);
ud = lua_touserdata(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
lua_pushnil(L); /* not a file */
else if (*((FILE **)ud) == NULL)
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
LJLIB_PUSH(top-3) LJLIB_SET(!) /* Set environment. */
LJLIB_CF(io_popen)
{
#if defined(LUA_USE_POSIX) || defined(LUA_USE_WIN)
const char *fname = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = io_file_new(L);
#ifdef LUA_USE_POSIX
fflush(NULL);
*pf = popen(fname, mode);
#else
*pf = _popen(fname, mode);
#endif
return (*pf == NULL) ? io_pushresult(L, 0, fname) : 1;
#else
luaL_error(L, LUA_QL("popen") " not supported");
#endif
}
#include "lj_libdef.h"
/* ------------------------------------------------------------------------ */
static void io_std_new(lua_State *L, FILE *fp, int k, const char *fname)
{
FILE **pf = io_file_new(L);
GCudata *ud = udataV(L->top-1);
GCtab *envt = tabV(L->top-2);
*pf = fp;
setgcref(ud->env, obj2gco(envt));
lj_gc_objbarrier(L, obj2gco(ud), envt);
if (k > 0) {
lua_pushvalue(L, -1);
lua_rawseti(L, -5, k);
}
lua_setfield(L, -3, fname);
}
static void io_fenv_new(lua_State *L, int narr, lua_CFunction cls)
{
lua_createtable(L, narr, 1);
lua_pushcfunction(L, cls);
lua_setfield(L, -2, "__close");
}
LUALIB_API int luaopen_io(lua_State *L)
{
LJ_LIB_REG_(L, NULL, io_method);
lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
io_fenv_new(L, 0, lj_cf_io_pipe_close); /* top-3 */
io_fenv_new(L, 2, lj_cf_io_file_close); /* top-2 */
LJ_LIB_REG(L, io);
io_fenv_new(L, 0, lj_cf_io_std_close);
io_std_new(L, stdin, IO_INPUT, "stdin");
io_std_new(L, stdout, IO_OUTPUT, "stdout");
io_std_new(L, stderr, 0, "stderr");
lua_pop(L, 1);
return 1;
}

589
src/lib_jit.c Normal file
View File

@ -0,0 +1,589 @@
/*
** JIT library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lib_jit_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_arch.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#if LJ_HASJIT
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
#endif
#include "lj_dispatch.h"
#include "lj_vm.h"
#include "lj_vmevent.h"
#include "lj_lib.h"
#include "luajit.h"
/* -- jit.* functions ----------------------------------------------------- */
#define LJLIB_MODULE_jit
static int setjitmode(lua_State *L, int mode)
{
int idx = 0;
if (L->base == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */
mode |= LUAJIT_MODE_ENGINE;
} else {
/* jit.on/off/flush(func|proto, nil|true|false) */
if (tvisfunc(L->base) || tvisproto(L->base))
idx = 1;
else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */
goto err;
if (L->base+1 < L->top && tvisbool(L->base+1))
mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC;
else
mode |= LUAJIT_MODE_FUNC;
}
if (luaJIT_setmode(L, idx, mode) != 1) {
err:
#if LJ_HASJIT
lj_err_arg(L, 1, LJ_ERR_NOLFUNC);
#else
lj_err_caller(L, LJ_ERR_NOJIT);
#endif
}
return 0;
}
LJLIB_CF(jit_on)
{
return setjitmode(L, LUAJIT_MODE_ON);
}
LJLIB_CF(jit_off)
{
return setjitmode(L, LUAJIT_MODE_OFF);
}
LJLIB_CF(jit_flush)
{
#if LJ_HASJIT
if (L->base < L->top && (tvisnum(L->base) || tvisstr(L->base))) {
int traceno = lj_lib_checkint(L, 1);
luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE);
return 0;
}
#endif
return setjitmode(L, LUAJIT_MODE_FLUSH);
}
#if LJ_HASJIT
/* Push a string for every flag bit that is set. */
static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base,
const char *str)
{
for (; *str; base <<= 1, str += 1+*str)
if (flags & base)
setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str));
}
#endif
LJLIB_CF(jit_status)
{
#if LJ_HASJIT
jit_State *J = L2J(L);
L->top = L->base;
setboolV(L->top++, (J->flags & JIT_F_ON) ? 1 : 0);
flagbits_to_strings(L, J->flags, JIT_F_CPU_FIRST, JIT_F_CPUSTRING);
flagbits_to_strings(L, J->flags, JIT_F_OPT_FIRST, JIT_F_OPTSTRING);
return L->top - L->base;
#else
setboolV(L->top++, 0);
return 1;
#endif
}
LJLIB_CF(jit_attach)
{
#ifdef LUAJIT_DISABLE_VMEVENT
luaL_error(L, "vmevent API disabled");
#else
GCfunc *fn = lj_lib_checkfunc(L, 1);
GCstr *s = lj_lib_optstr(L, 2);
luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE);
if (s) { /* Attach to given event. */
lua_pushvalue(L, 1);
lua_rawseti(L, -2, VMEVENT_HASHIDX(s->hash));
G(L)->vmevmask = VMEVENT_NOCACHE; /* Invalidate cache. */
} else { /* Detach if no event given. */
setnilV(L->top++);
while (lua_next(L, -2)) {
L->top--;
if (tvisfunc(L->top) && funcV(L->top) == fn) {
setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1));
}
}
}
#endif
return 0;
}
LJLIB_PUSH(top-4) LJLIB_SET(arch)
LJLIB_PUSH(top-3) LJLIB_SET(version_num)
LJLIB_PUSH(top-2) LJLIB_SET(version)
#include "lj_libdef.h"
/* -- jit.util.* functions ------------------------------------------------ */
#define LJLIB_MODULE_jit_util
/* -- Reflection API for Lua functions ------------------------------------ */
/* Return prototype of first argument (Lua function or prototype object) */
static GCproto *check_Lproto(lua_State *L, int nolua)
{
TValue *o = L->base;
if (L->top > o) {
if (tvisproto(o)) {
return protoV(o);
} else if (tvisfunc(o)) {
if (isluafunc(funcV(o)))
return funcproto(funcV(o));
else if (nolua)
return NULL;
}
}
lj_err_argt(L, 1, LUA_TFUNCTION);
return NULL; /* unreachable */
}
static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val)
{
setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val);
}
/* local info = jit.util.funcinfo(func [,pc]) */
LJLIB_CF(jit_util_funcinfo)
{
GCproto *pt = check_Lproto(L, 1);
if (pt) {
BCPos pc = (BCPos)lj_lib_optint(L, 2, 0);
GCtab *t;
lua_createtable(L, 0, 16); /* Increment hash size if fields are added. */
t = tabV(L->top-1);
setintfield(L, t, "linedefined", pt->linedefined);
setintfield(L, t, "lastlinedefined", pt->lastlinedefined);
setintfield(L, t, "stackslots", pt->framesize);
setintfield(L, t, "params", pt->numparams);
setintfield(L, t, "bytecodes", (int32_t)pt->sizebc);
setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc);
setintfield(L, t, "nconsts", (int32_t)pt->sizekn);
setintfield(L, t, "upvalues", (int32_t)pt->sizeuv);
if (pc > 0)
setintfield(L, t, "currentline", pt->lineinfo ? pt->lineinfo[pc-1] : 0);
lua_pushboolean(L, (pt->flags & PROTO_IS_VARARG));
lua_setfield(L, -2, "isvararg");
setstrV(L, L->top++, pt->chunkname);
lua_setfield(L, -2, "source");
lj_err_pushloc(L, pt, pc);
lua_setfield(L, -2, "loc");
} else {
GCfunc *fn = funcV(L->base);
GCtab *t;
lua_createtable(L, 0, 2); /* Increment hash size if fields are added. */
t = tabV(L->top-1);
setintfield(L, t, "ffid", fn->c.ffid);
setintfield(L, t, "upvalues", fn->c.nupvalues);
}
return 1;
}
/* local ins, m = jit.util.funcbc(func, pc) */
LJLIB_CF(jit_util_funcbc)
{
GCproto *pt = check_Lproto(L, 0);
BCPos pc = (BCPos)lj_lib_checkint(L, 2) - 1;
if (pc < pt->sizebc) {
BCIns ins = pt->bc[pc];
BCOp op = bc_op(ins);
lua_assert(op < BC__MAX);
setintV(L->top, ins);
setintV(L->top+1, lj_bc_mode[op]);
L->top += 2;
return 2;
}
return 0;
}
/* local k = jit.util.funck(func, idx) */
LJLIB_CF(jit_util_funck)
{
GCproto *pt = check_Lproto(L, 0);
MSize idx = (MSize)lj_lib_checkint(L, 2);
if ((int32_t)idx >= 0) {
if (idx < pt->sizekn) {
setnumV(L->top-1, pt->k.n[idx]);
return 1;
}
} else {
if (~idx < pt->sizekgc) {
GCobj *gc = gcref(pt->k.gc[idx]);
setgcV(L, L->top-1, &gc->gch, ~gc->gch.gct);
return 1;
}
}
return 0;
}
/* local name = jit.util.funcuvname(func, idx) */
LJLIB_CF(jit_util_funcuvname)
{
GCproto *pt = check_Lproto(L, 0);
uint32_t idx = (uint32_t)lj_lib_checkint(L, 2);
if (idx < pt->sizeuvname) {
setstrV(L, L->top-1, pt->uvname[idx]);
return 1;
}
return 0;
}
/* -- Reflection API for traces ------------------------------------------- */
#if LJ_HASJIT
/* Check trace argument. Must not throw for non-existent trace numbers. */
static Trace *jit_checktrace(lua_State *L)
{
TraceNo tr = (TraceNo)lj_lib_checkint(L, 1);
jit_State *J = L2J(L);
if (tr > 0 && tr < J->sizetrace)
return J->trace[tr];
return NULL;
}
/* local info = jit.util.traceinfo(tr) */
LJLIB_CF(jit_util_traceinfo)
{
Trace *T = jit_checktrace(L);
if (T) {
GCtab *t;
lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */
t = tabV(L->top-1);
setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1);
setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk);
setintfield(L, t, "link", T->link);
setintfield(L, t, "nexit", T->nsnap);
/* There are many more fields. Add them only when needed. */
return 1;
}
return 0;
}
/* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */
LJLIB_CF(jit_util_traceir)
{
Trace *T = jit_checktrace(L);
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
if (T && ref >= REF_BIAS && ref < T->nins) {
IRIns *ir = &T->ir[ref];
int32_t m = lj_ir_mode[ir->o];
setintV(L->top-2, m);
setintV(L->top-1, ir->ot);
setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0));
setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0));
setintV(L->top++, ir->prev);
return 5;
}
return 0;
}
/* local k, t [, slot] = jit.util.tracek(tr, idx) */
LJLIB_CF(jit_util_tracek)
{
Trace *T = jit_checktrace(L);
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
if (T && ref >= T->nk && ref < REF_BIAS) {
IRIns *ir = &T->ir[ref];
int32_t slot = -1;
if (ir->o == IR_KSLOT) {
slot = ir->op2;
ir = &T->ir[ir->op1];
}
lj_ir_kvalue(L, L->top-2, ir);
setintV(L->top-1, (int32_t)irt_type(ir->t));
if (slot == -1)
return 2;
setintV(L->top++, slot);
return 3;
}
return 0;
}
/* local snap = jit.util.tracesnap(tr, sn) */
LJLIB_CF(jit_util_tracesnap)
{
Trace *T = jit_checktrace(L);
SnapNo sn = (SnapNo)lj_lib_checkint(L, 2);
if (T && sn < T->nsnap) {
SnapShot *snap = &T->snap[sn];
IRRef2 *map = &T->snapmap[snap->mapofs];
BCReg s, nslots = snap->nslots;
GCtab *t;
lua_createtable(L, nslots ? (int)nslots : 1, 0);
t = tabV(L->top-1);
setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS);
for (s = 0; s < nslots; s++) {
TValue *o = lj_tab_setint(L, t, (int32_t)(s+1));
IRRef ref = snap_ref(map[s]);
if (ref)
setintV(o, (int32_t)ref - REF_BIAS);
else
setboolV(o, 0);
}
return 1;
}
return 0;
}
/* local mcode, addr, loop = jit.util.tracemc(tr) */
LJLIB_CF(jit_util_tracemc)
{
Trace *T = jit_checktrace(L);
if (T && T->mcode != NULL) {
setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode));
setnumV(L->top++, cast_num((intptr_t)T->mcode));
setintV(L->top++, T->mcloop);
return 3;
}
return 0;
}
/* local addr = jit.util.traceexitstub(idx) */
LJLIB_CF(jit_util_traceexitstub)
{
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1);
jit_State *J = L2J(L);
if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) {
setnumV(L->top-1, cast_num((intptr_t)exitstub_addr(J, exitno)));
return 1;
}
return 0;
}
#else
static int trace_nojit(lua_State *L)
{
UNUSED(L);
return 0;
}
#define lj_cf_jit_util_traceinfo trace_nojit
#define lj_cf_jit_util_traceir trace_nojit
#define lj_cf_jit_util_tracek trace_nojit
#define lj_cf_jit_util_tracesnap trace_nojit
#define lj_cf_jit_util_tracemc trace_nojit
#define lj_cf_jit_util_traceexitstub trace_nojit
#endif
#include "lj_libdef.h"
/* -- jit.opt module ------------------------------------------------------ */
#define LJLIB_MODULE_jit_opt
#if LJ_HASJIT
/* Parse optimization level. */
static int jitopt_level(jit_State *J, const char *str)
{
if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') {
uint32_t flags;
if (str[0] == '0') flags = JIT_F_OPT_0;
else if (str[0] == '1') flags = JIT_F_OPT_1;
else if (str[0] == '2') flags = JIT_F_OPT_2;
else flags = JIT_F_OPT_3;
J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags;
return 1; /* Ok. */
}
return 0; /* No match. */
}
/* Parse optimization flag. */
static int jitopt_flag(jit_State *J, const char *str)
{
const char *lst = JIT_F_OPTSTRING;
uint32_t opt;
int set = 1;
if (str[0] == '+') {
str++;
} else if (str[0] == '-') {
str++;
set = 0;
} else if (str[0] == 'n' && str[1] == 'o') {
str += str[2] == '-' ? 3 : 2;
set = 0;
}
for (opt = JIT_F_OPT_FIRST; ; opt <<= 1) {
size_t len = *(const uint8_t *)lst;
if (len == 0)
break;
if (strncmp(str, lst+1, len) == 0 && str[len] == '\0') {
if (set) J->flags |= opt; else J->flags &= ~opt;
return 1; /* Ok. */
}
lst += 1+len;
}
return 0; /* No match. */
}
/* Forward declaration. */
static void jit_init_hotcount(jit_State *J);
/* Parse optimization parameter. */
static int jitopt_param(jit_State *J, const char *str)
{
const char *lst = JIT_P_STRING;
int i;
for (i = 0; i < JIT_P__MAX; i++) {
size_t len = *(const uint8_t *)lst;
TValue tv;
lua_assert(len != 0);
if (strncmp(str, lst+1, len) == 0 && str[len] == '=' &&
lj_str_numconv(&str[len+1], &tv)) {
J->param[i] = lj_num2int(tv.n);
if (i == JIT_P_hotloop)
jit_init_hotcount(J);
return 1; /* Ok. */
}
lst += 1+len;
}
return 0; /* No match. */
}
#endif
/* jit.opt.start(flags...) */
LJLIB_CF(jit_opt_start)
{
#if LJ_HASJIT
jit_State *J = L2J(L);
int nargs = (int)(L->top - L->base);
if (nargs == 0) {
J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT;
} else {
int i;
for (i = 1; i <= nargs; i++) {
const char *str = strdata(lj_lib_checkstr(L, i));
if (!jitopt_level(J, str) &&
!jitopt_flag(J, str) &&
!jitopt_param(J, str))
lj_err_callerv(L, LJ_ERR_JITOPT, str);
}
}
#else
lj_err_caller(L, LJ_ERR_NOJIT);
#endif
return 0;
}
#include "lj_libdef.h"
/* -- JIT compiler initialization ----------------------------------------- */
#if LJ_HASJIT
/* Default values for JIT parameters. */
static const int32_t jit_param_default[JIT_P__MAX+1] = {
#define JIT_PARAMINIT(len, name, value) (value),
JIT_PARAMDEF(JIT_PARAMINIT)
#undef JIT_PARAMINIT
0
};
/* Initialize hotcount table. */
static void jit_init_hotcount(jit_State *J)
{
HotCount start = (HotCount)J->param[JIT_P_hotloop];
HotCount *hotcount = J2GG(J)->hotcount;
uint32_t i;
for (i = 0; i < HOTCOUNT_SIZE; i++)
hotcount[i] = start;
}
#endif
/* Arch-dependent CPU detection. */
static uint32_t jit_cpudetect(lua_State *L)
{
uint32_t flags = 0;
#if LJ_TARGET_X86ORX64
uint32_t vendor[4];
uint32_t features[4];
if (lj_vm_cpuid(0, vendor) && lj_vm_cpuid(1, features)) {
#if !LJ_HASJIT
#define JIT_F_CMOV 1
#endif
flags |= ((features[3] >> 15)&1) * JIT_F_CMOV;
#if LJ_HASJIT
flags |= ((features[3] >> 26)&1) * JIT_F_SSE2;
flags |= ((features[2] >> 19)&1) * JIT_F_SSE4_1;
if (vendor[2] == 0x6c65746e) { /* Intel. */
if ((features[0] & 0x0ff00f00) == 0x00000f00) /* P4. */
flags |= JIT_F_P4; /* Currently unused. */
else if ((features[0] & 0x0fff0ff0) == 0x000106c0) /* Atom. */
flags |= JIT_F_LEA_AGU;
} else if (vendor[2] == 0x444d4163) { /* AMD. */
uint32_t fam = (features[0] & 0x0ff00f00);
if (fam == 0x00000f00) /* K8. */
flags |= JIT_F_SPLIT_XMM;
if (fam >= 0x00000f00) /* K8, K10. */
flags |= JIT_F_PREFER_IMUL;
}
#endif
}
#ifndef LUAJIT_CPU_NOCMOV
if (!(flags & JIT_F_CMOV))
luaL_error(L, "Ancient CPU lacks CMOV support (recompile with -DLUAJIT_CPU_NOCMOV)");
#endif
#if LJ_HASJIT
if (!(flags & JIT_F_SSE2))
luaL_error(L, "Sorry, SSE2 CPU support required for this beta release");
#endif
UNUSED(L);
#else
#error "Missing CPU detection for this architecture"
#endif
return flags;
}
/* Initialize JIT compiler. */
static void jit_init(lua_State *L)
{
uint32_t flags = jit_cpudetect(L);
#if LJ_HASJIT
jit_State *J = L2J(L);
J->flags = flags | JIT_F_ON | JIT_F_OPT_DEFAULT;
memcpy(J->param, jit_param_default, sizeof(J->param));
jit_init_hotcount(J);
lj_dispatch_update(G(L));
#else
UNUSED(flags);
#endif
}
LUALIB_API int luaopen_jit(lua_State *L)
{
lua_pushliteral(L, LJ_ARCH_NAME);
lua_pushinteger(L, LUAJIT_VERSION_NUM);
lua_pushliteral(L, LUAJIT_VERSION);
LJ_LIB_REG(L, jit);
#ifndef LUAJIT_DISABLE_JITUTIL
LJ_LIB_REG_(L, "jit.util", jit_util);
#endif
LJ_LIB_REG_(L, "jit.opt", jit_opt);
L->top -= 2;
jit_init(L);
return 1;
}

188
src/lib_math.c Normal file
View File

@ -0,0 +1,188 @@
/*
** Math library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#include <math.h>
#define lib_math_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
#define LJLIB_MODULE_math
LJLIB_ASM(math_abs) LJLIB_REC(.)
{
lj_lib_checknum(L, 1);
return FFH_RETRY;
}
LJLIB_ASM_(math_floor) LJLIB_REC(math_round IRFPM_FLOOR)
LJLIB_ASM_(math_ceil) LJLIB_REC(math_round IRFPM_CEIL)
LJLIB_ASM_(math_sqrt) LJLIB_REC(math_unary IRFPM_SQRT)
LJLIB_ASM_(math_log) LJLIB_REC(math_unary IRFPM_LOG)
LJLIB_ASM_(math_log10) LJLIB_REC(math_unary IRFPM_LOG10)
LJLIB_ASM_(math_exp) LJLIB_REC(math_unary IRFPM_EXP)
LJLIB_ASM_(math_sin) LJLIB_REC(math_unary IRFPM_SIN)
LJLIB_ASM_(math_cos) LJLIB_REC(math_unary IRFPM_COS)
LJLIB_ASM_(math_tan) LJLIB_REC(math_unary IRFPM_TAN)
LJLIB_ASM_(math_asin) LJLIB_REC(math_atrig FF_math_asin)
LJLIB_ASM_(math_acos) LJLIB_REC(math_atrig FF_math_acos)
LJLIB_ASM_(math_atan) LJLIB_REC(math_atrig FF_math_atan)
LJLIB_ASM_(math_sinh)
LJLIB_ASM_(math_cosh)
LJLIB_ASM_(math_tanh)
LJLIB_ASM_(math_frexp)
LJLIB_ASM_(math_modf) LJLIB_REC(.)
LJLIB_PUSH(57.29577951308232)
LJLIB_ASM_(math_deg) LJLIB_REC(math_degrad)
LJLIB_PUSH(0.017453292519943295)
LJLIB_ASM_(math_rad) LJLIB_REC(math_degrad)
LJLIB_ASM(math_atan2) LJLIB_REC(math_binary IR_ATAN2)
{
lj_lib_checknum(L, 1);
lj_lib_checknum(L, 2);
return FFH_RETRY;
}
LJLIB_ASM_(math_ldexp) LJLIB_REC(math_binary IR_LDEXP)
LJLIB_ASM_(math_pow) LJLIB_REC(.)
LJLIB_ASM_(math_fmod)
LJLIB_ASM(math_min) LJLIB_REC(math_minmax IR_MIN)
{
int i = 0;
do { lj_lib_checknum(L, ++i); } while (L->base+i < L->top);
return FFH_RETRY;
}
LJLIB_ASM_(math_max) LJLIB_REC(math_minmax IR_MAX)
LJLIB_PUSH(3.14159265358979323846) LJLIB_SET(pi)
LJLIB_PUSH(1e310) LJLIB_SET(huge)
#ifdef __MACH__
LJ_FUNCA double lj_wrapper_sinh(double x) { return sinh(x); }
LJ_FUNCA double lj_wrapper_cosh(double x) { return cosh(x); }
LJ_FUNCA double lj_wrapper_tanh(double x) { return tanh(x); }
#endif
/* ------------------------------------------------------------------------ */
/* This implements a Tausworthe PRNG with period 2^223. Based on:
** Tables of maximally-equidistributed combined LFSR generators,
** Pierre L'Ecuyer, 1991, table 3, 1st entry.
** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
*/
/* PRNG state. */
typedef struct TW223State {
uint64_t gen[4]; /* State of the 4 LFSR generators. */
int valid; /* State is valid. */
} TW223State;
/* Union needed for bit-pattern conversion between uint64_t and double. */
typedef union { uint64_t u64; double d; } U64double;
/* Update generator i and compute a running xor of all states. */
#define TW223_GEN(i, k, q, s) \
z = tw->gen[i]; \
z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
r ^= z; tw->gen[i] = z;
/* PRNG step function. Returns a double in the range 0.0 <= d < 1.0. */
static double tw223_step(TW223State *tw)
{
uint64_t z, r = 0;
U64double u;
TW223_GEN(0, 63, 31, 18)
TW223_GEN(1, 58, 19, 28)
TW223_GEN(2, 55, 24, 7)
TW223_GEN(3, 47, 21, 8)
u.u64 = (r & (((uint64_t)1 << 52)-1)) | ((uint64_t)0x3ff << 52);
#if defined(__GNUC__) && LJ_TARGET_X86 && __pic__
/* Compensate for unbelievable GCC pessimization. */
{
volatile U64double u1;
u1.u64 = (uint64_t)0x3f8 << 52;
return u.d - u1.d;
}
#else
return u.d - 1.0;
#endif
}
/* PRNG initialization function. */
static void tw223_init(TW223State *tw, double d)
{
uint32_t r = 0x11090601; /* 64-k[i] as four 8 bit constants. */
int i;
for (i = 0; i < 4; i++) {
U64double u;
uint32_t m = 1u << (r&255);
r >>= 8;
u.d = d = d * 3.14159265358979323846 + 2.7182818284590452354;
if (u.u64 < m) u.u64 += m; /* Ensure k[i] MSB of gen[i] are non-zero. */
tw->gen[i] = u.u64;
}
tw->valid = 1;
for (i = 0; i < 10; i++)
tw223_step(tw);
}
/* PRNG extract function. */
LJLIB_PUSH(top-2) /* Upvalue holds userdata with TW223State. */
LJLIB_CF(math_random)
{
int n = cast_int(L->top - L->base);
TW223State *tw = (TW223State *)(uddata(udataV(lj_lib_upvalue(L, 1))));
double d;
if (LJ_UNLIKELY(!tw->valid)) tw223_init(tw, 0.0);
d = tw223_step(tw);
if (n > 0) {
double r1 = lj_lib_checknum(L, 1);
if (n == 1) {
d = floor(d*r1) + 1.0; /* d is an int in range [1, r1] */
} else {
double r2 = lj_lib_checknum(L, 2);
d = floor(d*(r2-r1+1.0)) + r1; /* d is an int in range [r1, r2] */
}
} /* else: d is a double in range [0, 1] */
setnumV(L->top++, d);
return 1;
}
/* PRNG seed function. */
LJLIB_PUSH(top-2) /* Upvalue holds userdata with TW223State. */
LJLIB_CF(math_randomseed)
{
TW223State *tw = (TW223State *)(uddata(udataV(lj_lib_upvalue(L, 1))));
tw223_init(tw, lj_lib_checknum(L, 1));
return 0;
}
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
LUALIB_API int luaopen_math(lua_State *L)
{
TW223State *tw;
tw = (TW223State *)lua_newuserdata(L, sizeof(TW223State));
tw->valid = 0; /* Use lazy initialization to save some time on startup. */
LJ_LIB_REG(L, math);
#if defined(LUA_COMPAT_MOD)
lua_getfield(L, -1, "fmod");
lua_setfield(L, -2, "mod");
#endif
return 1;
}

249
src/lib_os.c Normal file
View File

@ -0,0 +1,249 @@
/*
** OS library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <errno.h>
#include <locale.h>
#include <time.h>
#define lib_os_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#ifdef LUA_USE_POSIX
#include <unistd.h>
#else
#include <stdio.h>
#endif
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
#define LJLIB_MODULE_os
static int os_pushresult(lua_State *L, int i, const char *filename)
{
int en = errno; /* calls to Lua API may change this value */
if (i) {
setboolV(L->top-1, 1);
return 1;
} else {
setnilV(L->top-1);
lua_pushfstring(L, "%s: %s", filename, strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
LJLIB_CF(os_execute)
{
lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
return 1;
}
LJLIB_CF(os_remove)
{
const char *filename = luaL_checkstring(L, 1);
return os_pushresult(L, remove(filename) == 0, filename);
}
LJLIB_CF(os_rename)
{
const char *fromname = luaL_checkstring(L, 1);
const char *toname = luaL_checkstring(L, 2);
return os_pushresult(L, rename(fromname, toname) == 0, fromname);
}
LJLIB_CF(os_tmpname)
{
#ifdef LUA_USE_POSIX
char buf[15+1];
int fp;
strcpy(buf, "/tmp/lua_XXXXXX");
fp = mkstemp(buf);
if (fp != -1)
close(fp);
else
lj_err_caller(L, LJ_ERR_OSUNIQF);
#else
char buf[L_tmpnam];
if (tmpnam(buf) == NULL)
lj_err_caller(L, LJ_ERR_OSUNIQF);
#endif
lua_pushstring(L, buf);
return 1;
}
LJLIB_CF(os_getenv)
{
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
return 1;
}
LJLIB_CF(os_exit)
{
exit(lj_lib_optint(L, 1, EXIT_SUCCESS));
return 0; /* to avoid warnings */
}
LJLIB_CF(os_clock)
{
setnumV(L->top++, ((lua_Number)clock())*(1.0/(lua_Number)CLOCKS_PER_SEC));
return 1;
}
/* ------------------------------------------------------------------------ */
static void setfield(lua_State *L, const char *key, int value)
{
lua_pushinteger(L, value);
lua_setfield(L, -2, key);
}
static void setboolfield(lua_State *L, const char *key, int value)
{
if (value < 0) /* undefined? */
return; /* does not set field */
lua_pushboolean(L, value);
lua_setfield(L, -2, key);
}
static int getboolfield(lua_State *L, const char *key)
{
int res;
lua_getfield(L, -1, key);
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
}
static int getfield(lua_State *L, const char *key, int d)
{
int res;
lua_getfield(L, -1, key);
if (lua_isnumber(L, -1)) {
res = (int)lua_tointeger(L, -1);
} else {
if (d < 0)
lj_err_callerv(L, LJ_ERR_OSDATEF, key);
res = d;
}
lua_pop(L, 1);
return res;
}
LJLIB_CF(os_date)
{
const char *s = luaL_optstring(L, 1, "%c");
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
struct tm *stm;
if (*s == '!') { /* UTC? */
stm = gmtime(&t);
s++; /* skip `!' */
} else {
stm = localtime(&t);
}
if (stm == NULL) { /* invalid date? */
setnilV(L->top-1);
} else if (strcmp(s, "*t") == 0) {
lua_createtable(L, 0, 9); /* 9 = number of fields */
setfield(L, "sec", stm->tm_sec);
setfield(L, "min", stm->tm_min);
setfield(L, "hour", stm->tm_hour);
setfield(L, "day", stm->tm_mday);
setfield(L, "month", stm->tm_mon+1);
setfield(L, "year", stm->tm_year+1900);
setfield(L, "wday", stm->tm_wday+1);
setfield(L, "yday", stm->tm_yday+1);
setboolfield(L, "isdst", stm->tm_isdst);
} else {
char cc[3];
luaL_Buffer b;
cc[0] = '%'; cc[2] = '\0';
luaL_buffinit(L, &b);
for (; *s; s++) {
if (*s != '%' || *(s + 1) == '\0') { /* no conversion specifier? */
luaL_addchar(&b, *s);
} else {
size_t reslen;
char buff[200]; /* should be big enough for any conversion result */
cc[1] = *(++s);
reslen = strftime(buff, sizeof(buff), cc, stm);
luaL_addlstring(&b, buff, reslen);
}
}
luaL_pushresult(&b);
}
return 1;
}
LJLIB_CF(os_time)
{
time_t t;
if (lua_isnoneornil(L, 1)) { /* called without args? */
t = time(NULL); /* get current time */
} else {
struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */
ts.tm_sec = getfield(L, "sec", 0);
ts.tm_min = getfield(L, "min", 0);
ts.tm_hour = getfield(L, "hour", 12);
ts.tm_mday = getfield(L, "day", -1);
ts.tm_mon = getfield(L, "month", -1) - 1;
ts.tm_year = getfield(L, "year", -1) - 1900;
ts.tm_isdst = getboolfield(L, "isdst");
t = mktime(&ts);
}
if (t == (time_t)(-1))
lua_pushnil(L);
else
lua_pushnumber(L, (lua_Number)t);
return 1;
}
LJLIB_CF(os_difftime)
{
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
(time_t)(luaL_optnumber(L, 2, (lua_Number)0))));
return 1;
}
/* ------------------------------------------------------------------------ */
LJLIB_CF(os_setlocale)
{
GCstr *s = lj_lib_optstr(L, 1);
const char *str = s ? strdata(s) : NULL;
int opt = lj_lib_checkopt(L, 2, 6,
"\5ctype\7numeric\4time\7collate\10monetary\1\377\3all");
if (opt == 0) opt = LC_CTYPE;
else if (opt == 1) opt = LC_NUMERIC;
else if (opt == 2) opt = LC_TIME;
else if (opt == 3) opt = LC_COLLATE;
else if (opt == 4) opt = LC_MONETARY;
else if (opt == 6) opt = LC_ALL;
lua_pushstring(L, setlocale(opt, str));
return 1;
}
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
LUALIB_API int luaopen_os(lua_State *L)
{
LJ_LIB_REG(L, os);
return 1;
}

508
src/lib_package.c Normal file
View File

@ -0,0 +1,508 @@
/*
** Package library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lib_package_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
/* Error codes for ll_loadfunc. */
#define PACKAGE_ERR_LIB 1
#define PACKAGE_ERR_FUNC 2
/* Redefined in platform specific part. */
#define PACKAGE_LIB_FAIL "open"
#define setprogdir(L) ((void)0)
#if defined(LUA_DL_DLOPEN)
#include <dlfcn.h>
static void ll_unloadlib(void *lib)
{
dlclose(lib);
}
static void *ll_load(lua_State *L, const char *path)
{
void *lib = dlopen(path, RTLD_NOW);
if (lib == NULL) lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
{
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
if (f == NULL) lua_pushstring(L, dlerror());
return f;
}
#elif defined(LUA_DL_DLL)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef setprogdir
static void setprogdir(lua_State *L)
{
char buff[MAX_PATH + 1];
char *lb;
DWORD nsize = sizeof(buff);
DWORD n = GetModuleFileNameA(NULL, buff, nsize);
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) {
luaL_error(L, "unable to get ModuleFileName");
} else {
*lb = '\0';
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
lua_remove(L, -2); /* remove original string */
}
}
static void pusherror(lua_State *L)
{
DWORD error = GetLastError();
char buffer[128];
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, buffer, sizeof(buffer), NULL))
lua_pushstring(L, buffer);
else
lua_pushfstring(L, "system error %d\n", error);
}
static void ll_unloadlib(void *lib)
{
FreeLibrary((HINSTANCE)lib);
}
static void *ll_load(lua_State *L, const char *path)
{
HINSTANCE lib = LoadLibraryA(path);
if (lib == NULL) pusherror(L);
return lib;
}
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
{
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
if (f == NULL) pusherror(L);
return f;
}
#else
#undef PACKAGE_LIB_FAIL
#define PACKAGE_LIB_FAIL "absent"
#define DLMSG "dynamic libraries not enabled; check your Lua installation"
static void ll_unloadlib(void *lib)
{
(void)lib;
}
static void *ll_load(lua_State *L, const char *path)
{
(void)path;
lua_pushliteral(L, DLMSG);
return NULL;
}
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
{
(void)lib; (void)sym;
lua_pushliteral(L, DLMSG);
return NULL;
}
#endif
/* ------------------------------------------------------------------------ */
static void **ll_register(lua_State *L, const char *path)
{
void **plib;
lua_pushfstring(L, "LOADLIB: %s", path);
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
if (!lua_isnil(L, -1)) { /* is there an entry? */
plib = (void **)lua_touserdata(L, -1);
} else { /* no entry yet; create one */
lua_pop(L, 1);
plib = (void **)lua_newuserdata(L, sizeof(void *));
*plib = NULL;
luaL_getmetatable(L, "_LOADLIB");
lua_setmetatable(L, -2);
lua_pushfstring(L, "LOADLIB: %s", path);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
}
return plib;
}
static int ll_loadfunc(lua_State *L, const char *path, const char *sym)
{
void **reg = ll_register(L, path);
if (*reg == NULL) *reg = ll_load(L, path);
if (*reg == NULL) {
return PACKAGE_ERR_LIB; /* unable to load library */
} else {
lua_CFunction f = ll_sym(L, *reg, sym);
if (f == NULL)
return PACKAGE_ERR_FUNC; /* unable to find function */
lua_pushcfunction(L, f);
return 0; /* return function */
}
}
static int lj_cf_package_loadlib(lua_State *L)
{
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = ll_loadfunc(L, path, init);
if (stat == 0) { /* no errors? */
return 1; /* return the loaded function */
} else { /* error; error message is on stack top */
lua_pushnil(L);
lua_insert(L, -2);
lua_pushstring(L, (stat == PACKAGE_ERR_LIB) ? PACKAGE_LIB_FAIL : "init");
return 3; /* return nil, error message, and where */
}
}
static int lj_cf_package_unloadlib(lua_State *L)
{
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
if (*lib) ll_unloadlib(*lib);
*lib = NULL; /* mark library as closed */
return 0;
}
/* ------------------------------------------------------------------------ */
static int readable(const char *filename)
{
FILE *f = fopen(filename, "r"); /* try to open file */
if (f == NULL) return 0; /* open failed */
fclose(f);
return 1;
}
static const char *pushnexttemplate(lua_State *L, const char *path)
{
const char *l;
while (*path == *LUA_PATHSEP) path++; /* skip separators */
if (*path == '\0') return NULL; /* no more templates */
l = strchr(path, *LUA_PATHSEP); /* find next separator */
if (l == NULL) l = path + strlen(path);
lua_pushlstring(L, path, (size_t)(l - path)); /* template */
return l;
}
static const char *findfile(lua_State *L, const char *name,
const char *pname)
{
const char *path;
name = luaL_gsub(L, name, ".", LUA_DIRSEP);
lua_getfield(L, LUA_ENVIRONINDEX, pname);
path = lua_tostring(L, -1);
if (path == NULL)
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
lua_pushliteral(L, ""); /* error accumulator */
while ((path = pushnexttemplate(L, path)) != NULL) {
const char *filename;
filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
lua_remove(L, -2); /* remove path template */
if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
lua_remove(L, -2); /* remove file name */
lua_concat(L, 2); /* add entry to possible error message */
}
return NULL; /* not found */
}
static void loaderror(lua_State *L, const char *filename)
{
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
lua_tostring(L, 1), filename, lua_tostring(L, -1));
}
static int lj_cf_package_loader_lua(lua_State *L)
{
const char *filename;
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
if (luaL_loadfile(L, filename) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static const char *mkfuncname(lua_State *L, const char *modname)
{
const char *funcname;
const char *mark = strchr(modname, *LUA_IGMARK);
if (mark) modname = mark + 1;
funcname = luaL_gsub(L, modname, ".", "_");
funcname = lua_pushfstring(L, "luaopen_%s", funcname);
lua_remove(L, -2); /* remove 'gsub' result */
return funcname;
}
static int lj_cf_package_loader_c(lua_State *L)
{
const char *funcname;
const char *name = luaL_checkstring(L, 1);
const char *filename = findfile(L, name, "cpath");
if (filename == NULL) return 1; /* library not found in this path */
funcname = mkfuncname(L, name);
if (ll_loadfunc(L, filename, funcname) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static int lj_cf_package_loader_croot(lua_State *L)
{
const char *funcname;
const char *filename;
const char *name = luaL_checkstring(L, 1);
const char *p = strchr(name, '.');
int stat;
if (p == NULL) return 0; /* is root */
lua_pushlstring(L, name, (size_t)(p - name));
filename = findfile(L, lua_tostring(L, -1), "cpath");
if (filename == NULL) return 1; /* root not found */
funcname = mkfuncname(L, name);
if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
if (stat != PACKAGE_ERR_FUNC) loaderror(L, filename); /* real error */
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
name, filename);
return 1; /* function not found */
}
return 1;
}
static int lj_cf_package_loader_preload(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.preload") " must be a table");
lua_getfield(L, -1, name);
if (lua_isnil(L, -1)) /* not found? */
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
return 1;
}
/* ------------------------------------------------------------------------ */
static const int sentinel_ = 0;
#define sentinel ((void *)&sentinel_)
static int lj_cf_package_require(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
int i;
lua_settop(L, 1); /* _LOADED table will be at index 2 */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
if (lua_touserdata(L, -1) == sentinel) /* check loops */
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
return 1; /* package is already loaded */
}
/* else must load it; iterate over available loaders */
lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.loaders") " must be a table");
lua_pushliteral(L, ""); /* error message accumulator */
for (i = 1; ; i++) {
lua_rawgeti(L, -2, i); /* get a loader */
if (lua_isnil(L, -1))
luaL_error(L, "module " LUA_QS " not found:%s",
name, lua_tostring(L, -2));
lua_pushstring(L, name);
lua_call(L, 1, 1); /* call it */
if (lua_isfunction(L, -1)) /* did it find module? */
break; /* module loaded successfully */
else if (lua_isstring(L, -1)) /* loader returned error message? */
lua_concat(L, 2); /* accumulate it */
else
lua_pop(L, 1);
}
lua_pushlightuserdata(L, sentinel);
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
lua_call(L, 1, 1); /* run loaded module */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
lua_getfield(L, 2, name);
if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_setfield(L, 2, name); /* _LOADED[name] = true */
}
return 1;
}
/* ------------------------------------------------------------------------ */
static void setfenv(lua_State *L)
{
lua_Debug ar;
if (lua_getstack(L, 1, &ar) == 0 ||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
lua_iscfunction(L, -1))
luaL_error(L, LUA_QL("module") " not called from a Lua function");
lua_pushvalue(L, -2);
lua_setfenv(L, -2);
lua_pop(L, 1);
}
static void dooptions(lua_State *L, int n)
{
int i;
for (i = 2; i <= n; i++) {
lua_pushvalue(L, i); /* get option (a function) */
lua_pushvalue(L, -2); /* module */
lua_call(L, 1, 0);
}
}
static void modinit(lua_State *L, const char *modname)
{
const char *dot;
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_M"); /* module._M = module */
lua_pushstring(L, modname);
lua_setfield(L, -2, "_NAME");
dot = strrchr(modname, '.'); /* look for last dot in module name */
if (dot == NULL) dot = modname; else dot++;
/* set _PACKAGE as package name (full module name minus last part) */
lua_pushlstring(L, modname, (size_t)(dot - modname));
lua_setfield(L, -2, "_PACKAGE");
}
static int lj_cf_package_module(lua_State *L)
{
const char *modname = luaL_checkstring(L, 1);
int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
lj_err_callerv(L, LJ_ERR_BADMODN, modname);
lua_pushvalue(L, -1);
lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
}
/* check whether table already has a _NAME field */
lua_getfield(L, -1, "_NAME");
if (!lua_isnil(L, -1)) { /* is table an initialized module? */
lua_pop(L, 1);
} else { /* no; initialize it */
lua_pop(L, 1);
modinit(L, modname);
}
lua_pushvalue(L, -1);
setfenv(L);
dooptions(L, loaded - 1);
return 0;
}
static int lj_cf_package_seeall(lua_State *L)
{
luaL_checktype(L, 1, LUA_TTABLE);
if (!lua_getmetatable(L, 1)) {
lua_createtable(L, 0, 1); /* create new metatable */
lua_pushvalue(L, -1);
lua_setmetatable(L, 1);
}
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index"); /* mt.__index = _G */
return 0;
}
/* ------------------------------------------------------------------------ */
#define AUXMARK "\1"
static void setpath(lua_State *L, const char *fieldname, const char *envname,
const char *def)
{
const char *path = getenv(envname);
if (path == NULL) {
lua_pushstring(L, def);
} else {
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
LUA_PATHSEP AUXMARK LUA_PATHSEP);
luaL_gsub(L, path, AUXMARK, def);
lua_remove(L, -2);
}
setprogdir(L);
lua_setfield(L, -2, fieldname);
}
static const luaL_Reg package_lib[] = {
{ "loadlib", lj_cf_package_loadlib },
{ "seeall", lj_cf_package_seeall },
{ NULL, NULL }
};
static const luaL_Reg package_global[] = {
{ "module", lj_cf_package_module },
{ "require", lj_cf_package_require },
{ NULL, NULL }
};
static const lua_CFunction package_loaders[] =
{
lj_cf_package_loader_preload,
lj_cf_package_loader_lua,
lj_cf_package_loader_c,
lj_cf_package_loader_croot,
NULL
};
LUALIB_API int luaopen_package(lua_State *L)
{
int i;
luaL_newmetatable(L, "_LOADLIB");
lua_pushcfunction(L, lj_cf_package_unloadlib);
lua_setfield(L, -2, "__gc");
luaL_register(L, LUA_LOADLIBNAME, package_lib);
lua_pushvalue(L, -1);
lua_replace(L, LUA_ENVIRONINDEX);
lua_createtable(L, sizeof(package_loaders)/sizeof(package_loaders[0])-1, 0);
for (i = 0; package_loaders[i] != NULL; i++) {
lua_pushcfunction(L, package_loaders[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "loaders");
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT);
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT);
lua_pushliteral(L, LUA_PATH_CONFIG);
lua_setfield(L, -2, "config");
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
lua_setfield(L, -2, "loaded");
lua_newtable(L);
lua_setfield(L, -2, "preload");
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, package_global);
lua_pop(L, 1);
return 1;
}

790
src/lib_string.c Normal file
View File

@ -0,0 +1,790 @@
/*
** String library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <stdio.h>
#define lib_string_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_state.h"
#include "lj_ff.h"
#include "lj_ctype.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
#define LJLIB_MODULE_string
LJLIB_ASM(string_len) LJLIB_REC(.)
{
lj_lib_checkstr(L, 1);
return FFH_RETRY;
}
LJLIB_ASM(string_byte) LJLIB_REC(string_range 0)
{
GCstr *s = lj_lib_checkstr(L, 1);
int32_t len = (int32_t)s->len;
int32_t start = lj_lib_optint(L, 2, 1);
int32_t stop = lj_lib_optint(L, 3, start);
int32_t n, i;
const unsigned char *p;
if (stop < 0) stop += len+1;
if (start < 0) start += len+1;
if (start <= 0) start = 1;
if (stop > len) stop = len;
if (start > stop) return FFH_RES(0); /* Empty interval: return no results. */
start--;
n = stop - start;
if ((uint32_t)n > LUAI_MAXCSTACK)
lj_err_caller(L, LJ_ERR_STRSLC);
lj_state_checkstack(L, (MSize)n);
p = (const unsigned char *)strdata(s) + start;
for (i = 0; i < n; i++)
setintV(L->base + i-1, p[i]);
return FFH_RES(n);
}
LJLIB_ASM(string_char)
{
int i, nargs = cast_int(L->top - L->base);
char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, (size_t)nargs);
for (i = 1; i <= nargs; i++) {
int32_t k = lj_lib_checkint(L, i);
if (!checku8(k))
lj_err_arg(L, i, LJ_ERR_BADVAL);
buf[i-1] = (char)k;
}
setstrV(L, L->base-1, lj_str_new(L, buf, (size_t)nargs));
return FFH_RES(1);
}
LJLIB_ASM(string_sub) LJLIB_REC(string_range 1)
{
lj_lib_checkstr(L, 1);
lj_lib_checkint(L, 2);
setintV(L->base+2, lj_lib_optint(L, 3, -1));
return FFH_RETRY;
}
LJLIB_ASM(string_rep)
{
GCstr *s = lj_lib_checkstr(L, 1);
int32_t len = (int32_t)s->len;
int32_t k = lj_lib_checkint(L, 2);
int64_t tlen = (int64_t)k * len;
const char *src;
char *buf;
if (k <= 0) return FFH_RETRY;
if (tlen > LJ_MAX_STR)
lj_err_caller(L, LJ_ERR_STROV);
buf = lj_str_needbuf(L, &G(L)->tmpbuf, (MSize)tlen);
if (len <= 1) return FFH_RETRY; /* ASM code only needed buffer resize. */
src = strdata(s);
do {
int32_t i = 0;
do { *buf++ = src[i++]; } while (i < len);
} while (--k > 0);
setstrV(L, L->base-1, lj_str_new(L, G(L)->tmpbuf.buf, (size_t)tlen));
return FFH_RES(1);
}
LJLIB_ASM(string_reverse)
{
GCstr *s = lj_lib_checkstr(L, 1);
lj_str_needbuf(L, &G(L)->tmpbuf, s->len);
return FFH_RETRY;
}
LJLIB_ASM_(string_lower)
LJLIB_ASM_(string_upper)
/* ------------------------------------------------------------------------ */
LJLIB_CF(string_dump)
{
lj_err_caller(L, LJ_ERR_STRDUMP);
return 0; /* unreachable */
}
/* ------------------------------------------------------------------------ */
/* macro to `unsign' a character */
#define uchar(c) ((unsigned char)(c))
#define CAP_UNFINISHED (-1)
#define CAP_POSITION (-2)
typedef struct MatchState {
const char *src_init; /* init of source string */
const char *src_end; /* end (`\0') of source string */
lua_State *L;
int level; /* total number of captures (finished or unfinished) */
struct {
const char *init;
ptrdiff_t len;
} capture[LUA_MAXCAPTURES];
} MatchState;
#define L_ESC '%'
#define SPECIALS "^$*+?.([%-"
static int check_capture(MatchState *ms, int l)
{
l -= '1';
if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
return l;
}
static int capture_to_close(MatchState *ms)
{
int level = ms->level;
for (level--; level>=0; level--)
if (ms->capture[level].len == CAP_UNFINISHED) return level;
lj_err_caller(ms->L, LJ_ERR_STRPATC);
return 0; /* unreachable */
}
static const char *classend(MatchState *ms, const char *p)
{
switch (*p++) {
case L_ESC:
if (*p == '\0')
lj_err_caller(ms->L, LJ_ERR_STRPATE);
return p+1;
case '[':
if (*p == '^') p++;
do { /* look for a `]' */
if (*p == '\0')
lj_err_caller(ms->L, LJ_ERR_STRPATM);
if (*(p++) == L_ESC && *p != '\0')
p++; /* skip escapes (e.g. `%]') */
} while (*p != ']');
return p+1;
default:
return p;
}
}
static const unsigned char match_class_map[32] = {
0, LJ_CTYPE_ALPHA, 0, LJ_CTYPE_CNTRL, LJ_CTYPE_DIGIT, 0,0,0,0,0,0,0,
LJ_CTYPE_LOWER, 0,0,0, LJ_CTYPE_PUNCT, 0,0, LJ_CTYPE_SPACE, 0,
LJ_CTYPE_UPPER, 0, LJ_CTYPE_ALNUM, LJ_CTYPE_XDIGIT, 0,0,0,0,0,0,0
};
static int match_class(int c, int cl)
{
if ((cl & 0xc0) == 0x40) {
int t = match_class_map[(cl&0x1f)];
if (t) {
t = lj_ctype_isa(c, t);
return (cl & 0x20) ? t : !t;
}
if (cl == 'z') return c == 0;
if (cl == 'Z') return c != 0;
}
return (cl == c);
}
static int matchbracketclass(int c, const char *p, const char *ec)
{
int sig = 1;
if (*(p+1) == '^') {
sig = 0;
p++; /* skip the `^' */
}
while (++p < ec) {
if (*p == L_ESC) {
p++;
if (match_class(c, uchar(*p)))
return sig;
}
else if ((*(p+1) == '-') && (p+2 < ec)) {
p+=2;
if (uchar(*(p-2)) <= c && c <= uchar(*p))
return sig;
}
else if (uchar(*p) == c) return sig;
}
return !sig;
}
static int singlematch(int c, const char *p, const char *ep)
{
switch (*p) {
case '.': return 1; /* matches any char */
case L_ESC: return match_class(c, uchar(*(p+1)));
case '[': return matchbracketclass(c, p, ep-1);
default: return (uchar(*p) == c);
}
}
static const char *match(MatchState *ms, const char *s, const char *p);
static const char *matchbalance(MatchState *ms, const char *s, const char *p)
{
if (*p == 0 || *(p+1) == 0)
lj_err_caller(ms->L, LJ_ERR_STRPATU);
if (*s != *p) {
return NULL;
} else {
int b = *p;
int e = *(p+1);
int cont = 1;
while (++s < ms->src_end) {
if (*s == e) {
if (--cont == 0) return s+1;
} else if (*s == b) {
cont++;
}
}
}
return NULL; /* string ends out of balance */
}
static const char *max_expand(MatchState *ms, const char *s,
const char *p, const char *ep)
{
ptrdiff_t i = 0; /* counts maximum expand for item */
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
i++;
/* keeps trying to match with the maximum repetitions */
while (i>=0) {
const char *res = match(ms, (s+i), ep+1);
if (res) return res;
i--; /* else didn't match; reduce 1 repetition to try again */
}
return NULL;
}
static const char *min_expand(MatchState *ms, const char *s,
const char *p, const char *ep)
{
for (;;) {
const char *res = match(ms, s, ep+1);
if (res != NULL)
return res;
else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
s++; /* try with one more repetition */
else
return NULL;
}
}
static const char *start_capture(MatchState *ms, const char *s,
const char *p, int what)
{
const char *res;
int level = ms->level;
if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
ms->capture[level].init = s;
ms->capture[level].len = what;
ms->level = level+1;
if ((res=match(ms, s, p)) == NULL) /* match failed? */
ms->level--; /* undo capture */
return res;
}
static const char *end_capture(MatchState *ms, const char *s,
const char *p)
{
int l = capture_to_close(ms);
const char *res;
ms->capture[l].len = s - ms->capture[l].init; /* close capture */
if ((res = match(ms, s, p)) == NULL) /* match failed? */
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
return res;
}
static const char *match_capture(MatchState *ms, const char *s, int l)
{
size_t len;
l = check_capture(ms, l);
len = (size_t)ms->capture[l].len;
if ((size_t)(ms->src_end-s) >= len &&
memcmp(ms->capture[l].init, s, len) == 0)
return s+len;
else
return NULL;
}
static const char *match(MatchState *ms, const char *s, const char *p)
{
init: /* using goto's to optimize tail recursion */
switch (*p) {
case '(': /* start capture */
if (*(p+1) == ')') /* position capture? */
return start_capture(ms, s, p+2, CAP_POSITION);
else
return start_capture(ms, s, p+1, CAP_UNFINISHED);
case ')': /* end capture */
return end_capture(ms, s, p+1);
case L_ESC:
switch (*(p+1)) {
case 'b': /* balanced string? */
s = matchbalance(ms, s, p+2);
if (s == NULL) return NULL;
p+=4;
goto init; /* else return match(ms, s, p+4); */
case 'f': { /* frontier? */
const char *ep; char previous;
p += 2;
if (*p != '[')
lj_err_caller(ms->L, LJ_ERR_STRPATB);
ep = classend(ms, p); /* points to what is next */
previous = (s == ms->src_init) ? '\0' : *(s-1);
if (matchbracketclass(uchar(previous), p, ep-1) ||
!matchbracketclass(uchar(*s), p, ep-1)) return NULL;
p=ep;
goto init; /* else return match(ms, s, ep); */
}
default:
if (lj_ctype_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
s = match_capture(ms, s, uchar(*(p+1)));
if (s == NULL) return NULL;
p+=2;
goto init; /* else return match(ms, s, p+2) */
}
goto dflt; /* case default */
}
case '\0': /* end of pattern */
return s; /* match succeeded */
case '$':
if (*(p+1) == '\0') /* is the `$' the last char in pattern? */
return (s == ms->src_end) ? s : NULL; /* check end of string */
else
goto dflt;
default: dflt: { /* it is a pattern item */
const char *ep = classend(ms, p); /* points to what is next */
int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
switch (*ep) {
case '?': { /* optional */
const char *res;
if (m && ((res=match(ms, s+1, ep+1)) != NULL))
return res;
p=ep+1;
goto init; /* else return match(ms, s, ep+1); */
}
case '*': /* 0 or more repetitions */
return max_expand(ms, s, p, ep);
case '+': /* 1 or more repetitions */
return (m ? max_expand(ms, s+1, p, ep) : NULL);
case '-': /* 0 or more repetitions (minimum) */
return min_expand(ms, s, p, ep);
default:
if (!m) return NULL;
s++; p=ep;
goto init; /* else return match(ms, s+1, ep); */
}
}
}
}
static const char *lmemfind(const char *s1, size_t l1,
const char *s2, size_t l2)
{
if (l2 == 0) {
return s1; /* empty strings are everywhere */
} else if (l2 > l1) {
return NULL; /* avoids a negative `l1' */
} else {
const char *init; /* to search for a `*s2' inside `s1' */
l2--; /* 1st char will be checked by `memchr' */
l1 = l1-l2; /* `s2' cannot be found after that */
while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {
init++; /* 1st char is already checked */
if (memcmp(init, s2+1, l2) == 0) {
return init-1;
} else { /* correct `l1' and `s1' to try again */
l1 -= (size_t)(init-s1);
s1 = init;
}
}
return NULL; /* not found */
}
}
static void push_onecapture(MatchState *ms, int i, const char *s, const char *e)
{
if (i >= ms->level) {
if (i == 0) /* ms->level == 0, too */
lua_pushlstring(ms->L, s, (size_t)(e - s)); /* add whole match */
else
lj_err_caller(ms->L, LJ_ERR_STRCAPI);
} else {
ptrdiff_t l = ms->capture[i].len;
if (l == CAP_UNFINISHED) lj_err_caller(ms->L, LJ_ERR_STRCAPU);
if (l == CAP_POSITION)
lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);
else
lua_pushlstring(ms->L, ms->capture[i].init, (size_t)l);
}
}
static int push_captures(MatchState *ms, const char *s, const char *e)
{
int i;
int nlevels = (ms->level == 0 && s) ? 1 : ms->level;
luaL_checkstack(ms->L, nlevels, "too many captures");
for (i = 0; i < nlevels; i++)
push_onecapture(ms, i, s, e);
return nlevels; /* number of strings pushed */
}
static ptrdiff_t posrelat(ptrdiff_t pos, size_t len)
{
/* relative string position: negative means back from end */
if (pos < 0) pos += (ptrdiff_t)len + 1;
return (pos >= 0) ? pos : 0;
}
static int str_find_aux(lua_State *L, int find)
{
size_t l1, l2;
const char *s = luaL_checklstring(L, 1, &l1);
const char *p = luaL_checklstring(L, 2, &l2);
ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;
if (init < 0)
init = 0;
else if ((size_t)(init) > l1)
init = (ptrdiff_t)l1;
if (find && (lua_toboolean(L, 4) || /* explicit request? */
strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */
/* do a plain search */
const char *s2 = lmemfind(s+init, l1-(size_t)init, p, l2);
if (s2) {
lua_pushinteger(L, s2-s+1);
lua_pushinteger(L, s2-s+(ptrdiff_t)l2);
return 2;
}
} else {
MatchState ms;
int anchor = (*p == '^') ? (p++, 1) : 0;
const char *s1=s+init;
ms.L = L;
ms.src_init = s;
ms.src_end = s+l1;
do {
const char *res;
ms.level = 0;
if ((res=match(&ms, s1, p)) != NULL) {
if (find) {
lua_pushinteger(L, s1-s+1); /* start */
lua_pushinteger(L, res-s); /* end */
return push_captures(&ms, NULL, 0) + 2;
} else {
return push_captures(&ms, s1, res);
}
}
} while (s1++ < ms.src_end && !anchor);
}
lua_pushnil(L); /* not found */
return 1;
}
LJLIB_CF(string_find)
{
return str_find_aux(L, 1);
}
LJLIB_CF(string_match)
{
return str_find_aux(L, 0);
}
LJLIB_NOREG LJLIB_CF(string_gmatch_aux)
{
const char *p = strVdata(lj_lib_upvalue(L, 2));
GCstr *str = strV(lj_lib_upvalue(L, 1));
const char *s = strdata(str);
TValue *tvpos = lj_lib_upvalue(L, 3);
const char *src = s + tvpos->u32.lo;
MatchState ms;
ms.L = L;
ms.src_init = s;
ms.src_end = s + str->len;
for (; src <= ms.src_end; src++) {
const char *e;
ms.level = 0;
if ((e = match(&ms, src, p)) != NULL) {
int32_t pos = (int32_t)(e - s);
if (e == src) pos++; /* Ensure progress for empty match. */
tvpos->u32.lo = (uint32_t)pos;
return push_captures(&ms, src, e);
}
}
return 0; /* not found */
}
LJLIB_CF(string_gmatch)
{
lj_lib_checkstr(L, 1);
lj_lib_checkstr(L, 2);
L->top = L->base+3;
(L->top-1)->u64 = 0;
lua_pushcclosure(L, lj_cf_string_gmatch_aux, 3);
funcV(L->top-1)->c.ffid = FF_string_gmatch_aux;
return 1;
}
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
{
size_t l, i;
const char *news = lua_tolstring(ms->L, 3, &l);
for (i = 0; i < l; i++) {
if (news[i] != L_ESC) {
luaL_addchar(b, news[i]);
} else {
i++; /* skip ESC */
if (!lj_ctype_isdigit(uchar(news[i]))) {
luaL_addchar(b, news[i]);
} else if (news[i] == '0') {
luaL_addlstring(b, s, (size_t)(e - s));
} else {
push_onecapture(ms, news[i] - '1', s, e);
luaL_addvalue(b); /* add capture to accumulated result */
}
}
}
}
static void add_value(MatchState *ms, luaL_Buffer *b,
const char *s, const char *e)
{
lua_State *L = ms->L;
switch (lua_type(L, 3)) {
case LUA_TNUMBER:
case LUA_TSTRING: {
add_s(ms, b, s, e);
return;
}
case LUA_TFUNCTION: {
int n;
lua_pushvalue(L, 3);
n = push_captures(ms, s, e);
lua_call(L, n, 1);
break;
}
case LUA_TTABLE: {
push_onecapture(ms, 0, s, e);
lua_gettable(L, 3);
break;
}
}
if (!lua_toboolean(L, -1)) { /* nil or false? */
lua_pop(L, 1);
lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */
} else if (!lua_isstring(L, -1)) {
lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
}
luaL_addvalue(b); /* add result to accumulator */
}
LJLIB_CF(string_gsub)
{
size_t srcl;
const char *src = luaL_checklstring(L, 1, &srcl);
const char *p = luaL_checkstring(L, 2);
int tr = lua_type(L, 3);
int max_s = luaL_optint(L, 4, (int)(srcl+1));
int anchor = (*p == '^') ? (p++, 1) : 0;
int n = 0;
MatchState ms;
luaL_Buffer b;
if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
tr == LUA_TFUNCTION || tr == LUA_TTABLE))
lj_err_arg(L, 3, LJ_ERR_NOSFT);
luaL_buffinit(L, &b);
ms.L = L;
ms.src_init = src;
ms.src_end = src+srcl;
while (n < max_s) {
const char *e;
ms.level = 0;
e = match(&ms, src, p);
if (e) {
n++;
add_value(&ms, &b, src, e);
}
if (e && e>src) /* non empty match? */
src = e; /* skip it */
else if (src < ms.src_end)
luaL_addchar(&b, *src++);
else
break;
if (anchor)
break;
}
luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
luaL_pushresult(&b);
lua_pushinteger(L, n); /* number of substitutions */
return 2;
}
/* ------------------------------------------------------------------------ */
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */
#define MAX_FMTITEM 512
/* valid flags in a format specification */
#define FMT_FLAGS "-+ #0"
/*
** maximum size of each format specification (such as '%-099.99d')
** (+10 accounts for %99.99x plus margin of error)
*/
#define MAX_FMTSPEC (sizeof(FMT_FLAGS) + sizeof(LUA_INTFRMLEN) + 10)
static void addquoted(lua_State *L, luaL_Buffer *b, int arg)
{
GCstr *str = lj_lib_checkstr(L, arg);
int32_t len = (int32_t)str->len;
const char *s = strdata(str);
luaL_addchar(b, '"');
while (len--) {
switch (*s) {
case '"': case '\\': case '\n':
luaL_addchar(b, '\\');
luaL_addchar(b, *s);
break;
case '\r':
luaL_addlstring(b, "\\r", 2);
break;
case '\0':
luaL_addlstring(b, "\\000", 4);
break;
default:
luaL_addchar(b, *s);
break;
}
s++;
}
luaL_addchar(b, '"');
}
static const char *scanformat(lua_State *L, const char *strfrmt, char *form)
{
const char *p = strfrmt;
while (*p != '\0' && strchr(FMT_FLAGS, *p) != NULL) p++; /* skip flags */
if ((size_t)(p - strfrmt) >= sizeof(FMT_FLAGS))
lj_err_caller(L, LJ_ERR_STRFMTR);
if (lj_ctype_isdigit(uchar(*p))) p++; /* skip width */
if (lj_ctype_isdigit(uchar(*p))) p++; /* (2 digits at most) */
if (*p == '.') {
p++;
if (lj_ctype_isdigit(uchar(*p))) p++; /* skip precision */
if (lj_ctype_isdigit(uchar(*p))) p++; /* (2 digits at most) */
}
if (lj_ctype_isdigit(uchar(*p)))
lj_err_caller(L, LJ_ERR_STRFMTW);
*(form++) = '%';
strncpy(form, strfrmt, (size_t)(p - strfrmt + 1));
form += p - strfrmt + 1;
*form = '\0';
return p;
}
static void addintlen(char *form)
{
size_t l = strlen(form);
char spec = form[l - 1];
strcpy(form + l - 1, LUA_INTFRMLEN);
form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;
form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0';
}
LJLIB_CF(string_format)
{
int arg = 1;
GCstr *fmt = lj_lib_checkstr(L, arg);
const char *strfrmt = strdata(fmt);
const char *strfrmt_end = strfrmt + fmt->len;
luaL_Buffer b;
luaL_buffinit(L, &b);
while (strfrmt < strfrmt_end) {
if (*strfrmt != L_ESC) {
luaL_addchar(&b, *strfrmt++);
} else if (*++strfrmt == L_ESC) {
luaL_addchar(&b, *strfrmt++); /* %% */
} else { /* format item */
char form[MAX_FMTSPEC]; /* to store the format (`%...') */
char buff[MAX_FMTITEM]; /* to store the formatted item */
arg++;
strfrmt = scanformat(L, strfrmt, form);
switch (*strfrmt++) {
case 'c':
sprintf(buff, form, lj_lib_checkint(L, arg));
break;
case 'd': case 'i':
addintlen(form);
sprintf(buff, form, (LUA_INTFRM_T)lj_lib_checknum(L, arg));
break;
case 'o': case 'u': case 'x': case 'X':
addintlen(form);
sprintf(buff, form, (unsigned LUA_INTFRM_T)lj_lib_checknum(L, arg));
break;
case 'e': case 'E': case 'f': case 'g': case 'G':
sprintf(buff, form, (double)lj_lib_checknum(L, arg));
break;
case 'q':
addquoted(L, &b, arg);
continue;
case 'p':
lj_str_pushf(L, "%p", lua_topointer(L, arg));
luaL_addvalue(&b);
continue;
case 's': {
GCstr *str = lj_lib_checkstr(L, arg);
if (!strchr(form, '.') && str->len >= 100) {
/* no precision and string is too long to be formatted;
keep original string */
setstrV(L, L->top++, str);
luaL_addvalue(&b);
continue;
}
sprintf(buff, form, strdata(str));
break;
}
default:
lj_err_callerv(L, LJ_ERR_STRFMTO, *(strfrmt -1));
break;
}
luaL_addlstring(&b, buff, strlen(buff));
}
}
luaL_pushresult(&b);
return 1;
}
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
LUALIB_API int luaopen_string(lua_State *L)
{
GCtab *mt;
LJ_LIB_REG(L, string);
#if defined(LUA_COMPAT_GFIND)
lua_getfield(L, -1, "gmatch");
lua_setfield(L, -2, "gfind");
#endif
mt = lj_tab_new(L, 0, 1);
/* NOBARRIER: G(L)->mmname[] is a GC root. */
setgcref(G(L)->basemt[~LJ_TSTR], obj2gco(mt));
settabV(L, lj_tab_setstr(L, mt, strref(G(L)->mmname[MM_index])),
tabV(L->top-1));
mt->nomm = cast_byte(~(1u<<MM_index));
return 1;
}

276
src/lib_table.c Normal file
View File

@ -0,0 +1,276 @@
/*
** Table library.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lib_table_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_tab.h"
#include "lj_lib.h"
/* ------------------------------------------------------------------------ */
#define LJLIB_MODULE_table
LJLIB_CF(table_foreachi)
{
GCtab *t = lj_lib_checktab(L, 1);
GCfunc *func = lj_lib_checkfunc(L, 2);
MSize i, n = lj_tab_len(t);
for (i = 1; i <= n; i++) {
cTValue *val;
setfuncV(L, L->top, func);
setintV(L->top+1, i);
val = lj_tab_getint(t, (int32_t)i);
if (val) { copyTV(L, L->top+2, val); } else { setnilV(L->top+2); }
L->top += 3;
lua_call(L, 2, 1);
if (!tvisnil(L->top-1))
return 1;
L->top--;
}
return 0;
}
LJLIB_CF(table_foreach)
{
GCtab *t = lj_lib_checktab(L, 1);
GCfunc *func = lj_lib_checkfunc(L, 2);
L->top = L->base+3;
setnilV(L->top-1);
while (lj_tab_next(L, t, L->top-1)) {
copyTV(L, L->top+2, L->top);
copyTV(L, L->top+1, L->top-1);
setfuncV(L, L->top, func);
L->top += 3;
lua_call(L, 2, 1);
if (!tvisnil(L->top-1))
return 1;
L->top--;
}
return 0;
}
LJLIB_ASM(table_getn) LJLIB_REC(.)
{
lj_lib_checktab(L, 1);
return FFH_UNREACHABLE;
}
LJLIB_CF(table_maxn)
{
GCtab *t = lj_lib_checktab(L, 1);
TValue *array = tvref(t->array);
Node *node;
lua_Number m = 0;
uint32_t i;
for (i = 0; i < t->asize; i++)
if (!tvisnil(&array[i])) {
m = (lua_Number)i;
break;
}
node = noderef(t->node);
for (i = 0; i <= t->hmask; i++)
if (tvisnum(&node[i].key) && numV(&node[i].key) > m)
m = numV(&node[i].key);
setnumV(L->top-1, m);
return 1;
}
LJLIB_CF(table_insert)
{
GCtab *t = lj_lib_checktab(L, 1);
int32_t n, i = (int32_t)lj_tab_len(t) + 1;
int nargs = (int)((char *)L->top - (char *)L->base);
if (nargs != 2*sizeof(TValue)) {
if (nargs != 3*sizeof(TValue))
lj_err_caller(L, LJ_ERR_TABINS);
/* NOBARRIER: This just moves existing elements around. */
for (n = lj_lib_checkint(L, 2); i > n; i--) {
/* The set may invalidate the get pointer, so need to do it first! */
TValue *dst = lj_tab_setint(L, t, i);
cTValue *src = lj_tab_getint(t, i-1);
if (src) {
copyTV(L, dst, src);
} else {
setnilV(dst);
}
}
i = n;
}
{
TValue *dst = lj_tab_setint(L, t, i);
copyTV(L, dst, L->top-1);
lj_gc_barriert(L, t, dst);
}
return 0;
}
LJLIB_CF(table_remove)
{
GCtab *t = lj_lib_checktab(L, 1);
int32_t e = (int32_t)lj_tab_len(t);
int32_t pos = lj_lib_optint(L, 2, e);
if (!(1 <= pos && pos <= e)) /* position is outside bounds? */
return 0; /* nothing to remove */
lua_rawgeti(L, 1, pos);
/* NOBARRIER: This just moves existing elements around. */
for (; pos < e; pos++) {
cTValue *src = lj_tab_getint(t, pos+1);
TValue *dst = lj_tab_setint(L, t, pos);
if (src) {
copyTV(L, dst, src);
} else {
setnilV(dst);
}
}
setnilV(lj_tab_setint(L, t, e));
return 1;
}
LJLIB_CF(table_concat)
{
luaL_Buffer b;
GCtab *t = lj_lib_checktab(L, 1);
GCstr *sep = lj_lib_optstr(L, 2);
MSize seplen = sep ? sep->len : 0;
int32_t i = lj_lib_optint(L, 3, 1);
int32_t e = L->base+3 < L->top ? lj_lib_checkint(L, 4) :
(int32_t)lj_tab_len(t);
luaL_buffinit(L, &b);
if (i <= e) {
for (;;) {
cTValue *o;
lua_rawgeti(L, 1, i);
o = L->top-1;
if (!(tvisstr(o) || tvisnum(o)))
lj_err_callerv(L, LJ_ERR_TABCAT, typename(o), i);
luaL_addvalue(&b);
if (i++ == e) break;
if (seplen)
luaL_addlstring(&b, strdata(sep), seplen);
}
}
luaL_pushresult(&b);
return 1;
}
/* ------------------------------------------------------------------------ */
static void set2(lua_State *L, int i, int j)
{
lua_rawseti(L, 1, i);
lua_rawseti(L, 1, j);
}
static int sort_comp(lua_State *L, int a, int b)
{
if (!lua_isnil(L, 2)) { /* function? */
int res;
lua_pushvalue(L, 2);
lua_pushvalue(L, a-1); /* -1 to compensate function */
lua_pushvalue(L, b-2); /* -2 to compensate function and `a' */
lua_call(L, 2, 1);
res = lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
} else { /* a < b? */
return lua_lessthan(L, a, b);
}
}
static void auxsort(lua_State *L, int l, int u)
{
while (l < u) { /* for tail recursion */
int i, j;
/* sort elements a[l], a[(l+u)/2] and a[u] */
lua_rawgeti(L, 1, l);
lua_rawgeti(L, 1, u);
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */
set2(L, l, u); /* swap a[l] - a[u] */
else
lua_pop(L, 2);
if (u-l == 1) break; /* only 2 elements */
i = (l+u)/2;
lua_rawgeti(L, 1, i);
lua_rawgeti(L, 1, l);
if (sort_comp(L, -2, -1)) { /* a[i]<a[l]? */
set2(L, i, l);
} else {
lua_pop(L, 1); /* remove a[l] */
lua_rawgeti(L, 1, u);
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */
set2(L, i, u);
else
lua_pop(L, 2);
}
if (u-l == 2) break; /* only 3 elements */
lua_rawgeti(L, 1, i); /* Pivot */
lua_pushvalue(L, -1);
lua_rawgeti(L, 1, u-1);
set2(L, i, u-1);
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
i = l; j = u-1;
for (;;) { /* invariant: a[l..i] <= P <= a[j..u] */
/* repeat ++i until a[i] >= P */
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
if (i>u) lj_err_caller(L, LJ_ERR_TABSORT);
lua_pop(L, 1); /* remove a[i] */
}
/* repeat --j until a[j] <= P */
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
if (j<l) lj_err_caller(L, LJ_ERR_TABSORT);
lua_pop(L, 1); /* remove a[j] */
}
if (j<i) {
lua_pop(L, 3); /* pop pivot, a[i], a[j] */
break;
}
set2(L, i, j);
}
lua_rawgeti(L, 1, u-1);
lua_rawgeti(L, 1, i);
set2(L, u-1, i); /* swap pivot (a[u-1]) with a[i] */
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */
if (i-l < u-i) {
j=l; i=i-1; l=i+2;
} else {
j=i+1; i=u; u=j-2;
}
auxsort(L, j, i); /* call recursively the smaller one */
} /* repeat the routine for the larger one */
}
LJLIB_CF(table_sort)
{
GCtab *t = lj_lib_checktab(L, 1);
int32_t n = (int32_t)lj_tab_len(t);
lua_settop(L, 2);
if (!tvisnil(L->base+1))
lj_lib_checkfunc(L, 2);
auxsort(L, 1, n);
return 0;
}
/* ------------------------------------------------------------------------ */
#include "lj_libdef.h"
LUALIB_API int luaopen_table(lua_State *L)
{
LJ_LIB_REG(L, table);
return 1;
}

6
src/lj.supp Normal file
View File

@ -0,0 +1,6 @@
# Valgrind suppression file for LuaJIT 2.x.
{
Optimized string compare
Memcheck:Addr4
fun:lj_str_cmp
}

1232
src/lj_alloc.c Normal file

File diff suppressed because it is too large Load Diff

17
src/lj_alloc.h Normal file
View File

@ -0,0 +1,17 @@
/*
** Bundled memory allocator.
** Donated to the public domain.
*/
#ifndef _LJ_ALLOC_H
#define _LJ_ALLOC_H
#include "lj_def.h"
#ifndef LUAJIT_USE_SYSMALLOC
LJ_FUNC void *lj_alloc_create(void);
LJ_FUNC void lj_alloc_destroy(void *msp);
LJ_FUNC void *lj_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize);
#endif
#endif

1046
src/lj_api.c Normal file

File diff suppressed because it is too large Load Diff

88
src/lj_arch.h Normal file
View File

@ -0,0 +1,88 @@
/*
** Target architecture selection.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_ARCH_H
#define _LJ_ARCH_H
#include "lua.h"
/* Target endianess. */
#define LUAJIT_LE 0
#define LUAJIT_BE 1
/* Target architectures. */
#define LUAJIT_ARCH_X86 1
#define LUAJIT_ARCH_x86 1
#define LUAJIT_ARCH_X64 2
#define LUAJIT_ARCH_x64 2
/* Select native target if no target defined. */
#ifndef LUAJIT_TARGET
#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
#define LUAJIT_TARGET LUAJIT_ARCH_X86
#elif defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64)
#define LUAJIT_TARGET LUAJIT_ARCH_X64
#else
#error "No support for this architecture (yet)"
#endif
#endif
/* Set target properties. */
#if LUAJIT_TARGET == LUAJIT_ARCH_X86
#define LJ_ARCH_NAME "x86"
#define LJ_ARCH_BITS 32
#define LJ_ARCH_ENDIAN LUAJIT_LE
#define LJ_TARGET_X86 1
#define LJ_TARGET_X86ORX64 1
#define LJ_PAGESIZE 4096
#elif LUAJIT_TARGET == LUAJIT_ARCH_X64
#define LJ_ARCH_NAME "x64"
#define LJ_ARCH_BITS 64
#define LJ_ARCH_ENDIAN LUAJIT_LE
#define LJ_TARGET_X64 1
#define LJ_TARGET_X86ORX64 1
#define LJ_PAGESIZE 4096
#error "No support for x64 architecture (yet)"
#else
#error "No target architecture defined"
#endif
/* Disable or enable the JIT compiler. */
#if defined(LUAJIT_DISABLE_JIT) || defined(LJ_ARCH_NOJIT)
#define LJ_HASJIT 0
#else
#define LJ_HASJIT 1
#endif
#if LJ_ARCH_ENDIAN == LUAJIT_BE
#define LJ_ENDIAN_SELECT(le, be) be
#define LJ_ENDIAN_LOHI(lo, hi) hi lo
#else
#define LJ_ENDIAN_SELECT(le, be) le
#define LJ_ENDIAN_LOHI(lo, hi) lo hi
#endif
#if LJ_ARCH_BITS == 32
#define LJ_32 1
#define LJ_64 0
#elif LJ_ARCH_BITS == 64
#define LJ_32 0
#define LJ_64 1
#else
#error "Bad LJ_ARCH_BITS setting"
#endif
/* Whether target CPU masks the shift count by the operand length or not. */
#if LJ_TARGET_X86ORX64
#define LJ_TARGET_MASKEDSHIFT 1
#else
#define LJ_TARGET_MASKEDSHIFT 0
#endif
#endif

3324
src/lj_asm.c Normal file

File diff suppressed because it is too large Load Diff

17
src/lj_asm.h Normal file
View File

@ -0,0 +1,17 @@
/*
** IR assembler (SSA IR -> machine code).
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_ASM_H
#define _LJ_ASM_H
#include "lj_jit.h"
#if LJ_HASJIT
LJ_FUNC void lj_asm_trace(jit_State *J, Trace *T);
LJ_FUNC void lj_asm_patchexit(jit_State *J, Trace *T, ExitNo exitno,
MCode *target);
#endif
#endif

17
src/lj_bc.c Normal file
View File

@ -0,0 +1,17 @@
/*
** Bytecode instruction modes.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_bc_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_bc.h"
/* Bytecode instruction modes. */
LJ_DATADEF const uint16_t lj_bc_mode[BC__MAX+1] = {
BCDEF(BCMODE)
0
};

235
src/lj_bc.h Normal file
View File

@ -0,0 +1,235 @@
/*
** Bytecode instruction format.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_BC_H
#define _LJ_BC_H
#include "lj_def.h"
#include "lj_arch.h"
/* Bytecode instruction format, 32 bit wide, fields of 8 or 16 bit:
**
** +----+----+----+----+
** | B | C | A | OP | Format ABC
** +----+----+----+----+
** | D | A | OP | Format AD
** +--------------------
** MSB LSB
**
** In-memory instructions are always stored in host byte order.
*/
/* Operand ranges and related constants. */
#define BCMAX_A 0xff
#define BCMAX_B 0xff
#define BCMAX_C 0xff
#define BCMAX_D 0xffff
#define BCBIAS_J 0x8000
#define NO_REG BCMAX_A
#define NO_JMP (~(BCPos)0)
/* Macros to get instruction fields. */
#define bc_op(i) (cast(BCOp, (i)&0xff))
#define bc_a(i) (cast(BCReg, ((i)>>8)&0xff))
#define bc_b(i) (cast(BCReg, (i)>>24))
#define bc_c(i) (cast(BCReg, ((i)>>16)&0xff))
#define bc_d(i) (cast(BCReg, (i)>>16))
#define bc_j(i) ((ptrdiff_t)bc_d(i)-BCBIAS_J)
/* Macros to set instruction fields. */
#define setbc_byte(p, x, ofs) \
((uint8_t *)(p))[LJ_ENDIAN_SELECT(ofs, 3-ofs)] = cast_byte(x)
#define setbc_op(p, x) setbc_byte(p, (x), 0)
#define setbc_a(p, x) setbc_byte(p, (x), 1)
#define setbc_b(p, x) setbc_byte(p, (x), 3)
#define setbc_c(p, x) setbc_byte(p, (x), 2)
#define setbc_d(p, x) \
((uint16_t *)(p))[LJ_ENDIAN_SELECT(1, 0)] = cast(uint16_t, (x))
#define setbc_j(p, x) setbc_d(p, (BCPos)((int32_t)(x)+BCBIAS_J))
/* Macros to compose instructions. */
#define BCINS_ABC(o, a, b, c) \
(cast(BCIns, o)|(cast(BCIns, a)<<8)|\
(cast(BCIns, b)<<24)|(cast(BCIns, c)<<16))
#define BCINS_AD(o, a, d) \
(cast(BCIns, o)|(cast(BCIns, a)<<8)|(cast(BCIns, d)<<16))
#define BCINS_AJ(o, a, j) BCINS_AD(o, a, (BCPos)((int32_t)(j)+BCBIAS_J))
/* Bytecode instruction definition. Order matters, see below.
**
** (name, filler, Amode, Bmode, Cmode or Dmode, metamethod)
**
** The opcode name suffixes specify the type for RB/RC or RD:
** V = variable slot
** S = string const
** N = number const
** P = primitive type (~itype)
** B = unsigned byte literal
** M = multiple args/results
*/
#define BCDEF(_) \
/* Comparison ops. ORDER OPR. */ \
_(ISLT, var, ___, var, lt) \
_(ISGE, var, ___, var, lt) \
_(ISLE, var, ___, var, le) \
_(ISGT, var, ___, var, le) \
\
_(ISEQV, var, ___, var, eq) \
_(ISNEV, var, ___, var, eq) \
_(ISEQS, var, ___, str, eq) \
_(ISNES, var, ___, str, eq) \
_(ISEQN, var, ___, num, eq) \
_(ISNEN, var, ___, num, eq) \
_(ISEQP, var, ___, pri, eq) \
_(ISNEP, var, ___, pri, eq) \
\
/* Unary test and copy ops. */ \
_(ISTC, dst, ___, var, ___) \
_(ISFC, dst, ___, var, ___) \
_(IST, ___, ___, var, ___) \
_(ISF, ___, ___, var, ___) \
\
/* Unary ops. */ \
_(MOV, dst, ___, var, ___) \
_(NOT, dst, ___, var, ___) \
_(UNM, dst, ___, var, unm) \
_(LEN, dst, ___, var, len) \
\
/* Binary ops. ORDER OPR. VV last, POW must be next. */ \
_(ADDVN, dst, var, num, add) \
_(SUBVN, dst, var, num, sub) \
_(MULVN, dst, var, num, mul) \
_(DIVVN, dst, var, num, div) \
_(MODVN, dst, var, num, mod) \
\
_(ADDNV, dst, var, num, add) \
_(SUBNV, dst, var, num, sub) \
_(MULNV, dst, var, num, mul) \
_(DIVNV, dst, var, num, div) \
_(MODNV, dst, var, num, mod) \
\
_(ADDVV, dst, var, var, add) \
_(SUBVV, dst, var, var, sub) \
_(MULVV, dst, var, var, mul) \
_(DIVVV, dst, var, var, div) \
_(MODVV, dst, var, var, mod) \
\
_(POW, dst, var, var, pow) \
_(CAT, dst, rbase, rbase, concat) \
\
/* Constant ops. */ \
_(KSTR, dst, ___, str, ___) \
_(KSHORT, dst, ___, lits, ___) \
_(KNUM, dst, ___, num, ___) \
_(KPRI, dst, ___, pri, ___) \
_(KNIL, base, ___, base, ___) \
\
/* Upvalue and function ops. */ \
_(UGET, dst, ___, uv, ___) \
_(USETV, uv, ___, var, ___) \
_(USETS, uv, ___, str, ___) \
_(USETN, uv, ___, num, ___) \
_(USETP, uv, ___, pri, ___) \
_(UCLO, rbase, ___, jump, ___) \
_(FNEW, dst, ___, func, gc) \
\
/* Table ops. */ \
_(TNEW, dst, ___, lit, gc) \
_(TDUP, dst, ___, tab, gc) \
_(GGET, dst, ___, str, index) \
_(GSET, var, ___, str, newindex) \
_(TGETV, dst, var, var, index) \
_(TGETS, dst, var, str, index) \
_(TGETB, dst, var, lit, index) \
_(TSETV, var, var, var, newindex) \
_(TSETS, var, var, str, newindex) \
_(TSETB, var, var, lit, newindex) \
_(TSETM, base, ___, num, newindex) \
\
/* Calls and vararg handling. T = tail call. */ \
_(CALLM, base, lit, lit, call) \
_(CALL, base, lit, lit, call) \
_(CALLMT, base, ___, lit, call) \
_(CALLT, base, ___, lit, call) \
_(ITERC, base, lit, lit, call) \
_(VARG, base, lit, lit, ___) \
\
/* Returns. */ \
_(RETM, base, ___, lit, ___) \
_(RET, rbase, ___, lit, ___) \
_(RET0, rbase, ___, lit, ___) \
_(RET1, rbase, ___, lit, ___) \
\
/* Loops and branches. I/J = interp/JIT, I/C/L = init/call/loop. */ \
_(FORI, base, ___, jump, ___) \
_(JFORI, base, ___, jump, ___) \
\
_(FORL, base, ___, jump, ___) \
_(IFORL, base, ___, jump, ___) \
_(JFORL, base, ___, lit, ___) \
\
_(ITERL, base, ___, jump, ___) \
_(IITERL, base, ___, jump, ___) \
_(JITERL, base, ___, lit, ___) \
\
_(LOOP, rbase, ___, jump, ___) \
_(ILOOP, rbase, ___, jump, ___) \
_(JLOOP, rbase, ___, lit, ___) \
\
_(JMP, rbase, ___, jump, ___)
/* Bytecode opcode numbers. */
typedef enum {
#define BCENUM(name, ma, mb, mc, mt) BC_##name,
BCDEF(BCENUM)
#undef BCENUM
BC__MAX
} BCOp;
LJ_STATIC_ASSERT((int)BC_ISEQV+1 == (int)BC_ISNEV);
LJ_STATIC_ASSERT(((int)BC_ISEQV^1) == (int)BC_ISNEV);
LJ_STATIC_ASSERT(((int)BC_ISEQS^1) == (int)BC_ISNES);
LJ_STATIC_ASSERT(((int)BC_ISEQN^1) == (int)BC_ISNEN);
LJ_STATIC_ASSERT(((int)BC_ISEQP^1) == (int)BC_ISNEP);
LJ_STATIC_ASSERT(((int)BC_ISLT^1) == (int)BC_ISGE);
LJ_STATIC_ASSERT(((int)BC_ISLE^1) == (int)BC_ISGT);
LJ_STATIC_ASSERT(((int)BC_ISLT^3) == (int)BC_ISGT);
LJ_STATIC_ASSERT((int)BC_IST-(int)BC_ISTC == (int)BC_ISF-(int)BC_ISFC);
LJ_STATIC_ASSERT((int)BC_CALLT-(int)BC_CALL == (int)BC_CALLMT-(int)BC_CALLM);
LJ_STATIC_ASSERT((int)BC_CALLMT + 1 == (int)BC_CALLT);
LJ_STATIC_ASSERT((int)BC_RETM + 1 == (int)BC_RET);
LJ_STATIC_ASSERT((int)BC_FORL + 1 == (int)BC_IFORL);
LJ_STATIC_ASSERT((int)BC_FORL + 2 == (int)BC_JFORL);
LJ_STATIC_ASSERT((int)BC_ITERL + 1 == (int)BC_IITERL);
LJ_STATIC_ASSERT((int)BC_ITERL + 2 == (int)BC_JITERL);
LJ_STATIC_ASSERT((int)BC_LOOP + 1 == (int)BC_ILOOP);
LJ_STATIC_ASSERT((int)BC_LOOP + 2 == (int)BC_JLOOP);
/* Stack slots used by FORI/FORL, relative to operand A. */
enum {
FORL_IDX, FORL_STOP, FORL_STEP, FORL_EXT
};
/* Bytecode operand modes. ORDER BCMode */
typedef enum {
BCMnone, BCMdst, BCMbase, BCMvar, BCMrbase, BCMuv, /* Mode A must be <= 7 */
BCMlit, BCMlits, BCMpri, BCMnum, BCMstr, BCMtab, BCMfunc, BCMjump,
BCM_max
} BCMode;
#define BCM___ BCMnone
#define bcmode_a(op) (cast(BCMode, lj_bc_mode[op] & 7))
#define bcmode_b(op) (cast(BCMode, (lj_bc_mode[op]>>3) & 15))
#define bcmode_c(op) (cast(BCMode, (lj_bc_mode[op]>>7) & 15))
#define bcmode_d(op) bcmode_c(op)
#define bcmode_hasd(op) ((lj_bc_mode[op] & (15<<3)) == (BCMnone<<3))
#define bcmode_mm(op) (cast(MMS, lj_bc_mode[op]>>11))
#define BCMODE(name, ma, mb, mc, mm) \
(BCM##ma|(BCM##mb<<3)|(BCM##mc<<7)|(MM_##mm<<11)),
LJ_DATA const uint16_t lj_bc_mode[BC__MAX+1];
#endif

44
src/lj_ctype.c Normal file
View File

@ -0,0 +1,44 @@
/*
** Internal CTYPE replacement.
** Donated to the public domain.
**
** This is intended to replace the problematic libc single-byte NLS functions.
** These just don't make sense anymore with UTF-8 locales becoming the norm
** on POSIX systems. It never worked too well on Windows systems since hardly
** anyone bothered to call setlocale().
**
** Instead this table is hardcoded for ASCII, except for identifiers. These
** include the characters 128-255, too. This allows for the use of all
** non-ASCII chars as identifiers in the lexer. This is a broad definition,
** but works well in practice for both UTF-8 locales and most single-byte
** locales (such as ISO-8859-*).
**
** If you really need proper ctypes for UTF-8 strings, please use an add-on
** library such as slnunicode: http://luaforge.net/projects/sln/
*/
#define lj_ctype_c
#define LUA_CORE
#include "lj_ctype.h"
LJ_DATADEF const uint8_t lj_ctype_bits[257] = {
0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
152,152,152,152,152,152,152,152,152,152, 4, 4, 4, 4, 4, 4,
4,176,176,176,176,176,176,160,160,160,160,160,160,160,160,160,
160,160,160,160,160,160,160,160,160,160,160, 4, 4, 4, 4,132,
4,208,208,208,208,208,208,192,192,192,192,192,192,192,192,192,
192,192,192,192,192,192,192,192,192,192,192, 4, 4, 4, 4, 1,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,
128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128
};

40
src/lj_ctype.h Normal file
View File

@ -0,0 +1,40 @@
/*
** Internal CTYPE replacement.
** Donated to the public domain.
*/
#ifndef _LJ_CTYPE_H
#define _LJ_CTYPE_H
#include "lj_def.h"
#define LJ_CTYPE_CNTRL 0x01
#define LJ_CTYPE_SPACE 0x02
#define LJ_CTYPE_PUNCT 0x04
#define LJ_CTYPE_DIGIT 0x08
#define LJ_CTYPE_XDIGIT 0x10
#define LJ_CTYPE_UPPER 0x20
#define LJ_CTYPE_LOWER 0x40
#define LJ_CTYPE_IDENT 0x80
#define LJ_CTYPE_ALPHA (LJ_CTYPE_LOWER|LJ_CTYPE_UPPER)
#define LJ_CTYPE_ALNUM (LJ_CTYPE_ALPHA|LJ_CTYPE_DIGIT)
/* Only pass -1 or 0..255 to these macros. Never pass a signed char! */
#define lj_ctype_isa(c, t) (lj_ctype_bits[(c)+1] & t)
#define lj_ctype_iscntrl(c) lj_ctype_isa((c), LJ_CTYPE_CNTRL)
#define lj_ctype_isspace(c) lj_ctype_isa((c), LJ_CTYPE_SPACE)
#define lj_ctype_ispunct(c) lj_ctype_isa((c), LJ_CTYPE_PUNCT)
#define lj_ctype_isdigit(c) lj_ctype_isa((c), LJ_CTYPE_DIGIT)
#define lj_ctype_isxdigit(c) lj_ctype_isa((c), LJ_CTYPE_XDIGIT)
#define lj_ctype_isupper(c) lj_ctype_isa((c), LJ_CTYPE_UPPER)
#define lj_ctype_islower(c) lj_ctype_isa((c), LJ_CTYPE_LOWER)
#define lj_ctype_isident(c) lj_ctype_isa((c), LJ_CTYPE_IDENT)
#define lj_ctype_isalpha(c) lj_ctype_isa((c), LJ_CTYPE_ALPHA)
#define lj_ctype_isalnum(c) lj_ctype_isa((c), LJ_CTYPE_ALNUM)
#define lj_ctype_toupper(c) ((c) - (lj_ctype_islower(c) >> 1))
#define lj_ctype_tolower(c) ((c) + lj_ctype_isupper(c))
LJ_DATA const uint8_t lj_ctype_bits[257];
#endif

226
src/lj_def.h Normal file
View File

@ -0,0 +1,226 @@
/*
** LuaJIT common internal definitions.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_DEF_H
#define _LJ_DEF_H
#include "lua.h"
#ifdef _MSC_VER
/* MSVC is stuck in the last century and doesn't have C99's stdint.h. */
typedef __int8 int8_t;
typedef __int16 int16_t;
typedef __int32 int32_t;
typedef __int64 int64_t;
typedef unsigned __int8 uint8_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int64 uint64_t;
#ifdef _WIN64
typedef __int64 intptr_t;
typedef unsigned __int64 uintptr_t;
#else
typedef __int32 intptr_t;
typedef unsigned __int32 uintptr_t;
#endif
#else
#include <stdint.h>
#endif
/* Needed everywhere. */
#include <string.h>
#include <stdlib.h>
/* Various VM limits. */
#define LJ_MAX_MEM 0x7fffff00 /* Max. total memory allocation. */
#define LJ_MAX_ALLOC LJ_MAX_MEM /* Max. individual allocation length. */
#define LJ_MAX_STR LJ_MAX_MEM /* Max. string length. */
#define LJ_MAX_UDATA LJ_MAX_MEM /* Max. userdata length. */
#define LJ_MAX_STRTAB (1<<26) /* Max. string table size. */
#define LJ_MAX_HBITS 26 /* Max. hash bits. */
#define LJ_MAX_ABITS 28 /* Max. bits of array key. */
#define LJ_MAX_ASIZE ((1<<(LJ_MAX_ABITS-1))+1) /* Max. array part size. */
#define LJ_MAX_COLOSIZE 16 /* Max. elems for colocated array. */
#define LJ_MAX_LINE LJ_MAX_MEM /* Max. source code line number. */
#define LJ_MAX_XLEVEL 200 /* Max. syntactic nesting level. */
#define LJ_MAX_BCINS (1<<26) /* Max. # of bytecode instructions. */
#define LJ_MAX_SLOTS 250 /* Max. # of slots in a Lua func. */
#define LJ_MAX_LOCVAR 200 /* Max. # of local variables. */
#define LJ_MAX_UPVAL 60 /* Max. # of upvalues. */
#define LJ_MAX_IDXCHAIN 100 /* __index/__newindex chain limit. */
#define LJ_STACK_EXTRA 5 /* Extra stack space (metamethods). */
/* Minimum table/buffer sizes. */
#define LJ_MIN_GLOBAL 6 /* Min. global table size (hbits). */
#define LJ_MIN_REGISTRY 2 /* Min. registry size (hbits). */
#define LJ_MIN_STRTAB 256 /* Min. string table size (pow2). */
#define LJ_MIN_SBUF 32 /* Min. string buffer length. */
#define LJ_MIN_VECSZ 8 /* Min. size for growable vectors. */
#define LJ_MIN_IRSZ 32 /* Min. size for growable IR. */
#define LJ_MIN_KNUMSZ 16 /* Min. size for chained KNUM array. */
/* JIT compiler limits. */
#define LJ_MAX_JSLOTS 250 /* Max. # of stack slots for a trace. */
#define LJ_MAX_PHI 32 /* Max. # of PHIs for a loop. */
#define LJ_MAX_EXITSTUBGR 8 /* Max. # of exit stub groups. */
/* Various macros. */
#ifndef UNUSED
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
#endif
#ifndef cast
#define cast(t, exp) ((t)(exp))
#endif
#define U64x(hi, lo) (((uint64_t)0x##hi << 32) + (uint64_t)0x##lo)
#define cast_byte(i) cast(uint8_t, (i))
#define cast_num(i) cast(lua_Number, (i))
#define cast_int(i) cast(int, (i))
#define i32ptr(p) ((int32_t)(intptr_t)(void *)(p))
#define u32ptr(p) ((uint32_t)(intptr_t)(void *)(p))
#define checki8(x) ((x) == (int32_t)(int8_t)(x))
#define checku8(x) ((x) == (int32_t)(uint8_t)(x))
#define checki16(x) ((x) == (int32_t)(int16_t)(x))
/* Every half-decent C compiler transforms this into a rotate instruction. */
#define lj_rol(x, n) (((x)<<(n)) | ((x)>>(32-(n))))
#define lj_ror(x, n) (((x)<<(32-(n))) | ((x)>>(n)))
/* A really naive Bloom filter. But sufficient for our needs. */
typedef uintptr_t BloomFilter;
#define BLOOM_MASK (8*sizeof(BloomFilter) - 1)
#define bloombit(x) ((uintptr_t)1 << ((x) & BLOOM_MASK))
#define bloomset(b, x) ((b) |= bloombit((x)))
#define bloomtest(b, x) ((b) & bloombit((x)))
#if defined(__GNUC__)
#if (__GNUC__ < 3) || ((__GNUC__ == 3) && __GNUC_MINOR__ < 4)
#error "sorry, need GCC 3.4 or newer"
#endif
#define LJ_NORET __attribute__((noreturn))
#define LJ_ALIGN(n) __attribute__((aligned(n)))
#define LJ_INLINE inline
#define LJ_AINLINE inline __attribute__((always_inline))
#define LJ_NOINLINE __attribute__((noinline))
#if defined(__ELF__) || defined(__MACH__)
#define LJ_NOAPI extern __attribute__((visibility("hidden")))
#endif
/* Note: it's only beneficial to use fastcall on x86 and then only for up to
** two non-FP args. The amalgamated compile covers all LJ_FUNC cases. Only
** indirect calls and related tail-called C functions are marked as fastcall.
*/
#if defined(__i386__)
#define LJ_FASTCALL __attribute__((fastcall))
#endif
#define LJ_LIKELY(x) __builtin_expect(!!(x), 1)
#define LJ_UNLIKELY(x) __builtin_expect(!!(x), 0)
#define lj_ffs(x) ((uint32_t)__builtin_ctz(x))
/* Don't ask ... */
#if defined(__INTEL_COMPILER) && (defined(__i386__) || defined(__x86_64__))
static LJ_AINLINE uint32_t lj_fls(uint32_t x)
{
uint32_t r; __asm__("bsrl %1, %0" : "=r" (r) : "rm" (x) : "cc"); return r;
}
#else
#define lj_fls(x) ((uint32_t)(__builtin_clz(x)^31))
#endif
#if defined(__i386__) || defined(__x86_64__)
static LJ_AINLINE uint32_t lj_bswap(uint32_t x)
{
uint32_t r; __asm__("bswap %0" : "=r" (r) : "0" (x)); return r;
}
#else
#error "missing define for lj_bswap()"
#endif
#elif defined(_MSC_VER)
#define LJ_NORET __declspec(noreturn)
#define LJ_ALIGN(n) __declspec(align(n))
#define LJ_INLINE __inline
#define LJ_AINLINE __forceinline
#define LJ_NOINLINE __declspec(noinline)
#if defined(_M_IX86)
#define LJ_FASTCALL __fastcall
#endif
static LJ_AINLINE uint32_t lj_ffs(uint32_t x)
{
uint32_t r; _BitScanForward(&r, x); return r;
}
static LJ_AINLINE uint32_t lj_fls(uint32_t x)
{
uint32_t r; _BitScanReverse(&r, x); return r;
}
#define lj_bswap(x) (_byteswap_ulong((x)))
#else
#error "missing defines for your compiler"
#endif
/* Optional defines. */
#ifndef LJ_FASTCALL
#define LJ_FASTCALL
#endif
#ifndef LJ_NORET
#define LJ_NORET
#endif
#ifndef LJ_NOAPI
#define LJ_NOAPI extern
#endif
#ifndef LJ_LIKELY
#define LJ_LIKELY(x) (x)
#define LJ_UNLIKELY(x) (x)
#endif
/* Attributes for internal functions. */
#if defined(ljamalg_c)
#define LJ_DATA static
#define LJ_DATADEF static
#define LJ_FUNC static
#define LJ_ASMF LJ_NOAPI
#define LJ_FUNCA LJ_NOAPI
#else
#define LJ_DATA LJ_NOAPI
#define LJ_DATADEF
#define LJ_FUNC LJ_NOAPI
#define LJ_ASMF LJ_NOAPI
#define LJ_FUNCA LJ_NOAPI
#endif
#define LJ_FUNC_NORET LJ_FUNC LJ_NORET
#define LJ_FUNCA_NORET LJ_FUNCA LJ_NORET
#define LJ_ASMF_NORET LJ_ASMF LJ_NORET
/* Runtime assertions. */
#ifdef lua_assert
#define check_exp(c, e) (lua_assert(c), (e))
#define api_check(l, e) lua_assert(e)
#else
#define lua_assert(c) ((void)0)
#define check_exp(c, e) (e)
#define api_check luai_apicheck
#endif
/* Static assertions. */
#define LJ_ASSERT_NAME2(name, line) name ## line
#define LJ_ASSERT_NAME(line) LJ_ASSERT_NAME2(lj_assert_, line)
#define LJ_STATIC_ASSERT(cond) \
extern void LJ_ASSERT_NAME(__LINE__)(int STATIC_ASSERTION_FAILED[(cond)?1:-1])
#endif

284
src/lj_dispatch.c Normal file
View File

@ -0,0 +1,284 @@
/*
** Instruction dispatch handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_dispatch_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_state.h"
#include "lj_frame.h"
#include "lj_bc.h"
#if LJ_HASJIT
#include "lj_jit.h"
#endif
#include "lj_trace.h"
#include "lj_dispatch.h"
#include "lj_vm.h"
#include "luajit.h"
/* -- Dispatch table management ------------------------------------------- */
/* Initialize instruction dispatch table and hot counters. */
void lj_dispatch_init(GG_State *GG)
{
uint32_t i;
ASMFunction *disp = GG->dispatch;
for (i = 0; i < BC__MAX; i++)
disp[GG_DISP_STATIC+i] = disp[i] = makeasmfunc(lj_vm_op_ofs[i]);
/* The JIT engine is off by default. luaopen_jit() turns it on. */
disp[BC_FORL] = disp[BC_IFORL];
disp[BC_ITERL] = disp[BC_IITERL];
disp[BC_LOOP] = disp[BC_ILOOP];
}
/* Update dispatch table depending on various flags. */
void lj_dispatch_update(global_State *g)
{
uint8_t oldmode = g->dispatchmode;
uint8_t mode = 0;
#if LJ_HASJIT
mode |= (G2J(g)->flags & JIT_F_ON) ? 1 : 0;
mode |= G2J(g)->state != LJ_TRACE_IDLE ? 6 : 0;
#endif
mode |= (g->hookmask & HOOK_EVENTMASK) ? 2 : 0;
if (oldmode != mode) { /* Mode changed? */
ASMFunction *disp = G2GG(g)->dispatch;
ASMFunction f_forl, f_iterl, f_loop;
g->dispatchmode = mode;
if ((mode & 5) == 1) { /* Hotcount if JIT is on, but not when recording. */
f_forl = makeasmfunc(lj_vm_op_ofs[BC_FORL]);
f_iterl = makeasmfunc(lj_vm_op_ofs[BC_ITERL]);
f_loop = makeasmfunc(lj_vm_op_ofs[BC_LOOP]);
} else { /* Otherwise use the non-hotcounting instructions. */
f_forl = disp[GG_DISP_STATIC+BC_IFORL];
f_iterl = disp[GG_DISP_STATIC+BC_IITERL];
f_loop = disp[GG_DISP_STATIC+BC_ILOOP];
}
/* Set static loop ins first (may be copied below). */
disp[GG_DISP_STATIC+BC_FORL] = f_forl;
disp[GG_DISP_STATIC+BC_ITERL] = f_iterl;
disp[GG_DISP_STATIC+BC_LOOP] = f_loop;
if ((oldmode & 6) != (mode & 6)) { /* Need to change whole table? */
if ((mode & 6) == 0) { /* No hooks and no recording? */
/* Copy static dispatch table to dynamic dispatch table. */
memcpy(&disp[0], &disp[GG_DISP_STATIC], sizeof(ASMFunction)*BC__MAX);
} else {
/* The recording dispatch also checks for hooks. */
ASMFunction f = (mode & 6) == 6 ? lj_vm_record : lj_vm_hook;
uint32_t i;
for (i = 0; i < BC__MAX; i++)
disp[i] = f;
}
} else if ((mode & 6) == 0) { /* Fix dynamic loop ins unless overriden. */
disp[BC_FORL] = f_forl;
disp[BC_ITERL] = f_iterl;
disp[BC_LOOP] = f_loop;
}
}
}
/* -- JIT mode setting ---------------------------------------------------- */
#if LJ_HASJIT
/* Set JIT mode for a single prototype. */
static void setptmode(global_State *g, GCproto *pt, int mode)
{
if ((mode & LUAJIT_MODE_ON)) { /* (Re-)enable JIT compilation. */
pt->flags &= ~PROTO_NO_JIT;
lj_trace_reenableproto(pt); /* Unpatch all ILOOP etc. bytecodes. */
} else { /* Flush and/or disable JIT compilation. */
if (!(mode & LUAJIT_MODE_FLUSH))
pt->flags |= PROTO_NO_JIT;
lj_trace_flushproto(g, pt); /* Flush all traces of prototype. */
}
}
/* Recursively set the JIT mode for all children of a prototype. */
static void setptmode_all(global_State *g, GCproto *pt, int mode)
{
ptrdiff_t i;
for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++) {
GCobj *o = gcref(pt->k.gc[i]);
if (o->gch.gct == ~LJ_TPROTO) {
setptmode(g, gco2pt(o), mode);
setptmode_all(g, gco2pt(o), mode);
}
}
}
#endif
/* Public API function: control the JIT engine. */
int luaJIT_setmode(lua_State *L, int idx, int mode)
{
global_State *g = G(L);
int mm = mode & LUAJIT_MODE_MASK;
lj_trace_abort(g); /* Abort recording on any state change. */
/* Avoid pulling the rug from under our own feet. */
if ((g->hookmask & HOOK_GC))
lj_err_caller(L, LJ_ERR_NOGCMM);
switch (mm) {
#if LJ_HASJIT
case LUAJIT_MODE_ENGINE:
if ((mode & LUAJIT_MODE_FLUSH)) {
lj_trace_flushall(L);
} else {
if ((mode & LUAJIT_MODE_ON))
G2J(g)->flags |= (uint32_t)JIT_F_ON;
else
G2J(g)->flags &= ~(uint32_t)JIT_F_ON;
lj_dispatch_update(g);
}
break;
case LUAJIT_MODE_FUNC:
case LUAJIT_MODE_ALLFUNC:
case LUAJIT_MODE_ALLSUBFUNC: {
cTValue *tv = idx == 0 ? frame_prev(L->base-1) :
idx > 0 ? L->base + (idx-1) : L->top + idx;
GCproto *pt;
if ((idx == 0 || tvisfunc(tv)) && isluafunc(&gcval(tv)->fn))
pt = funcproto(&gcval(tv)->fn); /* Cannot use funcV() for frame slot. */
else if (tvisproto(tv))
pt = protoV(tv);
else
return 0; /* Failed. */
if (mm != LUAJIT_MODE_ALLSUBFUNC)
setptmode(g, pt, mode);
if (mm != LUAJIT_MODE_FUNC)
setptmode_all(g, pt, mode);
break;
}
case LUAJIT_MODE_TRACE:
if (!(mode & LUAJIT_MODE_FLUSH))
return 0; /* Failed. */
lj_trace_flush(G2J(g), idx);
break;
#else
case LUAJIT_MODE_ENGINE:
case LUAJIT_MODE_FUNC:
case LUAJIT_MODE_ALLFUNC:
case LUAJIT_MODE_ALLSUBFUNC:
UNUSED(idx);
if ((mode & LUAJIT_MODE_ON))
return 0; /* Failed. */
break;
#endif
default:
return 0; /* Failed. */
}
return 1; /* OK. */
}
/* Enforce (dynamic) linker error for version mismatches. See luajit.c. */
LUA_API void LUAJIT_VERSION_SYM(void)
{
}
/* -- Hooks --------------------------------------------------------------- */
/* This function can be called asynchronously (e.g. during a signal). */
LUA_API int lua_sethook(lua_State *L, lua_Hook func, int mask, int count)
{
global_State *g = G(L);
mask &= HOOK_EVENTMASK;
if (func == NULL || mask == 0) { mask = 0; func = NULL; } /* Consistency. */
g->hookf = func;
g->hookcount = g->hookcstart = (int32_t)count;
g->hookmask = (uint8_t)((g->hookmask & ~HOOK_EVENTMASK) | mask);
lj_trace_abort(g); /* Abort recording on any hook change. */
lj_dispatch_update(g);
return 1;
}
LUA_API lua_Hook lua_gethook(lua_State *L)
{
return G(L)->hookf;
}
LUA_API int lua_gethookmask(lua_State *L)
{
return G(L)->hookmask & HOOK_EVENTMASK;
}
LUA_API int lua_gethookcount(lua_State *L)
{
return (int)G(L)->hookcstart;
}
/* Call a hook. */
static void callhook(lua_State *L, int event, BCLine line)
{
global_State *g = G(L);
lua_Hook hookf = g->hookf;
if (hookf && !hook_active(g)) {
lua_Debug ar;
lj_trace_abort(g); /* Abort recording on any hook call. */
ar.event = event;
ar.currentline = line;
ar.i_ci = cast_int((L->base-1) - L->stack); /* Top frame, nextframe=NULL. */
lj_state_checkstack(L, 1+LUA_MINSTACK);
hook_enter(g);
hookf(L, &ar);
lua_assert(hook_active(g));
hook_leave(g);
}
}
/* -- Instruction dispatch callbacks -------------------------------------- */
/* Calculate number of used stack slots in the current frame. */
static BCReg cur_topslot(GCproto *pt, const BCIns *pc, uint32_t nres)
{
BCIns ins = pc[-1];
for (;;) {
switch (bc_op(ins)) {
case BC_UCLO: ins = pc[bc_j(ins)]; break;
case BC_CALLM:
case BC_CALLMT: return bc_a(ins) + bc_c(ins) + nres-1+1;
case BC_RETM: return bc_a(ins) + bc_d(ins) + nres-1;
case BC_TSETM: return bc_a(ins) + nres-1;
default: return pt->framesize;
}
}
}
/* Instruction dispatch callback for instr/line hooks or when recording. */
void lj_dispatch_ins(lua_State *L, const BCIns *pc, uint32_t nres)
{
GCfunc *fn = curr_func(L);
GCproto *pt = funcproto(fn);
BCReg slots = cur_topslot(pt, pc, nres);
global_State *g = G(L);
const BCIns *oldpc = cframe_Lpc(L);
cframe_Lpc(L) = pc;
L->top = L->base + slots; /* Fix top. */
#if LJ_HASJIT
{
jit_State *J = G2J(g);
if (J->state != LJ_TRACE_IDLE) {
J->L = L;
J->pc = pc-1;
J->fn = fn;
J->pt = pt;
lj_trace_ins(J);
}
}
#endif
if ((g->hookmask & LUA_MASKCOUNT) && g->hookcount == 0) {
g->hookcount = g->hookcstart;
callhook(L, LUA_HOOKCOUNT, -1);
}
if ((g->hookmask & LUA_MASKLINE) && pt->lineinfo) {
BCPos npc = (BCPos)(pc - pt->bc)-1;
BCPos opc = (BCPos)(oldpc - pt->bc)-1;
BCLine line = pt->lineinfo[npc];
if (npc == 0 || pc <= oldpc ||
opc >= pt->sizebc || line != pt->lineinfo[opc]) {
L->top = L->base + slots; /* Fix top again after instruction hook. */
callhook(L, LUA_HOOKLINE, line);
}
}
}

64
src/lj_dispatch.h Normal file
View File

@ -0,0 +1,64 @@
/*
** Instruction dispatch handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_DISPATCH_H
#define _LJ_DISPATCH_H
#include "lj_obj.h"
#include "lj_bc.h"
#if LJ_HASJIT
#include "lj_jit.h"
#endif
/* Type of hot counter. Must match the code in the assembler VM. */
/* 16 bits are sufficient. Only 0.0015% overhead with maximum slot penalty. */
typedef uint16_t HotCount;
/* Number of hot counter hash table entries (must be a power of two). */
#define HOTCOUNT_SIZE 64
#define HOTCOUNT_PCMASK ((HOTCOUNT_SIZE-1)*sizeof(HotCount))
#define HOTCOUNT_MIN_PENALTY 103
#define HOTCOUNT_MAX_PENALTY 60000
/* Global state, main thread and extra fields are allocated together. */
typedef struct GG_State {
lua_State L; /* Main thread. */
global_State g; /* Global state. */
#if LJ_HASJIT
jit_State J; /* JIT state. */
HotCount hotcount[HOTCOUNT_SIZE]; /* Hot counters. */
#endif
ASMFunction dispatch[2*BC__MAX]; /* Instruction dispatch tables. */
} GG_State;
#define GG_DISP_STATIC BC__MAX
#define GG_OFS(field) ((int)offsetof(GG_State, field))
#define G2GG(gl) \
((GG_State *)(((char *)(gl))-((char *)(&((GG_State *)0)->g))))
#define J2GG(j) \
((GG_State *)(((char *)(j))-((char *)(&((GG_State *)0)->J))))
#define L2GG(L) G2GG(G(L))
#define J2G(J) (&J2GG(J)->g)
#define G2J(gl) (&G2GG(gl)->J)
#define L2J(L) (&L2GG(L)->J)
#define GG_G2DISP (GG_OFS(dispatch) - GG_OFS(g))
#define GG_DISP2G (GG_OFS(g) - GG_OFS(dispatch))
#define GG_DISP2J (GG_OFS(J) - GG_OFS(dispatch))
#define GG_DISP2HOT (GG_OFS(hotcount) - GG_OFS(dispatch))
#define hotcount_get(gg, pc) \
(gg)->hotcount[(u32ptr(pc)>>2) & (HOTCOUNT_SIZE-1)]
#define hotcount_set(gg, pc, val) \
(hotcount_get((gg), (pc)) = (HotCount)(val))
/* Dispatch table management. */
LJ_FUNC void lj_dispatch_init(GG_State *GG);
LJ_FUNC void lj_dispatch_update(global_State *g);
/* Instruction dispatch callback for instr/line hooks or when recording. */
LJ_FUNCA void lj_dispatch_ins(lua_State *L, const BCIns *pc, uint32_t nres);
#endif

763
src/lj_err.c Normal file
View File

@ -0,0 +1,763 @@
/*
** Error handling and debugging API.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_err_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_func.h"
#include "lj_state.h"
#include "lj_frame.h"
#include "lj_bc.h"
#include "lj_trace.h"
#include "lj_vm.h"
/* -- Error messages ------------------------------------------------------ */
/* Error message strings. */
static const char *lj_err_allmsg =
#define ERRDEF(name, msg) msg "\0"
#include "lj_errmsg.h"
;
#define err2msg(em) (lj_err_allmsg+(int)(em))
/* -- Frame and function introspection ------------------------------------ */
static BCPos currentpc(lua_State *L, GCfunc *fn, cTValue *nextframe)
{
const BCIns *ins;
lua_assert(fn->c.gct == ~LJ_TFUNC || fn->c.gct == ~LJ_TTHREAD);
if (!isluafunc(fn)) { /* Cannot derive a PC for non-Lua functions. */
return ~(BCPos)0;
} else if (nextframe == NULL) { /* Lua function on top. */
ins = cframe_Lpc(L); /* Only happens during error/hook handling. */
} else {
if (frame_islua(nextframe)) {
ins = frame_pc(nextframe);
} else if (frame_iscont(nextframe)) {
ins = frame_contpc(nextframe);
} else {
/* Lua function below errfunc/gc/hook: find cframe to get the PC. */
void *cf = cframe_raw(L->cframe);
TValue *f = L->base-1;
while (f > nextframe) {
if (frame_islua(f)) {
f = frame_prevl(f);
} else {
if (frame_isc(f))
cf = cframe_raw(cframe_prev(cf));
f = frame_prevd(f);
}
}
if (cframe_prev(cf))
cf = cframe_raw(cframe_prev(cf));
ins = cframe_pc(cf);
}
}
return (BCPos)((ins - funcproto(fn)->bc) - 1);
}
static BCLine currentline(lua_State *L, GCfunc *fn, cTValue *nextframe)
{
BCPos pc = currentpc(L, fn, nextframe);
if (pc != ~(BCPos)0) {
GCproto *pt = funcproto(fn);
lua_assert(pc < pt->sizebc);
return pt->lineinfo ? pt->lineinfo[pc] : 0;
} else {
return -1;
}
}
static const char *getvarname(const GCproto *pt, BCPos pc, BCReg slot)
{
MSize i;
for (i = 0; i < pt->sizevarinfo && pt->varinfo[i].startpc <= pc; i++)
if (pc < pt->varinfo[i].endpc && slot-- == 0)
return strdata(pt->varinfo[i].name);
return NULL;
}
static const char *getobjname(GCproto *pt, const BCIns *ip, BCReg slot,
const char **name)
{
const char *lname;
restart:
lname = getvarname(pt, (BCPos)(ip - pt->bc), slot);
if (lname != NULL) { *name = lname; return "local"; }
while (--ip >= pt->bc) {
BCIns ins = *ip;
BCOp op = bc_op(ins);
BCReg ra = bc_a(ins);
if (bcmode_a(op) == BCMbase) {
if (slot >= ra && (op != BC_KNIL || slot <= bc_d(ins)))
return NULL;
} else if (bcmode_a(op) == BCMdst && ra == slot) {
switch (bc_op(ins)) {
case BC_MOV:
if (ra == slot) { slot = bc_d(ins); goto restart; }
break;
case BC_GGET:
*name = strdata(gco2str(gcref(pt->k.gc[~bc_d(ins)])));
return "global";
case BC_TGETS:
*name = strdata(gco2str(gcref(pt->k.gc[~bc_c(ins)])));
if (ip > pt->bc) {
BCIns insp = ip[-1];
if (bc_op(insp) == BC_MOV && bc_a(insp) == ra+1 &&
bc_d(insp) == bc_b(ins))
return "method";
}
return "field";
case BC_UGET:
*name = pt->uvname ? strdata(pt->uvname[bc_d(ins)]) : "?";
return "upvalue";
default:
return NULL;
}
}
}
return NULL;
}
static const char *getfuncname(lua_State *L, TValue *frame, const char **name)
{
MMS mm;
const BCIns *ip;
TValue *pframe;
GCfunc *fn;
BCPos pc;
if (frame_isvarg(frame))
frame = frame_prevd(frame);
pframe = frame_prev(frame);
fn = frame_func(pframe);
pc = currentpc(L, fn, frame);
if (pc == ~(BCPos)0)
return NULL;
lua_assert(pc < funcproto(fn)->sizebc);
ip = &funcproto(fn)->bc[pc];
mm = bcmode_mm(bc_op(*ip));
if (mm == MM_call) {
BCReg slot = bc_a(*ip);
if (bc_op(*ip) == BC_ITERC) slot -= 3;
return getobjname(funcproto(fn), ip, slot, name);
} else if (mm != MM_MAX) {
*name = strdata(strref(G(L)->mmname[mm]));
return "metamethod";
} else {
return NULL;
}
}
void lj_err_pushloc(lua_State *L, GCproto *pt, BCPos pc)
{
GCstr *name = pt->chunkname;
if (name) {
const char *s = strdata(name);
MSize i, len = name->len;
BCLine line;
if (pc)
line = pt->lineinfo ? pt->lineinfo[pc-1] : 0;
else
line = pt->linedefined;
if (*s == '@') {
s++; len--;
for (i = len; i > 0; i--)
if (s[i] == '/' || s[i] == '\\') {
s += i+1;
break;
}
lj_str_pushf(L, "%s:%d", s, line);
} else if (len > 40) {
lj_str_pushf(L, "%p:%d", pt, line);
} else if (*s == '=') {
lj_str_pushf(L, "%s:%d", s+1, line);
} else {
lj_str_pushf(L, "\"%s\":%d", s, line);
}
} else {
lj_str_pushf(L, "%p:%u", pt, pc);
}
}
static void err_chunkid(char *out, const char *src)
{
if (*src == '=') {
strncpy(out, src+1, LUA_IDSIZE); /* remove first char */
out[LUA_IDSIZE-1] = '\0'; /* ensures null termination */
} else if (*src == '@') { /* out = "source", or "...source" */
size_t l = strlen(++src); /* skip the `@' */
if (l >= LUA_IDSIZE) {
src += l-(LUA_IDSIZE-4); /* get last part of file name */
strcpy(out, "...");
out += 3;
}
strcpy(out, src);
} else { /* out = [string "string"] */
size_t len; /* Length, up to first control char. */
for (len = 0; len < LUA_IDSIZE-11; len++)
if (((const unsigned char *)src)[len] < ' ') break;
strcpy(out, "[string \""); out += 9;
if (src[len] != '\0') { /* must truncate? */
if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15;
strncpy(out, src, len); out += len;
strcpy(out, "..."); out += 3;
} else {
strcpy(out, src); out += len;
}
strcpy(out, "\"]");
}
}
/* -- Public debug API ---------------------------------------------------- */
static TValue *findlocal(lua_State *L, const lua_Debug *ar,
const char **name, BCReg slot)
{
uint32_t offset = (uint32_t)ar->i_ci & 0xffff;
uint32_t size = (uint32_t)ar->i_ci >> 16;
TValue *frame = L->stack + offset;
TValue *nextframe = size ? frame + size : NULL;
GCfunc *fn = frame_func(frame);
BCPos pc = currentpc(L, fn, nextframe);
if (pc != ~(BCPos)0 &&
(*name = getvarname(funcproto(fn), pc, slot-1)) != NULL)
;
else if (slot > 0 && frame + slot < (nextframe ? nextframe : L->top))
*name = "(*temporary)";
else
*name = NULL;
return frame+slot;
}
LUA_API const char *lua_getlocal(lua_State *L, const lua_Debug *ar, int n)
{
const char *name;
TValue *o = findlocal(L, ar, &name, (BCReg)n);
if (name) {
copyTV(L, L->top, o);
incr_top(L);
}
return name;
}
LUA_API const char *lua_setlocal(lua_State *L, const lua_Debug *ar, int n)
{
const char *name;
TValue *o = findlocal(L, ar, &name, (BCReg)n);
if (name)
copyTV(L, o, L->top-1);
L->top--;
return name;
}
LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar)
{
int status = 1;
TValue *frame = NULL;
TValue *nextframe = NULL;
GCfunc *fn;
if (*what == '>') {
TValue *func = L->top - 1;
api_check(L, tvisfunc(func));
fn = funcV(func);
L->top--;
what++;
} else {
uint32_t offset = (uint32_t)ar->i_ci & 0xffff;
uint32_t size = (uint32_t)ar->i_ci >> 16;
lua_assert(offset != 0);
frame = L->stack + offset;
if (size) nextframe = frame + size;
lua_assert(frame<=L->maxstack && (!nextframe || nextframe<=L->maxstack));
fn = frame_func(frame);
lua_assert(fn->c.gct == ~LJ_TFUNC);
}
for (; *what; what++) {
switch (*what) {
case 'S':
if (isluafunc(fn)) {
ar->source = strdata(funcproto(fn)->chunkname);
ar->linedefined = cast_int(funcproto(fn)->linedefined);
ar->lastlinedefined = cast_int(funcproto(fn)->lastlinedefined);
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
} else {
ar->source = "=[C]";
ar->linedefined = -1;
ar->lastlinedefined = -1;
ar->what = "C";
}
err_chunkid(ar->short_src, ar->source);
break;
case 'l':
ar->currentline = frame ? currentline(L, fn, nextframe) : -1;
break;
case 'u':
ar->nups = fn->c.nupvalues;
break;
case 'n':
ar->namewhat = frame ? getfuncname(L, frame, &ar->name) : NULL;
if (ar->namewhat == NULL) {
ar->namewhat = "";
ar->name = NULL;
}
break;
case 'f':
setfuncV(L, L->top, fn);
incr_top(L);
break;
case 'L':
if (isluafunc(fn)) {
GCtab *t = lj_tab_new(L, 0, 0);
BCLine *lineinfo = funcproto(fn)->lineinfo;
uint32_t i, szl = funcproto(fn)->sizelineinfo;
for (i = 0; i < szl; i++)
setboolV(lj_tab_setint(L, t, lineinfo[i]), 1);
settabV(L, L->top, t);
} else {
setnilV(L->top);
}
incr_top(L);
break;
default:
status = 0; /* Bad option. */
break;
}
}
return status;
}
cTValue *lj_err_getframe(lua_State *L, int level, int *size)
{
cTValue *frame, *nextframe;
/* Traverse frames backwards. */
for (nextframe = frame = L->base-1; frame > L->stack; ) {
if (frame_gc(frame) == obj2gco(L))
level++; /* Skip dummy frames. See lj_meta_call(). */
if (level-- == 0) {
*size = cast_int(nextframe - frame);
return frame; /* Level found. */
}
nextframe = frame;
if (frame_islua(frame)) {
frame = frame_prevl(frame);
} else {
if (frame_isvarg(frame))
level++; /* Skip vararg pseudo-frame. */
frame = frame_prevd(frame);
}
}
*size = level;
return NULL; /* Level not found. */
}
LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar)
{
int size;
cTValue *frame = lj_err_getframe(L, level, &size);
if (frame) {
ar->i_ci = (size << 16) + cast_int(frame - L->stack);
return 1;
} else {
ar->i_ci = level - size;
return 0;
}
}
/* -- Error handling ------------------------------------------------------ */
/* Return string object for error message. */
LJ_NOINLINE GCstr *lj_err_str(lua_State *L, ErrMsg em)
{
return lj_str_newz(L, err2msg(em));
}
/* Unwind Lua stack and add error message on top. */
LJ_NOINLINE static void unwindstack(lua_State *L, TValue *top, int errcode)
{
lj_func_closeuv(L, top);
switch (errcode) {
case LUA_ERRMEM:
setstrV(L, top, lj_err_str(L, LJ_ERR_ERRMEM));
break;
case LUA_ERRERR:
setstrV(L, top, lj_err_str(L, LJ_ERR_ERRERR));
break;
case LUA_ERRSYNTAX:
case LUA_ERRRUN:
copyTV(L, top, L->top - 1);
break;
default:
lua_assert(0);
break;
}
L->top = top+1;
lj_state_relimitstack(L);
}
/* Throw error. Find catch frame, unwind stack and continue. */
LJ_NOINLINE void lj_err_throw(lua_State *L, int errcode)
{
TValue *frame = L->base-1;
void *cf = L->cframe;
global_State *g = G(L);
if (L->status == LUA_ERRERR+1) { /* Don't touch the stack during lua_open. */
lj_vm_unwind_c(cf, errcode);
goto uncaught; /* unreachable */
}
lj_trace_abort(g);
setgcrefnull(g->jit_L);
L->status = 0;
while (cf) {
if (cframe_nres(cframe_raw(cf)) < 0) { /* cframe without frame? */
TValue *top = restorestack(L, -cframe_nres(cf));
if (frame < top) {
L->cframe = cframe_prev(cf);
L->base = frame+1;
unwindstack(L, top, errcode);
lj_vm_unwind_c(cf, errcode);
goto uncaught; /* unreachable */
}
}
if (frame <= L->stack)
break;
switch (frame_typep(frame)) {
case FRAME_LUA:
case FRAME_LUAP:
frame = frame_prevl(frame);
break;
case FRAME_C:
if (cframe_canyield(cf)) goto uncaught;
cf = cframe_prev(cf);
/* fallthrough */
case FRAME_CONT:
case FRAME_VARG:
frame = frame_prevd(frame);
break;
case FRAME_CP:
L->cframe = cframe_prev(cf);
L->base = frame_prevd(frame) + 1;
unwindstack(L, frame, errcode);
lj_vm_unwind_c(cf, errcode);
goto uncaught; /* unreachable */
case FRAME_PCALL:
hook_leave(g);
/* fallthrough */
case FRAME_PCALLH:
L->cframe = cf;
L->base = frame_prevd(frame) + 1;
unwindstack(L, L->base, errcode);
lj_vm_unwind_ff(cf);
goto uncaught; /* unreachable */
default:
lua_assert(0);
goto uncaught;
}
}
/* No catch frame found. Must be a resume or an unprotected error. */
uncaught:
L->status = cast_byte(errcode);
L->cframe = NULL;
if (cframe_canyield(cf)) { /* Resume? */
unwindstack(L, L->top, errcode);
lj_vm_unwind_c(cf, errcode);
}
/* Better rethrow on main thread than panic. */
{
if (L != mainthread(g))
lj_err_throw(mainthread(g), errcode);
if (g->panic) {
L->base = L->stack+1;
unwindstack(L, L->base, errcode);
g->panic(L);
}
}
exit(EXIT_FAILURE);
}
/* Find error function for runtime errors. Requires an extra stack traversal. */
static ptrdiff_t finderrfunc(lua_State *L)
{
TValue *frame = L->base-1;
void *cf = L->cframe;
while (frame > L->stack) {
lua_assert(cf != NULL);
while (cframe_nres(cframe_raw(cf)) < 0) { /* cframe without frame? */
if (frame >= restorestack(L, -cframe_nres(cf)))
break;
if (cframe_errfunc(cf) >= 0) /* Error handler not inherited (-1)? */
return cframe_errfunc(cf);
cf = cframe_prev(cf); /* Else unwind cframe and continue searching. */
if (cf == NULL)
return 0;
}
switch (frame_typep(frame)) {
case FRAME_LUA:
case FRAME_LUAP:
frame = frame_prevl(frame);
break;
case FRAME_C:
if (cframe_canyield(cf)) return 0;
cf = cframe_prev(cf);
/* fallthrough */
case FRAME_CONT:
case FRAME_VARG:
frame = frame_prevd(frame);
break;
case FRAME_CP:
if (cframe_errfunc(cf) >= 0)
return cframe_errfunc(cf);
frame = frame_prevd(frame);
break;
case FRAME_PCALL:
case FRAME_PCALLH:
if (frame_ftsz(frame) >= (ptrdiff_t)(2*sizeof(TValue))) /* xpcall? */
return savestack(L, frame-1); /* Point to xpcall's errorfunc. */
return 0;
default:
lua_assert(0);
return 0;
}
}
return 0;
}
/* Runtime error. */
LJ_NOINLINE void lj_err_run(lua_State *L)
{
ptrdiff_t ef = finderrfunc(L);
if (ef) {
TValue *errfunc = restorestack(L, ef);
TValue *top = L->top;
lj_trace_abort(G(L));
if (!tvisfunc(errfunc) || L->status == LUA_ERRERR)
lj_err_throw(L, LUA_ERRERR);
L->status = LUA_ERRERR;
copyTV(L, top, top-1);
copyTV(L, top-1, errfunc);
L->top = top+1;
lj_vm_call(L, top, 1+1); /* Stack: |errfunc|msg| -> |msg| */
}
lj_err_throw(L, LUA_ERRRUN);
}
/* Add location to error message. */
LJ_NOINLINE static void err_loc(lua_State *L, const char *msg,
cTValue *frame, cTValue *nextframe)
{
if (frame) {
GCfunc *fn = frame_func(frame);
if (isluafunc(fn)) {
char buff[LUA_IDSIZE];
BCLine line = currentline(L, fn, nextframe);
err_chunkid(buff, strdata(funcproto(fn)->chunkname));
lj_str_pushf(L, "%s:%d: %s", buff, line, msg);
return;
}
}
lj_str_pushf(L, "%s", msg);
}
/* Formatted runtime error message. */
LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...)
{
const char *msg;
va_list argp;
va_start(argp, em);
if (curr_funcisL(L)) L->top = curr_topL(L);
msg = lj_str_pushvf(L, err2msg(em), argp);
va_end(argp);
err_loc(L, msg, L->base-1, NULL);
lj_err_run(L);
}
/* Non-vararg variant for better calling conventions. */
LJ_NOINLINE void lj_err_msg(lua_State *L, ErrMsg em)
{
err_msgv(L, em);
}
/* Lexer error. */
LJ_NOINLINE void lj_err_lex(lua_State *L, const char *src, const char *tok,
BCLine line, ErrMsg em, va_list argp)
{
char buff[LUA_IDSIZE];
const char *msg;
err_chunkid(buff, src);
msg = lj_str_pushvf(L, err2msg(em), argp);
msg = lj_str_pushf(L, "%s:%d: %s", buff, line, msg);
if (tok)
lj_str_pushf(L, err2msg(LJ_ERR_XNEAR), msg, tok);
lj_err_throw(L, LUA_ERRSYNTAX);
}
/* Typecheck error for operands. */
LJ_NOINLINE void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm)
{
const char *tname = typename(o);
const char *oname = NULL;
const char *opname = err2msg(opm);
if (curr_funcisL(L)) {
GCproto *pt = curr_proto(L);
const BCIns *pc = cframe_Lpc(L) - 1;
const char *kind = getobjname(pt, pc, (BCReg)(o - L->base), &oname);
if (kind)
err_msgv(L, LJ_ERR_BADOPRT, opname, kind, oname, tname);
}
err_msgv(L, LJ_ERR_BADOPRV, opname, tname);
}
/* Typecheck error for ordered comparisons. */
LJ_NOINLINE void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2)
{
const char *t1 = typename(o1);
const char *t2 = typename(o2);
err_msgv(L, t1 == t2 ? LJ_ERR_BADCMPV : LJ_ERR_BADCMPT, t1, t2);
/* This assumes the two "boolean" entries are commoned by the C compiler. */
}
/* Typecheck error for __call. */
LJ_NOINLINE void lj_err_optype_call(lua_State *L, TValue *o)
{
/* Gross hack if lua_[p]call or pcall/xpcall fail for a non-callable object:
** L->base still points to the caller. So add a dummy frame with L instead
** of a function. See lua_getstack().
*/
const BCIns *pc = cframe_Lpc(L);
if (((ptrdiff_t)pc & FRAME_TYPE) != FRAME_LUA) {
const char *tname = typename(o);
setframe_pc(o, pc);
setframe_gc(o, obj2gco(L));
L->top = L->base = o+1;
err_msgv(L, LJ_ERR_BADCALL, tname);
}
lj_err_optype(L, o, LJ_ERR_OPCALL);
}
/* Error in context of caller. */
LJ_NOINLINE void lj_err_callermsg(lua_State *L, const char *msg)
{
cTValue *frame = L->base-1;
cTValue *pframe = frame_islua(frame) ? frame_prevl(frame) : NULL;
err_loc(L, msg, pframe, frame);
lj_err_run(L);
}
/* Formatted error in context of caller. */
LJ_NOINLINE void lj_err_callerv(lua_State *L, ErrMsg em, ...)
{
const char *msg;
va_list argp;
va_start(argp, em);
msg = lj_str_pushvf(L, err2msg(em), argp);
va_end(argp);
lj_err_callermsg(L, msg);
}
/* Error in context of caller. */
LJ_NOINLINE void lj_err_caller(lua_State *L, ErrMsg em)
{
lj_err_callermsg(L, err2msg(em));
}
/* Argument error message. */
LJ_NORET LJ_NOINLINE static void err_argmsg(lua_State *L, int narg,
const char *msg)
{
const char *fname = "?";
const char *ftype = getfuncname(L, L->base - 1, &fname);
if (ftype && ftype[3] == 'h' && --narg == 0) /* Check for "method". */
msg = lj_str_pushf(L, err2msg(LJ_ERR_BADSELF), fname, msg);
else
msg = lj_str_pushf(L, err2msg(LJ_ERR_BADARG), narg, fname, msg);
lj_err_callermsg(L, msg);
}
/* Formatted argument error. */
LJ_NOINLINE void lj_err_argv(lua_State *L, int narg, ErrMsg em, ...)
{
const char *msg;
va_list argp;
va_start(argp, em);
msg = lj_str_pushvf(L, err2msg(em), argp);
va_end(argp);
err_argmsg(L, narg, msg);
}
/* Argument error. */
LJ_NOINLINE void lj_err_arg(lua_State *L, int narg, ErrMsg em)
{
err_argmsg(L, narg, err2msg(em));
}
/* Typecheck error for arguments. */
LJ_NOINLINE void lj_err_argtype(lua_State *L, int narg, const char *xname)
{
TValue *o = L->base + narg-1;
const char *tname = o < L->top ? typename(o) : lj_obj_typename[0];
const char *msg = lj_str_pushf(L, err2msg(LJ_ERR_BADTYPE), xname, tname);
err_argmsg(L, narg, msg);
}
/* Typecheck error for arguments. */
LJ_NOINLINE void lj_err_argt(lua_State *L, int narg, int tt)
{
lj_err_argtype(L, narg, lj_obj_typename[tt+1]);
}
/* -- Public error handling API ------------------------------------------- */
LUA_API lua_CFunction lua_atpanic(lua_State *L, lua_CFunction panicf)
{
lua_CFunction old = G(L)->panic;
G(L)->panic = panicf;
return old;
}
/* Forwarders for the public API (C calling convention and no LJ_NORET). */
LUA_API int lua_error(lua_State *L)
{
lj_err_run(L);
return 0; /* unreachable */
}
LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *msg)
{
err_argmsg(L, narg, msg);
return 0; /* unreachable */
}
LUALIB_API int luaL_typerror(lua_State *L, int narg, const char *xname)
{
lj_err_argtype(L, narg, xname);
return 0; /* unreachable */
}
LUALIB_API void luaL_where(lua_State *L, int level)
{
int size;
cTValue *frame = lj_err_getframe(L, level, &size);
err_loc(L, "", frame, size ? frame+size : NULL);
}
LUALIB_API int luaL_error(lua_State *L, const char *fmt, ...)
{
const char *msg;
va_list argp;
va_start(argp, fmt);
msg = lj_str_pushvf(L, fmt, argp);
va_end(argp);
lj_err_callermsg(L, msg);
return 0; /* unreachable */
}

40
src/lj_err.h Normal file
View File

@ -0,0 +1,40 @@
/*
** Error handling and debugging support.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_ERR_H
#define _LJ_ERR_H
#include <stdarg.h>
#include "lj_obj.h"
typedef enum {
#define ERRDEF(name, msg) \
LJ_ERR_##name, LJ_ERR_##name##_ = LJ_ERR_##name + sizeof(msg)-1,
#include "lj_errmsg.h"
LJ_ERR__MAX
} ErrMsg;
LJ_FUNC GCstr *lj_err_str(lua_State *L, ErrMsg em);
LJ_FUNC_NORET void lj_err_throw(lua_State *L, int errcode);
LJ_FUNC_NORET void lj_err_run(lua_State *L);
LJ_FUNC_NORET void lj_err_msg(lua_State *L, ErrMsg em);
LJ_FUNC_NORET void lj_err_lex(lua_State *L, const char *src, const char *tok,
BCLine line, ErrMsg em, va_list argp);
LJ_FUNC_NORET void lj_err_optype(lua_State *L, cTValue *o, ErrMsg opm);
LJ_FUNC_NORET void lj_err_comp(lua_State *L, cTValue *o1, cTValue *o2);
LJ_FUNC_NORET void lj_err_optype_call(lua_State *L, TValue *o);
LJ_FUNC_NORET void lj_err_callermsg(lua_State *L, const char *msg);
LJ_FUNC_NORET void lj_err_callerv(lua_State *L, ErrMsg em, ...);
LJ_FUNC_NORET void lj_err_caller(lua_State *L, ErrMsg em);
LJ_FUNC_NORET void lj_err_arg(lua_State *L, int narg, ErrMsg em);
LJ_FUNC_NORET void lj_err_argv(lua_State *L, int narg, ErrMsg em, ...);
LJ_FUNC_NORET void lj_err_argtype(lua_State *L, int narg, const char *xname);
LJ_FUNC_NORET void lj_err_argt(lua_State *L, int narg, int tt);
LJ_FUNC void lj_err_pushloc(lua_State *L, GCproto *pt, BCPos pc);
LJ_FUNC cTValue *lj_err_getframe(lua_State *L, int level, int *size);
#endif

134
src/lj_errmsg.h Normal file
View File

@ -0,0 +1,134 @@
/*
** VM error messages.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
/* This file may be included multiple times with different ERRDEF macros. */
/* Basic error handling. */
ERRDEF(ERRMEM, "not enough memory")
ERRDEF(ERRERR, "error in error handling")
/* Allocations. */
ERRDEF(STROV, "string length overflow")
ERRDEF(UDATAOV, "userdata length overflow")
ERRDEF(STKOV, "stack overflow")
ERRDEF(STKOVM, "stack overflow (%s)")
ERRDEF(TABOV, "table overflow")
/* Table indexing. */
ERRDEF(NANIDX, "table index is NaN")
ERRDEF(NILIDX, "table index is nil")
ERRDEF(NEXTIDX, "invalid key to " LUA_QL("next"))
/* Metamethod resolving. */
ERRDEF(BADCALL, "attempt to call a %s value")
ERRDEF(BADOPRT, "attempt to %s %s " LUA_QS " (a %s value)")
ERRDEF(BADOPRV, "attempt to %s a %s value")
ERRDEF(BADCMPT, "attempt to compare %s with %s")
ERRDEF(BADCMPV, "attempt to compare two %s values")
ERRDEF(GETLOOP, "loop in gettable")
ERRDEF(SETLOOP, "loop in settable")
ERRDEF(OPCALL, "call")
ERRDEF(OPINDEX, "index")
ERRDEF(OPARITH, "perform arithmetic on")
ERRDEF(OPCAT, "concatenate")
ERRDEF(OPLEN, "get length of")
/* Type checks. */
ERRDEF(BADSELF, "calling " LUA_QS " on bad self (%s)")
ERRDEF(BADARG, "bad argument #%d to " LUA_QS " (%s)")
ERRDEF(BADTYPE, "%s expected, got %s")
ERRDEF(BADVAL, "invalid value")
ERRDEF(NOVAL, "value expected")
ERRDEF(NOCORO, "coroutine expected")
ERRDEF(NOTABN, "nil or table expected")
ERRDEF(NOLFUNC, "Lua function expected")
ERRDEF(NOFUNCL, "function or level expected")
ERRDEF(NOSFT, "string/function/table expected")
ERRDEF(NOPROXY, "boolean or proxy expected")
ERRDEF(FORINIT, LUA_QL("for") " initial value must be a number")
ERRDEF(FORLIM, LUA_QL("for") " limit must be a number")
ERRDEF(FORSTEP, LUA_QL("for") " step must be a number")
/* C API checks. */
ERRDEF(NOENV, "no calling environment")
ERRDEF(CYIELD, "attempt to yield across C-call boundary")
ERRDEF(BADLU, "bad light userdata pointer")
ERRDEF(NOGCMM, "bad action while in __gc metamethod")
/* Standard library function errors. */
ERRDEF(ASSERT, "assertion failed!")
ERRDEF(PROTMT, "cannot change a protected metatable")
ERRDEF(UNPACK, "too many results to unpack")
ERRDEF(RDRSTR, "reader function must return a string")
ERRDEF(PRTOSTR, LUA_QL("tostring") " must return a string to " LUA_QL("print"))
ERRDEF(IDXRNG, "index out of range")
ERRDEF(BASERNG, "base out of range")
ERRDEF(LVLRNG, "level out of range")
ERRDEF(INVLVL, "invalid level")
ERRDEF(INVOPT, "invalid option")
ERRDEF(INVOPTM, "invalid option " LUA_QS)
ERRDEF(INVFMT, "invalid format")
ERRDEF(SETFENV, LUA_QL("setfenv") " cannot change environment of given object")
ERRDEF(CORUN, "cannot resume running coroutine")
ERRDEF(CODEAD, "cannot resume dead coroutine")
ERRDEF(COSUSP, "cannot resume non-suspended coroutine")
ERRDEF(TABINS, "wrong number of arguments to " LUA_QL("insert"))
ERRDEF(TABCAT, "invalid value (%s) at index %d in table for " LUA_QL("concat"))
ERRDEF(TABSORT, "invalid order function for sorting")
ERRDEF(IOCLFL, "attempt to use a closed file")
ERRDEF(IOSTDCL, "standard file is closed")
ERRDEF(OSUNIQF, "unable to generate a unique filename")
ERRDEF(OSDATEF, "field " LUA_QS " missing in date table")
ERRDEF(STRDUMP, "cannot dump functions")
ERRDEF(STRSLC, "string slice too long")
ERRDEF(STRPATB, "missing " LUA_QL("[") " after " LUA_QL("%f") " in pattern")
ERRDEF(STRPATC, "invalid pattern capture")
ERRDEF(STRPATE, "malformed pattern (ends with " LUA_QL("%") ")")
ERRDEF(STRPATM, "malformed pattern (missing " LUA_QL("]") ")")
ERRDEF(STRPATU, "unbalanced pattern")
ERRDEF(STRCAPI, "invalid capture index")
ERRDEF(STRCAPN, "too many captures")
ERRDEF(STRCAPU, "unfinished capture")
ERRDEF(STRFMTO, "invalid option " LUA_QL("%%%c") " to " LUA_QL("format"))
ERRDEF(STRFMTR, "invalid format (repeated flags)")
ERRDEF(STRFMTW, "invalid format (width or precision too long)")
ERRDEF(STRGSRV, "invalid replacement value (a %s)")
ERRDEF(BADMODN, "name conflict for module " LUA_QS)
ERRDEF(NOJIT, "JIT compiler permanently disabled")
ERRDEF(JITOPT, "unknown or malformed optimization flag " LUA_QS)
/* Lexer/parser errors. */
ERRDEF(XNEAR, "%s near " LUA_QS)
ERRDEF(XELEM, "lexical element too long")
ERRDEF(XLINES, "chunk has too many lines")
ERRDEF(XLEVELS, "chunk has too many syntax levels")
ERRDEF(XNUMBER, "malformed number")
ERRDEF(XLSTR, "unfinished long string")
ERRDEF(XLCOM, "unfinished long comment")
ERRDEF(XSTR, "unfinished string")
ERRDEF(XESC, "escape sequence too large")
ERRDEF(XLDELIM, "invalid long string delimiter")
ERRDEF(XBCLOAD, "cannot load Lua bytecode")
ERRDEF(XTOKEN, LUA_QS " expected")
ERRDEF(XJUMP, "control structure too long")
ERRDEF(XSLOTS, "function or expression too complex")
ERRDEF(XLIMM, "main function has more than %d %s")
ERRDEF(XLIMF, "function at line %d has more than %d %s")
ERRDEF(XMATCH, LUA_QS " expected (to close " LUA_QS " at line %d)")
ERRDEF(XFIXUP, "function too long for return fixup")
ERRDEF(XPARAM, "<name> or " LUA_QL("...") " expected")
ERRDEF(XAMBIG, "ambiguous syntax (function call x new statement)")
ERRDEF(XFUNARG, "function arguments expected")
ERRDEF(XSYMBOL, "unexpected symbol")
ERRDEF(XDOTS, "cannot use " LUA_QL("...") " outside a vararg function")
ERRDEF(XSYNTAX, "syntax error")
ERRDEF(XBREAK, "no loop to break")
ERRDEF(XFOR, LUA_QL("=") " or " LUA_QL("in") " expected")
#undef ERRDEF
/* Detecting unused error messages:
awk -F, '/^ERRDEF/ { gsub(/ERRDEF./, ""); printf "grep -q LJ_ERR_%s *.[ch] || echo %s\n", $1, $1}' lj_errmsg.h | sh
*/

18
src/lj_ff.h Normal file
View File

@ -0,0 +1,18 @@
/*
** Fast function IDs.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_FF_H
#define _LJ_FF_H
/* Fast function ID. */
typedef enum {
FF_LUA_ = FF_LUA, /* Lua function (must be 0). */
FF_C_ = FF_C, /* Regular C function (must be 1). */
#define FFDEF(name) FF_##name,
#include "lj_ffdef.h"
FF__MAX
} FastFunc;
#endif

84
src/lj_frame.h Normal file
View File

@ -0,0 +1,84 @@
/*
** Stack frames.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_FRAME_H
#define _LJ_FRAME_H
#include "lj_obj.h"
#include "lj_bc.h"
/* -- Lua stack frame ----------------------------------------------------- */
/* Frame type markers in callee function slot (callee base-1). */
enum {
FRAME_LUA, FRAME_C, FRAME_CONT, FRAME_VARG,
FRAME_LUAP, FRAME_CP, FRAME_PCALL, FRAME_PCALLH
};
#define FRAME_TYPE 3
#define FRAME_P 4
#define FRAME_TYPEP (FRAME_TYPE|FRAME_P)
/* Macros to access and modify Lua frames. */
#define frame_gc(f) (gcref((f)->fr.func))
#define frame_func(f) (&frame_gc(f)->fn)
#define frame_ftsz(f) ((f)->fr.tp.ftsz)
#define frame_type(f) (frame_ftsz(f) & FRAME_TYPE)
#define frame_typep(f) (frame_ftsz(f) & FRAME_TYPEP)
#define frame_islua(f) (frame_type(f) == FRAME_LUA)
#define frame_isc(f) (frame_type(f) == FRAME_C)
#define frame_iscont(f) (frame_typep(f) == FRAME_CONT)
#define frame_isvarg(f) (frame_typep(f) == FRAME_VARG)
#define frame_ispcall(f) ((frame_ftsz(f) & 6) == FRAME_PCALL)
#define frame_pc(f) (mref((f)->fr.tp.pcr, const BCIns))
#define frame_contpc(f) (frame_pc((f)-1))
#if LJ_64
#define frame_contf(f) \
((ASMFunction)(void *)((intptr_t)lj_vm_asm_begin+(((f)-1)->u64 & 0xffffffff)))
#else
#define frame_contf(f) ((ASMFunction)gcrefp(((f)-1)->gcr, void))
#endif
#define frame_delta(f) (frame_ftsz(f) >> 3)
#define frame_sized(f) (frame_ftsz(f) & ~FRAME_TYPEP)
#define frame_prevl(f) ((f) - (1+bc_a(frame_pc(f)[-1])))
#define frame_prevd(f) ((TValue *)((char *)(f) - frame_sized(f)))
#define frame_prev(f) (frame_islua(f)?frame_prevl(f):frame_prevd(f))
/* Note: this macro does not skip over FRAME_VARG. */
#define setframe_pc(f, pc) (setmref((f)->fr.tp.pcr, (pc)))
#define setframe_gc(f, p) (setgcref((f)->fr.func, (p)))
/* -- C stack frame ------------------------------------------------------- */
/* Macros to access and modify the C stack frame chain. */
/* These definitions must match with the arch-specific *.dasc files. */
#if LJ_TARGET_X86
#define CFRAME_OFS_ERRF (15*sizeof(void *))
#define CFRAME_OFS_NRES (14*sizeof(void *))
#define CFRAME_OFS_PREV (13*sizeof(void *))
#define CFRAME_OFS_L (12*sizeof(void *))
#define CFRAME_OFS_PC (6*sizeof(void *))
#define CFRAME_SIZE (12*sizeof(void *))
#else
#error "Missing CFRAME_* definitions for this architecture"
#endif
#define CFRAME_RESUME 1
#define CFRAME_CANYIELD ((intptr_t)(CFRAME_RESUME))
#define CFRAME_RAWMASK (~CFRAME_CANYIELD)
#define cframe_errfunc(cf) (*(ptrdiff_t *)(((char *)cf)+CFRAME_OFS_ERRF))
#define cframe_nres(cf) (*(ptrdiff_t *)(((char *)cf)+CFRAME_OFS_NRES))
#define cframe_prev(cf) (*(void **)(((char *)cf)+CFRAME_OFS_PREV))
#define cframe_L(cf) (*(lua_State **)(((char *)cf)+CFRAME_OFS_L))
#define cframe_pc(cf) (*(const BCIns **)(((char *)cf)+CFRAME_OFS_PC))
#define cframe_canyield(cf) ((intptr_t)(cf) & CFRAME_CANYIELD)
#define cframe_raw(cf) ((void *)((intptr_t)(cf) & CFRAME_RAWMASK))
#define cframe_Lpc(L) cframe_pc(cframe_raw(L->cframe))
#endif

185
src/lj_func.c Normal file
View File

@ -0,0 +1,185 @@
/*
** Function handling (prototypes, functions and upvalues).
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_func_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_func.h"
#include "lj_trace.h"
#include "lj_vm.h"
/* -- Prototypes ---------------------------------------------------------- */
GCproto *lj_func_newproto(lua_State *L)
{
GCproto *pt = lj_mem_newobj(L, GCproto);
pt->gct = ~LJ_TPROTO;
pt->numparams = 0;
pt->framesize = 0;
pt->sizeuv = 0;
pt->flags = 0;
pt->trace = 0;
pt->k.n = NULL;
pt->bc = NULL;
pt->uv = NULL;
pt->sizebc = 0;
pt->sizekgc = 0;
pt->sizekn = 0;
pt->sizelineinfo = 0;
pt->sizevarinfo = 0;
pt->sizeuvname = 0;
pt->linedefined = 0;
pt->lastlinedefined = 0;
pt->lineinfo = NULL;
pt->varinfo = NULL;
pt->uvname = NULL;
pt->chunkname = NULL;
return pt;
}
void LJ_FASTCALL lj_func_freeproto(global_State *g, GCproto *pt)
{
MSize nkgc = round_nkgc(pt->sizekgc);
MSize sizek = nkgc*(MSize)sizeof(GCobj *) +
pt->sizekn*(MSize)sizeof(lua_Number);
lj_mem_free(g, pt->k.gc - nkgc, sizek);
lj_mem_freevec(g, pt->bc, pt->sizebc, BCIns);
lj_mem_freevec(g, pt->uv, pt->sizeuv, int16_t);
lj_mem_freevec(g, pt->lineinfo, pt->sizelineinfo, int32_t);
lj_mem_freevec(g, pt->varinfo, pt->sizevarinfo, struct VarInfo);
lj_mem_freevec(g, pt->uvname, pt->sizeuvname, GCstr *);
lj_trace_freeproto(g, pt);
lj_mem_freet(g, pt);
}
/* -- Upvalues ------------------------------------------------------------ */
static void unlinkuv(GCupval *uv)
{
lua_assert(uvprev(uvnext(uv)) == uv && uvnext(uvprev(uv)) == uv);
setgcrefr(uvnext(uv)->prev, uv->prev);
setgcrefr(uvprev(uv)->next, uv->next);
}
/* Find existing open upvalue for a stack slot or create a new one. */
static GCupval *func_finduv(lua_State *L, TValue *slot)
{
global_State *g = G(L);
GCRef *pp = &L->openupval;
GCupval *p;
GCupval *uv;
/* Search the sorted list of open upvalues. */
while (gcref(*pp) != NULL && (p = gco2uv(gcref(*pp)))->v >= slot) {
lua_assert(!p->closed && p->v != &p->tv);
if (p->v == slot) { /* Found open upvalue pointing to same slot? */
if (isdead(g, obj2gco(p))) /* Resurrect it, if it's dead. */
flipwhite(obj2gco(p));
return p;
}
pp = &p->nextgc;
}
/* No matching upvalue found. Create a new one. */
uv = lj_mem_newt(L, sizeof(GCupval), GCupval);
newwhite(g, uv);
uv->gct = ~LJ_TUPVAL;
uv->closed = 0; /* Still open. */
uv->v = slot; /* Pointing to the stack slot. */
/* NOBARRIER: The GCupval is new (marked white) and open. */
setgcrefr(uv->nextgc, *pp); /* Insert into sorted list of open upvalues. */
setgcref(*pp, obj2gco(uv));
setgcref(uv->prev, obj2gco(&g->uvhead)); /* Insert into GC list, too. */
setgcrefr(uv->next, g->uvhead.next);
setgcref(uvnext(uv)->prev, obj2gco(uv));
setgcref(g->uvhead.next, obj2gco(uv));
lua_assert(uvprev(uvnext(uv)) == uv && uvnext(uvprev(uv)) == uv);
return uv;
}
/* Close all open upvalues pointing to some stack level or above. */
void lj_func_closeuv(lua_State *L, TValue *level)
{
GCupval *uv;
global_State *g = G(L);
while (gcref(L->openupval) != NULL &&
(uv = gco2uv(gcref(L->openupval)))->v >= level) {
GCobj *o = obj2gco(uv);
lua_assert(!isblack(o) && !uv->closed && uv->v != &uv->tv);
setgcrefr(L->openupval, uv->nextgc); /* No longer in open list. */
if (isdead(g, o)) {
lj_func_freeuv(g, uv);
} else {
unlinkuv(uv);
lj_gc_closeuv(g, uv);
}
}
}
void LJ_FASTCALL lj_func_freeuv(global_State *g, GCupval *uv)
{
if (!uv->closed)
unlinkuv(uv);
lj_mem_freet(g, uv);
}
/* -- Functions (closures) ------------------------------------------------ */
GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env)
{
GCfunc *fn = cast(GCfunc *, lj_mem_newgco(L, sizeCfunc(nelems)));
fn->c.gct = ~LJ_TFUNC;
fn->c.ffid = FF_C;
fn->c.nupvalues = cast_byte(nelems);
/* NOBARRIER: The GCfunc is new (marked white). */
setgcref(fn->c.env, obj2gco(env));
fn->c.gate = lj_gate_c;
return fn;
}
GCfunc *lj_func_newL(lua_State *L, GCproto *pt, GCtab *env)
{
GCfunc *fn = cast(GCfunc *, lj_mem_newgco(L, sizeLfunc((MSize)pt->sizeuv)));
fn->l.gct = ~LJ_TFUNC;
fn->l.ffid = FF_LUA;
fn->l.nupvalues = cast_byte(pt->sizeuv);
/* NOBARRIER: The GCfunc is new (marked white). */
setgcref(fn->l.pt, obj2gco(pt));
setgcref(fn->l.env, obj2gco(env));
fn->l.gate = (pt->flags & PROTO_IS_VARARG) ? lj_gate_lv : lj_gate_lf;
return fn;
}
/* Do a GC check and create a new Lua function with inherited upvalues. */
GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent)
{
GCfunc *fn;
GCRef *puv;
uint32_t i, nuv;
TValue *base;
lj_gc_check_fixtop(L);
fn = lj_func_newL(L, pt, tabref(parent->env));
/* NOBARRIER: The GCfunc is new (marked white). */
puv = parent->uvptr;
nuv = fn->l.nupvalues;
base = L->base;
for (i = 0; i < nuv; i++) {
int v = pt->uv[i];
GCupval *uv = v < 0 ? &gcref(puv[~v])->uv : func_finduv(L, base + v);
setgcref(fn->l.uvptr[i], obj2gco(uv));
}
return fn;
}
void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *fn)
{
MSize size = isluafunc(fn) ? sizeLfunc((MSize)fn->l.nupvalues) :
sizeCfunc((MSize)fn->c.nupvalues);
lj_mem_free(g, fn, size);
}

25
src/lj_func.h Normal file
View File

@ -0,0 +1,25 @@
/*
** Function handling (prototypes, functions and upvalues).
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_FUNC_H
#define _LJ_FUNC_H
#include "lj_obj.h"
/* Prototypes. */
LJ_FUNC GCproto *lj_func_newproto(lua_State *L);
LJ_FUNC void LJ_FASTCALL lj_func_freeproto(global_State *g, GCproto *pt);
/* Upvalues. */
LJ_FUNCA void lj_func_closeuv(lua_State *L, TValue *level);
LJ_FUNC void LJ_FASTCALL lj_func_freeuv(global_State *g, GCupval *uv);
/* Functions (closures). */
LJ_FUNC GCfunc *lj_func_newC(lua_State *L, MSize nelems, GCtab *env);
LJ_FUNC GCfunc *lj_func_newL(lua_State *L, GCproto *pt, GCtab *env);
LJ_FUNCA GCfunc *lj_func_newL_gc(lua_State *L, GCproto *pt, GCfuncL *parent);
LJ_FUNC void LJ_FASTCALL lj_func_free(global_State *g, GCfunc *c);
#endif

800
src/lj_gc.c Normal file
View File

@ -0,0 +1,800 @@
/*
** Garbage collector.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_gc_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_func.h"
#include "lj_udata.h"
#include "lj_meta.h"
#include "lj_state.h"
#include "lj_frame.h"
#include "lj_trace.h"
#include "lj_vm.h"
#define GCSTEPSIZE 1024u
#define GCSWEEPMAX 40
#define GCSWEEPCOST 10
#define GCFINALIZECOST 100
/* Macros to set GCobj colors and flags. */
#define white2gray(x) ((x)->gch.marked &= cast_byte(~LJ_GC_WHITES))
#define black2gray(x) ((x)->gch.marked &= cast_byte(~LJ_GC_BLACK))
#define gray2black(x) ((x)->gch.marked |= LJ_GC_BLACK)
#define makewhite(g, x) \
((x)->gch.marked = ((x)->gch.marked & cast_byte(~LJ_GC_COLORS)) | curwhite(g))
#define isfinalized(u) ((u)->marked & LJ_GC_FINALIZED)
#define markfinalized(u) ((u)->marked |= LJ_GC_FINALIZED)
/* -- Mark phase ---------------------------------------------------------- */
/* Mark a TValue (if needed). */
#define gc_marktv(g, tv) \
{ lua_assert(!tvisgcv(tv) || (~itype(tv) == gcval(tv)->gch.gct)); \
if (tviswhite(tv)) gc_mark(g, gcV(tv)); }
/* Mark a GCobj (if needed). */
#define gc_markobj(g, o) \
{ if (iswhite(obj2gco(o))) gc_mark(g, obj2gco(o)); }
/* Mark a string object. */
#define gc_mark_str(s) ((s)->marked &= cast_byte(~LJ_GC_WHITES))
/* Mark a white GCobj. */
static void gc_mark(global_State *g, GCobj *o)
{
lua_assert(iswhite(o) && !isdead(g, o));
white2gray(o);
if (LJ_UNLIKELY(o->gch.gct == ~LJ_TUDATA)) {
GCtab *mt = tabref(gco2ud(o)->metatable);
gray2black(o); /* Userdata are never gray. */
if (mt) gc_markobj(g, mt);
gc_markobj(g, tabref(gco2ud(o)->env));
} else if (LJ_UNLIKELY(o->gch.gct == ~LJ_TUPVAL)) {
GCupval *uv = gco2uv(o);
gc_marktv(g, uv->v);
if (uv->closed)
gray2black(o); /* Closed upvalues are never gray. */
} else if (o->gch.gct != ~LJ_TSTR) {
lua_assert(o->gch.gct == ~LJ_TFUNC || o->gch.gct == ~LJ_TTAB ||
o->gch.gct == ~LJ_TTHREAD || o->gch.gct == ~LJ_TPROTO);
setgcrefr(o->gch.gclist, g->gc.gray);
setgcref(g->gc.gray, o);
}
}
/* Mark the base metatables. */
static void gc_mark_basemt(global_State *g)
{
int i;
for (i = 0; i < BASEMT_MAX; i++)
if (tabref(g->basemt[i]) != NULL)
gc_markobj(g, tabref(g->basemt[i]));
}
/* Start a GC cycle and mark the root set. */
static void gc_mark_start(global_State *g)
{
setgcrefnull(g->gc.gray);
setgcrefnull(g->gc.grayagain);
setgcrefnull(g->gc.weak);
gc_markobj(g, mainthread(g));
gc_markobj(g, tabref(mainthread(g)->env));
gc_marktv(g, &g->registrytv);
gc_mark_basemt(g);
g->gc.state = GCSpropagate;
}
/* Mark open upvalues. */
static void gc_mark_uv(global_State *g)
{
GCupval *uv;
for (uv = uvnext(&g->uvhead); uv != &g->uvhead; uv = uvnext(uv)) {
lua_assert(uvprev(uvnext(uv)) == uv && uvnext(uvprev(uv)) == uv);
if (isgray(obj2gco(uv)))
gc_marktv(g, uv->v);
}
}
/* Mark userdata in mmudata list. */
static void gc_mark_mmudata(global_State *g)
{
GCobj *root = gcref(g->gc.mmudata);
GCobj *u = root;
if (u) {
do {
u = gcnext(u);
makewhite(g, u); /* Could be from previous GC. */
gc_mark(g, u);
} while (u != root);
}
}
/* Separate userdata which which needs finalization to mmudata list. */
size_t lj_gc_separateudata(global_State *g, int all)
{
size_t m = 0;
GCRef *p = &mainthread(g)->nextgc;
GCobj *o;
while ((o = gcref(*p)) != NULL) {
if (!(iswhite(o) || all) || isfinalized(gco2ud(o))) {
p = &o->gch.nextgc; /* Nothing to do. */
} else if (!lj_meta_fastg(g, tabref(gco2ud(o)->metatable), MM_gc)) {
markfinalized(gco2ud(o)); /* Done, as there's no __gc metamethod. */
p = &o->gch.nextgc;
} else { /* Otherwise move userdata to be finalized to mmudata list. */
m += sizeudata(gco2ud(o));
markfinalized(gco2ud(o));
*p = o->gch.nextgc;
if (gcref(g->gc.mmudata)) { /* Link to end of mmudata list. */
GCobj *root = gcref(g->gc.mmudata);
setgcrefr(o->gch.nextgc, root->gch.nextgc);
setgcref(root->gch.nextgc, o);
setgcref(g->gc.mmudata, o);
} else { /* Create circular list. */
setgcref(o->gch.nextgc, o);
setgcref(g->gc.mmudata, o);
}
}
}
return m;
}
/* -- Propagation phase --------------------------------------------------- */
/* Traverse a table. */
static int gc_traverse_tab(global_State *g, GCtab *t)
{
int weak = 0;
cTValue *mode;
GCtab *mt = tabref(t->metatable);
if (mt)
gc_markobj(g, mt);
mode = lj_meta_fastg(g, mt, MM_mode);
if (mode && tvisstr(mode)) { /* Valid __mode field? */
const char *modestr = strVdata(mode);
int c;
while ((c = *modestr++)) {
if (c == 'k') weak |= LJ_GC_WEAKKEY;
else if (c == 'v') weak |= LJ_GC_WEAKVAL;
}
if (weak) { /* Weak tables are cleared in the atomic phase. */
t->marked = cast_byte((t->marked & ~LJ_GC_WEAK) | weak);
setgcrefr(t->gclist, g->gc.weak);
setgcref(g->gc.weak, obj2gco(t));
}
}
if (weak == LJ_GC_WEAK) /* Nothing to mark if both keys/values are weak. */
return 1;
if (!(weak & LJ_GC_WEAKVAL)) { /* Mark array part. */
MSize i, asize = t->asize;
for (i = 0; i < asize; i++)
gc_marktv(g, arrayslot(t, i));
}
if (t->hmask > 0) { /* Mark hash part. */
Node *node = noderef(t->node);
MSize i, hmask = t->hmask;
for (i = 0; i <= hmask; i++) {
Node *n = &node[i];
lua_assert(itype(&n->key) != LJ_TDEADKEY || tvisnil(&n->val));
if (!tvisnil(&n->val)) { /* Mark non-empty slot. */
lua_assert(!tvisnil(&n->key));
if (!(weak & LJ_GC_WEAKKEY)) gc_marktv(g, &n->key);
if (!(weak & LJ_GC_WEAKVAL)) gc_marktv(g, &n->val);
} else if (tvisgcv(&n->key)) { /* Leave GC key in, but mark as dead. */
setitype(&n->key, LJ_TDEADKEY);
}
}
}
return weak;
}
/* Traverse a function. */
static void gc_traverse_func(global_State *g, GCfunc *fn)
{
gc_markobj(g, tabref(fn->c.env));
if (isluafunc(fn)) {
uint32_t i;
lua_assert(fn->l.nupvalues == funcproto(fn)->sizeuv);
gc_markobj(g, funcproto(fn));
for (i = 0; i < fn->l.nupvalues; i++) /* Mark Lua function upvalues. */
gc_markobj(g, &gcref(fn->l.uvptr[i])->uv);
} else {
uint32_t i;
for (i = 0; i < fn->c.nupvalues; i++) /* Mark C function upvalues. */
gc_marktv(g, &fn->c.upvalue[i]);
}
}
#if LJ_HASJIT
/* Traverse a trace. */
static void gc_traverse_trace(global_State *g, Trace *T)
{
IRRef ref;
for (ref = T->nk; ref < REF_TRUE; ref++) {
IRIns *ir = &T->ir[ref];
if (ir->o == IR_KGC)
gc_markobj(g, ir_kgc(ir));
}
}
/* The current trace is a GC root while not anchored in the prototype (yet). */
#define gc_mark_curtrace(g) \
{ if (G2J(g)->state != LJ_TRACE_IDLE && G2J(g)->curtrace != 0) \
gc_traverse_trace(g, &G2J(g)->cur); }
#else
#define gc_mark_curtrace(g) UNUSED(g)
#endif
/* Traverse a prototype. */
static void gc_traverse_proto(global_State *g, GCproto *pt)
{
ptrdiff_t i;
#if LJ_HASJIT
jit_State *J = G2J(g);
TraceNo root, side;
/* Mark all root traces and attached side traces. */
for (root = pt->trace; root != 0; root = J->trace[root]->nextroot) {
for (side = J->trace[root]->nextside; side != 0;
side = J->trace[side]->nextside)
gc_traverse_trace(g, J->trace[side]);
gc_traverse_trace(g, J->trace[root]);
}
#endif
/* GC during prototype creation could cause NULL fields. */
if (pt->chunkname)
gc_mark_str(pt->chunkname);
for (i = -(ptrdiff_t)pt->sizekgc; i < 0; i++) /* Mark collectable consts. */
gc_markobj(g, gcref(pt->k.gc[i]));
for (i = 0; i < (ptrdiff_t)pt->sizeuvname; i++) /* Mark upvalue names. */
if (pt->uvname[i])
gc_mark_str(pt->uvname[i]);
for (i = 0; i < (ptrdiff_t)pt->sizevarinfo; i++) /* Mark names of locals. */
if (pt->varinfo[i].name)
gc_mark_str(pt->varinfo[i].name);
}
/* Traverse the frame structure of a stack. */
static TValue *gc_traverse_frames(global_State *g, lua_State *th)
{
TValue *frame, *top = th->top-1;
/* Note: extra vararg frame not skipped, marks function twice (harmless). */
for (frame = th->base-1; frame > th->stack; frame = frame_prev(frame)) {
GCfunc *fn = frame_func(frame);
TValue *ftop = frame;
if (isluafunc(fn)) ftop += funcproto(fn)->framesize;
if (ftop > top) top = ftop;
gc_markobj(g, frame_gc(frame)); /* Need to mark hidden function (or L). */
}
top++; /* Correct bias of -1 (frame == base-1). */
if (top > th->maxstack) top = th->maxstack;
return top;
}
/* Traverse a thread object. */
static void gc_traverse_thread(global_State *g, lua_State *th)
{
TValue *o, *lim;
gc_markobj(g, tabref(th->env));
for (o = th->stack+1; o < th->top; o++)
gc_marktv(g, o);
lim = gc_traverse_frames(g, th);
/* Extra cleanup required to avoid this marking problem:
**
** [aa[bb.X| X created.
** [aa[cc| GC called from (small) inner frame, X destroyed.
** [aa....X.| GC called again in (larger) outer frame, X resurrected (ouch).
**
** During GC in step 2 the stack must be cleaned up to the max. frame extent:
**
** ***| Slots cleaned
** [cc| from top of last frame
** [aa......| to max. frame extent.
*/
for (; o <= lim; o++)
setnilV(o);
lj_state_shrinkstack(th, (MSize)(lim - th->stack));
}
/* Propagate one gray object. Traverse it and turn it black. */
static size_t propagatemark(global_State *g)
{
GCobj *o = gcref(g->gc.gray);
lua_assert(isgray(o));
gray2black(o);
setgcrefr(g->gc.gray, o->gch.gclist); /* Remove from gray list. */
if (LJ_LIKELY(o->gch.gct == ~LJ_TTAB)) {
GCtab *t = gco2tab(o);
if (gc_traverse_tab(g, t))
black2gray(o); /* Keep weak tables gray. */
return sizeof(GCtab) + sizeof(TValue) * t->asize +
sizeof(Node) * (t->hmask + 1);
} else if (LJ_LIKELY(o->gch.gct == ~LJ_TFUNC)) {
GCfunc *fn = gco2func(o);
gc_traverse_func(g, fn);
return isluafunc(fn) ? sizeLfunc((MSize)fn->l.nupvalues) :
sizeCfunc((MSize)fn->c.nupvalues);
} else if (LJ_LIKELY(o->gch.gct == ~LJ_TPROTO)) {
GCproto *pt = gco2pt(o);
gc_traverse_proto(g, pt);
return sizeof(GCproto) + sizeof(BCIns) * pt->sizebc +
sizeof(GCobj *) * pt->sizekgc +
sizeof(lua_Number) * pt->sizekn +
sizeof(int16_t) * pt->sizeuv +
sizeof(int32_t) * pt->sizelineinfo +
sizeof(VarInfo) * pt->sizevarinfo +
sizeof(GCstr *) * pt->sizeuvname;
} else {
lua_State *th = gco2th(o);
setgcrefr(th->gclist, g->gc.grayagain);
setgcref(g->gc.grayagain, o);
black2gray(o); /* Threads are never black. */
gc_traverse_thread(g, th);
return sizeof(lua_State) + sizeof(TValue) * th->stacksize;
}
}
/* Propagate all gray objects. */
static size_t gc_propagate_gray(global_State *g)
{
size_t m = 0;
while (gcref(g->gc.gray) != NULL)
m += propagatemark(g);
return m;
}
/* -- Sweep phase --------------------------------------------------------- */
/* Try to shrink some common data structures. */
static void gc_shrink(global_State *g, lua_State *L)
{
if (g->strnum <= (g->strmask >> 2) && g->strmask > LJ_MIN_STRTAB*2-1)
lj_str_resize(L, g->strmask >> 1); /* Shrink string table. */
if (g->tmpbuf.sz > LJ_MIN_SBUF*2)
lj_str_resizebuf(L, &g->tmpbuf, g->tmpbuf.sz >> 1); /* Shrink temp buf. */
}
/* Type of GC free functions. */
typedef void (LJ_FASTCALL *GCFreeFunc)(global_State *g, GCobj *o);
/* GC free functions for LJ_TSTR .. LJ_TUDATA. ORDER LJ_T */
static const GCFreeFunc gc_freefunc[] = {
(GCFreeFunc)lj_str_free,
(GCFreeFunc)lj_func_freeuv,
(GCFreeFunc)lj_state_free,
(GCFreeFunc)lj_func_freeproto,
(GCFreeFunc)lj_func_free,
(GCFreeFunc)0,
(GCFreeFunc)lj_tab_free,
(GCFreeFunc)lj_udata_free
};
/* Full sweep of a GC list. */
#define gc_fullsweep(g, p) gc_sweep(g, (p), LJ_MAX_MEM)
/* Partial sweep of a GC list. */
static GCRef *gc_sweep(global_State *g, GCRef *p, uint32_t lim)
{
/* Mask with other white and LJ_GC_FIXED. Or LJ_GC_SFIXED on shutdown. */
int ow = otherwhite(g);
GCobj *o;
while ((o = gcref(*p)) != NULL && lim-- > 0) {
if (o->gch.gct == ~LJ_TTHREAD) /* Need to sweep open upvalues, too. */
gc_fullsweep(g, &gco2th(o)->openupval);
if (((o->gch.marked ^ LJ_GC_WHITES) & ow)) { /* Black or current white? */
lua_assert(!isdead(g, o) || (o->gch.marked & LJ_GC_FIXED));
makewhite(g, o); /* Value is alive, change to the current white. */
p = &o->gch.nextgc;
} else { /* Otherwise value is dead, free it. */
lua_assert(isdead(g, o) || ow == LJ_GC_SFIXED);
setgcrefr(*p, o->gch.nextgc);
if (o == gcref(g->gc.root))
setgcrefr(g->gc.root, o->gch.nextgc); /* Adjust list anchor. */
gc_freefunc[o->gch.gct - ~LJ_TSTR](g, o);
}
}
return p;
}
/* Check whether we can clear a key or a value slot from a table. */
static int gc_mayclear(cTValue *o, int val)
{
if (tvisgcv(o)) { /* Only collectable objects can be weak references. */
if (tvisstr(o)) { /* But strings cannot be used as weak references. */
gc_mark_str(strV(o)); /* And need to be marked. */
return 0;
}
if (iswhite(gcV(o)))
return 1; /* Object is about to be collected. */
if (tvisudata(o) && val && isfinalized(udataV(o)))
return 1; /* Finalized userdata is dropped only from values. */
}
return 0; /* Cannot clear. */
}
/* Clear collected entries from weak tables. */
static void gc_clearweak(GCobj *o)
{
while (o) {
GCtab *t = gco2tab(o);
lua_assert((t->marked & LJ_GC_WEAK));
if ((t->marked & LJ_GC_WEAKVAL)) {
MSize i, asize = t->asize;
for (i = 0; i < asize; i++) {
/* Clear array slot when value is about to be collected. */
TValue *tv = arrayslot(t, i);
if (gc_mayclear(tv, 1))
setnilV(tv);
}
}
if (t->hmask > 0) {
Node *node = noderef(t->node);
MSize i, hmask = t->hmask;
for (i = 0; i <= hmask; i++) {
Node *n = &node[i];
/* Clear hash slot when key or value is about to be collected. */
if (!tvisnil(&n->val) && (gc_mayclear(&n->key, 0) ||
gc_mayclear(&n->val, 1))) {
setnilV(&n->val);
if (tvisgcv(&n->key)) /* Leave GC key in, but mark as dead. */
setitype(&n->key, LJ_TDEADKEY);
}
}
}
o = gcref(t->gclist);
}
}
/* Finalize one userdata object from mmudata list. */
static void gc_finalize(lua_State *L)
{
global_State *g = G(L);
GCobj *o = gcnext(gcref(g->gc.mmudata));
GCudata *ud = gco2ud(o);
cTValue *mo;
/* Unchain from list of userdata to be finalized. */
if (o == gcref(g->gc.mmudata))
setgcrefnull(g->gc.mmudata);
else
setgcrefr(gcref(g->gc.mmudata)->gch.nextgc, ud->nextgc);
/* Add it back to the main userdata list and make it white. */
setgcrefr(ud->nextgc, mainthread(g)->nextgc);
setgcref(mainthread(g)->nextgc, o);
makewhite(g, o);
/* Resolve the __gc metamethod. */
mo = lj_meta_fastg(g, tabref(ud->metatable), MM_gc);
if (mo) {
/* Save and restore lots of state around the __gc callback. */
uint8_t oldh = hook_save(g);
MSize oldt = g->gc.threshold;
GCobj *oldjl = gcref(g->jit_L);
MSize oldjs = 0;
ptrdiff_t oldjb = 0;
int errcode;
TValue *top;
if (oldjl) {
oldjs = gco2th(oldjl)->stacksize;
oldjb = savestack(gco2th(oldjl), mref(g->jit_base, TValue ));
setgcrefnull(g->jit_L);
}
lj_trace_abort(g);
top = L->top;
L->top = top+2;
hook_entergc(g); /* Disable hooks and new traces during __gc. */
g->gc.threshold = LJ_MAX_MEM; /* Prevent GC steps. */
copyTV(L, top, mo);
setudataV(L, top+1, ud);
errcode = lj_vm_pcall(L, top+1, 1+0, -1); /* Stack: |mo|ud| -> | */
hook_restore(g, oldh);
g->gc.threshold = oldt; /* Restore GC threshold. */
if (oldjl) {
if (gco2th(oldjl)->stacksize < oldjs)
lj_state_growstack(gco2th(oldjl), oldjs - gco2th(oldjl)->stacksize);
setgcref(g->jit_L, oldjl);
setmref(g->jit_base, restorestack(gco2th(oldjl), oldjb));
}
if (errcode)
lj_err_throw(L, errcode); /* Propagate errors. */
}
}
/* Finalize all userdata objects from mmudata list. */
void lj_gc_finalizeudata(lua_State *L)
{
while (gcref(G(L)->gc.mmudata) != NULL)
gc_finalize(L);
}
/* Free all remaining GC objects. */
void lj_gc_freeall(global_State *g)
{
MSize i, strmask;
/* Free everything, except super-fixed objects (the main thread). */
g->gc.currentwhite = LJ_GC_WHITES | LJ_GC_SFIXED;
gc_fullsweep(g, &g->gc.root);
strmask = g->strmask;
for (i = 0; i <= strmask; i++) /* Free all string hash chains. */
gc_fullsweep(g, &g->strhash[i]);
}
/* -- Collector ----------------------------------------------------------- */
/* Atomic part of the GC cycle, transitioning from mark to sweep phase. */
static void atomic(global_State *g, lua_State *L)
{
size_t udsize;
gc_mark_uv(g); /* Need to remark open upvalues (the thread may be dead). */
gc_propagate_gray(g); /* Propagate any left-overs. */
setgcrefr(g->gc.gray, g->gc.weak); /* Empty the list of weak tables. */
setgcrefnull(g->gc.weak);
lua_assert(!iswhite(obj2gco(mainthread(g))));
gc_markobj(g, L); /* Mark running thread. */
gc_mark_curtrace(g); /* Mark current trace. */
gc_mark_basemt(g); /* Mark base metatables (again). */
gc_propagate_gray(g); /* Propagate all of the above. */
setgcrefr(g->gc.gray, g->gc.grayagain); /* Empty the 2nd chance list. */
setgcrefnull(g->gc.grayagain);
gc_propagate_gray(g); /* Propagate it. */
udsize = lj_gc_separateudata(g, 0); /* Separate userdata to be finalized. */
gc_mark_mmudata(g); /* Mark them. */
udsize += gc_propagate_gray(g); /* And propagate the marks. */
/* All marking done, clear weak tables. */
gc_clearweak(gcref(g->gc.weak));
/* Prepare for sweep phase. */
g->gc.currentwhite = cast_byte(otherwhite(g)); /* Flip current white. */
g->gc.sweepstr = 0;
g->gc.sweep = &g->gc.root;
g->gc.state = GCSsweepstring;
g->gc.estimate = g->gc.total - (MSize)udsize; /* Initial estimate. */
}
/* GC state machine. Returns a cost estimate for each step performed. */
static size_t gc_onestep(lua_State *L)
{
global_State *g = G(L);
switch (g->gc.state) {
case GCSpause:
gc_mark_start(g); /* Start a new GC cycle by marking all GC roots. */
return 0;
case GCSpropagate:
if (gcref(g->gc.gray) != NULL)
return propagatemark(g); /* Propagate one gray object. */
atomic(g, L); /* End of mark phase. */
return 0;
case GCSsweepstring: {
MSize old = g->gc.total;
gc_fullsweep(g, &g->strhash[g->gc.sweepstr++]); /* Sweep one chain. */
if (g->gc.sweepstr > g->strmask)
g->gc.state = GCSsweep; /* All string hash chains sweeped. */
lua_assert(old >= g->gc.total);
g->gc.estimate -= old - g->gc.total;
return GCSWEEPCOST;
}
case GCSsweep: {
MSize old = g->gc.total;
g->gc.sweep = gc_sweep(g, g->gc.sweep, GCSWEEPMAX); /* Partial sweep. */
if (gcref(*g->gc.sweep) == NULL) {
gc_shrink(g, L);
g->gc.state = GCSfinalize; /* End of sweep phase. */
}
lua_assert(old >= g->gc.total);
g->gc.estimate -= old - g->gc.total;
return GCSWEEPMAX*GCSWEEPCOST;
}
case GCSfinalize:
if (gcref(g->gc.mmudata) != NULL) {
gc_finalize(L); /* Finalize one userdata object. */
if (g->gc.estimate > GCFINALIZECOST)
g->gc.estimate -= GCFINALIZECOST;
return GCFINALIZECOST;
}
g->gc.state = GCSpause; /* End of GC cycle. */
g->gc.debt = 0;
return 0;
default:
lua_assert(0);
return 0;
}
}
/* Perform a limited amount of incremental GC steps. */
int lj_gc_step(lua_State *L)
{
global_State *g = G(L);
MSize lim;
int32_t ostate = g->vmstate;
setvmstate(g, GC);
lim = (GCSTEPSIZE/100) * g->gc.stepmul;
if (lim == 0)
lim = LJ_MAX_MEM;
g->gc.debt += g->gc.total - g->gc.threshold;
do {
lim -= (MSize)gc_onestep(L);
if (g->gc.state == GCSpause) {
lua_assert(g->gc.total >= g->gc.estimate);
g->gc.threshold = (g->gc.estimate/100) * g->gc.pause;
g->vmstate = ostate;
return 1; /* Finished a GC cycle. */
}
} while ((int32_t)lim > 0);
if (g->gc.debt < GCSTEPSIZE) {
g->gc.threshold = g->gc.total + GCSTEPSIZE;
} else {
g->gc.debt -= GCSTEPSIZE;
g->gc.threshold = g->gc.total;
}
g->vmstate = ostate;
return 0;
}
/* Ditto, but fix the stack top first. */
void lj_gc_step_fixtop(lua_State *L)
{
if (curr_funcisL(L)) L->top = curr_topL(L);
lj_gc_step(L);
}
/* Perform multiple GC steps. Called from JIT-compiled code. */
void lj_gc_step_jit(lua_State *L, const BCIns *pc, MSize steps)
{
cframe_pc(cframe_raw(L->cframe)) = pc;
L->top = curr_topL(L);
while (steps-- > 0 && lj_gc_step(L) == 0)
;
}
/* Perform a full GC cycle. */
void lj_gc_fullgc(lua_State *L)
{
global_State *g = G(L);
int32_t ostate = g->vmstate;
setvmstate(g, GC);
if (g->gc.state <= GCSpropagate) { /* Caught somewhere in the middle. */
g->gc.sweepstr = 0;
g->gc.sweep = &g->gc.root; /* Sweep everything (preserving it). */
setgcrefnull(g->gc.gray); /* Reset lists from partial propagation. */
setgcrefnull(g->gc.grayagain);
setgcrefnull(g->gc.weak);
g->gc.state = GCSsweepstring; /* Fast forward to the sweep phase. */
}
lua_assert(g->gc.state != GCSpause && g->gc.state != GCSpropagate);
while (g->gc.state != GCSfinalize) { /* Finish sweep. */
lua_assert(g->gc.state == GCSsweepstring || g->gc.state == GCSsweep);
gc_onestep(L);
}
/* Now perform a full GC. */
gc_mark_start(g);
while (g->gc.state != GCSpause)
gc_onestep(L);
g->gc.threshold = (g->gc.estimate/100) * g->gc.pause;
g->vmstate = ostate;
}
/* -- Write barriers ------------------------------------------------------ */
/* Move the GC propagation frontier back for tables (make it gray again). */
void lj_gc_barrierback(global_State *g, GCtab *t)
{
GCobj *o = obj2gco(t);
lua_assert(isblack(o) && !isdead(g, o));
lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause);
black2gray(o);
setgcrefr(t->gclist, g->gc.grayagain);
setgcref(g->gc.grayagain, o);
}
/* Move the GC propagation frontier forward. */
void lj_gc_barrierf(global_State *g, GCobj *o, GCobj *v)
{
lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause);
lua_assert(o->gch.gct != ~LJ_TTAB);
/* Preserve invariant during propagation. Otherwise it doesn't matter. */
if (g->gc.state == GCSpropagate)
gc_mark(g, v); /* Move frontier forward. */
else
makewhite(g, o); /* Make it white to avoid the following barrier. */
}
/* The reason for duplicating this is that it needs to be visible from ASM. */
void lj_gc_barrieruv(global_State *g, GCobj *o, GCobj *v)
{
lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause);
lua_assert(o->gch.gct == ~LJ_TUPVAL);
/* Preserve invariant during propagation. Otherwise it doesn't matter. */
if (g->gc.state == GCSpropagate)
gc_mark(g, v); /* Move frontier forward. */
else
makewhite(g, o); /* Make it white to avoid the following barrier. */
}
/* Close upvalue. Also needs a write barrier. */
void lj_gc_closeuv(global_State *g, GCupval *uv)
{
GCobj *o = obj2gco(uv);
/* Copy stack slot to upvalue itself and point to the copy. */
copyTV(mainthread(g), &uv->tv, uv->v);
uv->v = &uv->tv;
uv->closed = 1;
setgcrefr(o->gch.nextgc, g->gc.root);
setgcref(g->gc.root, o);
if (isgray(o)) { /* A closed upvalue is never gray, so fix this. */
if (g->gc.state == GCSpropagate) {
gray2black(o); /* Make it black and preserve invariant. */
if (tviswhite(uv->v))
lj_gc_barrierf(g, o, gcV(uv->v));
} else {
makewhite(g, o); /* Make it white, i.e. sweep the upvalue. */
lua_assert(g->gc.state != GCSfinalize && g->gc.state != GCSpause);
}
}
}
#if LJ_HASJIT
/* Mark a trace if it's saved during the propagation phase. */
void lj_gc_barriertrace(global_State *g, void *T)
{
if (g->gc.state == GCSpropagate)
gc_traverse_trace(g, (Trace *)T);
}
#endif
/* -- Allocator ----------------------------------------------------------- */
/* Call pluggable memory allocator to allocate or resize a fragment. */
void *lj_mem_realloc(lua_State *L, void *p, MSize osz, MSize nsz)
{
global_State *g = G(L);
lua_assert((osz == 0) == (p == NULL));
p = g->allocf(g->allocd, p, osz, nsz);
if (p == NULL && nsz > 0)
lj_err_throw(L, LUA_ERRMEM);
lua_assert((nsz == 0) == (p == NULL));
g->gc.total = (g->gc.total - osz) + nsz;
return p;
}
/* Allocate new GC object and link it to the root set. */
void *lj_mem_newgco(lua_State *L, MSize size)
{
global_State *g = G(L);
GCobj *o = (GCobj *)g->allocf(g->allocd, NULL, 0, size);
if (o == NULL)
lj_err_throw(L, LUA_ERRMEM);
g->gc.total += size;
setgcrefr(o->gch.nextgc, g->gc.root);
setgcref(g->gc.root, o);
newwhite(g, o);
return o;
}
/* Resize growable vector. */
void *lj_mem_grow(lua_State *L, void *p, MSize *szp, MSize lim, MSize esz)
{
MSize sz = (*szp) << 1;
if (sz < LJ_MIN_VECSZ)
sz = LJ_MIN_VECSZ;
if (sz > lim)
sz = lim;
p = lj_mem_realloc(L, p, (*szp)*esz, sz*esz);
*szp = sz;
return p;
}

102
src/lj_gc.h Normal file
View File

@ -0,0 +1,102 @@
/*
** Garbage collector.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_GC_H
#define _LJ_GC_H
#include "lj_obj.h"
/* Garbage collector states. Order matters. */
enum { GCSpause, GCSpropagate, GCSsweepstring, GCSsweep, GCSfinalize };
/* Bitmasks for marked field of GCobj. */
#define LJ_GC_WHITE0 0x01
#define LJ_GC_WHITE1 0x02
#define LJ_GC_BLACK 0x04
#define LJ_GC_FINALIZED 0x08
#define LJ_GC_WEAKKEY 0x08
#define LJ_GC_WEAKVAL 0x10
#define LJ_GC_FIXED 0x20
#define LJ_GC_SFIXED 0x40
#define LJ_GC_WHITES (LJ_GC_WHITE0 | LJ_GC_WHITE1)
#define LJ_GC_COLORS (LJ_GC_WHITES | LJ_GC_BLACK)
#define LJ_GC_WEAK (LJ_GC_WEAKKEY | LJ_GC_WEAKVAL)
/* Macros to test and set GCobj colors. */
#define iswhite(x) ((x)->gch.marked & LJ_GC_WHITES)
#define isblack(x) ((x)->gch.marked & LJ_GC_BLACK)
#define isgray(x) (!((x)->gch.marked & (LJ_GC_BLACK|LJ_GC_WHITES)))
#define tviswhite(x) (tvisgcv(x) && iswhite(gcV(x)))
#define otherwhite(g) (g->gc.currentwhite ^ LJ_GC_WHITES)
#define isdead(g, v) ((v)->gch.marked & otherwhite(g) & LJ_GC_WHITES)
#define curwhite(g) ((g)->gc.currentwhite & LJ_GC_WHITES)
#define newwhite(g, x) (obj2gco(x)->gch.marked = (uint8_t)curwhite(g))
#define flipwhite(x) ((x)->gch.marked ^= LJ_GC_WHITES)
#define fixstring(s) ((s)->marked |= LJ_GC_FIXED)
/* Collector. */
LJ_FUNC size_t lj_gc_separateudata(global_State *g, int all);
LJ_FUNC void lj_gc_finalizeudata(lua_State *L);
LJ_FUNC void lj_gc_freeall(global_State *g);
LJ_FUNCA int lj_gc_step(lua_State *L);
LJ_FUNCA void lj_gc_step_fixtop(lua_State *L);
LJ_FUNCA void lj_gc_step_jit(lua_State *L, const BCIns *pc, MSize steps);
LJ_FUNC void lj_gc_fullgc(lua_State *L);
/* GC check: drive collector forward if the GC threshold has been reached. */
#define lj_gc_check(L) \
{ if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) \
lj_gc_step(L); }
#define lj_gc_check_fixtop(L) \
{ if (LJ_UNLIKELY(G(L)->gc.total >= G(L)->gc.threshold)) \
lj_gc_step_fixtop(L); }
/* Write barriers. */
LJ_FUNC void lj_gc_barrierback(global_State *g, GCtab *t);
LJ_FUNC void lj_gc_barrierf(global_State *g, GCobj *o, GCobj *v);
LJ_FUNCA void lj_gc_barrieruv(global_State *g, GCobj *o, GCobj *v);
LJ_FUNC void lj_gc_closeuv(global_State *g, GCupval *uv);
LJ_FUNC void lj_gc_barriertrace(global_State *g, void *T);
/* Barrier for stores to table objects. TValue and GCobj variant. */
#define lj_gc_barriert(L, t, tv) \
{ if (tviswhite(tv) && isblack(obj2gco(t))) \
lj_gc_barrierback(G(L), (t)); }
#define lj_gc_objbarriert(L, t, o) \
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) \
lj_gc_barrierback(G(L), (t)); }
/* Barrier for stores to any other object. TValue and GCobj variant. */
#define lj_gc_barrier(L, p, tv) \
{ if (tviswhite(tv) && isblack(obj2gco(p))) \
lj_gc_barrierf(G(L), obj2gco(p), gcV(tv)); }
#define lj_gc_objbarrier(L, p, o) \
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
lj_gc_barrierf(G(L), obj2gco(p), obj2gco(o)); }
/* Allocator. */
LJ_FUNC void *lj_mem_realloc(lua_State *L, void *p, MSize osz, MSize nsz);
LJ_FUNC void *lj_mem_newgco(lua_State *L, MSize size);
LJ_FUNC void *lj_mem_grow(lua_State *L, void *p,
MSize *szp, MSize lim, MSize esz);
#define lj_mem_new(L, s) lj_mem_realloc(L, NULL, 0, (s))
#define lj_mem_free(g, p, osize) \
(g->gc.total -= (MSize)(osize), g->allocf(g->allocd, (p), (osize), 0))
#define lj_mem_newvec(L, n, t) ((t *)lj_mem_new(L, (MSize)((n)*sizeof(t))))
#define lj_mem_reallocvec(L, p, on, n, t) \
((p) = (t *)lj_mem_realloc(L, p, (on)*sizeof(t), (MSize)((n)*sizeof(t))))
#define lj_mem_growvec(L, p, n, m, t) \
((p) = (t *)lj_mem_grow(L, (p), &(n), (m), (MSize)sizeof(t)))
#define lj_mem_freevec(g, p, n, t) lj_mem_free(g, (p), (n)*sizeof(t))
#define lj_mem_newobj(L, t) ((t *)lj_mem_newgco(L, sizeof(t)))
#define lj_mem_newt(L, s, t) ((t *)lj_mem_new(L, (s)))
#define lj_mem_freet(g, p) lj_mem_free(g, (p), sizeof(*(p)))
#endif

739
src/lj_gdbjit.c Normal file
View File

@ -0,0 +1,739 @@
/*
** Client for the GDB JIT API.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_gdbjit_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_frame.h"
#include "lj_jit.h"
#include "lj_dispatch.h"
/* This is not compiled in by default.
** Enable with -DLUAJIT_USE_GDBJIT in the Makefile and recompile everything.
*/
#ifdef LUAJIT_USE_GDBJIT
/* The GDB JIT API allows JIT compilers to pass debug information about
** JIT-compiled code back to GDB. You need at least GDB 7.0 or higher
** to see it in action.
**
** This is a passive API, so it works even when not running under GDB
** or when attaching to an already running process. Alas, this implies
** enabling it always has a non-negligible overhead -- do not use in
** release mode!
**
** The LuaJIT GDB JIT client is rather minimal at the moment. It gives
** each trace a symbol name and adds a source location and frame unwind
** information. Obviously LuaJIT itself and any embedding C application
** should be compiled with debug symbols, too (see the Makefile).
**
** Traces are named TRACE_1, TRACE_2, ... these correspond to the trace
** numbers from -jv or -jdump. Use "break TRACE_1" or "tbreak TRACE_1" etc.
** to set breakpoints on specific traces (even ahead of their creation).
**
** The source location for each trace allows listing the corresponding
** source lines with the GDB command "list" (but only if the Lua source
** has been loaded from a file). Currently this is always set to the
** location where the trace has been started.
**
** Frame unwind information can be inspected with the GDB command
** "info frame". This also allows proper backtraces across JIT-compiled
** code with the GDB command "bt".
**
** You probably want to add the following settings to a .gdbinit file
** (or add them to ~/.gdbinit):
** set disassembly-flavor intel
** set breakpoint pending on
**
** Here's a sample GDB session:
** ------------------------------------------------------------------------
$ cat >x.lua
for outer=1,100 do
for inner=1,100 do end
end
^D
$ luajit -jv x.lua
[TRACE 1 x.lua:2]
[TRACE 2 (1/3) x.lua:1 -> 1]
$ gdb --quiet --args luajit x.lua
(gdb) tbreak TRACE_1
Function "TRACE_1" not defined.
Temporary breakpoint 1 (TRACE_1) pending.
(gdb) run
Starting program: luajit x.lua
Temporary breakpoint 1, TRACE_1 () at x.lua:2
2 for inner=1,100 do end
(gdb) list
1 for outer=1,100 do
2 for inner=1,100 do end
3 end
(gdb) bt
#0 TRACE_1 () at x.lua:2
#1 0x08053690 in lua_pcall [...]
[...]
#7 0x0806ff90 in main [...]
(gdb) disass TRACE_1
Dump of assembler code for function TRACE_1:
0xf7fd9fba <TRACE_1+0>: mov DWORD PTR ds:0xf7e0e2a0,0x1
0xf7fd9fc4 <TRACE_1+10>: movsd xmm7,QWORD PTR [edx+0x20]
[...]
0xf7fd9ff8 <TRACE_1+62>: jmp 0xf7fd2014
End of assembler dump.
(gdb) tbreak TRACE_2
Function "TRACE_2" not defined.
Temporary breakpoint 2 (TRACE_2) pending.
(gdb) cont
Continuing.
Temporary breakpoint 2, TRACE_2 () at x.lua:1
1 for outer=1,100 do
(gdb) info frame
Stack level 0, frame at 0xffffd7c0:
eip = 0xf7fd9f60 in TRACE_2 (x.lua:1); saved eip 0x8053690
called by frame at 0xffffd7e0
source language unknown.
Arglist at 0xffffd78c, args:
Locals at 0xffffd78c, Previous frame's sp is 0xffffd7c0
Saved registers:
ebx at 0xffffd7ac, ebp at 0xffffd7b8, esi at 0xffffd7b0, edi at 0xffffd7b4,
eip at 0xffffd7bc
(gdb)
** ------------------------------------------------------------------------
*/
/* -- GDB JIT API --------------------------------------------------------- */
/* GDB JIT actions. */
enum {
GDBJIT_NOACTION = 0,
GDBJIT_REGISTER,
GDBJIT_UNREGISTER
};
/* GDB JIT entry. */
typedef struct GDBJITentry {
struct GDBJITentry *next_entry;
struct GDBJITentry *prev_entry;
const char *symfile_addr;
uint64_t symfile_size;
} GDBJITentry;
/* GDB JIT descriptor. */
typedef struct GDBJITdesc {
uint32_t version;
uint32_t action_flag;
GDBJITentry *relevant_entry;
GDBJITentry *first_entry;
} GDBJITdesc;
GDBJITdesc __jit_debug_descriptor = {
1, GDBJIT_NOACTION, NULL, NULL
};
/* GDB sets a breakpoint at this function. */
void LJ_NOINLINE __jit_debug_register_code()
{
__asm__ __volatile__("");
};
/* -- In-memory ELF object definitions ------------------------------------ */
/* ELF definitions. */
typedef struct ELFheader {
uint8_t emagic[4];
uint8_t eclass;
uint8_t eendian;
uint8_t eversion;
uint8_t eosabi;
uint8_t eabiversion;
uint8_t epad[7];
uint16_t type;
uint16_t machine;
uint32_t version;
uintptr_t entry;
uintptr_t phofs;
uintptr_t shofs;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstridx;
} ELFheader;
typedef struct ELFsectheader {
uint32_t name;
uint32_t type;
uintptr_t flags;
uintptr_t addr;
uintptr_t ofs;
uintptr_t size;
uint32_t link;
uint32_t info;
uintptr_t align;
uintptr_t entsize;
} ELFsectheader;
#define ELFSECT_IDX_ABS 0xfff1
enum {
ELFSECT_TYPE_PROGBITS = 1,
ELFSECT_TYPE_SYMTAB = 2,
ELFSECT_TYPE_STRTAB = 3,
ELFSECT_TYPE_NOBITS = 8
};
#define ELFSECT_FLAGS_WRITE 1
#define ELFSECT_FLAGS_ALLOC 2
#define ELFSECT_FLAGS_EXEC 4
typedef struct ELFsymbol {
#if LJ_64
uint32_t name;
uint8_t info;
uint8_t other;
uint16_t sectidx;
uintptr_t value;
uint64_t size;
#else
uint32_t name;
uintptr_t value;
uint32_t size;
uint8_t info;
uint8_t other;
uint16_t sectidx;
#endif
} ELFsymbol;
enum {
ELFSYM_TYPE_FUNC = 2,
ELFSYM_TYPE_FILE = 4,
ELFSYM_BIND_LOCAL = 0 << 4,
ELFSYM_BIND_GLOBAL = 1 << 4,
};
/* DWARF definitions. */
#define DW_CIE_VERSION 1
enum {
DW_CFA_nop = 0x0,
DW_CFA_def_cfa = 0xc,
DW_CFA_def_cfa_offset = 0xe,
DW_CFA_advance_loc = 0x40,
DW_CFA_offset = 0x80
};
enum {
DW_EH_PE_udata4 = 3,
DW_EH_PE_textrel = 0x20
};
enum {
DW_TAG_compile_unit = 0x11
};
enum {
DW_children_no = 0,
DW_children_yes = 1
};
enum {
DW_AT_name = 0x03,
DW_AT_stmt_list = 0x10,
DW_AT_low_pc = 0x11,
DW_AT_high_pc = 0x12
};
enum {
DW_FORM_addr = 0x01,
DW_FORM_data4 = 0x06,
DW_FORM_string = 0x08
};
enum {
DW_LNS_extended_op = 0,
DW_LNS_copy = 1,
DW_LNS_advance_pc = 2,
DW_LNS_advance_line = 3
};
enum {
DW_LNE_end_sequence = 1,
DW_LNE_set_address = 2
};
enum {
#if LJ_TARGET_X86
DW_REG_AX, DW_REG_CX, DW_REG_DX, DW_REG_BX,
DW_REG_SP, DW_REG_BP, DW_REG_SI, DW_REG_DI,
DW_REG_RA,
#elif LJ_TARGET_X64
/* Yes, the order is strange, but correct. */
DW_REG_AX, DW_REG_DX, DW_REG_CX, DW_REG_BX,
DW_REG_SI, DW_REG_DI, DW_REG_BP, DW_REG_SP,
DW_REG_8, DW_REG_9, DW_REG_10, DW_REG_11,
DW_REG_12, DW_REG_13, DW_REG_14, DW_REG_15,
DW_REG_RA,
#else
#error "Unsupported target architecture"
#endif
};
/* Minimal list of sections for the in-memory ELF object. */
enum {
GDBJIT_SECT_NULL,
GDBJIT_SECT_text,
GDBJIT_SECT_eh_frame,
GDBJIT_SECT_shstrtab,
GDBJIT_SECT_strtab,
GDBJIT_SECT_symtab,
GDBJIT_SECT_debug_info,
GDBJIT_SECT_debug_abbrev,
GDBJIT_SECT_debug_line,
GDBJIT_SECT__MAX
};
enum {
GDBJIT_SYM_UNDEF,
GDBJIT_SYM_FILE,
GDBJIT_SYM_FUNC,
GDBJIT_SYM__MAX
};
/* In-memory ELF object. */
typedef struct GDBJITobj {
ELFheader hdr; /* ELF header. */
ELFsectheader sect[GDBJIT_SECT__MAX]; /* ELF sections. */
ELFsymbol sym[GDBJIT_SYM__MAX]; /* ELF symbol table. */
uint8_t space[4096]; /* Space for various section data. */
} GDBJITobj;
/* Combined structure for GDB JIT entry and ELF object. */
typedef struct GDBJITentryobj {
GDBJITentry entry;
size_t sz;
GDBJITobj obj;
} GDBJITentryobj;
/* Template for in-memory ELF header. */
static const ELFheader elfhdr_template = {
.emagic = { 0x7f, 'E', 'L', 'F' },
.eclass = LJ_64 ? 2 : 1,
.eendian = LJ_ENDIAN_SELECT(1, 2),
.eversion = 1,
#if defined(__linux__)
.eosabi = 0, /* Nope, it's not 3. */
#elif defined(__FreeBSD__)
.eosabi = 9,
#elif defined(__NetBSD__)
.eosabi = 2,
#elif defined(__OpenBSD__)
.eosabi = 12,
#elif defined(__solaris__)
.eosabi = 6,
#else
.eosabi = 0,
#endif
.eabiversion = 0,
.epad = { 0, 0, 0, 0, 0, 0, 0 },
.type = 1,
#if LJ_TARGET_X86
.machine = 3,
#elif LJ_TARGET_X64
.machine = 62,
#else
#error "Unsupported target architecture"
#endif
.version = 1,
.entry = 0,
.phofs = 0,
.shofs = offsetof(GDBJITobj, sect),
.flags = 0,
.ehsize = sizeof(ELFheader),
.phentsize = 0,
.phnum = 0,
.shentsize = sizeof(ELFsectheader),
.shnum = GDBJIT_SECT__MAX,
.shstridx = GDBJIT_SECT_shstrtab
};
/* -- In-memory ELF object generation ------------------------------------- */
/* Context for generating the ELF object for the GDB JIT API. */
typedef struct GDBJITctx {
uint8_t *p; /* Pointer to next address in obj.space. */
uint8_t *startp; /* Pointer to start address in obj.space. */
Trace *T; /* Generate symbols for this trace. */
uintptr_t mcaddr; /* Machine code address. */
MSize szmcode; /* Size of machine code. */
MSize spadjp; /* Stack adjustment for parent trace or interpreter. */
MSize spadj; /* Stack adjustment for trace itself. */
BCLine lineno; /* Starting line number. */
const char *filename; /* Starting file name. */
const char *trname; /* Name of trace. */
size_t objsize; /* Final size of ELF object. */
GDBJITobj obj; /* In-memory ELF object. */
} GDBJITctx;
/* Add a zero-terminated string. */
static uint32_t gdbjit_strz(GDBJITctx *ctx, const char *str)
{
uint8_t *p = ctx->p;
uint32_t ofs = (uint32_t)(p - ctx->startp);
do {
*p++ = (uint8_t)*str;
} while (*str++);
ctx->p = p;
return ofs;
}
/* Add a ULEB128 value. */
static void gdbjit_uleb128(GDBJITctx *ctx, uint32_t v)
{
uint8_t *p = ctx->p;
for (; v >= 0x80; v >>= 7)
*p++ = (uint8_t)((v & 0x7f) | 0x80);
*p++ = (uint8_t)v;
ctx->p = p;
}
/* Add a SLEB128 value. */
static void gdbjit_sleb128(GDBJITctx *ctx, int32_t v)
{
uint8_t *p = ctx->p;
for (; (uint32_t)(v+0x40) >= 0x80; v >>= 7)
*p++ = (uint8_t)((v & 0x7f) | 0x80);
*p++ = (uint8_t)(v & 0x7f);
ctx->p = p;
}
/* Shortcuts to generate DWARF structures. */
#define DB(x) (*p++ = (x))
#define DI8(x) (*(int8_t *)p = (x), p++)
#define DU16(x) (*(uint16_t *)p = (x), p += 2)
#define DU32(x) (*(uint32_t *)p = (x), p += 4)
#define DADDR(x) (*(uintptr_t *)p = (x), p += sizeof(uintptr_t))
#define DUV(x) (ctx->p = p, gdbjit_uleb128(ctx, (x)), p = ctx->p)
#define DSV(x) (ctx->p = p, gdbjit_sleb128(ctx, (x)), p = ctx->p)
#define DSTR(str) (ctx->p = p, gdbjit_strz(ctx, (str)), p = ctx->p)
#define DALIGNNOP(s) while ((uintptr_t)p & ((s)-1)) *p++ = DW_CFA_nop
#define DSECT(name, stmt) \
{ uint32_t *szp_##name = (uint32_t *)p; p += 4; stmt \
*szp_##name = (uint32_t)((p-(uint8_t *)szp_##name)-4); } \
/* Initialize ELF section headers. */
static void LJ_FASTCALL gdbjit_secthdr(GDBJITctx *ctx)
{
ELFsectheader *sect;
*ctx->p++ = '\0'; /* Empty string at start of string table. */
#define SECTDEF(id, tp, al) \
sect = &ctx->obj.sect[GDBJIT_SECT_##id]; \
sect->name = gdbjit_strz(ctx, "." #id); \
sect->type = ELFSECT_TYPE_##tp; \
sect->align = (al)
SECTDEF(text, NOBITS, 16);
sect->flags = ELFSECT_FLAGS_ALLOC|ELFSECT_FLAGS_EXEC;
sect->addr = ctx->mcaddr;
sect->ofs = 0;
sect->size = ctx->szmcode;
SECTDEF(eh_frame, PROGBITS, sizeof(uintptr_t));
sect->flags = ELFSECT_FLAGS_ALLOC;
SECTDEF(shstrtab, STRTAB, 1);
SECTDEF(strtab, STRTAB, 1);
SECTDEF(symtab, SYMTAB, sizeof(uintptr_t));
sect->ofs = offsetof(GDBJITobj, sym);
sect->size = sizeof(ctx->obj.sym);
sect->link = GDBJIT_SECT_strtab;
sect->entsize = sizeof(ELFsymbol);
sect->info = GDBJIT_SYM_FUNC;
SECTDEF(debug_info, PROGBITS, 1);
SECTDEF(debug_abbrev, PROGBITS, 1);
SECTDEF(debug_line, PROGBITS, 1);
#undef SECTDEF
}
/* Initialize symbol table. */
static void LJ_FASTCALL gdbjit_symtab(GDBJITctx *ctx)
{
ELFsymbol *sym;
*ctx->p++ = '\0'; /* Empty string at start of string table. */
sym = &ctx->obj.sym[GDBJIT_SYM_FILE];
sym->name = gdbjit_strz(ctx, "JIT mcode");
sym->sectidx = ELFSECT_IDX_ABS;
sym->info = ELFSYM_TYPE_FILE|ELFSYM_BIND_LOCAL;
sym = &ctx->obj.sym[GDBJIT_SYM_FUNC];
sym->name = gdbjit_strz(ctx, ctx->trname);
sym->sectidx = GDBJIT_SECT_text;
sym->value = 0;
sym->size = ctx->szmcode;
sym->info = ELFSYM_TYPE_FUNC|ELFSYM_BIND_GLOBAL;
}
/* Initialize .eh_frame section. */
static void LJ_FASTCALL gdbjit_ehframe(GDBJITctx *ctx)
{
uint8_t *p = ctx->p;
uint8_t *framep = p;
/* Emit DWARF EH CIE. */
DSECT(CIE,
DU32(0); /* Offset to CIE itself. */
DB(DW_CIE_VERSION);
DSTR("zR"); /* Augmentation. */
DUV(1); /* Code alignment factor. */
DSV(-(int32_t)sizeof(uintptr_t)); /* Data alignment factor. */
DB(DW_REG_RA); /* Return address register. */
DB(1); DB(DW_EH_PE_textrel|DW_EH_PE_udata4); /* Augmentation data. */
DB(DW_CFA_def_cfa); DUV(DW_REG_SP); DUV(sizeof(uintptr_t));
DB(DW_CFA_offset|DW_REG_RA); DUV(1);
DALIGNNOP(sizeof(uintptr_t));
)
/* Emit DWARF EH FDE. */
DSECT(FDE,
DU32((uint32_t)(p-framep)); /* Offset to CIE. */
DU32(0); /* Machine code offset relative to .text. */
DU32(ctx->szmcode); /* Machine code length. */
DB(0); /* Augmentation data. */
/* Registers saved in CFRAME. */
#if LJ_TARGET_X86
DB(DW_CFA_offset|DW_REG_BP); DUV(2);
DB(DW_CFA_offset|DW_REG_DI); DUV(3);
DB(DW_CFA_offset|DW_REG_SI); DUV(4);
DB(DW_CFA_offset|DW_REG_BX); DUV(5);
#elif LJ_TARGET_X64
/* Add saved registers for x64 CFRAME. */
#else
#error "Unsupported target architecture"
#endif
if (ctx->spadjp != ctx->spadj) { /* Parent/interpreter stack frame size. */
DB(DW_CFA_def_cfa_offset); DUV(ctx->spadjp);
DB(DW_CFA_advance_loc|1); /* Only an approximation. */
}
DB(DW_CFA_def_cfa_offset); DUV(ctx->spadj); /* Trace stack frame size. */
DALIGNNOP(sizeof(uintptr_t));
)
ctx->p = p;
}
/* Initialize .debug_info section. */
static void LJ_FASTCALL gdbjit_debuginfo(GDBJITctx *ctx)
{
uint8_t *p = ctx->p;
DSECT(info,
DU16(2); /* DWARF version. */
DU32(0); /* Abbrev offset. */
DB(sizeof(uintptr_t)); /* Pointer size. */
DUV(1); /* Abbrev #1: DW_TAG_compile_unit. */
DSTR(ctx->filename); /* DW_AT_name. */
DADDR(ctx->mcaddr); /* DW_AT_low_pc. */
DADDR(ctx->mcaddr + ctx->szmcode); /* DW_AT_high_pc. */
DU32(0); /* DW_AT_stmt_list. */
)
ctx->p = p;
}
/* Initialize .debug_abbrev section. */
static void LJ_FASTCALL gdbjit_debugabbrev(GDBJITctx *ctx)
{
uint8_t *p = ctx->p;
/* Abbrev #1: DW_TAG_compile_unit. */
DUV(1); DUV(DW_TAG_compile_unit);
DB(DW_children_no);
DUV(DW_AT_name); DUV(DW_FORM_string);
DUV(DW_AT_low_pc); DUV(DW_FORM_addr);
DUV(DW_AT_high_pc); DUV(DW_FORM_addr);
DUV(DW_AT_stmt_list); DUV(DW_FORM_data4);
DB(0); DB(0);
ctx->p = p;
}
#define DLNE(op, s) (DB(DW_LNS_extended_op), DUV(1+(s)), DB((op)))
/* Initialize .debug_line section. */
static void LJ_FASTCALL gdbjit_debugline(GDBJITctx *ctx)
{
uint8_t *p = ctx->p;
DSECT(line,
DU16(2); /* DWARF version. */
DSECT(header,
DB(1); /* Minimum instruction length. */
DB(1); /* is_stmt. */
DI8(0); /* Line base for special opcodes. */
DB(2); /* Line range for special opcodes. */
DB(3+1); /* Opcode base at DW_LNS_advance_line+1. */
DB(0); DB(1); DB(1); /* Standard opcode lengths. */
/* Directory table. */
DB(0);
/* File name table. */
DSTR(ctx->filename); DUV(0); DUV(0); DUV(0);
DB(0);
)
DLNE(DW_LNE_set_address, sizeof(uintptr_t)); DADDR(ctx->mcaddr);
if (ctx->lineno) {
DB(DW_LNS_advance_line); DSV(ctx->lineno-1);
}
DB(DW_LNS_copy);
DB(DW_LNS_advance_pc); DUV(ctx->szmcode);
DLNE(DW_LNE_end_sequence, 0);
)
ctx->p = p;
}
#undef DLNE
/* Undef shortcuts. */
#undef DB
#undef DI8
#undef DU16
#undef DU32
#undef DADDR
#undef DUV
#undef DSV
#undef DSTR
#undef DALIGNNOP
#undef DSECT
/* Type of a section initializer callback. */
typedef void (LJ_FASTCALL *GDBJITinitf)(GDBJITctx *ctx);
/* Call section initializer and set the section offset and size. */
static void gdbjit_initsect(GDBJITctx *ctx, int sect, GDBJITinitf initf)
{
ctx->startp = ctx->p;
ctx->obj.sect[sect].ofs = (uintptr_t)((char *)ctx->p - (char *)&ctx->obj);
initf(ctx);
ctx->obj.sect[sect].size = (uintptr_t)(ctx->p - ctx->startp);
}
#define SECTALIGN(p, a) \
((p) = (uint8_t *)(((uintptr_t)(p) + ((a)-1)) & ~(uintptr_t)((a)-1)))
/* Build in-memory ELF object. */
static void gdbjit_buildobj(GDBJITctx *ctx)
{
GDBJITobj *obj = &ctx->obj;
/* Fill in ELF header and clear structures. */
memcpy(&obj->hdr, &elfhdr_template, sizeof(ELFheader));
memset(&obj->sect, 0, sizeof(ELFsectheader)*GDBJIT_SECT__MAX);
memset(&obj->sym, 0, sizeof(ELFsymbol)*GDBJIT_SYM__MAX);
/* Initialize sections. */
ctx->p = obj->space;
gdbjit_initsect(ctx, GDBJIT_SECT_shstrtab, gdbjit_secthdr);
gdbjit_initsect(ctx, GDBJIT_SECT_strtab, gdbjit_symtab);
gdbjit_initsect(ctx, GDBJIT_SECT_debug_info, gdbjit_debuginfo);
gdbjit_initsect(ctx, GDBJIT_SECT_debug_abbrev, gdbjit_debugabbrev);
gdbjit_initsect(ctx, GDBJIT_SECT_debug_line, gdbjit_debugline);
SECTALIGN(ctx->p, sizeof(uintptr_t));
gdbjit_initsect(ctx, GDBJIT_SECT_eh_frame, gdbjit_ehframe);
ctx->objsize = (size_t)((char *)ctx->p - (char *)obj);
lua_assert(ctx->objsize < sizeof(GDBJITobj));
}
#undef SECTALIGN
/* -- Interface to GDB JIT API -------------------------------------------- */
/* Add new entry to GDB JIT symbol chain. */
static void gdbjit_newentry(lua_State *L, GDBJITctx *ctx)
{
/* Allocate memory for GDB JIT entry and ELF object. */
MSize sz = (MSize)(sizeof(GDBJITentryobj) - sizeof(GDBJITobj) + ctx->objsize);
GDBJITentryobj *eo = lj_mem_newt(L, sz, GDBJITentryobj);
memcpy(&eo->obj, &ctx->obj, ctx->objsize); /* Copy ELF object. */
eo->sz = sz;
ctx->T->gdbjit_entry = (void *)eo;
/* Link new entry to chain and register it. */
eo->entry.prev_entry = NULL;
eo->entry.next_entry = __jit_debug_descriptor.first_entry;
if (eo->entry.next_entry)
eo->entry.next_entry->prev_entry = &eo->entry;
eo->entry.symfile_addr = (const char *)&eo->obj;
eo->entry.symfile_size = ctx->objsize;
__jit_debug_descriptor.first_entry = &eo->entry;
__jit_debug_descriptor.relevant_entry = &eo->entry;
__jit_debug_descriptor.action_flag = GDBJIT_REGISTER;
__jit_debug_register_code();
}
/* Add debug info for newly compiled trace and notify GDB. */
void lj_gdbjit_addtrace(jit_State *J, Trace *T, TraceNo traceno)
{
GDBJITctx ctx;
lua_State *L = J->L;
GCproto *pt = &gcref(T->startpt)->pt;
TraceNo parent = T->ir[REF_BASE].op1;
uintptr_t pcofs = (uintptr_t)(T->snap[0].mapofs+T->snap[0].nslots);
const BCIns *startpc = (const BCIns *)(uintptr_t)T->snapmap[pcofs];
ctx.T = T;
ctx.mcaddr = (uintptr_t)T->mcode;
ctx.szmcode = T->szmcode;
ctx.spadjp = CFRAME_SIZE + (MSize)(parent ? J->trace[parent]->spadjust : 0);
ctx.spadj = CFRAME_SIZE + T->spadjust;
ctx.lineno = pt->lineinfo ? pt->lineinfo[startpc - pt->bc] : 0;
ctx.filename = strdata(pt->chunkname);
if (*ctx.filename == '@' || *ctx.filename == '=')
ctx.filename++;
else
ctx.filename = "(string)";
ctx.trname = lj_str_pushf(L, "TRACE_%d", traceno);
L->top--;
gdbjit_buildobj(&ctx);
gdbjit_newentry(L, &ctx);
}
/* Delete debug info for trace and notify GDB. */
void lj_gdbjit_deltrace(jit_State *J, Trace *T)
{
GDBJITentryobj *eo = (GDBJITentryobj *)T->gdbjit_entry;
if (eo) {
if (eo->entry.prev_entry)
eo->entry.prev_entry->next_entry = eo->entry.next_entry;
else
__jit_debug_descriptor.first_entry = eo->entry.next_entry;
if (eo->entry.next_entry)
eo->entry.next_entry->prev_entry = eo->entry.prev_entry;
__jit_debug_descriptor.relevant_entry = &eo->entry;
__jit_debug_descriptor.action_flag = GDBJIT_UNREGISTER;
__jit_debug_register_code();
lj_mem_free(J2G(J), eo, eo->sz);
}
}
#endif
#endif

22
src/lj_gdbjit.h Normal file
View File

@ -0,0 +1,22 @@
/*
** Client for the GDB JIT API.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_GDBJIT_H
#define _LJ_GDBJIT_H
#include "lj_obj.h"
#include "lj_jit.h"
#if LJ_HASJIT && defined(LUAJIT_USE_GDBJIT)
LJ_FUNC void lj_gdbjit_addtrace(jit_State *J, Trace *T, TraceNo traceno);
LJ_FUNC void lj_gdbjit_deltrace(jit_State *J, Trace *T);
#else
#define lj_gdbjit_addtrace(J, T, tn) UNUSED(T)
#define lj_gdbjit_deltrace(J, T) UNUSED(T)
#endif
#endif

461
src/lj_ir.c Normal file
View File

@ -0,0 +1,461 @@
/*
** SSA IR (Intermediate Representation) emitter.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_ir_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_gc.h"
#include "lj_str.h"
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
#include "lj_trace.h"
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
#define fins (&J->fold.ins)
/* Pass IR on to next optimization in chain (FOLD). */
#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J))
/* -- IR tables ----------------------------------------------------------- */
/* IR instruction modes. */
LJ_DATADEF const uint8_t lj_ir_mode[IR__MAX+1] = {
IRDEF(IRMODE)
0
};
/* -- IR emitter ---------------------------------------------------------- */
/* Grow IR buffer at the top. */
void LJ_FASTCALL lj_ir_growtop(jit_State *J)
{
IRIns *baseir = J->irbuf + J->irbotlim;
MSize szins = J->irtoplim - J->irbotlim;
if (szins) {
baseir = (IRIns *)lj_mem_realloc(J->L, baseir, szins*sizeof(IRIns),
2*szins*sizeof(IRIns));
J->irtoplim = J->irbotlim + 2*szins;
} else {
baseir = (IRIns *)lj_mem_realloc(J->L, NULL, 0, LJ_MIN_IRSZ*sizeof(IRIns));
J->irbotlim = REF_BASE - LJ_MIN_IRSZ/4;
J->irtoplim = J->irbotlim + LJ_MIN_IRSZ;
}
J->cur.ir = J->irbuf = baseir - J->irbotlim;
}
/* Grow IR buffer at the bottom or shift it up. */
static void lj_ir_growbot(jit_State *J)
{
IRIns *baseir = J->irbuf + J->irbotlim;
MSize szins = J->irtoplim - J->irbotlim;
lua_assert(szins != 0);
lua_assert(J->cur.nk == J->irbotlim);
if (J->cur.nins + (szins >> 1) < J->irtoplim) {
/* More than half of the buffer is free on top: shift up by a quarter. */
MSize ofs = szins >> 2;
memmove(baseir + ofs, baseir, (J->cur.nins - J->irbotlim)*sizeof(IRIns));
J->irbotlim -= ofs;
J->irtoplim -= ofs;
J->cur.ir = J->irbuf = baseir - J->irbotlim;
} else {
/* Double the buffer size, but split the growth amongst top/bottom. */
IRIns *newbase = lj_mem_newt(J->L, 2*szins*sizeof(IRIns), IRIns);
MSize ofs = szins >= 256 ? 128 : (szins >> 1); /* Limit bottom growth. */
memcpy(newbase + ofs, baseir, (J->cur.nins - J->irbotlim)*sizeof(IRIns));
lj_mem_free(G(J->L), baseir, szins*sizeof(IRIns));
J->irbotlim -= ofs;
J->irtoplim = J->irbotlim + 2*szins;
J->cur.ir = J->irbuf = newbase - J->irbotlim;
}
}
/* Emit IR without any optimizations. */
TRef LJ_FASTCALL lj_ir_emit(jit_State *J)
{
IRRef ref = lj_ir_nextins(J);
IRIns *ir = IR(ref);
IROp op = fins->o;
ir->prev = J->chain[op];
J->chain[op] = (IRRef1)ref;
ir->o = op;
ir->op1 = fins->op1;
ir->op2 = fins->op2;
J->guardemit.irt |= fins->t.irt;
return TREF(ref, irt_t((ir->t = fins->t)));
}
/* -- Interning of constants ---------------------------------------------- */
/*
** IR instructions for constants are kept between J->cur.nk >= ref < REF_BIAS.
** They are chained like all other instructions, but grow downwards.
** The are interned (like strings in the VM) to facilitate reference
** comparisons. The same constant must get the same reference.
*/
/* Get ref of next IR constant and optionally grow IR.
** Note: this may invalidate all IRIns *!
*/
static LJ_AINLINE IRRef ir_nextk(jit_State *J)
{
IRRef ref = J->cur.nk;
if (LJ_UNLIKELY(ref <= J->irbotlim)) lj_ir_growbot(J);
J->cur.nk = --ref;
return ref;
}
/* Intern int32_t constant. */
TRef LJ_FASTCALL lj_ir_kint(jit_State *J, int32_t k)
{
IRIns *ir, *cir = J->cur.ir;
IRRef ref;
for (ref = J->chain[IR_KINT]; ref; ref = cir[ref].prev)
if (cir[ref].i == k)
goto found;
ref = ir_nextk(J);
ir = IR(ref);
ir->i = k;
ir->t.irt = IRT_INT;
ir->o = IR_KINT;
ir->prev = J->chain[IR_KINT];
J->chain[IR_KINT] = (IRRef1)ref;
found:
return TREF(ref, IRT_INT);
}
/* The MRef inside the KNUM IR instruction holds the address of the constant
** (an aligned double or a special 64 bit pattern). The KNUM constants
** themselves are stored in a chained array and shared across traces.
**
** Rationale for choosing this data structure:
** - The address of the constants is embedded in the generated machine code
** and must never move. A resizable array or hash table wouldn't work.
** - Most apps need very few non-integer constants (less than a dozen).
** - Linear search is hard to beat in terms of speed and low complexity.
*/
typedef struct KNumArray {
MRef next; /* Pointer to next list. */
MSize numk; /* Number of used elements in this array. */
TValue k[LJ_MIN_KNUMSZ]; /* Array of constants. */
} KNumArray;
/* Free all chained arrays. */
void lj_ir_knum_freeall(jit_State *J)
{
KNumArray *kn;
for (kn = mref(J->knum, KNumArray); kn; ) {
KNumArray *next = mref(kn->next, KNumArray);
lj_mem_free(J2G(J), kn, sizeof(KNumArray));
kn = next;
}
}
/* Find KNUM constant in chained array or add it. */
static cTValue *ir_knum_find(jit_State *J, uint64_t nn)
{
KNumArray *kn, *knp = NULL;
TValue *ntv;
MSize idx;
/* Search for the constant in the whole chain of arrays. */
for (kn = mref(J->knum, KNumArray); kn; kn = mref(kn->next, KNumArray)) {
knp = kn; /* Remember previous element in list. */
for (idx = 0; idx < kn->numk; idx++) { /* Search one array. */
TValue *tv = &kn->k[idx];
if (tv->u64 == nn) /* Needed for +-0/NaN/absmask. */
return tv;
}
}
/* Constant was not found, need to add it. */
if (!(knp && knp->numk < LJ_MIN_KNUMSZ)) { /* Allocate a new array. */
KNumArray *nkn = lj_mem_newt(J->L, sizeof(KNumArray), KNumArray);
setmref(nkn->next, NULL);
nkn->numk = 0;
if (knp)
setmref(knp->next, nkn); /* Chain to the end of the list. */
else
setmref(J->knum, nkn); /* Link first array. */
knp = nkn;
}
ntv = &knp->k[knp->numk++]; /* Add to current array. */
ntv->u64 = nn;
return ntv;
}
/* Intern FP constant, given by its address. */
TRef lj_ir_knum_addr(jit_State *J, cTValue *tv)
{
IRIns *ir, *cir = J->cur.ir;
IRRef ref;
for (ref = J->chain[IR_KNUM]; ref; ref = cir[ref].prev)
if (ir_knum(&cir[ref]) == tv)
goto found;
ref = ir_nextk(J);
ir = IR(ref);
setmref(ir->ptr, tv);
ir->t.irt = IRT_NUM;
ir->o = IR_KNUM;
ir->prev = J->chain[IR_KNUM];
J->chain[IR_KNUM] = (IRRef1)ref;
found:
return TREF(ref, IRT_NUM);
}
/* Intern FP constant, given by its 64 bit pattern. */
TRef lj_ir_knum_nn(jit_State *J, uint64_t nn)
{
return lj_ir_knum_addr(J, ir_knum_find(J, nn));
}
/* Special 16 byte aligned SIMD constants. */
LJ_DATADEF LJ_ALIGN(16) cTValue lj_ir_knum_tv[4] = {
{ U64x(7fffffff,ffffffff) }, { U64x(7fffffff,ffffffff) },
{ U64x(80000000,00000000) }, { U64x(80000000,00000000) }
};
/* Check whether a number is int and return it. -0 is NOT considered an int. */
static int numistrueint(lua_Number n, int32_t *kp)
{
int32_t k = lj_num2int(n);
if (n == cast_num(k)) {
if (kp) *kp = k;
if (k == 0) { /* Special check for -0. */
TValue tv;
setnumV(&tv, n);
if (tv.u32.hi != 0)
return 0;
}
return 1;
}
return 0;
}
/* Intern number as int32_t constant if possible, otherwise as FP constant. */
TRef lj_ir_knumint(jit_State *J, lua_Number n)
{
int32_t k;
if (numistrueint(n, &k))
return lj_ir_kint(J, k);
else
return lj_ir_knum(J, n);
}
/* Intern GC object "constant". */
TRef lj_ir_kgc(jit_State *J, GCobj *o, IRType t)
{
IRIns *ir, *cir = J->cur.ir;
IRRef ref;
for (ref = J->chain[IR_KGC]; ref; ref = cir[ref].prev)
if (ir_kgc(&cir[ref]) == o)
goto found;
ref = ir_nextk(J);
ir = IR(ref);
/* NOBARRIER: Current trace is a GC root. */
setgcref(ir->gcr, o);
ir->t.irt = (uint8_t)t;
ir->o = IR_KGC;
ir->prev = J->chain[IR_KGC];
J->chain[IR_KGC] = (IRRef1)ref;
found:
return TREF(ref, t);
}
/* Intern 32 bit pointer constant. */
TRef lj_ir_kptr(jit_State *J, void *ptr)
{
IRIns *ir, *cir = J->cur.ir;
IRRef ref;
lua_assert((void *)(intptr_t)i32ptr(ptr) == ptr);
for (ref = J->chain[IR_KPTR]; ref; ref = cir[ref].prev)
if (mref(cir[ref].ptr, void) == ptr)
goto found;
ref = ir_nextk(J);
ir = IR(ref);
setmref(ir->ptr, ptr);
ir->t.irt = IRT_PTR;
ir->o = IR_KPTR;
ir->prev = J->chain[IR_KPTR];
J->chain[IR_KPTR] = (IRRef1)ref;
found:
return TREF(ref, IRT_PTR);
}
/* Intern typed NULL constant. */
TRef lj_ir_knull(jit_State *J, IRType t)
{
IRIns *ir, *cir = J->cur.ir;
IRRef ref;
for (ref = J->chain[IR_KNULL]; ref; ref = cir[ref].prev)
if (irt_t(cir[ref].t) == t)
goto found;
ref = ir_nextk(J);
ir = IR(ref);
ir->i = 0;
ir->t.irt = (uint8_t)t;
ir->o = IR_KNULL;
ir->prev = J->chain[IR_KNULL];
J->chain[IR_KNULL] = (IRRef1)ref;
found:
return TREF(ref, t);
}
/* Intern key slot. */
TRef lj_ir_kslot(jit_State *J, TRef key, IRRef slot)
{
IRIns *ir, *cir = J->cur.ir;
IRRef2 op12 = IRREF2((IRRef1)key, (IRRef1)slot);
IRRef ref;
/* Const part is not touched by CSE/DCE, so 0-65535 is ok for IRMlit here. */
lua_assert(tref_isk(key) && slot == (IRRef)(IRRef1)slot);
for (ref = J->chain[IR_KSLOT]; ref; ref = cir[ref].prev)
if (cir[ref].op12 == op12)
goto found;
ref = ir_nextk(J);
ir = IR(ref);
ir->op12 = op12;
ir->t.irt = IRT_PTR;
ir->o = IR_KSLOT;
ir->prev = J->chain[IR_KSLOT];
J->chain[IR_KSLOT] = (IRRef1)ref;
found:
return TREF(ref, IRT_PTR);
}
/* -- Access to IR constants ---------------------------------------------- */
/* Copy value of IR constant. */
void lj_ir_kvalue(lua_State *L, TValue *tv, const IRIns *ir)
{
UNUSED(L);
lua_assert(ir->o != IR_KSLOT); /* Common mistake. */
if (irt_isint(ir->t)) {
lua_assert(ir->o == IR_KINT);
setintV(tv, ir->i);
} else if (irt_isnum(ir->t)) {
lua_assert(ir->o == IR_KNUM);
setnumV(tv, ir_knum(ir)->n);
} else if (irt_ispri(ir->t)) {
lua_assert(ir->o == IR_KPRI);
setitype(tv, irt_toitype(ir->t));
} else {
if (ir->o == IR_KGC) {
lua_assert(irt_isgcv(ir->t));
setgcV(L, tv, &ir_kgc(ir)->gch, irt_toitype(ir->t));
} else {
lua_assert(ir->o == IR_KPTR || ir->o == IR_KNULL);
setlightudV(tv, mref(ir->ptr, void));
}
}
}
/* -- Convert IR operand types -------------------------------------------- */
/* Convert from integer or string to number. */
TRef LJ_FASTCALL lj_ir_tonum(jit_State *J, TRef tr)
{
if (!tref_isnum(tr)) {
if (tref_isinteger(tr))
tr = emitir(IRTN(IR_TONUM), tr, 0);
else if (tref_isstr(tr))
tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0);
else
lj_trace_err(J, LJ_TRERR_BADTYPE);
}
return tr;
}
/* Convert from integer or number to string. */
TRef LJ_FASTCALL lj_ir_tostr(jit_State *J, TRef tr)
{
if (!tref_isstr(tr)) {
if (!tref_isnumber(tr))
lj_trace_err(J, LJ_TRERR_BADTYPE);
tr = emitir(IRT(IR_TOSTR, IRT_STR), tr, 0);
}
return tr;
}
/* Convert from number or string to bitop operand (overflow wrapped). */
TRef LJ_FASTCALL lj_ir_tobit(jit_State *J, TRef tr)
{
if (!tref_isinteger(tr)) {
if (tref_isstr(tr))
tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0);
else if (!tref_isnum(tr))
lj_trace_err(J, LJ_TRERR_BADTYPE);
tr = emitir(IRTI(IR_TOBIT), tr, lj_ir_knum_tobit(J));
}
return tr;
}
/* Convert from number or string to integer (overflow undefined). */
TRef LJ_FASTCALL lj_ir_toint(jit_State *J, TRef tr)
{
if (!tref_isinteger(tr)) {
if (tref_isstr(tr))
tr = emitir(IRTG(IR_STRTO, IRT_NUM), tr, 0);
else if (!tref_isnum(tr))
lj_trace_err(J, LJ_TRERR_BADTYPE);
tr = emitir(IRTI(IR_TOINT), tr, IRTOINT_ANY);
}
return tr;
}
/* -- Miscellaneous IR ops ------------------------------------------------ */
/* Evaluate numeric comparison. */
int lj_ir_numcmp(lua_Number a, lua_Number b, IROp op)
{
switch (op) {
case IR_EQ: return (a == b);
case IR_NE: return (a != b);
case IR_LT: return (a < b);
case IR_GE: return (a >= b);
case IR_LE: return (a <= b);
case IR_GT: return (a > b);
case IR_ULT: return !(a >= b);
case IR_UGE: return !(a < b);
case IR_ULE: return !(a > b);
case IR_UGT: return !(a <= b);
default: lua_assert(0); return 0;
}
}
/* Evaluate string comparison. */
int lj_ir_strcmp(GCstr *a, GCstr *b, IROp op)
{
int res = lj_str_cmp(a, b);
switch (op) {
case IR_LT: return (res < 0);
case IR_GE: return (res >= 0);
case IR_LE: return (res <= 0);
case IR_GT: return (res > 0);
default: lua_assert(0); return 0;
}
}
/* Rollback IR to previous state. */
void lj_ir_rollback(jit_State *J, IRRef ref)
{
IRRef nins = J->cur.nins;
while (nins > ref) {
IRIns *ir;
nins--;
ir = IR(nins);
J->chain[ir->o] = ir->prev;
}
J->cur.nins = nins;
}
#undef IR
#undef fins
#undef emitir
#endif

429
src/lj_ir.h Normal file
View File

@ -0,0 +1,429 @@
/*
** SSA IR (Intermediate Representation) format.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_IR_H
#define _LJ_IR_H
#include "lj_obj.h"
/* IR instruction definition. Order matters, see below. */
#define IRDEF(_) \
/* Miscellaneous ops. */ \
_(NOP, N , ___, ___) \
_(BASE, N , lit, lit) \
_(LOOP, G , ___, ___) \
_(PHI, S , ref, ref) \
_(RENAME, S , ref, lit) \
\
/* Constants. */ \
_(KPRI, N , ___, ___) \
_(KINT, N , cst, ___) \
_(KGC, N , cst, ___) \
_(KPTR, N , cst, ___) \
_(KNULL, N , cst, ___) \
_(KNUM, N , cst, ___) \
_(KSLOT, N , ref, lit) \
\
/* Guarded assertions. */ \
/* Must be properly aligned to flip opposites (^1) and (un)ordered (^4). */ \
_(EQ, GC, ref, ref) \
_(NE, GC, ref, ref) \
\
_(ABC, G , ref, ref) \
_(FRAME, G , ref, ref) \
\
_(LT, G , ref, ref) \
_(GE, G , ref, ref) \
_(LE, G , ref, ref) \
_(GT, G , ref, ref) \
\
_(ULT, G , ref, ref) \
_(UGE, G , ref, ref) \
_(ULE, G , ref, ref) \
_(UGT, G , ref, ref) \
\
/* Bit ops. */ \
_(BNOT, N , ref, ___) \
_(BSWAP, N , ref, ___) \
_(BAND, C , ref, ref) \
_(BOR, C , ref, ref) \
_(BXOR, C , ref, ref) \
_(BSHL, N , ref, ref) \
_(BSHR, N , ref, ref) \
_(BSAR, N , ref, ref) \
_(BROL, N , ref, ref) \
_(BROR, N , ref, ref) \
\
/* Arithmetic ops. ORDER ARITH (FPMATH/POWI take the space for MOD/POW). */ \
_(ADD, C , ref, ref) \
_(SUB, N , ref, ref) \
_(MUL, C , ref, ref) \
_(DIV, N , ref, ref) \
\
_(FPMATH, N , ref, lit) \
_(POWI, N , ref, ref) \
\
_(NEG, N , ref, ref) \
_(ABS, N , ref, ref) \
_(ATAN2, N , ref, ref) \
_(LDEXP, N , ref, ref) \
_(MIN, C , ref, ref) \
_(MAX, C , ref, ref) \
\
/* Overflow-checking arithmetic ops. */ \
_(ADDOV, GC, ref, ref) \
_(SUBOV, G , ref, ref) \
\
/* Memory ops. A = array, H = hash, U = upvalue, F = field, S = stack. */ \
\
/* Memory references. */ \
_(AREF, R , ref, ref) \
_(HREFK, RG, ref, ref) \
_(HREF, L , ref, ref) \
_(NEWREF, S , ref, ref) \
_(UREFO, LG, ref, lit) \
_(UREFC, LG, ref, lit) \
_(FREF, R , ref, lit) \
_(STRREF, N , ref, ref) \
\
/* Loads and Stores. These must be in the same order. */ \
_(ALOAD, LG, ref, ___) \
_(HLOAD, LG, ref, ___) \
_(ULOAD, LG, ref, ___) \
_(FLOAD, L , ref, lit) \
_(SLOAD, LG, lit, lit) \
_(XLOAD, L , ref, lit) \
\
_(ASTORE, S , ref, ref) \
_(HSTORE, S , ref, ref) \
_(USTORE, S , ref, ref) \
_(FSTORE, S , ref, ref) \
\
/* String ops. */ \
_(SNEW, N , ref, ref) \
\
/* Table ops. */ \
_(TNEW, A , lit, lit) \
_(TDUP, A , ref, ___) \
_(TLEN, L , ref, ___) \
_(TBAR, S , ref, ___) \
_(OBAR, S , ref, ref) \
\
/* Type conversions. */ \
_(TONUM, N , ref, ___) \
_(TOINT, N , ref, lit) \
_(TOBIT, N , ref, ref) \
_(TOSTR, N , ref, ___) \
_(STRTO, G , ref, ___) \
\
/* End of list. */
/* IR opcodes (max. 256). */
typedef enum {
#define IRENUM(name, m, m1, m2) IR_##name,
IRDEF(IRENUM)
#undef IRENUM
IR__MAX
} IROp;
/* Stored opcode. */
typedef uint8_t IROp1;
LJ_STATIC_ASSERT(((int)IR_EQ^1) == (int)IR_NE);
LJ_STATIC_ASSERT(((int)IR_LT^1) == (int)IR_GE);
LJ_STATIC_ASSERT(((int)IR_LE^1) == (int)IR_GT);
LJ_STATIC_ASSERT(((int)IR_LT^3) == (int)IR_GT);
LJ_STATIC_ASSERT(((int)IR_LT^4) == (int)IR_ULT);
/* Delta between xLOAD and xSTORE. */
#define IRDELTA_L2S ((int)IR_ASTORE - (int)IR_ALOAD)
LJ_STATIC_ASSERT((int)IR_HLOAD + IRDELTA_L2S == (int)IR_HSTORE);
LJ_STATIC_ASSERT((int)IR_ULOAD + IRDELTA_L2S == (int)IR_USTORE);
LJ_STATIC_ASSERT((int)IR_FLOAD + IRDELTA_L2S == (int)IR_FSTORE);
/* FPMATH sub-functions. ORDER FPM. */
#define IRFPMDEF(_) \
_(FLOOR) _(CEIL) _(TRUNC) /* Must be first and in this order. */ \
_(SQRT) _(EXP) _(EXP2) _(LOG) _(LOG2) _(LOG10) \
_(SIN) _(COS) _(TAN) \
_(OTHER)
typedef enum {
#define FPMENUM(name) IRFPM_##name,
IRFPMDEF(FPMENUM)
#undef FPMENUM
IRFPM__MAX
} IRFPMathOp;
/* FLOAD field IDs. */
#define IRFLDEF(_) \
_(STR_LEN, GCstr, len) \
_(FUNC_ENV, GCfunc, l.env) \
_(TAB_META, GCtab, metatable) \
_(TAB_ARRAY, GCtab, array) \
_(TAB_NODE, GCtab, node) \
_(TAB_ASIZE, GCtab, asize) \
_(TAB_HMASK, GCtab, hmask) \
_(TAB_NOMM, GCtab, nomm) \
_(UDATA_META, GCudata, metatable)
typedef enum {
#define FLENUM(name, type, field) IRFL_##name,
IRFLDEF(FLENUM)
#undef FLENUM
IRFL__MAX
} IRFieldID;
/* SLOAD mode bits, stored in op2. */
#define IRSLOAD_INHERIT 1 /* Inherited by exits/side traces. */
#define IRSLOAD_READONLY 2 /* Read-only, omit slot store. */
#define IRSLOAD_PARENT 4 /* Coalesce with parent trace. */
/* XLOAD mode, stored in op2. */
#define IRXLOAD_UNALIGNED 1
/* TOINT mode, stored in op2. Ordered by strength of the checks. */
#define IRTOINT_CHECK 0 /* Number checked for integerness. */
#define IRTOINT_INDEX 1 /* Checked + special backprop rules. */
#define IRTOINT_ANY 2 /* Any FP number is ok. */
#define IRTOINT_TOBIT 3 /* Cache only: TOBIT conversion. */
/* IR operand mode (2 bit). */
typedef enum {
IRMref, /* IR reference. */
IRMlit, /* 16 bit unsigned literal. */
IRMcst, /* Constant literal: i, gcr or ptr. */
IRMnone /* Unused operand. */
} IRMode;
#define IRM___ IRMnone
/* Mode bits: Commutative, {Normal/Ref, Alloc, Load, Store}, Guard. */
#define IRM_C 0x10
#define IRM_N 0x00
#define IRM_R IRM_N
#define IRM_A 0x20
#define IRM_L 0x40
#define IRM_S 0x60
#define IRM_G 0x80
#define IRM_GC (IRM_G|IRM_C)
#define IRM_RG (IRM_R|IRM_G)
#define IRM_LG (IRM_L|IRM_G)
#define irm_op1(m) (cast(IRMode, (m)&3))
#define irm_op2(m) (cast(IRMode, ((m)>>2)&3))
#define irm_iscomm(m) ((m) & IRM_C)
#define irm_kind(m) ((m) & IRM_S)
#define irm_isguard(m) ((m) & IRM_G)
/* Stores or any other op with a guard has a side-effect. */
#define irm_sideeff(m) ((m) >= IRM_S)
#define IRMODE(name, m, m1, m2) ((IRM##m1)|((IRM##m2)<<2)|(IRM_##m)),
LJ_DATA const uint8_t lj_ir_mode[IR__MAX+1];
/* IR result type and flags (8 bit). */
typedef enum {
/* Map of itypes to non-negative numbers. ORDER LJ_T */
IRT_NIL,
IRT_FALSE,
IRT_TRUE,
IRT_LIGHTUD,
/* GCobj types are from here ... */
IRT_STR,
IRT_PTR, /* IRT_PTR never escapes the IR (map of LJ_TUPVAL). */
IRT_THREAD,
IRT_PROTO,
IRT_FUNC,
IRT_9, /* LJ_TDEADKEY is never used in the IR. */
IRT_TAB,
IRT_UDATA,
/* ... until here. */
IRT_NUM,
/* The various integers are only used in the IR and can only escape to
** a TValue after implicit or explicit conversion (TONUM). Their types
** must be contiguous and next to IRT_NUM (see the typerange macros below).
*/
IRT_INT,
IRT_I8,
IRT_U8,
IRT_I16,
IRT_U16,
/* There is room for 14 more types. */
/* Additional flags. */
IRT_MARK = 0x20, /* Marker for misc. purposes. */
IRT_GUARD = 0x40, /* Instruction is a guard. */
IRT_ISPHI = 0x80, /* Instruction is left or right PHI operand. */
/* Masks. */
IRT_TYPE = 0x1f,
IRT_T = 0xff
} IRType;
#define irtype_ispri(irt) ((uint32_t)(irt) <= IRT_TRUE)
/* Stored IRType. */
typedef struct IRType1 { uint8_t irt; } IRType1;
#define IRT(o, t) ((uint32_t)(((o)<<8) | (t)))
#define IRTI(o) (IRT((o), IRT_INT))
#define IRTN(o) (IRT((o), IRT_NUM))
#define IRTG(o, t) (IRT((o), IRT_GUARD|(t)))
#define IRTGI(o) (IRT((o), IRT_GUARD|IRT_INT))
#define irt_t(t) (cast(IRType, (t).irt))
#define irt_type(t) (cast(IRType, (t).irt & IRT_TYPE))
#define irt_sametype(t1, t2) ((((t1).irt ^ (t2).irt) & IRT_TYPE) == 0)
#define irt_typerange(t, first, last) \
((uint32_t)((t).irt & IRT_TYPE) - (uint32_t)(first) <= (uint32_t)(last-first))
#define irt_isnil(t) (irt_type(t) == IRT_NIL)
#define irt_ispri(t) ((uint32_t)irt_type(t) <= IRT_TRUE)
#define irt_isstr(t) (irt_type(t) == IRT_STR)
#define irt_isfunc(t) (irt_type(t) == IRT_FUNC)
#define irt_istab(t) (irt_type(t) == IRT_TAB)
#define irt_isnum(t) (irt_type(t) == IRT_NUM)
#define irt_isint(t) (irt_type(t) == IRT_INT)
#define irt_isi8(t) (irt_type(t) == IRT_I8)
#define irt_isu8(t) (irt_type(t) == IRT_U8)
#define irt_isi16(t) (irt_type(t) == IRT_I16)
#define irt_isu16(t) (irt_type(t) == IRT_U16)
#define irt_isinteger(t) (irt_typerange((t), IRT_INT, IRT_U16))
#define irt_isgcv(t) (irt_typerange((t), IRT_STR, IRT_UDATA))
#define irt_isaddr(t) (irt_typerange((t), IRT_LIGHTUD, IRT_UDATA))
#define itype2irt(tv) \
(~uitype(tv) < IRT_NUM ? cast(IRType, ~uitype(tv)) : IRT_NUM)
#define irt_toitype(t) ((int32_t)~(uint32_t)irt_type(t))
#define irt_isguard(t) ((t).irt & IRT_GUARD)
#define irt_ismarked(t) ((t).irt & IRT_MARK)
#define irt_setmark(t) ((t).irt |= IRT_MARK)
#define irt_clearmark(t) ((t).irt &= ~IRT_MARK)
#define irt_isphi(t) ((t).irt & IRT_ISPHI)
#define irt_setphi(t) ((t).irt |= IRT_ISPHI)
#define irt_clearphi(t) ((t).irt &= ~IRT_ISPHI)
/* Stored combined IR opcode and type. */
typedef uint16_t IROpT;
/* IR references. */
typedef uint16_t IRRef1; /* One stored reference. */
typedef uint32_t IRRef2; /* Two stored references. */
typedef uint32_t IRRef; /* Used to pass around references. */
/* Fixed references. */
enum {
REF_BIAS = 0x8000,
REF_TRUE = REF_BIAS-3,
REF_FALSE = REF_BIAS-2,
REF_NIL = REF_BIAS-1, /* \--- Constants grow downwards. */
REF_BASE = REF_BIAS, /* /--- IR grows upwards. */
REF_FIRST = REF_BIAS+1,
REF_DROP = 0xffff
};
/* Note: IRMlit operands must be < REF_BIAS, too!
** This allows for fast and uniform manipulation of all operands
** without looking up the operand mode in lj_ir_mode:
** - CSE calculates the maximum reference of two operands.
** This must work with mixed reference/literal operands, too.
** - DCE marking only checks for operand >= REF_BIAS.
** - LOOP needs to substitute reference operands.
** Constant references and literals must not be modified.
*/
#define IRREF2(lo, hi) ((IRRef2)(lo) | ((IRRef2)(hi) << 16))
#define irref_isk(ref) ((ref) < REF_BIAS)
/* Tagged IR references. */
typedef uint32_t TRef;
#define TREF(ref, t) (cast(TRef, (ref) + ((t)<<16)))
#define tref_ref(tr) (cast(IRRef1, (tr)))
#define tref_t(tr) (cast(IRType, (tr)>>16))
#define tref_type(tr) (cast(IRType, ((tr)>>16) & IRT_TYPE))
#define tref_typerange(tr, first, last) \
((((tr)>>16) & IRT_TYPE) - (TRef)(first) <= (TRef)(last-first))
#define tref_istype(tr, t) (((tr) & (IRT_TYPE<<16)) == ((t)<<16))
#define tref_isnil(tr) (tref_istype((tr), IRT_NIL))
#define tref_isfalse(tr) (tref_istype((tr), IRT_FALSE))
#define tref_istrue(tr) (tref_istype((tr), IRT_TRUE))
#define tref_isstr(tr) (tref_istype((tr), IRT_STR))
#define tref_isfunc(tr) (tref_istype((tr), IRT_FUNC))
#define tref_istab(tr) (tref_istype((tr), IRT_TAB))
#define tref_isudata(tr) (tref_istype((tr), IRT_UDATA))
#define tref_isnum(tr) (tref_istype((tr), IRT_NUM))
#define tref_isint(tr) (tref_istype((tr), IRT_INT))
#define tref_isbool(tr) (tref_typerange((tr), IRT_FALSE, IRT_TRUE))
#define tref_ispri(tr) (tref_typerange((tr), IRT_NIL, IRT_TRUE))
#define tref_istruecond(tr) (!tref_typerange((tr), IRT_NIL, IRT_FALSE))
#define tref_isinteger(tr) (tref_typerange((tr), IRT_INT, IRT_U16))
#define tref_isnumber(tr) (tref_typerange((tr), IRT_NUM, IRT_U16))
#define tref_isnumber_str(tr) (tref_isnumber((tr)) || tref_isstr((tr)))
#define tref_isgcv(tr) (tref_typerange((tr), IRT_STR, IRT_UDATA))
#define tref_isk(tr) (irref_isk(tref_ref((tr))))
#define tref_isk2(tr1, tr2) (irref_isk(tref_ref((tr1) | (tr2))))
#define TREF_PRI(t) (TREF(REF_NIL-(t), (t)))
#define TREF_NIL (TREF_PRI(IRT_NIL))
#define TREF_FALSE (TREF_PRI(IRT_FALSE))
#define TREF_TRUE (TREF_PRI(IRT_TRUE))
/* IR instruction format (64 bit).
**
** 16 16 8 8 8 8
** +-------+-------+---+---+---+---+
** | op1 | op2 | t | o | r | s |
** +-------+-------+---+---+---+---+
** | op12/i/gco | ot | prev | (alternative fields in union)
** +---------------+-------+-------+
** 32 16 16
**
** prev is only valid prior to register allocation and then reused for r + s.
*/
typedef union IRIns {
struct {
LJ_ENDIAN_LOHI(
IRRef1 op1; /* IR operand 1. */
, IRRef1 op2; /* IR operand 2. */
)
IROpT ot; /* IR opcode and type (overlaps t and o). */
IRRef1 prev; /* Previous ins in same chain (overlaps r and s). */
};
struct {
IRRef2 op12; /* IR operand 1 and 2 (overlaps op1 and op2). */
LJ_ENDIAN_LOHI(
IRType1 t; /* IR type. */
, IROp1 o; /* IR opcode. */
)
LJ_ENDIAN_LOHI(
uint8_t r; /* Register allocation (overlaps prev). */
, uint8_t s; /* Spill slot allocation (overlaps prev). */
)
};
int32_t i; /* 32 bit signed integer literal (overlaps op12). */
GCRef gcr; /* GCobj constant (overlaps op12). */
MRef ptr; /* Pointer constant (overlaps op12). */
} IRIns;
#define ir_kgc(ir) (gcref((ir)->gcr))
#define ir_kstr(ir) (gco2str(ir_kgc((ir))))
#define ir_ktab(ir) (gco2tab(ir_kgc((ir))))
#define ir_kfunc(ir) (gco2func(ir_kgc((ir))))
#define ir_knum(ir) (mref((ir)->ptr, cTValue))
#endif

128
src/lj_iropt.h Normal file
View File

@ -0,0 +1,128 @@
/*
** Common header for IR emitter and optimizations.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_IROPT_H
#define _LJ_IROPT_H
#include "lj_obj.h"
#include "lj_jit.h"
#if LJ_HASJIT
/* IR emitter. */
LJ_FUNC void LJ_FASTCALL lj_ir_growtop(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_ir_emit(jit_State *J);
/* Save current IR in J->fold.ins, but do not emit it (yet). */
static LJ_AINLINE void lj_ir_set_(jit_State *J, uint16_t ot, IRRef1 a, IRRef1 b)
{
J->fold.ins.ot = ot; J->fold.ins.op1 = a; J->fold.ins.op2 = b;
}
#define lj_ir_set(J, ot, a, b) \
lj_ir_set_(J, (uint16_t)(ot), (IRRef1)(a), (IRRef1)(b))
/* Get ref of next IR instruction and optionally grow IR.
** Note: this may invalidate all IRIns*!
*/
static LJ_AINLINE IRRef lj_ir_nextins(jit_State *J)
{
IRRef ref = J->cur.nins;
if (LJ_UNLIKELY(ref >= J->irtoplim)) lj_ir_growtop(J);
J->cur.nins = ref + 1;
return ref;
}
/* Interning of constants. */
LJ_FUNC TRef LJ_FASTCALL lj_ir_kint(jit_State *J, int32_t k);
LJ_FUNC void lj_ir_knum_freeall(jit_State *J);
LJ_FUNC TRef lj_ir_knum_addr(jit_State *J, cTValue *tv);
LJ_FUNC TRef lj_ir_knum_nn(jit_State *J, uint64_t nn);
LJ_FUNC TRef lj_ir_knumint(jit_State *J, lua_Number n);
LJ_FUNC TRef lj_ir_kgc(jit_State *J, GCobj *o, IRType t);
LJ_FUNC TRef lj_ir_kptr(jit_State *J, void *ptr);
LJ_FUNC TRef lj_ir_knull(jit_State *J, IRType t);
LJ_FUNC TRef lj_ir_kslot(jit_State *J, TRef key, IRRef slot);
static LJ_AINLINE TRef lj_ir_knum(jit_State *J, lua_Number n)
{
TValue tv;
tv.n = n;
return lj_ir_knum_nn(J, tv.u64);
}
#define lj_ir_kstr(J, str) lj_ir_kgc(J, obj2gco((str)), IRT_STR)
#define lj_ir_ktab(J, tab) lj_ir_kgc(J, obj2gco((tab)), IRT_TAB)
#define lj_ir_kfunc(J, func) lj_ir_kgc(J, obj2gco((func)), IRT_FUNC)
/* Special FP constants. */
#define lj_ir_knum_zero(J) lj_ir_knum_nn(J, U64x(00000000,00000000))
#define lj_ir_knum_one(J) lj_ir_knum_nn(J, U64x(3ff00000,00000000))
#define lj_ir_knum_tobit(J) lj_ir_knum_nn(J, U64x(43380000,00000000))
/* Special 16 byte aligned SIMD constants. */
LJ_DATA LJ_ALIGN(16) cTValue lj_ir_knum_tv[4];
#define lj_ir_knum_abs(J) lj_ir_knum_addr(J, &lj_ir_knum_tv[0])
#define lj_ir_knum_neg(J) lj_ir_knum_addr(J, &lj_ir_knum_tv[2])
/* Access to constants. */
LJ_FUNC void lj_ir_kvalue(lua_State *L, TValue *tv, const IRIns *ir);
/* Convert IR operand types. */
LJ_FUNC TRef LJ_FASTCALL lj_ir_tonum(jit_State *J, TRef tr);
LJ_FUNC TRef LJ_FASTCALL lj_ir_tostr(jit_State *J, TRef tr);
LJ_FUNC TRef LJ_FASTCALL lj_ir_tobit(jit_State *J, TRef tr);
LJ_FUNC TRef LJ_FASTCALL lj_ir_toint(jit_State *J, TRef tr);
/* Miscellaneous IR ops. */
LJ_FUNC int lj_ir_numcmp(lua_Number a, lua_Number b, IROp op);
LJ_FUNC int lj_ir_strcmp(GCstr *a, GCstr *b, IROp op);
LJ_FUNC void lj_ir_rollback(jit_State *J, IRRef ref);
/* Emit IR instructions with on-the-fly optimizations. */
LJ_FUNC TRef LJ_FASTCALL lj_opt_fold(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_cse(jit_State *J);
/* Special return values for the fold functions. */
enum {
NEXTFOLD, /* Couldn't fold, pass on. */
RETRYFOLD, /* Retry fold with modified fins. */
KINTFOLD, /* Return ref for int constant in fins->i. */
FAILFOLD, /* Guard would always fail. */
DROPFOLD, /* Guard eliminated. */
MAX_FOLD
};
#define INTFOLD(k) ((J->fold.ins.i = (k)), (TRef)KINTFOLD)
#define CONDFOLD(cond) ((TRef)FAILFOLD + (TRef)(cond))
#define LEFTFOLD (J->fold.ins.op1)
#define RIGHTFOLD (J->fold.ins.op2)
#define CSEFOLD (lj_opt_cse(J))
#define EMITFOLD (lj_ir_emit(J))
/* Load/store forwarding. */
LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_aload(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_hload(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_uload(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_fload(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_fwd_tlen(jit_State *J);
LJ_FUNC int lj_opt_fwd_wasnonnil(jit_State *J, IROpT loadop, IRRef xref);
/* Dead-store elimination. */
LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_ustore(jit_State *J);
LJ_FUNC TRef LJ_FASTCALL lj_opt_dse_fstore(jit_State *J);
/* Narrowing. */
LJ_FUNC TRef LJ_FASTCALL lj_opt_narrow_convert(jit_State *J);
LJ_FUNC TRef lj_opt_narrow_mod(jit_State *J, TRef rb, TRef rc);
LJ_FUNC TRef lj_opt_narrow_pow(jit_State *J, TRef rb, TRef rc, TValue *vc);
LJ_FUNC IRType lj_opt_narrow_forl(cTValue *forbase);
/* Optimization passes. */
LJ_FUNC void lj_opt_dce(jit_State *J);
LJ_FUNC int lj_opt_loop(jit_State *J);
#endif
#endif

279
src/lj_jit.h Normal file
View File

@ -0,0 +1,279 @@
/*
** Common definitions for the JIT compiler.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_JIT_H
#define _LJ_JIT_H
#include "lj_obj.h"
#include "lj_ir.h"
/* JIT engine flags. */
#define JIT_F_ON 0x00000001
/* CPU-specific JIT engine flags. */
#if LJ_TARGET_X86ORX64
#define JIT_F_CMOV 0x00000100
#define JIT_F_SSE2 0x00000200
#define JIT_F_SSE4_1 0x00000400
#define JIT_F_P4 0x00000800
#define JIT_F_PREFER_IMUL 0x00001000
#define JIT_F_SPLIT_XMM 0x00002000
#define JIT_F_LEA_AGU 0x00004000
/* Names for the CPU-specific flags. Must match the order above. */
#define JIT_F_CPU_FIRST JIT_F_CMOV
#define JIT_F_CPUSTRING "\4CMOV\4SSE2\6SSE4.1\2P4\3AMD\2K8\4ATOM"
#else
#error "Missing CPU-specific JIT engine flags"
#endif
/* Optimization flags. */
#define JIT_F_OPT_MASK 0x00ff0000
#define JIT_F_OPT_FOLD 0x00010000
#define JIT_F_OPT_CSE 0x00020000
#define JIT_F_OPT_DCE 0x00040000
#define JIT_F_OPT_FWD 0x00080000
#define JIT_F_OPT_DSE 0x00100000
#define JIT_F_OPT_NARROW 0x00200000
#define JIT_F_OPT_LOOP 0x00400000
#define JIT_F_OPT_FUSE 0x00800000
/* Optimizations names for -O. Must match the order above. */
#define JIT_F_OPT_FIRST JIT_F_OPT_FOLD
#define JIT_F_OPTSTRING \
"\4fold\3cse\3dce\3fwd\3dse\6narrow\4loop\4fuse"
/* Optimization levels set a fixed combination of flags. */
#define JIT_F_OPT_0 0
#define JIT_F_OPT_1 (JIT_F_OPT_FOLD|JIT_F_OPT_CSE|JIT_F_OPT_DCE)
#define JIT_F_OPT_2 (JIT_F_OPT_1|JIT_F_OPT_NARROW|JIT_F_OPT_LOOP)
#define JIT_F_OPT_3 (JIT_F_OPT_2|JIT_F_OPT_FWD|JIT_F_OPT_DSE|JIT_F_OPT_FUSE)
#define JIT_F_OPT_DEFAULT JIT_F_OPT_3
#ifdef LUA_USE_WIN
/* See: http://blogs.msdn.com/oldnewthing/archive/2003/10/08/55239.aspx */
#define JIT_P_sizemcode_DEFAULT 64
#else
/* Could go as low as 4K, but the mmap() overhead would be rather high. */
#define JIT_P_sizemcode_DEFAULT 32
#endif
/* Optimization parameters and their defaults. Length is a char in octal! */
#define JIT_PARAMDEF(_) \
_(\010, maxtrace, 1000) /* Max. # of traces in cache. */ \
_(\011, maxrecord, 2000) /* Max. # of recorded IR instructions. */ \
_(\012, maxirconst, 500) /* Max. # of IR constants of a trace. */ \
_(\007, maxside, 100) /* Max. # of side traces of a root trace. */ \
_(\007, maxsnap, 100) /* Max. # of snapshots for a trace. */ \
\
_(\007, hotloop, 57) /* # of iterations to detect a hot loop. */ \
_(\007, hotexit, 10) /* # of taken exits to start a side trace. */ \
_(\007, tryside, 4) /* # of attempts to compile a side trace. */ \
\
_(\012, instunroll, 4) /* Max. unroll for instable loops. */ \
_(\012, loopunroll, 7) /* Max. unroll for loop ops in side traces. */ \
_(\012, callunroll, 3) /* Max. unroll for recursive calls. */ \
_(\011, recunroll, 0) /* Max. unroll for true recursion. */ \
\
/* Size of each machine code area (in KBytes). */ \
_(\011, sizemcode, JIT_P_sizemcode_DEFAULT) \
/* Max. total size of all machine code areas (in KBytes). */ \
_(\010, maxmcode, 512) \
/* End of list. */
enum {
#define JIT_PARAMENUM(len, name, value) JIT_P_##name,
JIT_PARAMDEF(JIT_PARAMENUM)
#undef JIT_PARAMENUM
JIT_P__MAX
};
#define JIT_PARAMSTR(len, name, value) #len #name
#define JIT_P_STRING JIT_PARAMDEF(JIT_PARAMSTR)
/* Trace compiler state. */
typedef enum {
LJ_TRACE_IDLE, /* Trace compiler idle. */
LJ_TRACE_ACTIVE = 0x10,
LJ_TRACE_RECORD, /* Bytecode recording active. */
LJ_TRACE_START, /* New trace started. */
LJ_TRACE_END, /* End of trace. */
LJ_TRACE_ASM, /* Assemble trace. */
LJ_TRACE_ERR, /* Trace aborted with error. */
} TraceState;
/* Machine code type. */
typedef uint8_t MCode;
/* Stack snapshot header. */
typedef struct SnapShot {
uint16_t mapofs; /* Offset into snapshot map. */
IRRef1 ref; /* First IR ref for this snapshot. */
uint8_t nslots; /* Number of stack slots. */
uint8_t nframelinks; /* Number of frame links. */
uint8_t count; /* Count of taken exits for this snapshot. */
uint8_t unused1;
} SnapShot;
#define SNAPCOUNT_DONE 255 /* Already compiled and linked a side trace. */
#define snap_ref(sn) ((IRRef)(IRRef1)(sn))
#define snap_ridsp(sn) ((sn) >> 16)
/* Snapshot and exit numbers. */
typedef uint32_t SnapNo;
typedef uint32_t ExitNo;
/* Trace number. */
typedef uint32_t TraceNo; /* Used to pass around trace numbers. */
typedef uint16_t TraceNo1; /* Stored trace number. */
#define TRACE_INTERP 0 /* Fallback to interpreter. */
/* Trace anchor. */
typedef struct Trace {
IRIns *ir; /* IR instructions/constants. Biased with REF_BIAS. */
IRRef nins; /* Next IR instruction. Biased with REF_BIAS. */
IRRef nk; /* Lowest IR constant. Biased with REF_BIAS. */
SnapShot *snap; /* Snapshot array. */
IRRef2 *snapmap; /* Snapshot map. */
uint16_t nsnap; /* Number of snapshots. */
uint16_t nsnapmap; /* Number of snapshot map elements. */
GCRef startpt; /* Starting prototype. */
BCIns startins; /* Original bytecode of starting instruction. */
MCode *mcode; /* Start of machine code. */
MSize szmcode; /* Size of machine code. */
MSize mcloop; /* Offset of loop start in machine code. */
TraceNo1 link; /* Linked trace (or self for loops). */
TraceNo1 root; /* Root trace of side trace (or 0 for root traces). */
TraceNo1 nextroot; /* Next root trace for same prototype. */
TraceNo1 nextside; /* Next side trace of same root trace. */
uint16_t nchild; /* Number of child traces (root trace only). */
uint16_t spadjust; /* Stack pointer adjustment (offset in bytes). */
#ifdef LUAJIT_USE_GDBJIT
void *gdbjit_entry; /* GDB JIT entry. */
#endif
} Trace;
/* Round-robin penalty cache for bytecodes leading to aborted traces. */
typedef struct HotPenalty {
const BCIns *pc; /* Starting bytecode PC. */
uint16_t val; /* Penalty value, i.e. hotcount start. */
uint16_t reason; /* Abort reason (really TraceErr). */
} HotPenalty;
/* Number of slots for the penalty cache. Must be a power of 2. */
#define PENALTY_SLOTS 16
/* Round-robin backpropagation cache for narrowing conversions. */
typedef struct BPropEntry {
IRRef1 key; /* Key: original reference. */
IRRef1 val; /* Value: reference after conversion. */
IRRef mode; /* Mode for this entry (currently IRTOINT_*). */
} BPropEntry;
/* Number of slots for the backpropagation cache. Must be a power of 2. */
#define BPROP_SLOTS 16
/* Fold state is used to fold instructions on-the-fly. */
typedef struct FoldState {
IRIns ins; /* Currently emitted instruction. */
IRIns left; /* Instruction referenced by left operand. */
IRIns right; /* Instruction referenced by right operand. */
} FoldState;
/* JIT compiler state. */
typedef struct jit_State {
Trace cur; /* Current trace. */
lua_State *L; /* Current Lua state. */
const BCIns *pc; /* Current PC. */
BCReg maxslot; /* Relative to baseslot. */
uint32_t flags; /* JIT engine flags. */
TRef *base; /* Current frame base, points into J->slots. */
BCReg baseslot; /* Current frame base, offset into J->slots. */
GCfunc *fn; /* Current function. */
GCproto *pt; /* Current prototype. */
FoldState fold; /* Fold state. */
uint8_t mergesnap; /* Allowed to merge with next snapshot. */
uint8_t needsnap; /* Need snapshot before recording next bytecode. */
IRType1 guardemit; /* Accumulated IRT_GUARD for emitted instructions. */
uint8_t unused1;
const BCIns *bc_min; /* Start of allowed bytecode range for root trace. */
MSize bc_extent; /* Extent of the range. */
TraceState state; /* Trace compiler state. */
int32_t instunroll; /* Unroll counter for instable loops. */
int32_t loopunroll; /* Unroll counter for loop ops in side traces. */
int32_t tailcalled; /* Number of successive tailcalls. */
int32_t framedepth; /* Current frame depth. */
MRef knum; /* Pointer to chained array of KNUM constants. */
IRIns *irbuf; /* Temp. IR instruction buffer. Biased with REF_BIAS. */
IRRef irtoplim; /* Upper limit of instuction buffer (biased). */
IRRef irbotlim; /* Lower limit of instuction buffer (biased). */
IRRef loopref; /* Last loop reference or ref of final LOOP (or 0). */
SnapShot *snapbuf; /* Temp. snapshot buffer. */
IRRef2 *snapmapbuf; /* Temp. snapshot map buffer. */
MSize sizesnap; /* Size of temp. snapshot buffer. */
MSize sizesnapmap; /* Size of temp. snapshot map buffer. */
Trace **trace; /* Array of traces. */
TraceNo curtrace; /* Current trace number (if not 0). Kept in J->cur. */
TraceNo freetrace; /* Start of scan for next free trace. */
MSize sizetrace; /* Size of trace array. */
IRRef1 chain[IR__MAX]; /* IR instruction skip-list chain anchors. */
TRef slot[LJ_MAX_JSLOTS+LJ_STACK_EXTRA]; /* Stack slot map. */
int32_t param[JIT_P__MAX]; /* JIT engine parameters. */
MCode *exitstubgroup[LJ_MAX_EXITSTUBGR]; /* Exit stub group addresses. */
HotPenalty penalty[PENALTY_SLOTS]; /* Penalty slots. */
uint32_t penaltyslot; /* Round-robin index into penalty slots. */
BPropEntry bpropcache[BPROP_SLOTS]; /* Backpropagation cache slots. */
uint32_t bpropslot; /* Round-robin index into bpropcache slots. */
const BCIns *startpc; /* Bytecode PC of starting instruction. */
TraceNo parent; /* Parent of current side trace (0 for root traces). */
ExitNo exitno; /* Exit number in parent of current side trace. */
TValue errinfo; /* Additional info element for trace errors. */
MCode *mcarea; /* Base of current mcode area. */
MCode *mctop; /* Top of current mcode area. */
MCode *mcbot; /* Bottom of current mcode area. */
size_t szmcarea; /* Size of current mcode area. */
size_t szallmcarea; /* Total size of all allocated mcode areas. */
int mcprot; /* Protection of current mcode area. */
} jit_State;
/* Exit stubs. */
#if LJ_TARGET_X86ORX64
/* Limited by the range of a short fwd jump (127): (2+2)*(32-1)-2 = 122. */
#define EXITSTUB_SPACING (2+2)
#define EXITSTUBS_PER_GROUP 32
#else
#error "Missing CPU-specific exit stub definitions"
#endif
/* Return the address of an exit stub. */
static LJ_AINLINE MCode *exitstub_addr(jit_State *J, ExitNo exitno)
{
lua_assert(J->exitstubgroup[exitno / EXITSTUBS_PER_GROUP] != NULL);
return J->exitstubgroup[exitno / EXITSTUBS_PER_GROUP] +
EXITSTUB_SPACING*(exitno % EXITSTUBS_PER_GROUP);
}
#endif

393
src/lj_lex.c Normal file
View File

@ -0,0 +1,393 @@
/*
** Lexical analyzer.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_lex_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_lex.h"
#include "lj_parse.h"
#include "lj_ctype.h"
/* Lua lexer token names. */
static const char *const tokennames[] = {
#define TKSTR1(name) #name,
#define TKSTR2(name, sym) #sym,
TKDEF(TKSTR1, TKSTR2)
#undef TKSTR1
#undef TKSTR2
NULL
};
/* -- Buffer handling ----------------------------------------------------- */
#define char2int(c) cast(int, cast(uint8_t, (c)))
#define next(ls) \
(ls->current = (ls->n--) > 0 ? char2int(*ls->p++) : fillbuf(ls))
#define save_and_next(ls) (save(ls, ls->current), next(ls))
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
#define END_OF_STREAM (-1)
static int fillbuf(LexState *ls)
{
size_t sz;
const char *buf = ls->rfunc(ls->L, ls->rdata, &sz);
if (buf == NULL || sz == 0) return END_OF_STREAM;
ls->n = (MSize)sz - 1;
ls->p = buf;
return char2int(*(ls->p++));
}
static void save(LexState *ls, int c)
{
if (ls->sb.n + 1 > ls->sb.sz) {
MSize newsize;
if (ls->sb.sz >= LJ_MAX_STR/2)
lj_lex_error(ls, 0, LJ_ERR_XELEM);
newsize = ls->sb.sz * 2;
lj_str_resizebuf(ls->L, &ls->sb, newsize);
}
ls->sb.buf[ls->sb.n++] = cast(char, c);
}
static int check_next(LexState *ls, const char *set)
{
if (!strchr(set, ls->current))
return 0;
save_and_next(ls);
return 1;
}
static void inclinenumber(LexState *ls)
{
int old = ls->current;
lua_assert(currIsNewline(ls));
next(ls); /* skip `\n' or `\r' */
if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip `\n\r' or `\r\n' */
if (++ls->linenumber >= LJ_MAX_LINE)
lj_lex_error(ls, ls->token, LJ_ERR_XLINES);
}
/* -- Scanner for terminals ----------------------------------------------- */
static void read_numeral(LexState *ls, TValue *tv)
{
lua_assert(lj_ctype_isdigit(ls->current));
do {
save_and_next(ls);
} while (lj_ctype_isdigit(ls->current) || ls->current == '.');
if (check_next(ls, "Ee")) /* `E'? */
check_next(ls, "+-"); /* optional exponent sign */
while (lj_ctype_isident(ls->current))
save_and_next(ls);
save(ls, '\0');
if (!lj_str_numconv(ls->sb.buf, tv))
lj_lex_error(ls, TK_number, LJ_ERR_XNUMBER);
}
static int skip_sep(LexState *ls)
{
int count = 0;
int s = ls->current;
lua_assert(s == '[' || s == ']');
save_and_next(ls);
while (ls->current == '=') {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
}
static void read_long_string(LexState *ls, TValue *tv, int sep)
{
save_and_next(ls); /* skip 2nd `[' */
if (currIsNewline(ls)) /* string starts with a newline? */
inclinenumber(ls); /* skip it */
for (;;) {
switch (ls->current) {
case END_OF_STREAM:
lj_lex_error(ls, TK_eof, tv ? LJ_ERR_XLSTR : LJ_ERR_XLCOM);
break;
case ']':
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `]' */
goto endloop;
}
break;
case '\n':
case '\r':
save(ls, '\n');
inclinenumber(ls);
if (!tv) lj_str_resetbuf(&ls->sb); /* avoid wasting space */
break;
default:
if (tv) save_and_next(ls);
else next(ls);
break;
}
} endloop:
if (tv) {
GCstr *str = lj_parse_keepstr(ls, ls->sb.buf + (2 + (MSize)sep),
ls->sb.n - 2*(2 + (MSize)sep));
setstrV(ls->L, tv, str);
}
}
static void read_string(LexState *ls, int delim, TValue *tv)
{
save_and_next(ls);
while (ls->current != delim) {
switch (ls->current) {
case END_OF_STREAM:
lj_lex_error(ls, TK_eof, LJ_ERR_XSTR);
continue;
case '\n':
case '\r':
lj_lex_error(ls, TK_string, LJ_ERR_XSTR);
continue;
case '\\': {
int c;
next(ls); /* do not save the `\' */
switch (ls->current) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\n': case '\r': save(ls, '\n'); inclinenumber(ls); continue;
case END_OF_STREAM: continue; /* will raise an error next loop */
default:
if (!lj_ctype_isdigit(ls->current)) {
save_and_next(ls); /* handles \\, \", \', and \? */
} else { /* \xxx */
int i = 0;
c = 0;
do {
c = 10*c + (ls->current-'0');
next(ls);
} while (++i<3 && lj_ctype_isdigit(ls->current));
if (c > UCHAR_MAX)
lj_lex_error(ls, TK_string, LJ_ERR_XESC);
save(ls, c);
}
continue;
}
save(ls, c);
next(ls);
continue;
}
default:
save_and_next(ls);
break;
}
}
save_and_next(ls); /* skip delimiter */
setstrV(ls->L, tv, lj_parse_keepstr(ls, ls->sb.buf + 1, ls->sb.n - 2));
}
/* -- Main lexical scanner ------------------------------------------------ */
static int llex(LexState *ls, TValue *tv)
{
lj_str_resetbuf(&ls->sb);
for (;;) {
if (lj_ctype_isident(ls->current)) {
GCstr *s;
if (lj_ctype_isdigit(ls->current)) { /* Numeric literal. */
read_numeral(ls, tv);
return TK_number;
}
/* Identifier or reserved word. */
do {
save_and_next(ls);
} while (lj_ctype_isident(ls->current));
s = lj_parse_keepstr(ls, ls->sb.buf, ls->sb.n);
if (s->reserved > 0) /* Reserved word? */
return TK_OFS + s->reserved;
setstrV(ls->L, tv, s);
return TK_name;
}
switch (ls->current) {
case '\n':
case '\r':
inclinenumber(ls);
continue;
case ' ':
case '\t':
case '\v':
case '\f':
next(ls);
continue;
case '-':
next(ls);
if (ls->current != '-') return '-';
/* else is a comment */
next(ls);
if (ls->current == '[') {
int sep = skip_sep(ls);
lj_str_resetbuf(&ls->sb); /* `skip_sep' may dirty the buffer */
if (sep >= 0) {
read_long_string(ls, NULL, sep); /* long comment */
lj_str_resetbuf(&ls->sb);
continue;
}
}
/* else short comment */
while (!currIsNewline(ls) && ls->current != END_OF_STREAM)
next(ls);
continue;
case '[': {
int sep = skip_sep(ls);
if (sep >= 0) {
read_long_string(ls, tv, sep);
return TK_string;
} else if (sep == -1) {
return '[';
} else {
lj_lex_error(ls, TK_string, LJ_ERR_XLDELIM);
continue;
}
}
case '=':
next(ls);
if (ls->current != '=') return '='; else { next(ls); return TK_eq; }
case '<':
next(ls);
if (ls->current != '=') return '<'; else { next(ls); return TK_le; }
case '>':
next(ls);
if (ls->current != '=') return '>'; else { next(ls); return TK_ge; }
case '~':
next(ls);
if (ls->current != '=') return '~'; else { next(ls); return TK_ne; }
case '"':
case '\'':
read_string(ls, ls->current, tv);
return TK_string;
case '.':
save_and_next(ls);
if (check_next(ls, ".")) {
if (check_next(ls, "."))
return TK_dots; /* ... */
else
return TK_concat; /* .. */
} else if (!lj_ctype_isdigit(ls->current)) {
return '.';
} else {
read_numeral(ls, tv);
return TK_number;
}
case END_OF_STREAM:
return TK_eof;
default: {
int c = ls->current;
next(ls);
return c; /* Single-char tokens (+ - / ...). */
}
}
}
}
/* -- Lexer API ----------------------------------------------------------- */
void lj_lex_start(lua_State *L, LexState *ls)
{
ls->L = L;
ls->fs = NULL;
ls->n = 0;
ls->p = NULL;
ls->lookahead = TK_eof; /* No look-ahead token. */
ls->linenumber = 1;
ls->lastline = 1;
lj_str_resizebuf(ls->L, &ls->sb, LJ_MIN_SBUF);
next(ls); /* Read-ahead first char. */
if (ls->current == 0xef && ls->n >= 2 && char2int(ls->p[0]) == 0xbb &&
char2int(ls->p[1]) == 0xbf) { /* Skip UTF-8 BOM (if buffered). */
ls->n -= 2;
ls->p += 2;
next(ls);
}
if (ls->current == '#') { /* Skip POSIX #! header line. */
do {
next(ls);
if (ls->current == END_OF_STREAM) return;
} while (!currIsNewline(ls));
inclinenumber(ls);
}
if (ls->current == LUA_SIGNATURE[0]) {
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_XBCLOAD));
lj_err_throw(L, LUA_ERRSYNTAX);
}
/* This is an unanchored GCstr before it's stored in the prototype.
** Do this last since next() calls the reader which may call the GC.
*/
ls->chunkname = lj_str_newz(L, ls->chunkarg);
}
void lj_lex_next(LexState *ls)
{
ls->lastline = ls->linenumber;
if (LJ_LIKELY(ls->lookahead == TK_eof)) { /* No lookahead token? */
ls->token = llex(ls, &ls->tokenval); /* Get next token. */
} else { /* Otherwise return lookahead token. */
ls->token = ls->lookahead;
ls->lookahead = TK_eof;
ls->tokenval = ls->lookaheadval;
}
}
LexToken lj_lex_lookahead(LexState *ls)
{
lua_assert(ls->lookahead == TK_eof);
ls->lookahead = llex(ls, &ls->lookaheadval);
return ls->lookahead;
}
const char *lj_lex_token2str(LexState *ls, LexToken token)
{
if (token > TK_OFS)
return tokennames[token-TK_OFS-1];
else if (!lj_ctype_iscntrl(token))
return lj_str_pushf(ls->L, "%c", token);
else
return lj_str_pushf(ls->L, "char(%d)", token);
}
void lj_lex_error(LexState *ls, LexToken token, ErrMsg em, ...)
{
const char *tok;
va_list argp;
if (token == 0) {
tok = NULL;
} else if (token == TK_name || token == TK_string || token == TK_number) {
save(ls, '\0');
tok = ls->sb.buf;
} else {
tok = lj_lex_token2str(ls, token);
}
va_start(argp, em);
lj_err_lex(ls->L, strdata(ls->chunkname), tok, ls->linenumber, em, argp);
va_end(argp);
}
void lj_lex_init(lua_State *L)
{
uint32_t i;
for (i = 0; i < TK_RESERVED; i++) {
GCstr *s = lj_str_newz(L, tokennames[i]);
fixstring(s); /* Reserved words are never collected. */
s->reserved = cast_byte(i+1);
}
}

63
src/lj_lex.h Normal file
View File

@ -0,0 +1,63 @@
/*
** Lexical analyzer.
** Major parts taken verbatim from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#ifndef _LJ_LEX_H
#define _LJ_LEX_H
#include <stdarg.h>
#include "lj_obj.h"
#include "lj_err.h"
/* Lua lexer tokens. */
#define TKDEF(_, __) \
_(and) _(break) _(do) _(else) _(elseif) _(end) _(false) \
_(for) _(function) _(if) _(in) _(local) _(nil) _(not) _(or) \
_(repeat) _(return) _(then) _(true) _(until) _(while) \
__(concat, ..) __(dots, ...) __(eq, ==) __(ge, >=) __(le, <=) __(ne, ~=) \
__(number, <number>) __(name, <name>) __(string, <string>) __(eof, <eof>)
enum {
TK_OFS = 256,
#define TKENUM1(name) TK_##name,
#define TKENUM2(name, sym) TK_##name,
TKDEF(TKENUM1, TKENUM2)
#undef TKENUM1
#undef TKENUM2
TK_RESERVED = TK_while - TK_OFS
};
typedef int LexToken;
/* Lua lexer state. */
typedef struct LexState {
struct FuncState *fs; /* Current FuncState. Defined in lj_parse.c. */
struct lua_State *L; /* Lua state. */
TValue tokenval; /* Current token value. */
TValue lookaheadval; /* Lookahead token value. */
int current; /* Current character (charint). */
LexToken token; /* Current token. */
LexToken lookahead; /* Lookahead token. */
SBuf sb; /* String buffer for tokens. */
const char *p; /* Current position in input buffer. */
MSize n; /* Bytes left in input buffer. */
lua_Reader rfunc; /* Reader callback. */
void *rdata; /* Reader callback data. */
BCLine linenumber; /* Input line counter. */
BCLine lastline; /* Line of last token. */
GCstr *chunkname; /* Current chunk name (interned string). */
const char *chunkarg; /* Chunk name argument. */
uint32_t level; /* Syntactical nesting level. */
} LexState;
LJ_FUNC void lj_lex_start(lua_State *L, LexState *ls);
LJ_FUNC void lj_lex_next(LexState *ls);
LJ_FUNC LexToken lj_lex_lookahead(LexState *ls);
LJ_FUNC const char *lj_lex_token2str(LexState *ls, LexToken token);
LJ_FUNC_NORET void lj_lex_error(LexState *ls, LexToken token, ErrMsg em, ...);
LJ_FUNC void lj_lex_init(lua_State *L);
#endif

216
src/lj_lib.c Normal file
View File

@ -0,0 +1,216 @@
/*
** Library function support.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_lib_c
#define LUA_CORE
#include "lauxlib.h"
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_func.h"
#include "lj_vm.h"
#include "lj_lib.h"
/* -- Library initialization ---------------------------------------------- */
static GCtab *lib_create_table(lua_State *L, const char *libname, int hsize)
{
if (libname) {
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
lua_getfield(L, -1, libname);
if (!tvistab(L->top-1)) {
L->top--;
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, hsize) != NULL)
lj_err_callerv(L, LJ_ERR_BADMODN, libname);
settabV(L, L->top, tabV(L->top-1));
L->top++;
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
}
L->top--;
settabV(L, L->top-1, tabV(L->top));
} else {
lua_createtable(L, 0, hsize);
}
return tabV(L->top-1);
}
void lj_lib_register(lua_State *L, const char *libname,
const uint8_t *p, const lua_CFunction *cf)
{
GCtab *env = tabref(L->env);
GCfunc *ofn = NULL;
int ffid = *p++;
GCtab *tab = lib_create_table(L, libname, *p++);
ptrdiff_t tpos = L->top - L->base;
/* Avoid barriers further down. */
if (isblack(obj2gco(tab))) lj_gc_barrierback(G(L), tab);
tab->nomm = 0;
for (;;) {
uint32_t tag = *p++;
MSize len = tag & LIBINIT_LENMASK;
tag &= LIBINIT_TAGMASK;
if (tag != LIBINIT_STRING) {
const char *name;
MSize nuv = (MSize)(L->top - L->base - tpos);
GCfunc *fn = lj_func_newC(L, nuv, env);
if (nuv) {
L->top = L->base + tpos;
memcpy(fn->c.upvalue, L->top, sizeof(TValue)*nuv);
}
fn->c.ffid = (uint8_t)(ffid++);
name = (const char *)p;
p += len;
if (tag != LIBINIT_CF) {
fn->c.gate = makeasmfunc(p[0] + (p[1] << 8));
p += 2;
}
if (tag == LIBINIT_ASM_)
fn->c.f = ofn->c.f; /* Copy handler from previous function. */
else
fn->c.f = *cf++; /* Get cf or handler from C function table. */
if (len) {
/* NOBARRIER: See above for common barrier. */
setfuncV(L, lj_tab_setstr(L, tab, lj_str_new(L, name, len)), fn);
}
ofn = fn;
} else {
switch (tag | len) {
case LIBINIT_SET:
L->top -= 2;
if (tvisstr(L->top+1) && strV(L->top+1)->len == 0)
env = tabV(L->top);
else /* NOBARRIER: See above for common barrier. */
copyTV(L, lj_tab_set(L, tab, L->top+1), L->top);
break;
case LIBINIT_NUMBER:
memcpy(&L->top->n, p, sizeof(double));
L->top++;
p += sizeof(double);
break;
case LIBINIT_COPY:
copyTV(L, L->top, L->top - *p++);
L->top++;
break;
case LIBINIT_LASTCL:
setfuncV(L, L->top++, ofn);
break;
case LIBINIT_FFID:
ffid++;
break;
case LIBINIT_END:
return;
default:
setstrV(L, L->top++, lj_str_new(L, (const char *)p, len));
p += len;
break;
}
}
}
}
/* -- Type checks --------------------------------------------------------- */
TValue *lj_lib_checkany(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
if (o >= L->top)
lj_err_arg(L, narg, LJ_ERR_NOVAL);
return o;
}
GCstr *lj_lib_checkstr(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
if (o < L->top) {
if (LJ_LIKELY(tvisstr(o))) {
return strV(o);
} else if (tvisnum(o)) {
GCstr *s = lj_str_fromnum(L, &o->n);
setstrV(L, o, s);
return s;
}
}
lj_err_argt(L, narg, LUA_TSTRING);
return NULL; /* unreachable */
}
GCstr *lj_lib_optstr(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
return (o < L->top && !tvisnil(o)) ? lj_lib_checkstr(L, narg) : NULL;
}
lua_Number lj_lib_checknum(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
if (!(o < L->top &&
(tvisnum(o) || (tvisstr(o) && lj_str_numconv(strVdata(o), o)))))
lj_err_argt(L, narg, LUA_TNUMBER);
return numV(o);
}
int32_t lj_lib_checkint(lua_State *L, int narg)
{
return lj_num2int(lj_lib_checknum(L, narg));
}
int32_t lj_lib_optint(lua_State *L, int narg, int32_t def)
{
TValue *o = L->base + narg-1;
return (o < L->top && !tvisnil(o)) ? lj_lib_checkint(L, narg) : def;
}
GCfunc *lj_lib_checkfunc(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
if (!(o < L->top && tvisfunc(o)))
lj_err_argt(L, narg, LUA_TFUNCTION);
return funcV(o);
}
GCtab *lj_lib_checktab(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
if (!(o < L->top && tvistab(o)))
lj_err_argt(L, narg, LUA_TTABLE);
return tabV(o);
}
GCtab *lj_lib_checktabornil(lua_State *L, int narg)
{
TValue *o = L->base + narg-1;
if (o < L->top) {
if (tvistab(o))
return tabV(o);
else if (tvisnil(o))
return NULL;
}
lj_err_arg(L, narg, LJ_ERR_NOTABN);
return NULL; /* unreachable */
}
int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst)
{
GCstr *s = def >= 0 ? lj_lib_optstr(L, narg) : lj_lib_checkstr(L, narg);
if (s) {
const char *opt = strdata(s);
MSize len = s->len;
int i;
for (i = 0; *(const uint8_t *)lst; i++) {
if (*(const uint8_t *)lst == len && memcmp(opt, lst+1, len) == 0)
return i;
lst += 1+*(const uint8_t *)lst;
}
lj_err_argv(L, narg, LJ_ERR_INVOPTM, opt);
}
return def;
}

84
src/lj_lib.h Normal file
View File

@ -0,0 +1,84 @@
/*
** Library function support.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_LIB_H
#define _LJ_LIB_H
#include "lj_obj.h"
/*
** A fallback handler is called by the assembler VM if the fast path fails:
**
** - too few arguments: unrecoverable.
** - wrong argument type: recoverable, if coercion succeeds.
** - bad argument value: unrecoverable.
** - stack overflow: recoverable, if stack reallocation succeeds.
** - extra handling: recoverable.
**
** The unrecoverable cases throw an error with lj_err_arg(), lj_err_argtype(),
** lj_err_caller() or lj_err_callermsg().
** The recoverable cases return 0 or the number of results + 1.
** The assembler VM retries the fast path only if 0 is returned.
** This time the fallback must not be called again or it gets stuck in a loop.
*/
/* Return values from fallback handler. */
#define FFH_RETRY 0
#define FFH_UNREACHABLE FFH_RETRY
#define FFH_RES(n) ((n)+1)
LJ_FUNC TValue *lj_lib_checkany(lua_State *L, int narg);
LJ_FUNC GCstr *lj_lib_checkstr(lua_State *L, int narg);
LJ_FUNC GCstr *lj_lib_optstr(lua_State *L, int narg);
LJ_FUNC lua_Number lj_lib_checknum(lua_State *L, int narg);
LJ_FUNC int32_t lj_lib_checkint(lua_State *L, int narg);
LJ_FUNC int32_t lj_lib_optint(lua_State *L, int narg, int32_t def);
LJ_FUNC GCfunc *lj_lib_checkfunc(lua_State *L, int narg);
LJ_FUNC GCtab *lj_lib_checktab(lua_State *L, int narg);
LJ_FUNC GCtab *lj_lib_checktabornil(lua_State *L, int narg);
LJ_FUNC int lj_lib_checkopt(lua_State *L, int narg, int def, const char *lst);
#define lj_lib_opt(L, narg, gotarg, noarg) \
{ TValue *_o = L->base + (narg)-1; \
if (_o < L->top && !tvisnil(_o)) { gotarg } else { noarg } }
/* Avoid including lj_frame.h. */
#define lj_lib_upvalue(L, n) \
(&gcref((L->base-1)->fr.func)->fn.c.upvalue[(n)-1])
/* Library function declarations. Scanned by buildvm. */
#define LJLIB_CF(name) static int lj_cf_##name(lua_State *L)
#define LJLIB_ASM(name) static int lj_ffh_##name(lua_State *L)
#define LJLIB_ASM_(name)
#define LJLIB_SET(name)
#define LJLIB_PUSH(arg)
#define LJLIB_REC(handler)
#define LJLIB_NOREGUV
#define LJLIB_NOREG
#define LJ_LIB_REG(L, name) \
lj_lib_register(L, #name, lj_lib_init_##name, lj_lib_cf_##name)
#define LJ_LIB_REG_(L, regname, name) \
lj_lib_register(L, regname, lj_lib_init_##name, lj_lib_cf_##name)
LJ_FUNC void lj_lib_register(lua_State *L, const char *libname,
const uint8_t *init, const lua_CFunction *cf);
/* Library init data tags. */
#define LIBINIT_LENMASK 0x3f
#define LIBINIT_TAGMASK 0xc0
#define LIBINIT_CF 0x00
#define LIBINIT_ASM 0x40
#define LIBINIT_ASM_ 0x80
#define LIBINIT_STRING 0xc0
#define LIBINIT_MAXSTR 0x39
#define LIBINIT_SET 0xfa
#define LIBINIT_NUMBER 0xfb
#define LIBINIT_COPY 0xfc
#define LIBINIT_LASTCL 0xfd
#define LIBINIT_FFID 0xfe
#define LIBINIT_END 0xff
#endif

260
src/lj_mcode.c Normal file
View File

@ -0,0 +1,260 @@
/*
** Machine code management.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_mcode_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_gc.h"
#include "lj_jit.h"
#include "lj_mcode.h"
#include "lj_trace.h"
#include "lj_dispatch.h"
/* -- OS-specific functions ----------------------------------------------- */
#if defined(LUA_USE_WIN)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define MCPROT_RW PAGE_READWRITE
#define MCPROT_RX PAGE_EXECUTE_READ
#define MCPROT_RWX PAGE_EXECUTE_READWRITE
static LJ_AINLINE void *mcode_alloc(jit_State *J, size_t sz, DWORD prot)
{
void *p = VirtualAlloc(NULL, sz, MEM_RESERVE|MEM_COMMIT|MEM_TOP_DOWN, prot);
if (!p)
lj_trace_err(J, LJ_TRERR_MCODEAL);
return p;
}
static LJ_AINLINE void mcode_free(jit_State *J, void *p, size_t sz)
{
UNUSED(J); UNUSED(sz);
VirtualFree(p, 0, MEM_RELEASE);
}
static LJ_AINLINE void mcode_setprot(void *p, size_t sz, DWORD prot)
{
DWORD oprot;
VirtualProtect(p, sz, prot, &oprot);
}
#elif defined(LUA_USE_POSIX)
#include <sys/mman.h>
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#define MCPROT_RW (PROT_READ|PROT_WRITE)
#define MCPROT_RX (PROT_READ|PROT_EXEC)
#define MCPROT_RWX (PROT_READ|PROT_WRITE|PROT_EXEC)
static LJ_AINLINE void *mcode_alloc(jit_State *J, size_t sz, int prot)
{
void *p = mmap(NULL, sz, prot, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
if (p == MAP_FAILED)
lj_trace_err(J, LJ_TRERR_MCODEAL);
return p;
}
static LJ_AINLINE void mcode_free(jit_State *J, void *p, size_t sz)
{
UNUSED(J);
munmap(p, sz);
}
static LJ_AINLINE void mcode_setprot(void *p, size_t sz, int prot)
{
mprotect(p, sz, prot);
}
#else
/* Fallback allocator. This will fail if memory is not executable by default. */
#define LUAJIT_UNPROTECT_MCODE
#define MCPROT_RW 0
#define MCPROT_RX 0
#define MCPROT_RWX 0
static LJ_AINLINE void *mcode_alloc(jit_State *J, size_t sz, int prot)
{
UNUSED(prot);
return lj_mem_new(J->L, sz);
}
static LJ_AINLINE void mcode_free(jit_State *J, void *p, size_t sz)
{
lj_mem_free(J2G(J), p, sz);
}
#define mcode_setprot(p, sz, prot) UNUSED(p)
#endif
/* -- MCode area management ----------------------------------------------- */
/* Define this ONLY if the page protection twiddling becomes a bottleneck. */
#ifdef LUAJIT_UNPROTECT_MCODE
/* It's generally considered to be a potential security risk to have
** pages with simultaneous write *and* execute access in a process.
**
** Do not even think about using this mode for server processes or
** apps handling untrusted external data (such as a browser).
**
** The security risk is not in LuaJIT itself -- but if an adversary finds
** any *other* flaw in your C application logic, then any RWX memory page
** simplifies writing an exploit considerably.
*/
#define MCPROT_GEN MCPROT_RWX
#define MCPROT_RUN MCPROT_RWX
#else
/* This is the default behaviour and much safer:
**
** Most of the time the memory pages holding machine code are executable,
** but NONE of them is writable.
**
** The current memory area is marked read-write (but NOT executable) only
** during the short time window while the assembler generates machine code.
*/
#define MCPROT_GEN MCPROT_RW
#define MCPROT_RUN MCPROT_RX
#endif
/* Change protection of MCode area. */
static void mcode_protect(jit_State *J, int prot)
{
#ifdef LUAJIT_UNPROTECT_MCODE
UNUSED(J); UNUSED(prot);
#else
if (J->mcprot != prot) {
mcode_setprot(J->mcarea, J->szmcarea, prot);
J->mcprot = prot;
}
#endif
}
/* Linked list of MCode areas. */
typedef struct MCLink {
MCode *next; /* Next area. */
size_t size; /* Size of current area. */
} MCLink;
/* Allocate a new MCode area. */
static void mcode_allocarea(jit_State *J)
{
MCode *oldarea = J->mcarea;
size_t sz = (size_t)J->param[JIT_P_sizemcode] << 10;
sz = (sz + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1);
J->mcarea = (MCode *)mcode_alloc(J, sz, MCPROT_GEN);
J->szmcarea = sz;
J->mcprot = MCPROT_GEN;
J->mctop = (MCode *)((char *)J->mcarea + J->szmcarea);
J->mcbot = (MCode *)((char *)J->mcarea + sizeof(MCLink));
((MCLink *)J->mcarea)->next = oldarea;
((MCLink *)J->mcarea)->size = sz;
J->szallmcarea += sz;
}
/* Free all MCode areas. */
void lj_mcode_free(jit_State *J)
{
MCode *mc = J->mcarea;
J->mcarea = NULL;
J->szallmcarea = 0;
while (mc) {
MCode *next = ((MCLink *)mc)->next;
mcode_free(J, mc, ((MCLink *)mc)->size);
mc = next;
}
}
/* -- MCode transactions -------------------------------------------------- */
/* Reserve the remainder of the current MCode area. */
MCode *lj_mcode_reserve(jit_State *J, MCode **lim)
{
if (!J->mcarea)
mcode_allocarea(J);
else
mcode_protect(J, MCPROT_GEN);
*lim = J->mcbot;
return J->mctop;
}
/* Commit the top part of the current MCode area. */
void lj_mcode_commit(jit_State *J, MCode *top)
{
J->mctop = top;
mcode_protect(J, MCPROT_RUN);
}
/* Abort the reservation. */
void lj_mcode_abort(jit_State *J)
{
mcode_protect(J, MCPROT_RUN);
}
/* Set/reset protection to allow patching of MCode areas. */
MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish)
{
#ifdef LUAJIT_UNPROTECT_MCODE
UNUSED(J); UNUSED(ptr); UNUSED(finish);
return NULL;
#else
if (finish) {
if (J->mcarea == ptr)
mcode_protect(J, MCPROT_RUN);
else
mcode_setprot(ptr, ((MCLink *)ptr)->size, MCPROT_RUN);
return NULL;
} else {
MCode *mc = J->mcarea;
/* Try current area first to use the protection cache. */
if (ptr >= mc && ptr < mc + J->szmcarea) {
mcode_protect(J, MCPROT_GEN);
return mc;
}
/* Otherwise search through the list of MCode areas. */
for (;;) {
mc = ((MCLink *)mc)->next;
lua_assert(mc != NULL);
if (ptr >= mc && ptr < mc + ((MCLink *)mc)->size) {
mcode_setprot(mc, ((MCLink *)mc)->size, MCPROT_GEN);
return mc;
}
}
}
#endif
}
/* Limit of MCode reservation reached. */
void lj_mcode_limiterr(jit_State *J, size_t need)
{
size_t sizemcode, maxmcode;
lj_mcode_abort(J);
sizemcode = (size_t)J->param[JIT_P_sizemcode] << 10;
sizemcode = (sizemcode + LJ_PAGESIZE-1) & ~(size_t)(LJ_PAGESIZE - 1);
maxmcode = (size_t)J->param[JIT_P_maxmcode] << 10;
if ((size_t)need > sizemcode)
lj_trace_err(J, LJ_TRERR_MCODEOV); /* Too long for any area. */
if (J->szallmcarea + sizemcode > maxmcode)
lj_trace_err(J, LJ_TRERR_MCODEAL);
mcode_allocarea(J);
lj_trace_err(J, LJ_TRERR_MCODELM); /* Retry with new area. */
}
#endif

23
src/lj_mcode.h Normal file
View File

@ -0,0 +1,23 @@
/*
** Machine code management.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_MCODE_H
#define _LJ_MCODE_H
#include "lj_jit.h"
#if LJ_HASJIT
LJ_FUNC void lj_mcode_free(jit_State *J);
LJ_FUNC MCode *lj_mcode_reserve(jit_State *J, MCode **lim);
LJ_FUNC void lj_mcode_commit(jit_State *J, MCode *m);
LJ_FUNC void lj_mcode_abort(jit_State *J);
LJ_FUNC MCode *lj_mcode_patch(jit_State *J, MCode *ptr, int finish);
LJ_FUNC_NORET void lj_mcode_limiterr(jit_State *J, size_t need);
#define lj_mcode_commitbot(J, m) (J->mcbot = (m))
#endif
#endif

358
src/lj_meta.c Normal file
View File

@ -0,0 +1,358 @@
/*
** Metamethod handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_meta_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_meta.h"
#include "lj_bc.h"
#include "lj_vm.h"
/* -- Metamethod handling ------------------------------------------------- */
/* String interning of metamethod names for fast indexing. */
void lj_meta_init(lua_State *L)
{
#define MMNAME(name) "__" #name
const char *metanames = MMDEF(MMNAME);
#undef MMNAME
global_State *g = G(L);
const char *p, *q;
uint32_t i;
for (i = 0, p = metanames; *p; i++, p = q) {
GCstr *s;
for (q = p+2; *q && *q != '_'; q++) ;
s = lj_str_new(L, p, (size_t)(q-p));
fixstring(s); /* Never collect these names. */
/* NOBARRIER: g->mmname[] is a GC root. */
setgcref(g->mmname[i], obj2gco(s));
}
}
/* Negative caching of a few fast metamethods. See the lj_meta_fast() macro. */
cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name)
{
cTValue *mo = lj_tab_getstr(mt, name);
lua_assert(mm <= MM_FAST);
if (!mo || tvisnil(mo)) { /* No metamethod? */
mt->nomm |= cast_byte(1u<<mm); /* Set negative cache flag. */
return NULL;
}
return mo;
}
/* Lookup metamethod for object. */
cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm)
{
GCtab *mt;
if (tvistab(o))
mt = tabref(tabV(o)->metatable);
else if (tvisudata(o))
mt = tabref(udataV(o)->metatable);
else
mt = tabref(G(L)->basemt[itypemap(o)]);
if (mt) {
cTValue *mo = lj_tab_getstr(mt, strref(G(L)->mmname[mm]));
if (mo)
return mo;
}
return niltv(L);
}
/* Setup call to metamethod to be run by Assembler VM. */
static TValue *mmcall(lua_State *L, ASMFunction cont, cTValue *mo,
cTValue *a, cTValue *b)
{
/*
** |-- framesize -> top top+1 top+2 top+3
** before: [func slots ...]
** mm setup: [func slots ...] [cont|?] [mo|tmtype] [a] [b]
** in asm: [func slots ...] [cont|PC] [mo|delta] [a] [b]
** ^-- func base ^-- mm base
** after mm: [func slots ...] [result]
** ^-- copy to base[PC_RA] --/ for lj_cont_ra
** istruecond + branch for lj_cont_cond*
** ignore for lj_cont_nop
** next PC: [func slots ...]
*/
TValue *top = L->top;
if (curr_funcisL(L)) top = curr_topL(L);
setcont(top, cont); /* Assembler VM stores PC in upper word. */
copyTV(L, top+1, mo); /* Store metamethod and two arguments. */
copyTV(L, top+2, a);
copyTV(L, top+3, b);
return top+2; /* Return new base. */
}
/* -- C helpers for some instructions, called from assembler VM ----------- */
/* Helper for TGET*. __index chain and metamethod. */
cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k)
{
int loop;
for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) {
cTValue *mo;
if (tvistab(o)) {
GCtab *t = tabV(o);
cTValue *tv = lj_tab_get(L, t, k);
if (!tvisnil(tv) ||
!(mo = lj_meta_fast(L, tabref(t->metatable), MM_index)))
return tv;
} else if (tvisnil(mo = lj_meta_lookup(L, o, MM_index))) {
lj_err_optype(L, o, LJ_ERR_OPINDEX);
return NULL; /* unreachable */
}
if (tvisfunc(mo)) {
L->top = mmcall(L, lj_cont_ra, mo, o, k);
return NULL; /* Trigger metamethod call. */
}
o = mo;
}
lj_err_msg(L, LJ_ERR_GETLOOP);
return NULL; /* unreachable */
}
/* Helper for TSET*. __newindex chain and metamethod. */
TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k)
{
TValue tmp;
int loop;
for (loop = 0; loop < LJ_MAX_IDXCHAIN; loop++) {
cTValue *mo;
if (tvistab(o)) {
GCtab *t = tabV(o);
TValue *tv = lj_tab_set(L, t, k);
if (!tvisnil(tv) ||
!(mo = lj_meta_fast(L, tabref(t->metatable), MM_newindex))) {
if (isblack(obj2gco(t))) lj_gc_barrierback(G(L), t);
return tv;
}
} else if (tvisnil(mo = lj_meta_lookup(L, o, MM_newindex))) {
lj_err_optype(L, o, LJ_ERR_OPINDEX);
return NULL; /* unreachable */
}
if (tvisfunc(mo)) {
L->top = mmcall(L, lj_cont_nop, mo, o, k);
/* L->top+2 = v filled in by caller. */
return NULL; /* Trigger metamethod call. */
}
copyTV(L, &tmp, mo);
o = &tmp;
}
lj_err_msg(L, LJ_ERR_SETLOOP);
return NULL; /* unreachable */
}
static cTValue *str2num(cTValue *o, TValue *n)
{
if (tvisnum(o))
return o;
else if (tvisstr(o) && lj_str_numconv(strVdata(o), n))
return n;
else
return NULL;
}
/* Helper for arithmetic instructions. Coercion, metamethod. */
TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb, cTValue *rc,
BCReg op)
{
MMS mm = bcmode_mm(op);
TValue tempb, tempc;
cTValue *b, *c;
if ((b = str2num(rb, &tempb)) != NULL &&
(c = str2num(rc, &tempc)) != NULL) { /* Try coercion first. */
setnumV(ra, lj_vm_foldarith(numV(b), numV(c), (int)mm-MM_add));
return NULL;
} else {
cTValue *mo = lj_meta_lookup(L, rb, mm);
if (tvisnil(mo)) {
mo = lj_meta_lookup(L, rc, mm);
if (tvisnil(mo)) {
if (str2num(rb, &tempb) == NULL) rc = rb;
lj_err_optype(L, rc, LJ_ERR_OPARITH);
return NULL; /* unreachable */
}
}
return mmcall(L, lj_cont_ra, mo, rb, rc);
}
}
/* In-place coercion of a number to a string. */
static LJ_AINLINE int tostring(lua_State *L, TValue *o)
{
if (tvisstr(o)) {
return 1;
} else if (tvisnum(o)) {
setstrV(L, o, lj_str_fromnum(L, &o->n));
return 1;
} else {
return 0;
}
}
/* Helper for CAT. Coercion, iterative concat, __concat metamethod. */
TValue *lj_meta_cat(lua_State *L, TValue *top, int left)
{
do {
int n = 1;
if (!(tvisstr(top-1) || tvisnum(top-1)) || !tostring(L, top)) {
cTValue *mo = lj_meta_lookup(L, top-1, MM_concat);
if (tvisnil(mo)) {
mo = lj_meta_lookup(L, top, MM_concat);
if (tvisnil(mo)) {
if (tvisstr(top-1) || tvisnum(top-1)) top++;
lj_err_optype(L, top-1, LJ_ERR_OPCAT);
return NULL; /* unreachable */
}
}
/* One of the top two elements is not a string, call __cat metamethod:
**
** before: [...][CAT stack .........................]
** top-1 top top+1 top+2
** pick two: [...][CAT stack ...] [o1] [o2]
** setup mm: [...][CAT stack ...] [cont|?] [mo|tmtype] [o1] [o2]
** in asm: [...][CAT stack ...] [cont|PC] [mo|delta] [o1] [o2]
** ^-- func base ^-- mm base
** after mm: [...][CAT stack ...] <--push-- [result]
** next step: [...][CAT stack .............]
*/
copyTV(L, top+2, top) /* Careful with the order of stack copies! */
copyTV(L, top+1, top-1)
copyTV(L, top, mo)
setcont(top-1, lj_cont_cat);
return top+1; /* Trigger metamethod call. */
} else if (strV(top)->len == 0) { /* Shortcut. */
(void)tostring(L, top-1);
} else {
/* Pick as many strings as possible from the top and concatenate them:
**
** before: [...][CAT stack ...........................]
** pick str: [...][CAT stack ...] [...... strings ......]
** concat: [...][CAT stack ...] [result]
** next step: [...][CAT stack ............]
*/
MSize tlen = strV(top)->len;
char *buffer;
int i;
for (n = 1; n <= left && tostring(L, top-n); n++) {
MSize len = strV(top-n)->len;
if (len >= LJ_MAX_STR - tlen)
lj_err_msg(L, LJ_ERR_STROV);
tlen += len;
}
buffer = lj_str_needbuf(L, &G(L)->tmpbuf, tlen);
n--;
tlen = 0;
for (i = n; i >= 0; i--) {
MSize len = strV(top-i)->len;
memcpy(buffer + tlen, strVdata(top-i), len);
tlen += len;
}
setstrV(L, top-n, lj_str_new(L, buffer, tlen));
}
left -= n;
top -= n;
} while (left >= 1);
lj_gc_check_fixtop(L);
return NULL;
}
/* Helper for LEN. __len metamethod. */
TValue *lj_meta_len(lua_State *L, cTValue *o)
{
cTValue *mo = lj_meta_lookup(L, o, MM_len);
if (tvisnil(mo)) {
lj_err_optype(L, o, LJ_ERR_OPLEN);
return NULL; /* unreachable */
}
return mmcall(L, lj_cont_ra, mo, o, niltv(L));
}
/* Helper for equality comparisons. __eq metamethod. */
TValue *lj_meta_equal(lua_State *L, GCobj *o1, GCobj *o2, int ne)
{
/* Field metatable must be at same offset for GCtab and GCudata! */
cTValue *mo = lj_meta_fast(L, tabref(o1->gch.metatable), MM_eq);
if (mo) {
TValue *top;
int it;
if (tabref(o1->gch.metatable) != tabref(o2->gch.metatable)) {
cTValue *mo2 = lj_meta_fast(L, tabref(o2->gch.metatable), MM_eq);
if (mo2 == NULL || !lj_obj_equal(mo, mo2))
return cast(TValue *, (intptr_t)ne);
}
top = curr_top(L);
setcont(top, ne ? lj_cont_condf : lj_cont_condt);
copyTV(L, top+1, mo);
it = o1->gch.gct == ~LJ_TTAB ? LJ_TTAB : LJ_TUDATA;
setgcV(L, top+2, &o1->gch, it);
setgcV(L, top+3, &o2->gch, it);
return top+2; /* Trigger metamethod call. */
}
return cast(TValue *, (intptr_t)ne);
}
/* Helper for ordered comparisons. String compare, __lt/__le metamethods. */
TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op)
{
if (itype(o1) == itype(o2)) { /* Never called with two numbers. */
if (tvisstr(o1) && tvisstr(o2)) {
int32_t res = lj_str_cmp(strV(o1), strV(o2));
return cast(TValue *, (intptr_t)(((op&2) ? res <= 0 : res < 0) ^ (op&1)));
} else {
trymt:
while (1) {
ASMFunction cont = (op & 1) ? lj_cont_condf : lj_cont_condt;
MMS mm = (op & 2) ? MM_le : MM_lt;
cTValue *mo = lj_meta_lookup(L, o1, mm);
cTValue *mo2 = lj_meta_lookup(L, o2, mm);
if (tvisnil(mo) || !lj_obj_equal(mo, mo2)) {
if (op & 2) { /* MM_le not found: retry with MM_lt. */
cTValue *ot = o1; o1 = o2; o2 = ot; /* Swap operands. */
op ^= 3; /* Use LT and flip condition. */
continue;
}
goto err;
}
return mmcall(L, cont, mo, o1, o2);
}
}
} else if (tvisbool(o1) && tvisbool(o2)) {
goto trymt;
} else {
err:
lj_err_comp(L, o1, o2);
return NULL;
}
}
/* Helper for calls. __call metamethod. */
void lj_meta_call(lua_State *L, TValue *func, TValue *top)
{
cTValue *mo = lj_meta_lookup(L, func, MM_call);
TValue *p;
if (!tvisfunc(mo))
lj_err_optype_call(L, func);
for (p = top; p > func; p--) copyTV(L, p, p-1);
copyTV(L, func, mo);
}
/* Helper for FORI. Coercion. */
void lj_meta_for(lua_State *L, TValue *base)
{
if (!str2num(base, base)) lj_err_msg(L, LJ_ERR_FORINIT);
if (!str2num(base+1, base+1)) lj_err_msg(L, LJ_ERR_FORLIM);
if (!str2num(base+2, base+2)) lj_err_msg(L, LJ_ERR_FORSTEP);
}

33
src/lj_meta.h Normal file
View File

@ -0,0 +1,33 @@
/*
** Metamethod handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_META_H
#define _LJ_META_H
#include "lj_obj.h"
/* Metamethod handling */
LJ_FUNC void lj_meta_init(lua_State *L);
LJ_FUNC cTValue *lj_meta_cache(GCtab *mt, MMS mm, GCstr *name);
LJ_FUNC cTValue *lj_meta_lookup(lua_State *L, cTValue *o, MMS mm);
#define lj_meta_fastg(g, mt, mm) \
((mt) == NULL ? NULL : ((mt)->nomm & (1u<<(mm))) ? NULL : \
lj_meta_cache(mt, mm, strref((g)->mmname[mm])))
#define lj_meta_fast(L, mt, mm) lj_meta_fastg(G(L), mt, mm)
/* C helpers for some instructions, called from assembler VM. */
LJ_FUNCA cTValue *lj_meta_tget(lua_State *L, cTValue *o, cTValue *k);
LJ_FUNCA TValue *lj_meta_tset(lua_State *L, cTValue *o, cTValue *k);
LJ_FUNCA TValue *lj_meta_arith(lua_State *L, TValue *ra, cTValue *rb,
cTValue *rc, BCReg op);
LJ_FUNCA TValue *lj_meta_cat(lua_State *L, TValue *top, int left);
LJ_FUNCA TValue *lj_meta_len(lua_State *L, cTValue *o);
LJ_FUNCA TValue *lj_meta_equal(lua_State *L, GCobj *o1, GCobj *o2, int ne);
LJ_FUNCA TValue *lj_meta_comp(lua_State *L, cTValue *o1, cTValue *o2, int op);
LJ_FUNCA void lj_meta_call(lua_State *L, TValue *func, TValue *top);
LJ_FUNCA void lj_meta_for(lua_State *L, TValue *base);
#endif

41
src/lj_obj.c Normal file
View File

@ -0,0 +1,41 @@
/*
** Miscellaneous object handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_obj_c
#define LUA_CORE
#include "lj_obj.h"
/* Object type names. */
LJ_DATADEF const char *const lj_obj_typename[] = { /* ORDER LUA_T */
"no value", "nil", "boolean", "userdata", "number", "string",
"table", "function", "userdata", "thread", "proto", "upval"
};
LJ_DATADEF const char *const lj_obj_itypename[] = { /* ORDER LJ_T */
"nil", "boolean", "boolean", "userdata", "string", "upval", "thread",
"proto", "function", "deadkey", "table", "userdata", "number"
};
/* Compare two objects without calling metamethods. */
int lj_obj_equal(cTValue *o1, cTValue *o2)
{
if (itype(o1) == itype(o2)) {
if (tvispri(o1))
return 1;
if (!tvisnum(o1)) {
#if LJ_64
if (tvislightud(o1))
return o1->u64 == o2->u64;
else
#endif
return gcrefeq(o1->gcr, o2->gcr);
}
} else if (!tvisnum(o1) || !tvisnum(o2)) {
return 0;
}
return numV(o1) == numV(o2);
}

676
src/lj_obj.h Normal file
View File

@ -0,0 +1,676 @@
/*
** LuaJIT VM tags, values and objects.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#ifndef _LJ_OBJ_H
#define _LJ_OBJ_H
#include "lua.h"
#include "lj_def.h"
#include "lj_arch.h"
/* -- Memory references (32 bit address space) ---------------------------- */
/* Memory size. */
typedef uint32_t MSize;
/* Memory reference */
typedef struct MRef {
uint32_t ptr32; /* Pseudo 32 bit pointer. */
} MRef;
#define mref(r, t) ((t *)(void *)(uintptr_t)(r).ptr32)
#define setmref(r, p) ((r).ptr32 = (uint32_t)(uintptr_t)(void *)(p))
#define setmrefr(r, v) ((r).ptr32 = (v).ptr32)
/* -- GC object references (32 bit address space) ------------------------- */
/* GCobj reference */
typedef struct GCRef {
uint32_t gcptr32; /* Pseudo 32 bit pointer. */
} GCRef;
/* Common GC header for all collectable objects. */
#define GCHeader GCRef nextgc; uint8_t marked; uint8_t gct
/* This occupies 6 bytes, so use the next 2 bytes for non-32 bit fields. */
#define gcref(r) ((GCobj *)(uintptr_t)(r).gcptr32)
#define gcrefp(r, t) ((t *)(void *)(uintptr_t)(r).gcptr32)
#define gcrefu(r) ((r).gcptr32)
#define gcrefi(r) ((int32_t)(r).gcptr32)
#define gcrefeq(r1, r2) ((r1).gcptr32 == (r2).gcptr32)
#define gcnext(gc) (gcref((gc)->gch.nextgc))
#define setgcref(r, gc) ((r).gcptr32 = (uint32_t)(uintptr_t)&(gc)->gch)
#define setgcrefi(r, i) ((r).gcptr32 = (uint32_t)(i))
#define setgcrefp(r, p) ((r).gcptr32 = (uint32_t)(uintptr_t)(p))
#define setgcrefnull(r) ((r).gcptr32 = 0)
#define setgcrefr(r, v) ((r).gcptr32 = (v).gcptr32)
/* IMPORTANT NOTE:
**
** All uses of the setgcref* macros MUST be accompanied with a write barrier.
**
** This is to ensure the integrity of the incremental GC. The invariant
** to preserve is that a black object never points to a white object.
** I.e. never store a white object into a field of a black object.
**
** It's ok to LEAVE OUT the write barrier ONLY in the following cases:
** - The source is not a GC object (NULL).
** - The target is a GC root. I.e. everything in global_State.
** - The target is a lua_State field (threads are never black).
** - The target is a stack slot, see setgcV et al.
** - The target is an open upvalue, i.e. pointing to a stack slot.
** - The target is a newly created object (i.e. marked white). But make
** sure nothing invokes the GC inbetween.
** - The target and the source are the same object (self-reference).
** - The target already contains the object (e.g. moving elements around).
**
** The most common case is a store to a stack slot. All other cases where
** a barrier has been omitted are annotated with a NOBARRIER comment.
**
** The same logic applies for stores to table slots (array part or hash
** part). ALL uses of lj_tab_set* require a barrier for the stored *value*
** (if it's a GC object). The barrier for the *key* is already handled
** internally by lj_tab_newkey.
*/
/* -- Common type definitions --------------------------------------------- */
/* Types for handling bytecodes. Need this here, details in lj_bc.h. */
typedef uint32_t BCIns; /* Bytecode instruction. */
typedef uint32_t BCPos; /* Bytecode position. */
typedef uint32_t BCReg; /* Bytecode register. */
typedef int32_t BCLine; /* Bytecode line number. */
/* Internal assembler functions. Never call these directly from C. */
typedef void (*ASMFunction)(void);
/* Resizable string buffer. Need this here, details in lj_str.h. */
typedef struct SBuf {
char *buf; /* String buffer base. */
MSize n; /* String buffer length. */
MSize sz; /* String buffer size. */
} SBuf;
/* -- Tags and values ----------------------------------------------------- */
/* Frame link. */
typedef union {
int32_t ftsz; /* Frame type and size of previous frame. */
MRef pcr; /* Overlaps PC for Lua frames. */
} FrameLink;
/* Tagged value. */
typedef LJ_ALIGN(8) union TValue {
uint64_t u64; /* 64 bit pattern overlaps number. */
lua_Number n; /* Number object overlaps split tag/value object. */
struct {
LJ_ENDIAN_LOHI(
GCRef gcr; /* GCobj reference (if any). */
, int32_t it; /* Internal object tag. Must overlap MSW of number. */
)
};
struct {
LJ_ENDIAN_LOHI(
GCRef func; /* Function for next frame (or dummy L). */
, FrameLink tp; /* Link to previous frame. */
)
} fr;
struct {
LJ_ENDIAN_LOHI(
uint32_t lo; /* Lower 32 bits of number. */
, uint32_t hi; /* Upper 32 bits of number. */
)
} u32;
} TValue;
typedef const TValue cTValue;
#define tvref(r) (mref(r, TValue))
/* More external and GCobj tags for internal objects. */
#define LAST_TT LUA_TTHREAD
#define LUA_TPROTO (LAST_TT+1)
#define LUA_TUPVAL (LAST_TT+2)
#define LUA_TDEADKEY (LAST_TT+3)
/* Internal object tags.
**
** Internal tags overlap the MSW of a number object (must be a double).
** Interpreted as a double these are special NaNs. The FPU only generates
** one type of NaN (0xfff8_0000_0000_0000). So MSWs > 0xfff80000 are available
** for use as internal tags. Small negative numbers are used to shorten the
** encoding of type comparisons (reg/mem against sign-ext. 8 bit immediate).
**
** ---MSW---.---LSW---
** primitive types | itype | |
** lightuserdata | itype | void * | (32 bit platforms)
** lightuserdata |fffc| void * | (64 bit platforms, 48 bit pointers)
** GC objects | itype | GCRef |
** number -------double------
**
** ORDER LJ_T
** Primitive types nil/false/true must be first, lightuserdata next.
** GC objects are at the end, table/userdata must be lowest.
** Also check lj_ir.h for similar ordering constraints.
*/
#define LJ_TNIL (-1)
#define LJ_TFALSE (-2)
#define LJ_TTRUE (-3)
#define LJ_TLIGHTUD (-4)
#define LJ_TSTR (-5)
#define LJ_TUPVAL (-6)
#define LJ_TTHREAD (-7)
#define LJ_TPROTO (-8)
#define LJ_TFUNC (-9)
#define LJ_TDEADKEY (-10)
#define LJ_TTAB (-11)
#define LJ_TUDATA (-12)
/* This is just the canonical number type used in some places. */
#define LJ_TNUMX (-13)
#if LJ_64
#define LJ_TISNUM ((uint32_t)0xfff80000)
#else
#define LJ_TISNUM ((uint32_t)LJ_TNUMX)
#endif
#define LJ_TISTRUECOND ((uint32_t)LJ_TFALSE)
#define LJ_TISPRI ((uint32_t)LJ_TTRUE)
#define LJ_TISGCV ((uint32_t)(LJ_TSTR+1))
#define LJ_TISTABUD ((uint32_t)LJ_TTAB)
/* -- TValue getters/setters ---------------------------------------------- */
/* Macros to test types. */
#define itype(o) ((o)->it)
#define uitype(o) ((uint32_t)itype(o))
#define tvisnil(o) (itype(o) == LJ_TNIL)
#define tvisfalse(o) (itype(o) == LJ_TFALSE)
#define tvistrue(o) (itype(o) == LJ_TTRUE)
#define tvisbool(o) (tvisfalse(o) || tvistrue(o))
#if LJ_64
#define tvislightud(o) ((itype(o) >> 16) == LJ_TLIGHTUD)
#else
#define tvislightud(o) (itype(o) == LJ_TLIGHTUD)
#endif
#define tvisstr(o) (itype(o) == LJ_TSTR)
#define tvisfunc(o) (itype(o) == LJ_TFUNC)
#define tvisthread(o) (itype(o) == LJ_TTHREAD)
#define tvisproto(o) (itype(o) == LJ_TPROTO)
#define tvistab(o) (itype(o) == LJ_TTAB)
#define tvisudata(o) (itype(o) == LJ_TUDATA)
#define tvisnum(o) (uitype(o) <= LJ_TISNUM)
#define tvistruecond(o) (uitype(o) < LJ_TISTRUECOND)
#define tvispri(o) (uitype(o) >= LJ_TISPRI)
#define tvistabud(o) (uitype(o) <= LJ_TISTABUD) /* && !tvisnum() */
#define tvisgcv(o) \
((uitype(o) - LJ_TISGCV) > ((uint32_t)LJ_TNUMX - LJ_TISGCV))
/* Special macros to test numbers for NaN, +0, -0, +1 and raw equality. */
#define tvisnan(o) ((o)->n != (o)->n)
#define tvispzero(o) ((o)->u64 == 0)
#define tvismzero(o) ((o)->u64 == U64x(80000000,00000000))
#define tvispone(o) ((o)->u64 == U64x(3ff00000,00000000))
#define rawnumequal(o1, o2) ((o1)->u64 == (o2)->u64)
/* Macros to convert type ids. */
#if LJ_64
#define itypemap(o) \
(tvisnum(o) ? ~LJ_TNUMX : tvislightud(o) ? ~LJ_TLIGHTUD : ~itype(o))
#else
#define itypemap(o) (tvisnum(o) ? ~LJ_TNUMX : ~itype(o))
#endif
/* Macros to get tagged values. */
#define gcval(o) (gcref((o)->gcr))
#define boolV(o) check_exp(tvisbool(o), (LJ_TFALSE - (o)->it))
#if LJ_64
#define lightudV(o) check_exp(tvislightud(o), \
(void *)((o)->u64 & U64x(0000ffff,ffffffff)))
#else
#define lightudV(o) check_exp(tvislightud(o), gcrefp((o)->gcr, void))
#endif
#define gcV(o) check_exp(tvisgcv(o), gcval(o))
#define strV(o) check_exp(tvisstr(o), &gcval(o)->str)
#define funcV(o) check_exp(tvisfunc(o), &gcval(o)->fn)
#define threadV(o) check_exp(tvisthread(o), &gcval(o)->th)
#define protoV(o) check_exp(tvisproto(o), &gcval(o)->pt)
#define tabV(o) check_exp(tvistab(o), &gcval(o)->tab)
#define udataV(o) check_exp(tvisudata(o), &gcval(o)->ud)
#define numV(o) check_exp(tvisnum(o), (o)->n)
/* Macros to set tagged values. */
#define setitype(o, i) ((o)->it = (i))
#define setnilV(o) ((o)->it = LJ_TNIL)
#define setboolV(o, x) ((o)->it = LJ_TFALSE-(x))
#if LJ_64
#define checklightudptr(L, p) \
(((uint64_t)(p) >> 48) ? (lj_err_msg(L, LJ_ERR_BADLU), NULL) : (p))
#define setlightudV(o, x) \
((o)->u64 = (uint64_t)(x) | (((uint64_t)LJ_TLIGHTUD) << 48))
#define setcont(o, x) \
((o)->u64 = (uint64_t)(x) - (uint64_t)lj_vm_asm_begin)
#else
#define checklightudptr(L, p) (p)
#define setlightudV(o, x) \
{ TValue *i_o = (o); \
setgcrefp(i_o->gcr, (x)); i_o->it = LJ_TLIGHTUD; }
#define setcont(o, x) \
{ TValue *i_o = (o); \
setgcrefp(i_o->gcr, (x)); i_o->it = LJ_TLIGHTUD; }
#endif
#define tvchecklive(g, o) \
lua_assert(!tvisgcv(o) || \
((~itype(o) == gcval(o)->gch.gct) && !isdead(g, gcval(o))))
#define setgcV(L, o, x, itype) \
{ TValue *i_o = (o); \
setgcrefp(i_o->gcr, &(x)->nextgc); i_o->it = itype; \
tvchecklive(G(L), i_o); }
#define setstrV(L, o, x) setgcV(L, o, x, LJ_TSTR)
#define setthreadV(L, o, x) setgcV(L, o, x, LJ_TTHREAD)
#define setprotoV(L, o, x) setgcV(L, o, x, LJ_TPROTO)
#define setfuncV(L, o, x) setgcV(L, o, &(x)->l, LJ_TFUNC)
#define settabV(L, o, x) setgcV(L, o, x, LJ_TTAB)
#define setudataV(L, o, x) setgcV(L, o, x, LJ_TUDATA)
#define setnumV(o, x) ((o)->n = (x))
#define setnanV(o) ((o)->u64 = U64x(fff80000,00000000))
#define setintV(o, i) ((o)->n = cast_num((int32_t)(i)))
/* Copy tagged values. */
#define copyTV(L, o1, o2) \
{ cTValue *i_o2 = (o2); TValue *i_o1 = (o1); \
*i_o1 = *i_o2; tvchecklive(G(L), i_o1); }
/* -- String object ------------------------------------------------------- */
/* String object header. String payload follows. */
typedef struct GCstr {
GCHeader;
uint8_t reserved; /* Used by lexer for fast lookup of reserved words. */
uint8_t unused;
MSize hash; /* Hash of string. */
MSize len; /* Size of string. */
} GCstr;
#define strref(r) (&gcref((r))->str)
#define strdata(s) ((const char *)((s)+1))
#define strdatawr(s) ((char *)((s)+1))
#define strVdata(o) strdata(strV(o))
#define sizestring(s) (sizeof(struct GCstr)+(s)->len+1)
/* -- Userdata object ----------------------------------------------------- */
/* Userdata object. Payload follows. */
typedef struct GCudata {
GCHeader;
uint8_t unused1;
uint8_t unused2;
GCRef env; /* Should be at same offset in GCfunc. */
MSize len; /* Size of payload. */
GCRef metatable; /* Must be at same offset in GCtab. */
uint32_t align1; /* To force 8 byte alignment of the payload. */
} GCudata;
#define uddata(u) ((void *)((u)+1))
#define sizeudata(u) (sizeof(struct GCudata)+(u)->len)
/* -- Prototype object ---------------------------------------------------- */
/* Split constant array. Collectables are below, numbers above pointer. */
typedef union ProtoK {
lua_Number *n; /* Numbers. */
GCRef *gc; /* Collectable objects (strings/table/proto). */
} ProtoK;
#define SCALE_NUM_GCO ((int32_t)sizeof(lua_Number)/sizeof(GCRef))
#define round_nkgc(n) (((n) + SCALE_NUM_GCO-1) & ~(SCALE_NUM_GCO-1))
typedef struct VarInfo {
GCstr *name; /* Local variable name. */
BCPos startpc; /* First point where the local variable is active. */
BCPos endpc; /* First point where the local variable is dead. */
} VarInfo;
typedef struct GCproto {
GCHeader;
uint8_t numparams; /* Number of parameters. */
uint8_t framesize; /* Fixed frame size. */
MSize sizebc; /* Number of bytecode instructions. */
GCRef gclist;
ProtoK k; /* Split constant array (points to the middle). */
BCIns *bc; /* Array of bytecode instructions. */
int16_t *uv; /* Upvalue list. local >= 0. parent uv < 0. */
MSize sizekgc; /* Number of collectable constants. */
MSize sizekn; /* Number of lua_Number constants. */
uint8_t sizeuv; /* Number of upvalues. */
uint8_t flags; /* Miscellaneous flags (see below). */
uint16_t trace; /* Anchor for chain of root traces. */
/* ------ The following fields are for debugging/tracebacks only ------ */
MSize sizelineinfo; /* Size of lineinfo array (may be 0). */
MSize sizevarinfo; /* Size of local var info array (may be 0). */
MSize sizeuvname; /* Size of upvalue names array (may be 0). */
BCLine linedefined; /* First line of the function definition. */
BCLine lastlinedefined; /* Last line of the function definition. */
BCLine *lineinfo; /* Map from bytecode instructions to source lines. */
struct VarInfo *varinfo; /* Names and extents of local variables. */
GCstr **uvname; /* Upvalue names. */
GCstr *chunkname; /* Name of the chunk this function was defined in. */
} GCproto;
#define PROTO_IS_VARARG 0x01
#define PROTO_HAS_FNEW 0x02
#define PROTO_HAS_RETURN 0x04
#define PROTO_FIXUP_RETURN 0x08
#define PROTO_NO_JIT 0x10
#define PROTO_HAS_ILOOP 0x20
/* -- Upvalue object ------------------------------------------------------ */
typedef struct GCupval {
GCHeader;
uint8_t closed; /* Set if closed (i.e. uv->v == &uv->u.value). */
uint8_t unused;
union {
TValue tv; /* If closed: the value itself. */
struct { /* If open: double linked list, anchored at thread. */
GCRef prev;
GCRef next;
};
};
TValue *v; /* Points to stack slot (open) or above (closed). */
#if LJ_32
int32_t unusedv; /* For consistent alignment (32 bit only). */
#endif
} GCupval;
#define uvprev(uv_) (&gcref((uv_)->prev)->uv)
#define uvnext(uv_) (&gcref((uv_)->next)->uv)
/* -- Function object (closures) ------------------------------------------ */
/* Common header for functions. env should be at same offset in GCudata. */
#define GCfuncHeader \
GCHeader; uint8_t ffid; uint8_t nupvalues; \
GCRef env; GCRef gclist; ASMFunction gate
typedef struct GCfuncC {
GCfuncHeader;
lua_CFunction f; /* C function to be called. */
TValue upvalue[1]; /* Array of upvalues (TValue). */
} GCfuncC;
typedef struct GCfuncL {
GCfuncHeader;
GCRef pt; /* Link to prototype this function is based on. */
GCRef uvptr[1]; /* Array of _pointers_ to upvalue objects (GCupval). */
} GCfuncL;
typedef union GCfunc {
GCfuncC c;
GCfuncL l;
} GCfunc;
#define FF_LUA 0
#define FF_C 1
#define isluafunc(fn) ((fn)->c.ffid == FF_LUA)
#define iscfunc(fn) ((fn)->c.ffid == FF_C)
#define isffunc(fn) ((fn)->c.ffid > FF_C)
#define funcproto(fn) check_exp(isluafunc(fn), &gcref((fn)->l.pt)->pt)
#define sizeCfunc(n) (sizeof(GCfuncC) + sizeof(TValue)*((n)-1))
#define sizeLfunc(n) (sizeof(GCfuncL) + sizeof(TValue *)*((n)-1))
/* -- Table object -------------------------------------------------------- */
/* Hash node. */
typedef struct Node {
TValue val; /* Value object. Must be first field. */
TValue key; /* Key object. */
MRef next; /* Hash chain. */
int32_t unused; /* For consistent alignment. */
} Node;
LJ_STATIC_ASSERT(offsetof(Node, val) == 0);
typedef struct GCtab {
GCHeader;
uint8_t nomm; /* Negative cache for fast metamethods. */
int8_t colo; /* Array colocation. */
MRef array; /* Array part. */
GCRef gclist;
GCRef metatable; /* Must be at same offset in GCudata. */
MRef node; /* Hash part. */
uint32_t asize; /* Size of array part (keys [0, asize-1]). */
uint32_t hmask; /* Hash part mask (size of hash part - 1). */
MRef lastfree; /* Any free position is before this position. */
} GCtab;
#define sizetabcolo(n) ((n)*sizeof(TValue) + sizeof(GCtab))
#define tabref(r) (&gcref((r))->tab)
#define noderef(r) (mref((r), Node))
#define nextnode(n) (mref((n)->next, Node))
/* -- State objects ------------------------------------------------------- */
/* VM states. */
enum {
LJ_VMST_INTERP, /* Interpreter. */
LJ_VMST_C, /* C function. */
LJ_VMST_GC, /* Garbage collector. */
LJ_VMST_EXIT, /* Trace exit handler. */
LJ_VMST_RECORD, /* Trace recorder. */
LJ_VMST_OPT, /* Optimizer. */
LJ_VMST_ASM, /* Assembler. */
LJ_VMST__MAX
};
#define setvmstate(g, st) ((g)->vmstate = ~LJ_VMST_##st)
/* Metamethods. */
#define MMDEF(_) \
_(index) _(newindex) _(gc) _(mode) _(eq) \
/* Only the above (fast) metamethods are negative cached (max. 8). */ \
_(len) _(lt) _(le) _(concat) _(call) \
/* The following must be in ORDER ARITH. */ \
_(add) _(sub) _(mul) _(div) _(mod) _(pow) _(unm) \
/* The following are used in the standard libraries. */ \
_(metatable) _(tostring)
typedef enum {
#define MMENUM(name) MM_##name,
MMDEF(MMENUM)
#undef MMENUM
MM_MAX,
MM____ = MM_MAX,
MM_FAST = MM_eq
} MMS;
#define BASEMT_MAX ((~LJ_TNUMX)+1)
typedef struct GCState {
MSize total; /* Memory currently allocated. */
MSize threshold; /* Memory threshold. */
uint8_t currentwhite; /* Current white color. */
uint8_t state; /* GC state. */
uint8_t unused1;
uint8_t unused2;
MSize sweepstr; /* Sweep position in string table. */
GCRef root; /* List of all collectable objects. */
GCRef *sweep; /* Sweep position in root list. */
GCRef gray; /* List of gray objects. */
GCRef grayagain; /* List of objects for atomic traversal. */
GCRef weak; /* List of weak tables (to be cleared). */
GCRef mmudata; /* List of userdata (to be finalized). */
MSize stepmul; /* Incremental GC step granularity. */
MSize debt; /* Debt (how much GC is behind schedule). */
MSize estimate; /* Estimate of memory actually in use. */
MSize pause; /* Pause between successive GC cycles. */
} GCState;
/* Global state, shared by all threads of a Lua universe. */
typedef struct global_State {
GCRef *strhash; /* String hash table (hash chain anchors). */
MSize strmask; /* String hash mask (size of hash table - 1). */
MSize strnum; /* Number of strings in hash table. */
lua_Alloc allocf; /* Memory allocator. */
void *allocd; /* Memory allocator data. */
GCState gc; /* Garbage collector. */
SBuf tmpbuf; /* Temporary buffer for string concatenation. */
Node nilnode; /* Fallback 1-element hash part (nil key and value). */
uint8_t hookmask; /* Hook mask. */
uint8_t dispatchmode; /* Dispatch mode. */
uint8_t vmevmask; /* VM event mask. */
uint8_t unused1;
GCRef mainthref; /* Link to main thread. */
TValue registrytv; /* Anchor for registry. */
TValue tmptv; /* Temporary TValue. */
GCupval uvhead; /* Head of double-linked list of all open upvalues. */
int32_t hookcount; /* Instruction hook countdown. */
int32_t hookcstart; /* Start count for instruction hook counter. */
lua_Hook hookf; /* Hook function. */
lua_CFunction panic; /* Called as a last resort for errors. */
volatile int32_t vmstate; /* VM state or current JIT code trace number. */
GCRef jit_L; /* Current JIT code lua_State or NULL. */
MRef jit_base; /* Current JIT code L->base. */
GCRef basemt[BASEMT_MAX]; /* Metatables for base types. */
GCRef mmname[MM_MAX]; /* Array holding metamethod names. */
} global_State;
#define mainthread(g) (&gcref(g->mainthref)->th)
#define niltv(L) \
check_exp(tvisnil(&G(L)->nilnode.val), &G(L)->nilnode.val)
#define niltvg(g) \
check_exp(tvisnil(&(g)->nilnode.val), &(g)->nilnode.val)
/* Hook management. Hook event masks are defined in lua.h. */
#define HOOK_EVENTMASK 0x0f
#define HOOK_ACTIVE 0x10
#define HOOK_VMEVENT 0x20
#define HOOK_GC 0x40
#define hook_active(g) ((g)->hookmask & HOOK_ACTIVE)
#define hook_enter(g) ((g)->hookmask |= HOOK_ACTIVE)
#define hook_entergc(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_GC))
#define hook_vmevent(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_VMEVENT))
#define hook_leave(g) ((g)->hookmask &= ~HOOK_ACTIVE)
#define hook_save(g) ((g)->hookmask & ~HOOK_EVENTMASK)
#define hook_restore(g, h) \
((g)->hookmask = ((g)->hookmask & HOOK_EVENTMASK) | (h))
/* Per-thread state object. */
struct lua_State {
GCHeader;
uint8_t dummy_ffid; /* Fake FF_C for curr_funcisL() on dummy frames. */
uint8_t status; /* Thread status. */
MRef glref; /* Link to global state. */
GCRef gclist; /* GC chain. */
TValue *base; /* Base of currently executing function. */
TValue *top; /* First free slot in the stack. */
TValue *maxstack; /* Last free slot in the stack. */
TValue *stack; /* Stack base. */
GCRef openupval; /* List of open upvalues in the stack. */
GCRef env; /* Thread environment (table of globals). */
void *cframe; /* End of C stack frame chain. */
MSize stacksize; /* True stack size (incl. LJ_STACK_EXTRA). */
};
#define G(L) (mref(L->glref, global_State))
#define registry(L) (&G(L)->registrytv)
/* Macros to access the currently executing (Lua) function. */
#define curr_func(L) (&gcref((L->base-1)->fr.func)->fn)
#define curr_funcisL(L) (isluafunc(curr_func(L)))
#define curr_proto(L) (funcproto(curr_func(L)))
#define curr_topL(L) (L->base + curr_proto(L)->framesize)
#define curr_top(L) (curr_funcisL(L) ? curr_topL(L) : L->top)
/* -- GC object definition and conversions -------------------------------- */
/* GC header for generic access to common fields of GC objects. */
typedef struct GChead {
GCHeader;
uint8_t unused1;
uint8_t unused2;
GCRef env;
GCRef gclist;
GCRef metatable;
} GChead;
/* The env field SHOULD be at the same offset for all GC objects. */
LJ_STATIC_ASSERT(offsetof(GChead, env) == offsetof(GCfuncL, env));
LJ_STATIC_ASSERT(offsetof(GChead, env) == offsetof(GCudata, env));
/* The metatable field MUST be at the same offset for all GC objects. */
LJ_STATIC_ASSERT(offsetof(GChead, metatable) == offsetof(GCtab, metatable));
LJ_STATIC_ASSERT(offsetof(GChead, metatable) == offsetof(GCudata, metatable));
/* The gclist field MUST be at the same offset for all GC objects. */
LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(lua_State, gclist));
LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCproto, gclist));
LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCfuncL, gclist));
LJ_STATIC_ASSERT(offsetof(GChead, gclist) == offsetof(GCtab, gclist));
typedef union GCobj {
GChead gch;
GCstr str;
GCupval uv;
lua_State th;
GCproto pt;
GCfunc fn;
GCtab tab;
GCudata ud;
} GCobj;
/* Macros to convert a GCobj pointer into a specific value. */
#define gco2str(o) check_exp((o)->gch.gct == ~LJ_TSTR, &(o)->str)
#define gco2uv(o) check_exp((o)->gch.gct == ~LJ_TUPVAL, &(o)->uv)
#define gco2th(o) check_exp((o)->gch.gct == ~LJ_TTHREAD, &(o)->th)
#define gco2pt(o) check_exp((o)->gch.gct == ~LJ_TPROTO, &(o)->pt)
#define gco2func(o) check_exp((o)->gch.gct == ~LJ_TFUNC, &(o)->fn)
#define gco2tab(o) check_exp((o)->gch.gct == ~LJ_TTAB, &(o)->tab)
#define gco2ud(o) check_exp((o)->gch.gct == ~LJ_TUDATA, &(o)->ud)
/* Macro to convert any collectable object into a GCobj pointer. */
#define obj2gco(v) (cast(GCobj *, (v)))
/* -- Number to integer conversion ---------------------------------------- */
static LJ_AINLINE int32_t lj_num2bit(lua_Number n)
{
TValue o;
o.n = n + 6755399441055744.0; /* 2^52 + 2^51 */
return (int32_t)o.u32.lo;
}
#if (defined(__i386__) || defined(_M_IX86)) && !defined(__SSE2__)
#define lj_num2int(n) lj_num2bit((n))
#else
#define lj_num2int(n) ((int32_t)(n))
#endif
/* -- Miscellaneous object handling --------------------------------------- */
/* Names and maps for internal and external object tags. */
LJ_DATA const char *const lj_obj_typename[1+LUA_TUPVAL+1];
LJ_DATA const char *const lj_obj_itypename[~LJ_TNUMX+1];
#define typename(o) (lj_obj_itypename[itypemap(o)])
/* Compare two objects without calling metamethods. */
LJ_FUNC int lj_obj_equal(cTValue *o1, cTValue *o2);
#ifdef LUA_USE_ASSERT
#include "lj_gc.h"
#endif
#endif

79
src/lj_opt_dce.c Normal file
View File

@ -0,0 +1,79 @@
/*
** DCE: Dead Code Elimination. Pre-LOOP only -- ASM already performs DCE.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_opt_dce_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
/* Scan through all snapshots and mark all referenced instructions. */
static void dce_marksnap(jit_State *J)
{
SnapNo i, nsnap = J->cur.nsnap;
for (i = 0; i < nsnap; i++) {
SnapShot *snap = &J->cur.snap[i];
IRRef2 *map = &J->cur.snapmap[snap->mapofs];
BCReg s, nslots = snap->nslots;
for (s = 0; s < nslots; s++) {
IRRef ref = snap_ref(map[s]);
if (!irref_isk(ref))
irt_setmark(IR(ref)->t);
}
}
}
/* Backwards propagate marks. Replace unused instructions with NOPs. */
static void dce_propagate(jit_State *J)
{
IRRef1 *pchain[IR__MAX];
IRRef ins;
uint32_t i;
for (i = 0; i < IR__MAX; i++) pchain[i] = &J->chain[i];
for (ins = J->cur.nins-1; ins >= REF_FIRST; ins--) {
IRIns *ir = IR(ins);
if (irt_ismarked(ir->t)) {
irt_clearmark(ir->t);
pchain[ir->o] = &ir->prev;
} else if (!(irt_isguard(ir->t) || irm_sideeff(lj_ir_mode[ir->o]))) {
*pchain[ir->o] = ir->prev; /* Reroute original instruction chain. */
*pchain[IR_NOP] = (IRRef1)ins;
ir->t.irt = IRT_NIL;
ir->o = IR_NOP; /* Replace instruction with NOP. */
ir->op1 = ir->op2 = 0;
pchain[IR_NOP] = &ir->prev;
continue;
}
if (!irref_isk(ir->op1)) irt_setmark(IR(ir->op1)->t);
if (!irref_isk(ir->op2)) irt_setmark(IR(ir->op2)->t);
}
*pchain[IR_NOP] = 0; /* Terminate NOP chain. */
}
/* Dead Code Elimination.
**
** First backpropagate marks for all used instructions. Then replace
** the unused ones with a NOP. Note that compressing the IR to eliminate
** the NOPs does not pay off.
*/
void lj_opt_dce(jit_State *J)
{
if ((J->flags & JIT_F_OPT_DCE)) {
dce_marksnap(J);
dce_propagate(J);
}
}
#undef IR
#endif

1415
src/lj_opt_fold.c Normal file

File diff suppressed because it is too large Load Diff

358
src/lj_opt_loop.c Normal file
View File

@ -0,0 +1,358 @@
/*
** LOOP: Loop Optimizations.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_opt_loop_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
#include "lj_trace.h"
#include "lj_snap.h"
#include "lj_vm.h"
/* Loop optimization:
**
** Traditional Loop-Invariant Code Motion (LICM) splits the instructions
** of a loop into invariant and variant instructions. The invariant
** instructions are hoisted out of the loop and only the variant
** instructions remain inside the loop body.
**
** Unfortunately LICM is mostly useless for compiling dynamic languages.
** The IR has many guards and most of the subsequent instructions are
** control-dependent on them. The first non-hoistable guard would
** effectively prevent hoisting of all subsequent instructions.
**
** That's why we use a special form of unrolling using copy-substitution,
** combined with redundancy elimination:
**
** The recorded instruction stream is re-emitted to the compiler pipeline
** with substituted operands. The substitution table is filled with the
** refs returned by re-emitting each instruction. This can be done
** on-the-fly, because the IR is in strict SSA form, where every ref is
** defined before its use.
**
** This aproach generates two code sections, separated by the LOOP
** instruction:
**
** 1. The recorded instructions form a kind of pre-roll for the loop. It
** contains a mix of invariant and variant instructions and performs
** exactly one loop iteration (but not necessarily the 1st iteration).
**
** 2. The loop body contains only the variant instructions and performs
** all remaining loop iterations.
**
** On first sight that looks like a waste of space, because the variant
** instructions are present twice. But the key insight is that the
** pre-roll honors the control-dependencies for *both* the pre-roll itself
** *and* the loop body!
**
** It also means one doesn't have to explicitly model control-dependencies
** (which, BTW, wouldn't help LICM much). And it's much easier to
** integrate sparse snapshotting with this approach.
**
** One of the nicest aspects of this approach is that all of the
** optimizations of the compiler pipeline (FOLD, CSE, FWD, etc.) can be
** reused with only minor restrictions (e.g. one should not fold
** instructions across loop-carried dependencies).
**
** But in general all optimizations can be applied which only need to look
** backwards into the generated instruction stream. At any point in time
** during the copy-substitution process this contains both a static loop
** iteration (the pre-roll) and a dynamic one (from the to-be-copied
** instruction up to the end of the partial loop body).
**
** Since control-dependencies are implicitly kept, CSE also applies to all
** kinds of guards. The major advantage is that all invariant guards can
** be hoisted, too.
**
** Load/store forwarding works across loop iterations, too. This is
** important if loop-carried dependencies are kept in upvalues or tables.
** E.g. 'self.idx = self.idx + 1' deep down in some OO-style method may
** become a forwarded loop-recurrence after inlining.
**
** Since the IR is in SSA form, loop-carried dependencies have to be
** modeled with PHI instructions. The potential candidates for PHIs are
** collected on-the-fly during copy-substitution. After eliminating the
** redundant ones, PHI instructions are emitted *below* the loop body.
**
** Note that this departure from traditional SSA form doesn't change the
** semantics of the PHI instructions themselves. But it greatly simplifies
** on-the-fly generation of the IR and the machine code.
*/
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
/* Pass IR on to next optimization in chain (FOLD). */
#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J))
/* Emit raw IR without passing through optimizations. */
#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J))
/* -- PHI elimination ----------------------------------------------------- */
/* Emit or eliminate collected PHIs. */
static void loop_emit_phi(jit_State *J, IRRef1 *subst, IRRef1 *phi, IRRef nphi)
{
int pass2 = 0;
IRRef i, nslots;
IRRef invar = J->chain[IR_LOOP];
/* Pass #1: mark redundant and potentially redundant PHIs. */
for (i = 0; i < nphi; i++) {
IRRef lref = phi[i];
IRRef rref = subst[lref];
if (lref == rref || rref == REF_DROP) { /* Invariants are redundant. */
irt_setmark(IR(lref)->t);
} else if (!(IR(rref)->op1 == lref || IR(rref)->op2 == lref)) {
/* Quick check for simple recurrences failed, need pass2. */
irt_setmark(IR(lref)->t);
pass2 = 1;
}
}
/* Pass #2: traverse variant part and clear marks of non-redundant PHIs. */
if (pass2) {
for (i = J->cur.nins-1; i > invar; i--) {
IRIns *ir = IR(i);
if (!irref_isk(ir->op1)) irt_clearmark(IR(ir->op1)->t);
if (!irref_isk(ir->op2)) irt_clearmark(IR(ir->op2)->t);
}
}
/* Pass #3: add PHIs for variant slots without a corresponding SLOAD. */
nslots = J->baseslot+J->maxslot;
for (i = 1; i < nslots; i++) {
IRRef ref = tref_ref(J->slot[i]);
if (!irref_isk(ref) && ref != subst[ref]) {
IRIns *ir = IR(ref);
irt_clearmark(ir->t); /* Unmark potential uses, too. */
if (!irt_isphi(ir->t) && !irt_ispri(ir->t)) {
irt_setphi(ir->t);
if (nphi >= LJ_MAX_PHI)
lj_trace_err(J, LJ_TRERR_PHIOV);
phi[nphi++] = (IRRef1)ref;
}
}
}
/* Pass #4: emit PHI instructions or eliminate PHIs. */
for (i = 0; i < nphi; i++) {
IRRef lref = phi[i];
IRIns *ir = IR(lref);
if (!irt_ismarked(ir->t)) { /* Emit PHI if not marked. */
IRRef rref = subst[lref];
if (rref > invar)
irt_setphi(IR(rref)->t);
emitir_raw(IRT(IR_PHI, irt_type(ir->t)), lref, rref);
} else { /* Otherwise eliminate PHI. */
irt_clearmark(ir->t);
irt_clearphi(ir->t);
}
}
}
/* -- Loop unrolling using copy-substitution ------------------------------ */
/* Unroll loop. */
static void loop_unroll(jit_State *J)
{
IRRef1 phi[LJ_MAX_PHI];
uint32_t nphi = 0;
IRRef1 *subst;
SnapShot *osnap, *snap;
IRRef2 *loopmap;
BCReg loopslots;
MSize nsnap, nsnapmap;
IRRef ins, invar, osnapref;
/* Use temp buffer for substitution table.
** Only non-constant refs in [REF_BIAS,invar) are valid indexes.
** Note: don't call into the VM or run the GC or the buffer may be gone.
*/
invar = J->cur.nins;
subst = (IRRef1 *)lj_str_needbuf(J->L, &G(J->L)->tmpbuf,
(invar-REF_BIAS)*sizeof(IRRef1)) - REF_BIAS;
subst[REF_BASE] = REF_BASE;
/* LOOP separates the pre-roll from the loop body. */
emitir_raw(IRTG(IR_LOOP, IRT_NIL), 0, 0);
/* Ensure size for copy-substituted snapshots (minus #0 and loop snapshot). */
nsnap = J->cur.nsnap;
if (LJ_UNLIKELY(2*nsnap-2 > J->sizesnap)) {
MSize maxsnap = (MSize)J->param[JIT_P_maxsnap];
if (2*nsnap-2 > maxsnap)
lj_trace_err(J, LJ_TRERR_SNAPOV);
lj_mem_growvec(J->L, J->snapbuf, J->sizesnap, maxsnap, SnapShot);
J->cur.snap = J->snapbuf;
}
nsnapmap = J->cur.nsnapmap; /* Use temp. copy to avoid undo. */
if (LJ_UNLIKELY(nsnapmap*2 > J->sizesnapmap)) {
J->snapmapbuf = (IRRef2 *)lj_mem_realloc(J->L, J->snapmapbuf,
J->sizesnapmap*sizeof(IRRef2),
2*J->sizesnapmap*sizeof(IRRef2));
J->cur.snapmap = J->snapmapbuf;
J->sizesnapmap *= 2;
}
/* The loop snapshot is used for fallback substitutions. */
snap = &J->cur.snap[nsnap-1];
loopmap = &J->cur.snapmap[snap->mapofs];
loopslots = snap->nslots;
/* The PC of snapshot #0 and the loop snapshot must match. */
lua_assert(loopmap[loopslots] == J->cur.snapmap[J->cur.snap[0].nslots]);
/* Start substitution with snapshot #1 (#0 is empty for root traces). */
osnap = &J->cur.snap[1];
osnapref = osnap->ref;
/* Copy and substitute all recorded instructions and snapshots. */
for (ins = REF_FIRST; ins < invar; ins++) {
IRIns *ir;
IRRef op1, op2;
/* Copy-substitute snapshot. */
if (ins >= osnapref) {
IRRef2 *nmap, *omap = &J->cur.snapmap[osnap->mapofs];
BCReg s, nslots;
uint32_t nmapofs, nframelinks;
if (irt_isguard(J->guardemit)) { /* Guard inbetween? */
nmapofs = nsnapmap;
snap++; /* Add new snapshot. */
} else {
nmapofs = snap->mapofs; /* Overwrite previous snapshot. */
}
J->guardemit.irt = 0;
nslots = osnap->nslots;
nframelinks = osnap->nframelinks;
snap->mapofs = (uint16_t)nmapofs;
snap->ref = (IRRef1)J->cur.nins;
snap->nslots = (uint8_t)nslots;
snap->nframelinks = (uint8_t)nframelinks;
snap->count = 0;
osnap++;
osnapref = osnap->ref;
nsnapmap = nmapofs + nslots + nframelinks;
nmap = &J->cur.snapmap[nmapofs];
/* Substitute snapshot slots. */
for (s = 0; s < nslots; s++) {
IRRef ref = snap_ref(omap[s]);
if (ref) {
if (!irref_isk(ref))
ref = subst[ref];
} else if (s < loopslots) {
ref = loopmap[s];
}
nmap[s] = ref;
}
/* Copy frame links. */
nmap += nslots;
omap += nslots;
for (s = 0; s < nframelinks; s++)
nmap[s] = omap[s];
}
/* Substitute instruction operands. */
ir = IR(ins);
op1 = ir->op1;
if (!irref_isk(op1)) op1 = subst[op1];
op2 = ir->op2;
if (!irref_isk(op2)) op2 = subst[op2];
if (irm_kind(lj_ir_mode[ir->o]) == IRM_N &&
op1 == ir->op1 && op2 == ir->op2) { /* Regular invariant ins? */
subst[ins] = (IRRef1)ins; /* Shortcut. */
} else {
/* Re-emit substituted instruction to the FOLD/CSE/etc. pipeline. */
IRType1 t = ir->t; /* Get this first, since emitir may invalidate ir. */
IRRef ref = tref_ref(emitir(ir->ot & ~IRT_ISPHI, op1, op2));
subst[ins] = (IRRef1)ref;
if (ref != ins && ref < invar) { /* Loop-carried dependency? */
IRIns *irr = IR(ref);
/* Potential PHI? */
if (!irref_isk(ref) && !irt_isphi(irr->t) && !irt_ispri(irr->t)) {
irt_setphi(irr->t);
if (nphi >= LJ_MAX_PHI)
lj_trace_err(J, LJ_TRERR_PHIOV);
phi[nphi++] = (IRRef1)ref;
}
/* Check all loop-carried dependencies for type instability. */
if (!irt_sametype(t, irr->t)) {
if (irt_isnum(t) && irt_isinteger(irr->t)) /* Fix int->num case. */
subst[ins] = tref_ref(emitir(IRTN(IR_TONUM), ref, 0));
else
lj_trace_err(J, LJ_TRERR_TYPEINS);
}
}
}
}
if (irt_isguard(J->guardemit)) { /* Guard inbetween? */
J->cur.nsnapmap = (uint16_t)nsnapmap;
snap++;
} else {
J->cur.nsnapmap = (uint16_t)snap->mapofs; /* Last snapshot is redundant. */
}
J->cur.nsnap = (uint16_t)(snap - J->cur.snap);
lua_assert(J->cur.nsnapmap <= J->sizesnapmap);
loop_emit_phi(J, subst, phi, nphi);
}
/* Undo any partial changes made by the loop optimization. */
static void loop_undo(jit_State *J, IRRef ins)
{
lj_ir_rollback(J, ins);
for (ins--; ins >= REF_FIRST; ins--) { /* Remove flags. */
IRIns *ir = IR(ins);
irt_clearphi(ir->t);
irt_clearmark(ir->t);
}
}
/* Protected callback for loop optimization. */
static TValue *cploop_opt(lua_State *L, lua_CFunction dummy, void *ud)
{
UNUSED(L); UNUSED(dummy);
loop_unroll((jit_State *)ud);
return NULL;
}
/* Loop optimization. */
int lj_opt_loop(jit_State *J)
{
IRRef nins = J->cur.nins;
int errcode = lj_vm_cpcall(J->L, cploop_opt, NULL, J);
if (LJ_UNLIKELY(errcode)) {
lua_State *L = J->L;
if (errcode == LUA_ERRRUN && tvisnum(L->top-1)) { /* Trace error? */
int32_t e = lj_num2int(numV(L->top-1));
switch ((TraceError)e) {
case LJ_TRERR_TYPEINS: /* Type instability. */
case LJ_TRERR_GFAIL: /* Guard would always fail. */
/* Unrolling via recording fixes many cases, e.g. a flipped boolean. */
if (--J->instunroll < 0) /* But do not unroll forever. */
break;
L->top--; /* Remove error object. */
J->guardemit.irt = 0;
loop_undo(J, nins);
return 1; /* Loop optimization failed, continue recording. */
default:
break;
}
}
lj_err_throw(L, errcode); /* Propagate all other errors. */
}
return 0; /* Loop optimization is ok. */
}
#undef IR
#undef emitir
#undef emitir_raw
#endif

550
src/lj_opt_mem.c Normal file
View File

@ -0,0 +1,550 @@
/*
** Memory access optimizations.
** AA: Alias Analysis using high-level semantic disambiguation.
** FWD: Load Forwarding (L2L) + Store Forwarding (S2L).
** DSE: Dead-Store Elimination.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_opt_mem_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_tab.h"
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
#define fins (&J->fold.ins)
/*
** Caveat #1: return value is not always a TRef -- only use with tref_ref().
** Caveat #2: FWD relies on active CSE for xREF operands -- see lj_opt_fold().
*/
/* Return values from alias analysis. */
typedef enum {
ALIAS_NO, /* The two refs CANNOT alias (exact). */
ALIAS_MAY, /* The two refs MAY alias (inexact). */
ALIAS_MUST /* The two refs MUST alias (exact). */
} AliasRet;
/* -- ALOAD/HLOAD forwarding and ASTORE/HSTORE elimination ---------------- */
/* Alias analysis for array and hash access using key-based disambiguation. */
static AliasRet aa_ahref(jit_State *J, IRIns *refa, IRIns *refb)
{
IRRef ka = refa->op2;
IRRef kb = refb->op2;
IRIns *keya, *keyb;
if (refa == refb)
return ALIAS_MUST; /* Shortcut for same refs. */
keya = IR(ka);
if (keya->o == IR_KSLOT) { ka = keya->op1; keya = IR(ka); }
keyb = IR(kb);
if (keyb->o == IR_KSLOT) { kb = keyb->op1; keyb = IR(kb); }
if (ka == kb) {
/* Same key. Check for same table with different ref (NEWREF vs. HREF). */
IRIns *ta = refa;
IRIns *tb = refb;
if (ta->o == IR_HREFK || ta->o == IR_AREF) ta = IR(ta->op1);
if (tb->o == IR_HREFK || tb->o == IR_AREF) tb = IR(tb->op1);
if (ta->op1 == tb->op1)
return ALIAS_MUST; /* Same key, same table. */
else
return ALIAS_MAY; /* Same key, possibly different table. */
}
if (irref_isk(ka) && irref_isk(kb))
return ALIAS_NO; /* Different constant keys. */
if (refa->o == IR_AREF) {
/* Disambiguate array references based on index arithmetic. */
lua_assert(refb->o == IR_AREF);
if (refa->op1 == refb->op1) {
/* Same table, different non-const array keys. */
int32_t ofsa = 0, ofsb = 0;
IRRef basea = ka, baseb = kb;
/* Gather base and offset from t[base] or t[base+-ofs]. */
if (keya->o == IR_ADD && irref_isk(keya->op2)) {
basea = keya->op1;
ofsa = IR(keya->op2)->i;
if (basea == kb && ofsa != 0)
return ALIAS_NO; /* t[base+-ofs] vs. t[base]. */
}
if (keyb->o == IR_ADD && irref_isk(keyb->op2)) {
baseb = keyb->op1;
ofsb = IR(keyb->op2)->i;
if (ka == baseb && ofsb != 0)
return ALIAS_NO; /* t[base] vs. t[base+-ofs]. */
}
if (basea == baseb && ofsa != ofsb)
return ALIAS_NO; /* t[base+-o1] vs. t[base+-o2] and o1 != o2. */
}
} else {
/* Disambiguate hash references based on the type of their keys. */
lua_assert((refa->o==IR_HREF || refa->o==IR_HREFK || refa->o==IR_NEWREF) &&
(refb->o==IR_HREF || refb->o==IR_HREFK || refb->o==IR_NEWREF));
if (!irt_sametype(keya->t, keyb->t))
return ALIAS_NO; /* Different key types. */
}
return ALIAS_MAY; /* Anything else: we just don't know. */
}
/* Array and hash load forwarding. */
static TRef fwd_ahload(jit_State *J, IRRef xref)
{
IRIns *xr = IR(xref);
IRRef lim = xref; /* Search limit. */
IRRef ref;
/* Search for conflicting stores. */
ref = J->chain[fins->o+IRDELTA_L2S];
while (ref > xref) {
IRIns *store = IR(ref);
switch (aa_ahref(J, xr, IR(store->op1))) {
case ALIAS_NO: break; /* Continue searching. */
case ALIAS_MAY: lim = ref; goto conflict; /* Limit search for load. */
case ALIAS_MUST: return store->op2; /* Store forwarding. */
}
ref = store->prev;
}
/* No conflicting store (yet): const-fold loads from allocations. */
{
IRIns *ir = (xr->o == IR_HREFK || xr->o == IR_AREF) ? IR(xr->op1) : xr;
IRRef tab = ir->op1;
ir = IR(tab);
if (ir->o == IR_TNEW || (ir->o == IR_TDUP && irref_isk(xr->op2))) {
/* A NEWREF with a number key may end up pointing to the array part.
** But it's referenced from HSTORE and not found in the ASTORE chain.
** For now simply consider this a conflict without forwarding anything.
*/
if (xr->o == IR_AREF) {
IRRef ref2 = J->chain[IR_NEWREF];
while (ref2 > tab) {
IRIns *newref = IR(ref2);
if (irt_isnum(IR(newref->op2)->t))
goto conflict;
ref2 = newref->prev;
}
}
/* NEWREF inhibits CSE for HREF, and dependent FLOADs from HREFK/AREF.
** But the above search for conflicting stores was limited by xref.
** So continue searching, limited by the TNEW/TDUP. Store forwarding
** is ok, too. A conflict does NOT limit the search for a matching load.
*/
while (ref > tab) {
IRIns *store = IR(ref);
switch (aa_ahref(J, xr, IR(store->op1))) {
case ALIAS_NO: break; /* Continue searching. */
case ALIAS_MAY: goto conflict; /* Conflicting store. */
case ALIAS_MUST: return store->op2; /* Store forwarding. */
}
ref = store->prev;
}
lua_assert(ir->o != IR_TNEW || irt_isnil(fins->t));
if (irt_ispri(fins->t)) {
return TREF_PRI(irt_type(fins->t));
} else if (irt_isnum(fins->t) || irt_isstr(fins->t)) {
TValue keyv;
cTValue *tv;
IRIns *key = IR(xr->op2);
if (key->o == IR_KSLOT) key = IR(key->op1);
lj_ir_kvalue(J->L, &keyv, key);
tv = lj_tab_get(J->L, ir_ktab(IR(ir->op1)), &keyv);
lua_assert(itype2irt(tv) == irt_type(fins->t));
if (irt_isnum(fins->t))
return lj_ir_knum_nn(J, tv->u64);
else
return lj_ir_kstr(J, strV(tv));
}
/* Othwerwise: don't intern as a constant. */
}
}
conflict:
/* Try to find a matching load. Below the conflicting store, if any. */
ref = J->chain[fins->o];
while (ref > lim) {
IRIns *load = IR(ref);
if (load->op1 == xref)
return ref; /* Load forwarding. */
ref = load->prev;
}
return 0; /* Conflict or no match. */
}
/* Reassociate ALOAD across PHIs to handle t[i-1] forwarding case. */
static TRef fwd_aload_reassoc(jit_State *J)
{
IRIns *irx = IR(fins->op1);
IRIns *key = IR(irx->op2);
if (key->o == IR_ADD && irref_isk(key->op2)) {
IRIns *add2 = IR(key->op1);
if (add2->o == IR_ADD && irref_isk(add2->op2) &&
IR(key->op2)->i == -IR(add2->op2)->i) {
IRRef ref = J->chain[IR_AREF];
IRRef lim = add2->op1;
if (irx->op1 > lim) lim = irx->op1;
while (ref > lim) {
IRIns *ir = IR(ref);
if (ir->op1 == irx->op1 && ir->op2 == add2->op1)
return fwd_ahload(J, ref);
ref = ir->prev;
}
}
}
return 0;
}
/* ALOAD forwarding. */
TRef LJ_FASTCALL lj_opt_fwd_aload(jit_State *J)
{
IRRef ref;
if ((ref = fwd_ahload(J, fins->op1)) ||
(ref = fwd_aload_reassoc(J)))
return ref;
return EMITFOLD;
}
/* HLOAD forwarding. */
TRef LJ_FASTCALL lj_opt_fwd_hload(jit_State *J)
{
IRRef ref = fwd_ahload(J, fins->op1);
if (ref)
return ref;
return EMITFOLD;
}
/* ASTORE/HSTORE elimination. */
TRef LJ_FASTCALL lj_opt_dse_ahstore(jit_State *J)
{
IRRef xref = fins->op1; /* xREF reference. */
IRRef val = fins->op2; /* Stored value reference. */
IRIns *xr = IR(xref);
IRRef1 *refp = &J->chain[fins->o];
IRRef ref = *refp;
while (ref > xref) { /* Search for redundant or conflicting stores. */
IRIns *store = IR(ref);
switch (aa_ahref(J, xr, IR(store->op1))) {
case ALIAS_NO:
break; /* Continue searching. */
case ALIAS_MAY: /* Store to MAYBE the same location. */
if (store->op2 != val) /* Conflict if the value is different. */
goto doemit;
break; /* Otherwise continue searching. */
case ALIAS_MUST: /* Store to the same location. */
if (store->op2 == val) /* Same value: drop the new store. */
return DROPFOLD;
/* Different value: try to eliminate the redundant store. */
if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */
IRIns *ir;
/* Check for any intervening guards (includes conflicting loads). */
for (ir = IR(J->cur.nins-1); ir > store; ir--)
if (irt_isguard(ir->t))
goto doemit; /* No elimination possible. */
/* Remove redundant store from chain and replace with NOP. */
*refp = store->prev;
store->o = IR_NOP; /* Unchained NOP -- does anybody care? */
store->t.irt = IRT_NIL;
store->op1 = store->op2 = 0;
store->prev = 0;
/* Now emit the new store instead. */
}
goto doemit;
}
ref = *(refp = &store->prev);
}
doemit:
return EMITFOLD; /* Otherwise we have a conflict or simply no match. */
}
/* -- ULOAD forwarding ---------------------------------------------------- */
/* The current alias analysis for upvalues is very simplistic. It only
** disambiguates between the unique upvalues of the same function.
** This is good enough for now, since most upvalues are read-only.
**
** A more precise analysis would be feasible with the help of the parser:
** generate a unique key for every upvalue, even across all prototypes.
** Lacking a realistic use-case, it's unclear whether this is beneficial.
*/
static AliasRet aa_uref(IRIns *refa, IRIns *refb)
{
if (refa->o != refb->o)
return ALIAS_NO; /* Different UREFx type. */
if (refa->op1 != refb->op1)
return ALIAS_MAY; /* Different function. */
else if (refa->op2 == refb->op2)
return ALIAS_MUST; /* Same function, same upvalue idx. */
else
return ALIAS_NO; /* Same function, different upvalue idx. */
}
/* ULOAD forwarding. */
TRef LJ_FASTCALL lj_opt_fwd_uload(jit_State *J)
{
IRRef uref = fins->op1;
IRRef lim = uref; /* Search limit. */
IRIns *xr = IR(uref);
IRRef ref;
/* Search for conflicting stores. */
ref = J->chain[IR_USTORE];
while (ref > uref) {
IRIns *store = IR(ref);
switch (aa_uref(xr, IR(store->op1))) {
case ALIAS_NO: break; /* Continue searching. */
case ALIAS_MAY: lim = ref; goto conflict; /* Limit search for load. */
case ALIAS_MUST: return store->op2; /* Store forwarding. */
}
ref = store->prev;
}
conflict:
/* Try to find a matching load. Below the conflicting store, if any. */
ref = J->chain[IR_ULOAD];
while (ref > lim) {
IRIns *load = IR(ref);
if (load->op1 == uref)
return ref; /* Load forwarding. */
ref = load->prev;
}
return EMITFOLD; /* Conflict or no match. */
}
/* USTORE elimination. */
TRef LJ_FASTCALL lj_opt_dse_ustore(jit_State *J)
{
IRRef xref = fins->op1; /* xREF reference. */
IRRef val = fins->op2; /* Stored value reference. */
IRIns *xr = IR(xref);
IRRef1 *refp = &J->chain[IR_USTORE];
IRRef ref = *refp;
while (ref > xref) { /* Search for redundant or conflicting stores. */
IRIns *store = IR(ref);
switch (aa_uref(xr, IR(store->op1))) {
case ALIAS_NO:
break; /* Continue searching. */
case ALIAS_MAY: /* Store to MAYBE the same location. */
if (store->op2 != val) /* Conflict if the value is different. */
goto doemit;
break; /* Otherwise continue searching. */
case ALIAS_MUST: /* Store to the same location. */
if (store->op2 == val) /* Same value: drop the new store. */
return DROPFOLD;
/* Different value: try to eliminate the redundant store. */
if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */
IRIns *ir;
/* Check for any intervening guards (includes conflicting loads). */
for (ir = IR(J->cur.nins-1); ir > store; ir--)
if (irt_isguard(ir->t))
goto doemit; /* No elimination possible. */
/* Remove redundant store from chain and replace with NOP. */
*refp = store->prev;
store->o = IR_NOP; /* Unchained NOP -- does anybody care? */
store->t.irt = IRT_NIL;
store->op1 = store->op2 = 0;
store->prev = 0;
/* Now emit the new store instead. */
}
goto doemit;
}
ref = *(refp = &store->prev);
}
doemit:
return EMITFOLD; /* Otherwise we have a conflict or simply no match. */
}
/* -- FLOAD forwarding and FSTORE elimination ----------------------------- */
/* Alias analysis for field access.
** Field loads are cheap and field stores are rare.
** Simple disambiguation based on field types is good enough.
*/
static AliasRet aa_fref(IRIns *refa, IRIns *refb)
{
if (refa->op2 != refb->op2)
return ALIAS_NO; /* Different fields. */
if (refa->op1 == refb->op1)
return ALIAS_MUST; /* Same field, same object. */
else
return ALIAS_MAY; /* Same field, possibly different object. */
}
/* Only the loads for mutable fields end up here (see FOLD). */
TRef LJ_FASTCALL lj_opt_fwd_fload(jit_State *J)
{
IRRef oref = fins->op1; /* Object reference. */
IRRef fid = fins->op2; /* Field ID. */
IRRef lim = oref; /* Search limit. */
IRRef ref;
/* Search for conflicting stores. */
ref = J->chain[IR_FSTORE];
while (ref > oref) {
IRIns *store = IR(ref);
switch (aa_fref(fins, IR(store->op1))) {
case ALIAS_NO: break; /* Continue searching. */
case ALIAS_MAY: lim = ref; goto conflict; /* Limit search for load. */
case ALIAS_MUST: return store->op2; /* Store forwarding. */
}
ref = store->prev;
}
/* No conflicting store: const-fold field loads from allocations. */
if (fid == IRFL_TAB_META) {
IRIns *ir = IR(oref);
if (ir->o == IR_TNEW || ir->o == IR_TDUP)
return lj_ir_knull(J, IRT_TAB);
}
conflict:
/* Try to find a matching load. Below the conflicting store, if any. */
ref = J->chain[IR_FLOAD];
while (ref > lim) {
IRIns *load = IR(ref);
if (load->op1 == oref && load->op2 == fid)
return ref; /* Load forwarding. */
ref = load->prev;
}
return EMITFOLD; /* Otherwise we have a conflict or simply no match. */
}
/* FSTORE elimination. */
TRef LJ_FASTCALL lj_opt_dse_fstore(jit_State *J)
{
IRRef fref = fins->op1; /* FREF reference. */
IRRef val = fins->op2; /* Stored value reference. */
IRIns *xr = IR(fref);
IRRef1 *refp = &J->chain[IR_FSTORE];
IRRef ref = *refp;
while (ref > fref) { /* Search for redundant or conflicting stores. */
IRIns *store = IR(ref);
switch (aa_fref(xr, IR(store->op1))) {
case ALIAS_NO:
break; /* Continue searching. */
case ALIAS_MAY:
if (store->op2 != val) /* Conflict if the value is different. */
goto doemit;
break; /* Otherwise continue searching. */
case ALIAS_MUST:
if (store->op2 == val) /* Same value: drop the new store. */
return DROPFOLD;
/* Different value: try to eliminate the redundant store. */
if (ref > J->chain[IR_LOOP]) { /* Quick check to avoid crossing LOOP. */
IRIns *ir;
/* Check for any intervening guards or conflicting loads. */
for (ir = IR(J->cur.nins-1); ir > store; ir--)
if (irt_isguard(ir->t) || (ir->o == IR_FLOAD && ir->op2 == xr->op2))
goto doemit; /* No elimination possible. */
/* Remove redundant store from chain and replace with NOP. */
*refp = store->prev;
store->o = IR_NOP; /* Unchained NOP -- does anybody care? */
store->t.irt = IRT_NIL;
store->op1 = store->op2 = 0;
store->prev = 0;
/* Now emit the new store instead. */
}
goto doemit;
}
ref = *(refp = &store->prev);
}
doemit:
return EMITFOLD; /* Otherwise we have a conflict or simply no match. */
}
/* -- TLEN forwarding ----------------------------------------------------- */
/* This is rather simplistic right now, but better than nothing. */
TRef LJ_FASTCALL lj_opt_fwd_tlen(jit_State *J)
{
IRRef tab = fins->op1; /* Table reference. */
IRRef lim = tab; /* Search limit. */
IRRef ref;
/* Any ASTORE is a conflict and limits the search. */
if (J->chain[IR_ASTORE] > lim) lim = J->chain[IR_ASTORE];
/* Search for conflicting HSTORE with numeric key. */
ref = J->chain[IR_HSTORE];
while (ref > lim) {
IRIns *store = IR(ref);
IRIns *href = IR(store->op1);
IRIns *key = IR(href->op2);
if (irt_isnum(key->o == IR_KSLOT ? IR(key->op1)->t : key->t)) {
lim = ref; /* Conflicting store found, limits search for TLEN. */
break;
}
ref = store->prev;
}
/* Try to find a matching load. Below the conflicting store, if any. */
ref = J->chain[IR_TLEN];
while (ref > lim) {
IRIns *tlen = IR(ref);
if (tlen->op1 == tab)
return ref; /* Load forwarding. */
ref = tlen->prev;
}
return EMITFOLD; /* Otherwise we have a conflict or simply no match. */
}
/* -- ASTORE/HSTORE previous type analysis -------------------------------- */
/* Check whether the previous value for a table store is non-nil.
** This can be derived either from a previous store or from a previous
** load (because all loads from tables perform a type check).
**
** The result of the analysis can be used to avoid the metatable check
** and the guard against HREF returning niltv. Both of these are cheap,
** so let's not spend too much effort on the analysis.
**
** A result of 1 is exact: previous value CANNOT be nil.
** A result of 0 is inexact: previous value MAY be nil.
*/
int lj_opt_fwd_wasnonnil(jit_State *J, IROpT loadop, IRRef xref)
{
/* First check stores. */
IRRef ref = J->chain[loadop+IRDELTA_L2S];
while (ref > xref) {
IRIns *store = IR(ref);
if (store->op1 == xref) { /* Same xREF. */
/* A nil store MAY alias, but a non-nil store MUST alias. */
return !irt_isnil(store->t);
} else if (irt_isnil(store->t)) { /* Must check any nil store. */
IRRef skref = IR(store->op1)->op2;
IRRef xkref = IR(xref)->op2;
/* Same key type MAY alias. */
if (irt_sametype(IR(skref)->t, IR(xkref)->t)) {
if (skref == xkref || !irref_isk(skref) || !irref_isk(xkref))
return 0; /* A nil store with same const key or var key MAY alias. */
/* Different const keys CANNOT alias. */
} /* Different key types CANNOT alias. */
} /* Other non-nil stores MAY alias. */
ref = store->prev;
}
/* Check loads since nothing could be derived from stores. */
ref = J->chain[loadop];
while (ref > xref) {
IRIns *load = IR(ref);
if (load->op1 == xref) { /* Same xREF. */
/* A nil load MAY alias, but a non-nil load MUST alias. */
return !irt_isnil(load->t);
} /* Other non-nil loads MAY alias. */
ref = load->prev;
}
return 0; /* Nothing derived at all, previous value MAY be nil. */
}
/* ------------------------------------------------------------------------ */
#undef IR
#undef fins
#endif

430
src/lj_opt_narrow.c Normal file
View File

@ -0,0 +1,430 @@
/*
** NARROW: Narrowing of numbers to integers (double to int32_t).
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_opt_narrow_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_str.h"
#include "lj_bc.h"
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
#include "lj_trace.h"
/* Rationale for narrowing optimizations:
**
** Lua has only a single number type and this is a FP double by default.
** Narrowing doubles to integers does not pay off for the interpreter on a
** current-generation x86/x64 machine. Most FP operations need the same
** amount of execution resources as their integer counterparts, except
** with slightly longer latencies. Longer latencies are a non-issue for
** the interpreter, since they are usually hidden by other overhead.
**
** The total CPU execution bandwidth is the sum of the bandwidth of the FP
** and the integer units, because they execute in parallel. The FP units
** have an equal or higher bandwidth than the integer units. Not using
** them means losing execution bandwidth. Moving work away from them to
** the already quite busy integer units is a losing proposition.
**
** The situation for JIT-compiled code is a bit different: the higher code
** density makes the extra latencies much more visible. Tight loops expose
** the latencies for updating the induction variables. Array indexing
** requires narrowing conversions with high latencies and additional
** guards (to check that the index is really an integer). And many common
** optimizations only work on integers.
**
** One solution would be speculative, eager narrowing of all number loads.
** This causes many problems, like losing -0 or the need to resolve type
** mismatches between traces. It also effectively forces the integer type
** to have overflow-checking semantics. This impedes many basic
** optimizations and requires adding overflow checks to all integer
** arithmetic operations (whereas FP arithmetics can do without).
**
** Always replacing an FP op with an integer op plus an overflow check is
** counter-productive on a current-generation super-scalar CPU. Although
** the overflow check branches are highly predictable, they will clog the
** execution port for the branch unit and tie up reorder buffers. This is
** turning a pure data-flow dependency into a different data-flow
** dependency (with slightly lower latency) *plus* a control dependency.
** In general, you don't want to do this since latencies due to data-flow
** dependencies can be well hidden by out-of-order execution.
**
** A better solution is to keep all numbers as FP values and only narrow
** when it's beneficial to do so. LuaJIT uses predictive narrowing for
** induction variables and demand-driven narrowing for index expressions
** and bit operations. Additionally it can eliminate or hoists most of the
** resulting overflow checks. Regular arithmetic computations are never
** narrowed to integers.
**
** The integer type in the IR has convenient wrap-around semantics and
** ignores overflow. Extra operations have been added for
** overflow-checking arithmetic (ADDOV/SUBOV) instead of an extra type.
** Apart from reducing overall complexity of the compiler, this also
** nicely solves the problem where you want to apply algebraic
** simplifications to ADD, but not to ADDOV. And the assembler can use lea
** instead of an add for integer ADD, but not for ADDOV (lea does not
** affect the flags, but it helps to avoid register moves).
**
** Note that all of the above has to be reconsidered if LuaJIT is to be
** ported to architectures with slow FP operations or with no hardware FPU
** at all. In the latter case an integer-only port may be the best overall
** solution (if this still meets user demands).
*/
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
#define fins (&J->fold.ins)
/* Pass IR on to next optimization in chain (FOLD). */
#define emitir(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_opt_fold(J))
#define emitir_raw(ot, a, b) (lj_ir_set(J, (ot), (a), (b)), lj_ir_emit(J))
/* -- Elimination of narrowing type conversions --------------------------- */
/* Narrowing of index expressions and bit operations is demand-driven. The
** trace recorder emits a narrowing type conversion (TOINT or TOBIT) in
** all of these cases (e.g. array indexing or string indexing). FOLD
** already takes care of eliminating simple redundant conversions like
** TOINT(TONUM(x)) ==> x.
**
** But the surrounding code is FP-heavy and all arithmetic operations are
** performed on FP numbers. Consider a common example such as 'x=t[i+1]',
** with 'i' already an integer (due to induction variable narrowing). The
** index expression would be recorded as TOINT(ADD(TONUM(i), 1)), which is
** clearly suboptimal.
**
** One can do better by recursively backpropagating the narrowing type
** conversion across FP arithmetic operations. This turns FP ops into
** their corresponding integer counterparts. Depending on the semantics of
** the conversion they also need to check for overflow. Currently only ADD
** and SUB are supported.
**
** The above example can be rewritten as ADDOV(TOINT(TONUM(i)), 1) and
** then into ADDOV(i, 1) after folding of the conversions. The original FP
** ops remain in the IR and are eliminated by DCE since all references to
** them are gone.
**
** Special care has to be taken to avoid narrowing across an operation
** which is potentially operating on non-integral operands. One obvious
** case is when an expression contains a non-integral constant, but ends
** up as an integer index at runtime (like t[x+1.5] with x=0.5).
**
** Operations with two non-constant operands illustrate a similar problem
** (like t[a+b] with a=1.5 and b=2.5). Backpropagation has to stop there,
** unless it can be proven that either operand is integral (e.g. by CSEing
** a previous conversion). As a not-so-obvious corollary this logic also
** applies for a whole expression tree (e.g. t[(a+1)+(b+1)]).
**
** Correctness of the transformation is guaranteed by avoiding to expand
** the tree by adding more conversions than the one we would need to emit
** if not backpropagating. TOBIT employs a more optimistic rule, because
** the conversion has special semantics, designed to make the life of the
** compiler writer easier. ;-)
**
** Using on-the-fly backpropagation of an expression tree doesn't work
** because it's unknown whether the transform is correct until the end.
** This either requires IR rollback and cache invalidation for every
** subtree or a two-pass algorithm. The former didn't work out too well,
** so the code now combines a recursive collector with a stack-based
** emitter.
**
** [A recursive backpropagation algorithm with backtracking, employing
** skip-list lookup and round-robin caching, emitting stack operations
** on-the-fly for a stack-based interpreter -- and all of that in a meager
** kilobyte? Yep, compilers are a great treasure chest. Throw away your
** textbooks and read the codebase of a compiler today!]
**
** There's another optimization opportunity for array indexing: it's
** always accompanied by an array bounds-check. The outermost overflow
** check may be delegated to the ABC operation. This works because ABC is
** an unsigned comparison and wrap-around due to overflow creates negative
** numbers.
**
** But this optimization is only valid for constants that cannot overflow
** an int32_t into the range of valid array indexes [0..2^27+1). A check
** for +-2^30 is safe since -2^31 - 2^30 wraps to 2^30 and 2^31-1 + 2^30
** wraps to -2^30-1.
**
** It's also good enough in practice, since e.g. t[i+1] or t[i-10] are
** quite common. So the above example finally ends up as ADD(i, 1)!
**
** Later on, the assembler is able to fuse the whole array reference and
** the ADD into the memory operands of loads and other instructions. This
** is why LuaJIT is able to generate very pretty (and fast) machine code
** for array indexing. And that, my dear, concludes another story about
** one of the hidden secrets of LuaJIT ...
*/
/* Maximum backpropagation depth and maximum stack size. */
#define NARROW_MAX_BACKPROP 100
#define NARROW_MAX_STACK 256
/* Context used for narrowing of type conversions. */
typedef struct NarrowConv {
jit_State *J; /* JIT compiler state. */
IRRef2 *sp; /* Current stack pointer. */
IRRef2 *maxsp; /* Maximum stack pointer minus redzone. */
int lim; /* Limit on the number of emitted conversions. */
IRRef mode; /* Conversion mode (IRTOINT_*). */
IRRef2 stack[NARROW_MAX_STACK]; /* Stack holding the stack-machine code. */
} NarrowConv;
/* The stack machine has a 32 bit instruction format: [IROpT | IRRef1]
** The lower 16 bits hold a reference (or 0). The upper 16 bits hold
** the IR opcode + type or one of the following special opcodes:
*/
enum {
NARROW_REF, /* Push ref. */
NARROW_CONV, /* Push conversion of ref. */
NARROW_INT /* Push KINT ref. The next code holds an int32_t. */
};
/* Lookup a reference in the backpropagation cache. */
static IRRef narrow_bpc_get(jit_State *J, IRRef1 key, IRRef mode)
{
ptrdiff_t i;
for (i = 0; i < BPROP_SLOTS; i++) {
BPropEntry *bp = &J->bpropcache[i];
if (bp->key == key && bp->mode <= mode) /* Stronger checks are ok, too. */
return bp->val;
}
return 0;
}
/* Add an entry to the backpropagation cache. */
static void narrow_bpc_set(jit_State *J, IRRef1 key, IRRef1 val, IRRef mode)
{
uint32_t slot = J->bpropslot;
BPropEntry *bp = &J->bpropcache[slot];
J->bpropslot = (slot + 1) & (BPROP_SLOTS-1);
bp->key = key;
bp->val = val;
bp->mode = mode;
}
/* Backpropagate narrowing conversion. Return number of needed conversions. */
static int narrow_conv_backprop(NarrowConv *nc, IRRef ref, int depth)
{
jit_State *J = nc->J;
IRIns *ir = IR(ref);
IRRef cref;
/* Check the easy cases first. */
if (ir->o == IR_TONUM) { /* Undo inverse conversion. */
*nc->sp++ = IRREF2(ir->op1, NARROW_REF);
return 0;
} else if (ir->o == IR_KNUM) { /* Narrow FP constant. */
lua_Number n = ir_knum(ir)->n;
if (nc->mode == IRTOINT_TOBIT) { /* Allows a wider range of constants. */
int64_t k64 = (int64_t)n;
if (n == cast_num(k64)) { /* Only if constant doesn't lose precision. */
*nc->sp++ = IRREF2(0, NARROW_INT);
*nc->sp++ = (IRRef2)k64; /* But always truncate to 32 bits. */
return 0;
}
} else {
int32_t k = lj_num2int(n);
if (n == cast_num(k)) { /* Only if constant is really an integer. */
*nc->sp++ = IRREF2(0, NARROW_INT);
*nc->sp++ = (IRRef2)k;
return 0;
}
}
return 10; /* Never narrow other FP constants (this is rare). */
}
/* Try to CSE the conversion. Stronger checks are ok, too. */
for (cref = J->chain[fins->o]; cref > ref; cref = IR(cref)->prev)
if (IR(cref)->op1 == ref &&
irt_isguard(IR(cref)->t) >= irt_isguard(fins->t)) {
*nc->sp++ = IRREF2(cref, NARROW_REF);
return 0; /* Already there, no additional conversion needed. */
}
/* Backpropagate across ADD/SUB. */
if (ir->o == IR_ADD || ir->o == IR_SUB) {
/* Try cache lookup first. */
IRRef bpref, mode = nc->mode;
if (mode == IRTOINT_INDEX && depth > 0)
mode = IRTOINT_CHECK; /* Inner conversions need a stronger check. */
bpref = narrow_bpc_get(nc->J, (IRRef1)ref, mode);
if (bpref) {
*nc->sp++ = IRREF2(bpref, NARROW_REF);
return 0;
}
if (++depth < NARROW_MAX_BACKPROP && nc->sp < nc->maxsp) {
IRRef2 *savesp = nc->sp;
int count = narrow_conv_backprop(nc, ir->op1, depth);
count += narrow_conv_backprop(nc, ir->op2, depth);
if (count <= nc->lim) { /* Limit total number of conversions. */
*nc->sp++ = IRREF2(ref, IRTI(ir->o));
return count;
}
nc->sp = savesp; /* Too many conversions, need to backtrack. */
}
}
/* Otherwise add a conversion. */
*nc->sp++ = IRREF2(ref, NARROW_CONV);
return 1;
}
/* Emit the conversions collected during backpropagation. */
static IRRef narrow_conv_emit(jit_State *J, NarrowConv *nc)
{
/* The fins fields must be saved now -- emitir() overwrites them. */
IROpT guardot = irt_isguard(fins->t) ? IRTG(IR_ADDOV-IR_ADD, 0) : 0;
IROpT convot = fins->ot;
IRRef1 convop2 = fins->op2;
IRRef2 *next = nc->stack; /* List of instructions from backpropagation. */
IRRef2 *last = nc->sp;
IRRef2 *sp = nc->stack; /* Recycle the stack to store operands. */
while (next < last) { /* Simple stack machine to process the ins. list. */
IRRef2 ref = *next++;
IROpT op = ref >> 16;
if (op == NARROW_REF) {
*sp++ = ref;
} else if (op == NARROW_CONV) {
*sp++ = emitir_raw(convot, ref, convop2); /* Raw emit avoids a loop. */
} else if (op == NARROW_INT) {
lua_assert(next < last);
*sp++ = lj_ir_kint(J, *next++);
} else { /* Regular IROpT. Pops two operands and pushes one result. */
IRRef mode = nc->mode;
lua_assert(sp >= nc->stack+2);
sp--;
/* Omit some overflow checks for array indexing. See comments above. */
if (mode == IRTOINT_INDEX) {
if (next == last && irref_isk((IRRef1)sp[0]) &&
(uint32_t)IR((IRRef1)sp[0])->i + 0x40000000 < 0x80000000)
guardot = 0;
else
mode = IRTOINT_CHECK; /* Otherwise cache a stronger check. */
}
sp[-1] = emitir(op+guardot, sp[-1], sp[0]);
narrow_bpc_set(J, (IRRef1)ref, (IRRef1)sp[-1], mode); /* Add to cache. */
}
}
lua_assert(sp == nc->stack+1);
return nc->stack[0];
}
/* Narrow a type conversion of an arithmetic operation. */
TRef LJ_FASTCALL lj_opt_narrow_convert(jit_State *J)
{
if ((J->flags & JIT_F_OPT_NARROW)) {
NarrowConv nc;
nc.J = J;
nc.sp = nc.stack;
nc.maxsp = &nc.stack[NARROW_MAX_STACK-4];
if (fins->o == IR_TOBIT) {
nc.mode = IRTOINT_TOBIT; /* Used only in the backpropagation cache. */
nc.lim = 2; /* TOBIT can use a more optimistic rule. */
} else {
nc.mode = fins->op2;
nc.lim = 1;
}
if (narrow_conv_backprop(&nc, fins->op1, 0) <= nc.lim)
return narrow_conv_emit(J, &nc);
}
return NEXTFOLD;
}
/* -- Narrowing of arithmetic operators ----------------------------------- */
/* Check whether a number fits into an int32_t (-0 is ok, too). */
static int numisint(lua_Number n)
{
return (n == cast_num(lj_num2int(n)));
}
/* Narrowing of modulo operator. */
TRef lj_opt_narrow_mod(jit_State *J, TRef rb, TRef rc)
{
TRef tmp;
if ((J->flags & JIT_F_OPT_NARROW) &&
tref_isk(rc) && tref_isint(rc)) { /* Optimize x % k. */
int32_t k = IR(tref_ref(rc))->i;
if (k > 0 && (k & (k-1)) == 0) { /* i % 2^k ==> band(i, 2^k-1) */
if (tref_isint(rb))
return emitir(IRTI(IR_BAND), rb, lj_ir_kint(J, k-1));
}
}
/* b % c ==> b - floor(b/c)*c */
rb = lj_ir_tonum(J, rb);
rc = lj_ir_tonum(J, rc);
tmp = emitir(IRTN(IR_DIV), rb, rc);
tmp = emitir(IRTN(IR_FPMATH), tmp, IRFPM_FLOOR);
tmp = emitir(IRTN(IR_MUL), tmp, rc);
return emitir(IRTN(IR_SUB), rb, tmp);
}
/* Narrowing of power operator or math.pow. */
TRef lj_opt_narrow_pow(jit_State *J, TRef rb, TRef rc, TValue *vc)
{
lua_Number n;
if (tvisstr(vc) && !lj_str_numconv(strVdata(vc), vc))
lj_trace_err(J, LJ_TRERR_BADTYPE);
n = numV(vc);
/* Limit narrowing for pow to small exponents (or for two constants). */
if ((tref_isint(rc) && tref_isk(rc) && tref_isk(rb)) ||
((J->flags & JIT_F_OPT_NARROW) &&
(numisint(n) && n >= -65536.0 && n <= 65536.0))) {
TRef tmp;
if (!tref_isinteger(rc)) {
if (tref_isstr(rc))
rc = emitir(IRTG(IR_STRTO, IRT_NUM), rc, 0);
rc = emitir(IRTGI(IR_TOINT), rc, IRTOINT_CHECK); /* Guarded TOINT! */
}
if (!tref_isk(rc)) { /* Range guard: -65536 <= i <= 65536 */
tmp = emitir(IRTI(IR_ADD), rc, lj_ir_kint(J, 65536-2147483647-1));
emitir(IRTGI(IR_LE), tmp, lj_ir_kint(J, 2*65536-2147483647-1));
}
return emitir(IRTN(IR_POWI), rb, rc);
}
/* FOLD covers most cases, but some are easier to do here. */
if (tref_isk(rb) && tvispone(ir_knum(IR(tref_ref(rb)))))
return rb; /* 1 ^ x ==> 1 */
rc = lj_ir_tonum(J, rc);
if (tref_isk(rc) && ir_knum(IR(tref_ref(rc)))->n == 0.5)
return emitir(IRTN(IR_FPMATH), rb, IRFPM_SQRT); /* x ^ 0.5 ==> sqrt(x) */
/* Split up b^c into exp2(c*log2(b)). Assembler may rejoin later. */
rb = emitir(IRTN(IR_FPMATH), rb, IRFPM_LOG2);
rc = emitir(IRTN(IR_MUL), rb, rc);
return emitir(IRTN(IR_FPMATH), rc, IRFPM_EXP2);
}
/* -- Predictive narrowing of induction variables ------------------------- */
/* Narrow the FORL index type by looking at the runtime values. */
IRType lj_opt_narrow_forl(cTValue *forbase)
{
lua_assert(tvisnum(&forbase[FORL_IDX]) &&
tvisnum(&forbase[FORL_STOP]) &&
tvisnum(&forbase[FORL_STEP]));
/* Narrow only if the runtime values of start/stop/step are all integers. */
if (numisint(numV(&forbase[FORL_IDX])) &&
numisint(numV(&forbase[FORL_STOP])) &&
numisint(numV(&forbase[FORL_STEP]))) {
/* And if the loop index can't possibly overflow. */
lua_Number step = numV(&forbase[FORL_STEP]);
lua_Number sum = numV(&forbase[FORL_STOP]) + step;
if (0 <= step ? sum <= 2147483647.0 : sum >= -2147483648.0)
return IRT_INT;
}
return IRT_NUM;
}
#undef IR
#undef fins
#undef emitir
#undef emitir_raw
#endif

2198
src/lj_parse.c Normal file

File diff suppressed because it is too large Load Diff

15
src/lj_parse.h Normal file
View File

@ -0,0 +1,15 @@
/*
** Lua parser (source code -> bytecode).
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_PARSE_H
#define _LJ_PARSE_H
#include "lj_obj.h"
#include "lj_lex.h"
LJ_FUNC GCproto *lj_parse(LexState *ls);
LJ_FUNC GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t l);
#endif

2136
src/lj_record.c Normal file

File diff suppressed because it is too large Load Diff

17
src/lj_record.h Normal file
View File

@ -0,0 +1,17 @@
/*
** Trace recorder (bytecode -> SSA IR).
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_RECORD_H
#define _LJ_RECORD_H
#include "lj_obj.h"
#include "lj_jit.h"
#if LJ_HASJIT
LJ_FUNC void lj_record_ins(jit_State *J);
LJ_FUNC void lj_record_setup(jit_State *J);
#endif
#endif

286
src/lj_snap.c Normal file
View File

@ -0,0 +1,286 @@
/*
** Snapshot handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_snap_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_gc.h"
#include "lj_state.h"
#include "lj_frame.h"
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
#include "lj_trace.h"
#include "lj_snap.h"
#include "lj_target.h"
/* Some local macros to save typing. Undef'd at the end. */
#define IR(ref) (&J->cur.ir[(ref)])
/* -- Snapshot generation ------------------------------------------------- */
/* NYI: Snapshots are in need of a redesign. The current storage model for
** snapshot maps is too wasteful. They could be compressed (1D or 2D) and
** made more flexible at the same time. Iterators should no longer need to
** skip unmodified slots. IR_FRAME should be eliminated, too.
*/
/* Add all modified slots to the snapshot. */
static void snapshot_slots(jit_State *J, IRRef2 *map, BCReg nslots)
{
BCReg s;
for (s = 0; s < nslots; s++) {
IRRef ref = tref_ref(J->slot[s]);
if (ref) {
IRIns *ir = IR(ref);
if (ir->o == IR_SLOAD && ir->op1 == s && !(ir->op2 & IRSLOAD_INHERIT))
ref = 0;
}
map[s] = (IRRef2)ref;
}
}
/* Add frame links at the end of the snapshot. */
static MSize snapshot_framelinks(jit_State *J, IRRef2 *map)
{
cTValue *frame = J->L->base - 1;
cTValue *lim = J->L->base - J->baseslot;
MSize f = 0;
map[f++] = u32ptr(J->pc);
while (frame > lim) {
if (frame_islua(frame)) {
map[f++] = u32ptr(frame_pc(frame));
frame = frame_prevl(frame);
} else if (frame_ispcall(frame)) {
map[f++] = (uint32_t)frame_ftsz(frame);
frame = frame_prevd(frame);
} else if (frame_iscont(frame)) {
map[f++] = (uint32_t)frame_ftsz(frame);
map[f++] = u32ptr(frame_contpc(frame));
frame = frame_prevd(frame);
} else {
lua_assert(0);
}
}
return f;
}
/* Take a snapshot of the current stack. */
static void snapshot_stack(jit_State *J, SnapShot *snap, MSize nsnapmap)
{
BCReg nslots = J->baseslot + J->maxslot;
MSize nsm, nframelinks;
IRRef2 *p;
/* Conservative estimate. Continuation frames need 2 slots. */
nsm = nsnapmap + nslots + (uint32_t)J->framedepth*2+1;
if (LJ_UNLIKELY(nsm > J->sizesnapmap)) { /* Need to grow snapshot map? */
if (nsm < 2*J->sizesnapmap)
nsm = 2*J->sizesnapmap;
else if (nsm < 64)
nsm = 64;
J->snapmapbuf = (IRRef2 *)lj_mem_realloc(J->L, J->snapmapbuf,
J->sizesnapmap*sizeof(IRRef2), nsm*sizeof(IRRef2));
J->cur.snapmap = J->snapmapbuf;
J->sizesnapmap = nsm;
}
p = &J->cur.snapmap[nsnapmap];
snapshot_slots(J, p, nslots);
nframelinks = snapshot_framelinks(J, p + nslots);
J->cur.nsnapmap = (uint16_t)(nsnapmap + nslots + nframelinks);
snap->mapofs = (uint16_t)nsnapmap;
snap->ref = (IRRef1)J->cur.nins;
snap->nslots = (uint8_t)nslots;
snap->nframelinks = (uint8_t)nframelinks;
snap->count = 0;
}
/* Add or merge a snapshot. */
void lj_snap_add(jit_State *J)
{
MSize nsnap = J->cur.nsnap;
MSize nsnapmap = J->cur.nsnapmap;
/* Merge if no ins. inbetween or if requested and no guard inbetween. */
if (J->mergesnap ? !irt_isguard(J->guardemit) :
(nsnap > 0 && J->cur.snap[nsnap-1].ref == J->cur.nins)) {
nsnapmap = J->cur.snap[--nsnap].mapofs;
} else {
/* Need to grow snapshot buffer? */
if (LJ_UNLIKELY(nsnap >= J->sizesnap)) {
MSize maxsnap = (MSize)J->param[JIT_P_maxsnap];
if (nsnap >= maxsnap)
lj_trace_err(J, LJ_TRERR_SNAPOV);
lj_mem_growvec(J->L, J->snapbuf, J->sizesnap, maxsnap, SnapShot);
J->cur.snap = J->snapbuf;
}
J->cur.nsnap = (uint16_t)(nsnap+1);
}
J->mergesnap = 0;
J->guardemit.irt = 0;
snapshot_stack(J, &J->cur.snap[nsnap], nsnapmap);
}
/* Shrink last snapshot. */
void lj_snap_shrink(jit_State *J)
{
BCReg nslots = J->baseslot + J->maxslot;
SnapShot *snap = &J->cur.snap[J->cur.nsnap-1];
IRRef2 *oflinks = &J->cur.snapmap[snap->mapofs + snap->nslots];
IRRef2 *nflinks = &J->cur.snapmap[snap->mapofs + nslots];
uint32_t s, nframelinks = snap->nframelinks;
lua_assert(nslots < snap->nslots);
snap->nslots = (uint8_t)nslots;
J->cur.nsnapmap = (uint16_t)(snap->mapofs + nslots + nframelinks);
for (s = 0; s < nframelinks; s++) /* Move frame links down. */
nflinks[s] = oflinks[s];
}
/* -- Snapshot access ----------------------------------------------------- */
/* Initialize a Bloom Filter with all renamed refs.
** There are very few renames (often none), so the filter has
** very few bits set. This makes it suitable for negative filtering.
*/
static BloomFilter snap_renamefilter(Trace *T, SnapNo lim)
{
BloomFilter rfilt = 0;
IRIns *ir;
for (ir = &T->ir[T->nins-1]; ir->o == IR_RENAME; ir--)
if (ir->op2 <= lim)
bloomset(rfilt, ir->op1);
return rfilt;
}
/* Process matching renames to find the original RegSP. */
static RegSP snap_renameref(Trace *T, SnapNo lim, IRRef ref, RegSP rs)
{
IRIns *ir;
for (ir = &T->ir[T->nins-1]; ir->o == IR_RENAME; ir--)
if (ir->op1 == ref && ir->op2 <= lim)
rs = ir->prev;
return rs;
}
/* Convert a snapshot into a linear slot -> RegSP map. */
void lj_snap_regspmap(uint16_t *rsmap, Trace *T, SnapNo snapno)
{
SnapShot *snap = &T->snap[snapno];
BCReg s, nslots = snap->nslots;
IRRef2 *map = &T->snapmap[snap->mapofs];
BloomFilter rfilt = snap_renamefilter(T, snapno);
for (s = 0; s < nslots; s++) {
IRRef ref = snap_ref(map[s]);
if (!irref_isk(ref)) {
IRIns *ir = &T->ir[ref];
uint32_t rs = ir->prev;
if (bloomtest(rfilt, ref))
rs = snap_renameref(T, snapno, ref, rs);
rsmap[s] = (uint16_t)rs;
}
}
}
/* Restore interpreter state from exit state with the help of a snapshot. */
void lj_snap_restore(jit_State *J, void *exptr)
{
ExitState *ex = (ExitState *)exptr;
SnapNo snapno = J->exitno; /* For now, snapno == exitno. */
Trace *T = J->trace[J->parent];
SnapShot *snap = &T->snap[snapno];
BCReg s, nslots = snap->nslots;
IRRef2 *map = &T->snapmap[snap->mapofs];
IRRef2 *flinks = map + nslots + snap->nframelinks;
TValue *o, *newbase, *ntop;
BloomFilter rfilt = snap_renamefilter(T, snapno);
lua_State *L = J->L;
/* Make sure the stack is big enough for the slots from the snapshot. */
if (L->base + nslots >= L->maxstack) {
L->top = curr_topL(L);
lj_state_growstack(L, nslots - curr_proto(L)->framesize);
}
/* Fill stack slots with data from the registers and spill slots. */
newbase = NULL;
ntop = L->base;
for (s = 0, o = L->base-1; s < nslots; s++, o++) {
IRRef ref = snap_ref(map[s]);
if (ref) {
IRIns *ir = &T->ir[ref];
if (irref_isk(ref)) { /* Restore constant slot. */
lj_ir_kvalue(L, o, ir);
} else {
IRType1 t = ir->t;
RegSP rs = ir->prev;
if (LJ_UNLIKELY(bloomtest(rfilt, ref)))
rs = snap_renameref(T, snapno, ref, rs);
if (ra_hasspill(regsp_spill(rs))) { /* Restore from spill slot. */
int32_t *sps = &ex->spill[regsp_spill(rs)];
if (irt_isinteger(t)) {
setintV(o, *sps);
} else if (irt_isnum(t)) {
o->u64 = *(uint64_t *)sps;
} else {
lua_assert(!irt_ispri(t)); /* PRI refs never have a spill slot. */
setgcrefi(o->gcr, *sps);
setitype(o, irt_toitype(t));
}
} else if (ra_hasreg(regsp_reg(rs))) { /* Restore from register. */
Reg r = regsp_reg(rs);
if (irt_isinteger(t)) {
setintV(o, ex->gpr[r-RID_MIN_GPR]);
} else if (irt_isnum(t)) {
setnumV(o, ex->fpr[r-RID_MIN_FPR]);
} else {
if (!irt_ispri(t))
setgcrefi(o->gcr, ex->gpr[r-RID_MIN_GPR]);
setitype(o, irt_toitype(t));
}
} else { /* Restore frame slot. */
lua_assert(ir->o == IR_FRAME);
/* This works for both PTR and FUNC IR_FRAME. */
setgcrefp(o->fr.func, mref(T->ir[ir->op2].ptr, void));
if (s != 0) /* Do not overwrite link to previous frame. */
o->fr.tp.ftsz = (int32_t)*--flinks;
if (irt_isfunc(ir->t)) {
GCfunc *fn = gco2func(gcref(T->ir[ir->op2].gcr));
if (isluafunc(fn)) {
TValue *fs;
newbase = o+1;
fs = newbase + funcproto(fn)->framesize;
if (fs > ntop) ntop = fs; /* Update top for newly added frames. */
}
}
}
}
} else if (newbase) {
setnilV(o); /* Clear unreferenced slots of newly added frames. */
}
}
if (newbase) { /* Clear remainder of newly added frames. */
L->base = newbase;
if (ntop >= L->maxstack) { /* Need to grow the stack again. */
MSize need = (MSize)(ntop - o);
L->top = o;
lj_state_growstack(L, need);
o = L->top;
ntop = o + need;
}
L->top = curr_topL(L);
for (; o < ntop; o++)
setnilV(o);
} else { /* Must not clear slots of existing frame. */
L->top = curr_topL(L);
}
lua_assert(map + nslots == flinks-1);
J->pc = (const BCIns *)(uintptr_t)(*--flinks);
}
#undef IR
#endif

19
src/lj_snap.h Normal file
View File

@ -0,0 +1,19 @@
/*
** Snapshot handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_SNAP_H
#define _LJ_SNAP_H
#include "lj_obj.h"
#include "lj_jit.h"
#if LJ_HASJIT
LJ_FUNC void lj_snap_add(jit_State *J);
LJ_FUNC void lj_snap_shrink(jit_State *J);
LJ_FUNC void lj_snap_regspmap(uint16_t *rsmap, Trace *T, SnapNo snapno);
LJ_FUNC void lj_snap_restore(jit_State *J, void *exptr);
#endif
#endif

255
src/lj_state.c Normal file
View File

@ -0,0 +1,255 @@
/*
** State and stack handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_state_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_func.h"
#include "lj_meta.h"
#include "lj_state.h"
#include "lj_frame.h"
#include "lj_trace.h"
#include "lj_dispatch.h"
#include "lj_vm.h"
#include "lj_lex.h"
#include "lj_alloc.h"
/* -- Stack handling ------------------------------------------------------ */
/* Stack sizes. */
#define LJ_STACK_MIN LUA_MINSTACK /* Min. stack size. */
#define LJ_STACK_MAX LUAI_MAXSTACK /* Max. stack size. */
#define LJ_STACK_START (2*LJ_STACK_MIN) /* Starting stack size. */
#define LJ_STACK_MAXEX (LJ_STACK_MAX + 1 + LJ_STACK_EXTRA)
/* Explanation of LJ_STACK_EXTRA:
**
** Calls to metamethods store their arguments beyond the current top
** without checking for the stack limit. This avoids stack resizes which
** would invalidate passed TValue pointers. The stack check is performed
** later by the call gate. This can safely resize the stack or raise an
** error. Thus we need some extra slots beyond the current stack limit.
**
** Most metamethods need 4 slots above top (cont, mobj, arg1, arg2) plus
** one extra slot if mobj is not a function. Only lj_meta_tset needs 5
** slots above top, but then mobj is always a function. So we can get by
** with 5 extra slots.
*/
/* Resize stack slots and adjust pointers in state. */
static void resizestack(lua_State *L, MSize n)
{
TValue *oldst = L->stack;
ptrdiff_t delta;
MSize realsize = n + 1 + LJ_STACK_EXTRA;
GCobj *up;
lua_assert((MSize)(L->maxstack-L->stack) == L->stacksize-LJ_STACK_EXTRA-1);
lj_mem_reallocvec(L, L->stack, L->stacksize, realsize, TValue);
delta = (char *)L->stack - (char *)oldst;
L->maxstack = L->stack + n;
L->stacksize = realsize;
L->base = (TValue *)((char *)L->base + delta);
L->top = (TValue *)((char *)L->top + delta);
for (up = gcref(L->openupval); up != NULL; up = gcnext(up))
gco2uv(up)->v = (TValue *)((char *)gco2uv(up)->v + delta);
if (obj2gco(L) == gcref(G(L)->jit_L))
setmref(G(L)->jit_base, mref(G(L)->jit_base, char) + delta);
}
/* Relimit stack after error, in case the limit was overdrawn. */
void lj_state_relimitstack(lua_State *L)
{
if (L->stacksize > LJ_STACK_MAXEX && L->top - L->stack < LJ_STACK_MAX-1)
resizestack(L, LJ_STACK_MAX);
}
/* Try to shrink the stack (called from GC). */
void lj_state_shrinkstack(lua_State *L, MSize used)
{
if (L->stacksize > LJ_STACK_MAXEX)
return; /* Avoid stack shrinking while handling stack overflow. */
if (4*used < L->stacksize &&
2*(LJ_STACK_START+LJ_STACK_EXTRA) < L->stacksize &&
obj2gco(L) != gcref(G(L)->jit_L)) /* Don't shrink stack of live trace. */
resizestack(L, L->stacksize >> 1);
}
/* Try to grow stack. */
void lj_state_growstack(lua_State *L, MSize need)
{
if (L->stacksize > LJ_STACK_MAXEX) /* overflow while handling overflow? */
lj_err_throw(L, LUA_ERRERR);
resizestack(L, L->stacksize + (need > L->stacksize ? need : L->stacksize));
if (L->stacksize > LJ_STACK_MAXEX) {
if (curr_funcisL(L)) { /* Clear slots of incomplete Lua frame. */
TValue *top = curr_topL(L);
while (--top >= L->top) setnilV(top);
}
lj_err_msg(L, LJ_ERR_STKOV); /* ... to allow L->top = curr_topL(L). */
}
}
void lj_state_growstack1(lua_State *L)
{
lj_state_growstack(L, 1);
}
/* Allocate basic stack for new state. */
static void stack_init(lua_State *L1, lua_State *L)
{
L1->stack = lj_mem_newvec(L, LJ_STACK_START + LJ_STACK_EXTRA, TValue);
L1->stacksize = LJ_STACK_START + LJ_STACK_EXTRA;
L1->top = L1->stack;
L1->maxstack = L1->stack+(L1->stacksize - LJ_STACK_EXTRA)-1;
setthreadV(L1, L1->top, L1); /* needed for curr_funcisL() on empty stack */
setnilV(L1->top); /* but clear its type */
L1->base = ++L1->top;
}
/* -- State handling ------------------------------------------------------ */
/* Open parts that may cause memory-allocation errors. */
static TValue *cpluaopen(lua_State *L, lua_CFunction dummy, void *ud)
{
global_State *g = G(L);
UNUSED(dummy);
UNUSED(ud);
stack_init(L, L);
/* NOBARRIER: State initialization, all objects are white. */
setgcref(L->env, obj2gco(lj_tab_new(L, 0, LJ_MIN_GLOBAL)));
settabV(L, registry(L), lj_tab_new(L, 0, LJ_MIN_REGISTRY));
lj_str_resize(L, LJ_MIN_STRTAB-1);
lj_meta_init(L);
lj_lex_init(L);
fixstring(lj_err_str(L, LJ_ERR_ERRMEM)); /* Preallocate memory error msg. */
g->gc.threshold = 4*g->gc.total;
return NULL;
}
static void close_state(lua_State *L)
{
global_State *g = G(L);
#ifndef LUAJIT_USE_SYSMALLOC
if (g->allocf == lj_alloc_f) {
lj_alloc_destroy(g->allocd);
} else
#endif
{
lj_func_closeuv(L, L->stack);
lj_gc_freeall(g);
lua_assert(gcref(g->gc.root) == obj2gco(L));
lua_assert(g->strnum == 0);
lj_trace_freestate(g);
lj_mem_freevec(g, g->strhash, g->strmask+1, GCstr *);
lj_str_freebuf(g, &g->tmpbuf);
lj_mem_freevec(g, L->stack, L->stacksize, TValue);
lua_assert(g->gc.total == sizeof(GG_State));
g->allocf(g->allocd, G2GG(g), sizeof(GG_State), 0);
}
}
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
{
GG_State *GG = cast(GG_State *, f(ud, NULL, 0, sizeof(GG_State)));
lua_State *L = &GG->L;
global_State *g = &GG->g;
if (GG == NULL) return NULL;
memset(GG, 0, sizeof(GG_State));
L->gct = ~LJ_TTHREAD;
L->marked = LJ_GC_WHITE0 | LJ_GC_FIXED | LJ_GC_SFIXED; /* Prevent free. */
L->dummy_ffid = FF_C;
setmref(L->glref, g);
g->gc.currentwhite = LJ_GC_WHITE0 | LJ_GC_FIXED;
g->allocf = f;
g->allocd = ud;
setgcref(g->mainthref, obj2gco(L));
setgcref(g->uvhead.prev, obj2gco(&g->uvhead));
setgcref(g->uvhead.next, obj2gco(&g->uvhead));
g->strmask = ~(MSize)0;
setnilV(registry(L));
setnilV(&g->nilnode.val);
setnilV(&g->nilnode.key);
lj_str_initbuf(L, &g->tmpbuf);
g->gc.state = GCSpause;
setgcref(g->gc.root, obj2gco(L));
g->gc.sweep = &g->gc.root;
g->gc.total = sizeof(GG_State);
g->gc.pause = LUAI_GCPAUSE;
g->gc.stepmul = LUAI_GCMUL;
lj_dispatch_init((GG_State *)L);
L->status = LUA_ERRERR+1; /* Avoid touching the stack upon memory error. */
if (lj_vm_cpcall(L, cpluaopen, NULL, NULL) != 0) {
/* Memory allocation error: free partial state. */
close_state(L);
return NULL;
}
L->status = 0;
return L;
}
static TValue *cpfinalize(lua_State *L, lua_CFunction dummy, void *ud)
{
UNUSED(dummy);
UNUSED(ud);
lj_gc_finalizeudata(L);
/* Frame pop omitted. */
return NULL;
}
LUA_API void lua_close(lua_State *L)
{
global_State *g = G(L);
L = mainthread(g); /* Only the main thread can be closed. */
lj_func_closeuv(L, L->stack);
lj_gc_separateudata(g, 1); /* Separate udata which have GC metamethods. */
#if LJ_HASJIT
G2J(g)->flags &= ~JIT_F_ON;
G2J(g)->state = LJ_TRACE_IDLE;
lj_dispatch_update(g);
#endif
do {
hook_enter(g);
L->status = 0;
L->cframe = NULL;
L->base = L->top = L->stack + 1;
} while (lj_vm_cpcall(L, cpfinalize, NULL, NULL) != 0);
close_state(L);
}
lua_State *lj_state_new(lua_State *L)
{
lua_State *L1 = lj_mem_newobj(L, lua_State);
L1->gct = ~LJ_TTHREAD;
L1->dummy_ffid = FF_C;
L1->status = 0;
L1->stacksize = 0;
L1->stack = NULL;
L1->cframe = NULL;
/* NOBARRIER: The lua_State is new (marked white). */
setgcrefnull(L1->openupval);
setmrefr(L1->glref, L->glref);
setgcrefr(L1->env, L->env);
stack_init(L1, L); /* init stack */
lua_assert(iswhite(obj2gco(L1)));
return L1;
}
void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L)
{
lua_assert(L != mainthread(g));
lj_func_closeuv(L, L->stack);
lua_assert(gcref(L->openupval) == NULL);
lj_mem_freevec(g, L->stack, L->stacksize, TValue);
lj_mem_freet(g, L);
}

31
src/lj_state.h Normal file
View File

@ -0,0 +1,31 @@
/*
** State and stack handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_STATE_H
#define _LJ_STATE_H
#include "lj_obj.h"
#define incr_top(L) \
(++L->top >= L->maxstack && (lj_state_growstack1(L), 0))
#define savestack(L, p) ((char *)(p) - (char *)L->stack)
#define restorestack(L, n) ((TValue *)((char *)L->stack + (n)))
LJ_FUNC void lj_state_relimitstack(lua_State *L);
LJ_FUNC void lj_state_shrinkstack(lua_State *L, MSize used);
LJ_FUNCA void lj_state_growstack(lua_State *L, MSize need);
LJ_FUNCA void lj_state_growstack1(lua_State *L);
static LJ_AINLINE void lj_state_checkstack(lua_State *L, MSize need)
{
if ((MSize)((char *)L->maxstack-(char *)L->top) <= need*(MSize)sizeof(TValue))
lj_state_growstack(L, need);
}
LJ_FUNC lua_State *lj_state_new(lua_State *L);
LJ_FUNC void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L);
#endif

Some files were not shown because too many files have changed in this diff Show More