mirror of
https://github.com/wg/wrk
synced 2026-06-10 09:35:26 +08:00
Compare commits
31 Commits
+3
-1
@@ -1 +1,3 @@
|
||||
obj/*
|
||||
*.o
|
||||
*.a
|
||||
wrk
|
||||
|
||||
@@ -1,32 +1,64 @@
|
||||
CFLAGS := -std=c99 -Wall -O2 -pthread
|
||||
LDFLAGS := -pthread
|
||||
LIBS := -lm
|
||||
CFLAGS := -std=c99 -Wall -O2 -D_REENTRANT
|
||||
LIBS := -lpthread -lm -lcrypto -lssl
|
||||
|
||||
SRC := wrk.c aprintf.c stats.c units.c ae.c zmalloc.c http_parser.c tinymt64.c
|
||||
TARGET := $(shell uname -s | tr [A-Z] [a-z] 2>/dev/null || echo unknown)
|
||||
|
||||
ifeq ($(TARGET), sunos)
|
||||
CFLAGS += -D_PTHREADS -D_POSIX_C_SOURCE=200112L
|
||||
LIBS += -lsocket
|
||||
else ifeq ($(TARGET), darwin)
|
||||
LDFLAGS += -pagezero_size 10000 -image_base 100000000
|
||||
else ifeq ($(TARGET), linux)
|
||||
LIBS += -ldl
|
||||
LDFLAGS += -Wl,-E
|
||||
else ifeq ($(TARGET), freebsd)
|
||||
CFLAGS += -D_DECLARE_C99_LDBL_MATH
|
||||
LDFLAGS += -Wl,-E
|
||||
endif
|
||||
|
||||
SRC := wrk.c net.c ssl.c aprintf.c stats.c script.c units.c \
|
||||
ae.c zmalloc.c http_parser.c tinymt64.c
|
||||
BIN := wrk
|
||||
|
||||
ODIR := obj
|
||||
OBJ := $(patsubst %.c,$(ODIR)/%.o,$(SRC))
|
||||
|
||||
LDIR = deps/luajit/src
|
||||
LIBS := -lluajit $(LIBS)
|
||||
CFLAGS += -I $(LDIR)
|
||||
LDFLAGS += -L $(LDIR)
|
||||
|
||||
all: $(BIN)
|
||||
|
||||
clean:
|
||||
$(RM) $(BIN) obj/*
|
||||
@$(MAKE) -C deps/luajit clean
|
||||
|
||||
$(BIN): $(OBJ)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(BIN): $(OBJ) $(ODIR)/bytecode.o
|
||||
@echo LINK $(BIN)
|
||||
@$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
$(OBJ): config.h Makefile | $(ODIR)
|
||||
|
||||
$(ODIR):
|
||||
@mkdir $@
|
||||
$(ODIR): $(LDIR)/libluajit.a
|
||||
@mkdir -p $@
|
||||
|
||||
$(ODIR)/bytecode.o: scripts/wrk.lua
|
||||
@echo LUAJIT $<
|
||||
@$(SHELL) -c 'cd $(LDIR) && ./luajit -b $(CURDIR)/$< $(CURDIR)/$@'
|
||||
|
||||
$(ODIR)/%.o : %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
@echo CC $<
|
||||
@$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
$(LDIR)/libluajit.a:
|
||||
@echo Building LuaJit...
|
||||
@$(MAKE) -C $(LDIR) BUILDMODE=static
|
||||
|
||||
.PHONY: all clean
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .o
|
||||
.SUFFIXES: .c .o .lua
|
||||
|
||||
vpath %.c src
|
||||
vpath %.h src
|
||||
vpath %.c src
|
||||
vpath %.h src
|
||||
vpath %.lua scripts
|
||||
|
||||
@@ -14,7 +14,9 @@ This product includes software developed by Salvatore Sanfilippo and
|
||||
other contributors to the redis project.
|
||||
|
||||
Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
|
||||
Copyright (C) 2009 Harish Mallipeddi - harish.mallipeddi@gmail.com
|
||||
Copyright (c) 2012, Joyent, Inc. All rights reserved.
|
||||
|
||||
Copyright (c) 2006-2009, Salvatore Sanfilippo
|
||||
All rights reserved.
|
||||
@@ -79,6 +81,32 @@ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
=========================================================================
|
||||
== LuaJIT Notice ==
|
||||
=========================================================================
|
||||
|
||||
LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/
|
||||
|
||||
Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
=========================================================================
|
||||
== Tiny Mersenne Twister (TinyMT) Notice ==
|
||||
=========================================================================
|
||||
|
||||
@@ -4,23 +4,84 @@ wrk - a HTTP benchmarking tool
|
||||
load when run on a single multi-core CPU. It combines a multithreaded
|
||||
design with scalable event notification systems such as epoll and kqueue.
|
||||
|
||||
An optional LuaJIT script can perform HTTP request generation, response
|
||||
processing, and custom reporting.
|
||||
|
||||
Basic Usage
|
||||
|
||||
wrk -t8 -c400 -r10m http://localhost:8080/index.html
|
||||
wrk -t12 -c400 -d30s http://127.0.0.1:8080/index.html
|
||||
|
||||
This runs wrk with 8 threads, keeping 400 connections open, and making a
|
||||
total of 10 million HTTP GET requests to http://localhost:8080/index.html
|
||||
This runs a benchmark for 30 seconds, using 12 threads, and keeping
|
||||
400 HTTP connections open.
|
||||
|
||||
Output:
|
||||
|
||||
Making 10000000 requests to http://localhost:8080/index.html
|
||||
8 threads and 400 connections
|
||||
Running 30s test @ http://127.0.0.1:8080/index.html
|
||||
12 threads and 400 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 439.75us 350.49us 7.60ms 92.88%
|
||||
Req/Sec 61.13k 8.26k 72.00k 87.54%
|
||||
10000088 requests in 19.87s, 3.42GB read
|
||||
Requests/sec: 503396.23
|
||||
Transfer/sec: 176.16MB
|
||||
Latency 635.91us 0.89ms 12.92ms 93.69%
|
||||
Req/Sec 56.20k 8.07k 62.00k 86.54%
|
||||
22464657 requests in 30.00s, 17.76GB read
|
||||
Requests/sec: 748868.53
|
||||
Transfer/sec: 606.33MB
|
||||
|
||||
Scripting
|
||||
|
||||
wrk's public Lua API is:
|
||||
|
||||
init = function(args)
|
||||
request = function()
|
||||
response = function(status, headers, body)
|
||||
done = function(summary, latency, requests)
|
||||
|
||||
wrk = {
|
||||
scheme = "http",
|
||||
host = "localhost",
|
||||
port = nil,
|
||||
method = "GET",
|
||||
path = "/",
|
||||
headers = {},
|
||||
body = nil
|
||||
}
|
||||
|
||||
function wrk.format(method, path, headers, body)
|
||||
|
||||
wrk.format returns a HTTP request string containing the passed
|
||||
parameters merged with values from the wrk table.
|
||||
|
||||
global init -- function called when the thread is initialized
|
||||
global request -- function returning the HTTP message for each request
|
||||
global response -- optional function called with HTTP response data
|
||||
global done -- optional function called with results of run
|
||||
|
||||
The init() function receives any extra command line arguments for the
|
||||
script. Script arguments must be separated from wrk arguments with "--"
|
||||
and scripts that override init() but not request() must call wrk.init()
|
||||
|
||||
The done() function receives a table containing result data, and two
|
||||
statistics objects representing the sampled per-request latency and
|
||||
per-thread request rate. Duration and latency are microsecond values
|
||||
and rate is measured in requests per second.
|
||||
|
||||
latency.min -- minimum value seen
|
||||
latency.max -- maximum value seen
|
||||
latency.mean -- average value seen
|
||||
latency.stdev -- standard deviation
|
||||
latency:percentile(99.0) -- 99th percentile value
|
||||
latency[i] -- raw sample value
|
||||
|
||||
summary = {
|
||||
duration = N, -- run duration in microseconds
|
||||
requests = N, -- total completed requests
|
||||
bytes = N, -- total bytes received
|
||||
errors = {
|
||||
connect = N, -- total socket connection errors
|
||||
read = N, -- total socket read errors
|
||||
write = N, -- total socket write errors
|
||||
status = N, -- total HTTP status codes > 399
|
||||
timeout = N -- total request timeouts
|
||||
}
|
||||
}
|
||||
|
||||
Benchmarking Tips
|
||||
|
||||
@@ -29,9 +90,16 @@ Benchmarking Tips
|
||||
initial connection burst the server's listen(2) backlog should be greater
|
||||
than the number of concurrent connections being tested.
|
||||
|
||||
A user script that only changes the HTTP method, path, adds headers or
|
||||
a body, will have no performance impact. If multiple HTTP requests are
|
||||
necessary they should be pre-generated and returned via a quick lookup in
|
||||
the request() call. Per-request actions, particularly building a new HTTP
|
||||
request, and use of response() will necessarily reduce the amount of load
|
||||
that can be generated.
|
||||
|
||||
Acknowledgements
|
||||
|
||||
wrk contains code from a number of open source projects including the
|
||||
'ae' event loop from redis, the nginx/joyent/node.js 'http-parser' and
|
||||
the Tiny Mersenne Twister PRNG. Please consult the NOTICE file for
|
||||
licensing details.
|
||||
'ae' event loop from redis, the nginx/joyent/node.js 'http-parser',
|
||||
Mike Pall's LuaJIT, and the Tiny Mersenne Twister PRNG. Please consult
|
||||
the NOTICE file for licensing details.
|
||||
|
||||
Vendored
+56
@@ -0,0 +1,56 @@
|
||||
===============================================================================
|
||||
LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/
|
||||
|
||||
Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
[ MIT license: http://www.opensource.org/licenses/mit-license.php ]
|
||||
|
||||
===============================================================================
|
||||
[ LuaJIT includes code from Lua 5.1/5.2, which has this license statement: ]
|
||||
|
||||
Copyright (C) 1994-2012 Lua.org, PUC-Rio.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
===============================================================================
|
||||
[ LuaJIT includes code from dlmalloc, which has this license statement: ]
|
||||
|
||||
This is a version (aka dlmalloc) of malloc/free/realloc written by
|
||||
Doug Lea and released to the public domain, as explained at
|
||||
http://creativecommons.org/licenses/publicdomain
|
||||
|
||||
===============================================================================
|
||||
Vendored
+149
@@ -0,0 +1,149 @@
|
||||
##############################################################################
|
||||
# LuaJIT top level Makefile for installation. Requires GNU Make.
|
||||
#
|
||||
# Please read doc/install.html before changing any variables!
|
||||
#
|
||||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
||||
# Note: src/Makefile has many more configurable options.
|
||||
#
|
||||
# ##### This Makefile is NOT useful for Windows! #####
|
||||
# For MSVC, please follow the instructions given in src/msvcbuild.bat.
|
||||
# For MinGW and Cygwin, cd to src and run make with the Makefile there.
|
||||
#
|
||||
# Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
##############################################################################
|
||||
|
||||
MAJVER= 2
|
||||
MINVER= 0
|
||||
RELVER= 2
|
||||
VERSION= $(MAJVER).$(MINVER).$(RELVER)
|
||||
ABIVER= 5.1
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Change the installation path as needed. This automatically adjusts
|
||||
# the paths in src/luaconf.h, too. Note: PREFIX must be an absolute path!
|
||||
#
|
||||
export PREFIX= /usr/local
|
||||
##############################################################################
|
||||
|
||||
DPREFIX= $(DESTDIR)$(PREFIX)
|
||||
INSTALL_BIN= $(DPREFIX)/bin
|
||||
INSTALL_LIB= $(DPREFIX)/lib
|
||||
INSTALL_SHARE= $(DPREFIX)/share
|
||||
INSTALL_INC= $(DPREFIX)/include/luajit-$(MAJVER).$(MINVER)
|
||||
|
||||
INSTALL_LJLIBD= $(INSTALL_SHARE)/luajit-$(VERSION)
|
||||
INSTALL_JITLIB= $(INSTALL_LJLIBD)/jit
|
||||
INSTALL_LMODD= $(INSTALL_SHARE)/lua
|
||||
INSTALL_LMOD= $(INSTALL_LMODD)/$(ABIVER)
|
||||
INSTALL_CMODD= $(INSTALL_LIB)/lua
|
||||
INSTALL_CMOD= $(INSTALL_CMODD)/$(ABIVER)
|
||||
INSTALL_MAN= $(INSTALL_SHARE)/man/man1
|
||||
INSTALL_PKGCONFIG= $(INSTALL_LIB)/pkgconfig
|
||||
|
||||
INSTALL_TNAME= luajit-$(VERSION)
|
||||
INSTALL_TSYMNAME= luajit
|
||||
INSTALL_ANAME= libluajit-$(ABIVER).a
|
||||
INSTALL_SONAME= libluajit-$(ABIVER).so.$(MAJVER).$(MINVER).$(RELVER)
|
||||
INSTALL_SOSHORT= libluajit-$(ABIVER).so
|
||||
INSTALL_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).$(MINVER).$(RELVER).dylib
|
||||
INSTALL_DYLIBSHORT1= libluajit-$(ABIVER).dylib
|
||||
INSTALL_DYLIBSHORT2= libluajit-$(ABIVER).$(MAJVER).dylib
|
||||
INSTALL_PCNAME= luajit.pc
|
||||
|
||||
INSTALL_STATIC= $(INSTALL_LIB)/$(INSTALL_ANAME)
|
||||
INSTALL_DYN= $(INSTALL_LIB)/$(INSTALL_SONAME)
|
||||
INSTALL_SHORT1= $(INSTALL_LIB)/$(INSTALL_SOSHORT)
|
||||
INSTALL_SHORT2= $(INSTALL_LIB)/$(INSTALL_SOSHORT)
|
||||
INSTALL_T= $(INSTALL_BIN)/$(INSTALL_TNAME)
|
||||
INSTALL_TSYM= $(INSTALL_BIN)/$(INSTALL_TSYMNAME)
|
||||
INSTALL_PC= $(INSTALL_PKGCONFIG)/$(INSTALL_PCNAME)
|
||||
|
||||
INSTALL_DIRS= $(INSTALL_BIN) $(INSTALL_LIB) $(INSTALL_INC) $(INSTALL_MAN) \
|
||||
$(INSTALL_PKGCONFIG) $(INSTALL_JITLIB) $(INSTALL_LMOD) $(INSTALL_CMOD)
|
||||
UNINSTALL_DIRS= $(INSTALL_JITLIB) $(INSTALL_LJLIBD) $(INSTALL_INC) \
|
||||
$(INSTALL_LMOD) $(INSTALL_LMODD) $(INSTALL_CMOD) $(INSTALL_CMODD)
|
||||
|
||||
RM= rm -f
|
||||
MKDIR= mkdir -p
|
||||
RMDIR= rmdir 2>/dev/null
|
||||
SYMLINK= ln -sf
|
||||
INSTALL_X= install -m 0755
|
||||
INSTALL_F= install -m 0644
|
||||
UNINSTALL= $(RM)
|
||||
LDCONFIG= ldconfig -n
|
||||
SED_PC= sed -e "s|^prefix=.*|prefix=$(PREFIX)|"
|
||||
|
||||
FILE_T= luajit
|
||||
FILE_A= libluajit.a
|
||||
FILE_SO= libluajit.so
|
||||
FILE_MAN= luajit.1
|
||||
FILE_PC= luajit.pc
|
||||
FILES_INC= lua.h lualib.h lauxlib.h luaconf.h lua.hpp luajit.h
|
||||
FILES_JITLIB= bc.lua v.lua dump.lua dis_x86.lua dis_x64.lua dis_arm.lua \
|
||||
dis_ppc.lua dis_mips.lua dis_mipsel.lua bcsave.lua vmdef.lua
|
||||
|
||||
ifeq (,$(findstring Windows,$(OS)))
|
||||
ifeq (Darwin,$(shell uname -s))
|
||||
INSTALL_SONAME= $(INSTALL_DYLIBNAME)
|
||||
INSTALL_SHORT1= $(INSTALL_LIB)/$(INSTALL_DYLIBSHORT1)
|
||||
INSTALL_SHORT2= $(INSTALL_LIB)/$(INSTALL_DYLIBSHORT2)
|
||||
LDCONFIG= :
|
||||
endif
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
|
||||
INSTALL_DEP= src/luajit
|
||||
|
||||
default all $(INSTALL_DEP):
|
||||
@echo "==== Building LuaJIT $(VERSION) ===="
|
||||
$(MAKE) -C src
|
||||
@echo "==== Successfully built LuaJIT $(VERSION) ===="
|
||||
|
||||
install: $(INSTALL_DEP)
|
||||
@echo "==== Installing LuaJIT $(VERSION) to $(PREFIX) ===="
|
||||
$(MKDIR) $(INSTALL_DIRS)
|
||||
cd src && $(INSTALL_X) $(FILE_T) $(INSTALL_T)
|
||||
cd src && test -f $(FILE_A) && $(INSTALL_F) $(FILE_A) $(INSTALL_STATIC) || :
|
||||
$(RM) $(INSTALL_TSYM) $(INSTALL_DYN) $(INSTALL_SHORT1) $(INSTALL_SHORT2)
|
||||
cd src && test -f $(FILE_SO) && \
|
||||
$(INSTALL_X) $(FILE_SO) $(INSTALL_DYN) && \
|
||||
$(LDCONFIG) $(INSTALL_LIB) && \
|
||||
$(SYMLINK) $(INSTALL_SONAME) $(INSTALL_SHORT1) && \
|
||||
$(SYMLINK) $(INSTALL_SONAME) $(INSTALL_SHORT2) || :
|
||||
cd etc && $(INSTALL_F) $(FILE_MAN) $(INSTALL_MAN)
|
||||
cd etc && $(SED_PC) $(FILE_PC) > $(FILE_PC).tmp && \
|
||||
$(INSTALL_F) $(FILE_PC).tmp $(INSTALL_PC) && \
|
||||
$(RM) $(FILE_PC).tmp
|
||||
cd src && $(INSTALL_F) $(FILES_INC) $(INSTALL_INC)
|
||||
cd src/jit && $(INSTALL_F) $(FILES_JITLIB) $(INSTALL_JITLIB)
|
||||
$(SYMLINK) $(INSTALL_TNAME) $(INSTALL_TSYM)
|
||||
@echo "==== Successfully installed LuaJIT $(VERSION) to $(PREFIX) ===="
|
||||
|
||||
uninstall:
|
||||
@echo "==== Uninstalling LuaJIT $(VERSION) from $(PREFIX) ===="
|
||||
$(UNINSTALL) $(INSTALL_TSYM) $(INSTALL_T) $(INSTALL_STATIC) $(INSTALL_DYN) $(INSTALL_SHORT1) $(INSTALL_SHORT2) $(INSTALL_MAN)/$(FILE_MAN) $(INSTALL_PC)
|
||||
for file in $(FILES_JITLIB); do \
|
||||
$(UNINSTALL) $(INSTALL_JITLIB)/$$file; \
|
||||
done
|
||||
for file in $(FILES_INC); do \
|
||||
$(UNINSTALL) $(INSTALL_INC)/$$file; \
|
||||
done
|
||||
$(LDCONFIG) $(INSTALL_LIB)
|
||||
$(RMDIR) $(UNINSTALL_DIRS) || :
|
||||
@echo "==== Successfully uninstalled LuaJIT $(VERSION) from $(PREFIX) ===="
|
||||
|
||||
##############################################################################
|
||||
|
||||
amalg:
|
||||
@echo "Building LuaJIT $(VERSION)"
|
||||
$(MAKE) -C src amalg
|
||||
|
||||
clean:
|
||||
$(MAKE) -C src clean
|
||||
|
||||
.PHONY: all install amalg clean
|
||||
|
||||
##############################################################################
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
README for LuaJIT 2.0.2
|
||||
-----------------------
|
||||
|
||||
LuaJIT is a Just-In-Time (JIT) compiler for the Lua programming language.
|
||||
|
||||
Project Homepage: http://luajit.org/
|
||||
|
||||
LuaJIT is Copyright (C) 2005-2013 Mike Pall.
|
||||
LuaJIT is free software, released under the MIT license.
|
||||
See full Copyright Notice in the COPYRIGHT file or in luajit.h.
|
||||
|
||||
Documentation for LuaJIT is available in HTML format.
|
||||
Please point your favorite browser to:
|
||||
|
||||
doc/luajit.html
|
||||
|
||||
Vendored
+166
@@ -0,0 +1,166 @@
|
||||
/* Copyright (C) 2004-2013 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;
|
||||
}
|
||||
Vendored
+325
@@ -0,0 +1,325 @@
|
||||
/* Copyright (C) 2004-2013 Mike Pall.
|
||||
*
|
||||
* You are welcome to use the general ideas of this design for your own sites.
|
||||
* But please do not steal the stylesheet, the layout or the color scheme.
|
||||
*/
|
||||
/* colorscheme:
|
||||
*
|
||||
* site | head #4162bf/white | #6078bf/#e6ecff
|
||||
* ------+------ ----------------+-------------------
|
||||
* nav | main #bfcfff | #e6ecff/black
|
||||
*
|
||||
* nav: hiback loback #c5d5ff #b9c9f9
|
||||
* hiborder loborder #e6ecff #97a7d7
|
||||
* link hover #2142bf #ff0000
|
||||
*
|
||||
* link: link visited hover #2142bf #8122bf #ff0000
|
||||
*
|
||||
* main: boxback boxborder #f0f4ff #bfcfff
|
||||
*/
|
||||
body {
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size: 10pt;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: #e0e0e0;
|
||||
color: #000000;
|
||||
}
|
||||
a:link {
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
color: #2142bf;
|
||||
}
|
||||
a:visited {
|
||||
text-decoration: none;
|
||||
background: transparent;
|
||||
color: #8122bf;
|
||||
}
|
||||
a:hover, a:active {
|
||||
text-decoration: underline;
|
||||
background: transparent;
|
||||
color: #ff0000;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
margin: 0.5em 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
}
|
||||
h1 {
|
||||
font-size: 200%;
|
||||
line-height: 3em; /* really 6em relative to body, match #site span */
|
||||
margin: 0;
|
||||
}
|
||||
h2 {
|
||||
font-size: 150%;
|
||||
color: #606060;
|
||||
}
|
||||
h3 {
|
||||
font-size: 125%;
|
||||
color: #404040;
|
||||
}
|
||||
p {
|
||||
max-width: 600px;
|
||||
margin: 0 0 0.5em 0;
|
||||
padding: 0;
|
||||
}
|
||||
b {
|
||||
color: #404040;
|
||||
}
|
||||
ul, ol {
|
||||
max-width: 600px;
|
||||
margin: 0.5em 0;
|
||||
padding: 0 0 0 2em;
|
||||
}
|
||||
ul {
|
||||
list-style: outside square;
|
||||
}
|
||||
ol {
|
||||
list-style: outside decimal;
|
||||
}
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
dl {
|
||||
max-width: 600px;
|
||||
margin: 1em 0;
|
||||
padding: 1em;
|
||||
border: 1px solid #bfcfff;
|
||||
background: #f0f4ff;
|
||||
}
|
||||
dt {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
dt sup {
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
color: #808080;
|
||||
}
|
||||
dt a:visited {
|
||||
text-decoration: none;
|
||||
color: #2142bf;
|
||||
}
|
||||
dt a:hover, dt a:active {
|
||||
text-decoration: none;
|
||||
color: #ff0000;
|
||||
}
|
||||
dd {
|
||||
margin: 0.5em 0 0 2em;
|
||||
padding: 0;
|
||||
}
|
||||
div.tablewrap { /* for IE *sigh* */
|
||||
max-width: 600px;
|
||||
}
|
||||
table {
|
||||
table-layout: fixed;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
border: 1px solid #bfcfff;
|
||||
}
|
||||
tr {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
tr.odd {
|
||||
background: #f0f4ff;
|
||||
}
|
||||
tr.separate td {
|
||||
border-top: 1px solid #bfcfff;
|
||||
}
|
||||
td {
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 0.2em 0.5em;
|
||||
border: none;
|
||||
}
|
||||
tt, code, kbd, samp {
|
||||
font-family: Courier New, Courier, monospace;
|
||||
line-height: 1.2;
|
||||
font-size: 110%;
|
||||
}
|
||||
kbd {
|
||||
font-weight: bolder;
|
||||
}
|
||||
blockquote, pre {
|
||||
max-width: 600px;
|
||||
margin: 1em 2em;
|
||||
padding: 0;
|
||||
}
|
||||
pre {
|
||||
line-height: 1.1;
|
||||
}
|
||||
pre.code {
|
||||
line-height: 1.4;
|
||||
margin: 0.5em 0 1em 0.5em;
|
||||
padding: 0.5em 1em;
|
||||
border: 1px solid #bfcfff;
|
||||
background: #f0f4ff;
|
||||
}
|
||||
pre.mark {
|
||||
padding-left: 2em;
|
||||
}
|
||||
span.codemark {
|
||||
position:absolute;
|
||||
left: 16em;
|
||||
color: #4040c0;
|
||||
}
|
||||
span.mark {
|
||||
color: #4040c0;
|
||||
font-family: Courier New, Courier, monospace;
|
||||
line-height: 1.1;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
vertical-align: baseline;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
img.left {
|
||||
float: left;
|
||||
margin: 0.5em 1em 0.5em 0;
|
||||
}
|
||||
img.right {
|
||||
float: right;
|
||||
margin: 0.5em 0 0.5em 1em;
|
||||
}
|
||||
.indent {
|
||||
padding-left: 1em;
|
||||
}
|
||||
.flush {
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
.hide, .noscreen {
|
||||
display: none !important;
|
||||
}
|
||||
.ext {
|
||||
color: #ff8000;
|
||||
}
|
||||
.new {
|
||||
font-size: 6pt;
|
||||
vertical-align: middle;
|
||||
background: #ff8000;
|
||||
color: #ffffff;
|
||||
}
|
||||
#site {
|
||||
clear: both;
|
||||
float: left;
|
||||
width: 13em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
}
|
||||
#site a {
|
||||
font-size: 200%;
|
||||
}
|
||||
#site a:link, #site a:visited {
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
}
|
||||
#site span {
|
||||
line-height: 3em; /* really 6em relative to body, match h1 */
|
||||
}
|
||||
#logo {
|
||||
color: #ffb380;
|
||||
}
|
||||
#head {
|
||||
margin: 0;
|
||||
padding: 0 0 0 2em;
|
||||
border-left: solid 13em #4162bf;
|
||||
border-right: solid 3em #6078bf;
|
||||
background: #6078bf;
|
||||
color: #e6ecff;
|
||||
}
|
||||
#nav {
|
||||
clear: both;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
line-height: 1.5;
|
||||
width: 13em;
|
||||
padding-top: 1em;
|
||||
background: transparent;
|
||||
}
|
||||
#nav ul {
|
||||
list-style: none outside;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#nav li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#nav a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 2px 1em;
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
background: transparent;
|
||||
color: #2142bf;
|
||||
}
|
||||
#nav a:hover, #nav a:active {
|
||||
text-decoration: none;
|
||||
border-top: 1px solid #97a7d7;
|
||||
border-bottom: 1px solid #e6ecff;
|
||||
background: #b9c9f9;
|
||||
color: #ff0000;
|
||||
}
|
||||
#nav a.current, #nav a.current:hover, #nav a.current:active {
|
||||
border-top: 1px solid #e6ecff;
|
||||
border-bottom: 1px solid #97a7d7;
|
||||
background: #c5d5ff;
|
||||
color: #2142bf;
|
||||
}
|
||||
#nav ul ul a {
|
||||
padding: 0 1em 0 1.7em;
|
||||
}
|
||||
#nav ul ul ul a {
|
||||
padding: 0 0.5em 0 2.4em;
|
||||
}
|
||||
#main {
|
||||
line-height: 1.5;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 1em 2em;
|
||||
border-left: solid 13em #bfcfff;
|
||||
border-right: solid 3em #e6ecff;
|
||||
background: #e6ecff;
|
||||
}
|
||||
#foot {
|
||||
clear: both;
|
||||
font-size: 80%;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 0.5em;
|
||||
background: #6078bf;
|
||||
color: #ffffff;
|
||||
}
|
||||
#foot a:link, #foot a:visited {
|
||||
text-decoration: underline;
|
||||
background: transparent;
|
||||
color: #ffffff;
|
||||
}
|
||||
#foot a:hover, #foot a:active {
|
||||
text-decoration: underline;
|
||||
background: transparent;
|
||||
color: #bfcfff;
|
||||
}
|
||||
Vendored
+892
@@ -0,0 +1,892 @@
|
||||
<!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-2013, 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="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</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: #0000c0;">stable version</span> is <strong>LuaJIT 2.0.2</strong>.<br>
|
||||
</p>
|
||||
<p>
|
||||
Please check the
|
||||
<a href="http://luajit.org/changes.html"><span class="ext">»</span> Online Change History</a>
|
||||
to see whether newer versions are available.
|
||||
</p>
|
||||
|
||||
<div class="major" style="background: #d0d0ff;">
|
||||
<h2 id="LuaJIT-2.0.2">LuaJIT 2.0.2 — 2013-06-03</h2>
|
||||
<ul>
|
||||
<li>Fix memory access check for fast string interning.</li>
|
||||
<li>Fix MSVC intrinsics for older versions.</li>
|
||||
<li>Add missing GC steps for <tt>io.*</tt> functions.</li>
|
||||
<li>Fix spurious red zone overflows in machine code generation.</li>
|
||||
<li>Fix jump-range constrained mcode allocation.</li>
|
||||
<li>Inhibit DSE for implicit loads via calls.</li>
|
||||
<li>Fix builtin string to number conversion for overflow digits.</li>
|
||||
<li>Fix optional argument handling while recording builtins.</li>
|
||||
<li>Fix optional argument handling in <tt>table.concat()</tt>.</li>
|
||||
<li>Add partial support for building with MingW64 GCC 4.8-SEH.</li>
|
||||
<li>Add missing PHI barrier to <tt>string.sub(str, a, b) == kstr</tt> FOLD rule.</li>
|
||||
<li>Fix compatibility issues with Illumos.</li>
|
||||
<li>ARM: Fix cache flush/sync for exit stubs of JIT-compiled code.</li>
|
||||
<li>MIPS: Fix cache flush/sync for JIT-compiled code jump area.</li>
|
||||
<li>PPC: Add <tt>plt</tt> suffix for external calls from assembler code.</li>
|
||||
<li>FFI: Fix snapshot substitution in SPLIT pass.</li>
|
||||
<li>FFI/x86: Fix register allocation for 64 bit comparisons.</li>
|
||||
<li>FFI: Fix tailcall in lowest frame to C function with bool result.</li>
|
||||
<li>FFI: Ignore <tt>long</tt> type specifier in <tt>ffi.istype()</tt>.</li>
|
||||
<li>FFI: Fix calling conventions for 32 bit OSX and iOS simulator (struct returns).</li>
|
||||
<li>FFI: Fix calling conventions for ARM hard-float EABI (nested structs).</li>
|
||||
<li>FFI: Improve error messages for arithmetic and comparison operators.</li>
|
||||
<li>FFI: Insert no-op type conversion for pointer to integer cast.</li>
|
||||
<li>FFI: Fix unroll limit for <tt>ffi.fill()</tt>.</li>
|
||||
<li>FFI: Must sink <tt>XBAR</tt> together with <tt>XSTORE</tt>s.</li>
|
||||
<li>FFI: Preserve intermediate string for <tt>const char *</tt> conversion.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.1">LuaJIT 2.0.1 — 2013-02-19</h2>
|
||||
<ul>
|
||||
<li>Don't clear frame for out-of-memory error.</li>
|
||||
<li>Leave hook when resume catches error thrown from hook.</li>
|
||||
<li>Add missing GC steps for template table creation.</li>
|
||||
<li>Fix discharge order of comparisons in Lua parser.</li>
|
||||
<li>Improve buffer handling for <tt>io.read()</tt>.</li>
|
||||
<li>OSX: Add support for Mach-O object files to <tt>-b</tt> option.</li>
|
||||
<li>Fix PS3 port.</li>
|
||||
<li>Fix/enable Xbox 360 port.</li>
|
||||
<li>x86/x64: Always mark ref for shift count as non-weak.</li>
|
||||
<li>x64: Don't fuse implicitly 32-to-64 extended operands.</li>
|
||||
<li>ARM: Fix armhf call argument handling.</li>
|
||||
<li>ARM: Fix code generation for integer math.min/math.max.</li>
|
||||
<li>PPC/e500: Fix <tt>lj_vm_floor()</tt> for Inf/NaN.</li>
|
||||
<li>FFI: Change priority of table initializer variants for structs.</li>
|
||||
<li>FFI: Fix code generation for bool call result check on x86/x64.</li>
|
||||
<li>FFI: Load FFI library on-demand for bytecode with cdata literals.</li>
|
||||
<li>FFI: Fix handling of qualified transparent structs/unions.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0">LuaJIT 2.0.0 — 2012-11-08</h2>
|
||||
<ul>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix Android/x86 build.</li>
|
||||
<li>Fix recording of equality comparisons with <tt>__eq</tt> metamethods.</li>
|
||||
<li>Fix detection of immutable upvalues.</li>
|
||||
<li>Replace error with PANIC for callbacks from JIT-compiled code.</li>
|
||||
<li>Fix builtin string to number conversion for <tt>INT_MIN</tt>.</li>
|
||||
<li>Don't create unneeded array part for template tables.</li>
|
||||
<li>Fix <tt>CONV.num.int</tt> sinking.</li>
|
||||
<li>Don't propagate implicitly widened number to index metamethods.</li>
|
||||
<li>ARM: Fix ordered comparisons of number vs. non-number.</li>
|
||||
<li>FFI: Fix code generation for replay of sunk float fields.</li>
|
||||
<li>FFI: Fix signedness of bool.</li>
|
||||
<li>FFI: Fix recording of bool call result check on x86/x64.</li>
|
||||
<li>FFI: Fix stack-adjustment for <tt>__thiscall</tt> callbacks.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta11">LuaJIT 2.0.0-beta11 — 2012-10-16</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>Use ARM VFP instructions, if available (build-time detection).</li>
|
||||
<li>Add support for ARM hard-float EABI (<tt>armhf</tt>).</li>
|
||||
<li>Add PS3 port.</li>
|
||||
<li>Add many features from Lua 5.2, e.g. <tt>goto</tt>/labels.
|
||||
Refer to <a href="extensions.html#lua52">this list</a>.</li>
|
||||
<li>FFI: Add parameterized C types.</li>
|
||||
<li>FFI: Add support for copy constructors.</li>
|
||||
<li>FFI: Equality comparisons never raise an error (treat as unequal instead).</li>
|
||||
<li>FFI: Box all accessed or returned enums.</li>
|
||||
<li>FFI: Check for <tt>__new</tt> metamethod when calling a constructor.</li>
|
||||
<li>FFI: Handle <tt>__pairs</tt>/<tt>__ipairs</tt> metamethods for cdata objects.</li>
|
||||
<li>FFI: Convert <tt>io.*</tt> file handle to <tt>FILE *</tt> pointer (but as a <tt>void *</tt>).</li>
|
||||
<li>FFI: Detect and support type punning through unions.</li>
|
||||
<li>FFI: Improve various error messages.</li>
|
||||
</ul></li>
|
||||
<li>Build-system reorganization:
|
||||
<ul>
|
||||
<li>Reorganize directory layout:<br>
|
||||
<tt>lib/*</tt> → <tt>src/jit/*</tt><br>
|
||||
<tt>src/buildvm_*.dasc</tt> → <tt>src/vm_*.dasc</tt><br>
|
||||
<tt>src/buildvm_*.h</tt> → removed<br>
|
||||
<tt>src/buildvm*</tt> → <tt>src/host/*</tt></li>
|
||||
<li>Add minified Lua interpreter plus Lua BitOp (<tt>minilua</tt>) to run DynASM.</li>
|
||||
<li>Change DynASM bit operations to use Lua BitOp</li>
|
||||
<li>Translate only <tt>vm_*.dasc</tt> for detected target architecture.</li>
|
||||
<li>Improve target detection for <tt>msvcbuild.bat</tt>.</li>
|
||||
<li>Fix build issues on Cygwin and MinGW with optional MSys.</li>
|
||||
<li>Handle cross-compiles with FPU/no-FPU or hard-fp/soft-fp ABI mismatch.</li>
|
||||
<li>Remove some library functions for no-JIT/no-FFI builds.</li>
|
||||
<li>Add uninstall target to top-level Makefile.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Preserve snapshot #0 PC for all traces.</li>
|
||||
<li>Fix argument checks for <tt>coroutine.create()</tt>.</li>
|
||||
<li>Command line prints version and JIT status to <tt>stdout</tt>, not <tt>stderr</tt>.</li>
|
||||
<li>Fix userdata <tt>__gc</tt> separations at Lua state close.</li>
|
||||
<li>Fix <tt>TDUP</tt> to <tt>HLOAD</tt> forwarding for <tt>LJ_DUALNUM</tt> builds.</li>
|
||||
<li>Fix buffer check in bytecode writer.</li>
|
||||
<li>Make <tt>os.date()</tt> thread-safe.</li>
|
||||
<li>Add missing declarations for MSVC intrinsics.</li>
|
||||
<li>Fix dispatch table modifications for return hooks.</li>
|
||||
<li>Workaround for MSVC conversion bug (<tt>double</tt> → <tt>uint32_t</tt> → <tt>int32_t</tt>).</li>
|
||||
<li>Fix FOLD rule <tt>(i-j)-i => 0-j</tt>.</li>
|
||||
<li>Never use DWARF unwinder on Windows.</li>
|
||||
<li>Fix shrinking of direct mapped blocks in builtin allocator.</li>
|
||||
<li>Limit recursion depth in <tt>string.match()</tt> et al.</li>
|
||||
<li>Fix late despecialization of <tt>ITERN</tt> after loop has been entered.</li>
|
||||
<li>Fix <tt>'f'</tt> and <tt>'L'</tt> options for <tt>debug.getinfo()</tt> and <tt>lua_getinfo()</tt>.</li>
|
||||
<li>Fix <tt>package.searchpath()</tt>.</li>
|
||||
<li>OSX: Change dylib names to be consistent with other platforms.</li>
|
||||
<li>Android: Workaround for broken <tt>sprintf("%g", -0.0)</tt>.</li>
|
||||
<li>x86: Remove support for ancient CPUs without <tt>CMOV</tt> (before Pentium Pro).</li>
|
||||
<li>x86: Fix register allocation for calls returning register pair.</li>
|
||||
<li>x86/x64: Fix fusion of unsigned byte comparisons with swapped operands.</li>
|
||||
<li>ARM: Fix <tt>tonumber()</tt> argument check.</li>
|
||||
<li>ARM: Fix modulo operator and <tt>math.floor()</tt>/<tt>math.ceil()</tt> for <tt>inf</tt>/<tt>nan</tt>.</li>
|
||||
<li>ARM: Invoke SPLIT pass for leftover <tt>IR_TOBIT</tt>.</li>
|
||||
<li>ARM: Fix BASE register coalescing.</li>
|
||||
<li>PPC: Fix interpreter state setup in callbacks.</li>
|
||||
<li>PPC: Fix <tt>string.sub()</tt> range check.</li>
|
||||
<li>MIPS: Support generation of MIPS/MIPSEL bytecode object files.</li>
|
||||
<li>MIPS: Fix calls to <tt>floor()</tt>/<tt>ceil()</tt><tt>/trunc()</tt>.</li>
|
||||
<li>ARM/PPC: Detect more target architecture variants.</li>
|
||||
<li>ARM/PPC/e500/MIPS: Fix tailcalls from fast functions, esp. <tt>tostring()</tt>.</li>
|
||||
<li>ARM/PPC/MIPS: Fix rematerialization of FP constants.</li>
|
||||
<li>FFI: Don't call <tt>FreeLibrary()</tt> on our own EXE/DLL.</li>
|
||||
<li>FFI: Resolve metamethods for constructors, too.</li>
|
||||
<li>FFI: Properly disable callbacks on iOS (would require executable memory).</li>
|
||||
<li>FFI: Fix cdecl string parsing during recording.</li>
|
||||
<li>FFI: Show address pointed to for <tt>tostring(ref)</tt>, too.</li>
|
||||
<li>FFI: Fix alignment of C call argument/return structure.</li>
|
||||
<li>FFI: Initialize all fields of standard types.</li>
|
||||
<li>FFI: Fix callback handling when new C types are declared in callback.</li>
|
||||
<li>FFI: Fix recording of constructors for pointers.</li>
|
||||
<li>FFI: Always resolve metamethods for pointers to structs.</li>
|
||||
<li>FFI: Correctly propagate alignment when interning nested types.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Add allocation sinking and store sinking optimization.</li>
|
||||
<li>Constify immutable upvalues.</li>
|
||||
<li>Add builtin string to integer or FP number conversion. Improves cross-platform consistency and correctness.</li>
|
||||
<li>Create string hash slots in template tables for non-const values, too. Avoids later table resizes.</li>
|
||||
<li>Eliminate <tt>HREFK</tt> guard for template table references.</li>
|
||||
<li>Add various new FOLD rules.</li>
|
||||
<li>Don't use stack unwinding for <tt>lua_yield()</tt> (slow on x64).</li>
|
||||
<li>ARM, PPC, MIPS: Improve <tt>XLOAD</tt> operand fusion and register hinting.</li>
|
||||
<li>PPC, MIPS: Compile <tt>math.sqrt()</tt> to sqrt instruction, if available.</li>
|
||||
<li>FFI: Fold <tt>KPTR</tt> + constant offset in SPLIT pass.</li>
|
||||
<li>FFI: Optimize/inline <tt>ffi.copy()</tt> and <tt>ffi.fill()</tt>.</li>
|
||||
<li>FFI: Compile and optimize array/struct copies.</li>
|
||||
<li>FFI: Compile <tt>ffi.typeof(cdata|ctype)</tt>, <tt>ffi.sizeof()</tt>, <tt>ffi.alignof()</tt>, <tt>ffi.offsetof()</tt> and <tt>ffi.gc()</tt>.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta10">LuaJIT 2.0.0-beta10 — 2012-05-09</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>The MIPS of LuaJIT is complete. It requires a CPU conforming to the
|
||||
MIPS32 R1 architecture with hardware FPU. O32 hard-fp ABI,
|
||||
little-endian or big-endian.</li>
|
||||
<li>Auto-detect target arch via cross-compiler. No need for
|
||||
<tt>TARGET=arch</tt> anymore.</li>
|
||||
<li>Make DynASM compatible with Lua 5.2.</li>
|
||||
<li>From Lua 5.2: Try <tt>__tostring</tt> metamethod on non-string error
|
||||
messages..</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix parsing of hex literals with exponents.</li>
|
||||
<li>Fix bytecode dump for certain number constants.</li>
|
||||
<li>Fix argument type in error message for relative arguments.</li>
|
||||
<li>Fix argument error handling on Lua stacks without a frame.</li>
|
||||
<li>Add missing mcode limit check in assembler backend.</li>
|
||||
<li>Fix compilation on OpenBSD.</li>
|
||||
<li>Avoid recursive GC steps after GC-triggered trace exit.</li>
|
||||
<li>Replace <tt><unwind.h></tt> definitions with our own.</li>
|
||||
<li>Fix OSX build issues. Bump minimum required OSX version to 10.4.</li>
|
||||
<li>Fix discharge order of comparisons in Lua parser.</li>
|
||||
<li>Ensure running <tt>__gc</tt> of userdata created in <tt>__gc</tt>
|
||||
at state close.</li>
|
||||
<li>Limit number of userdata <tt>__gc</tt> separations at state close.</li>
|
||||
<li>Fix bytecode <tt>JMP</tt> slot range when optimizing
|
||||
<tt>and</tt>/<tt>or</tt> with constant LHS.</li>
|
||||
<li>Fix DSE of <tt>USTORE</tt>.</li>
|
||||
<li>Make <tt>lua_concat()</tt> work from C hook with partial frame.</li>
|
||||
<li>Add required PHIs for implicit conversions, e.g. via <tt>XREF</tt>
|
||||
forwarding.</li>
|
||||
<li>Add more comparison variants to Valgrind suppressions file.</li>
|
||||
<li>Disable loading bytecode with an extra header (BOM or <tt>#!</tt>).</li>
|
||||
<li>Fix PHI stack slot syncing.</li>
|
||||
<li>ARM: Reorder type/value tests to silence Valgrind.</li>
|
||||
<li>ARM: Fix register allocation for <tt>ldrd</tt>-optimized
|
||||
<tt>HREFK</tt>.</li>
|
||||
<li>ARM: Fix conditional branch fixup for <tt>OBAR</tt>.</li>
|
||||
<li>ARM: Invoke SPLIT pass for <tt>double</tt> args in FFI call.</li>
|
||||
<li>ARM: Handle all <tt>CALL*</tt> ops with <tt>double</tt> results in
|
||||
SPLIT pass.</li>
|
||||
<li>ARM: Fix rejoin of <tt>POW</tt> in SPLIT pass.</li>
|
||||
<li>ARM: Fix compilation of <tt>math.sinh</tt>, <tt>math.cosh</tt>,
|
||||
<tt>math.tanh</tt>.</li>
|
||||
<li>ARM, PPC: Avoid pointless arg clearing in <tt>BC_IFUNCF</tt>.</li>
|
||||
<li>PPC: Fix resume after yield from hook.</li>
|
||||
<li>PPC: Fix argument checking for <tt>rawget()</tt>.</li>
|
||||
<li>PPC: Fix fusion of floating-point <tt>XLOAD</tt>/<tt>XSTORE</tt>.</li>
|
||||
<li>PPC: Fix <tt>HREFK</tt> code generation for huge tables.</li>
|
||||
<li>PPC: Use builtin D-Cache/I-Cache sync code.</li>
|
||||
</ul></li>
|
||||
<li>FFI library:
|
||||
<ul>
|
||||
<li>Ignore empty statements in <tt>ffi.cdef()</tt>.</li>
|
||||
<li>Ignore number parsing errors while skipping definitions.</li>
|
||||
<li>Don't touch frame in callbacks with tailcalls to fast functions.</li>
|
||||
<li>Fix library unloading on POSIX systems.</li>
|
||||
<li>Finalize cdata before userdata when closing the state.</li>
|
||||
<li>Change <tt>ffi.load()</tt> library name resolution for Cygwin.</li>
|
||||
<li>Fix resolving of function name redirects on Windows/x86.</li>
|
||||
<li>Fix symbol resolving error messages on Windows.</li>
|
||||
<li>Fix blacklisting of C functions calling callbacks.</li>
|
||||
<li>Fix result type of pointer difference.</li>
|
||||
<li>Use correct PC in FFI metamethod error message.</li>
|
||||
<li>Allow <tt>'typedef _Bool int BOOL;'</tt> for the Windows API.</li>
|
||||
<li>Don't record test for bool result of call, if ignored.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta9">LuaJIT 2.0.0-beta9 — 2011-12-14</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>PPC port of LuaJIT is complete. Default is the dual-number port
|
||||
(usually faster). Single-number port selectable via <tt>src/Makefile</tt>
|
||||
at build time.</li>
|
||||
<li>Add FFI callback support.</li>
|
||||
<li>Extend <tt>-b</tt> to generate <tt>.c</tt>, <tt>.h</tt> or <tt>.obj/.o</tt>
|
||||
files with embedded bytecode.</li>
|
||||
<li>Allow loading embedded bytecode with <tt>require()</tt>.</li>
|
||||
<li>From Lua 5.2: Change to <tt>'\z'</tt> escape. Reject undefined escape
|
||||
sequences.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix OSX 10.7 build. Fix <tt>install_name</tt> and versioning on OSX.</li>
|
||||
<li>Fix iOS build.</li>
|
||||
<li>Install <tt>dis_arm.lua</tt>, too.</li>
|
||||
<li>Mark installed shared library as executable.</li>
|
||||
<li>Add debug option to <tt>msvcbuild.bat</tt> and improve error handling.</li>
|
||||
<li>Fix data-flow analysis for iterators.</li>
|
||||
<li>Fix forced unwinding triggered by external unwinder.</li>
|
||||
<li>Record missing <tt>for</tt> loop slot loads (return to lower frame).</li>
|
||||
<li>Always use ANSI variants of Windows system functions.</li>
|
||||
<li>Fix GC barrier for multi-result table constructor (<tt>TSETM</tt>).</li>
|
||||
<li>Fix/add various FOLD rules.</li>
|
||||
<li>Add potential PHI for number conversions due to type instability.</li>
|
||||
<li>Do not eliminate PHIs only referenced from other PHIs.</li>
|
||||
<li>Correctly anchor implicit number to string conversions in Lua/C API.</li>
|
||||
<li>Fix various stack limit checks.</li>
|
||||
<li>x64: Use thread-safe exceptions for external unwinding (GCC platforms).</li>
|
||||
<li>x64: Fix result type of cdata index conversions.</li>
|
||||
<li>x64: Fix <tt>math.random()</tt> and <tt>bit.bswap()</tt> code generation.</li>
|
||||
<li>x64: Fix <tt>lightuserdata</tt> comparisons.</li>
|
||||
<li>x64: Always extend stack-passed arguments to pointer size.</li>
|
||||
<li>ARM: Many fixes to code generation backend.</li>
|
||||
<li>PPC/e500: Fix dispatch for binop metamethods.</li>
|
||||
<li>PPC/e500: Save/restore condition registers when entering/leaving the VM.</li>
|
||||
<li>PPC/e500: Fix write barrier in stores of strings to upvalues.</li>
|
||||
</ul></li>
|
||||
<li>FFI library:
|
||||
<ul>
|
||||
<li>Fix C comment parsing.</li>
|
||||
<li>Fix snapshot optimization for cdata comparisons.</li>
|
||||
<li>Fix recording of const/enum lookups in namespaces.</li>
|
||||
<li>Fix call argument and return handling for <tt>I8/U8/I16/U16</tt> types.</li>
|
||||
<li>Fix unfused loads of float fields.</li>
|
||||
<li>Fix <tt>ffi.string()</tt> recording.</li>
|
||||
<li>Save <tt>GetLastError()</tt> around <tt>ffi.load()</tt> and symbol
|
||||
resolving, too.</li>
|
||||
<li>Improve ld script detection in <tt>ffi.load()</tt>.</li>
|
||||
<li>Record loads/stores to external variables in namespaces.</li>
|
||||
<li>Compile calls to stdcall, fastcall and vararg functions.</li>
|
||||
<li>Treat function ctypes like pointers in comparisons.</li>
|
||||
<li>Resolve <tt>__call</tt> metamethod for pointers, too.</li>
|
||||
<li>Record C function calls with bool return values.</li>
|
||||
<li>Record <tt>ffi.errno()</tt>.</li>
|
||||
<li>x86: Fix number to <tt>uint32_t</tt> conversion rounding.</li>
|
||||
<li>x86: Fix 64 bit arithmetic in assembler backend.</li>
|
||||
<li>x64: Fix struct-by-value calling conventions.</li>
|
||||
<li>ARM: Ensure invocation of SPLIT pass for float conversions.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Display trace types with <tt>-jv</tt> and <tt>-jdump</tt>.</li>
|
||||
<li>Record isolated calls. But prefer recording loops over calls.</li>
|
||||
<li>Specialize to prototype for non-monomorphic functions. Solves the
|
||||
trace-explosion problem for closure-heavy programming styles.</li>
|
||||
<li>Always generate a portable <tt>vmdef.lua</tt>. Easier for distros.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta8">LuaJIT 2.0.0-beta8 — 2011-06-23</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>Soft-float ARM port of LuaJIT is complete.</li>
|
||||
<li>Add support for bytecode loading/saving and <tt>-b</tt> command line
|
||||
option.</li>
|
||||
<li>From Lua 5.2: <tt>__len</tt> metamethod for tables
|
||||
(disabled by default).</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>ARM: Misc. fixes for interpreter.</li>
|
||||
<li>x86/x64: Fix <tt>bit.*</tt> argument checking in interpreter.</li>
|
||||
<li>Catch early out-of-memory in memory allocator initialization.</li>
|
||||
<li>Fix data-flow analysis for paths leading to an upvalue close.</li>
|
||||
<li>Fix check for missing arguments in <tt>string.format()</tt>.</li>
|
||||
<li>Fix Solaris/x86 build (note: not a supported target).</li>
|
||||
<li>Fix recording of loops with instable directions in side traces.</li>
|
||||
<li>x86/x64: Fix fusion of comparisons with <tt>u8</tt>/<tt>u16</tt>
|
||||
<tt>XLOAD</tt>.</li>
|
||||
<li>x86/x64: Fix register allocation for variable shifts.</li>
|
||||
</ul></li>
|
||||
<li>FFI library:
|
||||
<ul>
|
||||
<li>Add <tt>ffi.errno()</tt>. Save <tt>errno</tt>/<tt>GetLastError()</tt>
|
||||
around allocations etc.</li>
|
||||
<li>Fix <tt>__gc</tt> for VLA/VLS cdata objects.</li>
|
||||
<li>Fix recording of casts from 32 bit cdata pointers to integers.</li>
|
||||
<li><tt>tonumber(cdata)</tt> returns <tt>nil</tt> for non-numbers.</li>
|
||||
<li>Show address pointed to for <tt>tostring(pointer)</tt>.</li>
|
||||
<li>Print <tt>NULL</tt> pointers as <tt>"cdata<... *>: NULL"</tt>.</li>
|
||||
<li>Support <tt>__tostring</tt> metamethod for pointers to structs, too.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>More tuning for loop unrolling heuristics.</li>
|
||||
<li>Flatten and compress in-memory debug info (saves ~70%).</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta7">LuaJIT 2.0.0-beta7 — 2011-05-05</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>ARM port of the LuaJIT interpreter is complete.</li>
|
||||
<li>FFI library: Add <tt>ffi.gc()</tt>, <tt>ffi.metatype()</tt>,
|
||||
<tt>ffi.istype()</tt>.</li>
|
||||
<li>FFI library: Resolve ld script redirection in <tt>ffi.load()</tt>.</li>
|
||||
<li>From Lua 5.2: <tt>package.searchpath()</tt>, <tt>fp:read("*L")</tt>,
|
||||
<tt>load(string)</tt>.</li>
|
||||
<li>From Lua 5.2, disabled by default: empty statement,
|
||||
<tt>table.unpack()</tt>, modified <tt>coroutine.running()</tt>.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>FFI library: numerous fixes.</li>
|
||||
<li>Fix type mismatches in store-to-load forwarding.</li>
|
||||
<li>Fix error handling within metamethods.</li>
|
||||
<li>Fix <tt>table.maxn()</tt>.</li>
|
||||
<li>Improve accuracy of <tt>x^-k</tt> on x64.</li>
|
||||
<li>Fix code generation for Intel Atom in x64 mode.</li>
|
||||
<li>Fix narrowing of POW.</li>
|
||||
<li>Fix recording of retried fast functions.</li>
|
||||
<li>Fix code generation for <tt>bit.bnot()</tt> and multiplies.</li>
|
||||
<li>Fix error location within cpcall frames.</li>
|
||||
<li>Add workaround for old libgcc unwind bug.</li>
|
||||
<li>Fix <tt>lua_yield()</tt> and <tt>getmetatable(lightuserdata)</tt> on x64.</li>
|
||||
<li>Misc. fixes for PPC/e500 interpreter.</li>
|
||||
<li>Fix stack slot updates for down-recursion.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Add dual-number mode (int/double) for the VM. Enabled for ARM.</li>
|
||||
<li>Improve narrowing of arithmetic operators and <tt>for</tt> loops.</li>
|
||||
<li>Tune loop unrolling heuristics and increase trace recorder limits.</li>
|
||||
<li>Eliminate dead slots in snapshots using bytecode data-flow analysis.</li>
|
||||
<li>Avoid phantom stores to proxy tables.</li>
|
||||
<li>Optimize lookups in empty proxy tables.</li>
|
||||
<li>Improve bytecode optimization of <tt>and</tt>/<tt>or</tt> operators.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta6">LuaJIT 2.0.0-beta6 — 2011-02-11</h2>
|
||||
<ul>
|
||||
<li>New features:
|
||||
<ul>
|
||||
<li>PowerPC/e500v2 port of the LuaJIT interpreter is complete.</li>
|
||||
<li>Various minor features from Lua 5.2: Hex escapes in literals,
|
||||
<tt>'\*'</tt> escape, reversible <tt>string.format("%q",s)</tt>,
|
||||
<tt>"%g"</tt> pattern, <tt>table.sort</tt> checks callbacks,
|
||||
<tt>os.exit(status|true|false[,close])</tt>.</li>
|
||||
<li>Lua 5.2 <tt>__pairs</tt> and <tt>__ipairs</tt> metamethods
|
||||
(disabled by default).</li>
|
||||
<li>Initial release of the FFI library.</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix <tt>string.format()</tt> for non-finite numbers.</li>
|
||||
<li>Fix memory leak when compiled to use the built-in allocator.</li>
|
||||
<li>x86/x64: Fix unnecessary resize in <tt>TSETM</tt> bytecode.</li>
|
||||
<li>Fix various GC issues with traces and <tt>jit.flush()</tt>.</li>
|
||||
<li>x64: Fix fusion of indexes for array references.</li>
|
||||
<li>x86/x64: Fix stack overflow handling for coroutine results.</li>
|
||||
<li>Enable low-2GB memory allocation on FreeBSD/x64.</li>
|
||||
<li>Fix <tt>collectgarbage("count")</tt> result if more than 2GB is in use.</li>
|
||||
<li>Fix parsing of hex floats.</li>
|
||||
<li>x86/x64: Fix loop branch inversion with trailing
|
||||
<tt>HREF+NE/EQ</tt>.</li>
|
||||
<li>Add <tt>jit.os</tt> string.</li>
|
||||
<li><tt>coroutine.create()</tt> permits running C functions, too.</li>
|
||||
<li>Fix OSX build to work with newer ld64 versions.</li>
|
||||
<li>Fix bytecode optimization of <tt>and</tt>/<tt>or</tt> operators.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Emit specialized bytecode for <tt>pairs()</tt>/<tt>next()</tt>.</li>
|
||||
<li>Improve bytecode coalescing of <tt>nil</tt> constants.</li>
|
||||
<li>Compile calls to vararg functions.</li>
|
||||
<li>Compile <tt>select()</tt>.</li>
|
||||
<li>Improve alias analysis, esp. for loads from allocations.</li>
|
||||
<li>Tuning of various compiler heuristics.</li>
|
||||
<li>Refactor and extend IR conversion instructions.</li>
|
||||
<li>x86/x64: Various backend enhancements related to the FFI.</li>
|
||||
<li>Add SPLIT pass to split 64 bit IR instructions for 32 bit CPUs.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta5">LuaJIT 2.0.0-beta5 — 2010-08-24</h2>
|
||||
<ul>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix trace exit dispatch to function headers.</li>
|
||||
<li>Fix Windows and OSX builds with LUAJIT_DISABLE_JIT.</li>
|
||||
<li>Reorganize and fix placement of generated machine code on x64.</li>
|
||||
<li>Fix TNEW in x64 interpreter.</li>
|
||||
<li>Do not eliminate PHIs for values only referenced from side exits.</li>
|
||||
<li>OS-independent canonicalization of strings for non-finite numbers.</li>
|
||||
<li>Fix <tt>string.char()</tt> range check on x64.</li>
|
||||
<li>Fix <tt>tostring()</tt> resolving within <tt>print()</tt>.</li>
|
||||
<li>Fix error handling for <tt>next()</tt>.</li>
|
||||
<li>Fix passing of constant arguments to external calls on x64.</li>
|
||||
<li>Fix interpreter argument check for two-argument SSE math functions.</li>
|
||||
<li>Fix C frame chain corruption caused by <tt>lua_cpcall()</tt>.</li>
|
||||
<li>Fix return from <tt>pcall()</tt> within active hook.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Replace on-trace GC frame syncing with interpreter exit.</li>
|
||||
<li>Improve hash lookup specialization by not removing dead keys during GC.</li>
|
||||
<li>Turn traces into true GC objects.</li>
|
||||
<li>Avoid starting a GC cycle immediately after library init.</li>
|
||||
<li>Add weak guards to improve dead-code elimination.</li>
|
||||
<li>Speed up string interning.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta4">LuaJIT 2.0.0-beta4 — 2010-03-28</h2>
|
||||
<ul>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix precondition for on-trace creation of table keys.</li>
|
||||
<li>Fix <tt>{f()}</tt> on x64 when table is resized.</li>
|
||||
<li>Fix folding of ordered comparisons with same references.</li>
|
||||
<li>Fix snapshot restores for multi-result bytecodes.</li>
|
||||
<li>Fix potential hang when recording bytecode with nested closures.</li>
|
||||
<li>Fix recording of <tt>getmetatable()</tt>, <tt>tonumber()</tt> and bad argument types.</li>
|
||||
<li>Fix SLOAD fusion across returns to lower frames.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Add array bounds check elimination. <tt>-Oabc</tt> is enabled by default.</li>
|
||||
<li>More tuning for x64, e.g. smaller table objects.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta3">LuaJIT 2.0.0-beta3 — 2010-03-07</h2>
|
||||
<ul>
|
||||
<li>LuaJIT x64 port:
|
||||
<ul>
|
||||
<li>Port integrated memory allocator to Linux/x64, Windows/x64 and OSX/x64.</li>
|
||||
<li>Port interpreter and JIT compiler to x64.</li>
|
||||
<li>Port DynASM to x64.</li>
|
||||
<li>Many 32/64 bit cleanups in the VM.</li>
|
||||
<li>Allow building the interpreter with either x87 or SSE2 arithmetics.</li>
|
||||
<li>Add external unwinding and C++ exception interop (default on x64).</li>
|
||||
</ul></li>
|
||||
<li>Correctness and completeness:
|
||||
<ul>
|
||||
<li>Fix constructor bytecode generation for certain conditional values.</li>
|
||||
<li>Fix some cases of ordered string comparisons.</li>
|
||||
<li>Fix <tt>lua_tocfunction()</tt>.</li>
|
||||
<li>Fix cutoff register in JMP bytecode for some conditional expressions.</li>
|
||||
<li>Fix PHI marking algorithm for references from variant slots.</li>
|
||||
<li>Fix <tt>package.cpath</tt> for non-default PREFIX.</li>
|
||||
<li>Fix DWARF2 frame unwind information for interpreter on OSX.</li>
|
||||
<li>Drive the GC forward on string allocations in the parser.</li>
|
||||
<li>Implement call/return hooks (zero-cost if disabled).</li>
|
||||
<li>Implement yield from C hooks.</li>
|
||||
<li>Disable JIT compiler on older non-SSE2 CPUs instead of aborting.</li>
|
||||
</ul></li>
|
||||
<li>Structural and performance enhancements:
|
||||
<ul>
|
||||
<li>Compile recursive code (tail-, up- and down-recursion).</li>
|
||||
<li>Improve heuristics for bytecode penalties and blacklisting.</li>
|
||||
<li>Split CALL/FUNC recording and clean up fast function call semantics.</li>
|
||||
<li>Major redesign of internal function call handling.</li>
|
||||
<li>Improve FOR loop const specialization and integerness checks.</li>
|
||||
<li>Switch to pre-initialized stacks. Avoid frame-clearing.</li>
|
||||
<li>Colocation of prototypes and related data: bytecode, constants, debug info.</li>
|
||||
<li>Cleanup parser and streamline bytecode generation.</li>
|
||||
<li>Add support for weak IR references to register allocator.</li>
|
||||
<li>Switch to compressed, extensible snapshots.</li>
|
||||
<li>Compile returns to frames below the start frame.</li>
|
||||
<li>Improve alias analysis of upvalues using a disambiguation hash value.</li>
|
||||
<li>Compile floor/ceil/trunc to SSE2 helper calls or SSE4.1 instructions.</li>
|
||||
<li>Add generic C call handling to IR and backend.</li>
|
||||
<li>Improve KNUM fuse vs. load heuristics.</li>
|
||||
<li>Compile various <tt>io.*()</tt> functions.</li>
|
||||
<li>Compile <tt>math.sinh()</tt>, <tt>math.cosh()</tt>, <tt>math.tanh()</tt>
|
||||
and <tt>math.random()</tt>.</li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta2">LuaJIT 2.0.0-beta2 — 2009-11-09</h2>
|
||||
<ul>
|
||||
<li>Reorganize build system. Build static+shared library on POSIX.</li>
|
||||
<li>Allow C++ exception conversion on all platforms
|
||||
using a wrapper function.</li>
|
||||
<li>Automatically catch C++ exceptions and rethrow Lua error
|
||||
(DWARF2 only).</li>
|
||||
<li>Check for the correct x87 FPU precision at strategic points.</li>
|
||||
<li>Always use wrappers for libm functions.</li>
|
||||
<li>Resurrect metamethod name strings before copying them.</li>
|
||||
<li>Mark current trace, even if compiler is idle.</li>
|
||||
<li>Ensure FILE metatable is created only once.</li>
|
||||
<li>Fix type comparisons when different integer types are involved.</li>
|
||||
<li>Fix <tt>getmetatable()</tt> recording.</li>
|
||||
<li>Fix TDUP with dead keys in template table.</li>
|
||||
<li><tt>jit.flush(tr)</tt> returns status.
|
||||
Prevent manual flush of a trace that's still linked.</li>
|
||||
<li>Improve register allocation heuristics for invariant references.</li>
|
||||
<li>Compile the push/pop variants of <tt>table.insert()</tt> and
|
||||
<tt>table.remove()</tt>.</li>
|
||||
<li>Compatibility with MSVC <tt>link /debug</tt>.</li>
|
||||
<li>Fix <tt>lua_iscfunction()</tt>.</li>
|
||||
<li>Fix <tt>math.random()</tt> when compiled with <tt>-fpic</tt> (OSX).</li>
|
||||
<li>Fix <tt>table.maxn()</tt>.</li>
|
||||
<li>Bump <tt>MACOSX_DEPLOYMENT_TARGET</tt> to <tt>10.4</tt></li>
|
||||
<li><tt>luaL_check*()</tt> and <tt>luaL_opt*()</tt> now support
|
||||
negative arguments, too.<br>
|
||||
This matches the behavior of Lua 5.1, but not the specification.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-2.0.0-beta1">LuaJIT 2.0.0-beta1 — 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: #ffff80;">
|
||||
<h2 id="LuaJIT-1.1.8">LuaJIT 1.1.8 — 2012-04-16</h2>
|
||||
<ul>
|
||||
<li>Merged with Lua 5.1.5. Also integrated fixes for all
|
||||
<a href="http://www.lua.org/bugs.html#5.1.5"><span class="ext">»</span> <span class="ext">»</span> currently known bugs in Lua 5.1.5</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.7">LuaJIT 1.1.7 — 2011-05-05</h2>
|
||||
<ul>
|
||||
<li>Added fixes for the
|
||||
<a href="http://www.lua.org/bugs.html#5.1.4"><span class="ext">»</span> currently known bugs in Lua 5.1.4</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.6">LuaJIT 1.1.6 — 2010-03-28</h2>
|
||||
<ul>
|
||||
<li>Added fixes for the
|
||||
<a href="http://www.lua.org/bugs.html#5.1.4"><span class="ext">»</span> currently known bugs in Lua 5.1.4</a>.</li>
|
||||
<li>Removed wrong GC check in <tt>jit_createstate()</tt>.
|
||||
Thanks to Tim Mensch.</li>
|
||||
<li>Fixed bad assertions while compiling <tt>table.insert()</tt> and
|
||||
<tt>table.remove()</tt>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.5">LuaJIT 1.1.5 — 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">»</span> known bugs in Lua 5.1.3</a>.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.4">LuaJIT 1.1.4 — 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">»</span> 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 — 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">»</span> 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 trace=-</tt>).</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="LuaJIT-1.1.2">LuaJIT 1.1.2 — 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 — 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">»</span> 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 pre-processing, 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">»</span> Coco Change History</a>).
|
||||
</p>
|
||||
|
||||
<h2 id="LuaJIT-1.1.0">LuaJIT 1.1.0 — 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 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 ↔ 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="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">»</span> Coco Change History</a>).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="major" style="background: #ffffd0;">
|
||||
<h2 id="LuaJIT-1.0.3">LuaJIT 1.0.3 — 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 bug fixes).</li>
|
||||
</ul>
|
||||
<p>
|
||||
This is the first public release of LuaJIT.
|
||||
</p>
|
||||
|
||||
<h2 id="LuaJIT-1.0.2">LuaJIT 1.0.2 — 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 function signature handling in
|
||||
<tt>jit.opt_lib</tt>.</li>
|
||||
<li>Changed to index-based hints for inlining C 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 — 2005-08-31</h2>
|
||||
<ul>
|
||||
<li>Missing GC step in <tt>OP_CONCAT</tt>.</li>
|
||||
<li>Fix result handling for C –> 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 — 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 © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
<!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-2013, 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="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
Please send general questions to the
|
||||
<a href="http://luajit.org/list.html"><span class="ext">»</span> LuaJIT mailing list</a>.
|
||||
You can also send any questions you have directly to me:
|
||||
</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
var xS="@-:\" .0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ<abc>defghijklmnopqrstuvwxyz";function xD(s)
|
||||
{var len=s.length;var r="";for(var i=0;i<len;i++)
|
||||
{var c=s.charAt(i);var n=xS.indexOf(c);if(n!=-1)c=xS.charAt(69-n);r+=c;}
|
||||
document.write("<"+"p>"+r+"<"+"/p>\n");}
|
||||
//-->
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
xD("fyZKB8xv\"FJytmz8.KAB0u52D")
|
||||
//--></script>
|
||||
<noscript>
|
||||
<p><img src="img/contact.png" alt="Contact info in image" width="170" height="13">
|
||||
</p>
|
||||
</noscript>
|
||||
|
||||
<h2>Copyright</h2>
|
||||
<p>
|
||||
All documentation is
|
||||
Copyright © 2005-2013 Mike Pall.
|
||||
</p>
|
||||
|
||||
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+187
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Lua/C 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-2013, 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>Lua/C API Extensions</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a class="current" href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT adds some extensions to the standard Lua/C API. The LuaJIT include
|
||||
directory must be in the compiler search path (<tt>-I<i>path</i></tt>)
|
||||
to be able to include the required header for C code:
|
||||
</p>
|
||||
<pre class="code">
|
||||
#include "luajit.h"
|
||||
</pre>
|
||||
<p>
|
||||
Or for C++ code:
|
||||
</p>
|
||||
<pre class="code">
|
||||
#include "lua.hpp"
|
||||
</pre>
|
||||
|
||||
<h2 id="luaJIT_setmode"><tt>luaJIT_setmode(L, idx, mode)</tt>
|
||||
— Control VM</h2>
|
||||
<p>
|
||||
This is a C API extension to allow control of the VM from C code. The
|
||||
full prototype of <tt>LuaJIT_setmode</tt> is:
|
||||
</p>
|
||||
<pre class="code">
|
||||
LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode);
|
||||
</pre>
|
||||
<p>
|
||||
The returned status is either success (<tt>1</tt>) or failure (<tt>0</tt>).
|
||||
The second argument is either <tt>0</tt> or a stack index (similar to the
|
||||
other Lua/C API functions).
|
||||
</p>
|
||||
<p>
|
||||
The third argument specifies the mode, which is 'or'ed with a flag.
|
||||
The flag can be <tt>LUAJIT_MODE_OFF</tt> to turn a feature on,
|
||||
<tt>LUAJIT_MODE_ON</tt> to turn a feature off, or
|
||||
<tt>LUAJIT_MODE_FLUSH</tt> to flush cached code.
|
||||
</p>
|
||||
<p>
|
||||
The following modes are defined:
|
||||
</p>
|
||||
|
||||
<h3 id="mode_engine"><tt>luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|flag)</tt></h3>
|
||||
<p>
|
||||
Turn the whole JIT compiler on or off or flush the whole cache of compiled code.
|
||||
</p>
|
||||
|
||||
<h3 id="mode_func"><tt>luaJIT_setmode(L, idx, LUAJIT_MODE_FUNC|flag)</tt><br>
|
||||
<tt>luaJIT_setmode(L, idx, LUAJIT_MODE_ALLFUNC|flag)</tt><br>
|
||||
<tt>luaJIT_setmode(L, idx, LUAJIT_MODE_ALLSUBFUNC|flag)</tt></h3>
|
||||
<p>
|
||||
This sets the mode for the function at the stack index <tt>idx</tt> or
|
||||
the parent of the calling function (<tt>idx = 0</tt>). It either
|
||||
enables JIT compilation for a function, disables it and flushes any
|
||||
already compiled code or only flushes already compiled code. This
|
||||
applies recursively to all sub-functions of the function with
|
||||
<tt>LUAJIT_MODE_ALLFUNC</tt> or only to the sub-functions with
|
||||
<tt>LUAJIT_MODE_ALLSUBFUNC</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="mode_trace"><tt>luaJIT_setmode(L, trace,<br>
|
||||
LUAJIT_MODE_TRACE|LUAJIT_MODE_FLUSH)</tt></h3>
|
||||
<p>
|
||||
Flushes the specified root trace and all of its side traces from the cache.
|
||||
The code for the trace will be retained as long as there are any other
|
||||
traces which link to it.
|
||||
</p>
|
||||
|
||||
<h3 id="mode_wrapcfunc"><tt>luaJIT_setmode(L, idx, LUAJIT_MODE_WRAPCFUNC|flag)</tt></h3>
|
||||
<p>
|
||||
This mode defines a wrapper function for calls to C functions. If
|
||||
called with <tt>LUAJIT_MODE_ON</tt>, the stack index at <tt>idx</tt>
|
||||
must be a <tt>lightuserdata</tt> object holding a pointer to the wrapper
|
||||
function. From now on all C functions are called through the wrapper
|
||||
function. If called with <tt>LUAJIT_MODE_OFF</tt> this mode is turned
|
||||
off and all C functions are directly called.
|
||||
</p>
|
||||
<p>
|
||||
The wrapper function can be used for debugging purposes or to catch
|
||||
and convert foreign exceptions. But please read the section on
|
||||
<a href="extensions.html#exceptions">C++ exception interoperability</a>
|
||||
first. Recommended usage can be seen in this C++ code excerpt:
|
||||
</p>
|
||||
<pre class="code">
|
||||
#include <exception>
|
||||
#include "lua.hpp"
|
||||
|
||||
// Catch C++ exceptions and convert them to Lua error messages.
|
||||
// Customize as needed for your own exception classes.
|
||||
static int wrap_exceptions(lua_State *L, lua_CFunction f)
|
||||
{
|
||||
try {
|
||||
return f(L); // Call wrapped function and return result.
|
||||
} catch (const char *s) { // Catch and convert exceptions.
|
||||
lua_pushstring(L, s);
|
||||
} catch (std::exception& e) {
|
||||
lua_pushstring(L, e.what());
|
||||
} catch (...) {
|
||||
lua_pushliteral(L, "caught (...)");
|
||||
}
|
||||
return lua_error(L); // Rethrow as a Lua error.
|
||||
}
|
||||
|
||||
static int myinit(lua_State *L)
|
||||
{
|
||||
...
|
||||
// Define wrapper function and enable it.
|
||||
lua_pushlightuserdata(L, (void *)wrap_exceptions);
|
||||
luaJIT_setmode(L, -1, LUAJIT_MODE_WRAPCFUNC|LUAJIT_MODE_ON);
|
||||
lua_pop(L, 1);
|
||||
...
|
||||
}
|
||||
</pre>
|
||||
<p>
|
||||
Note that you can only define <b>a single global wrapper function</b>,
|
||||
so be careful when using this mechanism from multiple C++ modules.
|
||||
Also note that this mechanism is not without overhead.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+330
@@ -0,0 +1,330 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>FFI Library</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-2013, 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>FFI Library</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a class="current" href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
|
||||
The FFI library allows <b>calling external C functions</b> and
|
||||
<b>using C data structures</b> from pure Lua code.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
|
||||
The FFI library largely obviates the need to write tedious manual
|
||||
Lua/C bindings in C. No need to learn a separate binding language
|
||||
— <b>it parses plain C declarations!</b> These can be
|
||||
cut-n-pasted from C header files or reference manuals. It's up to
|
||||
the task of binding large libraries without the need for dealing with
|
||||
fragile binding generators.
|
||||
|
||||
</p>
|
||||
<p>
|
||||
The FFI library is tightly integrated into LuaJIT (it's not available
|
||||
as a separate module). The code generated by the JIT-compiler for
|
||||
accesses to C data structures from Lua code is on par with the
|
||||
code a C compiler would generate. Calls to C functions can
|
||||
be inlined in JIT-compiled code, unlike calls to functions bound via
|
||||
the classic Lua/C API.
|
||||
</p>
|
||||
<p>
|
||||
This page gives a short introduction to the usage of the FFI library.
|
||||
<em>Please use the FFI sub-topics in the navigation bar to learn more.</em>
|
||||
</p>
|
||||
|
||||
<h2 id="call">Motivating Example: Calling External C Functions</h2>
|
||||
<p>
|
||||
It's really easy to call an external C library function:
|
||||
</p>
|
||||
<pre class="code mark">
|
||||
<span class="codemark">①
|
||||
②
|
||||
|
||||
|
||||
③</span>local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">int printf(const char *fmt, ...);</span>
|
||||
]]
|
||||
ffi.C.printf("Hello %s!", "world")
|
||||
</pre>
|
||||
<p>
|
||||
So, let's pick that apart:
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">①</span> Load the FFI library.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">②</span> Add a C declaration
|
||||
for the function. The part inside the double-brackets (in green) is
|
||||
just standard C syntax.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">③</span> Call the named
|
||||
C function — Yes, it's that simple!
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Actually, what goes on behind the scenes is far from simple: <span
|
||||
style="color:#4040c0;">③</span> makes use of the standard
|
||||
C library namespace <tt>ffi.C</tt>. Indexing this namespace with
|
||||
a symbol name (<tt>"printf"</tt>) automatically binds it to the
|
||||
standard C library. The result is a special kind of object which,
|
||||
when called, runs the <tt>printf</tt> function. The arguments passed
|
||||
to this function are automatically converted from Lua objects to the
|
||||
corresponding C types.
|
||||
</p>
|
||||
<p>
|
||||
Ok, so maybe the use of <tt>printf()</tt> wasn't such a spectacular
|
||||
example. You could have done that with <tt>io.write()</tt> and
|
||||
<tt>string.format()</tt>, too. But you get the idea ...
|
||||
</p>
|
||||
<p>
|
||||
So here's something to pop up a message box on Windows:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">int MessageBoxA(void *w, const char *txt, const char *cap, int type);</span>
|
||||
]]
|
||||
ffi.C.MessageBoxA(nil, "Hello world!", "Test", 0)
|
||||
</pre>
|
||||
<p>
|
||||
Bing! Again, that was far too easy, no?
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Compare this with the effort required to bind that function using the
|
||||
classic Lua/C API: create an extra C file, add a C function
|
||||
that retrieves and checks the argument types passed from Lua and calls
|
||||
the actual C function, add a list of module functions and their
|
||||
names, add a <tt>luaopen_*</tt> function and register all module
|
||||
functions, compile and link it into a shared library (DLL), move it to
|
||||
the proper path, add Lua code that loads the module aaaand ... finally
|
||||
call the binding function. Phew!
|
||||
</p>
|
||||
|
||||
<h2 id="cdata">Motivating Example: Using C Data Structures</h2>
|
||||
<p>
|
||||
The FFI library allows you to create and access C data
|
||||
structures. Of course the main use for this is for interfacing with
|
||||
C functions. But they can be used stand-alone, too.
|
||||
</p>
|
||||
<p>
|
||||
Lua is built upon high-level data types. They are flexible, extensible
|
||||
and dynamic. That's why we all love Lua so much. Alas, this can be
|
||||
inefficient for certain tasks, where you'd really want a low-level
|
||||
data type. E.g. a large array of a fixed structure needs to be
|
||||
implemented with a big table holding lots of tiny tables. This imposes
|
||||
both a substantial memory overhead as well as a performance overhead.
|
||||
</p>
|
||||
<p>
|
||||
Here's a sketch of a library that operates on color images plus a
|
||||
simple benchmark. First, the plain Lua version:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local floor = math.floor
|
||||
|
||||
local function image_ramp_green(n)
|
||||
local img = {}
|
||||
local f = 255/(n-1)
|
||||
for i=1,n do
|
||||
img[i] = { red = 0, green = floor((i-1)*f), blue = 0, alpha = 255 }
|
||||
end
|
||||
return img
|
||||
end
|
||||
|
||||
local function image_to_grey(img, n)
|
||||
for i=1,n do
|
||||
local y = floor(0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue)
|
||||
img[i].red = y; img[i].green = y; img[i].blue = y
|
||||
end
|
||||
end
|
||||
|
||||
local N = 400*400
|
||||
local img = image_ramp_green(N)
|
||||
for i=1,1000 do
|
||||
image_to_grey(img, N)
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
This creates a table with 160.000 pixels, each of which is a table
|
||||
holding four number values in the range of 0-255. First an image with
|
||||
a green ramp is created (1D for simplicity), then the image is
|
||||
converted to greyscale 1000 times. Yes, that's silly, but I was in
|
||||
need of a simple example ...
|
||||
</p>
|
||||
<p>
|
||||
And here's the FFI version. The modified parts have been marked in
|
||||
bold:
|
||||
</p>
|
||||
<pre class="code mark">
|
||||
<span class="codemark">①
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
②
|
||||
|
||||
③
|
||||
④
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
③
|
||||
⑤</span><b>local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
</b><span style="color:#00a000;">typedef struct { uint8_t red, green, blue, alpha; } rgba_pixel;</span><b>
|
||||
]]</b>
|
||||
|
||||
local function image_ramp_green(n)
|
||||
<b>local img = ffi.new("rgba_pixel[?]", n)</b>
|
||||
local f = 255/(n-1)
|
||||
for i=<b>0,n-1</b> do
|
||||
<b>img[i].green = i*f</b>
|
||||
<b>img[i].alpha = 255</b>
|
||||
end
|
||||
return img
|
||||
end
|
||||
|
||||
local function image_to_grey(img, n)
|
||||
for i=<b>0,n-1</b> do
|
||||
local y = <b>0.3*img[i].red + 0.59*img[i].green + 0.11*img[i].blue</b>
|
||||
img[i].red = y; img[i].green = y; img[i].blue = y
|
||||
end
|
||||
end
|
||||
|
||||
local N = 400*400
|
||||
local img = image_ramp_green(N)
|
||||
for i=1,1000 do
|
||||
image_to_grey(img, N)
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
Ok, so that wasn't too difficult:
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">①</span> First, load the FFI
|
||||
library and declare the low-level data type. Here we choose a
|
||||
<tt>struct</tt> which holds four byte fields, one for each component
|
||||
of a 4x8 bit RGBA pixel.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">②</span> Creating the data
|
||||
structure with <tt>ffi.new()</tt> is straightforward — the
|
||||
<tt>'?'</tt> is a placeholder for the number of elements of a
|
||||
variable-length array.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">③</span> C arrays are
|
||||
zero-based, so the indexes have to run from <tt>0</tt> to
|
||||
<tt>n-1</tt>. One might want to allocate one more element instead to
|
||||
simplify converting legacy code.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">④</span> Since <tt>ffi.new()</tt>
|
||||
zero-fills the array by default, we only need to set the green and the
|
||||
alpha fields.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑤</span> The calls to
|
||||
<tt>math.floor()</tt> can be omitted here, because floating-point
|
||||
numbers are already truncated towards zero when converting them to an
|
||||
integer. This happens implicitly when the number is stored in the
|
||||
fields of each pixel.
|
||||
</p>
|
||||
<p>
|
||||
Now let's have a look at the impact of the changes: first, memory
|
||||
consumption for the image is down from 22 Megabytes to
|
||||
640 Kilobytes (400*400*4 bytes). That's a factor of 35x less! So,
|
||||
yes, tables do have a noticeable overhead. BTW: The original program
|
||||
would consume 40 Megabytes in plain Lua (on x64).
|
||||
</p>
|
||||
<p>
|
||||
Next, performance: the pure Lua version runs in 9.57 seconds (52.9
|
||||
seconds with the Lua interpreter) and the FFI version runs in 0.48
|
||||
seconds on my machine (YMMV). That's a factor of 20x faster (110x
|
||||
faster than the Lua interpreter).
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
The avid reader may notice that converting the pure Lua version over
|
||||
to use array indexes for the colors (<tt>[1]</tt> instead of
|
||||
<tt>.red</tt>, <tt>[2]</tt> instead of <tt>.green</tt> etc.) ought to
|
||||
be more compact and faster. This is certainly true (by a factor of
|
||||
~1.7x). Switching to a struct-of-arrays would help, too.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
However the resulting code would be less idiomatic and rather
|
||||
error-prone. And it still doesn't get even close to the performance of
|
||||
the FFI version of the code. Also, high-level data structures cannot
|
||||
be easily passed to other C functions, especially I/O functions,
|
||||
without undue conversion penalties.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+566
@@ -0,0 +1,566 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>ffi.* API Functions</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-2013, 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.abitable { width: 30em; line-height: 1.2; }
|
||||
tr.abihead td { font-weight: bold; }
|
||||
td.abiparam { font-weight: bold; width: 6em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1><tt>ffi.*</tt> API Functions</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a class="current" href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
This page describes the API functions provided by the FFI library in
|
||||
detail. It's recommended to read through the
|
||||
<a href="ext_ffi.html">introduction</a> and the
|
||||
<a href="ext_ffi_tutorial.html">FFI tutorial</a> first.
|
||||
</p>
|
||||
|
||||
<h2 id="glossary">Glossary</h2>
|
||||
<ul>
|
||||
<li><b>cdecl</b> — An abstract C type declaration (a Lua
|
||||
string).</li>
|
||||
<li><b>ctype</b> — A C type object. This is a special kind of
|
||||
<b>cdata</b> returned by <tt>ffi.typeof()</tt>. It serves as a
|
||||
<b>cdata</b> <a href="#ffi_new">constructor</a> when called.</li>
|
||||
<li><b>cdata</b> — A C data object. It holds a value of the
|
||||
corresponding <b>ctype</b>.</li>
|
||||
<li><b>ct</b> — A C type specification which can be used for
|
||||
most of the API functions. Either a <b>cdecl</b>, a <b>ctype</b> or a
|
||||
<b>cdata</b> serving as a template type.</li>
|
||||
<li><b>cb</b> — A callback object. This is a C data object
|
||||
holding a special function pointer. Calling this function from
|
||||
C code runs an associated Lua function.</li>
|
||||
<li><b>VLA</b> — A variable-length array is declared with a
|
||||
<tt>?</tt> instead of the number of elements, e.g. <tt>"int[?]"</tt>.
|
||||
The number of elements (<tt>nelem</tt>) must be given when it's
|
||||
<a href="#ffi_new">created</a>.</li>
|
||||
<li><b>VLS</b> — A variable-length struct is a <tt>struct</tt> C
|
||||
type where the last element is a <b>VLA</b>. The same rules for
|
||||
declaration and creation apply.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="decl">Declaring and Accessing External Symbols</h2>
|
||||
<p>
|
||||
External symbols must be declared first and can then be accessed by
|
||||
indexing a <a href="ext_ffi_semantics.html#clib">C library
|
||||
namespace</a>, which automatically binds the symbol to a specific
|
||||
library.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_cdef"><tt>ffi.cdef(def)</tt></h3>
|
||||
<p>
|
||||
Adds multiple C declarations for types or external symbols (named
|
||||
variables or functions). <tt>def</tt> must be a Lua string. It's
|
||||
recommended to use the syntactic sugar for string arguments as
|
||||
follows:
|
||||
</p>
|
||||
<pre class="code">
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">typedef struct foo { int a, b; } foo_t; // Declare a struct and typedef.
|
||||
int dofoo(foo_t *f, int n); /* Declare an external C function. */</span>
|
||||
]]
|
||||
</pre>
|
||||
<p>
|
||||
The contents of the string (the part in green above) must be a
|
||||
sequence of
|
||||
<a href="ext_ffi_semantics.html#clang">C declarations</a>,
|
||||
separated by semicolons. The trailing semicolon for a single
|
||||
declaration may be omitted.
|
||||
</p>
|
||||
<p>
|
||||
Please note that external symbols are only <em>declared</em>, but they
|
||||
are <em>not bound</em> to any specific address, yet. Binding is
|
||||
achieved with C library namespaces (see below).
|
||||
</p>
|
||||
<p style="color: #c00000;">
|
||||
C declarations are not passed through a C pre-processor,
|
||||
yet. No pre-processor tokens are allowed, except for
|
||||
<tt>#pragma pack</tt>. Replace <tt>#define</tt> in existing
|
||||
C header files with <tt>enum</tt>, <tt>static const</tt>
|
||||
or <tt>typedef</tt> and/or pass the files through an external
|
||||
C pre-processor (once). Be careful not to include unneeded or
|
||||
redundant declarations from unrelated header files.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_C"><tt>ffi.C</tt></h3>
|
||||
<p>
|
||||
This is the default C library namespace — note the
|
||||
uppercase <tt>'C'</tt>. It binds to the default set of symbols or
|
||||
libraries on the target system. These are more or less the same as a
|
||||
C compiler would offer by default, without specifying extra link
|
||||
libraries.
|
||||
</p>
|
||||
<p>
|
||||
On POSIX systems, this binds to symbols in the default or global
|
||||
namespace. This includes all exported symbols from the executable and
|
||||
any libraries loaded into the global namespace. This includes at least
|
||||
<tt>libc</tt>, <tt>libm</tt>, <tt>libdl</tt> (on Linux),
|
||||
<tt>libgcc</tt> (if compiled with GCC), as well as any exported
|
||||
symbols from the Lua/C API provided by LuaJIT itself.
|
||||
</p>
|
||||
<p>
|
||||
On Windows systems, this binds to symbols exported from the
|
||||
<tt>*.exe</tt>, the <tt>lua51.dll</tt> (i.e. the Lua/C API
|
||||
provided by LuaJIT itself), the C runtime library LuaJIT was linked
|
||||
with (<tt>msvcrt*.dll</tt>), <tt>kernel32.dll</tt>,
|
||||
<tt>user32.dll</tt> and <tt>gdi32.dll</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_load"><tt>clib = ffi.load(name [,global])</tt></h3>
|
||||
<p>
|
||||
This loads the dynamic library given by <tt>name</tt> and returns
|
||||
a new C library namespace which binds to its symbols. On POSIX
|
||||
systems, if <tt>global</tt> is <tt>true</tt>, the library symbols are
|
||||
loaded into the global namespace, too.
|
||||
</p>
|
||||
<p>
|
||||
If <tt>name</tt> is a path, the library is loaded from this path.
|
||||
Otherwise <tt>name</tt> is canonicalized in a system-dependent way and
|
||||
searched in the default search path for dynamic libraries:
|
||||
</p>
|
||||
<p>
|
||||
On POSIX systems, if the name contains no dot, the extension
|
||||
<tt>.so</tt> is appended. Also, the <tt>lib</tt> prefix is prepended
|
||||
if necessary. So <tt>ffi.load("z")</tt> looks for <tt>"libz.so"</tt>
|
||||
in the default shared library search path.
|
||||
</p>
|
||||
<p>
|
||||
On Windows systems, if the name contains no dot, the extension
|
||||
<tt>.dll</tt> is appended. So <tt>ffi.load("ws2_32")</tt> looks for
|
||||
<tt>"ws2_32.dll"</tt> in the default DLL search path.
|
||||
</p>
|
||||
|
||||
<h2 id="create">Creating cdata Objects</h2>
|
||||
<p>
|
||||
The following API functions create cdata objects (<tt>type()</tt>
|
||||
returns <tt>"cdata"</tt>). All created cdata objects are
|
||||
<a href="ext_ffi_semantics.html#gc">garbage collected</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_new"><tt>cdata = ffi.new(ct [,nelem] [,init...])<br>
|
||||
cdata = <em>ctype</em>([nelem,] [init...])</tt></h3>
|
||||
<p>
|
||||
Creates a cdata object for the given <tt>ct</tt>. VLA/VLS types
|
||||
require the <tt>nelem</tt> argument. The second syntax uses a ctype as
|
||||
a constructor and is otherwise fully equivalent.
|
||||
</p>
|
||||
<p>
|
||||
The cdata object is initialized according to the
|
||||
<a href="ext_ffi_semantics.html#init">rules for initializers</a>,
|
||||
using the optional <tt>init</tt> arguments. Excess initializers cause
|
||||
an error.
|
||||
</p>
|
||||
<p>
|
||||
Performance notice: if you want to create many objects of one kind,
|
||||
parse the cdecl only once and get its ctype with
|
||||
<tt>ffi.typeof()</tt>. Then use the ctype as a constructor repeatedly.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Please note that an anonymous <tt>struct</tt> declaration implicitly
|
||||
creates a new and distinguished ctype every time you use it for
|
||||
<tt>ffi.new()</tt>. This is probably <b>not</b> what you want,
|
||||
especially if you create more than one cdata object. Different anonymous
|
||||
<tt>structs</tt> are not considered assignment-compatible by the
|
||||
C standard, even though they may have the same fields! Also, they
|
||||
are considered different types by the JIT-compiler, which may cause an
|
||||
excessive number of traces. It's strongly suggested to either declare
|
||||
a named <tt>struct</tt> or <tt>typedef</tt> with <tt>ffi.cdef()</tt>
|
||||
or to create a single ctype object for an anonymous <tt>struct</tt>
|
||||
with <tt>ffi.typeof()</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_typeof"><tt>ctype = ffi.typeof(ct)</tt></h3>
|
||||
<p>
|
||||
Creates a ctype object for the given <tt>ct</tt>.
|
||||
</p>
|
||||
<p>
|
||||
This function is especially useful to parse a cdecl only once and then
|
||||
use the resulting ctype object as a <a href="#ffi_new">constructor</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_cast"><tt>cdata = ffi.cast(ct, init)</tt></h3>
|
||||
<p>
|
||||
Creates a scalar cdata object for the given <tt>ct</tt>. The cdata
|
||||
object is initialized with <tt>init</tt> using the "cast" variant of
|
||||
the <a href="ext_ffi_semantics.html#convert">C type conversion
|
||||
rules</a>.
|
||||
</p>
|
||||
<p>
|
||||
This functions is mainly useful to override the pointer compatibility
|
||||
checks or to convert pointers to addresses or vice versa.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_metatype"><tt>ctype = ffi.metatype(ct, metatable)</tt></h3>
|
||||
<p>
|
||||
Creates a ctype object for the given <tt>ct</tt> and associates it with
|
||||
a metatable. Only <tt>struct</tt>/<tt>union</tt> types, complex numbers
|
||||
and vectors are allowed. Other types may be wrapped in a
|
||||
<tt>struct</tt>, if needed.
|
||||
</p>
|
||||
<p>
|
||||
The association with a metatable is permanent and cannot be changed
|
||||
afterwards. Neither the contents of the <tt>metatable</tt> nor the
|
||||
contents of an <tt>__index</tt> table (if any) may be modified
|
||||
afterwards. The associated metatable automatically applies to all uses
|
||||
of this type, no matter how the objects are created or where they
|
||||
originate from. Note that pre-defined operations on types have
|
||||
precedence (e.g. declared field names cannot be overriden).
|
||||
</p>
|
||||
<p>
|
||||
All standard Lua metamethods are implemented. These are called directly,
|
||||
without shortcuts and on any mix of types. For binary operations, the
|
||||
left operand is checked first for a valid ctype metamethod. The
|
||||
<tt>__gc</tt> metamethod only applies to <tt>struct</tt>/<tt>union</tt>
|
||||
types and performs an implicit <a href="#ffi_gc"><tt>ffi.gc()</tt></a>
|
||||
call during creation of an instance.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_gc"><tt>cdata = ffi.gc(cdata, finalizer)</tt></h3>
|
||||
<p>
|
||||
Associates a finalizer with a pointer or aggregate cdata object. The
|
||||
cdata object is returned unchanged.
|
||||
</p>
|
||||
<p>
|
||||
This function allows safe integration of unmanaged resources into the
|
||||
automatic memory management of the LuaJIT garbage collector. Typical
|
||||
usage:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local p = ffi.gc(ffi.C.malloc(n), ffi.C.free)
|
||||
...
|
||||
p = nil -- Last reference to p is gone.
|
||||
-- GC will eventually run finalizer: ffi.C.free(p)
|
||||
</pre>
|
||||
<p>
|
||||
A cdata finalizer works like the <tt>__gc</tt> metamethod for userdata
|
||||
objects: when the last reference to a cdata object is gone, the
|
||||
associated finalizer is called with the cdata object as an argument. The
|
||||
finalizer can be a Lua function or a cdata function or cdata function
|
||||
pointer. An existing finalizer can be removed by setting a <tt>nil</tt>
|
||||
finalizer, e.g. right before explicitly deleting a resource:
|
||||
</p>
|
||||
<pre class="code">
|
||||
ffi.C.free(ffi.gc(p, nil)) -- Manually free the memory.
|
||||
</pre>
|
||||
|
||||
<h2 id="info">C Type Information</h2>
|
||||
<p>
|
||||
The following API functions return information about C types.
|
||||
They are most useful for inspecting cdata objects.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_sizeof"><tt>size = ffi.sizeof(ct [,nelem])</tt></h3>
|
||||
<p>
|
||||
Returns the size of <tt>ct</tt> in bytes. Returns <tt>nil</tt> if
|
||||
the size is not known (e.g. for <tt>"void"</tt> or function types).
|
||||
Requires <tt>nelem</tt> for VLA/VLS types, except for cdata objects.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_alignof"><tt>align = ffi.alignof(ct)</tt></h3>
|
||||
<p>
|
||||
Returns the minimum required alignment for <tt>ct</tt> in bytes.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_offsetof"><tt>ofs [,bpos,bsize] = ffi.offsetof(ct, field)</tt></h3>
|
||||
<p>
|
||||
Returns the offset (in bytes) of <tt>field</tt> relative to the start
|
||||
of <tt>ct</tt>, which must be a <tt>struct</tt>. Additionally returns
|
||||
the position and the field size (in bits) for bit fields.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_istype"><tt>status = ffi.istype(ct, obj)</tt></h3>
|
||||
<p>
|
||||
Returns <tt>true</tt> if <tt>obj</tt> has the C type given by
|
||||
<tt>ct</tt>. Returns <tt>false</tt> otherwise.
|
||||
</p>
|
||||
<p>
|
||||
C type qualifiers (<tt>const</tt> etc.) are ignored. Pointers are
|
||||
checked with the standard pointer compatibility rules, but without any
|
||||
special treatment for <tt>void *</tt>. If <tt>ct</tt> specifies a
|
||||
<tt>struct</tt>/<tt>union</tt>, then a pointer to this type is accepted,
|
||||
too. Otherwise the types must match exactly.
|
||||
</p>
|
||||
<p>
|
||||
Note: this function accepts all kinds of Lua objects for the
|
||||
<tt>obj</tt> argument, but always returns <tt>false</tt> for non-cdata
|
||||
objects.
|
||||
</p>
|
||||
|
||||
<h2 id="util">Utility Functions</h2>
|
||||
|
||||
<h3 id="ffi_errno"><tt>err = ffi.errno([newerr])</tt></h3>
|
||||
<p>
|
||||
Returns the error number set by the last C function call which
|
||||
indicated an error condition. If the optional <tt>newerr</tt> argument
|
||||
is present, the error number is set to the new value and the previous
|
||||
value is returned.
|
||||
</p>
|
||||
<p>
|
||||
This function offers a portable and OS-independent way to get and set the
|
||||
error number. Note that only <em>some</em> C functions set the error
|
||||
number. And it's only significant if the function actually indicated an
|
||||
error condition (e.g. with a return value of <tt>-1</tt> or
|
||||
<tt>NULL</tt>). Otherwise, it may or may not contain any previously set
|
||||
value.
|
||||
</p>
|
||||
<p>
|
||||
You're advised to call this function only when needed and as close as
|
||||
possible after the return of the related C function. The
|
||||
<tt>errno</tt> value is preserved across hooks, memory allocations,
|
||||
invocations of the JIT compiler and other internal VM activity. The same
|
||||
applies to the value returned by <tt>GetLastError()</tt> on Windows, but
|
||||
you need to declare and call it yourself.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_string"><tt>str = ffi.string(ptr [,len])</tt></h3>
|
||||
<p>
|
||||
Creates an interned Lua string from the data pointed to by
|
||||
<tt>ptr</tt>.
|
||||
</p>
|
||||
<p>
|
||||
If the optional argument <tt>len</tt> is missing, <tt>ptr</tt> is
|
||||
converted to a <tt>"char *"</tt> and the data is assumed to be
|
||||
zero-terminated. The length of the string is computed with
|
||||
<tt>strlen()</tt>.
|
||||
</p>
|
||||
<p>
|
||||
Otherwise <tt>ptr</tt> is converted to a <tt>"void *"</tt> and
|
||||
<tt>len</tt> gives the length of the data. The data may contain
|
||||
embedded zeros and need not be byte-oriented (though this may cause
|
||||
endianess issues).
|
||||
</p>
|
||||
<p>
|
||||
This function is mainly useful to convert (temporary)
|
||||
<tt>"const char *"</tt> pointers returned by
|
||||
C functions to Lua strings and store them or pass them to other
|
||||
functions expecting a Lua string. The Lua string is an (interned) copy
|
||||
of the data and bears no relation to the original data area anymore.
|
||||
Lua strings are 8 bit clean and may be used to hold arbitrary,
|
||||
non-character data.
|
||||
</p>
|
||||
<p>
|
||||
Performance notice: it's faster to pass the length of the string, if
|
||||
it's known. E.g. when the length is returned by a C call like
|
||||
<tt>sprintf()</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_copy"><tt>ffi.copy(dst, src, len)<br>
|
||||
ffi.copy(dst, str)</tt></h3>
|
||||
<p>
|
||||
Copies the data pointed to by <tt>src</tt> to <tt>dst</tt>.
|
||||
<tt>dst</tt> is converted to a <tt>"void *"</tt> and <tt>src</tt>
|
||||
is converted to a <tt>"const void *"</tt>.
|
||||
</p>
|
||||
<p>
|
||||
In the first syntax, <tt>len</tt> gives the number of bytes to copy.
|
||||
Caveat: if <tt>src</tt> is a Lua string, then <tt>len</tt> must not
|
||||
exceed <tt>#src+1</tt>.
|
||||
</p>
|
||||
<p>
|
||||
In the second syntax, the source of the copy must be a Lua string. All
|
||||
bytes of the string <em>plus a zero-terminator</em> are copied to
|
||||
<tt>dst</tt> (i.e. <tt>#src+1</tt> bytes).
|
||||
</p>
|
||||
<p>
|
||||
Performance notice: <tt>ffi.copy()</tt> may be used as a faster
|
||||
(inlinable) replacement for the C library functions
|
||||
<tt>memcpy()</tt>, <tt>strcpy()</tt> and <tt>strncpy()</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_fill"><tt>ffi.fill(dst, len [,c])</tt></h3>
|
||||
<p>
|
||||
Fills the data pointed to by <tt>dst</tt> with <tt>len</tt> constant
|
||||
bytes, given by <tt>c</tt>. If <tt>c</tt> is omitted, the data is
|
||||
zero-filled.
|
||||
</p>
|
||||
<p>
|
||||
Performance notice: <tt>ffi.fill()</tt> may be used as a faster
|
||||
(inlinable) replacement for the C library function
|
||||
<tt>memset(dst, c, len)</tt>. Please note the different
|
||||
order of arguments!
|
||||
</p>
|
||||
|
||||
<h2 id="target">Target-specific Information</h2>
|
||||
|
||||
<h3 id="ffi_abi"><tt>status = ffi.abi(param)</tt></h3>
|
||||
<p>
|
||||
Returns <tt>true</tt> if <tt>param</tt> (a Lua string) applies for the
|
||||
target ABI (Application Binary Interface). Returns <tt>false</tt>
|
||||
otherwise. The following parameters are currently defined:
|
||||
</p>
|
||||
<table class="abitable">
|
||||
<tr class="abihead">
|
||||
<td class="abiparam">Parameter</td>
|
||||
<td class="abidesc">Description</td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="abiparam">32bit</td><td class="abidesc">32 bit architecture</td></tr>
|
||||
<tr class="even">
|
||||
<td class="abiparam">64bit</td><td class="abidesc">64 bit architecture</td></tr>
|
||||
<tr class="odd separate">
|
||||
<td class="abiparam">le</td><td class="abidesc">Little-endian architecture</td></tr>
|
||||
<tr class="even">
|
||||
<td class="abiparam">be</td><td class="abidesc">Big-endian architecture</td></tr>
|
||||
<tr class="odd separate">
|
||||
<td class="abiparam">fpu</td><td class="abidesc">Target has a hardware FPU</td></tr>
|
||||
<tr class="even">
|
||||
<td class="abiparam">softfp</td><td class="abidesc">softfp calling conventions</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="abiparam">hardfp</td><td class="abidesc">hardfp calling conventions</td></tr>
|
||||
<tr class="even separate">
|
||||
<td class="abiparam">eabi</td><td class="abidesc">EABI variant of the standard ABI</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="abiparam">win</td><td class="abidesc">Windows variant of the standard ABI</td></tr>
|
||||
</table>
|
||||
|
||||
<h3 id="ffi_os"><tt>ffi.os</tt></h3>
|
||||
<p>
|
||||
Contains the target OS name. Same contents as
|
||||
<a href="ext_jit.html#jit_os"><tt>jit.os</tt></a>.
|
||||
</p>
|
||||
|
||||
<h3 id="ffi_arch"><tt>ffi.arch</tt></h3>
|
||||
<p>
|
||||
Contains the target architecture name. Same contents as
|
||||
<a href="ext_jit.html#jit_arch"><tt>jit.arch</tt></a>.
|
||||
</p>
|
||||
|
||||
<h2 id="callback">Methods for Callbacks</h2>
|
||||
<p>
|
||||
The C types for <a href="ext_ffi_semantics.html#callback">callbacks</a>
|
||||
have some extra methods:
|
||||
</p>
|
||||
|
||||
<h3 id="callback_free"><tt>cb:free()</tt></h3>
|
||||
<p>
|
||||
Free the resources associated with a callback. The associated Lua
|
||||
function is unanchored and may be garbage collected. The callback
|
||||
function pointer is no longer valid and must not be called anymore
|
||||
(it may be reused by a subsequently created callback).
|
||||
</p>
|
||||
|
||||
<h3 id="callback_set"><tt>cb:set(func)</tt></h3>
|
||||
<p>
|
||||
Associate a new Lua function with a callback. The C type of the
|
||||
callback and the callback function pointer are unchanged.
|
||||
</p>
|
||||
<p>
|
||||
This method is useful to dynamically switch the receiver of callbacks
|
||||
without creating a new callback each time and registering it again (e.g.
|
||||
with a GUI library).
|
||||
</p>
|
||||
|
||||
<h2 id="extended">Extended Standard Library Functions</h2>
|
||||
<p>
|
||||
The following standard library functions have been extended to work
|
||||
with cdata objects:
|
||||
</p>
|
||||
|
||||
<h3 id="tonumber"><tt>n = tonumber(cdata)</tt></h3>
|
||||
<p>
|
||||
Converts a number cdata object to a <tt>double</tt> and returns it as
|
||||
a Lua number. This is particularly useful for boxed 64 bit
|
||||
integer values. Caveat: this conversion may incur a precision loss.
|
||||
</p>
|
||||
|
||||
<h3 id="tostring"><tt>s = tostring(cdata)</tt></h3>
|
||||
<p>
|
||||
Returns a string representation of the value of 64 bit integers
|
||||
(<tt><b>"</b>nnn<b>LL"</b></tt> or <tt><b>"</b>nnn<b>ULL"</b></tt>) or
|
||||
complex numbers (<tt><b>"</b>re±im<b>i"</b></tt>). Otherwise
|
||||
returns a string representation of the C type of a ctype object
|
||||
(<tt><b>"ctype<</b>type<b>>"</b></tt>) or a cdata object
|
||||
(<tt><b>"cdata<</b>type<b>>: </b>address"</tt>), unless you
|
||||
override it with a <tt>__tostring</tt> metamethod (see
|
||||
<a href="#ffi_metatype"><tt>ffi.metatype()</tt></a>).
|
||||
</p>
|
||||
|
||||
<h3 id="pairs"><tt>iter, obj, start = pairs(cdata)<br>
|
||||
iter, obj, start = ipairs(cdata)<br></tt></h3>
|
||||
<p>
|
||||
Calls the <tt>__pairs</tt> or <tt>__ipairs</tt> metamethod of the
|
||||
corresponding ctype.
|
||||
</p>
|
||||
|
||||
<h2 id="literals">Extensions to the Lua Parser</h2>
|
||||
<p>
|
||||
The parser for Lua source code treats numeric literals with the
|
||||
suffixes <tt>LL</tt> or <tt>ULL</tt> as signed or unsigned 64 bit
|
||||
integers. Case doesn't matter, but uppercase is recommended for
|
||||
readability. It handles both decimal (<tt>42LL</tt>) and hexadecimal
|
||||
(<tt>0x2aLL</tt>) literals.
|
||||
</p>
|
||||
<p>
|
||||
The imaginary part of complex numbers can be specified by suffixing
|
||||
number literals with <tt>i</tt> or <tt>I</tt>, e.g. <tt>12.5i</tt>.
|
||||
Caveat: you'll need to use <tt>1i</tt> to get an imaginary part with
|
||||
the value one, since <tt>i</tt> itself still refers to a variable
|
||||
named <tt>i</tt>.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+1243
File diff suppressed because it is too large
Load Diff
+601
@@ -0,0 +1,601 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>FFI Tutorial</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-2013, 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.idiomtable { font-size: 90%; line-height: 1.2; }
|
||||
table.idiomtable tt { font-size: 100%; }
|
||||
table.idiomtable td { vertical-align: top; }
|
||||
tr.idiomhead td { font-weight: bold; }
|
||||
td.idiomlua b { font-weight: normal; color: #2142bf; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>FFI Tutorial</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a class="current" href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
This page is intended to give you an overview of the features of the FFI
|
||||
library by presenting a few use cases and guidelines.
|
||||
</p>
|
||||
<p>
|
||||
This page makes no attempt to explain all of the FFI library, though.
|
||||
You'll want to have a look at the <a href="ext_ffi_api.html">ffi.* API
|
||||
function reference</a> and the <a href="ext_ffi_semantics.html">FFI
|
||||
semantics</a> to learn more.
|
||||
</p>
|
||||
|
||||
<h2 id="load">Loading the FFI Library</h2>
|
||||
<p>
|
||||
The FFI library is built into LuaJIT by default, but it's not loaded
|
||||
and initialized by default. The suggested way to use the FFI library
|
||||
is to add the following to the start of every Lua file that needs one
|
||||
of its functions:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local ffi = require("ffi")
|
||||
</pre>
|
||||
<p>
|
||||
Please note this doesn't define an <tt>ffi</tt> variable in the table
|
||||
of globals — you really need to use the local variable. The
|
||||
<tt>require</tt> function ensures the library is only loaded once.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Note: If you want to experiment with the FFI from the interactive prompt
|
||||
of the command line executable, omit the <tt>local</tt>, as it doesn't
|
||||
preserve local variables across lines.
|
||||
</p>
|
||||
|
||||
<h2 id="sleep">Accessing Standard System Functions</h2>
|
||||
<p>
|
||||
The following code explains how to access standard system functions.
|
||||
We slowly print two lines of dots by sleeping for 10 milliseconds
|
||||
after each dot:
|
||||
</p>
|
||||
<pre class="code mark">
|
||||
<span class="codemark">
|
||||
①
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
②
|
||||
③
|
||||
④
|
||||
|
||||
|
||||
|
||||
⑤
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
⑥</span>local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">void Sleep(int ms);
|
||||
int poll(struct pollfd *fds, unsigned long nfds, int timeout);</span>
|
||||
]]
|
||||
|
||||
local sleep
|
||||
if ffi.os == "Windows" then
|
||||
function sleep(s)
|
||||
ffi.C.Sleep(s*1000)
|
||||
end
|
||||
else
|
||||
function sleep(s)
|
||||
ffi.C.poll(nil, 0, s*1000)
|
||||
end
|
||||
end
|
||||
|
||||
for i=1,160 do
|
||||
io.write("."); io.flush()
|
||||
sleep(0.01)
|
||||
end
|
||||
io.write("\n")
|
||||
</pre>
|
||||
<p>
|
||||
Here's the step-by-step explanation:
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">①</span> This defines the
|
||||
C library functions we're going to use. The part inside the
|
||||
double-brackets (in green) is just standard C syntax. You can
|
||||
usually get this info from the C header files or the
|
||||
documentation provided by each C library or C compiler.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">②</span> The difficulty we're
|
||||
facing here, is that there are different standards to choose from.
|
||||
Windows has a simple <tt>Sleep()</tt> function. On other systems there
|
||||
are a variety of functions available to achieve sub-second sleeps, but
|
||||
with no clear consensus. Thankfully <tt>poll()</tt> can be used for
|
||||
this task, too, and it's present on most non-Windows systems. The
|
||||
check for <tt>ffi.os</tt> makes sure we use the Windows-specific
|
||||
function only on Windows systems.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">③</span> Here we're wrapping the
|
||||
call to the C function in a Lua function. This isn't strictly
|
||||
necessary, but it's helpful to deal with system-specific issues only
|
||||
in one part of the code. The way we're wrapping it ensures the check
|
||||
for the OS is only done during initialization and not for every call.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">④</span> A more subtle point is
|
||||
that we defined our <tt>sleep()</tt> function (for the sake of this
|
||||
example) as taking the number of seconds, but accepting fractional
|
||||
seconds. Multiplying this by 1000 gets us milliseconds, but that still
|
||||
leaves it a Lua number, which is a floating-point value. Alas, the
|
||||
<tt>Sleep()</tt> function only accepts an integer value. Luckily for
|
||||
us, the FFI library automatically performs the conversion when calling
|
||||
the function (truncating the FP value towards zero, like in C).
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Some readers will notice that <tt>Sleep()</tt> is part of
|
||||
<tt>KERNEL32.DLL</tt> and is also a <tt>stdcall</tt> function. So how
|
||||
can this possibly work? The FFI library provides the <tt>ffi.C</tt>
|
||||
default C library namespace, which allows calling functions from
|
||||
the default set of libraries, like a C compiler would. Also, the
|
||||
FFI library automatically detects <tt>stdcall</tt> functions, so you
|
||||
don't need to declare them as such.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑤</span> The <tt>poll()</tt>
|
||||
function takes a couple more arguments we're not going to use. You can
|
||||
simply use <tt>nil</tt> to pass a <tt>NULL</tt> pointer and <tt>0</tt>
|
||||
for the <tt>nfds</tt> parameter. Please note that the
|
||||
number <tt>0</tt> <em>does not convert to a pointer value</em>,
|
||||
unlike in C++. You really have to pass pointers to pointer arguments
|
||||
and numbers to number arguments.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
The page on <a href="ext_ffi_semantics.html">FFI semantics</a> has all
|
||||
of the gory details about
|
||||
<a href="ext_ffi_semantics.html#convert">conversions between Lua
|
||||
objects and C types</a>. For the most part you don't have to deal
|
||||
with this, as it's performed automatically and it's carefully designed
|
||||
to bridge the semantic differences between Lua and C.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑥</span> Now that we have defined
|
||||
our own <tt>sleep()</tt> function, we can just call it from plain Lua
|
||||
code. That wasn't so bad, huh? Turning these boring animated dots into
|
||||
a fascinating best-selling game is left as an exercise for the reader.
|
||||
:-)
|
||||
</p>
|
||||
|
||||
<h2 id="zlib">Accessing the zlib Compression Library</h2>
|
||||
<p>
|
||||
The following code shows how to access the <a
|
||||
href="http://zlib.net/">zlib</a> compression library from Lua code.
|
||||
We'll define two convenience wrapper functions that take a string and
|
||||
compress or uncompress it to another string:
|
||||
</p>
|
||||
<pre class="code mark">
|
||||
<span class="codemark">
|
||||
①
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
②
|
||||
|
||||
|
||||
③
|
||||
|
||||
④
|
||||
|
||||
|
||||
⑤
|
||||
|
||||
|
||||
⑥
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
⑦</span>local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">unsigned long compressBound(unsigned long sourceLen);
|
||||
int compress2(uint8_t *dest, unsigned long *destLen,
|
||||
const uint8_t *source, unsigned long sourceLen, int level);
|
||||
int uncompress(uint8_t *dest, unsigned long *destLen,
|
||||
const uint8_t *source, unsigned long sourceLen);</span>
|
||||
]]
|
||||
local zlib = ffi.load(ffi.os == "Windows" and "zlib1" or "z")
|
||||
|
||||
local function compress(txt)
|
||||
local n = zlib.compressBound(#txt)
|
||||
local buf = ffi.new("uint8_t[?]", n)
|
||||
local buflen = ffi.new("unsigned long[1]", n)
|
||||
local res = zlib.compress2(buf, buflen, txt, #txt, 9)
|
||||
assert(res == 0)
|
||||
return ffi.string(buf, buflen[0])
|
||||
end
|
||||
|
||||
local function uncompress(comp, n)
|
||||
local buf = ffi.new("uint8_t[?]", n)
|
||||
local buflen = ffi.new("unsigned long[1]", n)
|
||||
local res = zlib.uncompress(buf, buflen, comp, #comp)
|
||||
assert(res == 0)
|
||||
return ffi.string(buf, buflen[0])
|
||||
end
|
||||
|
||||
-- Simple test code.
|
||||
local txt = string.rep("abcd", 1000)
|
||||
print("Uncompressed size: ", #txt)
|
||||
local c = compress(txt)
|
||||
print("Compressed size: ", #c)
|
||||
local txt2 = uncompress(c, #txt)
|
||||
assert(txt2 == txt)
|
||||
</pre>
|
||||
<p>
|
||||
Here's the step-by-step explanation:
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">①</span> This defines some of the
|
||||
C functions provided by zlib. For the sake of this example, some
|
||||
type indirections have been reduced and it uses the pre-defined
|
||||
fixed-size integer types, while still adhering to the zlib API/ABI.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">②</span> This loads the zlib shared
|
||||
library. On POSIX systems it's named <tt>libz.so</tt> and usually
|
||||
comes pre-installed. Since <tt>ffi.load()</tt> automatically adds any
|
||||
missing standard prefixes/suffixes, we can simply load the
|
||||
<tt>"z"</tt> library. On Windows it's named <tt>zlib1.dll</tt> and
|
||||
you'll have to download it first from the
|
||||
<a href="http://zlib.net/"><span class="ext">»</span> zlib site</a>. The check for
|
||||
<tt>ffi.os</tt> makes sure we pass the right name to
|
||||
<tt>ffi.load()</tt>.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">③</span> First, the maximum size of
|
||||
the compression buffer is obtained by calling the
|
||||
<tt>zlib.compressBound</tt> function with the length of the
|
||||
uncompressed string. The next line allocates a byte buffer of this
|
||||
size. The <tt>[?]</tt> in the type specification indicates a
|
||||
variable-length array (VLA). The actual number of elements of this
|
||||
array is given as the 2nd argument to <tt>ffi.new()</tt>.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">④</span> This may look strange at
|
||||
first, but have a look at the declaration of the <tt>compress2</tt>
|
||||
function from zlib: the destination length is defined as a pointer!
|
||||
This is because you pass in the maximum buffer size and get back the
|
||||
actual length that was used.
|
||||
</p>
|
||||
<p>
|
||||
In C you'd pass in the address of a local variable
|
||||
(<tt>&buflen</tt>). But since there's no address-of operator in
|
||||
Lua, we'll just pass in a one-element array. Conveniently it can be
|
||||
initialized with the maximum buffer size in one step. Calling the
|
||||
actual <tt>zlib.compress2</tt> function is then straightforward.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑤</span> We want to return the
|
||||
compressed data as a Lua string, so we'll use <tt>ffi.string()</tt>.
|
||||
It needs a pointer to the start of the data and the actual length. The
|
||||
length has been returned in the <tt>buflen</tt> array, so we'll just
|
||||
get it from there.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Note that since the function returns now, the <tt>buf</tt> and
|
||||
<tt>buflen</tt> variables will eventually be garbage collected. This
|
||||
is fine, because <tt>ffi.string()</tt> has copied the contents to a
|
||||
newly created (interned) Lua string. If you plan to call this function
|
||||
lots of times, consider reusing the buffers and/or handing back the
|
||||
results in buffers instead of strings. This will reduce the overhead
|
||||
for garbage collection and string interning.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑥</span> The <tt>uncompress</tt>
|
||||
functions does the exact opposite of the <tt>compress</tt> function.
|
||||
The compressed data doesn't include the size of the original string,
|
||||
so this needs to be passed in. Otherwise no surprises here.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑦</span> The code, that makes use
|
||||
of the functions we just defined, is just plain Lua code. It doesn't
|
||||
need to know anything about the LuaJIT FFI — the convenience
|
||||
wrapper functions completely hide it.
|
||||
</p>
|
||||
<p>
|
||||
One major advantage of the LuaJIT FFI is that you are now able to
|
||||
write those wrappers <em>in Lua</em>. And at a fraction of the time it
|
||||
would cost you to create an extra C module using the Lua/C API.
|
||||
Many of the simpler C functions can probably be used directly
|
||||
from your Lua code, without any wrappers.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Side note: the zlib API uses the <tt>long</tt> type for passing
|
||||
lengths and sizes around. But all those zlib functions actually only
|
||||
deal with 32 bit values. This is an unfortunate choice for a
|
||||
public API, but may be explained by zlib's history — we'll just
|
||||
have to deal with it.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
First, you should know that a <tt>long</tt> is a 64 bit type e.g.
|
||||
on POSIX/x64 systems, but a 32 bit type on Windows/x64 and on
|
||||
32 bit systems. Thus a <tt>long</tt> result can be either a plain
|
||||
Lua number or a boxed 64 bit integer cdata object, depending on
|
||||
the target system.
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Ok, so the <tt>ffi.*</tt> functions generally accept cdata objects
|
||||
wherever you'd want to use a number. That's why we get a away with
|
||||
passing <tt>n</tt> to <tt>ffi.string()</tt> above. But other Lua
|
||||
library functions or modules don't know how to deal with this. So for
|
||||
maximum portability one needs to use <tt>tonumber()</tt> on returned
|
||||
<tt>long</tt> results before passing them on. Otherwise the
|
||||
application might work on some systems, but would fail in a POSIX/x64
|
||||
environment.
|
||||
</p>
|
||||
|
||||
<h2 id="metatype">Defining Metamethods for a C Type</h2>
|
||||
<p>
|
||||
The following code explains how to define metamethods for a C type.
|
||||
We define a simple point type and add some operations to it:
|
||||
</p>
|
||||
<pre class="code mark">
|
||||
<span class="codemark">
|
||||
①
|
||||
|
||||
|
||||
|
||||
②
|
||||
|
||||
③
|
||||
|
||||
④
|
||||
|
||||
|
||||
|
||||
⑤
|
||||
|
||||
⑥</span>local ffi = require("ffi")
|
||||
ffi.cdef[[
|
||||
<span style="color:#00a000;">typedef struct { double x, y; } point_t;</span>
|
||||
]]
|
||||
|
||||
local point
|
||||
local mt = {
|
||||
__add = function(a, b) return point(a.x+b.x, a.y+b.y) end,
|
||||
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
|
||||
__index = {
|
||||
area = function(a) return a.x*a.x + a.y*a.y end,
|
||||
},
|
||||
}
|
||||
point = ffi.metatype("point_t", mt)
|
||||
|
||||
local a = point(3, 4)
|
||||
print(a.x, a.y) --> 3 4
|
||||
print(#a) --> 5
|
||||
print(a:area()) --> 25
|
||||
local b = a + point(0.5, 8)
|
||||
print(#b) --> 12.5
|
||||
</pre>
|
||||
<p>
|
||||
Here's the step-by-step explanation:
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">①</span> This defines the C type for a
|
||||
two-dimensional point object.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">②</span> We have to declare the variable
|
||||
holding the point constructor first, because it's used inside of a
|
||||
metamethod.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">③</span> Let's define an <tt>__add</tt>
|
||||
metamethod which adds the coordinates of two points and creates a new
|
||||
point object. For simplicity, this function assumes that both arguments
|
||||
are points. But it could be any mix of objects, if at least one operand
|
||||
is of the required type (e.g. adding a point plus a number or vice
|
||||
versa). Our <tt>__len</tt> metamethod returns the distance of a point to
|
||||
the origin.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">④</span> If we run out of operators, we can
|
||||
define named methods, too. Here the <tt>__index</tt> table defines an
|
||||
<tt>area</tt> function. For custom indexing needs, one might want to
|
||||
define <tt>__index</tt> and <tt>__newindex</tt> <em>functions</em> instead.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑤</span> This associates the metamethods with
|
||||
our C type. This only needs to be done once. For convenience, a
|
||||
constructor is returned by
|
||||
<a href="ext_ffi_api.html#ffi_metatype"><tt>ffi.metatype()</tt></a>.
|
||||
We're not required to use it, though. The original C type can still
|
||||
be used e.g. to create an array of points. The metamethods automatically
|
||||
apply to any and all uses of this type.
|
||||
</p>
|
||||
<p>
|
||||
Please note that the association with a metatable is permanent and
|
||||
<b>the metatable must not be modified afterwards!</b> Ditto for the
|
||||
<tt>__index</tt> table.
|
||||
</p>
|
||||
<p>
|
||||
<span class="mark">⑥</span> Here are some simple usage examples
|
||||
for the point type and their expected results. The pre-defined
|
||||
operations (such as <tt>a.x</tt>) can be freely mixed with the newly
|
||||
defined metamethods. Note that <tt>area</tt> is a method and must be
|
||||
called with the Lua syntax for methods: <tt>a:area()</tt>, not
|
||||
<tt>a.area()</tt>.
|
||||
</p>
|
||||
<p>
|
||||
The C type metamethod mechanism is most useful when used in
|
||||
conjunction with C libraries that are written in an object-oriented
|
||||
style. Creators return a pointer to a new instance and methods take an
|
||||
instance pointer as the first argument. Sometimes you can just point
|
||||
<tt>__index</tt> to the library namespace and <tt>__gc</tt> to the
|
||||
destructor and you're done. But often enough you'll want to add
|
||||
convenience wrappers, e.g. to return actual Lua strings or when
|
||||
returning multiple values.
|
||||
</p>
|
||||
<p>
|
||||
Some C libraries only declare instance pointers as an opaque
|
||||
<tt>void *</tt> type. In this case you can use a fake type for all
|
||||
declarations, e.g. a pointer to a named (incomplete) struct will do:
|
||||
<tt>typedef struct foo_type *foo_handle</tt>. The C side doesn't
|
||||
know what you declare with the LuaJIT FFI, but as long as the underlying
|
||||
types are compatible, everything still works.
|
||||
</p>
|
||||
|
||||
<h2 id="idioms">Translating C Idioms</h2>
|
||||
<p>
|
||||
Here's a list of common C idioms and their translation to the
|
||||
LuaJIT FFI:
|
||||
</p>
|
||||
<table class="idiomtable">
|
||||
<tr class="idiomhead">
|
||||
<td class="idiomdesc">Idiom</td>
|
||||
<td class="idiomc">C code</td>
|
||||
<td class="idiomlua">Lua code</td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="idiomdesc">Pointer dereference<br><tt>int *p;</tt></td><td class="idiomc"><tt>x = *p;<br>*p = y;</tt></td><td class="idiomlua"><tt>x = <b>p[0]</b><br><b>p[0]</b> = y</tt></td></tr>
|
||||
<tr class="even">
|
||||
<td class="idiomdesc">Pointer indexing<br><tt>int i, *p;</tt></td><td class="idiomc"><tt>x = p[i];<br>p[i+1] = y;</tt></td><td class="idiomlua"><tt>x = p[i]<br>p[i+1] = y</tt></td></tr>
|
||||
<tr class="odd">
|
||||
<td class="idiomdesc">Array indexing<br><tt>int i, a[];</tt></td><td class="idiomc"><tt>x = a[i];<br>a[i+1] = y;</tt></td><td class="idiomlua"><tt>x = a[i]<br>a[i+1] = y</tt></td></tr>
|
||||
<tr class="even separate">
|
||||
<td class="idiomdesc"><tt>struct</tt>/<tt>union</tt> dereference<br><tt>struct foo s;</tt></td><td class="idiomc"><tt>x = s.field;<br>s.field = y;</tt></td><td class="idiomlua"><tt>x = s.field<br>s.field = y</tt></td></tr>
|
||||
<tr class="odd">
|
||||
<td class="idiomdesc"><tt>struct</tt>/<tt>union</tt> pointer deref.<br><tt>struct foo *sp;</tt></td><td class="idiomc"><tt>x = sp->field;<br>sp->field = y;</tt></td><td class="idiomlua"><tt>x = <b>s.field</b><br><b>s.field</b> = y</tt></td></tr>
|
||||
<tr class="even separate">
|
||||
<td class="idiomdesc">Pointer arithmetic<br><tt>int i, *p;</tt></td><td class="idiomc"><tt>x = p + i;<br>y = p - i;</tt></td><td class="idiomlua"><tt>x = p + i<br>y = p - i</tt></td></tr>
|
||||
<tr class="odd">
|
||||
<td class="idiomdesc">Pointer difference<br><tt>int *p1, *p2;</tt></td><td class="idiomc"><tt>x = p1 - p2;</tt></td><td class="idiomlua"><tt>x = p1 - p2</tt></td></tr>
|
||||
<tr class="even">
|
||||
<td class="idiomdesc">Array element pointer<br><tt>int i, a[];</tt></td><td class="idiomc"><tt>x = &a[i];</tt></td><td class="idiomlua"><tt>x = <b>a+i</b></tt></td></tr>
|
||||
<tr class="odd">
|
||||
<td class="idiomdesc">Cast pointer to address<br><tt>int *p;</tt></td><td class="idiomc"><tt>x = (intptr_t)p;</tt></td><td class="idiomlua"><tt>x = <b>tonumber(<br> ffi.cast("intptr_t",<br> p))</b></tt></td></tr>
|
||||
<tr class="even separate">
|
||||
<td class="idiomdesc">Functions with outargs<br><tt>void foo(int *inoutlen);</tt></td><td class="idiomc"><tt>int len = x;<br>foo(&len);<br>y = len;</tt></td><td class="idiomlua"><tt><b>local len =<br> ffi.new("int[1]", x)<br>foo(len)<br>y = len[0]</b></tt></td></tr>
|
||||
<tr class="odd">
|
||||
<td class="idiomdesc"><a href="ext_ffi_semantics.html#convert_vararg">Vararg conversions</a><br><tt>int printf(char *fmt, ...);</tt></td><td class="idiomc"><tt>printf("%g", 1.0);<br>printf("%d", 1);<br> </tt></td><td class="idiomlua"><tt>printf("%g", 1);<br>printf("%d",<br> <b>ffi.new("int", 1)</b>)</tt></td></tr>
|
||||
</table>
|
||||
|
||||
<h2 id="cache">To Cache or Not to Cache</h2>
|
||||
<p>
|
||||
It's a common Lua idiom to cache library functions in local variables
|
||||
or upvalues, e.g.:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local byte, char = string.byte, string.char
|
||||
local function foo(x)
|
||||
return char(byte(x)+1)
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
This replaces several hash-table lookups with a (faster) direct use of
|
||||
a local or an upvalue. This is less important with LuaJIT, since the
|
||||
JIT compiler optimizes hash-table lookups a lot and is even able to
|
||||
hoist most of them out of the inner loops. It can't eliminate
|
||||
<em>all</em> of them, though, and it saves some typing for often-used
|
||||
functions. So there's still a place for this, even with LuaJIT.
|
||||
</p>
|
||||
<p>
|
||||
The situation is a bit different with C function calls via the
|
||||
FFI library. The JIT compiler has special logic to eliminate <em>all
|
||||
of the lookup overhead</em> for functions resolved from a
|
||||
<a href="ext_ffi_semantics.html#clib">C library namespace</a>!
|
||||
Thus it's not helpful and actually counter-productive to cache
|
||||
individual C functions like this:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local <b>funca</b>, <b>funcb</b> = ffi.C.funcb, ffi.C.funcb -- <span style="color:#c00000;">Not helpful!</span>
|
||||
local function foo(x, n)
|
||||
for i=1,n do <b>funcb</b>(<b>funca</b>(x, i), 1) end
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
This turns them into indirect calls and generates bigger and slower
|
||||
machine code. Instead you'll want to cache the namespace itself and
|
||||
rely on the JIT compiler to eliminate the lookups:
|
||||
</p>
|
||||
<pre class="code">
|
||||
local <b>C</b> = ffi.C -- <span style="color:#00a000;">Instead use this!</span>
|
||||
local function foo(x, n)
|
||||
for i=1,n do <b>C.funcb</b>(<b>C.funca</b>(x, i), 1) end
|
||||
end
|
||||
</pre>
|
||||
<p>
|
||||
This generates both shorter and faster code. So <b>don't cache
|
||||
C functions</b>, but <b>do</b> cache namespaces! Most often the
|
||||
namespace is already in a local variable at an outer scope, e.g. from
|
||||
<tt>local lib = ffi.load(...)</tt>. Note that copying
|
||||
it to a local variable in the function scope is unnecessary.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+199
@@ -0,0 +1,199 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>jit.* Library</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-2013, 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><tt>jit.*</tt> Library</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a class="current" href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
The functions in this built-in module control the behavior of the JIT
|
||||
compiler engine. Note that JIT-compilation is fully automatic —
|
||||
you probably won't need to use any of the following functions unless
|
||||
you have special needs.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_onoff"><tt>jit.on()<br>
|
||||
jit.off()</tt></h3>
|
||||
<p>
|
||||
Turns the whole JIT compiler on (default) or off.
|
||||
</p>
|
||||
<p>
|
||||
These functions are typically used with the command line options
|
||||
<tt>-j on</tt> or <tt>-j off</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_flush"><tt>jit.flush()</tt></h3>
|
||||
<p>
|
||||
Flushes the whole cache of compiled code.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_onoff_func"><tt>jit.on(func|true [,true|false])<br>
|
||||
jit.off(func|true [,true|false])<br>
|
||||
jit.flush(func|true [,true|false])</tt></h3>
|
||||
<p>
|
||||
<tt>jit.on</tt> enables JIT compilation for a Lua function (this is
|
||||
the default).
|
||||
</p>
|
||||
<p>
|
||||
<tt>jit.off</tt> disables JIT compilation for a Lua function and
|
||||
flushes any already compiled code from the code cache.
|
||||
</p>
|
||||
<p>
|
||||
<tt>jit.flush</tt> flushes the code, but doesn't affect the
|
||||
enable/disable status.
|
||||
</p>
|
||||
<p>
|
||||
The current function, i.e. the Lua function calling this library
|
||||
function, can also be specified by passing <tt>true</tt> as the first
|
||||
argument.
|
||||
</p>
|
||||
<p>
|
||||
If the second argument is <tt>true</tt>, JIT compilation is also
|
||||
enabled, disabled or flushed recursively for all sub-functions of a
|
||||
function. With <tt>false</tt> only the sub-functions are affected.
|
||||
</p>
|
||||
<p>
|
||||
The <tt>jit.on</tt> and <tt>jit.off</tt> functions only set a flag
|
||||
which is checked when the function is about to be compiled. They do
|
||||
not trigger immediate compilation.
|
||||
</p>
|
||||
<p>
|
||||
Typical usage is <tt>jit.off(true, true)</tt> in the main chunk
|
||||
of a module to turn off JIT compilation for the whole module for
|
||||
debugging purposes.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_flush_tr"><tt>jit.flush(tr)</tt></h3>
|
||||
<p>
|
||||
Flushes the root trace, specified by its number, and all of its side
|
||||
traces from the cache. The code for the trace will be retained as long
|
||||
as there are any other traces which link to it.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_status"><tt>status, ... = jit.status()</tt></h3>
|
||||
<p>
|
||||
Returns the current status of the JIT compiler. The first result is
|
||||
either <tt>true</tt> or <tt>false</tt> if the JIT compiler is turned
|
||||
on or off. The remaining results are strings for CPU-specific features
|
||||
and enabled optimizations.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_version"><tt>jit.version</tt></h3>
|
||||
<p>
|
||||
Contains the LuaJIT version string.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_version_num"><tt>jit.version_num</tt></h3>
|
||||
<p>
|
||||
Contains the version number of the LuaJIT core. Version xx.yy.zz
|
||||
is represented by the decimal number xxyyzz.
|
||||
</p>
|
||||
|
||||
<h3 id="jit_os"><tt>jit.os</tt></h3>
|
||||
<p>
|
||||
Contains the target OS name:
|
||||
"Windows", "Linux", "OSX", "BSD", "POSIX" or "Other".
|
||||
</p>
|
||||
|
||||
<h3 id="jit_arch"><tt>jit.arch</tt></h3>
|
||||
<p>
|
||||
Contains the target architecture name:
|
||||
"x86", "x64" or "ppcspe".
|
||||
</p>
|
||||
|
||||
<h2 id="jit_opt"><tt>jit.opt.*</tt> — JIT compiler optimization control</h2>
|
||||
<p>
|
||||
This sub-module provides the backend for the <tt>-O</tt> command line
|
||||
option.
|
||||
</p>
|
||||
<p>
|
||||
You can also use it programmatically, e.g.:
|
||||
</p>
|
||||
<pre class="code">
|
||||
jit.opt.start(2) -- same as -O2
|
||||
jit.opt.start("-dce")
|
||||
jit.opt.start("hotloop=10", "hotexit=2")
|
||||
</pre>
|
||||
<p>
|
||||
Unlike in LuaJIT 1.x, the module is built-in and
|
||||
<b>optimization is turned on by default!</b>
|
||||
It's no longer necessary to run <tt>require("jit.opt").start()</tt>,
|
||||
which was one of the ways to enable optimization.
|
||||
</p>
|
||||
|
||||
<h2 id="jit_util"><tt>jit.util.*</tt> — JIT compiler introspection</h2>
|
||||
<p>
|
||||
This sub-module holds functions to introspect the bytecode, generated
|
||||
traces, the IR and the generated machine code. The functionality
|
||||
provided by this module is still in flux and therefore undocumented.
|
||||
</p>
|
||||
<p>
|
||||
The debug modules <tt>-jbc</tt>, <tt>-jv</tt> and <tt>-jdump</tt> make
|
||||
extensive use of these functions. Please check out their source code,
|
||||
if you want to know more.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+408
@@ -0,0 +1,408 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>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-2013, 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.exc {
|
||||
line-height: 1.2;
|
||||
}
|
||||
tr.exchead td {
|
||||
font-weight: bold;
|
||||
}
|
||||
td.excplatform {
|
||||
width: 48%;
|
||||
}
|
||||
td.exccompiler {
|
||||
width: 29%;
|
||||
}
|
||||
td.excinterop {
|
||||
width: 23%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="http://luajit.org"><span>Lua<span id="logo">JIT</span></span></a>
|
||||
</div>
|
||||
<div id="head">
|
||||
<h1>Extensions</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a class="current" href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT is fully upwards-compatible with Lua 5.1. It supports all
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#5"><span class="ext">»</span> standard Lua
|
||||
library functions</a> and the full set of
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#3"><span class="ext">»</span> Lua/C API
|
||||
functions</a>.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT is also fully ABI-compatible to Lua 5.1 at the linker/dynamic
|
||||
loader level. This means you can compile a C module against the
|
||||
standard Lua headers and load the same shared library from either Lua
|
||||
or LuaJIT.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT extends the standard Lua VM with new functionality and adds
|
||||
several extension modules. Please note this page is only about
|
||||
<em>functional</em> enhancements and not about performance enhancements,
|
||||
such as the optimized VM, the faster interpreter or the JIT compiler.
|
||||
</p>
|
||||
|
||||
<h2 id="modules">Extensions Modules</h2>
|
||||
<p>
|
||||
LuaJIT comes with several built-in extension modules:
|
||||
</p>
|
||||
|
||||
<h3 id="bit"><tt>bit.*</tt> — Bitwise operations</h3>
|
||||
<p>
|
||||
LuaJIT supports all bitwise operations as defined by
|
||||
<a href="http://bitop.luajit.org"><span class="ext">»</span> Lua BitOp</a>:
|
||||
</p>
|
||||
<pre class="code">
|
||||
bit.tobit bit.tohex bit.bnot bit.band bit.bor bit.bxor
|
||||
bit.lshift bit.rshift bit.arshift bit.rol bit.ror bit.bswap
|
||||
</pre>
|
||||
<p>
|
||||
This module is a LuaJIT built-in — you don't need to download or
|
||||
install Lua BitOp. The Lua BitOp site has full documentation for all
|
||||
<a href="http://bitop.luajit.org/api.html"><span class="ext">»</span> 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>
|
||||
|
||||
<h3 id="ffi"><tt>ffi.*</tt> — FFI library</h3>
|
||||
<p>
|
||||
The <a href="ext_ffi.html">FFI library</a> allows calling external
|
||||
C functions and the use of C data structures from pure Lua
|
||||
code.
|
||||
</p>
|
||||
|
||||
<h3 id="jit"><tt>jit.*</tt> — JIT compiler control</h3>
|
||||
<p>
|
||||
The functions in this module
|
||||
<a href="ext_jit.html">control the behavior of the JIT compiler engine</a>.
|
||||
</p>
|
||||
|
||||
<h3 id="c_api">C API extensions</h3>
|
||||
<p>
|
||||
LuaJIT adds some
|
||||
<a href="ext_c_api.html">extra functions to the Lua/C API</a>.
|
||||
</p>
|
||||
|
||||
<h2 id="library">Enhanced Standard Library Functions</h2>
|
||||
|
||||
<h3 id="xpcall"><tt>xpcall(f, err [,args...])</tt> passes arguments</h3>
|
||||
<p>
|
||||
Unlike the standard implementation in Lua 5.1, <tt>xpcall()</tt>
|
||||
passes any arguments after the error function to the function
|
||||
which is called in a protected context.
|
||||
</p>
|
||||
|
||||
<h3 id="load"><tt>loadfile()</tt> etc. handle UTF-8 source code</h3>
|
||||
<p>
|
||||
Non-ASCII characters are handled transparently by the Lua source code parser.
|
||||
This allows the use of UTF-8 characters in identifiers and strings.
|
||||
A UTF-8 BOM is skipped at the start of the source code.
|
||||
</p>
|
||||
|
||||
<h3 id="tostring"><tt>tostring()</tt> etc. canonicalize NaN and ±Inf</h3>
|
||||
<p>
|
||||
All number-to-string conversions consistently convert non-finite numbers
|
||||
to the same strings on all platforms. NaN results in <tt>"nan"</tt>,
|
||||
positive infinity results in <tt>"inf"</tt> and negative infinity results
|
||||
in <tt>"-inf"</tt>.
|
||||
</p>
|
||||
|
||||
<h3 id="tonumber"><tt>tonumber()</tt> etc. use builtin string to number conversion</h3>
|
||||
<p>
|
||||
All string-to-number conversions consistently convert integer and
|
||||
floating-point inputs in decimal and hexadecimal on all platforms.
|
||||
<tt>strtod()</tt> is <em>not</em> used anymore, which avoids numerous
|
||||
problems with poor C library implementations. The builtin conversion
|
||||
function provides full precision according to the IEEE-754 standard, it
|
||||
works independently of the current locale and it supports hex floating-point
|
||||
numbers (e.g. <tt>0x1.5p-3</tt>).
|
||||
</p>
|
||||
|
||||
<h3 id="string_dump"><tt>string.dump(f [,strip])</tt> generates portable bytecode</h3>
|
||||
<p>
|
||||
An extra argument has been added to <tt>string.dump()</tt>. If set to
|
||||
<tt>true</tt>, 'stripped' bytecode without debug information is
|
||||
generated. This speeds up later bytecode loading and reduces memory
|
||||
usage. See also the
|
||||
<a href="running.html#opt_b"><tt>-b</tt> command line option</a>.
|
||||
</p>
|
||||
<p>
|
||||
The generated bytecode is portable and can be loaded on any architecture
|
||||
that LuaJIT supports, independent of word size or endianess. However the
|
||||
bytecode compatibility versions must match. Bytecode stays compatible
|
||||
for dot releases (x.y.0 → x.y.1), but may change with major or
|
||||
minor releases (2.0 → 2.1) or between any beta release. Foreign
|
||||
bytecode (e.g. from Lua 5.1) is incompatible and cannot be loaded.
|
||||
</p>
|
||||
|
||||
<h3 id="math_random">Enhanced PRNG for <tt>math.random()</tt></h3>
|
||||
<p>
|
||||
LuaJIT uses a Tausworthe PRNG with period 2^223 to implement
|
||||
<tt>math.random()</tt> and <tt>math.randomseed()</tt>. The quality of
|
||||
the PRNG results is much superior compared to the standard Lua
|
||||
implementation which uses the platform-specific ANSI rand().
|
||||
</p>
|
||||
<p>
|
||||
The PRNG generates the same sequences from the same seeds on all
|
||||
platforms and makes use of all bits in the seed argument.
|
||||
<tt>math.random()</tt> without arguments generates 52 pseudo-random bits
|
||||
for every call. The result is uniformly distributed between 0.0 and 1.0.
|
||||
It's correctly scaled up and rounded for <tt>math.random(n [,m])</tt> to
|
||||
preserve uniformity.
|
||||
</p>
|
||||
|
||||
<h3 id="io"><tt>io.*</tt> functions handle 64 bit file offsets</h3>
|
||||
<p>
|
||||
The file I/O functions in the standard <tt>io.*</tt> library handle
|
||||
64 bit file offsets. In particular this means it's possible
|
||||
to open files larger than 2 Gigabytes and to reposition or obtain
|
||||
the current file position for offsets beyond 2 GB
|
||||
(<tt>fp:seek()</tt> method).
|
||||
</p>
|
||||
|
||||
<h3 id="debug_meta"><tt>debug.*</tt> functions identify metamethods</h3>
|
||||
<p>
|
||||
<tt>debug.getinfo()</tt> and <tt>lua_getinfo()</tt> also return information
|
||||
about invoked metamethods. The <tt>namewhat</tt> field is set to
|
||||
<tt>"metamethod"</tt> and the <tt>name</tt> field has the name of
|
||||
the corresponding metamethod (e.g. <tt>"__index"</tt>).
|
||||
</p>
|
||||
|
||||
<h2 id="resumable">Fully Resumable VM</h2>
|
||||
<p>
|
||||
The LuaJIT VM is fully resumable. This means you can yield from a
|
||||
coroutine even across contexts, where this would not possible with
|
||||
the standard Lua 5.1 VM: e.g. you can yield across <tt>pcall()</tt>
|
||||
and <tt>xpcall()</tt>, across iterators and across metamethods.
|
||||
</p>
|
||||
|
||||
<h2 id="lua52">Extensions from Lua 5.2</h2>
|
||||
<p>
|
||||
LuaJIT supports some language and library extensions from Lua 5.2.
|
||||
Features that are unlikely to break existing code are unconditionally
|
||||
enabled:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>goto</tt> and <tt>::labels::</tt>.</li>
|
||||
<li>Hex escapes <tt>'\x3F'</tt> and <tt>'\*'</tt> escape in strings.</li>
|
||||
<li><tt>load(string|reader [, chunkname [,mode [,env]]])</tt>.</li>
|
||||
<li><tt>loadstring()</tt> is an alias for <tt>load()</tt>.</li>
|
||||
<li><tt>loadfile(filename [,mode [,env]])</tt>.</li>
|
||||
<li><tt>math.log(x [,base])</tt>.
|
||||
<li><tt>string.rep(s, n [,sep])</tt>.
|
||||
<li><tt>string.format()</tt>: <tt>%q</tt> reversible.
|
||||
<tt>%s</tt> checks <tt>__tostring</tt>.
|
||||
<tt>%a</tt> and <tt>"%A</tt> added.</li>
|
||||
<li>String matching pattern <tt>%g</tt> added.</li>
|
||||
<li><tt>io.read("*L")</tt>.</li>
|
||||
<li><tt>io.lines()</tt> and <tt>file:lines()</tt> process
|
||||
<tt>io.read()</tt> options.</li>
|
||||
<li><tt>os.exit(status|true|false [,close])</tt>.</li>
|
||||
<li><tt>package.searchpath(name, path [, sep [, rep]])</tt>.</li>
|
||||
<li><tt>package.loadlib(name, "*")</tt>.</li>
|
||||
<li><tt>debug.getinfo()</tt> returns <tt>nparams</tt> and <tt>isvararg</tt>
|
||||
for option <tt>"u"</tt>.</li>
|
||||
<li><tt>debug.getlocal()</tt> accepts function instead of level.</li>
|
||||
<li><tt>debug.getlocal()</tt> and <tt>debug.setlocal()</tt> accept negative
|
||||
indexes for varargs.</li>
|
||||
<li><tt>debug.getupvalue()</tt> and <tt>debug.setupvalue()</tt> handle
|
||||
C functions.</li>
|
||||
<li><tt>debug.upvalueid()</tt> and <tt>debug.upvaluejoin()</tt>.</li>
|
||||
<li>Command line option <tt>-E</tt>.</li>
|
||||
<li>Command line checks <tt>__tostring</tt> for errors.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Other features are only enabled, if LuaJIT is built with
|
||||
<tt>-DLUAJIT_ENABLE_LUA52COMPAT</tt>:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>goto</tt> is a keyword and not a valid variable name anymore.</li>
|
||||
<li><tt>break</tt> can be placed anywhere. Empty statements (<tt>;;</tt>)
|
||||
are allowed.</li>
|
||||
<li><tt>__lt</tt>, <tt>__le</tt> are invoked for mixed types.</li>
|
||||
<li><tt>__len</tt> for tables. <tt>rawlen()</tt> library function.</li>
|
||||
<li><tt>pairs()</tt> and <tt>ipairs()</tt> check for <tt>__pairs</tt> and
|
||||
<tt>__ipairs</tt>.</li>
|
||||
<li><tt>coroutine.running()</tt> returns two results.</li>
|
||||
<li><tt>table.pack()</tt> and <tt>table.unpack()</tt>
|
||||
(same as <tt>unpack()</tt>).</li>
|
||||
<li><tt>io.write()</tt> and <tt>file:write()</tt> return file handle
|
||||
instead of <tt>true</tt>.</li>
|
||||
<li><tt>os.execute()</tt> and <tt>pipe:close()</tt> return detailed
|
||||
exit status.</li>
|
||||
<li><tt>debug.setmetatable()</tt> returns object.</li>
|
||||
<li><tt>debug.getuservalue()</tt> and <tt>debug.setuservalue()</tt>.</li>
|
||||
<li>Remove <tt>math.mod()</tt>, <tt>string.gfind()</tt>.
|
||||
</ul>
|
||||
<p>
|
||||
Note: this provides only partial compatibility with Lua 5.2 at the
|
||||
language and Lua library level. LuaJIT is API+ABI-compatible with
|
||||
Lua 5.1, which prevents implementing features that would otherwise
|
||||
break the Lua/C API and ABI (e.g. <tt>_ENV</tt>).
|
||||
</p>
|
||||
|
||||
<h2 id="exceptions">C++ Exception Interoperability</h2>
|
||||
<p>
|
||||
LuaJIT has built-in support for interoperating with C++ exceptions.
|
||||
The available range of features depends on the target platform and
|
||||
the toolchain used to compile LuaJIT:
|
||||
</p>
|
||||
<table class="exc">
|
||||
<tr class="exchead">
|
||||
<td class="excplatform">Platform</td>
|
||||
<td class="exccompiler">Compiler</td>
|
||||
<td class="excinterop">Interoperability</td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="excplatform">POSIX/x64, DWARF2 unwinding</td>
|
||||
<td class="exccompiler">GCC 4.3+</td>
|
||||
<td class="excinterop"><b style="color: #00a000;">Full</b></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="excplatform">Other platforms, DWARF2 unwinding</td>
|
||||
<td class="exccompiler">GCC</td>
|
||||
<td class="excinterop"><b style="color: #c06000;">Limited</b></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="excplatform">Windows/x64</td>
|
||||
<td class="exccompiler">MSVC or WinSDK</td>
|
||||
<td class="excinterop"><b style="color: #00a000;">Full</b></td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="excplatform">Windows/x86</td>
|
||||
<td class="exccompiler">Any</td>
|
||||
<td class="excinterop"><b style="color: #a00000;">No</b></td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="excplatform">Other platforms</td>
|
||||
<td class="exccompiler">Other compilers</td>
|
||||
<td class="excinterop"><b style="color: #a00000;">No</b></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<b style="color: #00a000;">Full interoperability</b> means:
|
||||
</p>
|
||||
<ul>
|
||||
<li>C++ exceptions can be caught on the Lua side with <tt>pcall()</tt>,
|
||||
<tt>lua_pcall()</tt> etc.</li>
|
||||
<li>C++ exceptions will be converted to the generic Lua error
|
||||
<tt>"C++ exception"</tt>, unless you use the
|
||||
<a href="ext_c_api.html#mode_wrapcfunc">C call wrapper</a> feature.</li>
|
||||
<li>It's safe to throw C++ exceptions across non-protected Lua frames
|
||||
on the C stack. The contents of the C++ exception object
|
||||
pass through unmodified.</li>
|
||||
<li>Lua errors can be caught on the C++ side with <tt>catch(...)</tt>.
|
||||
The corresponding Lua error message can be retrieved from the Lua stack.</li>
|
||||
<li>Throwing Lua errors across C++ frames is safe. C++ destructors
|
||||
will be called.</li>
|
||||
</ul>
|
||||
<p>
|
||||
<b style="color: #c06000;">Limited interoperability</b> means:
|
||||
</p>
|
||||
<ul>
|
||||
<li>C++ exceptions can be caught on the Lua side with <tt>pcall()</tt>,
|
||||
<tt>lua_pcall()</tt> etc.</li>
|
||||
<li>C++ exceptions will be converted to the generic Lua error
|
||||
<tt>"C++ exception"</tt>, unless you use the
|
||||
<a href="ext_c_api.html#mode_wrapcfunc">C call wrapper</a> feature.</li>
|
||||
<li>C++ exceptions will be caught by non-protected Lua frames and
|
||||
are rethrown as a generic Lua error. The C++ exception object will
|
||||
be destroyed.</li>
|
||||
<li>Lua errors <b>cannot</b> be caught on the C++ side.</li>
|
||||
<li>Throwing Lua errors across C++ frames will <b>not</b> call
|
||||
C++ destructors.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<b style="color: #a00000;">No interoperability</b> means:
|
||||
</p>
|
||||
<ul>
|
||||
<li>It's <b>not</b> safe to throw C++ exceptions across Lua frames.</li>
|
||||
<li>C++ exceptions <b>cannot</b> be caught on the Lua side.</li>
|
||||
<li>Lua errors <b>cannot</b> be caught on the C++ side.</li>
|
||||
<li>Throwing Lua errors across C++ frames will <b>not</b> call
|
||||
C++ destructors.</li>
|
||||
<li>Additionally, on Windows/x86 with SEH-based C++ exceptions:
|
||||
it's <b>not</b> safe to throw a Lua error across any frames containing
|
||||
a C++ function with any try/catch construct or using variables with
|
||||
(implicit) destructors. This also applies to any functions which may be
|
||||
inlined in such a function. It doesn't matter whether <tt>lua_error()</tt>
|
||||
is called inside or outside of a try/catch or whether any object actually
|
||||
needs to be destroyed: the SEH chain is corrupted and this will eventually
|
||||
lead to the termination of the process.</li>
|
||||
</ul>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+184
@@ -0,0 +1,184 @@
|
||||
<!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-2013, 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="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<dl>
|
||||
<dt>Q: Where can I learn more about LuaJIT and Lua?</dt>
|
||||
<dd>
|
||||
<ul style="padding: 0;">
|
||||
<li>The <a href="http://luajit.org/list.html"><span class="ext">»</span> LuaJIT mailing list</a> focuses on topics
|
||||
related to LuaJIT.</li>
|
||||
<li>The <a href="http://wiki.luajit.org/"><span class="ext">»</span> LuaJIT wiki</a> gathers community
|
||||
resources about LuaJIT.</li>
|
||||
<li>News about Lua itself can be found at the
|
||||
<a href="http://www.lua.org/lua-l.html"><span class="ext">»</span> Lua mailing list</a>.
|
||||
The mailing list archives are worth checking out for older postings
|
||||
about LuaJIT.</li>
|
||||
<li>The <a href="http://lua.org"><span class="ext">»</span> main Lua.org site</a> has complete
|
||||
<a href="http://www.lua.org/docs.html"><span class="ext">»</span> documentation</a> of the language
|
||||
and links to books and papers about Lua.</li>
|
||||
<li>The community-managed <a href="http://lua-users.org/wiki/"><span class="ext">»</span> Lua Wiki</a>
|
||||
has information about diverse topics.</li>
|
||||
</ul>
|
||||
</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">»</span> Trace Compiler</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=JIT+Compiler"><span class="ext">»</span> JIT Compiler</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=Dynamic+Language+Optimizations"><span class="ext">»</span> Dynamic Language Optimizations</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=SSA+Form"><span class="ext">»</span> SSA Form</a><br>
|
||||
Search for: <a href="http://scholar.google.com/scholar?q=Linear+Scan+Register+Allocation"><span class="ext">»</span> Linear Scan Register Allocation</a><br>
|
||||
Here is a list of the <a href="http://article.gmane.org/gmane.comp.lang.lua.general/58908"><span class="ext">»</span> innovative features in LuaJIT</a>.<br>
|
||||
And, you know, reading the source is of course the only way to enlightenment. :-)
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<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">»</span> Lua 5.1
|
||||
vararg syntax</a>.</dd>
|
||||
</dl>
|
||||
|
||||
<dl>
|
||||
<dt>Q: Why do I get this error: "bad FPU precision"?<br>
|
||||
<dt>Q: I get weird behavior after initializing Direct3D.<br>
|
||||
<dt>Q: Some FPU operations crash after I load a Delphi DLL.<br>
|
||||
</dt>
|
||||
<dd>
|
||||
|
||||
DirectX/Direct3D (up to version 9) sets the x87 FPU to single-precision
|
||||
mode by default. This violates the Windows ABI and interferes with the
|
||||
operation of many programs — LuaJIT is affected, too. Please make
|
||||
sure you always use the <tt>D3DCREATE_FPU_PRESERVE</tt> flag when
|
||||
initializing Direct3D.<br>
|
||||
|
||||
Direct3D version 10 or higher do not show this behavior anymore.
|
||||
Consider testing your application with older versions, too.<br>
|
||||
|
||||
Similarly, the Borland/Delphi runtime modifies the FPU control word and
|
||||
enables FP exceptions. Of course this violates the Windows ABI, too.
|
||||
Please check the Delphi docs for the Set8087CW method.
|
||||
|
||||
</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, these 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 — it needs to generate native
|
||||
machine code. This means the code generator must be ported to each
|
||||
architecture. And the fast interpreter is written in assembler and
|
||||
must be ported, too. This is quite an undertaking.<br>
|
||||
The <a href="install.html">install documentation</a> shows the supported
|
||||
architectures. Other architectures will follow based on sufficient user
|
||||
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 — 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 © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Vendored
+613
@@ -0,0 +1,613 @@
|
||||
<!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-2013, 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.compat {
|
||||
line-height: 1.2;
|
||||
font-size: 80%;
|
||||
}
|
||||
table.compat td {
|
||||
border: 1px solid #bfcfff;
|
||||
height: 2.5em;
|
||||
}
|
||||
table.compat tr.compathead td {
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #bfcfff;
|
||||
}
|
||||
tr.compathead td.compatos {
|
||||
vertical-align: top;
|
||||
}
|
||||
table.compat td.compatcpu {
|
||||
width: 18%;
|
||||
border-right: 2px solid #bfcfff;
|
||||
}
|
||||
td.compatos {
|
||||
width: 21%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
td.compatno {
|
||||
background-color: #d0d0d0;
|
||||
}
|
||||
</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>Installation</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a class="current" href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT is only distributed as a source package. This page explains
|
||||
how to build and install LuaJIT with different operating systems
|
||||
and C compilers.
|
||||
</p>
|
||||
<p>
|
||||
For the impatient (on POSIX systems):
|
||||
</p>
|
||||
<pre class="code">
|
||||
make && sudo make install
|
||||
</pre>
|
||||
<p>
|
||||
LuaJIT currently builds out-of-the box on most systems.
|
||||
Here's the compatibility matrix for the supported combinations of
|
||||
operating systems, CPUs and compilers:
|
||||
</p>
|
||||
<table class="compat">
|
||||
<tr class="compathead">
|
||||
<td class="compatcpu">CPU / OS</td>
|
||||
<td class="compatos"><a href="#posix">Linux</a> or<br><a href="#android">Android</a></td>
|
||||
<td class="compatos"><a href="#posix">*BSD, Other</a></td>
|
||||
<td class="compatos"><a href="#posix">OSX 10.4+</a> or<br><a href="#ios">iOS 3.0+</a></td>
|
||||
<td class="compatos"><a href="#windows">Windows<br>XP/Vista/7</a></td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="compatcpu">x86 (32 bit)</td>
|
||||
<td class="compatos">GCC 4.x<br>GCC 3.4</td>
|
||||
<td class="compatos">GCC 4.x<br>GCC 3.4</td>
|
||||
<td class="compatos">GCC 4.x<br>GCC 3.4</td>
|
||||
<td class="compatos">MSVC, MSVC/EE<br>WinSDK<br>MinGW, Cygwin</td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="compatcpu">x64 (64 bit)</td>
|
||||
<td class="compatos">GCC 4.x</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos">GCC 4.x</td>
|
||||
<td class="compatos">MSVC + SDK v7.0<br>WinSDK v7.0</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="compatcpu"><a href="#cross2">ARMv5+<br>ARM9E+</a></td>
|
||||
<td class="compatos">GCC 4.2+</td>
|
||||
<td class="compatos">GCC 4.2+</td>
|
||||
<td class="compatos">GCC 4.2+</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="compatcpu"><a href="#cross2">PPC</a></td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos">GCC 4.3+<br>GCC 4.1 (<a href="#ps3">PS3</a>)</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos">XEDK (<a href="#xbox360">Xbox 360</a>)</td>
|
||||
</tr>
|
||||
<tr class="odd">
|
||||
<td class="compatcpu"><a href="#cross2">PPC/e500v2</a></td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos compatno"> </td>
|
||||
</tr>
|
||||
<tr class="even">
|
||||
<td class="compatcpu"><a href="#cross2">MIPS</a></td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos">GCC 4.3+</td>
|
||||
<td class="compatos compatno"> </td>
|
||||
<td class="compatos compatno"> </td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2>Configuring LuaJIT</h2>
|
||||
<p>
|
||||
The standard configuration should work fine for most installations.
|
||||
Usually there is no need to tweak the settings. The following files
|
||||
hold all user-configurable settings:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>src/luaconf.h</tt> sets some configuration variables.</li>
|
||||
<li><tt>Makefile</tt> has settings for <b>installing</b> LuaJIT (POSIX
|
||||
only).</li>
|
||||
<li><tt>src/Makefile</tt> has settings for <b>compiling</b> LuaJIT
|
||||
under POSIX, MinGW or Cygwin.</li>
|
||||
<li><tt>src/msvcbuild.bat</tt> has settings for compiling LuaJIT with
|
||||
MSVC or WinSDK.</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, the development headers and/or a complete SDK. E.g. on a current
|
||||
Debian/Ubuntu, install <tt>libc6-dev</tt> with the package manager.
|
||||
</p>
|
||||
<p>
|
||||
Download the current source package of LuaJIT (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.2.tar.gz
|
||||
cd LuaJIT-2.0.2</pre>
|
||||
<h3>Building LuaJIT</h3>
|
||||
<p>
|
||||
The supplied Makefiles try to auto-detect the settings needed for your
|
||||
operating system and your compiler. They need to be run with GNU Make,
|
||||
which is probably the default on your system, anyway. Simply run:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make
|
||||
</pre>
|
||||
<p>
|
||||
This always builds a native x86, x64 or PPC binary, depending on the host OS
|
||||
you're running this command on. Check the section on
|
||||
<a href="#cross">cross-compilation</a> for more options.
|
||||
</p>
|
||||
<p>
|
||||
By default, modules are only searched under the prefix <tt>/usr/local</tt>.
|
||||
You can add an extra prefix to the search paths by appending the
|
||||
<tt>PREFIX</tt> option, e.g.:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make PREFIX=/home/myself/lj2
|
||||
</pre>
|
||||
<p>
|
||||
Note for OSX: if the <tt>MACOSX_DEPLOYMENT_TARGET</tt> environment
|
||||
variable is not set, then it's forced to <tt>10.4</tt>.
|
||||
</p>
|
||||
<h3>Installing LuaJIT</h3>
|
||||
<p>
|
||||
The top-level Makefile installs LuaJIT by default under
|
||||
<tt>/usr/local</tt>, i.e. the executable ends up in
|
||||
<tt>/usr/local/bin</tt> and so on. You need root privileges
|
||||
to write to this path. So, assuming sudo is installed on your system,
|
||||
run the following command and enter your sudo password:
|
||||
</p>
|
||||
<pre class="code">
|
||||
sudo make install
|
||||
</pre>
|
||||
<p>
|
||||
Otherwise specify the directory prefix as an absolute path, e.g.:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make install PREFIX=/home/myself/lj2
|
||||
</pre>
|
||||
<p>
|
||||
Obviously the prefixes given during build and installation need to be the same.
|
||||
</p>
|
||||
|
||||
<h2 id="windows">Windows Systems</h2>
|
||||
<h3>Prerequisites</h3>
|
||||
<p>
|
||||
Either install one of the open source SDKs
|
||||
(<a href="http://mingw.org/"><span class="ext">»</span> MinGW</a> or
|
||||
<a href="http://www.cygwin.com/"><span class="ext">»</span> Cygwin</a>), which come with a modified
|
||||
GCC plus the required development headers.
|
||||
</p>
|
||||
<p>
|
||||
Or install Microsoft's Visual C++ (MSVC). The freely downloadable
|
||||
<a href="http://www.microsoft.com/Express/VC/"><span class="ext">»</span> Express Edition</a>
|
||||
works just fine, but only contains an x86 compiler.
|
||||
</p>
|
||||
<p>
|
||||
The freely downloadable
|
||||
<a href="http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx"><span class="ext">»</span> Windows SDK</a>
|
||||
only comes with command line tools, but this is all you need to build LuaJIT.
|
||||
It contains x86 and x64 compilers.
|
||||
</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", <tt>cd</tt> to the
|
||||
directory where you've unpacked the sources and run these commands:
|
||||
</p>
|
||||
<pre class="code">
|
||||
cd src
|
||||
msvcbuild
|
||||
</pre>
|
||||
<p>
|
||||
Then follow the installation instructions below.
|
||||
</p>
|
||||
<h3>Building with the Windows SDK</h3>
|
||||
<p>
|
||||
Open a "Windows SDK Command Shell" and select the x86 compiler:
|
||||
</p>
|
||||
<pre class="code">
|
||||
setenv /release /x86
|
||||
</pre>
|
||||
<p>
|
||||
Or select the x64 compiler:
|
||||
</p>
|
||||
<pre class="code">
|
||||
setenv /release /x64
|
||||
</pre>
|
||||
<p>
|
||||
Then <tt>cd</tt> to the directory where you've unpacked the sources
|
||||
and run these commands:
|
||||
</p>
|
||||
<pre class="code">
|
||||
cd src
|
||||
msvcbuild
|
||||
</pre>
|
||||
<p>
|
||||
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">
|
||||
mingw32-make
|
||||
</pre>
|
||||
<p>
|
||||
Or this command for Cygwin:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make
|
||||
</pre>
|
||||
<p>
|
||||
Then follow the installation instructions below.
|
||||
</p>
|
||||
<h3>Installing LuaJIT</h3>
|
||||
<p>
|
||||
Copy <tt>luajit.exe</tt> and <tt>lua51.dll</tt> (built in the <tt>src</tt>
|
||||
directory) to a newly created directory (any location is ok).
|
||||
Add <tt>lua</tt> and <tt>lua\jit</tt> directories below it and copy
|
||||
all Lua files from the <tt>src\jit</tt> directory of the distribution
|
||||
to the latter directory.
|
||||
</p>
|
||||
<p>
|
||||
There are no hardcoded
|
||||
absolute path names — all modules are loaded relative to the
|
||||
directory where <tt>luajit.exe</tt> is installed
|
||||
(see <tt>src/luaconf.h</tt>).
|
||||
</p>
|
||||
|
||||
<h2 id="cross">Cross-compiling LuaJIT</h2>
|
||||
<p>
|
||||
The GNU Makefile-based build system allows cross-compiling on any host
|
||||
for any supported target, as long as both architectures have the same
|
||||
pointer size. If you want to cross-compile to any 32 bit target on an
|
||||
x64 OS, you need to install the multilib development package (e.g.
|
||||
<tt>libc6-dev-i386</tt> on Debian/Ubuntu) and build a 32 bit host part
|
||||
(<tt>HOST_CC="gcc -m32"</tt>).
|
||||
</p>
|
||||
<p>
|
||||
You need to specify <tt>TARGET_SYS</tt> whenever the host OS and the
|
||||
target OS differ, or you'll get assembler or linker errors. E.g. if
|
||||
you're compiling on a Windows or OSX host for embedded Linux or Android,
|
||||
you need to add <tt>TARGET_SYS=Linux</tt> to the examples below. For a
|
||||
minimal target OS, you may need to disable the built-in allocator in
|
||||
<tt>src/Makefile</tt> and use <tt>TARGET_SYS=Other</tt>. The examples
|
||||
below only show some popular targets — please check the comments
|
||||
in <tt>src/Makefile</tt> for more details.
|
||||
</p>
|
||||
<pre class="code">
|
||||
# Cross-compile to a 32 bit binary on a multilib x64 OS
|
||||
make CC="gcc -m32"
|
||||
|
||||
# Cross-compile on Debian/Ubuntu for Windows (mingw32 package)
|
||||
make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows
|
||||
</pre>
|
||||
<p id="cross2">
|
||||
The <tt>CROSS</tt> prefix allows specifying a standard GNU cross-compile
|
||||
toolchain (Binutils, GCC and a matching libc). The prefix may vary
|
||||
depending on the <tt>--target</tt> the toolchain was built for (note the
|
||||
<tt>CROSS</tt> prefix has a trailing <tt>"-"</tt>). The examples below
|
||||
use the canonical toolchain triplets for Linux.
|
||||
</p>
|
||||
<p>
|
||||
Since there's often no easy way to detect CPU features at runtime, it's
|
||||
important to compile with the proper CPU or architecture settings. You
|
||||
can specify these when building the toolchain yourself. Or add
|
||||
<tt>-mcpu=...</tt> or <tt>-march=...</tt> to <tt>TARGET_CFLAGS</tt>. For
|
||||
ARM it's important to have the correct <tt>-mfloat-abi=...</tt> setting,
|
||||
too. Otherwise LuaJIT may not run at the full performance of your target
|
||||
CPU.
|
||||
</p>
|
||||
<pre class="code">
|
||||
# ARM soft-float
|
||||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabi- \
|
||||
TARGET_CFLAGS="-mfloat-abi=soft"
|
||||
|
||||
# ARM soft-float ABI with VFP (example for Cortex-A8)
|
||||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabi- \
|
||||
TARGET_CFLAGS="-mcpu=cortex-a8 -mfloat-abi=softfp"
|
||||
|
||||
# ARM hard-float ABI with VFP (armhf, requires recent toolchain)
|
||||
make HOST_CC="gcc -m32" CROSS=arm-linux-gnueabihf-
|
||||
|
||||
# PPC
|
||||
make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu-
|
||||
# PPC/e500v2 (fast interpreter only)
|
||||
make HOST_CC="gcc -m32" CROSS=powerpc-e500v2-linux-gnuspe-
|
||||
|
||||
# MIPS big-endian
|
||||
make HOST_CC="gcc -m32" CROSS=mips-linux-
|
||||
# MIPS little-endian
|
||||
make HOST_CC="gcc -m32" CROSS=mipsel-linux-
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="android">Android</b> using the <a href="http://developer.android.com/sdk/ndk/index.html"><span class="ext">»</span> Android NDK</a>.
|
||||
The environment variables need to match the install locations and the
|
||||
desired target platform. E.g. Android 4.0 corresponds to ABI level 14.
|
||||
For details check the folder <tt>docs</tt> in the NDK directory.
|
||||
</p>
|
||||
<p>
|
||||
Only a few common variations for the different CPUs, ABIs and platforms
|
||||
are listed. Please use your own judgement for which combination you want
|
||||
to build/deploy or which lowest common denominator you want to pick:
|
||||
</p>
|
||||
<pre class="code">
|
||||
# Android/ARM, armeabi (ARMv5TE soft-float), Android 2.2+ (Froyo)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=8
|
||||
NDKVER=$NDK/toolchains/arm-linux-androideabi-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/arm-linux-androideabi-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-arm"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF"
|
||||
|
||||
# Android/ARM, armeabi-v7a (ARMv7 VFP), Android 4.0+ (ICS)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=14
|
||||
NDKVER=$NDK/toolchains/arm-linux-androideabi-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/arm-linux-androideabi-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-arm"
|
||||
NDKARCH="-march=armv7-a -mfloat-abi=softfp -Wl,--fix-cortex-a8"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF $NDKARCH"
|
||||
|
||||
# Android/MIPS, mips (MIPS32R1 hard-float), Android 4.0+ (ICS)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=14
|
||||
NDKVER=$NDK/toolchains/mipsel-linux-android-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/mipsel-linux-android-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-mips"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF"
|
||||
|
||||
# Android/x86, x86 (i686 SSE3), Android 4.0+ (ICS)
|
||||
NDK=/opt/android/ndk
|
||||
NDKABI=14
|
||||
NDKVER=$NDK/toolchains/x86-4.6
|
||||
NDKP=$NDKVER/prebuilt/linux-x86/bin/i686-linux-android-
|
||||
NDKF="--sysroot $NDK/platforms/android-$NDKABI/arch-x86"
|
||||
make HOST_CC="gcc -m32" CROSS=$NDKP TARGET_FLAGS="$NDKF"
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="ios">iOS 3.0+</b> (iPhone/iPad) using the <a href="http://developer.apple.com/devcenter/ios/index.action"><span class="ext">»</span> iOS SDK</a>.
|
||||
The environment variables need to match the iOS SDK version:
|
||||
</p>
|
||||
<p style="font-size: 8pt;">
|
||||
Note: <b>the JIT compiler is disabled for iOS</b>, because regular iOS Apps
|
||||
are not allowed to generate code at runtime. You'll only get the performance
|
||||
of the LuaJIT interpreter on iOS. This is still faster than plain Lua, but
|
||||
much slower than the JIT compiler. Please complain to Apple, not me.
|
||||
Or use Android. :-p
|
||||
</p>
|
||||
<pre class="code">
|
||||
IXCODE=`xcode-select -print-path`
|
||||
ISDK=$IXCODE/Platforms/iPhoneOS.platform/Developer
|
||||
ISDKVER=iPhoneOS6.0.sdk
|
||||
ISDKP=$ISDK/usr/bin/
|
||||
ISDKF="-arch armv7 -isysroot $ISDK/SDKs/$ISDKVER"
|
||||
make HOST_CC="gcc -m32 -arch i386" CROSS=$ISDKP TARGET_FLAGS="$ISDKF" \
|
||||
TARGET_SYS=iOS
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="ps3">PS3</b> using the PS3 SDK from
|
||||
a Linux host or a Windows host (requires 32 bit MinGW (GCC) on the host,
|
||||
too). Due to restrictions on consoles, the JIT compiler is disabled and
|
||||
only the fast interpreter is built:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make HOST_CC="gcc -m32" CROSS=ppu-lv2-
|
||||
</pre>
|
||||
<p>
|
||||
You can cross-compile for <b id="xbox360">Xbox 360</b> using the
|
||||
Xbox 360 SDK (MSVC + XEDK). Due to restrictions on consoles, the
|
||||
JIT compiler is disabled and only the fast interpreter is built.
|
||||
</p>
|
||||
<p>
|
||||
Open a "Visual Studio .NET Command Prompt" (32 bit host compiler),
|
||||
<tt>cd</tt> to the directory where you've unpacked the sources and run
|
||||
the following commands. This builds a static library <tt>luajit20.lib</tt>,
|
||||
which can be linked against your game, just like the Lua library.
|
||||
</p>
|
||||
<pre class="code">
|
||||
cd src
|
||||
xedkbuild
|
||||
</pre>
|
||||
|
||||
<h2 id="embed">Embedding LuaJIT</h2>
|
||||
<p>
|
||||
LuaJIT is API-compatible with Lua 5.1. If you've already embedded Lua
|
||||
into your application, you probably don't need to do anything to switch
|
||||
to LuaJIT, except link with a different library:
|
||||
</p>
|
||||
<ul>
|
||||
<li>It's strongly suggested to build LuaJIT separately using the supplied
|
||||
build system. Please do <em>not</em> attempt to integrate the individual
|
||||
source files into your build tree. You'll most likely get the internal build
|
||||
dependencies wrong or mess up the compiler flags. Treat LuaJIT like any
|
||||
other external library and link your application with either the dynamic
|
||||
or static library, depending on your needs.</li>
|
||||
<li>If you want to load C modules compiled for plain Lua
|
||||
with <tt>require()</tt>, you need to make sure the public symbols
|
||||
(e.g. <tt>lua_pushnumber</tt>) are exported, too:
|
||||
<ul><li>On POSIX systems you can either link to the shared library
|
||||
or link the static library into your application. In the latter case
|
||||
you'll need to export all public symbols from your main executable
|
||||
(e.g. <tt>-Wl,-E</tt> on Linux) and add the external dependencies
|
||||
(e.g. <tt>-lm -ldl</tt> on Linux).</li>
|
||||
<li>Since Windows symbols are bound to a specific DLL name, you need to
|
||||
link to the <tt>lua51.dll</tt> created by the LuaJIT build (do not rename
|
||||
the DLL). You may link LuaJIT statically on Windows only if you don't
|
||||
intend to load Lua/C modules at runtime.
|
||||
</li></ul>
|
||||
</li>
|
||||
<li>
|
||||
If you're building a 64 bit application on OSX which links directly or
|
||||
indirectly against LuaJIT, you need to link your main executable
|
||||
with these flags:
|
||||
<pre class="code">
|
||||
-pagezero_size 10000 -image_base 100000000
|
||||
</pre>
|
||||
Also, it's recommended to <tt>rebase</tt> all (self-compiled) shared libraries
|
||||
which are loaded at runtime on OSX/x64 (e.g. C extension modules for Lua).
|
||||
See: <tt>man rebase</tt>
|
||||
</li>
|
||||
</ul>
|
||||
<p>Additional hints for initializing LuaJIT using the C API functions:</p>
|
||||
<ul>
|
||||
<li>Here's a
|
||||
<a href="http://lua-users.org/wiki/SimpleLuaApiExample"><span class="ext">»</span> simple example</a>
|
||||
for embedding Lua or LuaJIT into your application.</li>
|
||||
<li>Make sure you use <tt>luaL_newstate</tt>. Avoid using
|
||||
<tt>lua_newstate</tt>, since this uses the (slower) default memory
|
||||
allocator from your system (no support for this on x64).</li>
|
||||
<li>Make sure you use <tt>luaL_openlibs</tt> and not the old Lua 5.0 style
|
||||
of calling <tt>luaopen_base</tt> etc. directly.</li>
|
||||
<li>To change or extend the list of standard libraries to load, copy
|
||||
<tt>src/lib_init.c</tt> to your project and modify it accordingly.
|
||||
Make sure the <tt>jit</tt> library is loaded or the JIT compiler
|
||||
will not be activated.</li>
|
||||
<li>The <tt>bit.*</tt> module for bitwise operations
|
||||
is already built-in. There's no need to statically link
|
||||
<a href="http://bitop.luajit.org/"><span class="ext">»</span> Lua BitOp</a> to your application.</li>
|
||||
</ul>
|
||||
|
||||
<h2 id="distro">Hints for Distribution Maintainers</h2>
|
||||
<p>
|
||||
The LuaJIT build system has extra provisions for the needs of most
|
||||
POSIX-based distributions. If you're a package maintainer for
|
||||
a distribution, <em>please</em> make use of these features and
|
||||
avoid patching, subverting, autotoolizing or messing up the build system
|
||||
in unspeakable ways.
|
||||
</p>
|
||||
<p>
|
||||
There should be absolutely no need to patch <tt>luaconf.h</tt> or any
|
||||
of the Makefiles. And please do not hand-pick files for your packages —
|
||||
simply use whatever <tt>make install</tt> creates. There's a reason
|
||||
for all of the files <em>and</em> directories it creates.
|
||||
</p>
|
||||
<p>
|
||||
The build system uses GNU make and auto-detects most settings based on
|
||||
the host you're building it on. This should work fine for native builds,
|
||||
even when sandboxed. You may need to pass some of the following flags to
|
||||
<em>both</em> the <tt>make</tt> and the <tt>make install</tt> command lines
|
||||
for a regular distribution build:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>PREFIX</tt> overrides the installation path and should usually
|
||||
be set to <tt>/usr</tt>. Setting this also changes the module paths and
|
||||
the <tt>-rpath</tt> of the shared library.</li>
|
||||
<li><tt>DESTDIR</tt> is an absolute path which allows you to install
|
||||
to a shadow tree instead of the root tree of the build system.</li>
|
||||
<li>Have a look at the top-level <tt>Makefile</tt> and <tt>src/Makefile</tt>
|
||||
for additional variables to tweak. The following variables <em>may</em> be
|
||||
overridden, but it's <em>not</em> recommended, except for special needs
|
||||
like cross-builds:
|
||||
<tt>BUILDMODE, CC, HOST_CC, STATIC_CC, DYNAMIC_CC, CFLAGS, HOST_CFLAGS,
|
||||
TARGET_CFLAGS, LDFLAGS, HOST_LDFLAGS, TARGET_LDFLAGS, TARGET_SHLDFLAGS,
|
||||
TARGET_FLAGS, LIBS, HOST_LIBS, TARGET_LIBS, CROSS, HOST_SYS, TARGET_SYS
|
||||
</tt></li>
|
||||
</ul>
|
||||
<p>
|
||||
The build system has a special target for an amalgamated build, i.e.
|
||||
<tt>make amalg</tt>. This compiles the LuaJIT core as one huge C file
|
||||
and allows GCC to generate faster and shorter code. Alas, this requires
|
||||
lots of memory during the build. This may be a problem for some users,
|
||||
that's why it's not enabled by default. But it shouldn't be a problem for
|
||||
most build farms. It's recommended that binary distributions use this
|
||||
target for their LuaJIT builds.
|
||||
</p>
|
||||
<p>
|
||||
The tl;dr version of the above:
|
||||
</p>
|
||||
<pre class="code">
|
||||
make amalg PREFIX=/usr && \
|
||||
make install PREFIX=/usr DESTDIR=/tmp/buildroot
|
||||
</pre>
|
||||
<p>
|
||||
Finally, if you encounter any difficulties, please
|
||||
<a href="contact.html">contact me</a> first, instead of releasing a broken
|
||||
package onto unsuspecting users. Because they'll usually gonna complain
|
||||
to me (the upstream) and not you (the package maintainer), anyway.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+228
@@ -0,0 +1,228 @@
|
||||
<!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-2013, 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">
|
||||
<meta name="description" content="LuaJIT is a Just-In-Time (JIT) compiler for the Lua language.">
|
||||
<style type="text/css">
|
||||
table.feature {
|
||||
width: inherit;
|
||||
line-height: 1.2;
|
||||
margin: 0;
|
||||
}
|
||||
table.feature td {
|
||||
width: 80px;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border: 4px solid #e6ecff;
|
||||
border-radius: 12px;
|
||||
}
|
||||
table.os td {
|
||||
background: #7080d0;
|
||||
background-image: linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -moz-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -webkit-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -o-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
background-image: -ms-linear-gradient(#4060c0 10%, #b0b0ff 95%);
|
||||
}
|
||||
table.os1 td {
|
||||
color: #ffff80;
|
||||
}
|
||||
table.os2 td {
|
||||
color: #ffa040;
|
||||
}
|
||||
table.compiler td {
|
||||
color: #2080ff;
|
||||
background: #62bf41;
|
||||
background-image: linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -moz-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -webkit-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -o-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
background-image: -ms-linear-gradient(#62bf41 10%, #b0ffb0 95%);
|
||||
}
|
||||
table.cpu td {
|
||||
color: #ffff00;
|
||||
background: #cf7251;
|
||||
background-image: linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -moz-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -webkit-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -o-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
background-image: -ms-linear-gradient(#bf6241 10%, #ffb0b0 95%);
|
||||
}
|
||||
table.fcompat td {
|
||||
color: #2060e0;
|
||||
background: #61cfcf;
|
||||
background-image: linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -moz-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -webkit-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -o-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
background-image: -ms-linear-gradient(#41bfbf 10%, #b0ffff 95%);
|
||||
}
|
||||
table.stats td {
|
||||
color: #ffffff;
|
||||
background: #a0a0a0;
|
||||
background-image: linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -moz-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -webkit-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -o-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
background-image: -ms-linear-gradient(#808080 10%, #d0d0d0 95%);
|
||||
}
|
||||
table.stats td.speed {
|
||||
color: #ff4020;
|
||||
}
|
||||
table.stats td.kb {
|
||||
color: #ffff80;
|
||||
background: #808080;
|
||||
background-image: linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -moz-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -webkit-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -o-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
background-image: -ms-linear-gradient(#606060 10%, #c0c0c0 95%);
|
||||
}
|
||||
table.feature small {
|
||||
font-size: 50%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="site">
|
||||
<a href="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="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT is a <b>Just-In-Time Compiler</b> (JIT) for the
|
||||
<a href="http://www.lua.org/"><span class="ext">»</span> Lua</a> programming language.
|
||||
Lua is a powerful, dynamic and light-weight programming language.
|
||||
It may be embedded or used as a general-purpose, stand-alone language.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT is Copyright © 2005-2013 Mike Pall, released under the
|
||||
<a href="http://www.opensource.org/licenses/mit-license.php"><span class="ext">»</span> MIT open source license</a>.
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
|
||||
<h2>Compatibility</h2>
|
||||
<table class="feature os os1">
|
||||
<tr><td>Windows</td><td>Linux</td><td>BSD</td><td>OSX</td><td>POSIX</td></tr>
|
||||
</table>
|
||||
<table class="feature os os2">
|
||||
<tr><td><span style="font-size:90%;">Embedded</span></td><td>Android</td><td>iOS</td><td>PS3</td><td>Xbox 360</td></tr>
|
||||
</table>
|
||||
<table class="feature compiler">
|
||||
<tr><td>GCC</td><td>CLANG<br>LLVM</td><td>MSVC</td></tr>
|
||||
</table>
|
||||
<table class="feature cpu">
|
||||
<tr><td>x86</td><td>x64</td><td>ARM</td><td>PPC</td><td>e500</td><td>MIPS</td></tr>
|
||||
</table>
|
||||
<table class="feature fcompat">
|
||||
<tr><td>Lua 5.1<br>API+ABI</td><td>+ JIT</td><td>+ BitOp</td><td>+ FFI</td><td>Drop-in<br>DLL/.so</td></tr>
|
||||
</table>
|
||||
|
||||
<h2>Overview</h2>
|
||||
<table class="feature stats">
|
||||
<tr>
|
||||
<td class="speed">3x<br>- 100x</td>
|
||||
<td class="kb">115 <small>KB</small><br>VM</td>
|
||||
<td class="kb">90 <small>KB</small><br>JIT</td>
|
||||
<td class="kloc">63 <small>KLOC</small><br>C</td>
|
||||
<td class="kloc">24 <small>KLOC</small><br>ASM</td>
|
||||
<td class="kloc">11 <small>KLOC</small><br>Lua</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p style="margin-top: 1em;">
|
||||
LuaJIT has been successfully used as a <b>scripting middleware</b> in
|
||||
games, appliances, network and graphics apps, numerical simulations,
|
||||
trading platforms and many other specialty applications. It scales from
|
||||
embedded devices, smartphones, desktops up to server farms. It combines
|
||||
high flexibility with <a href="http://luajit.org/performance.html"><span class="ext">»</span> high performance</a>
|
||||
and an unmatched <b>low memory footprint</b>.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT has been in continuous development since 2005. It's widely
|
||||
considered to be <b>one of the fastest dynamic language
|
||||
implementations</b>. It has outperformed other dynamic languages on many
|
||||
cross-language benchmarks since its first release — often by a
|
||||
substantial margin.
|
||||
</p>
|
||||
<p>
|
||||
For <b>LuaJIT 2.0</b>, the whole VM has been rewritten from the ground up
|
||||
and relentlessly optimized for performance. It combines a <b>high-speed
|
||||
interpreter</b>, written in assembler, with a <b>state-of-the-art JIT
|
||||
compiler</b>.
|
||||
</p>
|
||||
<p>
|
||||
An innovative <b>trace compiler</b> is integrated with advanced,
|
||||
SSA-based optimizations and highly tuned code generation backends.
|
||||
A substantial reduction of the overhead associated with dynamic languages
|
||||
allows it to break into the performance range traditionally reserved for
|
||||
offline, static language compilers.
|
||||
</p>
|
||||
|
||||
<h2>More ...</h2>
|
||||
<p>
|
||||
Please select a sub-topic in the navigation bar to learn more about LuaJIT.
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+306
@@ -0,0 +1,306 @@
|
||||
<!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-2013, 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="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a class="current" href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
LuaJIT has only a single stand-alone executable, called <tt>luajit</tt> on
|
||||
POSIX systems or <tt>luajit.exe</tt> on Windows. It can be used to run simple
|
||||
Lua statements or whole Lua applications from the command line. It has an
|
||||
interactive mode, too.
|
||||
</p>
|
||||
|
||||
<h2 id="options">Command Line Options</h2>
|
||||
<p>
|
||||
The <tt>luajit</tt> stand-alone executable is just a slightly modified
|
||||
version of the regular <tt>lua</tt> stand-alone executable.
|
||||
It supports the same basic options, too. <tt>luajit -h</tt>
|
||||
prints a short list of the available options. Please have a look at the
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#6"><span class="ext">»</span> Lua manual</a>
|
||||
for details.
|
||||
</p>
|
||||
<p>
|
||||
LuaJIT has some additional options:
|
||||
</p>
|
||||
|
||||
<h3 id="opt_b"><tt>-b[options] input output</tt></h3>
|
||||
<p>
|
||||
This option saves or lists bytecode. The following additional options
|
||||
are accepted:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>-l</tt> — Only list bytecode.</li>
|
||||
<li><tt>-s</tt> — Strip debug info (this is the default).</li>
|
||||
<li><tt>-g</tt> — Keep debug info.</li>
|
||||
<li><tt>-n name</tt> — Set module name (default: auto-detect from input name)</li>
|
||||
<li><tt>-t type</tt> — Set output file type (default: auto-detect from output name).</li>
|
||||
<li><tt>-a arch</tt> — Override architecture for object files (default: native).</li>
|
||||
<li><tt>-o os</tt> — Override OS for object files (default: native).</li>
|
||||
<li><tt>-e chunk</tt> — Use chunk string as input.</li>
|
||||
<li><tt>-</tt> (a single minus sign) — Use stdin as input and/or stdout as output.</li>
|
||||
</ul>
|
||||
<p>
|
||||
The output file type is auto-detected from the extension of the output
|
||||
file name:
|
||||
</p>
|
||||
<ul>
|
||||
<li><tt>c</tt> — C source file, exported bytecode data.</li>
|
||||
<li><tt>h</tt> — C header file, static bytecode data.</li>
|
||||
<li><tt>obj</tt> or <tt>o</tt> — Object file, exported bytecode data
|
||||
(OS- and architecture-specific).</li>
|
||||
<li><tt>raw</tt> or any other extension — Raw bytecode file (portable).
|
||||
</ul>
|
||||
<p>
|
||||
Notes:
|
||||
</p>
|
||||
<ul>
|
||||
<li>See also <a href="extensions.html#string_dump">string.dump()</a>
|
||||
for information on bytecode portability and compatibility.</li>
|
||||
<li>A file in raw bytecode format is auto-detected and can be loaded like
|
||||
any Lua source file. E.g. directly from the command line or with
|
||||
<tt>loadfile()</tt>, <tt>dofile()</tt> etc.</li>
|
||||
<li>To statically embed the bytecode of a module in your application,
|
||||
generate an object file and just link it with your application.</li>
|
||||
<li>On most ELF-based systems (e.g. Linux) you need to explicitly export the
|
||||
global symbols when linking your application, e.g. with: <tt>-Wl,-E</tt></li>
|
||||
<li><tt>require()</tt> tries to load embedded bytecode data from exported
|
||||
symbols (in <tt>*.exe</tt> or <tt>lua51.dll</tt> on Windows) and from
|
||||
shared libraries in <tt>package.cpath</tt>.</li>
|
||||
</ul>
|
||||
<p>
|
||||
Typical usage examples:
|
||||
</p>
|
||||
<pre class="code">
|
||||
luajit -b test.lua test.out # Save bytecode to test.out
|
||||
luajit -bg test.lua test.out # Keep debug info
|
||||
luajit -be "print('hello world')" test.out # Save cmdline script
|
||||
|
||||
luajit -bl test.lua # List to stdout
|
||||
luajit -bl test.lua test.txt # List to test.txt
|
||||
luajit -ble "print('hello world')" # List cmdline script
|
||||
|
||||
luajit -b test.lua test.obj # Generate object file
|
||||
# Link test.obj with your application and load it with require("test")
|
||||
</pre>
|
||||
|
||||
<h3 id="opt_j"><tt>-j cmd[=arg[,arg...]]</tt></h3>
|
||||
<p>
|
||||
This option performs a LuaJIT control command or activates one of the
|
||||
loadable extension modules. The command is first looked up in the
|
||||
<tt>jit.*</tt> library. If no matching function is found, a module
|
||||
named <tt>jit.<cmd></tt> is loaded and the <tt>start()</tt>
|
||||
function of the module is called with the specified arguments (if
|
||||
any). The space between <tt>-j</tt> and <tt>cmd</tt> is optional.
|
||||
</p>
|
||||
<p>
|
||||
Here are the available LuaJIT control commands:
|
||||
</p>
|
||||
<ul>
|
||||
<li id="j_on"><tt>-jon</tt> — Turns the JIT compiler on (default).</li>
|
||||
<li id="j_off"><tt>-joff</tt> — Turns the JIT compiler off (only use the interpreter).</li>
|
||||
<li id="j_flush"><tt>-jflush</tt> — Flushes the whole cache of compiled code.</li>
|
||||
<li id="j_v"><tt>-jv</tt> — Shows verbose information about the progress of the JIT compiler.</li>
|
||||
<li id="j_dump"><tt>-jdump</tt> — Dumps the code and structures used in various compiler stages.</li>
|
||||
</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.2/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 extremely fast (we are talking
|
||||
about the microsecond to millisecond range). Disabling optimizations
|
||||
doesn't have any visible impact on its overhead, but usually generates
|
||||
code that runs slower.
|
||||
</p>
|
||||
<p>
|
||||
The first form sets an optimization level — this enables a
|
||||
specific mix of optimization flags. <tt>-O0</tt> turns off all
|
||||
optimizations and higher numbers enable more optimizations. Omitting
|
||||
the level (i.e. just <tt>-O</tt>) sets the default optimization level,
|
||||
which is <tt>-O3</tt> in the current version.
|
||||
</p>
|
||||
<p>
|
||||
The second form adds or removes individual optimization flags.
|
||||
The third form sets a parameter for the VM or the JIT compiler
|
||||
to a specific value.
|
||||
</p>
|
||||
<p>
|
||||
You can either use this option multiple times (like <tt>-Ocse
|
||||
-O-dce -Ohotloop=10</tt>) or separate several settings with a comma
|
||||
(like <tt>-O+cse,-dce,hotloop=10</tt>). The settings are applied from
|
||||
left to right and later settings override earlier ones. You can freely
|
||||
mix the three forms, but note that setting an optimization level
|
||||
overrides all earlier flags.
|
||||
</p>
|
||||
<p>
|
||||
Here are the available flags and at what optimization levels they
|
||||
are enabled:
|
||||
</p>
|
||||
<table class="opt">
|
||||
<tr class="opthead">
|
||||
<td class="flag_name">Flag</td>
|
||||
<td class="flag_level">-O1</td>
|
||||
<td class="flag_level">-O2</td>
|
||||
<td class="flag_level">-O3</td>
|
||||
<td class="flag_desc"> </td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="flag_name">fold</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Constant Folding, Simplifications and Reassociation</td></tr>
|
||||
<tr class="even">
|
||||
<td class="flag_name">cse</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Common-Subexpression Elimination</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="flag_name">dce</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Dead-Code Elimination</td></tr>
|
||||
<tr class="even">
|
||||
<td class="flag_name">narrow</td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Narrowing of numbers to integers</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="flag_name">loop</td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_level">•</td><td class="flag_desc">Loop Optimizations (code hoisting)</td></tr>
|
||||
<tr class="even">
|
||||
<td class="flag_name">fwd</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Load Forwarding (L2L) and Store Forwarding (S2L)</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="flag_name">dse</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Dead-Store Elimination</td></tr>
|
||||
<tr class="even">
|
||||
<td class="flag_name">abc</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Array Bounds Check Elimination</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="flag_name">sink</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Allocation/Store Sinking</td></tr>
|
||||
<tr class="even">
|
||||
<td class="flag_name">fuse</td><td class="flag_level"> </td><td class="flag_level"> </td><td class="flag_level">•</td><td class="flag_desc">Fusion of operands into instructions</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
Here are the parameters and their default settings:
|
||||
</p>
|
||||
<table class="opt">
|
||||
<tr class="opthead">
|
||||
<td class="param_name">Parameter</td>
|
||||
<td class="param_default">Default</td>
|
||||
<td class="param_desc"> </td>
|
||||
</tr>
|
||||
<tr class="odd separate">
|
||||
<td class="param_name">maxtrace</td><td class="param_default">1000</td><td class="param_desc">Max. number of traces in the cache</td></tr>
|
||||
<tr class="even">
|
||||
<td class="param_name">maxrecord</td><td class="param_default">4000</td><td class="param_desc">Max. number of recorded IR instructions</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="param_name">maxirconst</td><td class="param_default">500</td><td class="param_desc">Max. number of IR constants of a trace</td></tr>
|
||||
<tr class="even">
|
||||
<td class="param_name">maxside</td><td class="param_default">100</td><td class="param_desc">Max. number of side traces of a root trace</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="param_name">maxsnap</td><td class="param_default">500</td><td class="param_desc">Max. number of snapshots for a trace</td></tr>
|
||||
<tr class="even separate">
|
||||
<td class="param_name">hotloop</td><td class="param_default">56</td><td class="param_desc">Number of iterations to detect a hot loop or hot call</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="param_name">hotexit</td><td class="param_default">10</td><td class="param_desc">Number of taken exits to start a side trace</td></tr>
|
||||
<tr class="even">
|
||||
<td class="param_name">tryside</td><td class="param_default">4</td><td class="param_desc">Number of attempts to compile a side trace</td></tr>
|
||||
<tr class="odd separate">
|
||||
<td class="param_name">instunroll</td><td class="param_default">4</td><td class="param_desc">Max. unroll factor for instable loops</td></tr>
|
||||
<tr class="even">
|
||||
<td class="param_name">loopunroll</td><td class="param_default">15</td><td class="param_desc">Max. unroll factor for loop ops in side traces</td></tr>
|
||||
<tr class="odd">
|
||||
<td class="param_name">callunroll</td><td class="param_default">3</td><td class="param_desc">Max. unroll factor for pseudo-recursive calls</td></tr>
|
||||
<tr class="even">
|
||||
<td class="param_name">recunroll</td><td class="param_default">2</td><td class="param_desc">Min. unroll factor for true recursion</td></tr>
|
||||
<tr class="odd separate">
|
||||
<td class="param_name">sizemcode</td><td class="param_default">32</td><td class="param_desc">Size of each machine code area in KBytes (Windows: 64K)</td></tr>
|
||||
<tr class="even">
|
||||
<td class="param_name">maxmcode</td><td class="param_default">512</td><td class="param_desc">Max. total size of all machine code areas in KBytes</td></tr>
|
||||
</table>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Status & 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-2013, 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 & Roadmap</h1>
|
||||
</div>
|
||||
<div id="nav">
|
||||
<ul><li>
|
||||
<a href="luajit.html">LuaJIT</a>
|
||||
<ul><li>
|
||||
<a href="http://luajit.org/download.html">Download <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="install.html">Installation</a>
|
||||
</li><li>
|
||||
<a href="running.html">Running</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="extensions.html">Extensions</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi.html">FFI Library</a>
|
||||
<ul><li>
|
||||
<a href="ext_ffi_tutorial.html">FFI Tutorial</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_api.html">ffi.* API</a>
|
||||
</li><li>
|
||||
<a href="ext_ffi_semantics.html">FFI Semantics</a>
|
||||
</li></ul>
|
||||
</li><li>
|
||||
<a href="ext_jit.html">jit.* Library</a>
|
||||
</li><li>
|
||||
<a href="ext_c_api.html">Lua/C API</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/performance.html">Performance <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://wiki.luajit.org/">Wiki <span class="ext">»</span></a>
|
||||
</li><li>
|
||||
<a href="http://luajit.org/list.html">Mailing List <span class="ext">»</span></a>
|
||||
</li></ul>
|
||||
</div>
|
||||
<div id="main">
|
||||
<p>
|
||||
<span style="color: #0000c0;">LuaJIT 2.0</span> is the current
|
||||
<span style="color: #0000c0;">stable branch</span>. This branch is in
|
||||
feature-freeze — new features will only be added to LuaJIT 2.1.
|
||||
</p>
|
||||
|
||||
<h2>Current Status</h2>
|
||||
<p>
|
||||
LuaJIT ought to run all Lua 5.1-compatible source code just fine.
|
||||
It's considered a serious bug if the VM crashes or produces unexpected
|
||||
results — please report this.
|
||||
</p>
|
||||
<p>
|
||||
Known incompatibilities and issues in LuaJIT 2.0:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
There are some differences in <b>implementation-defined</b> behavior.
|
||||
These either have a good reason, are arbitrary design choices
|
||||
or are due to quirks in the VM. The latter cases may get fixed if a
|
||||
demonstrable need is shown.
|
||||
</li>
|
||||
<li>
|
||||
The Lua <b>debug API</b> is missing a couple of features (return
|
||||
hooks for non-Lua functions) and shows slightly different behavior
|
||||
in LuaJIT (no per-coroutine hooks, no tail call counting).
|
||||
</li>
|
||||
<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>
|
||||
<li>
|
||||
Currently some <b>out-of-memory</b> errors from <b>on-trace code</b> are not
|
||||
handled correctly. The error may fall through an on-trace
|
||||
<tt>pcall</tt> or it may be passed on to the function set with
|
||||
<tt>lua_atpanic</tt> on x64. This issue will be fixed with the new
|
||||
garbage collector.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2>Roadmap</h2>
|
||||
<p>
|
||||
Please refer to the
|
||||
<a href="http://www.freelists.org/post/luajit/LuaJIT-Roadmap-20122013"><span class="ext">»</span> LuaJIT Roadmap 2012/2013</a> and an
|
||||
<a href="http://www.freelists.org/post/luajit/LuaJIT-Roadmap-20122013-UPDATE"><span class="ext">»</span> update on release planning</a> for details.
|
||||
</p>
|
||||
<p>
|
||||
</p>
|
||||
<br class="flush">
|
||||
</div>
|
||||
<div id="foot">
|
||||
<hr class="hide">
|
||||
Copyright © 2005-2013 Mike Pall
|
||||
<span class="noprint">
|
||||
·
|
||||
<a href="contact.html">Contact</a>
|
||||
</span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+456
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
** DynASM ARM encoding engine.
|
||||
** Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "arm"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC,
|
||||
DASM_IMM, DASM_IMM12, DASM_IMM16, DASM_IMML8, DASM_IMML12, DASM_IMMV8,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
static int dasm_imm12(unsigned int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 16; i++, n = (n << 2) | (n >> 30))
|
||||
if (n <= 255) return (int)(n + (i << 8));
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
case DASM_IMM16:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
if ((ins & 0x8000))
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMMV8:
|
||||
CK((n & 3) == 0, RANGE_I);
|
||||
n >>= 2;
|
||||
case DASM_IMML8:
|
||||
case DASM_IMML12:
|
||||
CK(n >= 0 ? ((n>>((ins>>5)&31)) == 0) :
|
||||
(((-n)>>((ins>>5)&31)) == 0), RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
CK(dasm_imm12((unsigned int)n) != -1, RANGE_I);
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: case DASM_IMM12: case DASM_IMM16:
|
||||
case DASM_IMML8: case DASM_IMML12: case DASM_IMMV8: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins&2047), !(ins&2048));
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0xe1a00000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
CK(n >= 0, UNDEF_LG);
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base) - 4;
|
||||
patchrel:
|
||||
if ((ins & 0x800) == 0) {
|
||||
CK((n & 3) == 0 && ((n+0x02000000) >> 26) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n >> 2) & 0x00ffffff);
|
||||
} else if ((ins & 0x1000)) {
|
||||
CK((n & 3) == 0 && -256 <= n && n <= 256, RANGE_REL);
|
||||
goto patchimml8;
|
||||
} else if ((ins & 0x2000) == 0) {
|
||||
CK((n & 3) == 0 && -4096 <= n && n <= 4096, RANGE_REL);
|
||||
goto patchimml;
|
||||
} else {
|
||||
CK((n & 3) == 0 && -1020 <= n && n <= 1020, RANGE_REL);
|
||||
n >>= 2;
|
||||
goto patchimml;
|
||||
}
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= ((n>>((ins>>10)&31)) & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
case DASM_IMM12:
|
||||
cp[-1] |= dasm_imm12((unsigned int)n);
|
||||
break;
|
||||
case DASM_IMM16:
|
||||
cp[-1] |= ((n & 0xf000) << 4) | (n & 0x0fff);
|
||||
break;
|
||||
case DASM_IMML8: patchimml8:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | (n & 0x0f) | ((n & 0xf0) << 4)) :
|
||||
((-n & 0x0f) | ((-n & 0xf0) << 4));
|
||||
break;
|
||||
case DASM_IMML12: case DASM_IMMV8: patchimml:
|
||||
cp[-1] |= n >= 0 ? (0x00800000 | n) : (-n);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+1122
File diff suppressed because it is too large
Load Diff
Vendored
+416
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
** DynASM MIPS encoding engine.
|
||||
** Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "mips"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16) - 0xff00;
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1);
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
CK(n >= 0, UNDEF_LG);
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n);
|
||||
if (ins & 2048)
|
||||
n = n - (int)((char *)cp - base);
|
||||
else
|
||||
n = (n + (int)base) & 0x0fffffff;
|
||||
patchrel:
|
||||
CK((n & 3) == 0 &&
|
||||
((n + ((ins & 2048) ? 0x00020000 : 0)) >>
|
||||
((ins & 2048) ? 18 : 28)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n>>2) & ((ins & 2048) ? 0x0000ffff: 0x03ffffff));
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+953
@@ -0,0 +1,953 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM MIPS module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Module information:
|
||||
local _info = {
|
||||
arch = "mips",
|
||||
description = "DynASM MIPS module",
|
||||
version = "1.3.0",
|
||||
vernum = 10300,
|
||||
release = "2012-01-23",
|
||||
author = "Mike Pall",
|
||||
license = "MIT",
|
||||
}
|
||||
|
||||
-- Exported glue functions for the arch-specific module.
|
||||
local _M = { _info = _info }
|
||||
|
||||
-- Cache library functions.
|
||||
local type, tonumber, pairs, ipairs = type, tonumber, pairs, ipairs
|
||||
local assert, setmetatable = assert, setmetatable
|
||||
local _s = string
|
||||
local sub, format, byte, char = _s.sub, _s.format, _s.byte, _s.char
|
||||
local match, gmatch = _s.match, _s.gmatch
|
||||
local concat, sort = table.concat, table.sort
|
||||
local bit = bit or require("bit")
|
||||
local band, shl, sar, tohex = bit.band, bit.lshift, bit.arshift, bit.tohex
|
||||
|
||||
-- Inherited tables and callbacks.
|
||||
local g_opt, g_arch
|
||||
local wline, werror, wfatal, wwarn
|
||||
|
||||
-- Action name list.
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local action_names = {
|
||||
"STOP", "SECTION", "ESC", "REL_EXT",
|
||||
"ALIGN", "REL_LG", "LABEL_LG",
|
||||
"REL_PC", "LABEL_PC", "IMM",
|
||||
}
|
||||
|
||||
-- Maximum number of section buffer positions for dasm_put().
|
||||
-- CHECK: Keep this in sync with the C code!
|
||||
local maxsecpos = 25 -- Keep this low, to avoid excessively long C lines.
|
||||
|
||||
-- Action name -> action number.
|
||||
local map_action = {}
|
||||
for n,name in ipairs(action_names) do
|
||||
map_action[name] = n-1
|
||||
end
|
||||
|
||||
-- Action list buffer.
|
||||
local actlist = {}
|
||||
|
||||
-- Argument list for next dasm_put(). Start with offset 0 into action list.
|
||||
local actargs = { 0 }
|
||||
|
||||
-- Current number of section buffer positions for dasm_put().
|
||||
local secpos = 1
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump action names and numbers.
|
||||
local function dumpactions(out)
|
||||
out:write("DynASM encoding engine action codes:\n")
|
||||
for n,name in ipairs(action_names) do
|
||||
local num = map_action[name]
|
||||
out:write(format(" %-10s %02X %d\n", name, num, num))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write action list buffer as a huge static C array.
|
||||
local function writeactions(out, name)
|
||||
local nn = #actlist
|
||||
if nn == 0 then nn = 1; actlist[0] = map_action.STOP end
|
||||
out:write("static const unsigned int ", name, "[", nn, "] = {\n")
|
||||
for i = 1,nn-1 do
|
||||
assert(out:write("0x", tohex(actlist[i]), ",\n"))
|
||||
end
|
||||
assert(out:write("0x", tohex(actlist[nn]), "\n};\n\n"))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Add word to action list.
|
||||
local function wputxw(n)
|
||||
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||
actlist[#actlist+1] = n
|
||||
end
|
||||
|
||||
-- Add action to list with optional arg. Advance buffer pos, too.
|
||||
local function waction(action, val, a, num)
|
||||
local w = assert(map_action[action], "bad action name `"..action.."'")
|
||||
wputxw(0xff000000 + w * 0x10000 + (val or 0))
|
||||
if a then actargs[#actargs+1] = a end
|
||||
if a or num then secpos = secpos + (num or 1) end
|
||||
end
|
||||
|
||||
-- Flush action list (intervening C code or buffer pos overflow).
|
||||
local function wflush(term)
|
||||
if #actlist == actargs[1] then return end -- Nothing to flush.
|
||||
if not term then waction("STOP") end -- Terminate action list.
|
||||
wline(format("dasm_put(Dst, %s);", concat(actargs, ", ")), true)
|
||||
actargs = { #actlist } -- Actionlist offset is 1st arg to next dasm_put().
|
||||
secpos = 1 -- The actionlist offset occupies a buffer position, too.
|
||||
end
|
||||
|
||||
-- Put escaped word.
|
||||
local function wputw(n)
|
||||
if n >= 0xff000000 then waction("ESC") end
|
||||
wputxw(n)
|
||||
end
|
||||
|
||||
-- Reserve position for word.
|
||||
local function wpos()
|
||||
local pos = #actlist+1
|
||||
actlist[pos] = ""
|
||||
return pos
|
||||
end
|
||||
|
||||
-- Store word to reserved position.
|
||||
local function wputpos(pos, n)
|
||||
assert(n >= 0 and n <= 0xffffffff and n % 1 == 0, "word out of range")
|
||||
actlist[pos] = n
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Global label name -> global label number. With auto assignment on 1st use.
|
||||
local next_global = 20
|
||||
local map_global = setmetatable({}, { __index = function(t, name)
|
||||
if not match(name, "^[%a_][%w_]*$") then werror("bad global label") end
|
||||
local n = next_global
|
||||
if n > 2047 then werror("too many global labels") end
|
||||
next_global = n + 1
|
||||
t[name] = n
|
||||
return n
|
||||
end})
|
||||
|
||||
-- Dump global labels.
|
||||
local function dumpglobals(out, lvl)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("Global labels:\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(format(" %s\n", t[i]))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write global label enum.
|
||||
local function writeglobals(out, prefix)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("enum {\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(" ", prefix, t[i], ",\n")
|
||||
end
|
||||
out:write(" ", prefix, "_MAX\n};\n")
|
||||
end
|
||||
|
||||
-- Write global label names.
|
||||
local function writeglobalnames(out, name)
|
||||
local t = {}
|
||||
for name, n in pairs(map_global) do t[n] = name end
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=20,next_global-1 do
|
||||
out:write(" \"", t[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Extern label name -> extern label number. With auto assignment on 1st use.
|
||||
local next_extern = 0
|
||||
local map_extern_ = {}
|
||||
local map_extern = setmetatable({}, { __index = function(t, name)
|
||||
-- No restrictions on the name for now.
|
||||
local n = next_extern
|
||||
if n > 2047 then werror("too many extern labels") end
|
||||
next_extern = n + 1
|
||||
t[name] = n
|
||||
map_extern_[n] = name
|
||||
return n
|
||||
end})
|
||||
|
||||
-- Dump extern labels.
|
||||
local function dumpexterns(out, lvl)
|
||||
out:write("Extern labels:\n")
|
||||
for i=0,next_extern-1 do
|
||||
out:write(format(" %s\n", map_extern_[i]))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
-- Write extern label names.
|
||||
local function writeexternnames(out, name)
|
||||
out:write("static const char *const ", name, "[] = {\n")
|
||||
for i=0,next_extern-1 do
|
||||
out:write(" \"", map_extern_[i], "\",\n")
|
||||
end
|
||||
out:write(" (const char *)0\n};\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Arch-specific maps.
|
||||
local map_archdef = { sp="r29", ra="r31" } -- Ext. register name -> int. name.
|
||||
|
||||
local map_type = {} -- Type name -> { ctype, reg }
|
||||
local ctypenum = 0 -- Type number (for Dt... macros).
|
||||
|
||||
-- Reverse defines for registers.
|
||||
function _M.revdef(s)
|
||||
if s == "r29" then return "sp"
|
||||
elseif s == "r31" then return "ra" end
|
||||
return s
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Template strings for MIPS instructions.
|
||||
local map_op = {
|
||||
-- First-level opcodes.
|
||||
j_1 = "08000000J",
|
||||
jal_1 = "0c000000J",
|
||||
b_1 = "10000000B",
|
||||
beqz_2 = "10000000SB",
|
||||
beq_3 = "10000000STB",
|
||||
bnez_2 = "14000000SB",
|
||||
bne_3 = "14000000STB",
|
||||
blez_2 = "18000000SB",
|
||||
bgtz_2 = "1c000000SB",
|
||||
addi_3 = "20000000TSI",
|
||||
li_2 = "24000000TI",
|
||||
addiu_3 = "24000000TSI",
|
||||
slti_3 = "28000000TSI",
|
||||
sltiu_3 = "2c000000TSI",
|
||||
andi_3 = "30000000TSU",
|
||||
lu_2 = "34000000TU",
|
||||
ori_3 = "34000000TSU",
|
||||
xori_3 = "38000000TSU",
|
||||
lui_2 = "3c000000TU",
|
||||
beqzl_2 = "50000000SB",
|
||||
beql_3 = "50000000STB",
|
||||
bnezl_2 = "54000000SB",
|
||||
bnel_3 = "54000000STB",
|
||||
blezl_2 = "58000000SB",
|
||||
bgtzl_2 = "5c000000SB",
|
||||
lb_2 = "80000000TO",
|
||||
lh_2 = "84000000TO",
|
||||
lwl_2 = "88000000TO",
|
||||
lw_2 = "8c000000TO",
|
||||
lbu_2 = "90000000TO",
|
||||
lhu_2 = "94000000TO",
|
||||
lwr_2 = "98000000TO",
|
||||
sb_2 = "a0000000TO",
|
||||
sh_2 = "a4000000TO",
|
||||
swl_2 = "a8000000TO",
|
||||
sw_2 = "ac000000TO",
|
||||
swr_2 = "b8000000TO",
|
||||
cache_2 = "bc000000NO",
|
||||
ll_2 = "c0000000TO",
|
||||
lwc1_2 = "c4000000HO",
|
||||
pref_2 = "cc000000NO",
|
||||
ldc1_2 = "d4000000HO",
|
||||
sc_2 = "e0000000TO",
|
||||
swc1_2 = "e4000000HO",
|
||||
sdc1_2 = "f4000000HO",
|
||||
|
||||
-- Opcode SPECIAL.
|
||||
nop_0 = "00000000",
|
||||
sll_3 = "00000000DTA",
|
||||
movf_2 = "00000001DS",
|
||||
movf_3 = "00000001DSC",
|
||||
movt_2 = "00010001DS",
|
||||
movt_3 = "00010001DSC",
|
||||
srl_3 = "00000002DTA",
|
||||
rotr_3 = "00200002DTA",
|
||||
sra_3 = "00000003DTA",
|
||||
sllv_3 = "00000004DTS",
|
||||
srlv_3 = "00000006DTS",
|
||||
rotrv_3 = "00000046DTS",
|
||||
srav_3 = "00000007DTS",
|
||||
jr_1 = "00000008S",
|
||||
jalr_1 = "0000f809S",
|
||||
jalr_2 = "00000009DS",
|
||||
movz_3 = "0000000aDST",
|
||||
movn_3 = "0000000bDST",
|
||||
syscall_0 = "0000000c",
|
||||
syscall_1 = "0000000cY",
|
||||
break_0 = "0000000d",
|
||||
break_1 = "0000000dY",
|
||||
sync_0 = "0000000f",
|
||||
mfhi_1 = "00000010D",
|
||||
mthi_1 = "00000011S",
|
||||
mflo_1 = "00000012D",
|
||||
mtlo_1 = "00000013S",
|
||||
mult_2 = "00000018ST",
|
||||
multu_2 = "00000019ST",
|
||||
div_2 = "0000001aST",
|
||||
divu_2 = "0000001bST",
|
||||
add_3 = "00000020DST",
|
||||
move_2 = "00000021DS",
|
||||
addu_3 = "00000021DST",
|
||||
sub_3 = "00000022DST",
|
||||
negu_2 = "00000023DT",
|
||||
subu_3 = "00000023DST",
|
||||
and_3 = "00000024DST",
|
||||
or_3 = "00000025DST",
|
||||
xor_3 = "00000026DST",
|
||||
not_2 = "00000027DS",
|
||||
nor_3 = "00000027DST",
|
||||
slt_3 = "0000002aDST",
|
||||
sltu_3 = "0000002bDST",
|
||||
tge_2 = "00000030ST",
|
||||
tge_3 = "00000030STZ",
|
||||
tgeu_2 = "00000031ST",
|
||||
tgeu_3 = "00000031STZ",
|
||||
tlt_2 = "00000032ST",
|
||||
tlt_3 = "00000032STZ",
|
||||
tltu_2 = "00000033ST",
|
||||
tltu_3 = "00000033STZ",
|
||||
teq_2 = "00000034ST",
|
||||
teq_3 = "00000034STZ",
|
||||
tne_2 = "00000036ST",
|
||||
tne_3 = "00000036STZ",
|
||||
|
||||
-- Opcode REGIMM.
|
||||
bltz_2 = "04000000SB",
|
||||
bgez_2 = "04010000SB",
|
||||
bltzl_2 = "04020000SB",
|
||||
bgezl_2 = "04030000SB",
|
||||
tgei_2 = "04080000SI",
|
||||
tgeiu_2 = "04090000SI",
|
||||
tlti_2 = "040a0000SI",
|
||||
tltiu_2 = "040b0000SI",
|
||||
teqi_2 = "040c0000SI",
|
||||
tnei_2 = "040e0000SI",
|
||||
bltzal_2 = "04100000SB",
|
||||
bal_1 = "04110000B",
|
||||
bgezal_2 = "04110000SB",
|
||||
bltzall_2 = "04120000SB",
|
||||
bgezall_2 = "04130000SB",
|
||||
synci_1 = "041f0000O",
|
||||
|
||||
-- Opcode SPECIAL2.
|
||||
madd_2 = "70000000ST",
|
||||
maddu_2 = "70000001ST",
|
||||
mul_3 = "70000002DST",
|
||||
msub_2 = "70000004ST",
|
||||
msubu_2 = "70000005ST",
|
||||
clz_2 = "70000020DS=",
|
||||
clo_2 = "70000021DS=",
|
||||
sdbbp_0 = "7000003f",
|
||||
sdbbp_1 = "7000003fY",
|
||||
|
||||
-- Opcode SPECIAL3.
|
||||
ext_4 = "7c000000TSAM", -- Note: last arg is msbd = size-1
|
||||
ins_4 = "7c000004TSAM", -- Note: last arg is msb = pos+size-1
|
||||
wsbh_2 = "7c0000a0DT",
|
||||
seb_2 = "7c000420DT",
|
||||
seh_2 = "7c000620DT",
|
||||
rdhwr_2 = "7c00003bTD",
|
||||
|
||||
-- Opcode COP0.
|
||||
mfc0_2 = "40000000TD",
|
||||
mfc0_3 = "40000000TDW",
|
||||
mtc0_2 = "40800000TD",
|
||||
mtc0_3 = "40800000TDW",
|
||||
rdpgpr_2 = "41400000DT",
|
||||
di_0 = "41606000",
|
||||
di_1 = "41606000T",
|
||||
ei_0 = "41606020",
|
||||
ei_1 = "41606020T",
|
||||
wrpgpr_2 = "41c00000DT",
|
||||
tlbr_0 = "42000001",
|
||||
tlbwi_0 = "42000002",
|
||||
tlbwr_0 = "42000006",
|
||||
tlbp_0 = "42000008",
|
||||
eret_0 = "42000018",
|
||||
deret_0 = "4200001f",
|
||||
wait_0 = "42000020",
|
||||
|
||||
-- Opcode COP1.
|
||||
mfc1_2 = "44000000TG",
|
||||
cfc1_2 = "44400000TG",
|
||||
mfhc1_2 = "44600000TG",
|
||||
mtc1_2 = "44800000TG",
|
||||
ctc1_2 = "44c00000TG",
|
||||
mthc1_2 = "44e00000TG",
|
||||
|
||||
bc1f_1 = "45000000B",
|
||||
bc1f_2 = "45000000CB",
|
||||
bc1t_1 = "45010000B",
|
||||
bc1t_2 = "45010000CB",
|
||||
bc1fl_1 = "45020000B",
|
||||
bc1fl_2 = "45020000CB",
|
||||
bc1tl_1 = "45030000B",
|
||||
bc1tl_2 = "45030000CB",
|
||||
|
||||
["add.s_3"] = "46000000FGH",
|
||||
["sub.s_3"] = "46000001FGH",
|
||||
["mul.s_3"] = "46000002FGH",
|
||||
["div.s_3"] = "46000003FGH",
|
||||
["sqrt.s_2"] = "46000004FG",
|
||||
["abs.s_2"] = "46000005FG",
|
||||
["mov.s_2"] = "46000006FG",
|
||||
["neg.s_2"] = "46000007FG",
|
||||
["round.l.s_2"] = "46000008FG",
|
||||
["trunc.l.s_2"] = "46000009FG",
|
||||
["ceil.l.s_2"] = "4600000aFG",
|
||||
["floor.l.s_2"] = "4600000bFG",
|
||||
["round.w.s_2"] = "4600000cFG",
|
||||
["trunc.w.s_2"] = "4600000dFG",
|
||||
["ceil.w.s_2"] = "4600000eFG",
|
||||
["floor.w.s_2"] = "4600000fFG",
|
||||
["movf.s_2"] = "46000011FG",
|
||||
["movf.s_3"] = "46000011FGC",
|
||||
["movt.s_2"] = "46010011FG",
|
||||
["movt.s_3"] = "46010011FGC",
|
||||
["movz.s_3"] = "46000012FGT",
|
||||
["movn.s_3"] = "46000013FGT",
|
||||
["recip.s_2"] = "46000015FG",
|
||||
["rsqrt.s_2"] = "46000016FG",
|
||||
["cvt.d.s_2"] = "46000021FG",
|
||||
["cvt.w.s_2"] = "46000024FG",
|
||||
["cvt.l.s_2"] = "46000025FG",
|
||||
["cvt.ps.s_3"] = "46000026FGH",
|
||||
["c.f.s_2"] = "46000030GH",
|
||||
["c.f.s_3"] = "46000030VGH",
|
||||
["c.un.s_2"] = "46000031GH",
|
||||
["c.un.s_3"] = "46000031VGH",
|
||||
["c.eq.s_2"] = "46000032GH",
|
||||
["c.eq.s_3"] = "46000032VGH",
|
||||
["c.ueq.s_2"] = "46000033GH",
|
||||
["c.ueq.s_3"] = "46000033VGH",
|
||||
["c.olt.s_2"] = "46000034GH",
|
||||
["c.olt.s_3"] = "46000034VGH",
|
||||
["c.ult.s_2"] = "46000035GH",
|
||||
["c.ult.s_3"] = "46000035VGH",
|
||||
["c.ole.s_2"] = "46000036GH",
|
||||
["c.ole.s_3"] = "46000036VGH",
|
||||
["c.ule.s_2"] = "46000037GH",
|
||||
["c.ule.s_3"] = "46000037VGH",
|
||||
["c.sf.s_2"] = "46000038GH",
|
||||
["c.sf.s_3"] = "46000038VGH",
|
||||
["c.ngle.s_2"] = "46000039GH",
|
||||
["c.ngle.s_3"] = "46000039VGH",
|
||||
["c.seq.s_2"] = "4600003aGH",
|
||||
["c.seq.s_3"] = "4600003aVGH",
|
||||
["c.ngl.s_2"] = "4600003bGH",
|
||||
["c.ngl.s_3"] = "4600003bVGH",
|
||||
["c.lt.s_2"] = "4600003cGH",
|
||||
["c.lt.s_3"] = "4600003cVGH",
|
||||
["c.nge.s_2"] = "4600003dGH",
|
||||
["c.nge.s_3"] = "4600003dVGH",
|
||||
["c.le.s_2"] = "4600003eGH",
|
||||
["c.le.s_3"] = "4600003eVGH",
|
||||
["c.ngt.s_2"] = "4600003fGH",
|
||||
["c.ngt.s_3"] = "4600003fVGH",
|
||||
|
||||
["add.d_3"] = "46200000FGH",
|
||||
["sub.d_3"] = "46200001FGH",
|
||||
["mul.d_3"] = "46200002FGH",
|
||||
["div.d_3"] = "46200003FGH",
|
||||
["sqrt.d_2"] = "46200004FG",
|
||||
["abs.d_2"] = "46200005FG",
|
||||
["mov.d_2"] = "46200006FG",
|
||||
["neg.d_2"] = "46200007FG",
|
||||
["round.l.d_2"] = "46200008FG",
|
||||
["trunc.l.d_2"] = "46200009FG",
|
||||
["ceil.l.d_2"] = "4620000aFG",
|
||||
["floor.l.d_2"] = "4620000bFG",
|
||||
["round.w.d_2"] = "4620000cFG",
|
||||
["trunc.w.d_2"] = "4620000dFG",
|
||||
["ceil.w.d_2"] = "4620000eFG",
|
||||
["floor.w.d_2"] = "4620000fFG",
|
||||
["movf.d_2"] = "46200011FG",
|
||||
["movf.d_3"] = "46200011FGC",
|
||||
["movt.d_2"] = "46210011FG",
|
||||
["movt.d_3"] = "46210011FGC",
|
||||
["movz.d_3"] = "46200012FGT",
|
||||
["movn.d_3"] = "46200013FGT",
|
||||
["recip.d_2"] = "46200015FG",
|
||||
["rsqrt.d_2"] = "46200016FG",
|
||||
["cvt.s.d_2"] = "46200020FG",
|
||||
["cvt.w.d_2"] = "46200024FG",
|
||||
["cvt.l.d_2"] = "46200025FG",
|
||||
["c.f.d_2"] = "46200030GH",
|
||||
["c.f.d_3"] = "46200030VGH",
|
||||
["c.un.d_2"] = "46200031GH",
|
||||
["c.un.d_3"] = "46200031VGH",
|
||||
["c.eq.d_2"] = "46200032GH",
|
||||
["c.eq.d_3"] = "46200032VGH",
|
||||
["c.ueq.d_2"] = "46200033GH",
|
||||
["c.ueq.d_3"] = "46200033VGH",
|
||||
["c.olt.d_2"] = "46200034GH",
|
||||
["c.olt.d_3"] = "46200034VGH",
|
||||
["c.ult.d_2"] = "46200035GH",
|
||||
["c.ult.d_3"] = "46200035VGH",
|
||||
["c.ole.d_2"] = "46200036GH",
|
||||
["c.ole.d_3"] = "46200036VGH",
|
||||
["c.ule.d_2"] = "46200037GH",
|
||||
["c.ule.d_3"] = "46200037VGH",
|
||||
["c.sf.d_2"] = "46200038GH",
|
||||
["c.sf.d_3"] = "46200038VGH",
|
||||
["c.ngle.d_2"] = "46200039GH",
|
||||
["c.ngle.d_3"] = "46200039VGH",
|
||||
["c.seq.d_2"] = "4620003aGH",
|
||||
["c.seq.d_3"] = "4620003aVGH",
|
||||
["c.ngl.d_2"] = "4620003bGH",
|
||||
["c.ngl.d_3"] = "4620003bVGH",
|
||||
["c.lt.d_2"] = "4620003cGH",
|
||||
["c.lt.d_3"] = "4620003cVGH",
|
||||
["c.nge.d_2"] = "4620003dGH",
|
||||
["c.nge.d_3"] = "4620003dVGH",
|
||||
["c.le.d_2"] = "4620003eGH",
|
||||
["c.le.d_3"] = "4620003eVGH",
|
||||
["c.ngt.d_2"] = "4620003fGH",
|
||||
["c.ngt.d_3"] = "4620003fVGH",
|
||||
|
||||
["add.ps_3"] = "46c00000FGH",
|
||||
["sub.ps_3"] = "46c00001FGH",
|
||||
["mul.ps_3"] = "46c00002FGH",
|
||||
["abs.ps_2"] = "46c00005FG",
|
||||
["mov.ps_2"] = "46c00006FG",
|
||||
["neg.ps_2"] = "46c00007FG",
|
||||
["movf.ps_2"] = "46c00011FG",
|
||||
["movf.ps_3"] = "46c00011FGC",
|
||||
["movt.ps_2"] = "46c10011FG",
|
||||
["movt.ps_3"] = "46c10011FGC",
|
||||
["movz.ps_3"] = "46c00012FGT",
|
||||
["movn.ps_3"] = "46c00013FGT",
|
||||
["cvt.s.pu_2"] = "46c00020FG",
|
||||
["cvt.s.pl_2"] = "46c00028FG",
|
||||
["pll.ps_3"] = "46c0002cFGH",
|
||||
["plu.ps_3"] = "46c0002dFGH",
|
||||
["pul.ps_3"] = "46c0002eFGH",
|
||||
["puu.ps_3"] = "46c0002fFGH",
|
||||
["c.f.ps_2"] = "46c00030GH",
|
||||
["c.f.ps_3"] = "46c00030VGH",
|
||||
["c.un.ps_2"] = "46c00031GH",
|
||||
["c.un.ps_3"] = "46c00031VGH",
|
||||
["c.eq.ps_2"] = "46c00032GH",
|
||||
["c.eq.ps_3"] = "46c00032VGH",
|
||||
["c.ueq.ps_2"] = "46c00033GH",
|
||||
["c.ueq.ps_3"] = "46c00033VGH",
|
||||
["c.olt.ps_2"] = "46c00034GH",
|
||||
["c.olt.ps_3"] = "46c00034VGH",
|
||||
["c.ult.ps_2"] = "46c00035GH",
|
||||
["c.ult.ps_3"] = "46c00035VGH",
|
||||
["c.ole.ps_2"] = "46c00036GH",
|
||||
["c.ole.ps_3"] = "46c00036VGH",
|
||||
["c.ule.ps_2"] = "46c00037GH",
|
||||
["c.ule.ps_3"] = "46c00037VGH",
|
||||
["c.sf.ps_2"] = "46c00038GH",
|
||||
["c.sf.ps_3"] = "46c00038VGH",
|
||||
["c.ngle.ps_2"] = "46c00039GH",
|
||||
["c.ngle.ps_3"] = "46c00039VGH",
|
||||
["c.seq.ps_2"] = "46c0003aGH",
|
||||
["c.seq.ps_3"] = "46c0003aVGH",
|
||||
["c.ngl.ps_2"] = "46c0003bGH",
|
||||
["c.ngl.ps_3"] = "46c0003bVGH",
|
||||
["c.lt.ps_2"] = "46c0003cGH",
|
||||
["c.lt.ps_3"] = "46c0003cVGH",
|
||||
["c.nge.ps_2"] = "46c0003dGH",
|
||||
["c.nge.ps_3"] = "46c0003dVGH",
|
||||
["c.le.ps_2"] = "46c0003eGH",
|
||||
["c.le.ps_3"] = "46c0003eVGH",
|
||||
["c.ngt.ps_2"] = "46c0003fGH",
|
||||
["c.ngt.ps_3"] = "46c0003fVGH",
|
||||
|
||||
["cvt.s.w_2"] = "46800020FG",
|
||||
["cvt.d.w_2"] = "46800021FG",
|
||||
|
||||
["cvt.s.l_2"] = "46a00020FG",
|
||||
["cvt.d.l_2"] = "46a00021FG",
|
||||
|
||||
-- Opcode COP1X.
|
||||
lwxc1_2 = "4c000000FX",
|
||||
ldxc1_2 = "4c000001FX",
|
||||
luxc1_2 = "4c000005FX",
|
||||
swxc1_2 = "4c000008FX",
|
||||
sdxc1_2 = "4c000009FX",
|
||||
suxc1_2 = "4c00000dFX",
|
||||
prefx_2 = "4c00000fMX",
|
||||
["alnv.ps_4"] = "4c00001eFGHS",
|
||||
["madd.s_4"] = "4c000020FRGH",
|
||||
["madd.d_4"] = "4c000021FRGH",
|
||||
["madd.ps_4"] = "4c000026FRGH",
|
||||
["msub.s_4"] = "4c000028FRGH",
|
||||
["msub.d_4"] = "4c000029FRGH",
|
||||
["msub.ps_4"] = "4c00002eFRGH",
|
||||
["nmadd.s_4"] = "4c000030FRGH",
|
||||
["nmadd.d_4"] = "4c000031FRGH",
|
||||
["nmadd.ps_4"] = "4c000036FRGH",
|
||||
["nmsub.s_4"] = "4c000038FRGH",
|
||||
["nmsub.d_4"] = "4c000039FRGH",
|
||||
["nmsub.ps_4"] = "4c00003eFRGH",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function parse_gpr(expr)
|
||||
local tname, ovreg = match(expr, "^([%w_]+):(r[1-3]?[0-9])$")
|
||||
local tp = map_type[tname or expr]
|
||||
if tp then
|
||||
local reg = ovreg or tp.reg
|
||||
if not reg then
|
||||
werror("type `"..(tname or expr).."' needs a register override")
|
||||
end
|
||||
expr = reg
|
||||
end
|
||||
local r = match(expr, "^r([1-3]?[0-9])$")
|
||||
if r then
|
||||
r = tonumber(r)
|
||||
if r <= 31 then return r, tp end
|
||||
end
|
||||
werror("bad register name `"..expr.."'")
|
||||
end
|
||||
|
||||
local function parse_fpr(expr)
|
||||
local r = match(expr, "^f([1-3]?[0-9])$")
|
||||
if r then
|
||||
r = tonumber(r)
|
||||
if r <= 31 then return r end
|
||||
end
|
||||
werror("bad register name `"..expr.."'")
|
||||
end
|
||||
|
||||
local function parse_imm(imm, bits, shift, scale, signed)
|
||||
local n = tonumber(imm)
|
||||
if n then
|
||||
local m = sar(n, scale)
|
||||
if shl(m, scale) == n then
|
||||
if signed then
|
||||
local s = sar(m, bits-1)
|
||||
if s == 0 then return shl(m, shift)
|
||||
elseif s == -1 then return shl(m + shl(1, bits), shift) end
|
||||
else
|
||||
if sar(m, bits) == 0 then return shl(m, shift) end
|
||||
end
|
||||
end
|
||||
werror("out of range immediate `"..imm.."'")
|
||||
elseif match(imm, "^[rf]([1-3]?[0-9])$") or
|
||||
match(imm, "^([%w_]+):([rf][1-3]?[0-9])$") then
|
||||
werror("expected immediate operand, got register")
|
||||
else
|
||||
waction("IMM", (signed and 32768 or 0)+scale*1024+bits*32+shift, imm)
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function parse_disp(disp)
|
||||
local imm, reg = match(disp, "^(.*)%(([%w_:]+)%)$")
|
||||
if imm then
|
||||
local r = shl(parse_gpr(reg), 21)
|
||||
local extname = match(imm, "^extern%s+(%S+)$")
|
||||
if extname then
|
||||
waction("REL_EXT", map_extern[extname], nil, 1)
|
||||
return r
|
||||
else
|
||||
return r + parse_imm(imm, 16, 0, 0, true)
|
||||
end
|
||||
end
|
||||
local reg, tailr = match(disp, "^([%w_:]+)%s*(.*)$")
|
||||
if reg and tailr ~= "" then
|
||||
local r, tp = parse_gpr(reg)
|
||||
if tp then
|
||||
waction("IMM", 32768+16*32, format(tp.ctypefmt, tailr))
|
||||
return shl(r, 21)
|
||||
end
|
||||
end
|
||||
werror("bad displacement `"..disp.."'")
|
||||
end
|
||||
|
||||
local function parse_index(idx)
|
||||
local rt, rs = match(idx, "^(.*)%(([%w_:]+)%)$")
|
||||
if rt then
|
||||
rt = parse_gpr(rt)
|
||||
rs = parse_gpr(rs)
|
||||
return shl(rt, 16) + shl(rs, 21)
|
||||
end
|
||||
werror("bad index `"..idx.."'")
|
||||
end
|
||||
|
||||
local function parse_label(label, def)
|
||||
local prefix = sub(label, 1, 2)
|
||||
-- =>label (pc label reference)
|
||||
if prefix == "=>" then
|
||||
return "PC", 0, sub(label, 3)
|
||||
end
|
||||
-- ->name (global label reference)
|
||||
if prefix == "->" then
|
||||
return "LG", map_global[sub(label, 3)]
|
||||
end
|
||||
if def then
|
||||
-- [1-9] (local label definition)
|
||||
if match(label, "^[1-9]$") then
|
||||
return "LG", 10+tonumber(label)
|
||||
end
|
||||
else
|
||||
-- [<>][1-9] (local label reference)
|
||||
local dir, lnum = match(label, "^([<>])([1-9])$")
|
||||
if dir then -- Fwd: 1-9, Bkwd: 11-19.
|
||||
return "LG", lnum + (dir == ">" and 0 or 10)
|
||||
end
|
||||
-- extern label (extern label reference)
|
||||
local extname = match(label, "^extern%s+(%S+)$")
|
||||
if extname then
|
||||
return "EXT", map_extern[extname]
|
||||
end
|
||||
end
|
||||
werror("bad label `"..label.."'")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Handle opcodes defined with template strings.
|
||||
map_op[".template__"] = function(params, template, nparams)
|
||||
if not params then return sub(template, 9) end
|
||||
local op = tonumber(sub(template, 1, 8), 16)
|
||||
local n = 1
|
||||
|
||||
-- Limit number of section buffer positions used by a single dasm_put().
|
||||
-- A single opcode needs a maximum of 2 positions (ins/ext).
|
||||
if secpos+2 > maxsecpos then wflush() end
|
||||
local pos = wpos()
|
||||
|
||||
-- Process each character.
|
||||
for p in gmatch(sub(template, 9), ".") do
|
||||
if p == "D" then
|
||||
op = op + shl(parse_gpr(params[n]), 11); n = n + 1
|
||||
elseif p == "T" then
|
||||
op = op + shl(parse_gpr(params[n]), 16); n = n + 1
|
||||
elseif p == "S" then
|
||||
op = op + shl(parse_gpr(params[n]), 21); n = n + 1
|
||||
elseif p == "F" then
|
||||
op = op + shl(parse_fpr(params[n]), 6); n = n + 1
|
||||
elseif p == "G" then
|
||||
op = op + shl(parse_fpr(params[n]), 11); n = n + 1
|
||||
elseif p == "H" then
|
||||
op = op + shl(parse_fpr(params[n]), 16); n = n + 1
|
||||
elseif p == "R" then
|
||||
op = op + shl(parse_fpr(params[n]), 21); n = n + 1
|
||||
elseif p == "I" then
|
||||
op = op + parse_imm(params[n], 16, 0, 0, true); n = n + 1
|
||||
elseif p == "U" then
|
||||
op = op + parse_imm(params[n], 16, 0, 0, false); n = n + 1
|
||||
elseif p == "O" then
|
||||
op = op + parse_disp(params[n]); n = n + 1
|
||||
elseif p == "X" then
|
||||
op = op + parse_index(params[n]); n = n + 1
|
||||
elseif p == "B" or p == "J" then
|
||||
local mode, n, s = parse_label(params[n], false)
|
||||
if p == "B" then n = n + 2048 end
|
||||
waction("REL_"..mode, n, s, 1)
|
||||
n = n + 1
|
||||
elseif p == "A" then
|
||||
op = op + parse_imm(params[n], 5, 6, 0, false); n = n + 1
|
||||
elseif p == "M" then
|
||||
op = op + parse_imm(params[n], 5, 11, 0, false); n = n + 1
|
||||
elseif p == "N" then
|
||||
op = op + parse_imm(params[n], 5, 16, 0, false); n = n + 1
|
||||
elseif p == "C" then
|
||||
op = op + parse_imm(params[n], 3, 18, 0, false); n = n + 1
|
||||
elseif p == "V" then
|
||||
op = op + parse_imm(params[n], 3, 8, 0, false); n = n + 1
|
||||
elseif p == "W" then
|
||||
op = op + parse_imm(params[n], 3, 0, 0, false); n = n + 1
|
||||
elseif p == "Y" then
|
||||
op = op + parse_imm(params[n], 20, 6, 0, false); n = n + 1
|
||||
elseif p == "Z" then
|
||||
op = op + parse_imm(params[n], 10, 6, 0, false); n = n + 1
|
||||
elseif p == "=" then
|
||||
op = op + shl(band(op, 0xf800), 5) -- Copy D to T for clz, clo.
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
end
|
||||
wputpos(pos, op)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcode to mark the position where the action list is to be emitted.
|
||||
map_op[".actionlist_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeactions(out, name) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the global enum is to be emitted.
|
||||
map_op[".globals_1"] = function(params)
|
||||
if not params then return "prefix" end
|
||||
local prefix = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeglobals(out, prefix) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the global names are to be emitted.
|
||||
map_op[".globalnames_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeglobalnames(out, name) end)
|
||||
end
|
||||
|
||||
-- Pseudo-opcode to mark the position where the extern names are to be emitted.
|
||||
map_op[".externnames_1"] = function(params)
|
||||
if not params then return "cvar" end
|
||||
local name = params[1] -- No syntax check. You get to keep the pieces.
|
||||
wline(function(out) writeexternnames(out, name) end)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Label pseudo-opcode (converted from trailing colon form).
|
||||
map_op[".label_1"] = function(params)
|
||||
if not params then return "[1-9] | ->global | =>pcexpr" end
|
||||
if secpos+1 > maxsecpos then wflush() end
|
||||
local mode, n, s = parse_label(params[1], true)
|
||||
if mode == "EXT" then werror("bad label definition") end
|
||||
waction("LABEL_"..mode, n, s, 1)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcodes for data storage.
|
||||
map_op[".long_*"] = function(params)
|
||||
if not params then return "imm..." end
|
||||
for _,p in ipairs(params) do
|
||||
local n = tonumber(p)
|
||||
if not n then werror("bad immediate `"..p.."'") end
|
||||
if n < 0 then n = n + 2^32 end
|
||||
wputw(n)
|
||||
if secpos+2 > maxsecpos then wflush() end
|
||||
end
|
||||
end
|
||||
|
||||
-- Alignment pseudo-opcode.
|
||||
map_op[".align_1"] = function(params)
|
||||
if not params then return "numpow2" end
|
||||
if secpos+1 > maxsecpos then wflush() end
|
||||
local align = tonumber(params[1])
|
||||
if align then
|
||||
local x = align
|
||||
-- Must be a power of 2 in the range (2 ... 256).
|
||||
for i=1,8 do
|
||||
x = x / 2
|
||||
if x == 1 then
|
||||
waction("ALIGN", align-1, nil, 1) -- Action byte is 2**n-1.
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
werror("bad alignment")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pseudo-opcode for (primitive) type definitions (map to C types).
|
||||
map_op[".type_3"] = function(params, nparams)
|
||||
if not params then
|
||||
return nparams == 2 and "name, ctype" or "name, ctype, reg"
|
||||
end
|
||||
local name, ctype, reg = params[1], params[2], params[3]
|
||||
if not match(name, "^[%a_][%w_]*$") then
|
||||
werror("bad type name `"..name.."'")
|
||||
end
|
||||
local tp = map_type[name]
|
||||
if tp then
|
||||
werror("duplicate type `"..name.."'")
|
||||
end
|
||||
-- Add #type to defines. A bit unclean to put it in map_archdef.
|
||||
map_archdef["#"..name] = "sizeof("..ctype..")"
|
||||
-- Add new type and emit shortcut define.
|
||||
local num = ctypenum + 1
|
||||
map_type[name] = {
|
||||
ctype = ctype,
|
||||
ctypefmt = format("Dt%X(%%s)", num),
|
||||
reg = reg,
|
||||
}
|
||||
wline(format("#define Dt%X(_V) (int)(ptrdiff_t)&(((%s *)0)_V)", num, ctype))
|
||||
ctypenum = num
|
||||
end
|
||||
map_op[".type_2"] = map_op[".type_3"]
|
||||
|
||||
-- Dump type definitions.
|
||||
local function dumptypes(out, lvl)
|
||||
local t = {}
|
||||
for name in pairs(map_type) do t[#t+1] = name end
|
||||
sort(t)
|
||||
out:write("Type definitions:\n")
|
||||
for _,name in ipairs(t) do
|
||||
local tp = map_type[name]
|
||||
local reg = tp.reg or ""
|
||||
out:write(format(" %-20s %-20s %s\n", name, tp.ctype, reg))
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Set the current section.
|
||||
function _M.section(num)
|
||||
waction("SECTION", num)
|
||||
wflush(true) -- SECTION is a terminal action.
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump architecture description.
|
||||
function _M.dumparch(out)
|
||||
out:write(format("DynASM %s version %s, released %s\n\n",
|
||||
_info.arch, _info.version, _info.release))
|
||||
dumpactions(out)
|
||||
end
|
||||
|
||||
-- Dump all user defined elements.
|
||||
function _M.dumpdef(out, lvl)
|
||||
dumptypes(out, lvl)
|
||||
dumpglobals(out, lvl)
|
||||
dumpexterns(out, lvl)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Pass callbacks from/to the DynASM core.
|
||||
function _M.passcb(wl, we, wf, ww)
|
||||
wline, werror, wfatal, wwarn = wl, we, wf, ww
|
||||
return wflush
|
||||
end
|
||||
|
||||
-- Setup the arch-specific module.
|
||||
function _M.setup(arch, opt)
|
||||
g_arch, g_opt = arch, opt
|
||||
end
|
||||
|
||||
-- Merge the core maps and the arch-specific maps.
|
||||
function _M.mergemaps(map_coreop, map_def)
|
||||
setmetatable(map_op, { __index = map_coreop })
|
||||
setmetatable(map_def, { __index = map_archdef })
|
||||
return map_op, map_def
|
||||
end
|
||||
|
||||
return _M
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Vendored
+412
@@ -0,0 +1,412 @@
|
||||
/*
|
||||
** DynASM PPC encoding engine.
|
||||
** Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "ppc"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. */
|
||||
enum {
|
||||
DASM_STOP, DASM_SECTION, DASM_ESC, DASM_REL_EXT,
|
||||
/* The following actions need a buffer position. */
|
||||
DASM_ALIGN, DASM_REL_LG, DASM_LABEL_LG,
|
||||
/* The following actions also have an argument. */
|
||||
DASM_REL_PC, DASM_LABEL_PC, DASM_IMM,
|
||||
DASM__MAX
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_REL 0x15000000
|
||||
#define DASM_S_UNDEF_LG 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned int *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status = DASM_S_RANGE_##st|(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs;
|
||||
int *b;
|
||||
|
||||
if (pos >= sec->epos) {
|
||||
DASM_M_GROW(Dst, int, sec->buf, sec->bsize,
|
||||
sec->bsize + 2*DASM_MAXSECPOS*sizeof(int));
|
||||
sec->rbuf = sec->buf - DASM_POS2BIAS(pos);
|
||||
sec->epos = (int)sec->bsize/sizeof(int) - DASM_MAXSECPOS+DASM_POS2BIAS(pos);
|
||||
}
|
||||
|
||||
b = sec->rbuf;
|
||||
b[pos++] = start;
|
||||
|
||||
va_start(ap, start);
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
if (action >= DASM__MAX) {
|
||||
ofs += 4;
|
||||
} else {
|
||||
int *pl, n = action >= DASM_REL_PC ? va_arg(ap, int) : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: goto stop;
|
||||
case DASM_SECTION:
|
||||
n = (ins & 255); CK(n < D->maxsection, RANGE_SEC);
|
||||
D->section = &D->sections[n]; goto stop;
|
||||
case DASM_ESC: p++; ofs += 4; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs += (ins & 255); b[pos++] = ofs; break;
|
||||
case DASM_REL_LG:
|
||||
n = (ins & 2047) - 10; pl = D->lglabels + n;
|
||||
/* Bkwd rel or global. */
|
||||
if (n >= 0) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl += 10; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
pl = D->lglabels + (ins & 2047) - 10; CKPL(lg, LG); goto putlabel;
|
||||
case DASM_LABEL_PC:
|
||||
pl = D->pclabels + n; CKPL(pc, PC);
|
||||
putlabel:
|
||||
n = *pl; /* n > 0: Collapse rel chain and replace with label pos. */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = pos;
|
||||
}
|
||||
*pl = -pos; /* Label exists now. */
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
break;
|
||||
case DASM_IMM:
|
||||
#ifdef DASM_CHECKS
|
||||
CK((n & ((1<<((ins>>10)&31))-1)) == 0, RANGE_I);
|
||||
#endif
|
||||
n >>= ((ins>>10)&31);
|
||||
#ifdef DASM_CHECKS
|
||||
if (ins & 0x8000)
|
||||
CK(((n + (1<<(((ins>>5)&31)-1)))>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
else
|
||||
CK((n>>((ins>>5)&31)) == 0, RANGE_I);
|
||||
#endif
|
||||
b[pos++] = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop:
|
||||
va_end(ap);
|
||||
sec->pos = pos;
|
||||
sec->ofs = ofs;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Pass 2: Link sections, shrink aligns, fix label offsets. */
|
||||
int dasm_link(Dst_DECL, size_t *szp)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int secnum;
|
||||
int ofs = 0;
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
*szp = 0;
|
||||
if (D->status != DASM_S_OK) return D->status;
|
||||
{
|
||||
int pc;
|
||||
for (pc = 0; pc*sizeof(int) < D->pcsize; pc++)
|
||||
if (D->pclabels[pc] > 0) return DASM_S_UNDEF_PC|pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
{ /* Handle globals not defined in this translation unit. */
|
||||
int idx;
|
||||
for (idx = 20; idx*sizeof(int) < D->lgsize; idx++) {
|
||||
int n = D->lglabels[idx];
|
||||
/* Undefined label: Collapse rel chain and replace with marker (< 0). */
|
||||
while (n > 0) { int *pb = DASM_POS2PTR(D, n); n = *pb; *pb = -idx; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Combine all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->rbuf;
|
||||
int pos = DASM_SEC2POS(secnum);
|
||||
int lastpos = sec->pos;
|
||||
|
||||
while (pos != lastpos) {
|
||||
dasm_ActList p = D->actionlist + b[pos++];
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: p++; break;
|
||||
case DASM_REL_EXT: break;
|
||||
case DASM_ALIGN: ofs -= (b[pos++] + ofs) & (ins & 255); break;
|
||||
case DASM_REL_LG: case DASM_REL_PC: pos++; break;
|
||||
case DASM_LABEL_LG: case DASM_LABEL_PC: b[pos++] += ofs; break;
|
||||
case DASM_IMM: pos++; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
ofs += sec->ofs; /* Next section starts right after current section. */
|
||||
}
|
||||
|
||||
D->codesize = ofs; /* Total size of all code sections */
|
||||
*szp = ofs;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) return DASM_S_##st|(p-D->actionlist-1); } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 3: Encode sections. */
|
||||
int dasm_encode(Dst_DECL, void *buffer)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
char *base = (char *)buffer;
|
||||
unsigned int *cp = (unsigned int *)buffer;
|
||||
int secnum;
|
||||
|
||||
/* Encode all code sections. No support for data sections (yet). */
|
||||
for (secnum = 0; secnum < D->maxsection; secnum++) {
|
||||
dasm_Section *sec = D->sections + secnum;
|
||||
int *b = sec->buf;
|
||||
int *endb = sec->rbuf + sec->pos;
|
||||
|
||||
while (b != endb) {
|
||||
dasm_ActList p = D->actionlist + *b++;
|
||||
while (1) {
|
||||
unsigned int ins = *p++;
|
||||
unsigned int action = (ins >> 16);
|
||||
int n = (action >= DASM_ALIGN && action < DASM__MAX) ? *b++ : 0;
|
||||
switch (action) {
|
||||
case DASM_STOP: case DASM_SECTION: goto stop;
|
||||
case DASM_ESC: *cp++ = *p++; break;
|
||||
case DASM_REL_EXT:
|
||||
n = DASM_EXTERN(Dst, (unsigned char *)cp, (ins & 2047), 1) - 4;
|
||||
goto patchrel;
|
||||
case DASM_ALIGN:
|
||||
ins &= 255; while ((((char *)cp - base) & ins)) *cp++ = 0x60000000;
|
||||
break;
|
||||
case DASM_REL_LG:
|
||||
CK(n >= 0, UNDEF_LG);
|
||||
case DASM_REL_PC:
|
||||
CK(n >= 0, UNDEF_PC);
|
||||
n = *DASM_POS2PTR(D, n) - (int)((char *)cp - base);
|
||||
patchrel:
|
||||
CK((n & 3) == 0 &&
|
||||
(((n+4) + ((ins & 2048) ? 0x00008000 : 0x02000000)) >>
|
||||
((ins & 2048) ? 16 : 26)) == 0, RANGE_REL);
|
||||
cp[-1] |= ((n+4) & ((ins & 2048) ? 0x0000fffc: 0x03fffffc));
|
||||
break;
|
||||
case DASM_LABEL_LG:
|
||||
ins &= 2047; if (ins >= 20) D->globals[ins-10] = (void *)(base + n);
|
||||
break;
|
||||
case DASM_LABEL_PC: break;
|
||||
case DASM_IMM:
|
||||
cp[-1] |= (n & ((1<<((ins>>5)&31))-1)) << (ins&31);
|
||||
break;
|
||||
default: *cp++ = ins; break;
|
||||
}
|
||||
}
|
||||
stop: (void)0;
|
||||
}
|
||||
}
|
||||
|
||||
if (base + D->codesize != (char *)cp) /* Check for phase errors. */
|
||||
return DASM_S_PHASE;
|
||||
return DASM_S_OK;
|
||||
}
|
||||
#undef CK
|
||||
|
||||
/* Get PC label offset. */
|
||||
int dasm_getpclabel(Dst_DECL, unsigned int pc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (pc*sizeof(int) < D->pcsize) {
|
||||
int pos = D->pclabels[pc];
|
||||
if (pos < 0) return *DASM_POS2PTR(D, -pos);
|
||||
if (pos > 0) return -1; /* Undefined. */
|
||||
}
|
||||
return -2; /* Unused or out of range. */
|
||||
}
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
int dasm_checkstep(Dst_DECL, int secmatch)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
if (D->status == DASM_S_OK) {
|
||||
int i;
|
||||
for (i = 1; i <= 9; i++) {
|
||||
if (D->lglabels[i] > 0) { D->status = DASM_S_UNDEF_LG|i; break; }
|
||||
D->lglabels[i] = 0;
|
||||
}
|
||||
}
|
||||
if (D->status == DASM_S_OK && secmatch >= 0 &&
|
||||
D->section != &D->sections[secmatch])
|
||||
D->status = DASM_S_MATCH_SEC|(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+1249
File diff suppressed because it is too large
Load Diff
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
** DynASM encoding engine prototypes.
|
||||
** Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#ifndef _DASM_PROTO_H
|
||||
#define _DASM_PROTO_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#define DASM_IDENT "DynASM 1.3.0"
|
||||
#define DASM_VERSION 10300 /* 1.3.0 */
|
||||
|
||||
#ifndef Dst_DECL
|
||||
#define Dst_DECL dasm_State **Dst
|
||||
#endif
|
||||
|
||||
#ifndef Dst_REF
|
||||
#define Dst_REF (*Dst)
|
||||
#endif
|
||||
|
||||
#ifndef DASM_FDEF
|
||||
#define DASM_FDEF extern
|
||||
#endif
|
||||
|
||||
#ifndef DASM_M_GROW
|
||||
#define DASM_M_GROW(ctx, t, p, sz, need) \
|
||||
do { \
|
||||
size_t _sz = (sz), _need = (need); \
|
||||
if (_sz < _need) { \
|
||||
if (_sz < 16) _sz = 16; \
|
||||
while (_sz < _need) _sz += _sz; \
|
||||
(p) = (t *)realloc((p), _sz); \
|
||||
if ((p) == NULL) exit(1); \
|
||||
(sz) = _sz; \
|
||||
} \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef DASM_M_FREE
|
||||
#define DASM_M_FREE(ctx, p, sz) free(p)
|
||||
#endif
|
||||
|
||||
/* Internal DynASM encoder state. */
|
||||
typedef struct dasm_State dasm_State;
|
||||
|
||||
|
||||
/* Initialize and free DynASM state. */
|
||||
DASM_FDEF void dasm_init(Dst_DECL, int maxsection);
|
||||
DASM_FDEF void dasm_free(Dst_DECL);
|
||||
|
||||
/* Setup global array. Must be called before dasm_setup(). */
|
||||
DASM_FDEF void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl);
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
DASM_FDEF void dasm_growpc(Dst_DECL, unsigned int maxpc);
|
||||
|
||||
/* Setup encoder. */
|
||||
DASM_FDEF void dasm_setup(Dst_DECL, const void *actionlist);
|
||||
|
||||
/* Feed encoder with actions. Calls are generated by pre-processor. */
|
||||
DASM_FDEF void dasm_put(Dst_DECL, int start, ...);
|
||||
|
||||
/* Link sections and return the resulting size. */
|
||||
DASM_FDEF int dasm_link(Dst_DECL, size_t *szp);
|
||||
|
||||
/* Encode sections into buffer. */
|
||||
DASM_FDEF int dasm_encode(Dst_DECL, void *buffer);
|
||||
|
||||
/* Get PC label offset. */
|
||||
DASM_FDEF int dasm_getpclabel(Dst_DECL, unsigned int pc);
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
/* Optional sanity checker to call between isolated encoding steps. */
|
||||
DASM_FDEF int dasm_checkstep(Dst_DECL, int secmatch);
|
||||
#else
|
||||
#define dasm_checkstep(a, b) 0
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _DASM_PROTO_H */
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
------------------------------------------------------------------------------
|
||||
-- DynASM x64 module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- See dynasm.lua for full copyright notice.
|
||||
------------------------------------------------------------------------------
|
||||
-- This module just sets 64 bit mode for the combined x86/x64 module.
|
||||
-- All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
x64 = true -- Using a global is an ugly, but effective solution.
|
||||
return require("dasm_x86")
|
||||
Vendored
+471
@@ -0,0 +1,471 @@
|
||||
/*
|
||||
** DynASM x86 encoding engine.
|
||||
** Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
** Released under the MIT license. See dynasm.lua for full copyright notice.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define DASM_ARCH "x86"
|
||||
|
||||
#ifndef DASM_EXTERN
|
||||
#define DASM_EXTERN(a,b,c,d) 0
|
||||
#endif
|
||||
|
||||
/* Action definitions. DASM_STOP must be 255. */
|
||||
enum {
|
||||
DASM_DISP = 233,
|
||||
DASM_IMM_S, DASM_IMM_B, DASM_IMM_W, DASM_IMM_D, DASM_IMM_WB, DASM_IMM_DB,
|
||||
DASM_VREG, DASM_SPACE, DASM_SETLABEL, DASM_REL_A, DASM_REL_LG, DASM_REL_PC,
|
||||
DASM_IMM_LG, DASM_IMM_PC, DASM_LABEL_LG, DASM_LABEL_PC, DASM_ALIGN,
|
||||
DASM_EXTERN, DASM_ESC, DASM_MARK, DASM_SECTION, DASM_STOP
|
||||
};
|
||||
|
||||
/* Maximum number of section buffer positions for a single dasm_put() call. */
|
||||
#define DASM_MAXSECPOS 25
|
||||
|
||||
/* DynASM encoder status codes. Action list offset or number are or'ed in. */
|
||||
#define DASM_S_OK 0x00000000
|
||||
#define DASM_S_NOMEM 0x01000000
|
||||
#define DASM_S_PHASE 0x02000000
|
||||
#define DASM_S_MATCH_SEC 0x03000000
|
||||
#define DASM_S_RANGE_I 0x11000000
|
||||
#define DASM_S_RANGE_SEC 0x12000000
|
||||
#define DASM_S_RANGE_LG 0x13000000
|
||||
#define DASM_S_RANGE_PC 0x14000000
|
||||
#define DASM_S_RANGE_VREG 0x15000000
|
||||
#define DASM_S_UNDEF_L 0x21000000
|
||||
#define DASM_S_UNDEF_PC 0x22000000
|
||||
|
||||
/* Macros to convert positions (8 bit section + 24 bit index). */
|
||||
#define DASM_POS2IDX(pos) ((pos)&0x00ffffff)
|
||||
#define DASM_POS2BIAS(pos) ((pos)&0xff000000)
|
||||
#define DASM_SEC2POS(sec) ((sec)<<24)
|
||||
#define DASM_POS2SEC(pos) ((pos)>>24)
|
||||
#define DASM_POS2PTR(D, pos) (D->sections[DASM_POS2SEC(pos)].rbuf + (pos))
|
||||
|
||||
/* Action list type. */
|
||||
typedef const unsigned char *dasm_ActList;
|
||||
|
||||
/* Per-section structure. */
|
||||
typedef struct dasm_Section {
|
||||
int *rbuf; /* Biased buffer pointer (negative section bias). */
|
||||
int *buf; /* True buffer pointer. */
|
||||
size_t bsize; /* Buffer size in bytes. */
|
||||
int pos; /* Biased buffer position. */
|
||||
int epos; /* End of biased buffer position - max single put. */
|
||||
int ofs; /* Byte offset into section. */
|
||||
} dasm_Section;
|
||||
|
||||
/* Core structure holding the DynASM encoding state. */
|
||||
struct dasm_State {
|
||||
size_t psize; /* Allocated size of this structure. */
|
||||
dasm_ActList actionlist; /* Current actionlist pointer. */
|
||||
int *lglabels; /* Local/global chain/pos ptrs. */
|
||||
size_t lgsize;
|
||||
int *pclabels; /* PC label chains/pos ptrs. */
|
||||
size_t pcsize;
|
||||
void **globals; /* Array of globals (bias -10). */
|
||||
dasm_Section *section; /* Pointer to active section. */
|
||||
size_t codesize; /* Total size of all code sections. */
|
||||
int maxsection; /* 0 <= sectionidx < maxsection. */
|
||||
int status; /* Status code. */
|
||||
dasm_Section sections[1]; /* All sections. Alloc-extended. */
|
||||
};
|
||||
|
||||
/* The size of the core structure depends on the max. number of sections. */
|
||||
#define DASM_PSZ(ms) (sizeof(dasm_State)+(ms-1)*sizeof(dasm_Section))
|
||||
|
||||
|
||||
/* Initialize DynASM state. */
|
||||
void dasm_init(Dst_DECL, int maxsection)
|
||||
{
|
||||
dasm_State *D;
|
||||
size_t psz = 0;
|
||||
int i;
|
||||
Dst_REF = NULL;
|
||||
DASM_M_GROW(Dst, struct dasm_State, Dst_REF, psz, DASM_PSZ(maxsection));
|
||||
D = Dst_REF;
|
||||
D->psize = psz;
|
||||
D->lglabels = NULL;
|
||||
D->lgsize = 0;
|
||||
D->pclabels = NULL;
|
||||
D->pcsize = 0;
|
||||
D->globals = NULL;
|
||||
D->maxsection = maxsection;
|
||||
for (i = 0; i < maxsection; i++) {
|
||||
D->sections[i].buf = NULL; /* Need this for pass3. */
|
||||
D->sections[i].rbuf = D->sections[i].buf - DASM_SEC2POS(i);
|
||||
D->sections[i].bsize = 0;
|
||||
D->sections[i].epos = 0; /* Wrong, but is recalculated after resize. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Free DynASM state. */
|
||||
void dasm_free(Dst_DECL)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
for (i = 0; i < D->maxsection; i++)
|
||||
if (D->sections[i].buf)
|
||||
DASM_M_FREE(Dst, D->sections[i].buf, D->sections[i].bsize);
|
||||
if (D->pclabels) DASM_M_FREE(Dst, D->pclabels, D->pcsize);
|
||||
if (D->lglabels) DASM_M_FREE(Dst, D->lglabels, D->lgsize);
|
||||
DASM_M_FREE(Dst, D, D->psize);
|
||||
}
|
||||
|
||||
/* Setup global label array. Must be called before dasm_setup(). */
|
||||
void dasm_setupglobal(Dst_DECL, void **gl, unsigned int maxgl)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
D->globals = gl - 10; /* Negative bias to compensate for locals. */
|
||||
DASM_M_GROW(Dst, int, D->lglabels, D->lgsize, (10+maxgl)*sizeof(int));
|
||||
}
|
||||
|
||||
/* Grow PC label array. Can be called after dasm_setup(), too. */
|
||||
void dasm_growpc(Dst_DECL, unsigned int maxpc)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
size_t osz = D->pcsize;
|
||||
DASM_M_GROW(Dst, int, D->pclabels, D->pcsize, maxpc*sizeof(int));
|
||||
memset((void *)(((unsigned char *)D->pclabels)+osz), 0, D->pcsize-osz);
|
||||
}
|
||||
|
||||
/* Setup encoder. */
|
||||
void dasm_setup(Dst_DECL, const void *actionlist)
|
||||
{
|
||||
dasm_State *D = Dst_REF;
|
||||
int i;
|
||||
D->actionlist = (dasm_ActList)actionlist;
|
||||
D->status = DASM_S_OK;
|
||||
D->section = &D->sections[0];
|
||||
memset((void *)D->lglabels, 0, D->lgsize);
|
||||
if (D->pclabels) memset((void *)D->pclabels, 0, D->pcsize);
|
||||
for (i = 0; i < D->maxsection; i++) {
|
||||
D->sections[i].pos = DASM_SEC2POS(i);
|
||||
D->sections[i].ofs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef DASM_CHECKS
|
||||
#define CK(x, st) \
|
||||
do { if (!(x)) { \
|
||||
D->status = DASM_S_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#define CKPL(kind, st) \
|
||||
do { if ((size_t)((char *)pl-(char *)D->kind##labels) >= D->kind##size) { \
|
||||
D->status=DASM_S_RANGE_##st|(int)(p-D->actionlist-1); return; } } while (0)
|
||||
#else
|
||||
#define CK(x, st) ((void)0)
|
||||
#define CKPL(kind, st) ((void)0)
|
||||
#endif
|
||||
|
||||
/* Pass 1: Store actions and args, link branches/labels, estimate offsets. */
|
||||
void dasm_put(Dst_DECL, int start, ...)
|
||||
{
|
||||
va_list ap;
|
||||
dasm_State *D = Dst_REF;
|
||||
dasm_ActList p = D->actionlist + start;
|
||||
dasm_Section *sec = D->section;
|
||||
int pos = sec->pos, ofs = sec->ofs, mrm = 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;
|
||||
/* Bkwd rel or global. */
|
||||
if (n <= 246) { CK(n>=10||*pl<0, RANGE_LG); CKPL(lg, LG); goto putrel; }
|
||||
pl -= 246; n = *pl;
|
||||
if (n < 0) n = 0; /* Start new chain for fwd rel if label exists. */
|
||||
goto linkrel;
|
||||
case DASM_REL_PC:
|
||||
case DASM_IMM_PC: pl = D->pclabels + va_arg(ap, int); CKPL(pc, PC);
|
||||
putrel:
|
||||
n = *pl;
|
||||
if (n < 0) { /* Label exists. Get label pos and store it. */
|
||||
b[pos] = -n;
|
||||
} else {
|
||||
linkrel:
|
||||
b[pos] = n; /* Else link to rel chain, anchored at label. */
|
||||
*pl = pos;
|
||||
}
|
||||
pos++;
|
||||
ofs += 4; /* Maximum offset needed. */
|
||||
if (action == DASM_REL_LG || action == DASM_REL_PC)
|
||||
b[pos++] = ofs; /* Store pass1 offset estimate. */
|
||||
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|(int)(D->section-D->sections);
|
||||
return D->status;
|
||||
}
|
||||
#endif
|
||||
|
||||
Vendored
+1931
File diff suppressed because it is too large
Load Diff
Vendored
+1095
File diff suppressed because it is too large
Load Diff
Vendored
+88
@@ -0,0 +1,88 @@
|
||||
.TH luajit 1 "" "" "LuaJIT documentation"
|
||||
.SH NAME
|
||||
luajit \- Just-In-Time Compiler for the Lua Language
|
||||
\fB
|
||||
.SH SYNOPSIS
|
||||
.B luajit
|
||||
[\fIoptions\fR]... [\fIscript\fR [\fIargs\fR]...]
|
||||
.SH "WEB SITE"
|
||||
.IR http://luajit.org
|
||||
.SH DESCRIPTION
|
||||
.PP
|
||||
This is the command-line program to run Lua programs with \fBLuaJIT\fR.
|
||||
.PP
|
||||
\fBLuaJIT\fR is a just-in-time (JIT) compiler for the Lua language.
|
||||
The virtual machine (VM) is based on a fast interpreter combined with
|
||||
a trace compiler. It can significantly improve the performance of Lua programs.
|
||||
.PP
|
||||
\fBLuaJIT\fR is API\- and ABI-compatible with the VM of the standard
|
||||
Lua\ 5.1 interpreter. When embedding the VM into an application,
|
||||
the built library can be used as a drop-in replacement.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI "\-e " chunk
|
||||
Run the given chunk of Lua code.
|
||||
.TP
|
||||
.BI "\-l " library
|
||||
Load the named library, just like \fBrequire("\fR\fIlibrary\fR\fB")\fR.
|
||||
.TP
|
||||
.BI "\-b " ...
|
||||
Save or list bytecode. Run without arguments to get help on options.
|
||||
.TP
|
||||
.BI "\-j " command
|
||||
Perform LuaJIT control command (optional space after \fB\-j\fR).
|
||||
.TP
|
||||
.BI "\-O" [opt]
|
||||
Control LuaJIT optimizations.
|
||||
.TP
|
||||
.B "\-i"
|
||||
Run in interactive mode.
|
||||
.TP
|
||||
.B "\-v"
|
||||
Show \fBLuaJIT\fR version.
|
||||
.TP
|
||||
.B "\-E"
|
||||
Ignore environment variables.
|
||||
.TP
|
||||
.B "\-\-"
|
||||
Stop processing options.
|
||||
.TP
|
||||
.B "\-"
|
||||
Read script from stdin instead.
|
||||
.PP
|
||||
After all options are processed, the given \fIscript\fR is run.
|
||||
The arguments are passed in the global \fIarg\fR table.
|
||||
.PP
|
||||
Interactive mode is only entered, if no \fIscript\fR and no \fB\-e\fR
|
||||
option is given. Interactive mode can be left with EOF (\fICtrl\-Z\fB).
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
luajit hello.lua world
|
||||
|
||||
Prints "Hello world", assuming \fIhello.lua\fR contains:
|
||||
.br
|
||||
print("Hello", arg[1])
|
||||
.TP
|
||||
luajit \-e "local x=0; for i=1,1e9 do x=x+i end; print(x)"
|
||||
|
||||
Calculates the sum of the numbers from 1 to 1000000000.
|
||||
.br
|
||||
And finishes in a reasonable amount of time, too.
|
||||
.TP
|
||||
luajit \-jv \-e "for i=1,10 do for j=1,10 do for k=1,100 do end end end"
|
||||
|
||||
Runs some nested loops and shows the resulting traces.
|
||||
.SH COPYRIGHT
|
||||
.PP
|
||||
\fBLuaJIT\fR is Copyright \(co 2005-2013 Mike Pall.
|
||||
.br
|
||||
\fBLuaJIT\fR is open source software, released under the MIT license.
|
||||
.SH SEE ALSO
|
||||
.PP
|
||||
More details in the provided HTML docs or at:
|
||||
.IR http://luajit.org
|
||||
.br
|
||||
More about the Lua language can be found at:
|
||||
.IR http://lua.org/docs.html
|
||||
.PP
|
||||
lua(1)
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
# Package information for LuaJIT to be used by pkg-config.
|
||||
majver=2
|
||||
minver=0
|
||||
relver=2
|
||||
version=${majver}.${minver}.${relver}
|
||||
abiver=5.1
|
||||
|
||||
prefix=/usr/local
|
||||
exec_prefix=${prefix}
|
||||
libdir=${exec_prefix}/lib
|
||||
libname=luajit-${abiver}
|
||||
includedir=${prefix}/include/luajit-${majver}.${minver}
|
||||
|
||||
INSTALL_LMOD=${prefix}/share/lua/${abiver}
|
||||
INSTALL_CMOD=${prefix}/lib/lua/${abiver}
|
||||
|
||||
Name: LuaJIT
|
||||
Description: Just-in-time compiler for Lua
|
||||
URL: http://luajit.org
|
||||
Version: ${version}
|
||||
Requires:
|
||||
Libs: -L${libdir} -l${libname}
|
||||
Libs.private: -Wl,-E -lm -ldl
|
||||
Cflags: -I${includedir}
|
||||
Vendored
+677
@@ -0,0 +1,677 @@
|
||||
##############################################################################
|
||||
# LuaJIT Makefile. Requires GNU Make.
|
||||
#
|
||||
# Please read doc/install.html before changing any variables!
|
||||
#
|
||||
# Suitable for POSIX platforms (Linux, *BSD, OSX etc.).
|
||||
# Also works with MinGW and Cygwin on Windows.
|
||||
# Please check msvcbuild.bat for building with MSVC on Windows.
|
||||
#
|
||||
# Copyright (C) 2005-2012 Mike Pall. See Copyright Notice in luajit.h
|
||||
##############################################################################
|
||||
|
||||
MAJVER= 2
|
||||
MINVER= 0
|
||||
RELVER= 2
|
||||
ABIVER= 5.1
|
||||
NODOTABIVER= 51
|
||||
|
||||
##############################################################################
|
||||
############################# COMPILER OPTIONS #############################
|
||||
##############################################################################
|
||||
# These options mainly affect the speed of the JIT compiler itself, not the
|
||||
# speed of the JIT-compiled code. Turn any of the optional settings on by
|
||||
# removing the '#' in front of them. Make sure you force a full recompile
|
||||
# with "make clean", followed by "make" if you change any options.
|
||||
#
|
||||
# LuaJIT builds as a native 32 or 64 bit binary by default.
|
||||
CC= gcc
|
||||
#
|
||||
# Use this if you want to force a 32 bit build on a 64 bit multilib OS.
|
||||
#CC= gcc -m32
|
||||
#
|
||||
# Since the assembler part does NOT maintain a frame pointer, it's pointless
|
||||
# to slow down the C part by not omitting it. Debugging, tracebacks and
|
||||
# unwinding are not affected -- the assembler part has frame unwind
|
||||
# information and GCC emits it where needed (x64) or with -g (see CCDEBUG).
|
||||
CCOPT= -O2 -fomit-frame-pointer
|
||||
# Use this if you want to generate a smaller binary (but it's slower):
|
||||
#CCOPT= -Os -fomit-frame-pointer
|
||||
# Note: it's no longer recommended to use -O3 with GCC 4.x.
|
||||
# The I-Cache bloat usually outweighs the benefits from aggressive inlining.
|
||||
#
|
||||
# Target-specific compiler options:
|
||||
#
|
||||
# x86 only: it's recommended to compile at least for i686. Better yet,
|
||||
# compile for an architecture that has SSE2, too (-msse -msse2).
|
||||
#
|
||||
# x86/x64 only: For GCC 4.2 or higher and if you don't intend to distribute
|
||||
# the binaries to a different machine you could also use: -march=native
|
||||
#
|
||||
CCOPT_x86= -march=i686
|
||||
CCOPT_x64=
|
||||
CCOPT_arm=
|
||||
CCOPT_ppc=
|
||||
CCOPT_ppcspe=
|
||||
CCOPT_mips=
|
||||
#
|
||||
CCDEBUG=
|
||||
# Uncomment the next line to generate debug information:
|
||||
#CCDEBUG= -g
|
||||
#
|
||||
CCWARN= -Wall
|
||||
# Uncomment the next line to enable more warnings:
|
||||
#CCWARN+= -Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
################################ BUILD MODE ################################
|
||||
##############################################################################
|
||||
# The default build mode is mixed mode on POSIX. On Windows this is the same
|
||||
# as dynamic mode.
|
||||
#
|
||||
# Mixed mode creates a static + dynamic library and a statically linked luajit.
|
||||
BUILDMODE= mixed
|
||||
#
|
||||
# Static mode creates a static library and a statically linked luajit.
|
||||
#BUILDMODE= static
|
||||
#
|
||||
# Dynamic mode creates a dynamic library and a dynamically linked luajit.
|
||||
# Note: this executable will only run when the library is installed!
|
||||
#BUILDMODE= dynamic
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
################################# FEATURES #################################
|
||||
##############################################################################
|
||||
# Enable/disable these features as needed, but make sure you force a full
|
||||
# recompile with "make clean", followed by "make".
|
||||
XCFLAGS=
|
||||
#
|
||||
# Permanently disable the FFI extension to reduce the size of the LuaJIT
|
||||
# executable. But please consider that the FFI library is compiled-in,
|
||||
# but NOT loaded by default. It only allocates any memory, if you actually
|
||||
# make use of it.
|
||||
#XCFLAGS+= -DLUAJIT_DISABLE_FFI
|
||||
#
|
||||
# Features from Lua 5.2 that are unlikely to break existing code are
|
||||
# enabled by default. Some other features that *might* break some existing
|
||||
# code (e.g. __pairs or os.execute() return values) can be enabled here.
|
||||
# Note: this does not provide full compatibility with Lua 5.2 at this time.
|
||||
#XCFLAGS+= -DLUAJIT_ENABLE_LUA52COMPAT
|
||||
#
|
||||
# Disable the JIT compiler, i.e. turn LuaJIT into a pure interpreter.
|
||||
#XCFLAGS+= -DLUAJIT_DISABLE_JIT
|
||||
#
|
||||
# Some architectures (e.g. PPC) can use either single-number (1) or
|
||||
# dual-number (2) mode. Uncomment one of these lines to override the
|
||||
# default mode. Please see LJ_ARCH_NUMMODE in lj_arch.h for details.
|
||||
#XCFLAGS+= -DLUAJIT_NUMMODE=1
|
||||
#XCFLAGS+= -DLUAJIT_NUMMODE=2
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
############################ DEBUGGING SUPPORT #############################
|
||||
##############################################################################
|
||||
# Enable these options as needed, but make sure you force a full recompile
|
||||
# with "make clean", followed by "make".
|
||||
# Note that most of these are NOT suitable for benchmarking or release mode!
|
||||
#
|
||||
# Use the system provided memory allocator (realloc) instead of the
|
||||
# bundled memory allocator. This is slower, but sometimes helpful for
|
||||
# debugging. It's helpful for Valgrind's memcheck tool, too. This option
|
||||
# cannot be enabled on x64, since the built-in allocator is mandatory.
|
||||
#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.
|
||||
# Use --suppressions=lj.supp to avoid some false positives.
|
||||
#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!
|
||||
##############################################################################
|
||||
|
||||
##############################################################################
|
||||
# Flags and options for host and target.
|
||||
##############################################################################
|
||||
|
||||
# You can override the following variables at the make command line:
|
||||
# CC HOST_CC STATIC_CC DYNAMIC_CC
|
||||
# CFLAGS HOST_CFLAGS TARGET_CFLAGS
|
||||
# LDFLAGS HOST_LDFLAGS TARGET_LDFLAGS TARGET_SHLDFLAGS
|
||||
# LIBS HOST_LIBS TARGET_LIBS
|
||||
# CROSS HOST_SYS TARGET_SYS TARGET_FLAGS
|
||||
#
|
||||
# Cross-compilation examples:
|
||||
# make HOST_CC="gcc -m32" CROSS=i586-mingw32msvc- TARGET_SYS=Windows
|
||||
# make HOST_CC="gcc -m32" CROSS=powerpc-linux-gnu-
|
||||
|
||||
CCOPTIONS= $(CCDEBUG) $(CCOPT) $(CCWARN) $(XCFLAGS) $(CFLAGS)
|
||||
LDOPTIONS= $(CCDEBUG) $(LDFLAGS)
|
||||
|
||||
HOST_CC= $(CC)
|
||||
HOST_RM= rm -f
|
||||
# If left blank, minilua is built and used. You can supply an installed
|
||||
# copy of (plain) Lua 5.1 or 5.2, plus Lua BitOp. E.g. with: HOST_LUA=lua
|
||||
HOST_LUA=
|
||||
|
||||
HOST_XCFLAGS= -I.
|
||||
HOST_XLDFLAGS=
|
||||
HOST_XLIBS=
|
||||
HOST_ACFLAGS= $(CCOPTIONS) $(HOST_XCFLAGS) $(TARGET_ARCH) $(HOST_CFLAGS)
|
||||
HOST_ALDFLAGS= $(LDOPTIONS) $(HOST_XLDFLAGS) $(HOST_LDFLAGS)
|
||||
HOST_ALIBS= $(HOST_XLIBS) $(LIBS) $(HOST_LIBS)
|
||||
|
||||
STATIC_CC = $(CROSS)$(CC)
|
||||
DYNAMIC_CC = $(CROSS)$(CC) -fPIC
|
||||
TARGET_CC= $(STATIC_CC)
|
||||
TARGET_STCC= $(STATIC_CC)
|
||||
TARGET_DYNCC= $(DYNAMIC_CC)
|
||||
TARGET_LD= $(CROSS)$(CC)
|
||||
TARGET_AR= $(CROSS)ar rcus
|
||||
TARGET_STRIP= $(CROSS)strip
|
||||
|
||||
TARGET_SONAME= libluajit-$(ABIVER).so.$(MAJVER)
|
||||
TARGET_DYLIBNAME= libluajit-$(ABIVER).$(MAJVER).dylib
|
||||
TARGET_DYLIBPATH= $(or $(PREFIX),/usr/local)/lib/$(TARGET_DYLIBNAME)
|
||||
TARGET_DLLNAME= lua$(NODOTABIVER).dll
|
||||
TARGET_XSHLDFLAGS= -shared -fPIC -Wl,-soname,$(TARGET_SONAME)
|
||||
TARGET_DYNXLDOPTS=
|
||||
|
||||
TARGET_LFSFLAGS= -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE
|
||||
TARGET_XCFLAGS= $(TARGET_LFSFLAGS) -U_FORTIFY_SOURCE
|
||||
TARGET_XLDFLAGS=
|
||||
TARGET_XLIBS= -lm
|
||||
TARGET_TCFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS)
|
||||
TARGET_ACFLAGS= $(CCOPTIONS) $(TARGET_XCFLAGS) $(TARGET_FLAGS) $(TARGET_CFLAGS)
|
||||
TARGET_ALDFLAGS= $(LDOPTIONS) $(TARGET_XLDFLAGS) $(TARGET_FLAGS) $(TARGET_LDFLAGS)
|
||||
TARGET_ASHLDFLAGS= $(LDOPTIONS) $(TARGET_XSHLDFLAGS) $(TARGET_FLAGS) $(TARGET_SHLDFLAGS)
|
||||
TARGET_ALIBS= $(TARGET_XLIBS) $(LIBS) $(TARGET_LIBS)
|
||||
|
||||
TARGET_TESTARCH=$(shell $(TARGET_CC) $(TARGET_TCFLAGS) -E lj_arch.h -dM)
|
||||
ifneq (,$(findstring LJ_TARGET_X64 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x64
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_X86 ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= x86
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_ARM ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= arm
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_PPC ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= ppc
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_PPCSPE ,$(TARGET_TESTARCH)))
|
||||
TARGET_LJARCH= ppcspe
|
||||
else
|
||||
ifneq (,$(findstring LJ_TARGET_MIPS ,$(TARGET_TESTARCH)))
|
||||
ifneq (,$(findstring MIPSEL ,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH= -D__MIPSEL__=1
|
||||
endif
|
||||
TARGET_LJARCH= mips
|
||||
else
|
||||
$(error Unsupported target architecture)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring LJ_TARGET_PS3 1,$(TARGET_TESTARCH)))
|
||||
TARGET_SYS= PS3
|
||||
TARGET_ARCH+= -D__CELLOS_LV2__
|
||||
TARGET_XCFLAGS+= -DLUAJIT_USE_SYSMALLOC
|
||||
endif
|
||||
ifneq (,$(findstring LJ_NO_UNWIND 1,$(TARGET_TESTARCH)))
|
||||
TARGET_ARCH+= -DLUAJIT_NO_UNWIND
|
||||
endif
|
||||
|
||||
TARGET_XCFLAGS+= $(CCOPT_$(TARGET_LJARCH))
|
||||
TARGET_ARCH+= $(patsubst %,-DLUAJIT_TARGET=LUAJIT_ARCH_%,$(TARGET_LJARCH))
|
||||
|
||||
ifneq (,$(PREFIX))
|
||||
ifneq (/usr/local,$(PREFIX))
|
||||
TARGET_XCFLAGS+= -DLUA_XROOT=\"$(PREFIX)/\"
|
||||
ifneq (/usr,$(PREFIX))
|
||||
TARGET_DYNXLDOPTS= -Wl,-rpath,$(PREFIX)/lib
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# System detection.
|
||||
##############################################################################
|
||||
|
||||
ifeq (Windows,$(findstring Windows,$(OS))$(MSYSTEM)$(TERM))
|
||||
HOST_SYS= Windows
|
||||
HOST_RM= del
|
||||
else
|
||||
HOST_SYS:= $(shell uname -s)
|
||||
ifneq (,$(findstring MINGW,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= mingw
|
||||
endif
|
||||
ifneq (,$(findstring CYGWIN,$(HOST_SYS)))
|
||||
HOST_SYS= Windows
|
||||
HOST_MSYS= cygwin
|
||||
endif
|
||||
endif
|
||||
|
||||
TARGET_SYS?= $(HOST_SYS)
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
TARGET_STRIP+= --strip-unneeded
|
||||
TARGET_XSHLDFLAGS= -shared
|
||||
TARGET_DYNXLDOPTS=
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
ifeq (,$(MACOSX_DEPLOYMENT_TARGET))
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.4
|
||||
endif
|
||||
TARGET_STRIP+= -x
|
||||
TARGET_AR+= 2>/dev/null
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC
|
||||
TARGET_DYNXLDOPTS=
|
||||
TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER)
|
||||
ifeq (x64,$(TARGET_LJARCH))
|
||||
TARGET_XLDFLAGS+= -pagezero_size 10000 -image_base 100000000
|
||||
TARGET_XSHLDFLAGS+= -image_base 7fff04c4a000
|
||||
endif
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
TARGET_STRIP+= -x
|
||||
TARGET_AR+= 2>/dev/null
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
TARGET_XSHLDFLAGS= -dynamiclib -single_module -undefined dynamic_lookup -fPIC
|
||||
TARGET_DYNXLDOPTS=
|
||||
TARGET_XSHLDFLAGS+= -install_name $(TARGET_DYLIBPATH) -compatibility_version $(MAJVER).$(MINVER) -current_version $(MAJVER).$(MINVER).$(RELVER)
|
||||
else
|
||||
ifneq (,$(findstring stack-protector,$(shell $(TARGET_CC) -dumpspecs)))
|
||||
TARGET_XCFLAGS+= -fno-stack-protector
|
||||
endif
|
||||
ifneq (SunOS,$(TARGET_SYS))
|
||||
ifneq (PS3,$(TARGET_SYS))
|
||||
TARGET_XLDFLAGS+= -Wl,-E
|
||||
endif
|
||||
endif
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
ifeq (GNU/kFreeBSD,$(TARGET_SYS))
|
||||
TARGET_XLIBS+= -ldl
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(HOST_SYS),$(TARGET_SYS))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -malign-double -DLUAJIT_OS=LUAJIT_OS_WINDOWS
|
||||
else
|
||||
ifeq (Linux,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_LINUX
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX
|
||||
else
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OSX
|
||||
else
|
||||
HOST_XCFLAGS+= -DLUAJIT_OS=LUAJIT_OS_OTHER
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (,$(CCDEBUG))
|
||||
TARGET_STRIP= @:
|
||||
endif
|
||||
|
||||
##############################################################################
|
||||
# Files and pathnames.
|
||||
##############################################################################
|
||||
|
||||
MINILUA_O= host/minilua.o
|
||||
MINILUA_LIBS= -lm
|
||||
MINILUA_T= host/minilua
|
||||
MINILUA_X= $(MINILUA_T)
|
||||
|
||||
ifeq (,$(HOST_LUA))
|
||||
HOST_LUA= $(MINILUA_X)
|
||||
DASM_DEP= $(MINILUA_T)
|
||||
endif
|
||||
|
||||
DASM_DIR= ../dynasm
|
||||
DASM= $(HOST_LUA) $(DASM_DIR)/dynasm.lua
|
||||
DASM_XFLAGS=
|
||||
DASM_AFLAGS=
|
||||
DASM_ARCH= $(TARGET_LJARCH)
|
||||
|
||||
ifneq (,$(findstring LJ_ARCH_BITS 64,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D P64
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASJIT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D JIT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_HASFFI 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FFI
|
||||
endif
|
||||
ifneq (,$(findstring LJ_DUALNUM 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D DUALNUM
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_HASFPU 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D FPU
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=1
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ARCH_HASFPU=0
|
||||
endif
|
||||
ifeq (,$(findstring LJ_ABI_SOFTFP 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D HFABI
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=0
|
||||
else
|
||||
TARGET_ARCH+= -DLJ_ABI_SOFTFP=1
|
||||
endif
|
||||
DASM_AFLAGS+= -D VER=$(subst LJ_ARCH_VERSION_,,$(filter LJ_ARCH_VERSION_%,$(subst LJ_ARCH_VERSION ,LJ_ARCH_VERSION_,$(TARGET_TESTARCH))))
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D WIN
|
||||
endif
|
||||
ifeq (x86,$(TARGET_LJARCH))
|
||||
ifneq (,$(findstring __SSE2__ 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D SSE
|
||||
endif
|
||||
else
|
||||
ifeq (x64,$(TARGET_LJARCH))
|
||||
DASM_ARCH= x86
|
||||
else
|
||||
ifeq (arm,$(TARGET_LJARCH))
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D IOS
|
||||
endif
|
||||
else
|
||||
ifeq (ppc,$(TARGET_LJARCH))
|
||||
ifneq (,$(findstring LJ_ARCH_SQRT 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D SQRT
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_ROUND 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D ROUND
|
||||
endif
|
||||
ifneq (,$(findstring LJ_ARCH_PPC64 1,$(TARGET_TESTARCH)))
|
||||
DASM_AFLAGS+= -D GPR64
|
||||
endif
|
||||
ifeq (PS3,$(TARGET_SYS))
|
||||
DASM_AFLAGS+= -D PPE -D TOC
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS)
|
||||
DASM_DASC= vm_$(DASM_ARCH).dasc
|
||||
|
||||
BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \
|
||||
host/buildvm_lib.o host/buildvm_fold.o
|
||||
BUILDVM_T= host/buildvm
|
||||
BUILDVM_X= $(BUILDVM_T)
|
||||
|
||||
HOST_O= $(MINILUA_O) $(BUILDVM_O)
|
||||
HOST_T= $(MINILUA_T) $(BUILDVM_T)
|
||||
|
||||
LJVM_S= lj_vm.s
|
||||
LJVM_O= lj_vm.o
|
||||
LJVM_BOUT= $(LJVM_S)
|
||||
LJVM_MODE= elfasm
|
||||
|
||||
LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \
|
||||
lib_io.o lib_os.o lib_package.o lib_debug.o lib_jit.o lib_ffi.o
|
||||
LJLIB_C= $(LJLIB_O:.o=.c)
|
||||
|
||||
LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o \
|
||||
lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \
|
||||
lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \
|
||||
lj_api.o lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \
|
||||
lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \
|
||||
lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \
|
||||
lj_mcode.o lj_snap.o lj_record.o lj_crecord.o lj_ffrecord.o \
|
||||
lj_asm.o lj_trace.o lj_gdbjit.o \
|
||||
lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \
|
||||
lj_carith.o lj_clib.o lj_cparse.o \
|
||||
lj_lib.o lj_alloc.o lib_aux.o \
|
||||
$(LJLIB_O) lib_init.o
|
||||
|
||||
LJVMCORE_O= $(LJVM_O) $(LJCORE_O)
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o)
|
||||
|
||||
LIB_VMDEF= jit/vmdef.lua
|
||||
LIB_VMDEFP= $(LIB_VMDEF)
|
||||
|
||||
LUAJIT_O= luajit.o
|
||||
LUAJIT_A= libluajit.a
|
||||
LUAJIT_SO= libluajit.so
|
||||
LUAJIT_T= luajit
|
||||
|
||||
ALL_T= $(LUAJIT_T) $(LUAJIT_A) $(LUAJIT_SO) $(HOST_T)
|
||||
ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \
|
||||
host/buildvm_arch.h
|
||||
ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP)
|
||||
WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk
|
||||
ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o $(WIN_RM)
|
||||
|
||||
##############################################################################
|
||||
# Build mode handling.
|
||||
##############################################################################
|
||||
|
||||
# Mixed mode defaults.
|
||||
TARGET_O= $(LUAJIT_A)
|
||||
TARGET_T= $(LUAJIT_T) $(LUAJIT_SO)
|
||||
TARGET_DEP= $(LIB_VMDEF) $(LUAJIT_SO)
|
||||
|
||||
ifeq (Windows,$(TARGET_SYS))
|
||||
TARGET_DYNCC= $(STATIC_CC)
|
||||
LJVM_MODE= peobj
|
||||
LJVM_BOUT= $(LJVM_O)
|
||||
LUAJIT_T= luajit.exe
|
||||
ifeq (cygwin,$(HOST_MSYS))
|
||||
LUAJIT_SO= cyg$(TARGET_DLLNAME)
|
||||
else
|
||||
LUAJIT_SO= $(TARGET_DLLNAME)
|
||||
endif
|
||||
# Mixed mode is not supported on Windows. And static mode doesn't work well.
|
||||
# C modules cannot be loaded, because they bind to lua51.dll.
|
||||
ifneq (static,$(BUILDMODE))
|
||||
BUILDMODE= dynamic
|
||||
TARGET_XCFLAGS+= -DLUA_BUILD_AS_DLL
|
||||
endif
|
||||
endif
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
LJVM_MODE= machasm
|
||||
endif
|
||||
ifeq (SunOS,$(TARGET_SYS))
|
||||
BUILDMODE= static
|
||||
endif
|
||||
ifeq (PS3,$(TARGET_SYS))
|
||||
BUILDMODE= static
|
||||
endif
|
||||
|
||||
ifeq (Windows,$(HOST_SYS))
|
||||
MINILUA_T= host/minilua.exe
|
||||
BUILDVM_T= host/buildvm.exe
|
||||
ifeq (,$(HOST_MSYS))
|
||||
MINILUA_X= host\minilua
|
||||
BUILDVM_X= host\buildvm
|
||||
ALL_RM:= $(subst /,\,$(ALL_RM))
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq (static,$(BUILDMODE))
|
||||
TARGET_DYNCC= @:
|
||||
TARGET_T= $(LUAJIT_T)
|
||||
TARGET_DEP= $(LIB_VMDEF)
|
||||
else
|
||||
ifeq (dynamic,$(BUILDMODE))
|
||||
ifneq (Windows,$(TARGET_SYS))
|
||||
TARGET_CC= $(DYNAMIC_CC)
|
||||
endif
|
||||
TARGET_DYNCC= @:
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O)
|
||||
TARGET_O= $(LUAJIT_SO)
|
||||
TARGET_XLDFLAGS+= $(TARGET_DYNXLDOPTS)
|
||||
else
|
||||
ifeq (Darwin,$(TARGET_SYS))
|
||||
TARGET_DYNCC= @:
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O)
|
||||
endif
|
||||
ifeq (iOS,$(TARGET_SYS))
|
||||
TARGET_DYNCC= @:
|
||||
LJVMCORE_DYNO= $(LJVMCORE_O)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
Q= @
|
||||
E= @echo
|
||||
#Q=
|
||||
#E= @:
|
||||
|
||||
##############################################################################
|
||||
# Make targets.
|
||||
##############################################################################
|
||||
|
||||
default all: $(TARGET_T)
|
||||
|
||||
amalg:
|
||||
@grep "^[+|]" ljamalg.c
|
||||
$(MAKE) all "LJCORE_O=ljamalg.o"
|
||||
|
||||
clean:
|
||||
$(HOST_RM) $(ALL_RM)
|
||||
|
||||
depend:
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -f $$file || touch $$file; \
|
||||
done
|
||||
@$(HOST_CC) $(HOST_ACFLAGS) -MM *.c host/*.c | \
|
||||
sed -e "s| [^ ]*/dasm_\S*\.h||g" \
|
||||
-e "s|^\([^l ]\)|host/\1|" \
|
||||
-e "s| lj_target_\S*\.h| lj_target_*.h|g" \
|
||||
-e "s| lj_emit_\S*\.h| lj_emit_*.h|g" \
|
||||
-e "s| lj_asm_\S*\.h| lj_asm_*.h|g" >Makefile.dep
|
||||
@for file in $(ALL_HDRGEN); do \
|
||||
test -s $$file || $(HOST_RM) $$file; \
|
||||
done
|
||||
|
||||
.PHONY: default all amalg clean depend
|
||||
|
||||
##############################################################################
|
||||
# Rules for generated files.
|
||||
##############################################################################
|
||||
|
||||
$(MINILUA_T): $(MINILUA_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(MINILUA_O) $(MINILUA_LIBS) $(HOST_ALIBS)
|
||||
|
||||
host/buildvm_arch.h: $(DASM_DASC) $(DASM_DEP)
|
||||
$(E) "DYNASM $@"
|
||||
$(Q)$(DASM) $(DASM_FLAGS) -o $@ $(DASM_DASC)
|
||||
|
||||
host/buildvm.o: $(DASM_DIR)/dasm_*.h
|
||||
|
||||
$(BUILDVM_T): $(BUILDVM_O)
|
||||
$(E) "HOSTLINK $@"
|
||||
$(Q)$(HOST_CC) $(HOST_ALDFLAGS) -o $@ $(BUILDVM_O) $(HOST_ALIBS)
|
||||
|
||||
$(LJVM_BOUT): $(BUILDVM_T)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m $(LJVM_MODE) -o $@
|
||||
|
||||
lj_bcdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m bcdef -o $@ $(LJLIB_C)
|
||||
|
||||
lj_ffdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m ffdef -o $@ $(LJLIB_C)
|
||||
|
||||
lj_libdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m libdef -o $@ $(LJLIB_C)
|
||||
|
||||
lj_recdef.h: $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m recdef -o $@ $(LJLIB_C)
|
||||
|
||||
$(LIB_VMDEF): $(BUILDVM_T) $(LJLIB_C)
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m vmdef -o $(LIB_VMDEFP) $(LJLIB_C)
|
||||
|
||||
lj_folddef.h: $(BUILDVM_T) lj_opt_fold.c
|
||||
$(E) "BUILDVM $@"
|
||||
$(Q)$(BUILDVM_X) -m folddef -o $@ lj_opt_fold.c
|
||||
|
||||
##############################################################################
|
||||
# Object file rules.
|
||||
##############################################################################
|
||||
|
||||
%.o: %.c
|
||||
$(E) "CC $@"
|
||||
$(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $<
|
||||
$(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
%.o: %.s
|
||||
$(E) "ASM $@"
|
||||
$(Q)$(TARGET_DYNCC) $(TARGET_ACFLAGS) -c -o $(@:.o=_dyn.o) $<
|
||||
$(Q)$(TARGET_CC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
$(LUAJIT_O):
|
||||
$(E) "CC $@"
|
||||
$(Q)$(TARGET_STCC) $(TARGET_ACFLAGS) -c -o $@ $<
|
||||
|
||||
$(HOST_O): %.o: %.c
|
||||
$(E) "HOSTCC $@"
|
||||
$(Q)$(HOST_CC) $(HOST_ACFLAGS) -c -o $@ $<
|
||||
|
||||
include Makefile.dep
|
||||
|
||||
##############################################################################
|
||||
# Target file rules.
|
||||
##############################################################################
|
||||
|
||||
$(LUAJIT_A): $(LJVMCORE_O)
|
||||
$(E) "AR $@"
|
||||
$(Q)$(TARGET_AR) $@ $(LJVMCORE_O)
|
||||
|
||||
# The dependency on _O, but linking with _DYNO is intentional.
|
||||
$(LUAJIT_SO): $(LJVMCORE_O)
|
||||
$(E) "DYNLINK $@"
|
||||
$(Q)$(TARGET_LD) $(TARGET_ASHLDFLAGS) -o $@ $(LJVMCORE_DYNO) $(TARGET_ALIBS)
|
||||
$(Q)$(TARGET_STRIP) $@
|
||||
|
||||
$(LUAJIT_T): $(TARGET_O) $(LUAJIT_O) $(TARGET_DEP)
|
||||
$(E) "LINK $@"
|
||||
$(Q)$(TARGET_LD) $(TARGET_ALDFLAGS) -o $@ $(LUAJIT_O) $(TARGET_O) $(TARGET_ALIBS)
|
||||
$(Q)$(TARGET_STRIP) $@
|
||||
$(E) "OK Successfully built LuaJIT"
|
||||
|
||||
##############################################################################
|
||||
Vendored
+226
@@ -0,0 +1,226 @@
|
||||
lib_aux.o: lib_aux.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_state.h lj_trace.h lj_jit.h lj_ir.h \
|
||||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_lib.h 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_debug.h lj_str.h \
|
||||
lj_tab.h lj_meta.h lj_state.h lj_ctype.h lj_cconv.h lj_bc.h lj_ff.h \
|
||||
lj_ffdef.h lj_dispatch.h lj_jit.h lj_ir.h lj_char.h lj_strscan.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_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_lib.h \
|
||||
lj_libdef.h
|
||||
lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h \
|
||||
lj_ctype.h lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h \
|
||||
lj_ccallback.h lj_clib.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h
|
||||
lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h
|
||||
lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_state.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_debug.h lj_str.h lj_tab.h \
|
||||
lj_bc.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_target.h \
|
||||
lj_target_*.h lj_dispatch.h lj_vm.h lj_vmevent.h lj_lib.h luajit.h \
|
||||
lj_libdef.h
|
||||
lib_math.o: lib_math.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
|
||||
lj_def.h lj_arch.h lj_lib.h lj_vm.h lj_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_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_bcdump.h lj_lex.h lj_char.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_debug.h lj_str.h lj_tab.h lj_func.h lj_udata.h \
|
||||
lj_meta.h lj_state.h lj_bc.h lj_frame.h lj_trace.h lj_jit.h lj_ir.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_vm.h lj_strscan.h
|
||||
lj_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_frame.h lj_bc.h lj_ctype.h lj_ir.h lj_jit.h \
|
||||
lj_ircall.h lj_iropt.h lj_mcode.h lj_trace.h lj_dispatch.h lj_traceerr.h \
|
||||
lj_snap.h lj_asm.h lj_vm.h lj_target.h lj_target_*.h lj_emit_*.h \
|
||||
lj_asm_*.h
|
||||
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
|
||||
lj_bcdef.h
|
||||
lj_bcread.o: lj_bcread.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_bc.h lj_ctype.h \
|
||||
lj_cdata.h lualib.h lj_lex.h lj_bcdump.h lj_state.h
|
||||
lj_bcwrite.o: lj_bcwrite.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_str.h lj_bc.h lj_ctype.h lj_dispatch.h lj_jit.h lj_ir.h \
|
||||
lj_bcdump.h lj_lex.h lj_err.h lj_errmsg.h lj_vm.h
|
||||
lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h lj_carith.h
|
||||
lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h lj_ccall.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
|
||||
lj_traceerr.h
|
||||
lj_ccallback.o: lj_ccallback.c lj_obj.h lua.h luaconf.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_state.h lj_frame.h \
|
||||
lj_bc.h lj_ctype.h lj_cconv.h lj_ccall.h lj_ccallback.h lj_target.h \
|
||||
lj_target_*.h lj_mcode.h lj_jit.h lj_ir.h lj_trace.h lj_dispatch.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_cconv.o: lj_cconv.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_tab.h lj_ctype.h lj_gc.h lj_cdata.h lj_cconv.h \
|
||||
lj_ccallback.h
|
||||
lj_cdata.o: lj_cdata.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h
|
||||
lj_char.o: lj_char.c lj_char.h lj_def.h lua.h luaconf.h
|
||||
lj_clib.o: lj_clib.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_tab.h lj_str.h lj_udata.h lj_ctype.h lj_cconv.h \
|
||||
lj_cdata.h lj_clib.h
|
||||
lj_cparse.o: lj_cparse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_cparse.h lj_frame.h \
|
||||
lj_bc.h lj_vm.h lj_char.h lj_strscan.h
|
||||
lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h \
|
||||
lj_gc.h lj_cdata.h lj_cparse.h lj_cconv.h lj_clib.h lj_ccall.h lj_ff.h \
|
||||
lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_snap.h \
|
||||
lj_crecord.h
|
||||
lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_ccallback.h
|
||||
lj_debug.o: lj_debug.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_state.h lj_frame.h \
|
||||
lj_bc.h lj_jit.h lj_ir.h
|
||||
lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_func.h lj_str.h lj_tab.h lj_meta.h lj_debug.h \
|
||||
lj_state.h lj_frame.h lj_bc.h lj_ff.h lj_ffdef.h lj_jit.h lj_ir.h \
|
||||
lj_ccallback.h lj_ctype.h lj_gc.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_debug.h lj_str.h lj_func.h lj_state.h lj_frame.h lj_bc.h \
|
||||
lj_ff.h lj_ffdef.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_ffrecord.o: lj_ffrecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ff.h \
|
||||
lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h lj_crecord.h \
|
||||
lj_vm.h lj_strscan.h lj_recdef.h
|
||||
lj_func.o: lj_func.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_func.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_gc.o: lj_gc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_udata.h lj_meta.h \
|
||||
lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_cdata.h lj_trace.h lj_jit.h \
|
||||
lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h
|
||||
lj_gdbjit.o: lj_gdbjit.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_frame.h lj_bc.h lj_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_tab.h lj_ir.h lj_jit.h lj_ircall.h lj_iropt.h lj_trace.h \
|
||||
lj_dispatch.h lj_bc.h lj_traceerr.h lj_ctype.h lj_cdata.h lj_carith.h \
|
||||
lj_vm.h lj_strscan.h lj_lib.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_tab.h lj_ctype.h lj_cdata.h lualib.h \
|
||||
lj_state.h lj_lex.h lj_parse.h lj_char.h lj_strscan.h
|
||||
lj_lib.o: lj_lib.c lauxlib.h lua.h luaconf.h lj_obj.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_bc.h \
|
||||
lj_dispatch.h lj_jit.h lj_ir.h lj_vm.h lj_strscan.h lj_lib.h
|
||||
lj_load.o: lj_load.c lua.h luaconf.h lauxlib.h lj_obj.h lj_def.h \
|
||||
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_func.h lj_frame.h \
|
||||
lj_bc.h lj_vm.h lj_lex.h lj_bcdump.h lj_parse.h
|
||||
lj_mcode.o: lj_mcode.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_jit.h lj_ir.h lj_mcode.h lj_trace.h lj_dispatch.h lj_bc.h \
|
||||
lj_traceerr.h lj_vm.h
|
||||
lj_meta.o: lj_meta.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \
|
||||
lj_vm.h lj_strscan.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_tab.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \
|
||||
lj_bc.h lj_traceerr.h lj_ctype.h lj_gc.h lj_carith.h lj_vm.h \
|
||||
lj_strscan.h lj_folddef.h
|
||||
lj_opt_loop.o: lj_opt_loop.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_err.h lj_errmsg.h lj_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_bc.h lj_ir.h lj_jit.h lj_iropt.h lj_trace.h lj_dispatch.h \
|
||||
lj_traceerr.h lj_vm.h lj_strscan.h
|
||||
lj_opt_sink.o: lj_opt_sink.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_ir.h lj_jit.h lj_iropt.h lj_target.h lj_target_*.h
|
||||
lj_opt_split.o: lj_opt_split.c lj_obj.h lua.h luaconf.h lj_def.h \
|
||||
lj_arch.h lj_err.h lj_errmsg.h lj_str.h lj_ir.h lj_jit.h lj_ircall.h \
|
||||
lj_iropt.h lj_vm.h
|
||||
lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_tab.h lj_func.h \
|
||||
lj_state.h lj_bc.h lj_ctype.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_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \
|
||||
lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h \
|
||||
lj_iropt.h lj_trace.h lj_dispatch.h lj_traceerr.h lj_record.h \
|
||||
lj_ffrecord.h lj_snap.h lj_vm.h
|
||||
lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_tab.h lj_state.h lj_frame.h lj_bc.h lj_ir.h lj_jit.h lj_iropt.h \
|
||||
lj_trace.h lj_dispatch.h lj_traceerr.h lj_snap.h lj_target.h \
|
||||
lj_target_*.h lj_ctype.h lj_cdata.h
|
||||
lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_func.h lj_meta.h \
|
||||
lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_trace.h lj_jit.h lj_ir.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_vm.h lj_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_char.h
|
||||
lj_strscan.o: lj_strscan.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_char.h lj_strscan.h
|
||||
lj_tab.o: lj_tab.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
|
||||
lj_err.h lj_errmsg.h lj_tab.h
|
||||
lj_trace.o: lj_trace.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_str.h lj_frame.h lj_bc.h \
|
||||
lj_state.h lj_ir.h lj_jit.h lj_iropt.h lj_mcode.h lj_trace.h \
|
||||
lj_dispatch.h lj_traceerr.h lj_snap.h lj_gdbjit.h lj_record.h lj_asm.h \
|
||||
lj_vm.h lj_vmevent.h lj_target.h lj_target_*.h
|
||||
lj_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
|
||||
lj_vmmath.o: lj_vmmath.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
|
||||
lj_ir.h lj_vm.h
|
||||
ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_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_ctype.h lj_cdata.h \
|
||||
lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_err.c \
|
||||
lj_debug.h lj_ff.h lj_ffdef.h lj_char.c lj_char.h lj_bc.c lj_bcdef.h \
|
||||
lj_obj.c lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_strscan.h \
|
||||
lj_debug.c lj_state.c lj_lex.h lj_alloc.h lj_dispatch.c lj_ccallback.h \
|
||||
luajit.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c lj_api.c \
|
||||
lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h \
|
||||
lj_bcwrite.c lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c \
|
||||
lj_ccall.c lj_ccall.h lj_ccallback.c lj_target.h lj_target_*.h \
|
||||
lj_mcode.h lj_carith.c lj_carith.h lj_clib.c lj_clib.h lj_cparse.c \
|
||||
lj_cparse.h lj_lib.c lj_lib.h lj_ir.c lj_ircall.h lj_iropt.h \
|
||||
lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c \
|
||||
lj_opt_loop.c lj_snap.h lj_opt_split.c lj_opt_sink.c lj_mcode.c \
|
||||
lj_snap.c lj_record.c lj_record.h lj_ffrecord.h lj_crecord.c \
|
||||
lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h \
|
||||
lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c \
|
||||
lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c \
|
||||
lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_ffi.c \
|
||||
lib_init.c
|
||||
luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h
|
||||
host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \
|
||||
lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \
|
||||
lj_ircall.h lj_ir.h lj_jit.h lj_frame.h lj_bc.h lj_dispatch.h lj_ctype.h \
|
||||
lj_gc.h lj_ccall.h lj_ctype.h luajit.h \
|
||||
host/buildvm_arch.h lj_traceerr.h
|
||||
host/buildvm_asm.o: host/buildvm_asm.c host/buildvm.h lj_def.h lua.h luaconf.h \
|
||||
lj_arch.h lj_bc.h lj_def.h lj_arch.h
|
||||
host/buildvm_fold.o: host/buildvm_fold.c host/buildvm.h lj_def.h lua.h \
|
||||
luaconf.h lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_ir.h lj_obj.h
|
||||
host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \
|
||||
lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_lib.h lj_obj.h
|
||||
host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \
|
||||
luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h
|
||||
host/minilua.o: host/minilua.c
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
The files in this directory are only used during the build process of LuaJIT.
|
||||
For cross-compilation, they must be executed on the host, not on the target.
|
||||
|
||||
These files should NOT be installed!
|
||||
Vendored
+516
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
** LuaJIT VM builder.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** This is a tool to build the hand-tuned assembler code required for
|
||||
** LuaJIT's bytecode interpreter. It supports a variety of output formats
|
||||
** to feed different toolchains (see usage() below).
|
||||
**
|
||||
** This tool is not particularly optimized because it's only used while
|
||||
** _building_ LuaJIT. There's no point in distributing or installing it.
|
||||
** Only the object code generated by this tool is linked into LuaJIT.
|
||||
**
|
||||
** Caveat: some memory is not free'd, error handling is lazy.
|
||||
** It's a one-shot tool -- any effort fixing this would be wasted.
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_bc.h"
|
||||
#include "lj_ir.h"
|
||||
#include "lj_ircall.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_dispatch.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_ccall.h"
|
||||
#endif
|
||||
#include "luajit.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* DynASM glue definitions. */
|
||||
#define Dst ctx
|
||||
#define Dst_DECL BuildCtx *ctx
|
||||
#define Dst_REF (ctx->D)
|
||||
#define DASM_CHECKS 1
|
||||
|
||||
#include "../dynasm/dasm_proto.h"
|
||||
|
||||
/* Glue macros for DynASM. */
|
||||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type);
|
||||
|
||||
#define DASM_EXTERN(ctx, addr, idx, type) \
|
||||
collect_reloc(ctx, addr, idx, type)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Avoid trouble if cross-compiling for an x86 target. Speed doesn't matter. */
|
||||
#define DASM_ALIGNED_WRITES 1
|
||||
|
||||
/* Embed architecture-specific DynASM encoder. */
|
||||
#if LJ_TARGET_X86ORX64
|
||||
#include "../dynasm/dasm_x86.h"
|
||||
#elif LJ_TARGET_ARM
|
||||
#include "../dynasm/dasm_arm.h"
|
||||
#elif LJ_TARGET_PPC
|
||||
#include "../dynasm/dasm_ppc.h"
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
#include "../dynasm/dasm_ppc.h"
|
||||
#elif LJ_TARGET_MIPS
|
||||
#include "../dynasm/dasm_mips.h"
|
||||
#else
|
||||
#error "No support for this architecture (yet)"
|
||||
#endif
|
||||
|
||||
/* Embed generated architecture-specific backend. */
|
||||
#include "buildvm_arch.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
void owrite(BuildCtx *ctx, const void *ptr, size_t sz)
|
||||
{
|
||||
if (fwrite(ptr, 1, sz, ctx->fp) != sz) {
|
||||
fprintf(stderr, "Error: cannot write to output file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Emit code as raw bytes. Only used for DynASM debugging. */
|
||||
static void emit_raw(BuildCtx *ctx)
|
||||
{
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
}
|
||||
|
||||
/* -- Build machine code -------------------------------------------------- */
|
||||
|
||||
static const char *sym_decorate(BuildCtx *ctx,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
char name[256];
|
||||
char *p;
|
||||
#if LJ_64
|
||||
const char *symprefix = ctx->mode == BUILD_machasm ? "_" : "";
|
||||
#elif LJ_TARGET_XBOX360
|
||||
const char *symprefix = "";
|
||||
#else
|
||||
const char *symprefix = ctx->mode != BUILD_elfasm ? "_" : "";
|
||||
#endif
|
||||
sprintf(name, "%s%s%s", symprefix, prefix, suffix);
|
||||
p = strchr(name, '@');
|
||||
if (p) {
|
||||
#if LJ_TARGET_X86ORX64
|
||||
if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj))
|
||||
name[0] = '@';
|
||||
else
|
||||
*p = '\0';
|
||||
#elif (LJ_TARGET_PPC || LJ_TARGET_PPCSPE) && !LJ_TARGET_CONSOLE
|
||||
/* Keep @plt. */
|
||||
#else
|
||||
*p = '\0';
|
||||
#endif
|
||||
}
|
||||
p = (char *)malloc(strlen(name)+1); /* MSVC doesn't like strdup. */
|
||||
strcpy(p, name);
|
||||
return p;
|
||||
}
|
||||
|
||||
#define NRELOCSYM (sizeof(extnames)/sizeof(extnames[0])-1)
|
||||
|
||||
static int relocmap[NRELOCSYM];
|
||||
|
||||
/* Collect external relocations. */
|
||||
static int collect_reloc(BuildCtx *ctx, uint8_t *addr, int idx, int type)
|
||||
{
|
||||
if (ctx->nreloc >= BUILD_MAX_RELOC) {
|
||||
fprintf(stderr, "Error: too many relocations, increase BUILD_MAX_RELOC.\n");
|
||||
exit(1);
|
||||
}
|
||||
if (relocmap[idx] < 0) {
|
||||
relocmap[idx] = ctx->nrelocsym;
|
||||
ctx->relocsym[ctx->nrelocsym] = sym_decorate(ctx, "", extnames[idx]);
|
||||
ctx->nrelocsym++;
|
||||
}
|
||||
ctx->reloc[ctx->nreloc].ofs = (int32_t)(addr - ctx->code);
|
||||
ctx->reloc[ctx->nreloc].sym = relocmap[idx];
|
||||
ctx->reloc[ctx->nreloc].type = type;
|
||||
ctx->nreloc++;
|
||||
#if LJ_TARGET_XBOX360
|
||||
return (int)(ctx->code - addr) + 4; /* Encode symbol offset of .text. */
|
||||
#else
|
||||
return 0; /* Encode symbol offset of 0. */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Naive insertion sort. Performance doesn't matter here. */
|
||||
static void sym_insert(BuildCtx *ctx, int32_t ofs,
|
||||
const char *prefix, const char *suffix)
|
||||
{
|
||||
ptrdiff_t i = ctx->nsym++;
|
||||
while (i > 0) {
|
||||
if (ctx->sym[i-1].ofs <= ofs)
|
||||
break;
|
||||
ctx->sym[i] = ctx->sym[i-1];
|
||||
i--;
|
||||
}
|
||||
ctx->sym[i].ofs = ofs;
|
||||
ctx->sym[i].name = sym_decorate(ctx, prefix, suffix);
|
||||
}
|
||||
|
||||
/* Build the machine code. */
|
||||
static int build_code(BuildCtx *ctx)
|
||||
{
|
||||
int status;
|
||||
int i;
|
||||
|
||||
/* Initialize DynASM structures. */
|
||||
ctx->nglob = GLOB__MAX;
|
||||
ctx->glob = (void **)malloc(ctx->nglob*sizeof(void *));
|
||||
memset(ctx->glob, 0, ctx->nglob*sizeof(void *));
|
||||
ctx->nreloc = 0;
|
||||
|
||||
ctx->globnames = globnames;
|
||||
ctx->relocsym = (const char **)malloc(NRELOCSYM*sizeof(const char *));
|
||||
ctx->nrelocsym = 0;
|
||||
for (i = 0; i < (int)NRELOCSYM; i++) relocmap[i] = -1;
|
||||
|
||||
ctx->dasm_ident = DASM_IDENT;
|
||||
ctx->dasm_arch = DASM_ARCH;
|
||||
|
||||
dasm_init(Dst, DASM_MAXSECTION);
|
||||
dasm_setupglobal(Dst, ctx->glob, ctx->nglob);
|
||||
dasm_setup(Dst, build_actionlist);
|
||||
|
||||
/* Call arch-specific backend to emit the code. */
|
||||
ctx->npc = build_backend(ctx);
|
||||
|
||||
/* Finalize the code. */
|
||||
(void)dasm_checkstep(Dst, -1);
|
||||
if ((status = dasm_link(Dst, &ctx->codesz))) return status;
|
||||
ctx->code = (uint8_t *)malloc(ctx->codesz);
|
||||
if ((status = dasm_encode(Dst, (void *)ctx->code))) return status;
|
||||
|
||||
/* Allocate symbol table and bytecode offsets. */
|
||||
ctx->beginsym = sym_decorate(ctx, "", LABEL_PREFIX "vm_asm_begin");
|
||||
ctx->sym = (BuildSym *)malloc((ctx->npc+ctx->nglob+1)*sizeof(BuildSym));
|
||||
ctx->nsym = 0;
|
||||
ctx->bc_ofs = (int32_t *)malloc(ctx->npc*sizeof(int32_t));
|
||||
|
||||
/* Collect the opcodes (PC labels). */
|
||||
for (i = 0; i < ctx->npc; i++) {
|
||||
int32_t ofs = dasm_getpclabel(Dst, i);
|
||||
if (ofs < 0) return 0x22000000|i;
|
||||
ctx->bc_ofs[i] = ofs;
|
||||
if ((LJ_HASJIT ||
|
||||
!(i == BC_JFORI || i == BC_JFORL || i == BC_JITERL || i == BC_JLOOP ||
|
||||
i == BC_IFORL || i == BC_IITERL || i == BC_ILOOP)) &&
|
||||
(LJ_HASFFI || i != BC_KCDATA))
|
||||
sym_insert(ctx, ofs, LABEL_PREFIX_BC, bc_names[i]);
|
||||
}
|
||||
|
||||
/* Collect the globals (named labels). */
|
||||
for (i = 0; i < ctx->nglob; i++) {
|
||||
const char *gl = globnames[i];
|
||||
int len = (int)strlen(gl);
|
||||
if (!ctx->glob[i]) {
|
||||
fprintf(stderr, "Error: undefined global %s\n", gl);
|
||||
exit(2);
|
||||
}
|
||||
/* Skip the _Z symbols. */
|
||||
if (!(len >= 2 && gl[len-2] == '_' && gl[len-1] == 'Z'))
|
||||
sym_insert(ctx, (int32_t)((uint8_t *)(ctx->glob[i]) - ctx->code),
|
||||
LABEL_PREFIX, globnames[i]);
|
||||
}
|
||||
|
||||
/* Close the address range. */
|
||||
sym_insert(ctx, (int32_t)ctx->codesz, "", "");
|
||||
ctx->nsym--;
|
||||
|
||||
dasm_free(Dst);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- Generate VM enums --------------------------------------------------- */
|
||||
|
||||
const char *const bc_names[] = {
|
||||
#define BCNAME(name, ma, mb, mc, mt) #name,
|
||||
BCDEF(BCNAME)
|
||||
#undef BCNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const ir_names[] = {
|
||||
#define IRNAME(name, m, m1, m2) #name,
|
||||
IRDEF(IRNAME)
|
||||
#undef IRNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irt_names[] = {
|
||||
#define IRTNAME(name, size) #name,
|
||||
IRTDEF(IRTNAME)
|
||||
#undef IRTNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irfpm_names[] = {
|
||||
#define FPMNAME(name) #name,
|
||||
IRFPMDEF(FPMNAME)
|
||||
#undef FPMNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const irfield_names[] = {
|
||||
#define FLNAME(name, ofs) #name,
|
||||
IRFLDEF(FLNAME)
|
||||
#undef FLNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
const char *const ircall_names[] = {
|
||||
#define IRCALLNAME(cond, name, nargs, kind, type, flags) #name,
|
||||
IRCALLDEF(IRCALLNAME)
|
||||
#undef IRCALLNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *const trace_errors[] = {
|
||||
#define TREDEF(name, msg) msg,
|
||||
#include "lj_traceerr.h"
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *lower(char *buf, const char *s)
|
||||
{
|
||||
char *p = buf;
|
||||
while (*s) {
|
||||
*p++ = (*s >= 'A' && *s <= 'Z') ? *s+0x20 : *s;
|
||||
s++;
|
||||
}
|
||||
*p = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Emit C source code for bytecode-related definitions. */
|
||||
static void emit_bcdef(BuildCtx *ctx)
|
||||
{
|
||||
int i;
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_ofs[] = {\n");
|
||||
for (i = 0; i < ctx->npc; i++) {
|
||||
if (i != 0)
|
||||
fprintf(ctx->fp, ",\n");
|
||||
fprintf(ctx->fp, "%d", ctx->bc_ofs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit VM definitions as Lua code for debug modules. */
|
||||
static void emit_vmdef(BuildCtx *ctx)
|
||||
{
|
||||
char buf[80];
|
||||
int i;
|
||||
fprintf(ctx->fp, "-- This is a generated file. DO NOT EDIT!\n\n");
|
||||
fprintf(ctx->fp, "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, "ircall = {\n[0]=");
|
||||
for (i = 0; ircall_names[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\",\n", ircall_names[i]);
|
||||
fprintf(ctx->fp, "}\n\n");
|
||||
|
||||
fprintf(ctx->fp, "traceerr = {\n[0]=");
|
||||
for (i = 0; trace_errors[i]; i++)
|
||||
fprintf(ctx->fp, "\"%s\",\n", trace_errors[i]);
|
||||
fprintf(ctx->fp, "}\n\n");
|
||||
}
|
||||
|
||||
/* -- Argument parsing ---------------------------------------------------- */
|
||||
|
||||
/* Build mode names. */
|
||||
static const char *const modenames[] = {
|
||||
#define BUILDNAME(name) #name,
|
||||
BUILDDEF(BUILDNAME)
|
||||
#undef BUILDNAME
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Print usage information and exit. */
|
||||
static void usage(void)
|
||||
{
|
||||
int i;
|
||||
fprintf(stderr, LUAJIT_VERSION " VM builder.\n");
|
||||
fprintf(stderr, LUAJIT_COPYRIGHT ", " LUAJIT_URL "\n");
|
||||
fprintf(stderr, "Target architecture: " LJ_ARCH_NAME "\n\n");
|
||||
fprintf(stderr, "Usage: buildvm -m mode [-o outfile] [infiles...]\n\n");
|
||||
fprintf(stderr, "Available modes:\n");
|
||||
for (i = 0; i < BUILD__MAX; i++)
|
||||
fprintf(stderr, " %s\n", modenames[i]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Parse the output mode name. */
|
||||
static BuildMode parsemode(const char *mode)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; modenames[i]; i++)
|
||||
if (!strcmp(mode, modenames[i]))
|
||||
return (BuildMode)i;
|
||||
usage();
|
||||
return (BuildMode)-1;
|
||||
}
|
||||
|
||||
/* Parse arguments. */
|
||||
static void parseargs(BuildCtx *ctx, char **argv)
|
||||
{
|
||||
const char *a;
|
||||
int i;
|
||||
ctx->mode = (BuildMode)-1;
|
||||
ctx->outname = "-";
|
||||
for (i = 1; (a = argv[i]) != NULL; i++) {
|
||||
if (a[0] != '-')
|
||||
break;
|
||||
switch (a[1]) {
|
||||
case '-':
|
||||
if (a[2]) goto err;
|
||||
i++;
|
||||
goto ok;
|
||||
case '\0':
|
||||
goto ok;
|
||||
case 'm':
|
||||
i++;
|
||||
if (a[2] || argv[i] == NULL) goto err;
|
||||
ctx->mode = parsemode(argv[i]);
|
||||
break;
|
||||
case 'o':
|
||||
i++;
|
||||
if (a[2] || argv[i] == NULL) goto err;
|
||||
ctx->outname = argv[i];
|
||||
break;
|
||||
default: err:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
ok:
|
||||
ctx->args = argv+i;
|
||||
if (ctx->mode == (BuildMode)-1) goto err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
BuildCtx ctx_;
|
||||
BuildCtx *ctx = &ctx_;
|
||||
int status, binmode;
|
||||
|
||||
if (sizeof(void *) != 4*LJ_32+8*LJ_64) {
|
||||
fprintf(stderr,"Error: pointer size mismatch in cross-build.\n");
|
||||
fprintf(stderr,"Try: make HOST_CC=\"gcc -m32\" CROSS=...\n\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
UNUSED(argc);
|
||||
parseargs(ctx, argv);
|
||||
|
||||
if ((status = build_code(ctx))) {
|
||||
fprintf(stderr,"Error: DASM error %08x\n", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (ctx->mode) {
|
||||
case BUILD_peobj:
|
||||
case BUILD_raw:
|
||||
binmode = 1;
|
||||
break;
|
||||
default:
|
||||
binmode = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->outname[0] == '-' && ctx->outname[1] == '\0') {
|
||||
ctx->fp = stdout;
|
||||
#if defined(_WIN32)
|
||||
if (binmode)
|
||||
_setmode(_fileno(stdout), _O_BINARY); /* Yuck. */
|
||||
#endif
|
||||
} else if (!(ctx->fp = fopen(ctx->outname, binmode ? "wb" : "w"))) {
|
||||
fprintf(stderr, "Error: cannot open output file '%s': %s\n",
|
||||
ctx->outname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
case BUILD_coffasm:
|
||||
case BUILD_machasm:
|
||||
emit_asm(ctx);
|
||||
emit_asm_debug(ctx);
|
||||
break;
|
||||
case BUILD_peobj:
|
||||
emit_peobj(ctx);
|
||||
break;
|
||||
case BUILD_raw:
|
||||
emit_raw(ctx);
|
||||
break;
|
||||
case BUILD_bcdef:
|
||||
emit_bcdef(ctx);
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_vmdef:
|
||||
emit_vmdef(ctx);
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_ffdef:
|
||||
case BUILD_libdef:
|
||||
case BUILD_recdef:
|
||||
emit_lib(ctx);
|
||||
break;
|
||||
case BUILD_folddef:
|
||||
emit_fold(ctx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
fflush(ctx->fp);
|
||||
if (ferror(ctx->fp)) {
|
||||
fprintf(stderr, "Error: cannot write to output file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
fclose(ctx->fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Vendored
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
** LuaJIT VM builder.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _BUILDVM_H
|
||||
#define _BUILDVM_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "lj_def.h"
|
||||
#include "lj_arch.h"
|
||||
|
||||
/* Hardcoded limits. Increase as needed. */
|
||||
#define BUILD_MAX_RELOC 200 /* Max. number of relocations. */
|
||||
#define BUILD_MAX_FOLD 4096 /* Max. number of fold rules. */
|
||||
|
||||
/* Prefix for scanned library definitions. */
|
||||
#define LIBDEF_PREFIX "LJLIB_"
|
||||
|
||||
/* Prefix for scanned fold definitions. */
|
||||
#define FOLDDEF_PREFIX "LJFOLD"
|
||||
|
||||
/* Prefixes for generated labels. */
|
||||
#define LABEL_PREFIX "lj_"
|
||||
#define LABEL_PREFIX_BC LABEL_PREFIX "BC_"
|
||||
#define LABEL_PREFIX_FF LABEL_PREFIX "ff_"
|
||||
#define LABEL_PREFIX_CF LABEL_PREFIX "cf_"
|
||||
#define LABEL_PREFIX_FFH LABEL_PREFIX "ffh_"
|
||||
#define LABEL_PREFIX_LIBCF LABEL_PREFIX "lib_cf_"
|
||||
#define LABEL_PREFIX_LIBINIT LABEL_PREFIX "lib_init_"
|
||||
|
||||
/* Forward declaration. */
|
||||
struct dasm_State;
|
||||
|
||||
/* Build modes. */
|
||||
#define BUILDDEF(_) \
|
||||
_(elfasm) _(coffasm) _(machasm) _(peobj) _(raw) \
|
||||
_(bcdef) _(ffdef) _(libdef) _(recdef) _(vmdef) \
|
||||
_(folddef)
|
||||
|
||||
typedef enum {
|
||||
#define BUILDENUM(name) BUILD_##name,
|
||||
BUILDDEF(BUILDENUM)
|
||||
#undef BUILDENUM
|
||||
BUILD__MAX
|
||||
} BuildMode;
|
||||
|
||||
/* Code relocation. */
|
||||
typedef struct BuildReloc {
|
||||
int32_t ofs;
|
||||
int sym;
|
||||
int type;
|
||||
} BuildReloc;
|
||||
|
||||
typedef struct BuildSym {
|
||||
const char *name;
|
||||
int32_t ofs;
|
||||
} BuildSym;
|
||||
|
||||
/* Build context structure. */
|
||||
typedef struct BuildCtx {
|
||||
/* DynASM state pointer. Should be first member. */
|
||||
struct dasm_State *D;
|
||||
/* Parsed command line. */
|
||||
BuildMode mode;
|
||||
FILE *fp;
|
||||
const char *outname;
|
||||
char **args;
|
||||
/* Code and symbols generated by DynASM. */
|
||||
uint8_t *code;
|
||||
size_t codesz;
|
||||
int npc, nglob, nsym, nreloc, nrelocsym;
|
||||
void **glob;
|
||||
BuildSym *sym;
|
||||
const char **relocsym;
|
||||
int32_t *bc_ofs;
|
||||
const char *beginsym;
|
||||
/* Strings generated by DynASM. */
|
||||
const char *const *globnames;
|
||||
const char *dasm_ident;
|
||||
const char *dasm_arch;
|
||||
/* Relocations. */
|
||||
BuildReloc reloc[BUILD_MAX_RELOC];
|
||||
} BuildCtx;
|
||||
|
||||
extern void owrite(BuildCtx *ctx, const void *ptr, size_t sz);
|
||||
extern void emit_asm(BuildCtx *ctx);
|
||||
extern void emit_peobj(BuildCtx *ctx);
|
||||
extern void emit_lib(BuildCtx *ctx);
|
||||
extern void emit_fold(BuildCtx *ctx);
|
||||
|
||||
extern const char *const bc_names[];
|
||||
extern const char *const ir_names[];
|
||||
extern const char *const irt_names[];
|
||||
extern const char *const irfpm_names[];
|
||||
extern const char *const irfield_names[];
|
||||
extern const char *const ircall_names[];
|
||||
|
||||
#endif
|
||||
Vendored
+313
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
** LuaJIT VM builder: Assembler source code emitter.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if LJ_TARGET_X86ORX64
|
||||
/* Emit bytes piecewise as assembler text. */
|
||||
static void emit_asm_bytes(BuildCtx *ctx, uint8_t *p, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i++) {
|
||||
if ((i & 15) == 0)
|
||||
fprintf(ctx->fp, "\t.byte %d", p[i]);
|
||||
else
|
||||
fprintf(ctx->fp, ",%d", p[i]);
|
||||
if ((i & 15) == 15) putc('\n', ctx->fp);
|
||||
}
|
||||
if ((n & 15) != 0) putc('\n', ctx->fp);
|
||||
}
|
||||
|
||||
/* Emit relocation */
|
||||
static void emit_asm_reloc(BuildCtx *ctx, int type, const char *sym)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
if (type)
|
||||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
|
||||
else
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", sym);
|
||||
if (type)
|
||||
fprintf(ctx->fp, "\t.long %s-.-4\n", sym);
|
||||
else
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
default: /* BUILD_machasm for relative relocations handled below. */
|
||||
fprintf(ctx->fp, "\t.long %s\n", sym);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *const jccnames[] = {
|
||||
"jo", "jno", "jb", "jnb", "jz", "jnz", "jbe", "ja",
|
||||
"js", "jns", "jpe", "jpo", "jl", "jge", "jle", "jg"
|
||||
};
|
||||
|
||||
/* Emit 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);
|
||||
fprintf(ctx->fp, "\t%s %s\n", opname, sym);
|
||||
}
|
||||
#else
|
||||
/* Emit words piecewise as assembler text. */
|
||||
static void emit_asm_words(BuildCtx *ctx, uint8_t *p, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < n; i += 4) {
|
||||
if ((i & 15) == 0)
|
||||
fprintf(ctx->fp, "\t.long 0x%08x", *(uint32_t *)(p+i));
|
||||
else
|
||||
fprintf(ctx->fp, ",0x%08x", *(uint32_t *)(p+i));
|
||||
if ((i & 15) == 12) putc('\n', ctx->fp);
|
||||
}
|
||||
if ((n & 15) != 0) putc('\n', ctx->fp);
|
||||
}
|
||||
|
||||
/* Emit relocation as part of an instruction. */
|
||||
static void emit_asm_wordreloc(BuildCtx *ctx, uint8_t *p, int n,
|
||||
const char *sym)
|
||||
{
|
||||
uint32_t ins;
|
||||
emit_asm_words(ctx, p, n-4);
|
||||
ins = *(uint32_t *)(p+n-4);
|
||||
#if LJ_TARGET_ARM
|
||||
if ((ins & 0xff000000u) == 0xfa000000u) {
|
||||
fprintf(ctx->fp, "\tblx %s\n", sym);
|
||||
} else if ((ins & 0x0e000000u) == 0x0a000000u) {
|
||||
fprintf(ctx->fp, "\t%s%.2s %s\n", (ins & 0x01000000u) ? "bl" : "b",
|
||||
"eqnecsccmiplvsvchilsgeltgtle" + 2*(ins >> 28), sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_PPC || LJ_TARGET_PPCSPE
|
||||
#if LJ_TARGET_PS3
|
||||
#define TOCPREFIX "."
|
||||
#else
|
||||
#define TOCPREFIX ""
|
||||
#endif
|
||||
if ((ins >> 26) == 16) {
|
||||
fprintf(ctx->fp, "\t%s %d, %d, " TOCPREFIX "%s\n",
|
||||
(ins & 1) ? "bcl" : "bc", (ins >> 21) & 31, (ins >> 16) & 31, sym);
|
||||
} else if ((ins >> 26) == 18) {
|
||||
fprintf(ctx->fp, "\t%s " TOCPREFIX "%s\n", (ins & 1) ? "bl" : "b", sym);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
}
|
||||
#elif LJ_TARGET_MIPS
|
||||
fprintf(stderr,
|
||||
"Error: unsupported opcode %08x for %s symbol relocation.\n",
|
||||
ins, sym);
|
||||
exit(1);
|
||||
#else
|
||||
#error "missing relocation support for this architecture"
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_ARM
|
||||
#define ELFASM_PX "%%"
|
||||
#else
|
||||
#define ELFASM_PX "@"
|
||||
#endif
|
||||
|
||||
/* Emit an assembler label. */
|
||||
static void emit_asm_label(BuildCtx *ctx, const char *name, int size, int isfunc)
|
||||
{
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
#if LJ_TARGET_PS3
|
||||
if (!strncmp(name, "lj_vm_", 6) &&
|
||||
strcmp(name, ctx->beginsym) &&
|
||||
!strstr(name, "hook")) {
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.globl %s\n"
|
||||
"\t.section \".opd\",\"aw\"\n"
|
||||
"%s:\n"
|
||||
"\t.long .%s,.TOC.@tocbase32\n"
|
||||
"\t.size %s,8\n"
|
||||
"\t.previous\n"
|
||||
"\t.globl .%s\n"
|
||||
"\t.hidden .%s\n"
|
||||
"\t.type .%s, " ELFASM_PX "function\n"
|
||||
"\t.size .%s, %d\n"
|
||||
".%s:\n",
|
||||
name, name, name, name, name, name, name, name, size, name);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.globl %s\n"
|
||||
"\t.hidden %s\n"
|
||||
"\t.type %s, " ELFASM_PX "%s\n"
|
||||
"\t.size %s, %d\n"
|
||||
"%s:\n",
|
||||
name, name, name, isfunc ? "function" : "object", name, size, name);
|
||||
break;
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\n\t.globl %s\n", name);
|
||||
if (isfunc)
|
||||
fprintf(ctx->fp, "\t.def %s; .scl 3; .type 32; .endef\n", name);
|
||||
fprintf(ctx->fp, "%s:\n", name);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
fprintf(ctx->fp,
|
||||
"\n\t.private_extern %s\n"
|
||||
"%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)
|
||||
{
|
||||
int i, rel;
|
||||
|
||||
fprintf(ctx->fp, "\t.file \"buildvm_%s.dasc\"\n", ctx->dasm_arch);
|
||||
fprintf(ctx->fp, "\t.text\n");
|
||||
emit_asm_align(ctx, 4);
|
||||
|
||||
#if LJ_TARGET_PS3
|
||||
emit_asm_label(ctx, ctx->beginsym, ctx->codesz, 0);
|
||||
#else
|
||||
emit_asm_label(ctx, ctx->beginsym, 0, 0);
|
||||
#endif
|
||||
if (ctx->mode != BUILD_machasm)
|
||||
fprintf(ctx->fp, ".Lbegin:\n");
|
||||
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
|
||||
/* This should really be moved into buildvm_arm.dasc. */
|
||||
fprintf(ctx->fp,
|
||||
".fnstart\n"
|
||||
".save {r4, r5, r6, r7, r8, r9, r10, r11, lr}\n"
|
||||
".pad #28\n");
|
||||
#endif
|
||||
#if LJ_TARGET_MIPS
|
||||
fprintf(ctx->fp, ".set nomips16\n.abicalls\n.set noreorder\n.set nomacro\n");
|
||||
#endif
|
||||
|
||||
for (i = rel = 0; i < ctx->nsym; i++) {
|
||||
int32_t ofs = ctx->sym[i].ofs;
|
||||
int32_t next = ctx->sym[i+1].ofs;
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND && LJ_HASFFI
|
||||
if (!strcmp(ctx->sym[i].name, "lj_vm_ffi_call"))
|
||||
fprintf(ctx->fp,
|
||||
".globl lj_err_unwind_arm\n"
|
||||
".personality lj_err_unwind_arm\n"
|
||||
".fnend\n"
|
||||
".fnstart\n"
|
||||
".save {r4, r5, r11, lr}\n"
|
||||
".setfp r11, sp\n");
|
||||
#endif
|
||||
emit_asm_label(ctx, ctx->sym[i].name, next - ofs, 1);
|
||||
while (rel < ctx->nreloc && ctx->reloc[rel].ofs <= next) {
|
||||
BuildReloc *r = &ctx->reloc[rel];
|
||||
int n = r->ofs - ofs;
|
||||
#if LJ_TARGET_X86ORX64
|
||||
if (ctx->mode == BUILD_machasm && r->type != 0) {
|
||||
emit_asm_reloc_mach(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
|
||||
} else {
|
||||
emit_asm_bytes(ctx, ctx->code+ofs, n);
|
||||
emit_asm_reloc(ctx, r->type, ctx->relocsym[r->sym]);
|
||||
}
|
||||
ofs += n+4;
|
||||
#else
|
||||
emit_asm_wordreloc(ctx, ctx->code+ofs, n, ctx->relocsym[r->sym]);
|
||||
ofs += n;
|
||||
#endif
|
||||
rel++;
|
||||
}
|
||||
#if LJ_TARGET_X86ORX64
|
||||
emit_asm_bytes(ctx, ctx->code+ofs, next-ofs);
|
||||
#else
|
||||
emit_asm_words(ctx, ctx->code+ofs, next-ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if LJ_TARGET_ARM && defined(__GNUC__) && !LJ_NO_UNWIND
|
||||
fprintf(ctx->fp,
|
||||
#if !LJ_HASFFI
|
||||
".globl lj_err_unwind_arm\n"
|
||||
".personality lj_err_unwind_arm\n"
|
||||
#endif
|
||||
".fnend\n");
|
||||
#endif
|
||||
|
||||
fprintf(ctx->fp, "\n");
|
||||
switch (ctx->mode) {
|
||||
case BUILD_elfasm:
|
||||
#if !LJ_TARGET_PS3
|
||||
fprintf(ctx->fp, "\t.section .note.GNU-stack,\"\"," ELFASM_PX "progbits\n");
|
||||
#endif
|
||||
#if LJ_TARGET_PPCSPE
|
||||
/* Soft-float ABI + SPE. */
|
||||
fprintf(ctx->fp, "\t.gnu_attribute 4, 2\n\t.gnu_attribute 8, 3\n");
|
||||
#elif LJ_TARGET_PPC && !LJ_TARGET_PS3
|
||||
/* Hard-float ABI. */
|
||||
fprintf(ctx->fp, "\t.gnu_attribute 4, 1\n");
|
||||
#endif
|
||||
/* fallthrough */
|
||||
case BUILD_coffasm:
|
||||
fprintf(ctx->fp, "\t.ident \"%s\"\n", ctx->dasm_ident);
|
||||
break;
|
||||
case BUILD_machasm:
|
||||
fprintf(ctx->fp,
|
||||
"\t.cstring\n"
|
||||
"\t.ascii \"%s\\0\"\n", ctx->dasm_ident);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(ctx->fp, "\n");
|
||||
}
|
||||
|
||||
Vendored
+229
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
** LuaJIT VM builder: IR folding hash table generator.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ir.h"
|
||||
|
||||
/* Context for the folding hash table generator. */
|
||||
static int lineno;
|
||||
static 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 (allowlit && !strncmp(p, "IRCALL_", 7)) {
|
||||
for (i = 0; ircall_names[i]; i++)
|
||||
if (!strcmp(ircall_names[i], p+7))
|
||||
return i;
|
||||
} else if (allowlit && !strncmp(p, "IRCONV_", 7)) {
|
||||
for (i = 0; irt_names[i]; i++) {
|
||||
const char *r = strchr(p+7, '_');
|
||||
if (r && !strncmp(irt_names[i], p+7, r-(p+7))) {
|
||||
uint32_t j;
|
||||
for (j = 0; irt_names[j]; j++)
|
||||
if (!strcmp(irt_names[j], r+1))
|
||||
return (i << 5) + j;
|
||||
}
|
||||
}
|
||||
} else if (allowlit && *p >= '0' && *p <= '9') {
|
||||
for (i = 0; *p >= '0' && *p <= '9'; p++)
|
||||
i = i*10 + (*p - '0');
|
||||
if (*p == '\0')
|
||||
return i;
|
||||
} else if (allowany && !strcmp("any", p)) {
|
||||
return allowany;
|
||||
} else {
|
||||
for (i = 0; ir_names[i]; i++)
|
||||
if (!strcmp(ir_names[i], p))
|
||||
return i;
|
||||
}
|
||||
fprintf(stderr, "Error: bad fold definition token \"%s\" at line %d\n", p, lineno);
|
||||
exit(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Parse a fold rule. */
|
||||
static void foldrule(char *p)
|
||||
{
|
||||
uint32_t op = nexttoken(&p, 0, 0);
|
||||
uint32_t left = nexttoken(&p, 0, 0x7f);
|
||||
uint32_t right = nexttoken(&p, 1, 0x3ff);
|
||||
uint32_t key = (funcidx << 24) | (op << 17) | (left << 10) | right;
|
||||
uint32_t i;
|
||||
if (nkeys >= BUILD_MAX_FOLD) {
|
||||
fprintf(stderr, "Error: too many fold rules, increase BUILD_MAX_FOLD.\n");
|
||||
exit(1);
|
||||
}
|
||||
/* Simple insertion sort to detect duplicates. */
|
||||
for (i = nkeys; i > 0; i--) {
|
||||
if ((foldkeys[i-1]&0xffffff) < (key & 0xffffff))
|
||||
break;
|
||||
if ((foldkeys[i-1]&0xffffff) == (key & 0xffffff)) {
|
||||
fprintf(stderr, "Error: duplicate fold definition at line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
foldkeys[i] = foldkeys[i-1];
|
||||
}
|
||||
foldkeys[i] = key;
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
/* Emit C source code for IR folding hash table. */
|
||||
void emit_fold(BuildCtx *ctx)
|
||||
{
|
||||
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
||||
const char *fname = ctx->args[0];
|
||||
FILE *fp;
|
||||
|
||||
if (fname == NULL) {
|
||||
fprintf(stderr, "Error: missing input filename\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (fname[0] == '-' && fname[1] == '\0') {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
||||
fname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
fprintf(ctx->fp, "static const FoldFunc fold_func[] = {\n");
|
||||
|
||||
lineno = 0;
|
||||
funcidx = 0;
|
||||
nkeys = 0;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
lineno++;
|
||||
/* The prefix must be at the start of a line, otherwise it's ignored. */
|
||||
if (!strncmp(buf, FOLDDEF_PREFIX, sizeof(FOLDDEF_PREFIX)-1)) {
|
||||
char *p = buf+sizeof(FOLDDEF_PREFIX)-1;
|
||||
char *q = strchr(p, ')');
|
||||
if (p[0] == '(' && q) {
|
||||
p++;
|
||||
*q = '\0';
|
||||
foldrule(p);
|
||||
} else if ((p[0] == 'F' || p[0] == 'X') && p[1] == '(' && q) {
|
||||
p += 2;
|
||||
*q = '\0';
|
||||
if (funcidx)
|
||||
fprintf(ctx->fp, ",\n");
|
||||
if (p[-2] == 'X')
|
||||
fprintf(ctx->fp, " %s", p);
|
||||
else
|
||||
fprintf(ctx->fp, " fold_%s", p);
|
||||
funcidx++;
|
||||
} else {
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
fprintf(stderr, "Error: unknown fold definition tag %s%s at line %d\n",
|
||||
FOLDDEF_PREFIX, p, lineno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
|
||||
makehash(ctx);
|
||||
}
|
||||
|
||||
Vendored
+398
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
** LuaJIT VM builder: library definition compiler.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "buildvm.h"
|
||||
#include "lj_obj.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* Context for library definitions. */
|
||||
static uint8_t obuf[8192];
|
||||
static uint8_t *optr;
|
||||
static char modname[80];
|
||||
static size_t modnamelen;
|
||||
static char funcname[80];
|
||||
static int modstate, regfunc;
|
||||
static int ffid, recffid, ffasmfunc;
|
||||
|
||||
enum {
|
||||
REGFUNC_OK,
|
||||
REGFUNC_NOREG,
|
||||
REGFUNC_NOREGUV
|
||||
};
|
||||
|
||||
static void libdef_name(const char *p, int kind)
|
||||
{
|
||||
size_t n = strlen(p);
|
||||
if (kind != LIBINIT_STRING) {
|
||||
if (n > modnamelen && p[modnamelen] == '_' &&
|
||||
!strncmp(p, modname, modnamelen)) {
|
||||
p += modnamelen+1;
|
||||
n -= modnamelen+1;
|
||||
}
|
||||
}
|
||||
if (n > LIBINIT_MAXSTR) {
|
||||
fprintf(stderr, "Error: string too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
if (optr+1+n+2 > obuf+sizeof(obuf)) { /* +2 for caller. */
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = (uint8_t)(n | kind);
|
||||
memcpy(optr, p, n);
|
||||
optr += n;
|
||||
}
|
||||
|
||||
static void libdef_endmodule(BuildCtx *ctx)
|
||||
{
|
||||
if (modstate != 0) {
|
||||
char line[80];
|
||||
const uint8_t *p;
|
||||
int n;
|
||||
if (modstate == 1)
|
||||
fprintf(ctx->fp, " (lua_CFunction)0");
|
||||
fprintf(ctx->fp, "\n};\n");
|
||||
fprintf(ctx->fp, "static const uint8_t %s%s[] = {\n",
|
||||
LABEL_PREFIX_LIBINIT, modname);
|
||||
line[0] = '\0';
|
||||
for (n = 0, p = obuf; p < optr; p++) {
|
||||
n += sprintf(line+n, "%d,", *p);
|
||||
if (n >= 75) {
|
||||
fprintf(ctx->fp, "%s\n", line);
|
||||
n = 0;
|
||||
line[0] = '\0';
|
||||
}
|
||||
}
|
||||
fprintf(ctx->fp, "%s%d\n};\n#endif\n\n", line, LIBINIT_END);
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_module(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
libdef_endmodule(ctx);
|
||||
optr = obuf;
|
||||
*optr++ = (uint8_t)ffid;
|
||||
*optr++ = (uint8_t)ffasmfunc;
|
||||
*optr++ = 0; /* Hash table size. */
|
||||
modstate = 1;
|
||||
fprintf(ctx->fp, "#ifdef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
||||
fprintf(ctx->fp, "#undef %sMODULE_%s\n", LIBDEF_PREFIX, p);
|
||||
fprintf(ctx->fp, "static const lua_CFunction %s%s[] = {\n",
|
||||
LABEL_PREFIX_LIBCF, p);
|
||||
}
|
||||
modnamelen = strlen(p);
|
||||
if (modnamelen > sizeof(modname)-1) {
|
||||
fprintf(stderr, "Error: module name too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(modname, p);
|
||||
}
|
||||
|
||||
static int find_ffofs(BuildCtx *ctx, const char *name)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ctx->nglob; i++) {
|
||||
const char *gl = ctx->globnames[i];
|
||||
if (gl[0] == 'f' && gl[1] == 'f' && gl[2] == '_' && !strcmp(gl+3, name)) {
|
||||
return (int)((uint8_t *)ctx->glob[i] - ctx->code);
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Error: undefined fast function %s%s\n",
|
||||
LABEL_PREFIX_FF, name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void libdef_func(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
if (arg != LIBINIT_CF)
|
||||
ffasmfunc++;
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
if (modstate == 0) {
|
||||
fprintf(stderr, "Error: no module for function definition %s\n", p);
|
||||
exit(1);
|
||||
}
|
||||
if (regfunc == REGFUNC_NOREG) {
|
||||
if (optr+1 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_FFID;
|
||||
} else {
|
||||
if (arg != LIBINIT_ASM_) {
|
||||
if (modstate != 1) fprintf(ctx->fp, ",\n");
|
||||
modstate = 2;
|
||||
fprintf(ctx->fp, " %s%s", arg ? LABEL_PREFIX_FFH : LABEL_PREFIX_CF, p);
|
||||
}
|
||||
if (regfunc != REGFUNC_NOREGUV) obuf[2]++; /* Bump hash table size. */
|
||||
libdef_name(regfunc == REGFUNC_NOREGUV ? "" : p, arg);
|
||||
}
|
||||
} else if (ctx->mode == BUILD_ffdef) {
|
||||
fprintf(ctx->fp, "FFDEF(%s)\n", p);
|
||||
} else if (ctx->mode == BUILD_recdef) {
|
||||
if (strlen(p) > sizeof(funcname)-1) {
|
||||
fprintf(stderr, "Error: function name too long: '%s'\n", p);
|
||||
exit(1);
|
||||
}
|
||||
strcpy(funcname, p);
|
||||
} else if (ctx->mode == BUILD_vmdef) {
|
||||
int i;
|
||||
for (i = 1; p[i] && modname[i-1]; i++)
|
||||
if (p[i] == '_') p[i] = '.';
|
||||
fprintf(ctx->fp, "\"%s\",\n", p);
|
||||
} else if (ctx->mode == BUILD_bcdef) {
|
||||
if (arg != LIBINIT_CF)
|
||||
fprintf(ctx->fp, ",\n%d", find_ffofs(ctx, p));
|
||||
}
|
||||
ffid++;
|
||||
regfunc = REGFUNC_OK;
|
||||
}
|
||||
|
||||
static uint32_t find_rec(char *name)
|
||||
{
|
||||
char *p = (char *)obuf;
|
||||
uint32_t n;
|
||||
for (n = 2; *p; n++) {
|
||||
if (strcmp(p, name) == 0)
|
||||
return n;
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
if (p+strlen(name)+1 >= (char *)obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
strcpy(p, name);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void libdef_rec(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_recdef) {
|
||||
char *q;
|
||||
uint32_t n;
|
||||
for (; recffid+1 < ffid; recffid++)
|
||||
fprintf(ctx->fp, ",\n0");
|
||||
recffid = ffid;
|
||||
if (*p == '.') p = funcname;
|
||||
q = strchr(p, ' ');
|
||||
if (q) *q++ = '\0';
|
||||
n = find_rec(p);
|
||||
if (q)
|
||||
fprintf(ctx->fp, ",\n0x%02x00+(%s)", n, q);
|
||||
else
|
||||
fprintf(ctx->fp, ",\n0x%02x00", n);
|
||||
}
|
||||
}
|
||||
|
||||
static void memcpy_endian(void *dst, void *src, size_t n)
|
||||
{
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b == LJ_ENDIAN_SELECT(1, 0)) {
|
||||
memcpy(dst, src, n);
|
||||
} else {
|
||||
size_t i;
|
||||
for (i = 0; i < n; i++)
|
||||
((uint8_t *)dst)[i] = ((uint8_t *)src)[n-i-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_push(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
int len = (int)strlen(p);
|
||||
if (*p == '"') {
|
||||
if (len > 1 && p[len-1] == '"') {
|
||||
p[len-1] = '\0';
|
||||
libdef_name(p+1, LIBINIT_STRING);
|
||||
return;
|
||||
}
|
||||
} else if (*p >= '0' && *p <= '9') {
|
||||
char *ep;
|
||||
double d = strtod(p, &ep);
|
||||
if (*ep == '\0') {
|
||||
if (optr+1+sizeof(double) > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_NUMBER;
|
||||
memcpy_endian(optr, &d, sizeof(double));
|
||||
optr += sizeof(double);
|
||||
return;
|
||||
}
|
||||
} else if (!strcmp(p, "lastcl")) {
|
||||
if (optr+1 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_LASTCL;
|
||||
return;
|
||||
} else if (len > 4 && !strncmp(p, "top-", 4)) {
|
||||
if (optr+2 > obuf+sizeof(obuf)) {
|
||||
fprintf(stderr, "Error: output buffer overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
*optr++ = LIBINIT_COPY;
|
||||
*optr++ = (uint8_t)atoi(p+4);
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "Error: bad value for %sPUSH(%s)\n", LIBDEF_PREFIX, p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_set(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(arg);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
if (p[0] == '!' && p[1] == '\0') p[0] = '\0'; /* Set env. */
|
||||
libdef_name(p, LIBINIT_STRING);
|
||||
*optr++ = LIBINIT_SET;
|
||||
obuf[2]++; /* Bump hash table size. */
|
||||
}
|
||||
}
|
||||
|
||||
static void libdef_regfunc(BuildCtx *ctx, char *p, int arg)
|
||||
{
|
||||
UNUSED(ctx); UNUSED(p);
|
||||
regfunc = arg;
|
||||
}
|
||||
|
||||
typedef void (*LibDefFunc)(BuildCtx *ctx, char *p, int arg);
|
||||
|
||||
typedef struct LibDefHandler {
|
||||
const char *suffix;
|
||||
const char *stop;
|
||||
const LibDefFunc func;
|
||||
const int arg;
|
||||
} LibDefHandler;
|
||||
|
||||
static const LibDefHandler libdef_handlers[] = {
|
||||
{ "MODULE_", " \t\r\n", libdef_module, 0 },
|
||||
{ "CF(", ")", libdef_func, LIBINIT_CF },
|
||||
{ "ASM(", ")", libdef_func, LIBINIT_ASM },
|
||||
{ "ASM_(", ")", libdef_func, LIBINIT_ASM_ },
|
||||
{ "REC(", ")", libdef_rec, 0 },
|
||||
{ "PUSH(", ")", libdef_push, 0 },
|
||||
{ "SET(", ")", libdef_set, 0 },
|
||||
{ "NOREGUV", NULL, libdef_regfunc, REGFUNC_NOREGUV },
|
||||
{ "NOREG", NULL, libdef_regfunc, REGFUNC_NOREG },
|
||||
{ NULL, NULL, (LibDefFunc)0, 0 }
|
||||
};
|
||||
|
||||
/* Emit C source code for library function definitions. */
|
||||
void emit_lib(BuildCtx *ctx)
|
||||
{
|
||||
const char *fname;
|
||||
|
||||
if (ctx->mode == BUILD_ffdef || ctx->mode == BUILD_libdef ||
|
||||
ctx->mode == BUILD_recdef)
|
||||
fprintf(ctx->fp, "/* This is a generated file. DO NOT EDIT! */\n\n");
|
||||
else if (ctx->mode == BUILD_vmdef)
|
||||
fprintf(ctx->fp, "ffnames = {\n[0]=\"Lua\",\n\"C\",\n");
|
||||
if (ctx->mode == BUILD_recdef)
|
||||
fprintf(ctx->fp, "static const uint16_t recff_idmap[] = {\n0,\n0x0100");
|
||||
recffid = ffid = FF_C+1;
|
||||
ffasmfunc = 0;
|
||||
|
||||
while ((fname = *ctx->args++)) {
|
||||
char buf[256]; /* We don't care about analyzing lines longer than that. */
|
||||
FILE *fp;
|
||||
if (fname[0] == '-' && fname[1] == '\0') {
|
||||
fp = stdin;
|
||||
} else {
|
||||
fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open input file '%s': %s\n",
|
||||
fname, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
modstate = 0;
|
||||
regfunc = REGFUNC_OK;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
char *p;
|
||||
/* Simplistic pre-processor. Only handles top-level #if/#endif. */
|
||||
if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
||||
int ok = 1;
|
||||
if (!strcmp(buf, "#if LJ_52\n"))
|
||||
ok = LJ_52;
|
||||
else if (!strcmp(buf, "#if LJ_HASJIT\n"))
|
||||
ok = LJ_HASJIT;
|
||||
else if (!strcmp(buf, "#if LJ_HASFFI\n"))
|
||||
ok = LJ_HASFFI;
|
||||
if (!ok) {
|
||||
int lvl = 1;
|
||||
while (fgets(buf, sizeof(buf), fp) != NULL) {
|
||||
if (buf[0] == '#' && buf[1] == 'e' && buf[2] == 'n') {
|
||||
if (--lvl == 0) break;
|
||||
} else if (buf[0] == '#' && buf[1] == 'i' && buf[2] == 'f') {
|
||||
lvl++;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (p = buf; (p = strstr(p, LIBDEF_PREFIX)) != NULL; ) {
|
||||
const LibDefHandler *ldh;
|
||||
p += sizeof(LIBDEF_PREFIX)-1;
|
||||
for (ldh = libdef_handlers; ldh->suffix != NULL; ldh++) {
|
||||
size_t n, len = strlen(ldh->suffix);
|
||||
if (!strncmp(p, ldh->suffix, len)) {
|
||||
p += len;
|
||||
n = ldh->stop ? strcspn(p, ldh->stop) : 0;
|
||||
if (!p[n]) break;
|
||||
p[n] = '\0';
|
||||
ldh->func(ctx, p, ldh->arg);
|
||||
p += n+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ldh->suffix == NULL) {
|
||||
buf[strlen(buf)-1] = '\0';
|
||||
fprintf(stderr, "Error: unknown library definition tag %s%s\n",
|
||||
LIBDEF_PREFIX, p);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
if (ctx->mode == BUILD_libdef) {
|
||||
libdef_endmodule(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->mode == BUILD_ffdef) {
|
||||
fprintf(ctx->fp, "\n#undef FFDEF\n\n");
|
||||
fprintf(ctx->fp,
|
||||
"#ifndef FF_NUM_ASMFUNC\n#define FF_NUM_ASMFUNC %d\n#endif\n\n",
|
||||
ffasmfunc);
|
||||
} else if (ctx->mode == BUILD_vmdef) {
|
||||
fprintf(ctx->fp, "}\n\n");
|
||||
} else if (ctx->mode == BUILD_bcdef) {
|
||||
int i;
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
fprintf(ctx->fp, "LJ_DATADEF const uint16_t lj_bc_mode[] = {\n");
|
||||
fprintf(ctx->fp, "BCDEF(BCMODE)\n");
|
||||
for (i = ffasmfunc-1; i > 0; i--)
|
||||
fprintf(ctx->fp, "BCMODE_FF,\n");
|
||||
fprintf(ctx->fp, "BCMODE_FF\n};\n\n");
|
||||
} else if (ctx->mode == BUILD_recdef) {
|
||||
char *p = (char *)obuf;
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
fprintf(ctx->fp, "static const RecordFunc recff_func[] = {\n"
|
||||
"recff_nyi,\n"
|
||||
"recff_c");
|
||||
while (*p) {
|
||||
fprintf(ctx->fp, ",\nrecff_%s", p);
|
||||
p += strlen(p)+1;
|
||||
}
|
||||
fprintf(ctx->fp, "\n};\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+368
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
** LuaJIT VM builder: PE object emitter.
|
||||
** Copyright (C) 2005-2013 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 || LJ_TARGET_PPC
|
||||
|
||||
/* Context for PE object emitter. */
|
||||
static char *strtab;
|
||||
static size_t strtabofs;
|
||||
|
||||
/* -- PE object definitions ----------------------------------------------- */
|
||||
|
||||
/* PE header. */
|
||||
typedef struct PEheader {
|
||||
uint16_t arch;
|
||||
uint16_t nsects;
|
||||
uint32_t time;
|
||||
uint32_t symtabofs;
|
||||
uint32_t nsyms;
|
||||
uint16_t opthdrsz;
|
||||
uint16_t flags;
|
||||
} PEheader;
|
||||
|
||||
/* PE section. */
|
||||
typedef struct PEsection {
|
||||
char name[8];
|
||||
uint32_t vsize;
|
||||
uint32_t vaddr;
|
||||
uint32_t size;
|
||||
uint32_t ofs;
|
||||
uint32_t relocofs;
|
||||
uint32_t lineofs;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
|
||||
/* PE relocation. */
|
||||
typedef struct PEreloc {
|
||||
uint32_t vaddr;
|
||||
uint32_t symidx;
|
||||
uint16_t type;
|
||||
} PEreloc;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_RELOC_SIZE (4+4+2)
|
||||
|
||||
/* PE symbol table entry. */
|
||||
typedef struct PEsym {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
} n;
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl;
|
||||
uint8_t naux;
|
||||
} PEsym;
|
||||
|
||||
/* PE symbol table auxiliary entry for a section. */
|
||||
typedef struct PEsymaux {
|
||||
uint32_t size;
|
||||
uint16_t nreloc;
|
||||
uint16_t nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel;
|
||||
uint8_t unused[3];
|
||||
} PEsymaux;
|
||||
|
||||
/* Cannot use sizeof, because it pads up to the max. alignment. */
|
||||
#define PEOBJ_SYM_SIZE (8+4+2+2+1+1)
|
||||
|
||||
/* PE object CPU specific defines. */
|
||||
#if LJ_TARGET_X86
|
||||
#define PEOBJ_ARCH_TARGET 0x014c
|
||||
#define PEOBJ_RELOC_REL32 0x14 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x06
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#elif LJ_TARGET_X64
|
||||
#define PEOBJ_ARCH_TARGET 0x8664
|
||||
#define PEOBJ_RELOC_REL32 0x04 /* MS: REL32, GNU: DISP32. */
|
||||
#define PEOBJ_RELOC_DIR32 0x02
|
||||
#define PEOBJ_RELOC_ADDR32NB 0x03
|
||||
#define PEOBJ_RELOC_OFS 0
|
||||
#define PEOBJ_TEXT_FLAGS 0x60500020 /* 60=r+x, 50=align16, 20=code. */
|
||||
#elif LJ_TARGET_PPC
|
||||
#define PEOBJ_ARCH_TARGET 0x01f2
|
||||
#define PEOBJ_RELOC_REL32 0x06
|
||||
#define PEOBJ_RELOC_DIR32 0x02
|
||||
#define PEOBJ_RELOC_OFS (-4)
|
||||
#define PEOBJ_TEXT_FLAGS 0x60400020 /* 60=r+x, 40=align8, 20=code. */
|
||||
#endif
|
||||
|
||||
/* Section numbers (0-based). */
|
||||
enum {
|
||||
PEOBJ_SECT_ABS = -2,
|
||||
PEOBJ_SECT_UNDEF = -1,
|
||||
PEOBJ_SECT_TEXT,
|
||||
#if LJ_TARGET_X64
|
||||
PEOBJ_SECT_PDATA,
|
||||
PEOBJ_SECT_XDATA,
|
||||
#endif
|
||||
PEOBJ_SECT_RDATA_Z,
|
||||
PEOBJ_NSECTIONS
|
||||
};
|
||||
|
||||
/* Symbol types. */
|
||||
#define PEOBJ_TYPE_NULL 0
|
||||
#define PEOBJ_TYPE_FUNC 0x20
|
||||
|
||||
/* Symbol storage class. */
|
||||
#define PEOBJ_SCL_EXTERN 2
|
||||
#define PEOBJ_SCL_STATIC 3
|
||||
|
||||
/* -- PE object emitter --------------------------------------------------- */
|
||||
|
||||
/* Emit PE object symbol. */
|
||||
static void emit_peobj_sym(BuildCtx *ctx, const char *name, uint32_t value,
|
||||
int sect, int type, int scl)
|
||||
{
|
||||
PEsym sym;
|
||||
size_t len = strlen(name);
|
||||
if (!strtab) { /* Pass 1: only calculate string table length. */
|
||||
if (len > 8) strtabofs += len+1;
|
||||
return;
|
||||
}
|
||||
if (len <= 8) {
|
||||
memcpy(sym.n.name, name, len);
|
||||
memset(sym.n.name+len, 0, 8-len);
|
||||
} else {
|
||||
sym.n.nameref[0] = 0;
|
||||
sym.n.nameref[1] = (uint32_t)strtabofs;
|
||||
memcpy(strtab + strtabofs, name, len);
|
||||
strtab[strtabofs+len] = 0;
|
||||
strtabofs += len+1;
|
||||
}
|
||||
sym.value = value;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = (uint16_t)type;
|
||||
sym.scl = (uint8_t)scl;
|
||||
sym.naux = 0;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit PE object section symbol. */
|
||||
static void emit_peobj_sym_sect(BuildCtx *ctx, PEsection *pesect, int sect)
|
||||
{
|
||||
PEsym sym;
|
||||
PEsymaux aux;
|
||||
if (!strtab) return; /* Pass 1: no output. */
|
||||
memcpy(sym.n.name, pesect[sect].name, 8);
|
||||
sym.value = 0;
|
||||
sym.sect = (int16_t)(sect+1); /* 1-based section number. */
|
||||
sym.type = PEOBJ_TYPE_NULL;
|
||||
sym.scl = PEOBJ_SCL_STATIC;
|
||||
sym.naux = 1;
|
||||
owrite(ctx, &sym, PEOBJ_SYM_SIZE);
|
||||
memset(&aux, 0, sizeof(PEsymaux));
|
||||
aux.size = pesect[sect].size;
|
||||
aux.nreloc = pesect[sect].nreloc;
|
||||
owrite(ctx, &aux, PEOBJ_SYM_SIZE);
|
||||
}
|
||||
|
||||
/* Emit Windows PE object file. */
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
PEheader pehdr;
|
||||
PEsection pesect[PEOBJ_NSECTIONS];
|
||||
uint32_t sofs;
|
||||
int i, nrsym;
|
||||
union { uint8_t b; uint32_t u; } host_endian;
|
||||
|
||||
sofs = sizeof(PEheader) + PEOBJ_NSECTIONS*sizeof(PEsection);
|
||||
|
||||
/* Fill in PE sections. */
|
||||
memset(&pesect, 0, PEOBJ_NSECTIONS*sizeof(PEsection));
|
||||
memcpy(pesect[PEOBJ_SECT_TEXT].name, ".text", sizeof(".text")-1);
|
||||
pesect[PEOBJ_SECT_TEXT].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].size = (uint32_t)ctx->codesz);
|
||||
pesect[PEOBJ_SECT_TEXT].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_TEXT].nreloc = (uint16_t)ctx->nreloc) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 60 = read+execute, 50 = align16, 20 = code. */
|
||||
pesect[PEOBJ_SECT_TEXT].flags = PEOBJ_TEXT_FLAGS;
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
memcpy(pesect[PEOBJ_SECT_PDATA].name, ".pdata", sizeof(".pdata")-1);
|
||||
pesect[PEOBJ_SECT_PDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].size = 6*4);
|
||||
pesect[PEOBJ_SECT_PDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_PDATA].nreloc = 6) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_PDATA].flags = 0x40300040;
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_XDATA].name, ".xdata", sizeof(".xdata")-1);
|
||||
pesect[PEOBJ_SECT_XDATA].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].size = 8*2+4+6*2); /* See below. */
|
||||
pesect[PEOBJ_SECT_XDATA].relocofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE;
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_XDATA].flags = 0x40300040;
|
||||
#endif
|
||||
|
||||
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
|
||||
pesect[PEOBJ_SECT_RDATA_Z].ofs = sofs;
|
||||
sofs += (pesect[PEOBJ_SECT_RDATA_Z].size = (uint32_t)strlen(ctx->dasm_ident)+1);
|
||||
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */
|
||||
pesect[PEOBJ_SECT_RDATA_Z].flags = 0x40300040;
|
||||
|
||||
/* Fill in PE header. */
|
||||
pehdr.arch = PEOBJ_ARCH_TARGET;
|
||||
pehdr.nsects = PEOBJ_NSECTIONS;
|
||||
pehdr.time = 0; /* Timestamp is optional. */
|
||||
pehdr.symtabofs = sofs;
|
||||
pehdr.opthdrsz = 0;
|
||||
pehdr.flags = 0;
|
||||
|
||||
/* Compute the size of the symbol table:
|
||||
** @feat.00 + nsections*2
|
||||
** + asm_start + nsym
|
||||
** + nrsym
|
||||
*/
|
||||
nrsym = ctx->nrelocsym;
|
||||
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
|
||||
#if LJ_TARGET_X64
|
||||
pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win64. */
|
||||
#endif
|
||||
|
||||
/* Write PE object header and all sections. */
|
||||
owrite(ctx, &pehdr, sizeof(PEheader));
|
||||
owrite(ctx, &pesect, sizeof(PEsection)*PEOBJ_NSECTIONS);
|
||||
|
||||
/* Write .text section. */
|
||||
host_endian.u = 1;
|
||||
if (host_endian.b != LJ_ENDIAN_SELECT(1, 0)) {
|
||||
#if LJ_TARGET_PPC
|
||||
uint32_t *p = (uint32_t *)ctx->code;
|
||||
int n = (int)(ctx->codesz >> 2);
|
||||
for (i = 0; i < n; i++, p++)
|
||||
*p = lj_bswap(*p); /* Byteswap .text section. */
|
||||
#else
|
||||
fprintf(stderr, "Error: different byte order for host and target\n");
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
owrite(ctx, ctx->code, ctx->codesz);
|
||||
for (i = 0; i < ctx->nreloc; i++) {
|
||||
PEreloc reloc;
|
||||
reloc.vaddr = (uint32_t)ctx->reloc[i].ofs + PEOBJ_RELOC_OFS;
|
||||
reloc.symidx = 1+2+ctx->reloc[i].sym; /* Reloc syms are after .text sym. */
|
||||
reloc.type = ctx->reloc[i].type ? PEOBJ_RELOC_REL32 : PEOBJ_RELOC_DIR32;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
{ /* Write .pdata section. */
|
||||
uint32_t fcofs = (uint32_t)ctx->sym[ctx->nsym-1].ofs;
|
||||
uint32_t pdata[3]; /* Start of .text, end of .text and .xdata. */
|
||||
PEreloc reloc;
|
||||
pdata[0] = 0; pdata[1] = fcofs; pdata[2] = 0;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
pdata[0] = fcofs; pdata[1] = (uint32_t)ctx->codesz; pdata[2] = 20;
|
||||
owrite(ctx, &pdata, sizeof(pdata));
|
||||
reloc.vaddr = 0; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 4; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 8; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 12; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 16; reloc.symidx = 1+2+nrsym+2+2+1;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
reloc.vaddr = 20; reloc.symidx = 1+2+nrsym+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
{ /* Write .xdata section. */
|
||||
uint16_t xdata[8+2+6];
|
||||
PEreloc reloc;
|
||||
xdata[0] = 0x01|0x08|0x10; /* Ver. 1, uhandler/ehandler, prolog size 0. */
|
||||
xdata[1] = 0x0005; /* Number of unwind codes, no frame pointer. */
|
||||
xdata[2] = 0x4200; /* Stack offset 4*8+8 = aword*5. */
|
||||
xdata[3] = 0x3000; /* Push rbx. */
|
||||
xdata[4] = 0x6000; /* Push rsi. */
|
||||
xdata[5] = 0x7000; /* Push rdi. */
|
||||
xdata[6] = 0x5000; /* Push rbp. */
|
||||
xdata[7] = 0; /* Alignment. */
|
||||
xdata[8] = xdata[9] = 0; /* Relocated address of exception handler. */
|
||||
xdata[10] = 0x01; /* Ver. 1, no handler, prolog size 0. */
|
||||
xdata[11] = 0x1504; /* Number of unwind codes, fp = rbp, fpofs = 16. */
|
||||
xdata[12] = 0x0300; /* set_fpreg. */
|
||||
xdata[13] = 0x0200; /* stack offset 0*8+8 = aword*1. */
|
||||
xdata[14] = 0x3000; /* Push rbx. */
|
||||
xdata[15] = 0x5000; /* Push rbp. */
|
||||
owrite(ctx, &xdata, sizeof(xdata));
|
||||
reloc.vaddr = 2*8; reloc.symidx = 1+2+nrsym+2+2;
|
||||
reloc.type = PEOBJ_RELOC_ADDR32NB;
|
||||
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Write .rdata$Z section. */
|
||||
owrite(ctx, ctx->dasm_ident, strlen(ctx->dasm_ident)+1);
|
||||
|
||||
/* Write symbol table. */
|
||||
strtab = NULL; /* 1st pass: collect string sizes. */
|
||||
for (;;) {
|
||||
strtabofs = 4;
|
||||
/* Mark as SafeSEH compliant. */
|
||||
emit_peobj_sym(ctx, "@feat.00", 1,
|
||||
PEOBJ_SECT_ABS, PEOBJ_TYPE_NULL, PEOBJ_SCL_STATIC);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_TEXT);
|
||||
for (i = 0; i < nrsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->relocsym[i], 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
#if LJ_TARGET_X64
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
|
||||
emit_peobj_sym(ctx, "lj_err_unwind_win64", 0,
|
||||
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
#endif
|
||||
|
||||
emit_peobj_sym(ctx, ctx->beginsym, 0,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_NULL, PEOBJ_SCL_EXTERN);
|
||||
for (i = 0; i < ctx->nsym; i++)
|
||||
emit_peobj_sym(ctx, ctx->sym[i].name, (uint32_t)ctx->sym[i].ofs,
|
||||
PEOBJ_SECT_TEXT, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
|
||||
|
||||
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_RDATA_Z);
|
||||
|
||||
if (strtab)
|
||||
break;
|
||||
/* 2nd pass: alloc strtab, write syms and copy strings. */
|
||||
strtab = (char *)malloc(strtabofs);
|
||||
*(uint32_t *)strtab = (uint32_t)strtabofs;
|
||||
}
|
||||
|
||||
/* Write string table. */
|
||||
owrite(ctx, strtab, strtabofs);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void emit_peobj(BuildCtx *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
fprintf(stderr, "Error: no PE object support for this target\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+427
@@ -0,0 +1,427 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Lua script to generate a customized, minified version of Lua.
|
||||
-- The resulting 'minilua' is used for the build process of LuaJIT.
|
||||
----------------------------------------------------------------------------
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
local sub, match, gsub = string.sub, string.match, string.gsub
|
||||
|
||||
local LUA_VERSION = "5.1.5"
|
||||
local LUA_SOURCE
|
||||
|
||||
local function usage()
|
||||
io.stderr:write("Usage: ", arg and arg[0] or "genminilua",
|
||||
" lua-", LUA_VERSION, "-source-dir\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function find_sources()
|
||||
LUA_SOURCE = arg and arg[1]
|
||||
if not LUA_SOURCE then usage() end
|
||||
if sub(LUA_SOURCE, -1) ~= "/" then LUA_SOURCE = LUA_SOURCE.."/" end
|
||||
local fp = io.open(LUA_SOURCE .. "lua.h")
|
||||
if not fp then
|
||||
LUA_SOURCE = LUA_SOURCE.."src/"
|
||||
fp = io.open(LUA_SOURCE .. "lua.h")
|
||||
if not fp then usage() end
|
||||
end
|
||||
local all = fp:read("*a")
|
||||
fp:close()
|
||||
if not match(all, 'LUA_RELEASE%s*"Lua '..LUA_VERSION..'"') then
|
||||
io.stderr:write("Error: version mismatch\n")
|
||||
usage()
|
||||
end
|
||||
end
|
||||
|
||||
local LUA_FILES = {
|
||||
"lmem.c", "lobject.c", "ltm.c", "lfunc.c", "ldo.c", "lstring.c", "ltable.c",
|
||||
"lgc.c", "lstate.c", "ldebug.c", "lzio.c", "lopcodes.c",
|
||||
"llex.c", "lcode.c", "lparser.c", "lvm.c", "lapi.c", "lauxlib.c",
|
||||
"lbaselib.c", "ltablib.c", "liolib.c", "loslib.c", "lstrlib.c", "linit.c",
|
||||
}
|
||||
|
||||
local REMOVE_LIB = {}
|
||||
gsub([[
|
||||
collectgarbage dofile gcinfo getfenv getmetatable load print rawequal rawset
|
||||
select tostring xpcall
|
||||
foreach foreachi getn maxn setn
|
||||
popen tmpfile seek setvbuf __tostring
|
||||
clock date difftime execute getenv rename setlocale time tmpname
|
||||
dump gfind len reverse
|
||||
LUA_LOADLIBNAME LUA_MATHLIBNAME LUA_DBLIBNAME
|
||||
]], "%S+", function(name)
|
||||
REMOVE_LIB[name] = true
|
||||
end)
|
||||
|
||||
local REMOVE_EXTINC = { ["<assert.h>"] = true, ["<locale.h>"] = true, }
|
||||
|
||||
local CUSTOM_MAIN = [[
|
||||
typedef unsigned int UB;
|
||||
static UB barg(lua_State *L,int idx){
|
||||
union{lua_Number n;U64 b;}bn;
|
||||
bn.n=lua_tonumber(L,idx)+6755399441055744.0;
|
||||
if (bn.n==0.0&&!lua_isnumber(L,idx))luaL_typerror(L,idx,"number");
|
||||
return(UB)bn.b;
|
||||
}
|
||||
#define BRET(b) lua_pushnumber(L,(lua_Number)(int)(b));return 1;
|
||||
static int tobit(lua_State *L){
|
||||
BRET(barg(L,1))}
|
||||
static int bnot(lua_State *L){
|
||||
BRET(~barg(L,1))}
|
||||
static int band(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b&=barg(L,i);BRET(b)}
|
||||
static int bor(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b|=barg(L,i);BRET(b)}
|
||||
static int bxor(lua_State *L){
|
||||
int i;UB b=barg(L,1);for(i=lua_gettop(L);i>1;i--)b^=barg(L,i);BRET(b)}
|
||||
static int lshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b<<n)}
|
||||
static int rshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET(b>>n)}
|
||||
static int arshift(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((int)b>>n)}
|
||||
static int rol(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b<<n)|(b>>(32-n)))}
|
||||
static int ror(lua_State *L){
|
||||
UB b=barg(L,1),n=barg(L,2)&31;BRET((b>>n)|(b<<(32-n)))}
|
||||
static int bswap(lua_State *L){
|
||||
UB b=barg(L,1);b=(b>>24)|((b>>8)&0xff00)|((b&0xff00)<<8)|(b<<24);BRET(b)}
|
||||
static int tohex(lua_State *L){
|
||||
UB b=barg(L,1);
|
||||
int n=lua_isnone(L,2)?8:(int)barg(L,2);
|
||||
const char *hexdigits="0123456789abcdef";
|
||||
char buf[8];
|
||||
int i;
|
||||
if(n<0){n=-n;hexdigits="0123456789ABCDEF";}
|
||||
if(n>8)n=8;
|
||||
for(i=(int)n;--i>=0;){buf[i]=hexdigits[b&15];b>>=4;}
|
||||
lua_pushlstring(L,buf,(size_t)n);
|
||||
return 1;
|
||||
}
|
||||
static const struct luaL_Reg bitlib[] = {
|
||||
{"tobit",tobit},
|
||||
{"bnot",bnot},
|
||||
{"band",band},
|
||||
{"bor",bor},
|
||||
{"bxor",bxor},
|
||||
{"lshift",lshift},
|
||||
{"rshift",rshift},
|
||||
{"arshift",arshift},
|
||||
{"rol",rol},
|
||||
{"ror",ror},
|
||||
{"bswap",bswap},
|
||||
{"tohex",tohex},
|
||||
{NULL,NULL}
|
||||
};
|
||||
int main(int argc, char **argv){
|
||||
lua_State *L = luaL_newstate();
|
||||
int i;
|
||||
luaL_openlibs(L);
|
||||
luaL_register(L, "bit", bitlib);
|
||||
if (argc < 2) return sizeof(void *);
|
||||
lua_createtable(L, 0, 1);
|
||||
lua_pushstring(L, argv[1]);
|
||||
lua_rawseti(L, -2, 0);
|
||||
lua_setglobal(L, "arg");
|
||||
if (luaL_loadfile(L, argv[1]))
|
||||
goto err;
|
||||
for (i = 2; i < argc; i++)
|
||||
lua_pushstring(L, argv[i]);
|
||||
if (lua_pcall(L, argc - 2, 0, 0)) {
|
||||
err:
|
||||
fprintf(stderr, "Error: %s\n", lua_tostring(L, -1));
|
||||
return 1;
|
||||
}
|
||||
lua_close(L);
|
||||
return 0;
|
||||
}
|
||||
]]
|
||||
|
||||
local function read_sources()
|
||||
local t = {}
|
||||
for i, name in ipairs(LUA_FILES) do
|
||||
local fp = assert(io.open(LUA_SOURCE..name, "r"))
|
||||
t[i] = fp:read("*a")
|
||||
assert(fp:close())
|
||||
end
|
||||
t[#t+1] = CUSTOM_MAIN
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local includes = {}
|
||||
|
||||
local function merge_includes(src)
|
||||
return gsub(src, '#include%s*"([^"]*)"%s*\n', function(name)
|
||||
if includes[name] then return "" end
|
||||
includes[name] = true
|
||||
local fp = assert(io.open(LUA_SOURCE..name, "r"))
|
||||
local src = fp:read("*a")
|
||||
assert(fp:close())
|
||||
src = gsub(src, "#ifndef%s+%w+_h\n#define%s+%w+_h\n", "")
|
||||
src = gsub(src, "#endif%s*$", "")
|
||||
return merge_includes(src)
|
||||
end)
|
||||
end
|
||||
|
||||
local function get_license(src)
|
||||
return match(src, "/%*+\n%* Copyright %(.-%*/\n")
|
||||
end
|
||||
|
||||
local function fold_lines(src)
|
||||
return gsub(src, "\\\n", " ")
|
||||
end
|
||||
|
||||
local strings = {}
|
||||
|
||||
local function save_str(str)
|
||||
local n = #strings+1
|
||||
strings[n] = str
|
||||
return "\1"..n.."\2"
|
||||
end
|
||||
|
||||
local function save_strings(src)
|
||||
src = gsub(src, '"[^"\n]*"', save_str)
|
||||
return gsub(src, "'[^'\n]*'", save_str)
|
||||
end
|
||||
|
||||
local function restore_strings(src)
|
||||
return gsub(src, "\1(%d+)\2", function(numstr)
|
||||
return strings[tonumber(numstr)]
|
||||
end)
|
||||
end
|
||||
|
||||
local function def_istrue(def)
|
||||
return def == "INT_MAX > 2147483640L" or
|
||||
def == "LUAI_BITSINT >= 32" or
|
||||
def == "SIZE_Bx < LUAI_BITSINT-1" or
|
||||
def == "cast" or
|
||||
def == "defined(LUA_CORE)" or
|
||||
def == "MINSTRTABSIZE" or
|
||||
def == "LUA_MINBUFFER" or
|
||||
def == "HARDSTACKTESTS" or
|
||||
def == "UNUSED"
|
||||
end
|
||||
|
||||
local head, defs = {[[
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int64 U64;
|
||||
#else
|
||||
typedef unsigned long long U64;
|
||||
#endif
|
||||
]]}, {}
|
||||
|
||||
local function preprocess(src)
|
||||
local t = { match(src, "^(.-)#") }
|
||||
local lvl, on, oldon = 0, true, {}
|
||||
for pp, def, txt in string.gmatch(src, "#(%w+) *([^\n]*)\n([^#]*)") do
|
||||
if pp == "if" or pp == "ifdef" or pp == "ifndef" then
|
||||
lvl = lvl + 1
|
||||
oldon[lvl] = on
|
||||
on = def_istrue(def)
|
||||
elseif pp == "else" then
|
||||
if oldon[lvl] then
|
||||
if on == false then on = true else on = false end
|
||||
end
|
||||
elseif pp == "elif" then
|
||||
if oldon[lvl] then
|
||||
on = def_istrue(def)
|
||||
end
|
||||
elseif pp == "endif" then
|
||||
on = oldon[lvl]
|
||||
lvl = lvl - 1
|
||||
elseif on then
|
||||
if pp == "include" then
|
||||
if not head[def] and not REMOVE_EXTINC[def] then
|
||||
head[def] = true
|
||||
head[#head+1] = "#include "..def.."\n"
|
||||
end
|
||||
elseif pp == "define" then
|
||||
local k, sp, v = match(def, "([%w_]+)(%s*)(.*)")
|
||||
if k and not (sp == "" and sub(v, 1, 1) == "(") then
|
||||
defs[k] = gsub(v, "%a[%w_]*", function(tok)
|
||||
return defs[tok] or tok
|
||||
end)
|
||||
else
|
||||
t[#t+1] = "#define "..def.."\n"
|
||||
end
|
||||
elseif pp ~= "undef" then
|
||||
error("unexpected directive: "..pp.." "..def)
|
||||
end
|
||||
end
|
||||
if on then t[#t+1] = txt end
|
||||
end
|
||||
return gsub(table.concat(t), "%a[%w_]*", function(tok)
|
||||
return defs[tok] or tok
|
||||
end)
|
||||
end
|
||||
|
||||
local function merge_header(src, license)
|
||||
local hdr = string.format([[
|
||||
/* This is a heavily customized and minimized copy of Lua %s. */
|
||||
/* It's only used to build LuaJIT. It does NOT have all standard functions! */
|
||||
]], LUA_VERSION)
|
||||
return hdr..license..table.concat(head)..src
|
||||
end
|
||||
|
||||
local function strip_unused1(src)
|
||||
return gsub(src, '( {"?([%w_]+)"?,%s+%a[%w_]*},\n)', function(line, func)
|
||||
return REMOVE_LIB[func] and "" or line
|
||||
end)
|
||||
end
|
||||
|
||||
local function strip_unused2(src)
|
||||
return gsub(src, "Symbolic Execution.-}=", "")
|
||||
end
|
||||
|
||||
local function strip_unused3(src)
|
||||
src = gsub(src, "extern", "static")
|
||||
src = gsub(src, "\nstatic([^\n]-)%(([^)]*)%)%(", "\nstatic%1 %2(")
|
||||
src = gsub(src, "#define lua_assert[^\n]*\n", "")
|
||||
src = gsub(src, "lua_assert%b();?", "")
|
||||
src = gsub(src, "default:\n}", "default:;\n}")
|
||||
src = gsub(src, "lua_lock%b();", "")
|
||||
src = gsub(src, "lua_unlock%b();", "")
|
||||
src = gsub(src, "luai_threadyield%b();", "")
|
||||
src = gsub(src, "luai_userstateopen%b();", "{}")
|
||||
src = gsub(src, "luai_userstate%w+%b();", "")
|
||||
src = gsub(src, "%(%(c==.*luaY_parser%)", "luaY_parser")
|
||||
src = gsub(src, "trydecpoint%(ls,seminfo%)",
|
||||
"luaX_lexerror(ls,\"malformed number\",TK_NUMBER)")
|
||||
src = gsub(src, "int c=luaZ_lookahead%b();", "")
|
||||
src = gsub(src, "luaL_register%(L,[^,]*,co_funcs%);\nreturn 2;",
|
||||
"return 1;")
|
||||
src = gsub(src, "getfuncname%b():", "NULL:")
|
||||
src = gsub(src, "getobjname%b():", "NULL:")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)\n[^\n]*\n", "")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*%)%b{}\n", "")
|
||||
src = gsub(src, "if%([^\n]*hookmask[^\n]*&&\n[^\n]*%b{}\n", "")
|
||||
src = gsub(src, "(twoto%b()%()", "%1(size_t)")
|
||||
src = gsub(src, "i<sizenode", "i<(int)sizenode")
|
||||
return gsub(src, "\n\n+", "\n")
|
||||
end
|
||||
|
||||
local function strip_comments(src)
|
||||
return gsub(src, "/%*.-%*/", " ")
|
||||
end
|
||||
|
||||
local function strip_whitespace(src)
|
||||
src = gsub(src, "^%s+", "")
|
||||
src = gsub(src, "%s*\n%s*", "\n")
|
||||
src = gsub(src, "[ \t]+", " ")
|
||||
src = gsub(src, "(%W) ", "%1")
|
||||
return gsub(src, " (%W)", "%1")
|
||||
end
|
||||
|
||||
local function rename_tokens1(src)
|
||||
src = gsub(src, "getline", "getline_")
|
||||
src = gsub(src, "struct ([%w_]+)", "ZX%1")
|
||||
return gsub(src, "union ([%w_]+)", "ZY%1")
|
||||
end
|
||||
|
||||
local function rename_tokens2(src)
|
||||
src = gsub(src, "ZX([%w_]+)", "struct %1")
|
||||
return gsub(src, "ZY([%w_]+)", "union %1")
|
||||
end
|
||||
|
||||
local function func_gather(src)
|
||||
local nodes, list = {}, {}
|
||||
local pos, len = 1, #src
|
||||
while pos < len do
|
||||
local d, w = match(src, "^(#define ([%w_]+)[^\n]*\n)", pos)
|
||||
if d then
|
||||
local n = #list+1
|
||||
list[n] = d
|
||||
nodes[w] = n
|
||||
else
|
||||
local s
|
||||
d, w, s = match(src, "^(([%w_]+)[^\n]*([{;])\n)", pos)
|
||||
if not d then
|
||||
d, w, s = match(src, "^(([%w_]+)[^(]*%b()([{;])\n)", pos)
|
||||
if not d then d = match(src, "^[^\n]*\n", pos) end
|
||||
end
|
||||
if s == "{" then
|
||||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
|
||||
if sub(d, -2) == "{\n" then
|
||||
d = d..sub(match(src, "^%b{}[^;\n]*;?\n", pos+#d-2), 3)
|
||||
end
|
||||
end
|
||||
local k, v = nil, d
|
||||
if w == "typedef" then
|
||||
if match(d, "^typedef enum") then
|
||||
head[#head+1] = d
|
||||
else
|
||||
k = match(d, "([%w_]+);\n$")
|
||||
if not k then k = match(d, "^.-%(.-([%w_]+)%)%(") end
|
||||
end
|
||||
elseif w == "enum" then
|
||||
head[#head+1] = v
|
||||
elseif w ~= nil then
|
||||
k = match(d, "^[^\n]-([%w_]+)[(%[=]")
|
||||
if k then
|
||||
if w ~= "static" and k ~= "main" then v = "static "..d end
|
||||
else
|
||||
k = w
|
||||
end
|
||||
end
|
||||
if w and k then
|
||||
local o = nodes[k]
|
||||
if o then nodes["*"..k] = o end
|
||||
local n = #list+1
|
||||
list[n] = v
|
||||
nodes[k] = n
|
||||
end
|
||||
end
|
||||
pos = pos + #d
|
||||
end
|
||||
return nodes, list
|
||||
end
|
||||
|
||||
local function func_visit(nodes, list, used, n)
|
||||
local i = nodes[n]
|
||||
for m in string.gmatch(list[i], "[%w_]+") do
|
||||
if nodes[m] then
|
||||
local j = used[m]
|
||||
if not j then
|
||||
used[m] = i
|
||||
func_visit(nodes, list, used, m)
|
||||
elseif i < j then
|
||||
used[m] = i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function func_collect(src)
|
||||
local nodes, list = func_gather(src)
|
||||
local used = {}
|
||||
func_visit(nodes, list, used, "main")
|
||||
for n,i in pairs(nodes) do
|
||||
local j = used[n]
|
||||
if j and j < i then used["*"..n] = j end
|
||||
end
|
||||
for n,i in pairs(nodes) do
|
||||
if not used[n] then list[i] = "" end
|
||||
end
|
||||
return table.concat(list)
|
||||
end
|
||||
|
||||
find_sources()
|
||||
local src = read_sources()
|
||||
src = merge_includes(src)
|
||||
local license = get_license(src)
|
||||
src = fold_lines(src)
|
||||
src = strip_unused1(src)
|
||||
src = save_strings(src)
|
||||
src = strip_unused2(src)
|
||||
src = strip_comments(src)
|
||||
src = preprocess(src)
|
||||
src = strip_whitespace(src)
|
||||
src = strip_unused3(src)
|
||||
src = rename_tokens1(src)
|
||||
src = func_collect(src)
|
||||
src = rename_tokens2(src)
|
||||
src = restore_strings(src)
|
||||
src = merge_header(src, license)
|
||||
io.write(src)
|
||||
Vendored
+7769
File diff suppressed because it is too large
Load Diff
Vendored
+191
@@ -0,0 +1,191 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT bytecode listing module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module lists the bytecode of a Lua function. If it's loaded by -jbc
|
||||
-- it hooks into the parser and lists all functions of a chunk as they
|
||||
-- are parsed.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jbc -e 'local x=0; for i=1,1e6 do x=x+i end; print(x)'
|
||||
-- luajit -jbc=- foo.lua
|
||||
-- luajit -jbc=foo.list foo.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_LISTFILE. The file is overwritten every time the module
|
||||
-- is started.
|
||||
--
|
||||
-- This module can also be used programmatically:
|
||||
--
|
||||
-- local bc = require("jit.bc")
|
||||
--
|
||||
-- local function foo() print("hello") end
|
||||
--
|
||||
-- bc.dump(foo) --> -- BYTECODE -- [...]
|
||||
-- print(bc.line(foo, 2)) --> 0002 KSTR 1 1 ; "hello"
|
||||
--
|
||||
-- local out = {
|
||||
-- -- Do something with each line:
|
||||
-- write = function(t, ...) io.write(...) end,
|
||||
-- close = function(t) end,
|
||||
-- flush = function(t) end,
|
||||
-- }
|
||||
-- bc.dump(foo, out)
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local bit = require("bit")
|
||||
local sub, gsub, format = string.sub, string.gsub, string.format
|
||||
local byte, band, shr = string.byte, bit.band, bit.rshift
|
||||
local funcinfo, funcbc, funck = jutil.funcinfo, jutil.funcbc, jutil.funck
|
||||
local funcuvname = jutil.funcuvname
|
||||
local bcnames = vmdef.bcnames
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
-- Return one bytecode line.
|
||||
local function bcline(func, pc, prefix)
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then return end
|
||||
local ma, mb, mc = band(m, 7), band(m, 15*8), band(m, 15*128)
|
||||
local a = band(shr(ins, 8), 0xff)
|
||||
local oidx = 6*band(ins, 0xff)
|
||||
local op = sub(bcnames, oidx+1, oidx+6)
|
||||
local s = format("%04d %s %-6s %3s ",
|
||||
pc, prefix or " ", op, ma == 0 and "" or a)
|
||||
local d = shr(ins, 16)
|
||||
if mc == 13*128 then -- BCMjump
|
||||
return format("%s=> %04d\n", s, pc+d-0x7fff)
|
||||
end
|
||||
if mb ~= 0 then
|
||||
d = band(d, 0xff)
|
||||
elseif mc == 0 then
|
||||
return s.."\n"
|
||||
end
|
||||
local kc
|
||||
if mc == 10*128 then -- BCMstr
|
||||
kc = funck(func, -d-1)
|
||||
kc = format(#kc > 40 and '"%.40s"~' or '"%s"', gsub(kc, "%c", ctlsub))
|
||||
elseif mc == 9*128 then -- BCMnum
|
||||
kc = funck(func, d)
|
||||
if op == "TSETM " then kc = kc - 2^52 end
|
||||
elseif mc == 12*128 then -- BCMfunc
|
||||
local fi = funcinfo(funck(func, -d-1))
|
||||
if fi.ffid then
|
||||
kc = vmdef.ffnames[fi.ffid]
|
||||
else
|
||||
kc = fi.loc
|
||||
end
|
||||
elseif mc == 5*128 then -- BCMuv
|
||||
kc = funcuvname(func, d)
|
||||
end
|
||||
if ma == 5 then -- BCMuv
|
||||
local ka = funcuvname(func, a)
|
||||
if kc then kc = ka.." ; "..kc else kc = ka end
|
||||
end
|
||||
if mb ~= 0 then
|
||||
local b = shr(ins, 24)
|
||||
if kc then return format("%s%3d %3d ; %s\n", s, b, d, kc) end
|
||||
return format("%s%3d %3d\n", s, b, d)
|
||||
end
|
||||
if kc then return format("%s%3d ; %s\n", s, d, kc) end
|
||||
if mc == 7*128 and d > 32767 then d = d - 65536 end -- BCMlits
|
||||
return format("%s%3d\n", s, d)
|
||||
end
|
||||
|
||||
-- Collect branch targets of a function.
|
||||
local function bctargets(func)
|
||||
local target = {}
|
||||
for pc=1,1000000000 do
|
||||
local ins, m = funcbc(func, pc)
|
||||
if not ins then break end
|
||||
if band(m, 15*128) == 13*128 then target[pc+shr(ins, 16)-0x7fff] = true end
|
||||
end
|
||||
return target
|
||||
end
|
||||
|
||||
-- Dump bytecode instructions of a function.
|
||||
local function bcdump(func, out, all)
|
||||
if not out then out = stdout end
|
||||
local fi = funcinfo(func)
|
||||
if all and fi.children then
|
||||
for n=-1,-1000000000,-1 do
|
||||
local k = funck(func, n)
|
||||
if not k then break end
|
||||
if type(k) == "proto" then bcdump(k, out, true) end
|
||||
end
|
||||
end
|
||||
out:write(format("-- BYTECODE -- %s-%d\n", fi.loc, fi.lastlinedefined))
|
||||
local target = bctargets(func)
|
||||
for pc=1,1000000000 do
|
||||
local s = bcline(func, pc, target[pc] and "=>")
|
||||
if not s then break end
|
||||
out:write(s)
|
||||
end
|
||||
out:write("\n")
|
||||
out:flush()
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
-- List handler.
|
||||
local function h_list(func)
|
||||
return bcdump(func, out)
|
||||
end
|
||||
|
||||
-- Detach list handler.
|
||||
local function bclistoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(h_list)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach list handler.
|
||||
local function bcliston(outfile)
|
||||
if active then bclistoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_LISTFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(h_list, "bc")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
line = bcline
|
||||
dump = bcdump
|
||||
targets = bctargets
|
||||
|
||||
on = bcliston
|
||||
off = bclistoff
|
||||
start = bcliston -- For -j command line option.
|
||||
|
||||
Vendored
+659
@@ -0,0 +1,659 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT module to save/list bytecode.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module saves or lists the bytecode for an input file.
|
||||
-- It's run by the -b command line option.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
|
||||
local bit = require("bit")
|
||||
|
||||
-- Symbol name prefix for LuaJIT bytecode.
|
||||
local LJBC_PREFIX = "luaJIT_BC_"
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function usage()
|
||||
io.stderr:write[[
|
||||
Save LuaJIT bytecode: luajit -b[options] input output
|
||||
-l Only list bytecode.
|
||||
-s Strip debug info (default).
|
||||
-g Keep debug info.
|
||||
-n name Set module name (default: auto-detect from input name).
|
||||
-t type Set output file type (default: auto-detect from output name).
|
||||
-a arch Override architecture for object files (default: native).
|
||||
-o os Override OS for object files (default: native).
|
||||
-e chunk Use chunk string as input.
|
||||
-- Stop handling options.
|
||||
- Use stdin as input and/or stdout as output.
|
||||
|
||||
File types: c h obj o raw (default)
|
||||
]]
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function check(ok, ...)
|
||||
if ok then return ok, ... end
|
||||
io.stderr:write("luajit: ", ...)
|
||||
io.stderr:write("\n")
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
local function readfile(input)
|
||||
if type(input) == "function" then return input end
|
||||
if input == "-" then input = nil end
|
||||
return check(loadfile(input))
|
||||
end
|
||||
|
||||
local function savefile(name, mode)
|
||||
if name == "-" then return io.stdout end
|
||||
return check(io.open(name, mode))
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_type = {
|
||||
raw = "raw", c = "c", h = "h", o = "obj", obj = "obj",
|
||||
}
|
||||
|
||||
local map_arch = {
|
||||
x86 = true, x64 = true, arm = true, ppc = true, ppcspe = true,
|
||||
mips = true, mipsel = true,
|
||||
}
|
||||
|
||||
local map_os = {
|
||||
linux = true, windows = true, osx = true, freebsd = true, netbsd = true,
|
||||
openbsd = true, solaris = true,
|
||||
}
|
||||
|
||||
local function checkarg(str, map, err)
|
||||
str = string.lower(str)
|
||||
local s = check(map[str], "unknown ", err)
|
||||
return s == true and str or s
|
||||
end
|
||||
|
||||
local function detecttype(str)
|
||||
local ext = string.match(string.lower(str), "%.(%a+)$")
|
||||
return map_type[ext] or "raw"
|
||||
end
|
||||
|
||||
local function checkmodname(str)
|
||||
check(string.match(str, "^[%w_.%-]+$"), "bad module name")
|
||||
return string.gsub(str, "[%.%-]", "_")
|
||||
end
|
||||
|
||||
local function detectmodname(str)
|
||||
if type(str) == "string" then
|
||||
local tail = string.match(str, "[^/\\]+$")
|
||||
if tail then str = tail end
|
||||
local head = string.match(str, "^(.*)%.[^.]*$")
|
||||
if head then str = head end
|
||||
str = string.match(str, "^[%w_.%-]+")
|
||||
else
|
||||
str = nil
|
||||
end
|
||||
check(str, "cannot derive module name, use -n name")
|
||||
return string.gsub(str, "[%.%-]", "_")
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bcsave_tail(fp, output, s)
|
||||
local ok, err = fp:write(s)
|
||||
if ok and output ~= "-" then ok, err = fp:close() end
|
||||
check(ok, "cannot write ", output, ": ", err)
|
||||
end
|
||||
|
||||
local function bcsave_raw(output, s)
|
||||
local fp = savefile(output, "wb")
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_c(ctx, output, s)
|
||||
local fp = savefile(output, "w")
|
||||
if ctx.type == "c" then
|
||||
fp:write(string.format([[
|
||||
#ifdef _cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
__declspec(dllexport)
|
||||
#endif
|
||||
const char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname))
|
||||
else
|
||||
fp:write(string.format([[
|
||||
#define %s%s_SIZE %d
|
||||
static const char %s%s[] = {
|
||||
]], LJBC_PREFIX, ctx.modname, #s, LJBC_PREFIX, ctx.modname))
|
||||
end
|
||||
local t, n, m = {}, 0, 0
|
||||
for i=1,#s do
|
||||
local b = tostring(string.byte(s, i))
|
||||
m = m + #b + 1
|
||||
if m > 78 then
|
||||
fp:write(table.concat(t, ",", 1, n), ",\n")
|
||||
n, m = 0, #b + 1
|
||||
end
|
||||
n = n + 1
|
||||
t[n] = b
|
||||
end
|
||||
bcsave_tail(fp, output, table.concat(t, ",", 1, n).."\n};\n")
|
||||
end
|
||||
|
||||
local function bcsave_elfobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint32_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF32header;
|
||||
typedef struct {
|
||||
uint8_t emagic[4], eclass, eendian, eversion, eosabi, eabiversion, epad[7];
|
||||
uint16_t type, machine;
|
||||
uint32_t version;
|
||||
uint64_t entry, phofs, shofs;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize, phentsize, phnum, shentsize, shnum, shstridx;
|
||||
} ELF64header;
|
||||
typedef struct {
|
||||
uint32_t name, type, flags, addr, ofs, size, link, info, align, entsize;
|
||||
} ELF32sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, type;
|
||||
uint64_t flags, addr, ofs, size;
|
||||
uint32_t link, info;
|
||||
uint64_t align, entsize;
|
||||
} ELF64sectheader;
|
||||
typedef struct {
|
||||
uint32_t name, value, size;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
} ELF32symbol;
|
||||
typedef struct {
|
||||
uint32_t name;
|
||||
uint8_t info, other;
|
||||
uint16_t sectidx;
|
||||
uint64_t value, size;
|
||||
} ELF64symbol;
|
||||
typedef struct {
|
||||
ELF32header hdr;
|
||||
ELF32sectheader sect[6];
|
||||
ELF32symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF32obj;
|
||||
typedef struct {
|
||||
ELF64header hdr;
|
||||
ELF64sectheader sect[6];
|
||||
ELF64symbol sym[2];
|
||||
uint8_t space[4096];
|
||||
} ELF64obj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local is64, isbe = false, false
|
||||
if ctx.arch == "x64" then
|
||||
is64 = true
|
||||
elseif ctx.arch == "ppc" or ctx.arch == "ppcspe" or ctx.arch == "mips" then
|
||||
isbe = true
|
||||
end
|
||||
|
||||
-- Handle different host/target endianess.
|
||||
local function f32(x) return x end
|
||||
local f16, fofs = f32, f32
|
||||
if ffi.abi("be") ~= isbe then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
if is64 then
|
||||
local two32 = ffi.cast("int64_t", 2^32)
|
||||
function fofs(x) return bit.bswap(x)*two32 end
|
||||
else
|
||||
fofs = f32
|
||||
end
|
||||
end
|
||||
|
||||
-- Create ELF object and fill in header.
|
||||
local o = ffi.new(is64 and "ELF64obj" or "ELF32obj")
|
||||
local hdr = o.hdr
|
||||
if ctx.os == "bsd" or ctx.os == "other" then -- Determine native hdr.eosabi.
|
||||
local bf = assert(io.open("/bin/ls", "rb"))
|
||||
local bs = bf:read(9)
|
||||
bf:close()
|
||||
ffi.copy(o, bs, 9)
|
||||
check(hdr.emagic[0] == 127, "no support for writing native object files")
|
||||
else
|
||||
hdr.emagic = "\127ELF"
|
||||
hdr.eosabi = ({ freebsd=9, netbsd=2, openbsd=12, solaris=6 })[ctx.os] or 0
|
||||
end
|
||||
hdr.eclass = is64 and 2 or 1
|
||||
hdr.eendian = isbe and 2 or 1
|
||||
hdr.eversion = 1
|
||||
hdr.type = f16(1)
|
||||
hdr.machine = f16(({ x86=3, x64=62, arm=40, ppc=20, ppcspe=20, mips=8, mipsel=8 })[ctx.arch])
|
||||
if ctx.arch == "mips" or ctx.arch == "mipsel" then
|
||||
hdr.flags = 0x50001006
|
||||
end
|
||||
hdr.version = f32(1)
|
||||
hdr.shofs = fofs(ffi.offsetof(o, "sect"))
|
||||
hdr.ehsize = f16(ffi.sizeof(hdr))
|
||||
hdr.shentsize = f16(ffi.sizeof(o.sect[0]))
|
||||
hdr.shnum = f16(6)
|
||||
hdr.shstridx = f16(2)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
local sofs, ofs = ffi.offsetof(o, "space"), 1
|
||||
for i,name in ipairs{
|
||||
".symtab", ".shstrtab", ".strtab", ".rodata", ".note.GNU-stack",
|
||||
} do
|
||||
local sect = o.sect[i]
|
||||
sect.align = fofs(1)
|
||||
sect.name = f32(ofs)
|
||||
ffi.copy(o.space+ofs, name)
|
||||
ofs = ofs + #name+1
|
||||
end
|
||||
o.sect[1].type = f32(2) -- .symtab
|
||||
o.sect[1].link = f32(3)
|
||||
o.sect[1].info = f32(1)
|
||||
o.sect[1].align = fofs(8)
|
||||
o.sect[1].ofs = fofs(ffi.offsetof(o, "sym"))
|
||||
o.sect[1].entsize = fofs(ffi.sizeof(o.sym[0]))
|
||||
o.sect[1].size = fofs(ffi.sizeof(o.sym))
|
||||
o.sym[1].name = f32(1)
|
||||
o.sym[1].sectidx = f16(4)
|
||||
o.sym[1].size = fofs(#s)
|
||||
o.sym[1].info = 17
|
||||
o.sect[2].type = f32(3) -- .shstrtab
|
||||
o.sect[2].ofs = fofs(sofs)
|
||||
o.sect[2].size = fofs(ofs)
|
||||
o.sect[3].type = f32(3) -- .strtab
|
||||
o.sect[3].ofs = fofs(sofs + ofs)
|
||||
o.sect[3].size = fofs(#symname+1)
|
||||
ffi.copy(o.space+ofs+1, symname)
|
||||
ofs = ofs + #symname + 2
|
||||
o.sect[4].type = f32(1) -- .rodata
|
||||
o.sect[4].flags = fofs(2)
|
||||
o.sect[4].ofs = fofs(sofs + ofs)
|
||||
o.sect[4].size = fofs(#s)
|
||||
o.sect[5].type = f32(1) -- .note.GNU-stack
|
||||
o.sect[5].ofs = fofs(sofs + ofs + #s)
|
||||
|
||||
-- Write ELF object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_peobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct {
|
||||
uint16_t arch, nsects;
|
||||
uint32_t time, symtabofs, nsyms;
|
||||
uint16_t opthdrsz, flags;
|
||||
} PEheader;
|
||||
typedef struct {
|
||||
char name[8];
|
||||
uint32_t vsize, vaddr, size, ofs, relocofs, lineofs;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t flags;
|
||||
} PEsection;
|
||||
typedef struct __attribute((packed)) {
|
||||
union {
|
||||
char name[8];
|
||||
uint32_t nameref[2];
|
||||
};
|
||||
uint32_t value;
|
||||
int16_t sect;
|
||||
uint16_t type;
|
||||
uint8_t scl, naux;
|
||||
} PEsym;
|
||||
typedef struct __attribute((packed)) {
|
||||
uint32_t size;
|
||||
uint16_t nreloc, nline;
|
||||
uint32_t cksum;
|
||||
uint16_t assoc;
|
||||
uint8_t comdatsel, unused[3];
|
||||
} PEsymaux;
|
||||
typedef struct {
|
||||
PEheader hdr;
|
||||
PEsection sect[2];
|
||||
// Must be an even number of symbol structs.
|
||||
PEsym sym0;
|
||||
PEsymaux sym0aux;
|
||||
PEsym sym1;
|
||||
PEsymaux sym1aux;
|
||||
PEsym sym2;
|
||||
PEsym sym3;
|
||||
uint32_t strtabsize;
|
||||
uint8_t space[4096];
|
||||
} PEobj;
|
||||
]]
|
||||
local symname = LJBC_PREFIX..ctx.modname
|
||||
local is64 = false
|
||||
if ctx.arch == "x86" then
|
||||
symname = "_"..symname
|
||||
elseif ctx.arch == "x64" then
|
||||
is64 = true
|
||||
end
|
||||
local symexport = " /EXPORT:"..symname..",DATA "
|
||||
|
||||
-- The file format is always little-endian. Swap if the host is big-endian.
|
||||
local function f32(x) return x end
|
||||
local f16 = f32
|
||||
if ffi.abi("be") then
|
||||
f32 = bit.bswap
|
||||
function f16(x) return bit.rshift(bit.bswap(x), 16) end
|
||||
end
|
||||
|
||||
-- Create PE object and fill in header.
|
||||
local o = ffi.new("PEobj")
|
||||
local hdr = o.hdr
|
||||
hdr.arch = f16(({ x86=0x14c, x64=0x8664, arm=0x1c0, ppc=0x1f2, mips=0x366, mipsel=0x366 })[ctx.arch])
|
||||
hdr.nsects = f16(2)
|
||||
hdr.symtabofs = f32(ffi.offsetof(o, "sym0"))
|
||||
hdr.nsyms = f32(6)
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
o.sect[0].name = ".drectve"
|
||||
o.sect[0].size = f32(#symexport)
|
||||
o.sect[0].flags = f32(0x00100a00)
|
||||
o.sym0.sect = f16(1)
|
||||
o.sym0.scl = 3
|
||||
o.sym0.name = ".drectve"
|
||||
o.sym0.naux = 1
|
||||
o.sym0aux.size = f32(#symexport)
|
||||
o.sect[1].name = ".rdata"
|
||||
o.sect[1].size = f32(#s)
|
||||
o.sect[1].flags = f32(0x40300040)
|
||||
o.sym1.sect = f16(2)
|
||||
o.sym1.scl = 3
|
||||
o.sym1.name = ".rdata"
|
||||
o.sym1.naux = 1
|
||||
o.sym1aux.size = f32(#s)
|
||||
o.sym2.sect = f16(2)
|
||||
o.sym2.scl = 2
|
||||
o.sym2.nameref[1] = f32(4)
|
||||
o.sym3.sect = f16(-1)
|
||||
o.sym3.scl = 2
|
||||
o.sym3.value = f32(1)
|
||||
o.sym3.name = "@feat.00" -- Mark as SafeSEH compliant.
|
||||
ffi.copy(o.space, symname)
|
||||
local ofs = #symname + 1
|
||||
o.strtabsize = f32(ofs + 4)
|
||||
o.sect[0].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
ffi.copy(o.space + ofs, symexport)
|
||||
ofs = ofs + #symexport
|
||||
o.sect[1].ofs = f32(ffi.offsetof(o, "space") + ofs)
|
||||
|
||||
-- Write PE object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, ffi.sizeof(o)-4096+ofs))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_machobj(ctx, output, s, ffi)
|
||||
ffi.cdef[[
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags;
|
||||
} mach_header;
|
||||
typedef struct
|
||||
{
|
||||
mach_header; uint32_t reserved;
|
||||
} mach_header_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize;
|
||||
char segname[16];
|
||||
uint32_t vmaddr, vmsize, fileoff, filesize;
|
||||
uint32_t maxprot, initprot, nsects, flags;
|
||||
} mach_segment_command;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize;
|
||||
char segname[16];
|
||||
uint64_t vmaddr, vmsize, fileoff, filesize;
|
||||
uint32_t maxprot, initprot, nsects, flags;
|
||||
} mach_segment_command_64;
|
||||
typedef struct {
|
||||
char sectname[16], segname[16];
|
||||
uint32_t addr, size;
|
||||
uint32_t offset, align, reloff, nreloc, flags;
|
||||
uint32_t reserved1, reserved2;
|
||||
} mach_section;
|
||||
typedef struct {
|
||||
char sectname[16], segname[16];
|
||||
uint64_t addr, size;
|
||||
uint32_t offset, align, reloff, nreloc, flags;
|
||||
uint32_t reserved1, reserved2, reserved3;
|
||||
} mach_section_64;
|
||||
typedef struct {
|
||||
uint32_t cmd, cmdsize, symoff, nsyms, stroff, strsize;
|
||||
} mach_symtab_command;
|
||||
typedef struct {
|
||||
int32_t strx;
|
||||
uint8_t type, sect;
|
||||
int16_t desc;
|
||||
uint32_t value;
|
||||
} mach_nlist;
|
||||
typedef struct {
|
||||
uint32_t strx;
|
||||
uint8_t type, sect;
|
||||
uint16_t desc;
|
||||
uint64_t value;
|
||||
} mach_nlist_64;
|
||||
typedef struct
|
||||
{
|
||||
uint32_t magic, nfat_arch;
|
||||
} mach_fat_header;
|
||||
typedef struct
|
||||
{
|
||||
uint32_t cputype, cpusubtype, offset, size, align;
|
||||
} mach_fat_arch;
|
||||
typedef struct {
|
||||
struct {
|
||||
mach_header hdr;
|
||||
mach_segment_command seg;
|
||||
mach_section sec;
|
||||
mach_symtab_command sym;
|
||||
} arch[1];
|
||||
mach_nlist sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_obj;
|
||||
typedef struct {
|
||||
struct {
|
||||
mach_header_64 hdr;
|
||||
mach_segment_command_64 seg;
|
||||
mach_section_64 sec;
|
||||
mach_symtab_command sym;
|
||||
} arch[1];
|
||||
mach_nlist_64 sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_obj_64;
|
||||
typedef struct {
|
||||
mach_fat_header fat;
|
||||
mach_fat_arch fat_arch[4];
|
||||
struct {
|
||||
mach_header hdr;
|
||||
mach_segment_command seg;
|
||||
mach_section sec;
|
||||
mach_symtab_command sym;
|
||||
} arch[4];
|
||||
mach_nlist sym_entry;
|
||||
uint8_t space[4096];
|
||||
} mach_fat_obj;
|
||||
]]
|
||||
local symname = '_'..LJBC_PREFIX..ctx.modname
|
||||
local isfat, is64, align, mobj = false, false, 4, "mach_obj"
|
||||
if ctx.arch == "x64" then
|
||||
is64, align, mobj = true, 8, "mach_obj_64"
|
||||
elseif ctx.arch == "arm" then
|
||||
isfat, mobj = true, "mach_fat_obj"
|
||||
else
|
||||
check(ctx.arch == "x86", "unsupported architecture for OSX")
|
||||
end
|
||||
local function aligned(v, a) return bit.band(v+a-1, -a) end
|
||||
local be32 = bit.bswap -- Mach-O FAT is BE, supported archs are LE.
|
||||
|
||||
-- Create Mach-O object and fill in header.
|
||||
local o = ffi.new(mobj)
|
||||
local mach_size = aligned(ffi.offsetof(o, "space")+#symname+2, align)
|
||||
local cputype = ({ x86={7}, x64={0x01000007}, arm={7,12,12,12} })[ctx.arch]
|
||||
local cpusubtype = ({ x86={3}, x64={3}, arm={3,6,9,11} })[ctx.arch]
|
||||
if isfat then
|
||||
o.fat.magic = be32(0xcafebabe)
|
||||
o.fat.nfat_arch = be32(#cpusubtype)
|
||||
end
|
||||
|
||||
-- Fill in sections and symbols.
|
||||
for i=0,#cpusubtype-1 do
|
||||
local ofs = 0
|
||||
if isfat then
|
||||
local a = o.fat_arch[i]
|
||||
a.cputype = be32(cputype[i+1])
|
||||
a.cpusubtype = be32(cpusubtype[i+1])
|
||||
-- Subsequent slices overlap each other to share data.
|
||||
ofs = ffi.offsetof(o, "arch") + i*ffi.sizeof(o.arch[0])
|
||||
a.offset = be32(ofs)
|
||||
a.size = be32(mach_size-ofs+#s)
|
||||
end
|
||||
local a = o.arch[i]
|
||||
a.hdr.magic = is64 and 0xfeedfacf or 0xfeedface
|
||||
a.hdr.cputype = cputype[i+1]
|
||||
a.hdr.cpusubtype = cpusubtype[i+1]
|
||||
a.hdr.filetype = 1
|
||||
a.hdr.ncmds = 2
|
||||
a.hdr.sizeofcmds = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)+ffi.sizeof(a.sym)
|
||||
a.seg.cmd = is64 and 0x19 or 0x1
|
||||
a.seg.cmdsize = ffi.sizeof(a.seg)+ffi.sizeof(a.sec)
|
||||
a.seg.vmsize = #s
|
||||
a.seg.fileoff = mach_size-ofs
|
||||
a.seg.filesize = #s
|
||||
a.seg.maxprot = 1
|
||||
a.seg.initprot = 1
|
||||
a.seg.nsects = 1
|
||||
ffi.copy(a.sec.sectname, "__data")
|
||||
ffi.copy(a.sec.segname, "__DATA")
|
||||
a.sec.size = #s
|
||||
a.sec.offset = mach_size-ofs
|
||||
a.sym.cmd = 2
|
||||
a.sym.cmdsize = ffi.sizeof(a.sym)
|
||||
a.sym.symoff = ffi.offsetof(o, "sym_entry")-ofs
|
||||
a.sym.nsyms = 1
|
||||
a.sym.stroff = ffi.offsetof(o, "sym_entry")+ffi.sizeof(o.sym_entry)-ofs
|
||||
a.sym.strsize = aligned(#symname+2, align)
|
||||
end
|
||||
o.sym_entry.type = 0xf
|
||||
o.sym_entry.sect = 1
|
||||
o.sym_entry.strx = 1
|
||||
ffi.copy(o.space+1, symname)
|
||||
|
||||
-- Write Macho-O object file.
|
||||
local fp = savefile(output, "wb")
|
||||
fp:write(ffi.string(o, mach_size))
|
||||
bcsave_tail(fp, output, s)
|
||||
end
|
||||
|
||||
local function bcsave_obj(ctx, output, s)
|
||||
local ok, ffi = pcall(require, "ffi")
|
||||
check(ok, "FFI library required to write this file type")
|
||||
if ctx.os == "windows" then
|
||||
return bcsave_peobj(ctx, output, s, ffi)
|
||||
elseif ctx.os == "osx" then
|
||||
return bcsave_machobj(ctx, output, s, ffi)
|
||||
else
|
||||
return bcsave_elfobj(ctx, output, s, ffi)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local function bclist(input, output)
|
||||
local f = readfile(input)
|
||||
require("jit.bc").dump(f, savefile(output, "w"), true)
|
||||
end
|
||||
|
||||
local function bcsave(ctx, input, output)
|
||||
local f = readfile(input)
|
||||
local s = string.dump(f, ctx.strip)
|
||||
local t = ctx.type
|
||||
if not t then
|
||||
t = detecttype(output)
|
||||
ctx.type = t
|
||||
end
|
||||
if t == "raw" then
|
||||
bcsave_raw(output, s)
|
||||
else
|
||||
if not ctx.modname then ctx.modname = detectmodname(input) end
|
||||
if t == "obj" then
|
||||
bcsave_obj(ctx, output, s)
|
||||
else
|
||||
bcsave_c(ctx, output, s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function docmd(...)
|
||||
local arg = {...}
|
||||
local n = 1
|
||||
local list = false
|
||||
local ctx = {
|
||||
strip = true, arch = jit.arch, os = string.lower(jit.os),
|
||||
type = false, modname = false,
|
||||
}
|
||||
while n <= #arg do
|
||||
local a = arg[n]
|
||||
if type(a) == "string" and string.sub(a, 1, 1) == "-" and a ~= "-" then
|
||||
table.remove(arg, n)
|
||||
if a == "--" then break end
|
||||
for m=2,#a do
|
||||
local opt = string.sub(a, m, m)
|
||||
if opt == "l" then
|
||||
list = true
|
||||
elseif opt == "s" then
|
||||
ctx.strip = true
|
||||
elseif opt == "g" then
|
||||
ctx.strip = false
|
||||
else
|
||||
if arg[n] == nil or m ~= #a then usage() end
|
||||
if opt == "e" then
|
||||
if n ~= 1 then usage() end
|
||||
arg[1] = check(loadstring(arg[1]))
|
||||
elseif opt == "n" then
|
||||
ctx.modname = checkmodname(table.remove(arg, n))
|
||||
elseif opt == "t" then
|
||||
ctx.type = checkarg(table.remove(arg, n), map_type, "file type")
|
||||
elseif opt == "a" then
|
||||
ctx.arch = checkarg(table.remove(arg, n), map_arch, "architecture")
|
||||
elseif opt == "o" then
|
||||
ctx.os = checkarg(table.remove(arg, n), map_os, "OS name")
|
||||
else
|
||||
usage()
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
n = n + 1
|
||||
end
|
||||
end
|
||||
if list then
|
||||
if #arg == 0 or #arg > 2 then usage() end
|
||||
bclist(arg[1], arg[2] or "-")
|
||||
else
|
||||
if #arg ~= 2 then usage() end
|
||||
bcsave(ctx, arg[1], arg[2])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
start = docmd -- Process -b command line option.
|
||||
|
||||
Vendored
+689
@@ -0,0 +1,689 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT ARM disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles most user-mode ARMv7 instructions
|
||||
-- NYI: Advanced SIMD and VFP instructions.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, ror, tohex = bit.band, bit.bor, bit.ror, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_loadc = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFmDN", "vstmFNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrFdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushFdr", _ = "vstmdbFNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovFDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopFdr", _ = "vldmFNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrFdl", "vldmdbFNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
[11] = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGmDN", "vstmGNdr",
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vstrGdl",
|
||||
{ shift = 16, mask = 15, [13] = "vpushGdr", _ = "vstmdbGNdr", }
|
||||
},
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "vmovGDNm",
|
||||
{ shift = 16, mask = 15, [13] = "vpopGdr", _ = "vldmGNdr", },
|
||||
_ = {
|
||||
shift = 21, mask = 1,
|
||||
[0] = "vldrGdl", "vldmdbGNdr",
|
||||
},
|
||||
},
|
||||
},
|
||||
_ = {
|
||||
shift = 0, mask = 0 -- NYI ldc, mcrr, mrrc.
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfps = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaF.dnm", "vmlsF.dnm",
|
||||
[0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
|
||||
[0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
|
||||
[0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
|
||||
[0x20000] = "vdivF.dnm",
|
||||
[0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
|
||||
[0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
|
||||
[0x2c000] = "vmovF.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovF.dm", "vabsF.dm",
|
||||
[0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
|
||||
[0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
|
||||
[0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
|
||||
[0x0e01] = "vcvtG.dF.m",
|
||||
[0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
|
||||
[0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
|
||||
[0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
|
||||
},
|
||||
}
|
||||
|
||||
local map_vfpd = {
|
||||
shift = 6, mask = 0x2c001,
|
||||
[0] = "vmlaG.dnm", "vmlsG.dnm",
|
||||
[0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
|
||||
[0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
|
||||
[0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
|
||||
[0x20000] = "vdivG.dnm",
|
||||
[0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
|
||||
[0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
|
||||
[0x2c000] = "vmovG.dY",
|
||||
[0x2c001] = {
|
||||
shift = 7, mask = 0x1e01,
|
||||
[0] = "vmovG.dm", "vabsG.dm",
|
||||
[0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
|
||||
[0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
|
||||
[0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
|
||||
[0x0e01] = "vcvtF.dG.m",
|
||||
[0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
|
||||
[0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
|
||||
[0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
|
||||
},
|
||||
}
|
||||
|
||||
local map_datac = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = {
|
||||
shift = 8, mask = 15,
|
||||
[10] = map_vfps,
|
||||
[11] = map_vfpd,
|
||||
-- NYI cdp, mcr, mrc.
|
||||
},
|
||||
{
|
||||
shift = 8, mask = 15,
|
||||
[10] = {
|
||||
shift = 20, mask = 15,
|
||||
[0] = "vmovFnD", "vmovFDn",
|
||||
[14] = "vmsrD",
|
||||
[15] = { shift = 12, mask = 15, [15] = "vmrs", _ = "vmrsD", },
|
||||
},
|
||||
},
|
||||
},
|
||||
"svcT",
|
||||
}
|
||||
|
||||
local map_loadcu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP load/store.
|
||||
}
|
||||
|
||||
local map_datacu = {
|
||||
shift = 0, mask = 0, -- NYI unconditional CP data.
|
||||
}
|
||||
|
||||
local map_simddata = {
|
||||
shift = 0, mask = 0, -- NYI SIMD data.
|
||||
}
|
||||
|
||||
local map_simdload = {
|
||||
shift = 0, mask = 0, -- NYI SIMD load/store, preload.
|
||||
}
|
||||
|
||||
local map_preload = {
|
||||
shift = 0, mask = 0, -- NYI preload.
|
||||
}
|
||||
|
||||
local map_media = {
|
||||
shift = 20, mask = 31,
|
||||
[0] = false,
|
||||
{ --01
|
||||
shift = 5, mask = 7,
|
||||
[0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
|
||||
"sadd8DNM", false, false, "ssub8DNM",
|
||||
},
|
||||
{ --02
|
||||
shift = 5, mask = 7,
|
||||
[0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
|
||||
"qadd8DNM", false, false, "qsub8DNM",
|
||||
},
|
||||
{ --03
|
||||
shift = 5, mask = 7,
|
||||
[0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
|
||||
"shadd8DNM", false, false, "shsub8DNM",
|
||||
},
|
||||
false,
|
||||
{ --05
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
|
||||
"uadd8DNM", false, false, "usub8DNM",
|
||||
},
|
||||
{ --06
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
|
||||
"uqadd8DNM", false, false, "uqsub8DNM",
|
||||
},
|
||||
{ --07
|
||||
shift = 5, mask = 7,
|
||||
[0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
|
||||
"uhadd8DNM", false, false, "uhsub8DNM",
|
||||
},
|
||||
{ --08
|
||||
shift = 5, mask = 7,
|
||||
[0] = "pkhbtDNMU", false, "pkhtbDNMU",
|
||||
{ shift = 16, mask = 15, [15] = "sxtb16DMU", _ = "sxtab16DNMU", },
|
||||
"pkhbtDNMU", "selDNM", "pkhtbDNMU",
|
||||
},
|
||||
false,
|
||||
{ --0a
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxtbDMU", _ = "sxtabDNMU", },
|
||||
"ssatDxMu", false, "ssatDxMu",
|
||||
},
|
||||
{ --0b
|
||||
shift = 5, mask = 7,
|
||||
[0] = "ssatDxMu", "revDM", "ssatDxMu",
|
||||
{ shift = 16, mask = 15, [15] = "sxthDMU", _ = "sxtahDNMU", },
|
||||
"ssatDxMu", "rev16DM", "ssatDxMu",
|
||||
},
|
||||
{ --0c
|
||||
shift = 5, mask = 7,
|
||||
[3] = { shift = 16, mask = 15, [15] = "uxtb16DMU", _ = "uxtab16DNMU", },
|
||||
},
|
||||
false,
|
||||
{ --0e
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "usat16DwM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxtbDMU", _ = "uxtabDNMU", },
|
||||
"usatDwMu", false, "usatDwMu",
|
||||
},
|
||||
{ --0f
|
||||
shift = 5, mask = 7,
|
||||
[0] = "usatDwMu", "rbitDM", "usatDwMu",
|
||||
{ shift = 16, mask = 15, [15] = "uxthDMU", _ = "uxtahDNMU", },
|
||||
"usatDwMu", "revshDM", "usatDwMu",
|
||||
},
|
||||
{ --10
|
||||
shift = 12, mask = 15,
|
||||
[15] = {
|
||||
shift = 5, mask = 7,
|
||||
"smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
|
||||
},
|
||||
_ = {
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
|
||||
},
|
||||
},
|
||||
false, false, false,
|
||||
{ --14
|
||||
shift = 5, mask = 7,
|
||||
[0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
|
||||
},
|
||||
{ --15
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "smmulNMS", _ = "smmlaNMSD", },
|
||||
{ shift = 12, mask = 15, [15] = "smmulrNMS", _ = "smmlarNMSD", },
|
||||
false, false, false, false,
|
||||
"smmlsNMSD", "smmlsrNMSD",
|
||||
},
|
||||
false, false,
|
||||
{ --18
|
||||
shift = 5, mask = 7,
|
||||
[0] = { shift = 12, mask = 15, [15] = "usad8NMS", _ = "usada8NMSD", },
|
||||
},
|
||||
false,
|
||||
{ --1a
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1b
|
||||
shift = 5, mask = 3, [2] = "sbfxDMvw",
|
||||
},
|
||||
{ --1c
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1d
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 0, mask = 15, [15] = "bfcDvX", _ = "bfiDMvX", },
|
||||
},
|
||||
{ --1e
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
{ --1f
|
||||
shift = 5, mask = 3, [2] = "ubfxDMvw",
|
||||
},
|
||||
}
|
||||
|
||||
local map_load = {
|
||||
shift = 21, mask = 9,
|
||||
{
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 5,
|
||||
[0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
|
||||
}
|
||||
}
|
||||
|
||||
local map_load1 = {
|
||||
shift = 4, mask = 1,
|
||||
[0] = map_load, map_media,
|
||||
}
|
||||
|
||||
local map_loadm = {
|
||||
shift = 20, mask = 1,
|
||||
[0] = {
|
||||
shift = 23, mask = 3,
|
||||
[0] = "stmdaNR", "stmNR",
|
||||
{ shift = 16, mask = 63, [45] = "pushR", _ = "stmdbNR", }, "stmibNR",
|
||||
},
|
||||
{
|
||||
shift = 23, mask = 3,
|
||||
[0] = "ldmdaNR", { shift = 16, mask = 63, [61] = "popR", _ = "ldmNR", },
|
||||
"ldmdbNR", "ldmibNR",
|
||||
},
|
||||
}
|
||||
|
||||
local map_data = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
|
||||
"addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
|
||||
"tstNP", "teqNP", "cmpNP", "cmnNP",
|
||||
"orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
|
||||
}
|
||||
|
||||
local map_mul = {
|
||||
shift = 21, mask = 7,
|
||||
[0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
|
||||
"umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
|
||||
}
|
||||
|
||||
local map_sync = {
|
||||
shift = 20, mask = 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
|
||||
[0] = "swpDMN", false, false, false,
|
||||
"swpbDMN", false, false, false,
|
||||
"strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
|
||||
"strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
|
||||
}
|
||||
|
||||
local map_mulh = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = { shift = 5, mask = 3,
|
||||
[0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
|
||||
{ shift = 5, mask = 3,
|
||||
[0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
|
||||
}
|
||||
|
||||
local map_misc = {
|
||||
shift = 4, mask = 7,
|
||||
-- NYI: decode PSR bits of msr.
|
||||
[0] = { shift = 21, mask = 1, [0] = "mrsD", "msrM", },
|
||||
{ shift = 21, mask = 3, "bxM", false, "clzDM", },
|
||||
{ shift = 21, mask = 3, "bxjM", },
|
||||
{ shift = 21, mask = 3, "blxM", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
|
||||
false,
|
||||
{ shift = 21, mask = 3, "bkptK", },
|
||||
}
|
||||
|
||||
local map_datar = {
|
||||
shift = 4, mask = 9,
|
||||
[9] = {
|
||||
shift = 5, mask = 3,
|
||||
[0] = { shift = 24, mask = 1, [0] = map_mul, map_sync, },
|
||||
{ shift = 20, mask = 1, [0] = "strhDL", "ldrhDL", },
|
||||
{ shift = 20, mask = 1, [0] = "ldrdDL", "ldrsbDL", },
|
||||
{ shift = 20, mask = 1, [0] = "strdDL", "ldrshDL", },
|
||||
},
|
||||
_ = {
|
||||
shift = 20, mask = 25,
|
||||
[16] = { shift = 7, mask = 1, [0] = map_misc, map_mulh, },
|
||||
_ = {
|
||||
shift = 0, mask = 0xffffffff,
|
||||
[bor(0xe1a00000)] = "nop",
|
||||
_ = map_data,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
local map_datai = {
|
||||
shift = 20, mask = 31, -- NYI: decode PSR bits of msr. Decode imm12.
|
||||
[16] = "movwDW", [20] = "movtDW",
|
||||
[18] = { shift = 0, mask = 0xf00ff, [0] = "nopv6", _ = "msrNW", },
|
||||
[22] = "msrNW",
|
||||
_ = map_data,
|
||||
}
|
||||
|
||||
local map_branch = {
|
||||
shift = 24, mask = 1,
|
||||
[0] = "bB", "blB"
|
||||
}
|
||||
|
||||
local map_condins = {
|
||||
[0] = map_datar, map_datai, map_load, map_load1,
|
||||
map_loadm, map_branch, map_loadc, map_datac
|
||||
}
|
||||
|
||||
-- NYI: setend.
|
||||
local map_uncondins = {
|
||||
[0] = false, map_simddata, map_simdload, map_preload,
|
||||
false, "blxB", map_loadcu, map_datacu,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
|
||||
}
|
||||
|
||||
local map_cond = {
|
||||
[0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
|
||||
"hi", "ls", "ge", "lt", "gt", "le", "al",
|
||||
}
|
||||
|
||||
local map_shift = { [0] = "lsl", "lsr", "asr", "ror", }
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then
|
||||
extra = "\t->"..sym
|
||||
elseif band(ctx.op, 0x0e000000) ~= 0x0a000000 then
|
||||
extra = "\t; 0x"..tohex(ctx.rel)
|
||||
end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-5s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-5s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Format operand 2 of load/store opcodes.
|
||||
local function fmtload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local x, ofs
|
||||
local ext = (band(op, 0x04000000) == 0)
|
||||
if not ext and band(op, 0x02000000) == 0 then
|
||||
ofs = band(op, 4095)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
elseif ext and band(op, 0x00400000) ~= 0 then
|
||||
ofs = band(op, 15) + band(rshift(op, 4), 0xf0)
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
ofs = "#"..ofs
|
||||
else
|
||||
ofs = map_gpr[band(op, 15)]
|
||||
if ext or band(op, 0xfe0) == 0 then
|
||||
elseif band(op, 0xfe0) == 0x60 then
|
||||
ofs = format("%s, rrx", ofs)
|
||||
else
|
||||
local sh = band(rshift(op, 7), 31)
|
||||
if sh == 0 then sh = 32 end
|
||||
ofs = format("%s, %s #%d", ofs, map_shift[band(rshift(op, 5), 3)], sh)
|
||||
end
|
||||
if band(op, 0x00800000) == 0 then ofs = "-"..ofs end
|
||||
end
|
||||
if ofs == "#0" then
|
||||
x = format("[%s]", base)
|
||||
elseif band(op, 0x01000000) == 0 then
|
||||
x = format("[%s], %s", base, ofs)
|
||||
else
|
||||
x = format("[%s, %s]", base, ofs)
|
||||
end
|
||||
if band(op, 0x01200000) == 0x01200000 then x = x.."!" end
|
||||
return x
|
||||
end
|
||||
|
||||
-- Format operand 2 of vector load/store opcodes.
|
||||
local function fmtvload(ctx, op, pos)
|
||||
local base = map_gpr[band(rshift(op, 16), 15)]
|
||||
local ofs = band(op, 255)*4
|
||||
if band(op, 0x00800000) == 0 then ofs = -ofs end
|
||||
if base == "pc" then ctx.rel = ctx.addr + pos + 8 + ofs end
|
||||
if ofs == 0 then
|
||||
return format("[%s]", base)
|
||||
else
|
||||
return format("[%s, #%d]", base, ofs)
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtvr(op, vr, sh0, sh1)
|
||||
if vr == "s" then
|
||||
return format("s%d", 2*band(rshift(op, sh0), 15)+band(rshift(op, sh1), 1))
|
||||
else
|
||||
return format("d%d", band(rshift(op, sh0), 15)+band(rshift(op, sh1-4), 16))
|
||||
end
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
local operands = {}
|
||||
local suffix = ""
|
||||
local last, name, pat
|
||||
local vr
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local cond = rshift(op, 28)
|
||||
local opat
|
||||
if cond == 15 then
|
||||
opat = map_uncondins[band(rshift(op, 25), 7)]
|
||||
else
|
||||
if cond ~= 14 then suffix = map_cond[cond] end
|
||||
opat = map_condins[band(rshift(op, 25), 7)]
|
||||
end
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
name, pat = match(opat, "^([a-z0-9]*)(.*)")
|
||||
if sub(pat, 1, 1) == "." then
|
||||
local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
|
||||
suffix = suffix..s2
|
||||
pat = p2
|
||||
end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "D" then
|
||||
x = map_gpr[band(rshift(op, 12), 15)]
|
||||
elseif p == "N" then
|
||||
x = map_gpr[band(rshift(op, 16), 15)]
|
||||
elseif p == "S" then
|
||||
x = map_gpr[band(rshift(op, 8), 15)]
|
||||
elseif p == "M" then
|
||||
x = map_gpr[band(op, 15)]
|
||||
elseif p == "d" then
|
||||
x = fmtvr(op, vr, 12, 22)
|
||||
elseif p == "n" then
|
||||
x = fmtvr(op, vr, 16, 7)
|
||||
elseif p == "m" then
|
||||
x = fmtvr(op, vr, 0, 5)
|
||||
elseif p == "P" then
|
||||
if band(op, 0x02000000) ~= 0 then
|
||||
x = ror(band(op, 255), 2*band(rshift(op, 8), 15))
|
||||
else
|
||||
x = map_gpr[band(op, 15)]
|
||||
if band(op, 0xff0) ~= 0 then
|
||||
operands[#operands+1] = x
|
||||
local s = map_shift[band(rshift(op, 5), 3)]
|
||||
local r = nil
|
||||
if band(op, 0xf90) == 0 then
|
||||
if s == "ror" then s = "rrx" else r = "#32" end
|
||||
elseif band(op, 0x10) == 0 then
|
||||
r = "#"..band(rshift(op, 7), 31)
|
||||
else
|
||||
r = map_gpr[band(rshift(op, 8), 15)]
|
||||
end
|
||||
if name == "mov" then name = s; x = r
|
||||
elseif r then x = format("%s %s", s, r)
|
||||
else x = s end
|
||||
end
|
||||
end
|
||||
elseif p == "L" then
|
||||
x = fmtload(ctx, op, pos)
|
||||
elseif p == "l" then
|
||||
x = fmtvload(ctx, op, pos)
|
||||
elseif p == "B" then
|
||||
local addr = ctx.addr + pos + 8 + arshift(lshift(op, 8), 6)
|
||||
if cond == 15 then addr = addr + band(rshift(op, 23), 2) end
|
||||
ctx.rel = addr
|
||||
x = "0x"..tohex(addr)
|
||||
elseif p == "F" then
|
||||
vr = "s"
|
||||
elseif p == "G" then
|
||||
vr = "d"
|
||||
elseif p == "." then
|
||||
suffix = suffix..(vr == "s" and ".f32" or ".f64")
|
||||
elseif p == "R" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 1 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local t = {}
|
||||
for i=0,15 do
|
||||
if band(rshift(op, i), 1) == 1 then t[#t+1] = map_gpr[i] end
|
||||
end
|
||||
x = "{"..concat(t, ", ").."}"
|
||||
elseif p == "r" then
|
||||
if band(op, 0x00200000) ~= 0 and #operands == 2 then
|
||||
operands[1] = operands[1].."!"
|
||||
end
|
||||
local s = tonumber(sub(last, 2))
|
||||
local n = band(op, 255)
|
||||
if vr == "d" then n = rshift(n, 1) end
|
||||
operands[#operands] = format("{%s-%s%d}", last, vr, s+n-1)
|
||||
elseif p == "W" then
|
||||
x = band(op, 0x0fff) + band(rshift(op, 4), 0xf000)
|
||||
elseif p == "T" then
|
||||
x = "#0x"..tohex(band(op, 0x00ffffff), 6)
|
||||
elseif p == "U" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "u" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
if band(op, 0x40) == 0 then
|
||||
if x == 0 then x = nil else x = "lsl #"..x end
|
||||
else
|
||||
if x == 0 then x = "asr #32" else x = "asr #"..x end
|
||||
end
|
||||
elseif p == "v" then
|
||||
x = band(rshift(op, 7), 31)
|
||||
elseif p == "w" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "x" then
|
||||
x = band(rshift(op, 16), 31) + 1
|
||||
elseif p == "X" then
|
||||
x = band(rshift(op, 16), 31) - last + 1
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 12), 0xf0) + band(op, 0x0f)
|
||||
elseif p == "K" then
|
||||
x = "#0x"..tohex(band(rshift(op, 4), 0x0000fff0) + band(op, 15), 4)
|
||||
elseif p == "s" then
|
||||
if band(op, 0x00100000) ~= 0 then suffix = "s"..suffix end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then
|
||||
last = x
|
||||
if type(x) == "number" then x = "#"..x end
|
||||
operands[#operands+1] = x
|
||||
end
|
||||
end
|
||||
|
||||
return putop(ctx, name..suffix, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
ctx.pos = ofs
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 16 then return map_gpr[r] end
|
||||
return "d"..(r-16)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
disass = disass_
|
||||
regname = regname_
|
||||
|
||||
Vendored
+428
@@ -0,0 +1,428 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPS disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all standard MIPS32R1/R2 instructions.
|
||||
-- Default mode is big-endian, but see: dis_mipsel.lua
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
|
||||
local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
|
||||
local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
|
||||
|
||||
local map_special = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
|
||||
map_movci, map_srl, "sraDTA",
|
||||
"sllvDTS", false, map_srlv, "sravDTS",
|
||||
"jrS", "jalrD1S", "movzDST", "movnDST",
|
||||
"syscallY", "breakY", false, "sync",
|
||||
"mfhiD", "mthiS", "mfloD", "mtloS",
|
||||
false, false, false, false,
|
||||
"multST", "multuST", "divST", "divuST",
|
||||
false, false, false, false,
|
||||
"addDST", "addu|moveDST0", "subDST", "subu|neguDS0T",
|
||||
"andDST", "orDST", "xorDST", "nor|notDST0",
|
||||
false, false, "sltDST", "sltuDST",
|
||||
false, false, false, false,
|
||||
"tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
|
||||
"teqSTZ", false, "tneSTZ",
|
||||
}
|
||||
|
||||
local map_special2 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "maddST", "madduST", "mulDST", false,
|
||||
"msubST", "msubuST",
|
||||
[32] = "clzDS", [33] = "cloDS",
|
||||
[63] = "sdbbpY",
|
||||
}
|
||||
|
||||
local map_bshfl = {
|
||||
shift = 6, mask = 31,
|
||||
[2] = "wsbhDT",
|
||||
[16] = "sebDT",
|
||||
[24] = "sehDT",
|
||||
}
|
||||
|
||||
local map_special3 = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "extTSAK", [4] = "insTSAL",
|
||||
[32] = map_bshfl,
|
||||
[59] = "rdhwrTD",
|
||||
}
|
||||
|
||||
local map_regimm = {
|
||||
shift = 16, mask = 31,
|
||||
[0] = "bltzSB", "bgezSB", "bltzlSB", "bgezlSB",
|
||||
false, false, false, false,
|
||||
"tgeiSI", "tgeiuSI", "tltiSI", "tltiuSI",
|
||||
"teqiSI", false, "tneiSI", false,
|
||||
"bltzalSB", "bgezalSB", "bltzallSB", "bgezallSB",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, "synciSO",
|
||||
}
|
||||
|
||||
local map_cop0 = {
|
||||
shift = 25, mask = 1,
|
||||
[0] = {
|
||||
shift = 21, mask = 15,
|
||||
[0] = "mfc0TDW", [4] = "mtc0TDW",
|
||||
[10] = "rdpgprDT",
|
||||
[11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
|
||||
[14] = "wrpgprDT",
|
||||
}, {
|
||||
shift = 0, mask = 63,
|
||||
[1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
|
||||
[24] = "eret", [31] = "deret",
|
||||
[32] = "wait",
|
||||
},
|
||||
}
|
||||
|
||||
local map_cop1s = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.sFGH", "sub.sFGH", "mul.sFGH", "div.sFGH",
|
||||
"sqrt.sFG", "abs.sFG", "mov.sFG", "neg.sFG",
|
||||
"round.l.sFG", "trunc.l.sFG", "ceil.l.sFG", "floor.l.sFG",
|
||||
"round.w.sFG", "trunc.w.sFG", "ceil.w.sFG", "floor.w.sFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
|
||||
"movz.sFGT", "movn.sFGT",
|
||||
false, "recip.sFG", "rsqrt.sFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, "cvt.d.sFG", false, false,
|
||||
"cvt.w.sFG", "cvt.l.sFG", "cvt.ps.sFGH", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.sVGH", "c.un.sVGH", "c.eq.sVGH", "c.ueq.sVGH",
|
||||
"c.olt.sVGH", "c.ult.sVGH", "c.ole.sVGH", "c.ule.sVGH",
|
||||
"c.sf.sVGH", "c.ngle.sVGH", "c.seq.sVGH", "c.ngl.sVGH",
|
||||
"c.lt.sVGH", "c.nge.sVGH", "c.le.sVGH", "c.ngt.sVGH",
|
||||
}
|
||||
|
||||
local map_cop1d = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.dFGH", "sub.dFGH", "mul.dFGH", "div.dFGH",
|
||||
"sqrt.dFG", "abs.dFG", "mov.dFG", "neg.dFG",
|
||||
"round.l.dFG", "trunc.l.dFG", "ceil.l.dFG", "floor.l.dFG",
|
||||
"round.w.dFG", "trunc.w.dFG", "ceil.w.dFG", "floor.w.dFG",
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
|
||||
"movz.dFGT", "movn.dFGT",
|
||||
false, "recip.dFG", "rsqrt.dFG", false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.dFG", false, false, false,
|
||||
"cvt.w.dFG", "cvt.l.dFG", false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"c.f.dVGH", "c.un.dVGH", "c.eq.dVGH", "c.ueq.dVGH",
|
||||
"c.olt.dVGH", "c.ult.dVGH", "c.ole.dVGH", "c.ule.dVGH",
|
||||
"c.df.dVGH", "c.ngle.dVGH", "c.deq.dVGH", "c.ngl.dVGH",
|
||||
"c.lt.dVGH", "c.nge.dVGH", "c.le.dVGH", "c.ngt.dVGH",
|
||||
}
|
||||
|
||||
local map_cop1ps = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "add.psFGH", "sub.psFGH", "mul.psFGH", false,
|
||||
false, "abs.psFG", "mov.psFG", "neg.psFG",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false,
|
||||
{ shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
|
||||
"movz.psFGT", "movn.psFGT",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.puFG", false, false, false,
|
||||
false, false, false, false,
|
||||
"cvt.s.plFG", false, false, false,
|
||||
"pll.psFGH", "plu.psFGH", "pul.psFGH", "puu.psFGH",
|
||||
"c.f.psVGH", "c.un.psVGH", "c.eq.psVGH", "c.ueq.psVGH",
|
||||
"c.olt.psVGH", "c.ult.psVGH", "c.ole.psVGH", "c.ule.psVGH",
|
||||
"c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
|
||||
"c.lt.psVGH", "c.nge.psVGH", "c.le.psVGH", "c.ngt.psVGH",
|
||||
}
|
||||
|
||||
local map_cop1w = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
|
||||
}
|
||||
|
||||
local map_cop1l = {
|
||||
shift = 0, mask = 63,
|
||||
[32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
|
||||
}
|
||||
|
||||
local map_cop1bc = {
|
||||
shift = 16, mask = 3,
|
||||
[0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
|
||||
}
|
||||
|
||||
local map_cop1 = {
|
||||
shift = 21, mask = 31,
|
||||
[0] = "mfc1TG", false, "cfc1TG", "mfhc1TG",
|
||||
"mtc1TG", false, "ctc1TG", "mthc1TG",
|
||||
map_cop1bc, false, false, false,
|
||||
false, false, false, false,
|
||||
map_cop1s, map_cop1d, false, false,
|
||||
map_cop1w, map_cop1l, map_cop1ps,
|
||||
}
|
||||
|
||||
local map_cop1x = {
|
||||
shift = 0, mask = 63,
|
||||
[0] = "lwxc1FSX", "ldxc1FSX", false, false,
|
||||
false, "luxc1FSX", false, false,
|
||||
"swxc1FSX", "sdxc1FSX", false, false,
|
||||
false, "suxc1FSX", false, "prefxMSX",
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, false, false,
|
||||
false, false, "alnv.psFGHS", false,
|
||||
"madd.sFRGH", "madd.dFRGH", false, false,
|
||||
false, false, "madd.psFRGH", false,
|
||||
"msub.sFRGH", "msub.dFRGH", false, false,
|
||||
false, false, "msub.psFRGH", false,
|
||||
"nmadd.sFRGH", "nmadd.dFRGH", false, false,
|
||||
false, false, "nmadd.psFRGH", false,
|
||||
"nmsub.sFRGH", "nmsub.dFRGH", false, false,
|
||||
false, false, "nmsub.psFRGH", false,
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = map_special, map_regimm, "jJ", "jalJ",
|
||||
"beq|beqz|bST00B", "bne|bnezST0B", "blezSB", "bgtzSB",
|
||||
"addiTSI", "addiu|liTS0I", "sltiTSI", "sltiuTSI",
|
||||
"andiTSU", "ori|liTS0U", "xoriTSU", "luiTU",
|
||||
map_cop0, map_cop1, false, map_cop1x,
|
||||
"beql|beqzlST0B", "bnel|bnezlST0B", "blezlSB", "bgtzlSB",
|
||||
false, false, false, false,
|
||||
map_special2, false, false, map_special3,
|
||||
"lbTSO", "lhTSO", "lwlTSO", "lwTSO",
|
||||
"lbuTSO", "lhuTSO", "lwrTSO", false,
|
||||
"sbTSO", "shTSO", "swlTSO", "swTSO",
|
||||
false, false, "swrTSO", "cacheNSO",
|
||||
"llTSO", "lwc1HSO", "lwc2TSO", "prefNSO",
|
||||
false, "ldc1HSO", "ldc2TSO", false,
|
||||
"scTSO", "swc1HSO", "swc2TSO", false,
|
||||
false, "sdc1HSO", "sdc2TSO", false,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
local function get_be(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
end
|
||||
|
||||
local function get_le(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local op = ctx:get()
|
||||
local operands = {}
|
||||
local last = nil
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = map_pri[rshift(op, 26)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "S" then
|
||||
x = map_gpr[band(rshift(op, 21), 31)]
|
||||
elseif p == "T" then
|
||||
x = map_gpr[band(rshift(op, 16), 31)]
|
||||
elseif p == "D" then
|
||||
x = map_gpr[band(rshift(op, 11), 31)]
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, 6), 31)
|
||||
elseif p == "G" then
|
||||
x = "f"..band(rshift(op, 11), 31)
|
||||
elseif p == "H" then
|
||||
x = "f"..band(rshift(op, 16), 31)
|
||||
elseif p == "R" then
|
||||
x = "f"..band(rshift(op, 21), 31)
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, 6), 31)
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, 11), 31)
|
||||
elseif p == "N" then
|
||||
x = band(rshift(op, 16), 31)
|
||||
elseif p == "C" then
|
||||
x = band(rshift(op, 18), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "K" then
|
||||
x = band(rshift(op, 11), 31) + 1
|
||||
elseif p == "L" then
|
||||
x = band(rshift(op, 11), 31) - last + 1
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "O" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "X" then
|
||||
local index = map_gpr[band(rshift(op, 16), 31)]
|
||||
operands[#operands] = format("%s(%s)", index, last)
|
||||
elseif p == "B" then
|
||||
x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 16)*4 + 4
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "J" then
|
||||
x = band(ctx.addr + ctx.pos, 0xf0000000) + band(op, 0x03ffffff)*4
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "V" then
|
||||
x = band(rshift(op, 8), 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "W" then
|
||||
x = band(op, 7)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Y" then
|
||||
x = band(rshift(op, 6), 0x000fffff)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, 6), 1023)
|
||||
if x == 0 then x = nil end
|
||||
elseif p == "0" then
|
||||
if last == "r0" or last == 0 then
|
||||
local n = #operands
|
||||
operands[n] = nil
|
||||
last = operands[n-1]
|
||||
if altname then
|
||||
local a1, a2 = match(altname, "([^|]*)|(.*)")
|
||||
if a1 then name, altname = a1, a2
|
||||
else name = altname end
|
||||
end
|
||||
end
|
||||
elseif p == "1" then
|
||||
if last == "ra" then
|
||||
operands[#operands] = nil
|
||||
end
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
ctx.get = get_be
|
||||
return ctx
|
||||
end
|
||||
|
||||
local function create_el_(code, addr, out)
|
||||
local ctx = create_(code, addr, out)
|
||||
ctx.get = get_le
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
local function disass_el_(code, addr, out)
|
||||
create_el_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
create_el = create_el_
|
||||
disass = disass_
|
||||
disass_el = disass_el_
|
||||
regname = regname_
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT MIPSEL disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the little-endian functions from the
|
||||
-- MIPS disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local require = require
|
||||
|
||||
module(...)
|
||||
|
||||
local dis_mips = require(_PACKAGE.."dis_mips")
|
||||
|
||||
create = dis_mips.create_el
|
||||
disass = dis_mips.disass_el
|
||||
regname = dis_mips.regname
|
||||
|
||||
Vendored
+591
@@ -0,0 +1,591 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT PPC disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT/X license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- It disassembles all common, non-privileged 32/64 bit PowerPC instructions
|
||||
-- plus the e500 SPE instructions and some Cell/Xenon extensions.
|
||||
--
|
||||
-- NYI: VMX, VMX128
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local type = type
|
||||
local sub, byte, format = string.sub, string.byte, string.format
|
||||
local match, gmatch, gsub = string.match, string.gmatch, string.gsub
|
||||
local concat = table.concat
|
||||
local bit = require("bit")
|
||||
local band, bor, tohex = bit.band, bit.bor, bit.tohex
|
||||
local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- Primary and extended opcode maps
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_crops = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "mcrfXX",
|
||||
[33] = "crnor|crnotCCC=", [129] = "crandcCCC",
|
||||
[193] = "crxor|crclrCCC%", [225] = "crnandCCC",
|
||||
[257] = "crandCCC", [289] = "creqv|crsetCCC%",
|
||||
[417] = "crorcCCC", [449] = "cror|crmoveCCC=",
|
||||
[16] = "b_lrKB", [528] = "b_ctrKB",
|
||||
[150] = "isync",
|
||||
}
|
||||
|
||||
local map_rlwinm = setmetatable({
|
||||
shift = 0, mask = -1,
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
local rot = band(rshift(x, 11), 31)
|
||||
local mb = band(rshift(x, 6), 31)
|
||||
local me = band(rshift(x, 1), 31)
|
||||
if mb == 0 and me == 31-rot then
|
||||
return "slwiRR~A."
|
||||
elseif me == 31 and mb == 32-rot then
|
||||
return "srwiRR~-A."
|
||||
else
|
||||
return "rlwinmRR~AAA."
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
local map_rld = {
|
||||
shift = 2, mask = 7,
|
||||
[0] = "rldiclRR~HM.", "rldicrRR~HM.", "rldicRR~HM.", "rldimiRR~HM.",
|
||||
{
|
||||
shift = 1, mask = 1,
|
||||
[0] = "rldclRR~RM.", "rldcrRR~RM.",
|
||||
},
|
||||
}
|
||||
|
||||
local map_ext = setmetatable({
|
||||
shift = 1, mask = 1023,
|
||||
|
||||
[0] = "cmp_YLRR", [32] = "cmpl_YLRR",
|
||||
[4] = "twARR", [68] = "tdARR",
|
||||
|
||||
[8] = "subfcRRR.", [40] = "subfRRR.",
|
||||
[104] = "negRR.", [136] = "subfeRRR.",
|
||||
[200] = "subfzeRR.", [232] = "subfmeRR.",
|
||||
[520] = "subfcoRRR.", [552] = "subfoRRR.",
|
||||
[616] = "negoRR.", [648] = "subfeoRRR.",
|
||||
[712] = "subfzeoRR.", [744] = "subfmeoRR.",
|
||||
|
||||
[9] = "mulhduRRR.", [73] = "mulhdRRR.", [233] = "mulldRRR.",
|
||||
[457] = "divduRRR.", [489] = "divdRRR.",
|
||||
[745] = "mulldoRRR.",
|
||||
[969] = "divduoRRR.", [1001] = "divdoRRR.",
|
||||
|
||||
[10] = "addcRRR.", [138] = "addeRRR.",
|
||||
[202] = "addzeRR.", [234] = "addmeRR.", [266] = "addRRR.",
|
||||
[522] = "addcoRRR.", [650] = "addeoRRR.",
|
||||
[714] = "addzeoRR.", [746] = "addmeoRR.", [778] = "addoRRR.",
|
||||
|
||||
[11] = "mulhwuRRR.", [75] = "mulhwRRR.", [235] = "mullwRRR.",
|
||||
[459] = "divwuRRR.", [491] = "divwRRR.",
|
||||
[747] = "mullwoRRR.",
|
||||
[971] = "divwouRRR.", [1003] = "divwoRRR.",
|
||||
|
||||
[15] = "iselltRRR", [47] = "iselgtRRR", [79] = "iseleqRRR",
|
||||
|
||||
[144] = { shift = 20, mask = 1, [0] = "mtcrfRZ~", "mtocrfRZ~", },
|
||||
[19] = { shift = 20, mask = 1, [0] = "mfcrR", "mfocrfRZ", },
|
||||
[371] = { shift = 11, mask = 1023, [392] = "mftbR", [424] = "mftbuR", },
|
||||
[339] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mferR", [256] = "mflrR", [288] = "mfctrR", [16] = "mfspefscrR",
|
||||
},
|
||||
[467] = {
|
||||
shift = 11, mask = 1023,
|
||||
[32] = "mtxerR", [256] = "mtlrR", [288] = "mtctrR", [16] = "mtspefscrR",
|
||||
},
|
||||
|
||||
[20] = "lwarxRR0R", [84] = "ldarxRR0R",
|
||||
|
||||
[21] = "ldxRR0R", [53] = "lduxRRR",
|
||||
[149] = "stdxRR0R", [181] = "stduxRRR",
|
||||
[341] = "lwaxRR0R", [373] = "lwauxRRR",
|
||||
|
||||
[23] = "lwzxRR0R", [55] = "lwzuxRRR",
|
||||
[87] = "lbzxRR0R", [119] = "lbzuxRRR",
|
||||
[151] = "stwxRR0R", [183] = "stwuxRRR",
|
||||
[215] = "stbxRR0R", [247] = "stbuxRRR",
|
||||
[279] = "lhzxRR0R", [311] = "lhzuxRRR",
|
||||
[343] = "lhaxRR0R", [375] = "lhauxRRR",
|
||||
[407] = "sthxRR0R", [439] = "sthuxRRR",
|
||||
|
||||
[54] = "dcbst-R0R", [86] = "dcbf-R0R",
|
||||
[150] = "stwcxRR0R.", [214] = "stdcxRR0R.",
|
||||
[246] = "dcbtst-R0R", [278] = "dcbt-R0R",
|
||||
[310] = "eciwxRR0R", [438] = "ecowxRR0R",
|
||||
[470] = "dcbi-RR",
|
||||
|
||||
[598] = {
|
||||
shift = 21, mask = 3,
|
||||
[0] = "sync", "lwsync", "ptesync",
|
||||
},
|
||||
[758] = "dcba-RR",
|
||||
[854] = "eieio", [982] = "icbi-R0R", [1014] = "dcbz-R0R",
|
||||
|
||||
[26] = "cntlzwRR~", [58] = "cntlzdRR~",
|
||||
[122] = "popcntbRR~",
|
||||
[154] = "prtywRR~", [186] = "prtydRR~",
|
||||
|
||||
[28] = "andRR~R.", [60] = "andcRR~R.", [124] = "nor|notRR~R=.",
|
||||
[284] = "eqvRR~R.", [316] = "xorRR~R.",
|
||||
[412] = "orcRR~R.", [444] = "or|mrRR~R=.", [476] = "nandRR~R.",
|
||||
[508] = "cmpbRR~R",
|
||||
|
||||
[512] = "mcrxrX",
|
||||
|
||||
[532] = "ldbrxRR0R", [660] = "stdbrxRR0R",
|
||||
|
||||
[533] = "lswxRR0R", [597] = "lswiRR0A",
|
||||
[661] = "stswxRR0R", [725] = "stswiRR0A",
|
||||
|
||||
[534] = "lwbrxRR0R", [662] = "stwbrxRR0R",
|
||||
[790] = "lhbrxRR0R", [918] = "sthbrxRR0R",
|
||||
|
||||
[535] = "lfsxFR0R", [567] = "lfsuxFRR",
|
||||
[599] = "lfdxFR0R", [631] = "lfduxFRR",
|
||||
[663] = "stfsxFR0R", [695] = "stfsuxFRR",
|
||||
[727] = "stfdxFR0R", [759] = "stfduxFR0R",
|
||||
[855] = "lfiwaxFR0R",
|
||||
[983] = "stfiwxFR0R",
|
||||
|
||||
[24] = "slwRR~R.",
|
||||
|
||||
[27] = "sldRR~R.", [536] = "srwRR~R.",
|
||||
[792] = "srawRR~R.", [824] = "srawiRR~A.",
|
||||
|
||||
[794] = "sradRR~R.", [826] = "sradiRR~H.", [827] = "sradiRR~H.",
|
||||
[922] = "extshRR~.", [954] = "extsbRR~.", [986] = "extswRR~.",
|
||||
|
||||
[539] = "srdRR~R.",
|
||||
},
|
||||
{ __index = function(t, x)
|
||||
if band(x, 31) == 15 then return "iselRRRC" end
|
||||
end
|
||||
})
|
||||
|
||||
local map_ld = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "ldRRE", "lduRRE", "lwaRRE",
|
||||
}
|
||||
|
||||
local map_std = {
|
||||
shift = 0, mask = 3,
|
||||
[0] = "stdRRE", "stduRRE",
|
||||
}
|
||||
|
||||
local map_fps = {
|
||||
shift = 5, mask = 1,
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivsFFF.", false,
|
||||
"fsubsFFF.", "faddsFFF.", "fsqrtsF-F.", false,
|
||||
"fresF-F.", "fmulsFF-F.", "frsqrtesF-F.", false,
|
||||
"fmsubsFFFF~.", "fmaddsFFFF~.", "fnmsubsFFFF~.", "fnmaddsFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_fpd = {
|
||||
shift = 5, mask = 1,
|
||||
[0] = {
|
||||
shift = 1, mask = 1023,
|
||||
[0] = "fcmpuXFF", [32] = "fcmpoXFF", [64] = "mcrfsXX",
|
||||
[38] = "mtfsb1A.", [70] = "mtfsb0A.", [134] = "mtfsfiA>>-A>",
|
||||
[8] = "fcpsgnFFF.", [40] = "fnegF-F.", [72] = "fmrF-F.",
|
||||
[136] = "fnabsF-F.", [264] = "fabsF-F.",
|
||||
[12] = "frspF-F.",
|
||||
[14] = "fctiwF-F.", [15] = "fctiwzF-F.",
|
||||
[583] = "mffsF.", [711] = "mtfsfZF.",
|
||||
[392] = "frinF-F.", [424] = "frizF-F.",
|
||||
[456] = "fripF-F.", [488] = "frimF-F.",
|
||||
[814] = "fctidF-F.", [815] = "fctidzF-F.", [846] = "fcfidF-F.",
|
||||
},
|
||||
{
|
||||
shift = 1, mask = 15,
|
||||
[0] = false, false, "fdivFFF.", false,
|
||||
"fsubFFF.", "faddFFF.", "fsqrtF-F.", "fselFFFF~.",
|
||||
"freF-F.", "fmulFF-F.", "frsqrteF-F.", false,
|
||||
"fmsubFFFF~.", "fmaddFFFF~.", "fnmsubFFFF~.", "fnmaddFFFF~.",
|
||||
}
|
||||
}
|
||||
|
||||
local map_spe = {
|
||||
shift = 0, mask = 2047,
|
||||
|
||||
[512] = "evaddwRRR", [514] = "evaddiwRAR~",
|
||||
[516] = "evsubwRRR~", [518] = "evsubiwRAR~",
|
||||
[520] = "evabsRR", [521] = "evnegRR",
|
||||
[522] = "evextsbRR", [523] = "evextshRR", [524] = "evrndwRR",
|
||||
[525] = "evcntlzwRR", [526] = "evcntlswRR",
|
||||
|
||||
[527] = "brincRRR",
|
||||
|
||||
[529] = "evandRRR", [530] = "evandcRRR", [534] = "evxorRRR",
|
||||
[535] = "evor|evmrRRR=", [536] = "evnor|evnotRRR=",
|
||||
[537] = "eveqvRRR", [539] = "evorcRRR", [542] = "evnandRRR",
|
||||
|
||||
[544] = "evsrwuRRR", [545] = "evsrwsRRR",
|
||||
[546] = "evsrwiuRRA", [547] = "evsrwisRRA",
|
||||
[548] = "evslwRRR", [550] = "evslwiRRA",
|
||||
[552] = "evrlwRRR", [553] = "evsplatiRS",
|
||||
[554] = "evrlwiRRA", [555] = "evsplatfiRS",
|
||||
[556] = "evmergehiRRR", [557] = "evmergeloRRR",
|
||||
[558] = "evmergehiloRRR", [559] = "evmergelohiRRR",
|
||||
|
||||
[560] = "evcmpgtuYRR", [561] = "evcmpgtsYRR",
|
||||
[562] = "evcmpltuYRR", [563] = "evcmpltsYRR",
|
||||
[564] = "evcmpeqYRR",
|
||||
|
||||
[632] = "evselRRR", [633] = "evselRRRW",
|
||||
[634] = "evselRRRW", [635] = "evselRRRW",
|
||||
[636] = "evselRRRW", [637] = "evselRRRW",
|
||||
[638] = "evselRRRW", [639] = "evselRRRW",
|
||||
|
||||
[640] = "evfsaddRRR", [641] = "evfssubRRR",
|
||||
[644] = "evfsabsRR", [645] = "evfsnabsRR", [646] = "evfsnegRR",
|
||||
[648] = "evfsmulRRR", [649] = "evfsdivRRR",
|
||||
[652] = "evfscmpgtYRR", [653] = "evfscmpltYRR", [654] = "evfscmpeqYRR",
|
||||
[656] = "evfscfuiR-R", [657] = "evfscfsiR-R",
|
||||
[658] = "evfscfufR-R", [659] = "evfscfsfR-R",
|
||||
[660] = "evfsctuiR-R", [661] = "evfsctsiR-R",
|
||||
[662] = "evfsctufR-R", [663] = "evfsctsfR-R",
|
||||
[664] = "evfsctuizR-R", [666] = "evfsctsizR-R",
|
||||
[668] = "evfststgtYRR", [669] = "evfststltYRR", [670] = "evfststeqYRR",
|
||||
|
||||
[704] = "efsaddRRR", [705] = "efssubRRR",
|
||||
[708] = "efsabsRR", [709] = "efsnabsRR", [710] = "efsnegRR",
|
||||
[712] = "efsmulRRR", [713] = "efsdivRRR",
|
||||
[716] = "efscmpgtYRR", [717] = "efscmpltYRR", [718] = "efscmpeqYRR",
|
||||
[719] = "efscfdR-R",
|
||||
[720] = "efscfuiR-R", [721] = "efscfsiR-R",
|
||||
[722] = "efscfufR-R", [723] = "efscfsfR-R",
|
||||
[724] = "efsctuiR-R", [725] = "efsctsiR-R",
|
||||
[726] = "efsctufR-R", [727] = "efsctsfR-R",
|
||||
[728] = "efsctuizR-R", [730] = "efsctsizR-R",
|
||||
[732] = "efststgtYRR", [733] = "efststltYRR", [734] = "efststeqYRR",
|
||||
|
||||
[736] = "efdaddRRR", [737] = "efdsubRRR",
|
||||
[738] = "efdcfuidR-R", [739] = "efdcfsidR-R",
|
||||
[740] = "efdabsRR", [741] = "efdnabsRR", [742] = "efdnegRR",
|
||||
[744] = "efdmulRRR", [745] = "efddivRRR",
|
||||
[746] = "efdctuidzR-R", [747] = "efdctsidzR-R",
|
||||
[748] = "efdcmpgtYRR", [749] = "efdcmpltYRR", [750] = "efdcmpeqYRR",
|
||||
[751] = "efdcfsR-R",
|
||||
[752] = "efdcfuiR-R", [753] = "efdcfsiR-R",
|
||||
[754] = "efdcfufR-R", [755] = "efdcfsfR-R",
|
||||
[756] = "efdctuiR-R", [757] = "efdctsiR-R",
|
||||
[758] = "efdctufR-R", [759] = "efdctsfR-R",
|
||||
[760] = "efdctuizR-R", [762] = "efdctsizR-R",
|
||||
[764] = "efdtstgtYRR", [765] = "efdtstltYRR", [766] = "efdtsteqYRR",
|
||||
|
||||
[768] = "evlddxRR0R", [769] = "evlddRR8",
|
||||
[770] = "evldwxRR0R", [771] = "evldwRR8",
|
||||
[772] = "evldhxRR0R", [773] = "evldhRR8",
|
||||
[776] = "evlhhesplatxRR0R", [777] = "evlhhesplatRR2",
|
||||
[780] = "evlhhousplatxRR0R", [781] = "evlhhousplatRR2",
|
||||
[782] = "evlhhossplatxRR0R", [783] = "evlhhossplatRR2",
|
||||
[784] = "evlwhexRR0R", [785] = "evlwheRR4",
|
||||
[788] = "evlwhouxRR0R", [789] = "evlwhouRR4",
|
||||
[790] = "evlwhosxRR0R", [791] = "evlwhosRR4",
|
||||
[792] = "evlwwsplatxRR0R", [793] = "evlwwsplatRR4",
|
||||
[796] = "evlwhsplatxRR0R", [797] = "evlwhsplatRR4",
|
||||
|
||||
[800] = "evstddxRR0R", [801] = "evstddRR8",
|
||||
[802] = "evstdwxRR0R", [803] = "evstdwRR8",
|
||||
[804] = "evstdhxRR0R", [805] = "evstdhRR8",
|
||||
[816] = "evstwhexRR0R", [817] = "evstwheRR4",
|
||||
[820] = "evstwhoxRR0R", [821] = "evstwhoRR4",
|
||||
[824] = "evstwwexRR0R", [825] = "evstwweRR4",
|
||||
[828] = "evstwwoxRR0R", [829] = "evstwwoRR4",
|
||||
|
||||
[1027] = "evmhessfRRR", [1031] = "evmhossfRRR", [1032] = "evmheumiRRR",
|
||||
[1033] = "evmhesmiRRR", [1035] = "evmhesmfRRR", [1036] = "evmhoumiRRR",
|
||||
[1037] = "evmhosmiRRR", [1039] = "evmhosmfRRR", [1059] = "evmhessfaRRR",
|
||||
[1063] = "evmhossfaRRR", [1064] = "evmheumiaRRR", [1065] = "evmhesmiaRRR",
|
||||
[1067] = "evmhesmfaRRR", [1068] = "evmhoumiaRRR", [1069] = "evmhosmiaRRR",
|
||||
[1071] = "evmhosmfaRRR", [1095] = "evmwhssfRRR", [1096] = "evmwlumiRRR",
|
||||
[1100] = "evmwhumiRRR", [1101] = "evmwhsmiRRR", [1103] = "evmwhsmfRRR",
|
||||
[1107] = "evmwssfRRR", [1112] = "evmwumiRRR", [1113] = "evmwsmiRRR",
|
||||
[1115] = "evmwsmfRRR", [1127] = "evmwhssfaRRR", [1128] = "evmwlumiaRRR",
|
||||
[1132] = "evmwhumiaRRR", [1133] = "evmwhsmiaRRR", [1135] = "evmwhsmfaRRR",
|
||||
[1139] = "evmwssfaRRR", [1144] = "evmwumiaRRR", [1145] = "evmwsmiaRRR",
|
||||
[1147] = "evmwsmfaRRR",
|
||||
|
||||
[1216] = "evaddusiaawRR", [1217] = "evaddssiaawRR",
|
||||
[1218] = "evsubfusiaawRR", [1219] = "evsubfssiaawRR",
|
||||
[1220] = "evmraRR",
|
||||
[1222] = "evdivwsRRR", [1223] = "evdivwuRRR",
|
||||
[1224] = "evaddumiaawRR", [1225] = "evaddsmiaawRR",
|
||||
[1226] = "evsubfumiaawRR", [1227] = "evsubfsmiaawRR",
|
||||
|
||||
[1280] = "evmheusiaawRRR", [1281] = "evmhessiaawRRR",
|
||||
[1283] = "evmhessfaawRRR", [1284] = "evmhousiaawRRR",
|
||||
[1285] = "evmhossiaawRRR", [1287] = "evmhossfaawRRR",
|
||||
[1288] = "evmheumiaawRRR", [1289] = "evmhesmiaawRRR",
|
||||
[1291] = "evmhesmfaawRRR", [1292] = "evmhoumiaawRRR",
|
||||
[1293] = "evmhosmiaawRRR", [1295] = "evmhosmfaawRRR",
|
||||
[1320] = "evmhegumiaaRRR", [1321] = "evmhegsmiaaRRR",
|
||||
[1323] = "evmhegsmfaaRRR", [1324] = "evmhogumiaaRRR",
|
||||
[1325] = "evmhogsmiaaRRR", [1327] = "evmhogsmfaaRRR",
|
||||
[1344] = "evmwlusiaawRRR", [1345] = "evmwlssiaawRRR",
|
||||
[1352] = "evmwlumiaawRRR", [1353] = "evmwlsmiaawRRR",
|
||||
[1363] = "evmwssfaaRRR", [1368] = "evmwumiaaRRR",
|
||||
[1369] = "evmwsmiaaRRR", [1371] = "evmwsmfaaRRR",
|
||||
[1408] = "evmheusianwRRR", [1409] = "evmhessianwRRR",
|
||||
[1411] = "evmhessfanwRRR", [1412] = "evmhousianwRRR",
|
||||
[1413] = "evmhossianwRRR", [1415] = "evmhossfanwRRR",
|
||||
[1416] = "evmheumianwRRR", [1417] = "evmhesmianwRRR",
|
||||
[1419] = "evmhesmfanwRRR", [1420] = "evmhoumianwRRR",
|
||||
[1421] = "evmhosmianwRRR", [1423] = "evmhosmfanwRRR",
|
||||
[1448] = "evmhegumianRRR", [1449] = "evmhegsmianRRR",
|
||||
[1451] = "evmhegsmfanRRR", [1452] = "evmhogumianRRR",
|
||||
[1453] = "evmhogsmianRRR", [1455] = "evmhogsmfanRRR",
|
||||
[1472] = "evmwlusianwRRR", [1473] = "evmwlssianwRRR",
|
||||
[1480] = "evmwlumianwRRR", [1481] = "evmwlsmianwRRR",
|
||||
[1491] = "evmwssfanRRR", [1496] = "evmwumianRRR",
|
||||
[1497] = "evmwsmianRRR", [1499] = "evmwsmfanRRR",
|
||||
}
|
||||
|
||||
local map_pri = {
|
||||
[0] = false, false, "tdiARI", "twiARI",
|
||||
map_spe, false, false, "mulliRRI",
|
||||
"subficRRI", false, "cmpl_iYLRU", "cmp_iYLRI",
|
||||
"addicRRI", "addic.RRI", "addi|liRR0I", "addis|lisRR0I",
|
||||
"b_KBJ", "sc", "bKJ", map_crops,
|
||||
"rlwimiRR~AAA.", map_rlwinm, false, "rlwnmRR~RAA.",
|
||||
"oriNRR~U", "orisRR~U", "xoriRR~U", "xorisRR~U",
|
||||
"andi.RR~U", "andis.RR~U", map_rld, map_ext,
|
||||
"lwzRRD", "lwzuRRD", "lbzRRD", "lbzuRRD",
|
||||
"stwRRD", "stwuRRD", "stbRRD", "stbuRRD",
|
||||
"lhzRRD", "lhzuRRD", "lhaRRD", "lhauRRD",
|
||||
"sthRRD", "sthuRRD", "lmwRRD", "stmwRRD",
|
||||
"lfsFRD", "lfsuFRD", "lfdFRD", "lfduFRD",
|
||||
"stfsFRD", "stfsuFRD", "stfdFRD", "stfduFRD",
|
||||
false, false, map_ld, map_fps,
|
||||
false, false, map_std, map_fpd,
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local map_gpr = {
|
||||
[0] = "r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
|
||||
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31",
|
||||
}
|
||||
|
||||
local map_cond = { [0] = "lt", "gt", "eq", "so", "ge", "le", "ne", "ns", }
|
||||
|
||||
-- Format a condition bit.
|
||||
local function condfmt(cond)
|
||||
if cond <= 3 then
|
||||
return map_cond[band(cond, 3)]
|
||||
else
|
||||
return format("4*cr%d+%s", rshift(cond, 2), map_cond[band(cond, 3)])
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Output a nicely formatted line with an opcode and operands.
|
||||
local function putop(ctx, text, operands)
|
||||
local pos = ctx.pos
|
||||
local extra = ""
|
||||
if ctx.rel then
|
||||
local sym = ctx.symtab[ctx.rel]
|
||||
if sym then extra = "\t->"..sym end
|
||||
end
|
||||
if ctx.hexdump > 0 then
|
||||
ctx.out(format("%08x %s %-7s %s%s\n",
|
||||
ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
|
||||
else
|
||||
ctx.out(format("%08x %-7s %s%s\n",
|
||||
ctx.addr+pos, text, concat(operands, ", "), extra))
|
||||
end
|
||||
ctx.pos = pos + 4
|
||||
end
|
||||
|
||||
-- Fallback for unknown opcodes.
|
||||
local function unknown(ctx)
|
||||
return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
|
||||
end
|
||||
|
||||
-- Disassemble a single instruction.
|
||||
local function disass_ins(ctx)
|
||||
local pos = ctx.pos
|
||||
local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
|
||||
local op = bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
|
||||
local operands = {}
|
||||
local last = nil
|
||||
local rs = 21
|
||||
ctx.op = op
|
||||
ctx.rel = nil
|
||||
|
||||
local opat = map_pri[rshift(b0, 2)]
|
||||
while type(opat) ~= "string" do
|
||||
if not opat then return unknown(ctx) end
|
||||
opat = opat[band(rshift(op, opat.shift), opat.mask)]
|
||||
end
|
||||
local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
|
||||
local altname, pat2 = match(pat, "|([a-z0-9_.]*)(.*)")
|
||||
if altname then pat = pat2 end
|
||||
|
||||
for p in gmatch(pat, ".") do
|
||||
local x = nil
|
||||
if p == "R" then
|
||||
x = map_gpr[band(rshift(op, rs), 31)]
|
||||
rs = rs - 5
|
||||
elseif p == "F" then
|
||||
x = "f"..band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "A" then
|
||||
x = band(rshift(op, rs), 31)
|
||||
rs = rs - 5
|
||||
elseif p == "S" then
|
||||
x = arshift(lshift(op, 27-rs), 27)
|
||||
rs = rs - 5
|
||||
elseif p == "I" then
|
||||
x = arshift(lshift(op, 16), 16)
|
||||
elseif p == "U" then
|
||||
x = band(op, 0xffff)
|
||||
elseif p == "D" or p == "E" then
|
||||
local disp = arshift(lshift(op, 16), 16)
|
||||
if p == "E" then disp = band(disp, -4) end
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p >= "2" and p <= "8" then
|
||||
local disp = band(rshift(op, rs), 31) * p
|
||||
if last == "r0" then last = "0" end
|
||||
operands[#operands] = format("%d(%s)", disp, last)
|
||||
elseif p == "H" then
|
||||
x = band(rshift(op, rs), 31) + lshift(band(op, 2), 4)
|
||||
rs = rs - 5
|
||||
elseif p == "M" then
|
||||
x = band(rshift(op, rs), 31) + band(op, 0x20)
|
||||
elseif p == "C" then
|
||||
x = condfmt(band(rshift(op, rs), 31))
|
||||
rs = rs - 5
|
||||
elseif p == "B" then
|
||||
local bo = rshift(op, 21)
|
||||
local cond = band(rshift(op, 16), 31)
|
||||
local cn = ""
|
||||
rs = rs - 10
|
||||
if band(bo, 4) == 0 then
|
||||
cn = band(bo, 2) == 0 and "dnz" or "dz"
|
||||
if band(bo, 0x10) == 0 then
|
||||
cn = cn..(band(bo, 8) == 0 and "f" or "t")
|
||||
end
|
||||
if band(bo, 0x10) == 0 then x = condfmt(cond) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
elseif band(bo, 0x10) == 0 then
|
||||
cn = map_cond[band(cond, 3) + (band(bo, 8) == 0 and 4 or 0)]
|
||||
if cond > 3 then x = "cr"..rshift(cond, 2) end
|
||||
name = name..(band(bo, 1) == band(rshift(op, 15), 1) and "-" or "+")
|
||||
end
|
||||
name = gsub(name, "_", cn)
|
||||
elseif p == "J" then
|
||||
x = arshift(lshift(op, 27-rs), 29-rs)*4
|
||||
if band(op, 2) == 0 then x = ctx.addr + pos + x end
|
||||
ctx.rel = x
|
||||
x = "0x"..tohex(x)
|
||||
elseif p == "K" then
|
||||
if band(op, 1) ~= 0 then name = name.."l" end
|
||||
if band(op, 2) ~= 0 then name = name.."a" end
|
||||
elseif p == "X" or p == "Y" then
|
||||
x = band(rshift(op, rs+2), 7)
|
||||
if x == 0 and p == "Y" then x = nil else x = "cr"..x end
|
||||
rs = rs - 5
|
||||
elseif p == "W" then
|
||||
x = "cr"..band(op, 7)
|
||||
elseif p == "Z" then
|
||||
x = band(rshift(op, rs-4), 255)
|
||||
rs = rs - 10
|
||||
elseif p == ">" then
|
||||
operands[#operands] = rshift(operands[#operands], 1)
|
||||
elseif p == "0" then
|
||||
if last == "r0" then
|
||||
operands[#operands] = nil
|
||||
if altname then name = altname end
|
||||
end
|
||||
elseif p == "L" then
|
||||
name = gsub(name, "_", band(op, 0x00200000) ~= 0 and "d" or "w")
|
||||
elseif p == "." then
|
||||
if band(op, 1) == 1 then name = name.."." end
|
||||
elseif p == "N" then
|
||||
if op == 0x60000000 then name = "nop"; break end
|
||||
elseif p == "~" then
|
||||
local n = #operands
|
||||
operands[n-1], operands[n] = operands[n], operands[n-1]
|
||||
elseif p == "=" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] then
|
||||
operands[n] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "%" then
|
||||
local n = #operands
|
||||
if last == operands[n-1] and last == operands[n-2] then
|
||||
operands[n] = nil
|
||||
operands[n-1] = nil
|
||||
name = altname
|
||||
end
|
||||
elseif p == "-" then
|
||||
rs = rs - 5
|
||||
else
|
||||
assert(false)
|
||||
end
|
||||
if x then operands[#operands+1] = x; last = x end
|
||||
end
|
||||
|
||||
return putop(ctx, name, operands)
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Disassemble a block of code.
|
||||
local function disass_block(ctx, ofs, len)
|
||||
if not ofs then ofs = 0 end
|
||||
local stop = len and ofs+len or #ctx.code
|
||||
stop = stop - stop % 4
|
||||
ctx.pos = ofs - ofs % 4
|
||||
ctx.rel = nil
|
||||
while ctx.pos < stop do disass_ins(ctx) end
|
||||
end
|
||||
|
||||
-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
|
||||
local function create_(code, addr, out)
|
||||
local ctx = {}
|
||||
ctx.code = code
|
||||
ctx.addr = addr or 0
|
||||
ctx.out = out or io.write
|
||||
ctx.symtab = {}
|
||||
ctx.disass = disass_block
|
||||
ctx.hexdump = 8
|
||||
return ctx
|
||||
end
|
||||
|
||||
-- Simple API: disassemble code (a string) at address and output via out.
|
||||
local function disass_(code, addr, out)
|
||||
create_(code, addr, out):disass()
|
||||
end
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 32 then return map_gpr[r] end
|
||||
return "f"..(r-32)
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
disass = disass_
|
||||
regname = regname_
|
||||
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x64 disassembler wrapper module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This module just exports the 64 bit functions from the combined
|
||||
-- x86/x64 disassembler module. All the interesting stuff is there.
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local require = require
|
||||
|
||||
module(...)
|
||||
|
||||
local dis_x86 = require(_PACKAGE.."dis_x86")
|
||||
|
||||
create = dis_x86.create64
|
||||
disass = dis_x86.disass64
|
||||
regname = dis_x86.regname64
|
||||
|
||||
Vendored
+836
@@ -0,0 +1,836 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT x86/x64 disassembler module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
-- This is a helper module used by the LuaJIT machine code dumper module.
|
||||
--
|
||||
-- Sending small code snippets to an external disassembler and mixing the
|
||||
-- output with our own stuff was too fragile. So I had to bite the bullet
|
||||
-- and write yet another x86 disassembler. Oh well ...
|
||||
--
|
||||
-- The output format is very similar to what ndisasm generates. But it has
|
||||
-- been developed independently by looking at the opcode tables from the
|
||||
-- Intel and AMD manuals. The supported instruction set is quite extensive
|
||||
-- and reflects what a current generation Intel or AMD CPU implements in
|
||||
-- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
|
||||
-- SSE4.1, SSE4.2, SSE4a 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|cvtsi2ssXrVmt|cvtpi2pdXrMm|cvtsi2sdXrVmt",
|
||||
"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", "callUmp", "$call farDmp",
|
||||
"jmpUmp", "$jmp farDmp", "pushUm" },
|
||||
sldt = { "sldt", "str", "lldt", "ltr", "verr", "verw" },
|
||||
sgdt = { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
|
||||
"smsw", nil, "lmsw", "vm*$invlpg" },
|
||||
bt = { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
|
||||
cmpxchg = { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
|
||||
nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
|
||||
pshiftw = { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
|
||||
pshiftd = { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
|
||||
pshiftq = { nil, nil, "psrlq", nil, nil, nil, "psllq" },
|
||||
pshiftdq = { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
|
||||
fxsave = { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
|
||||
nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
|
||||
prefetch = { "prefetch", "prefetchw" },
|
||||
prefetcht = { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
|
||||
}
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Maps for register names.
|
||||
local map_regs = {
|
||||
B = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
B64 = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
|
||||
"r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
|
||||
W = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
|
||||
"r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
|
||||
D = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
||||
"r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
|
||||
Q = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
|
||||
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
|
||||
M = { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
|
||||
"mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
|
||||
X = { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
|
||||
"xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
|
||||
}
|
||||
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
|
||||
|
||||
-- Return register name for RID.
|
||||
local function regname_(r)
|
||||
if r < 8 then return map_regs.D[r+1] end
|
||||
return map_regs.X[r-7]
|
||||
end
|
||||
|
||||
local function regname64_(r)
|
||||
if r < 16 then return map_regs.Q[r+1] end
|
||||
return map_regs.X[r-15]
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
create = create_
|
||||
create64 = create64_
|
||||
disass = disass_
|
||||
disass64 = disass64_
|
||||
regname = regname_
|
||||
regname64 = regname64_
|
||||
|
||||
Vendored
+700
@@ -0,0 +1,700 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- LuaJIT compiler dump module.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module can be used to debug the JIT compiler itself. It dumps the
|
||||
-- code representations and structures used in various compiler stages.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jdump -e "local x=0; for i=1,1e6 do x=x+i end; print(x)"
|
||||
-- luajit -jdump=im -e "for i=1,1000 do for j=1,1000 do end end" | less -R
|
||||
-- luajit -jdump=is myapp.lua | less -R
|
||||
-- luajit -jdump=-b myapp.lua
|
||||
-- luajit -jdump=+aH,myapp.html myapp.lua
|
||||
-- luajit -jdump=ixT,myapp.dump myapp.lua
|
||||
--
|
||||
-- The first argument specifies the dump mode. The second argument gives
|
||||
-- the output file name. Default output is to stdout, unless the environment
|
||||
-- variable LUAJIT_DUMPFILE is set. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- Different features can be turned on or off with the dump mode. If the
|
||||
-- mode starts with a '+', the following features are added to the default
|
||||
-- set of features; a '-' removes them. Otherwise the features are replaced.
|
||||
--
|
||||
-- The following dump features are available (* marks the default):
|
||||
--
|
||||
-- * t Print a line for each started, ended or aborted trace (see also -jv).
|
||||
-- * b Dump the traced bytecode.
|
||||
-- * i Dump the IR (intermediate representation).
|
||||
-- r Augment the IR with register/stack slots.
|
||||
-- s Dump the snapshot map.
|
||||
-- * m Dump the generated machine code.
|
||||
-- x Print each taken trace exit.
|
||||
-- X Print each taken trace exit and the contents of all registers.
|
||||
--
|
||||
-- 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 == 20002, "LuaJIT core/library version mismatch")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, funcbc = jutil.funcinfo, jutil.funcbc
|
||||
local traceinfo, traceir, tracek = jutil.traceinfo, jutil.traceir, jutil.tracek
|
||||
local tracemc, tracesnap = jutil.tracemc, jutil.tracesnap
|
||||
local traceexitstub, ircalladdr = jutil.traceexitstub, jutil.ircalladdr
|
||||
local bit = require("bit")
|
||||
local band, 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, disass
|
||||
|
||||
-- Active flag, output file handle and dump mode.
|
||||
local active, out, dumpmode
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local symtabmt = { __index = false }
|
||||
local symtab = {}
|
||||
local nexitsym = 0
|
||||
|
||||
-- Fill nested symbol table with per-trace exit stub addresses.
|
||||
local function fillsymtab_tr(tr, nexit)
|
||||
local t = {}
|
||||
symtabmt.__index = t
|
||||
if jit.arch == "mips" or jit.arch == "mipsel" then
|
||||
t[traceexitstub(tr, 0)] = "exit"
|
||||
return
|
||||
end
|
||||
for i=0,nexit-1 do
|
||||
local addr = traceexitstub(tr, i)
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
local addr = traceexitstub(tr, nexit)
|
||||
if addr then t[addr] = "stack_check" end
|
||||
end
|
||||
|
||||
-- Fill symbol table with trace exit stub addresses.
|
||||
local function fillsymtab(tr, nexit)
|
||||
local t = symtab
|
||||
if nexitsym == 0 then
|
||||
local ircall = vmdef.ircall
|
||||
for i=0,#ircall do
|
||||
local addr = ircalladdr(i)
|
||||
if addr ~= 0 then t[addr] = ircall[i] end
|
||||
end
|
||||
end
|
||||
if nexitsym == 1000000 then -- Per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
elseif nexit > nexitsym then -- Shared exit stubs.
|
||||
for i=nexitsym,nexit-1 do
|
||||
local addr = traceexitstub(i)
|
||||
if addr == nil then -- Fall back to per-trace exit stubs.
|
||||
fillsymtab_tr(tr, nexit)
|
||||
setmetatable(symtab, symtabmt)
|
||||
nexit = 1000000
|
||||
break
|
||||
end
|
||||
t[addr] = tostring(i)
|
||||
end
|
||||
nexitsym = nexit
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function dumpwrite(s)
|
||||
out:write(s)
|
||||
end
|
||||
|
||||
-- Disassemble machine code.
|
||||
local function dump_mcode(tr)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local mcode, addr, loop = tracemc(tr)
|
||||
if not mcode then return end
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
out:write("---- TRACE ", tr, " mcode ", #mcode, "\n")
|
||||
local ctx = disass.create(mcode, addr, dumpwrite)
|
||||
ctx.hexdump = 0
|
||||
ctx.symtab = fillsymtab(tr, info.nexit)
|
||||
if loop ~= 0 then
|
||||
symtab[addr+loop] = "LOOP"
|
||||
ctx:disass(0, loop)
|
||||
out:write("->LOOP:\n")
|
||||
ctx:disass(loop, #mcode-loop)
|
||||
symtab[addr+loop] = nil
|
||||
else
|
||||
ctx:disass(0, #mcode)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local irtype_text = {
|
||||
[0] = "nil",
|
||||
"fal",
|
||||
"tru",
|
||||
"lud",
|
||||
"str",
|
||||
"p32",
|
||||
"thr",
|
||||
"pro",
|
||||
"fun",
|
||||
"p64",
|
||||
"cdt",
|
||||
"tab",
|
||||
"udt",
|
||||
"flt",
|
||||
"num",
|
||||
"i8 ",
|
||||
"u8 ",
|
||||
"i16",
|
||||
"u16",
|
||||
"int",
|
||||
"u32",
|
||||
"i64",
|
||||
"u64",
|
||||
"sfp",
|
||||
}
|
||||
|
||||
local colortype_ansi = {
|
||||
[0] = "%s",
|
||||
"%s",
|
||||
"%s",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[32m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[1m%s\027[m",
|
||||
"%s",
|
||||
"\027[33m%s\027[m",
|
||||
"\027[31m%s\027[m",
|
||||
"\027[36m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[34m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
"\027[35m%s\027[m",
|
||||
}
|
||||
|
||||
local function colorize_text(s, 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 = { ["<"] = "<", [">"] = ">", ["&"] = "&", }
|
||||
|
||||
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, span.irt_lud { color: #00c0c0; }
|
||||
span.irt_num { color: #4040c0; }
|
||||
span.irt_int, span.irt_i8, span.irt_u8, span.irt_i16, span.irt_u16 { color: #b040b0; }
|
||||
</style>
|
||||
]]
|
||||
|
||||
local colorize, irtype
|
||||
|
||||
-- Lookup tables to convert some literals into names.
|
||||
local litname = {
|
||||
["SLOAD "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = ""
|
||||
if band(mode, 1) ~= 0 then s = s.."P" end
|
||||
if band(mode, 2) ~= 0 then s = s.."F" end
|
||||
if band(mode, 4) ~= 0 then s = s.."T" end
|
||||
if band(mode, 8) ~= 0 then s = s.."C" end
|
||||
if band(mode, 16) ~= 0 then s = s.."R" end
|
||||
if band(mode, 32) ~= 0 then s = s.."I" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["XLOAD "] = { [0] = "", "R", "V", "RV", "U", "RU", "VU", "RVU", },
|
||||
["CONV "] = setmetatable({}, { __index = function(t, mode)
|
||||
local s = irtype[band(mode, 31)]
|
||||
s = irtype[band(shr(mode, 5), 31)].."."..s
|
||||
if band(mode, 0x400) ~= 0 then s = s.." trunc"
|
||||
elseif band(mode, 0x800) ~= 0 then s = s.." sext" end
|
||||
local c = shr(mode, 14)
|
||||
if c == 2 then s = s.." index" elseif c == 3 then s = s.." check" end
|
||||
t[mode] = s
|
||||
return s
|
||||
end}),
|
||||
["FLOAD "] = vmdef.irfield,
|
||||
["FREF "] = vmdef.irfield,
|
||||
["FPMATH"] = vmdef.irfpm,
|
||||
}
|
||||
|
||||
local function ctlsub(c)
|
||||
if c == "\n" then return "\\n"
|
||||
elseif c == "\r" then return "\\r"
|
||||
elseif c == "\t" then return "\\t"
|
||||
else return format("\\%03d", byte(c))
|
||||
end
|
||||
end
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
local function formatk(tr, idx)
|
||||
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
|
||||
s = fmtfunc(k)
|
||||
elseif tn == "table" then
|
||||
s = format("{%p}", k)
|
||||
elseif tn == "userdata" then
|
||||
if t == 12 then
|
||||
s = format("userdata:%p", k)
|
||||
else
|
||||
s = format("[%p]", k)
|
||||
if s == "[0x00000000]" then s = "NULL" end
|
||||
end
|
||||
elseif t == 21 then -- int64_t
|
||||
s = sub(tostring(k), 1, -3)
|
||||
if sub(s, 1, 1) ~= "-" then s = "+"..s end
|
||||
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)
|
||||
local n = 2
|
||||
for s=0,snap[1]-1 do
|
||||
local sn = snap[n]
|
||||
if shr(sn, 24) == s then
|
||||
n = n + 1
|
||||
local ref = band(sn, 0xffff) - 0x8000 -- REF_BIAS
|
||||
if ref < 0 then
|
||||
out:write(formatk(tr, ref))
|
||||
elseif band(sn, 0x80000) ~= 0 then -- SNAP_SOFTFPNUM
|
||||
out:write(colorize(format("%04d/%04d", ref, ref+1), 14))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ref)
|
||||
out:write(colorize(format("%04d", ref), band(ot, 31)))
|
||||
end
|
||||
out:write(band(sn, 0x10000) == 0 and " " or "|") -- SNAP_FRAME
|
||||
else
|
||||
out:write("---- ")
|
||||
end
|
||||
end
|
||||
out:write("]\n")
|
||||
end
|
||||
|
||||
-- Dump snapshots (not interleaved with IR).
|
||||
local function dump_snap(tr)
|
||||
out:write("---- TRACE ", tr, " snapshots\n")
|
||||
for i=0,1000000000 do
|
||||
local snap = tracesnap(tr, i)
|
||||
if not snap then break end
|
||||
out:write(format("#%-3d %04d [ ", i, snap[0]))
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return a register name or stack slot for a rid/sp location.
|
||||
local function ridsp_name(ridsp, ins)
|
||||
if not disass then disass = require("jit.dis_"..jit.arch) end
|
||||
local rid, slot = band(ridsp, 0xff), shr(ridsp, 8)
|
||||
if rid == 253 or rid == 254 then
|
||||
return (slot == 0 or slot == 255) and " {sink" or format(" {%04d", ins-slot)
|
||||
end
|
||||
if ridsp > 255 then return format("[%x]", slot*4) end
|
||||
if rid < 128 then return disass.regname(rid) end
|
||||
return ""
|
||||
end
|
||||
|
||||
-- Dump CALL* function ref and return optional ctype.
|
||||
local function dumpcallfunc(tr, ins)
|
||||
local ctype
|
||||
if ins > 0 then
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
if band(ot, 31) == 0 then -- nil type means CARG(func, ctype).
|
||||
ins = op1
|
||||
ctype = formatk(tr, op2)
|
||||
end
|
||||
end
|
||||
if ins < 0 then
|
||||
out:write(format("[0x%x](", tonumber((tracek(tr, ins)))))
|
||||
else
|
||||
out:write(format("%04d (", ins))
|
||||
end
|
||||
return ctype
|
||||
end
|
||||
|
||||
-- Recursively gather CALL* args and dump them.
|
||||
local function dumpcallargs(tr, ins)
|
||||
if ins < 0 then
|
||||
out:write(formatk(tr, ins))
|
||||
else
|
||||
local m, ot, op1, op2 = traceir(tr, ins)
|
||||
local oidx = 6*shr(ot, 8)
|
||||
local op = sub(vmdef.irnames, oidx+1, oidx+6)
|
||||
if op == "CARG " then
|
||||
dumpcallargs(tr, op1)
|
||||
if op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(" ", format("%04d", op2))
|
||||
end
|
||||
else
|
||||
out:write(format("%04d", ins))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Dump IR and interleaved snapshots.
|
||||
local function dump_ir(tr, dumpsnap, dumpreg)
|
||||
local info = traceinfo(tr)
|
||||
if not info then return end
|
||||
local nins = info.nins
|
||||
out:write("---- TRACE ", tr, " IR\n")
|
||||
local irnames = vmdef.irnames
|
||||
local snapref = 65536
|
||||
local snap, snapno
|
||||
if dumpsnap then
|
||||
snap = tracesnap(tr, 0)
|
||||
snapref = snap[0]
|
||||
snapno = 0
|
||||
end
|
||||
for ins=1,nins do
|
||||
if ins >= snapref then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
snapno = snapno + 1
|
||||
snap = tracesnap(tr, snapno)
|
||||
snapref = snap and snap[0] or 65536
|
||||
end
|
||||
local m, ot, op1, op2, ridsp = traceir(tr, ins)
|
||||
local oidx, t = 6*shr(ot, 8), band(ot, 31)
|
||||
local op = sub(irnames, oidx+1, oidx+6)
|
||||
if op == "LOOP " then
|
||||
if dumpreg then
|
||||
out:write(format("%04d ------------ LOOP ------------\n", ins))
|
||||
else
|
||||
out:write(format("%04d ------ LOOP ------------\n", ins))
|
||||
end
|
||||
elseif op ~= "NOP " and op ~= "CARG " and
|
||||
(dumpreg or op ~= "RENAME") then
|
||||
local rid = band(ridsp, 255)
|
||||
if dumpreg then
|
||||
out:write(format("%04d %-6s", ins, ridsp_name(ridsp, ins)))
|
||||
else
|
||||
out:write(format("%04d ", ins))
|
||||
end
|
||||
out:write(format("%s%s %s %s ",
|
||||
(rid == 254 or rid == 253) and "}" or
|
||||
(band(ot, 128) == 0 and " " or ">"),
|
||||
band(ot, 64) == 0 and " " or "+",
|
||||
irtype[t], op))
|
||||
local m1, m2 = band(m, 3), band(m, 3*4)
|
||||
if sub(op, 1, 4) == "CALL" then
|
||||
local ctype
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
out:write(format("%-10s (", vmdef.ircall[op2]))
|
||||
else
|
||||
ctype = dumpcallfunc(tr, op2)
|
||||
end
|
||||
if op1 ~= -1 then dumpcallargs(tr, op1) end
|
||||
out:write(")")
|
||||
if ctype then out:write(" ctype ", ctype) end
|
||||
elseif op == "CNEW " and op2 == -1 then
|
||||
out:write(formatk(tr, op1))
|
||||
elseif m1 ~= 3 then -- op1 != IRMnone
|
||||
if op1 < 0 then
|
||||
out:write(formatk(tr, op1))
|
||||
else
|
||||
out:write(format(m1 == 0 and "%04d" or "#%-3d", op1))
|
||||
end
|
||||
if m2 ~= 3*4 then -- op2 != IRMnone
|
||||
if m2 == 1*4 then -- op2 == IRMlit
|
||||
local litn = litname[op]
|
||||
if litn and litn[op2] then
|
||||
out:write(" ", litn[op2])
|
||||
elseif op == "UREFO " or op == "UREFC " then
|
||||
out:write(format(" #%-3d", shr(op2, 8)))
|
||||
else
|
||||
out:write(format(" #%-3d", op2))
|
||||
end
|
||||
elseif op2 < 0 then
|
||||
out:write(" ", formatk(tr, op2))
|
||||
else
|
||||
out:write(format(" %04d", op2))
|
||||
end
|
||||
end
|
||||
end
|
||||
out:write("\n")
|
||||
end
|
||||
end
|
||||
if snap then
|
||||
if dumpreg then
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
else
|
||||
out:write(format(".... SNAP #%-3d [ ", snapno))
|
||||
end
|
||||
printsnap(tr, snap)
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local recprefix = ""
|
||||
local recdepth = 0
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
err = format(vmdef.traceerr[err], info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "stop" or (what == "abort" and dumpmode.a) then
|
||||
if dumpmode.i then dump_ir(tr, dumpmode.s, dumpmode.r and what == "stop")
|
||||
elseif dumpmode.s then dump_snap(tr) end
|
||||
if dumpmode.m then dump_mcode(tr) end
|
||||
end
|
||||
if what == "start" then
|
||||
if dumpmode.H then out:write('<pre class="ljdump">\n') end
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
if otr then out:write(" ", otr, "/", oex) end
|
||||
out:write(" ", fmtfunc(func, pc), "\n")
|
||||
recprefix = ""
|
||||
elseif what == "stop" or what == "abort" then
|
||||
out:write("---- TRACE ", tr, " ", what)
|
||||
recprefix = nil
|
||||
if what == "abort" then
|
||||
out:write(" ", fmtfunc(func, pc), " -- ", fmterr(otr, oex), "\n")
|
||||
else
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if link == tr or link == 0 then
|
||||
out:write(" -> ", ltype, "\n")
|
||||
elseif ltype == "root" then
|
||||
out:write(" -> ", link, "\n")
|
||||
else
|
||||
out:write(" -> ", link, " ", ltype, "\n")
|
||||
end
|
||||
end
|
||||
if dumpmode.H then out:write("</pre>\n\n") else out:write("\n") end
|
||||
else
|
||||
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
|
||||
if pc >= 0 then
|
||||
line = bcline(func, pc, recprefix)
|
||||
if dumpmode.H then line = gsub(line, "[<>&]", html_escape) end
|
||||
else
|
||||
line = "0000 "..recprefix.." FUNCC \n"
|
||||
callee = func
|
||||
end
|
||||
if pc <= 0 then
|
||||
out:write(sub(line, 1, -2), " ; ", fmtfunc(func), "\n")
|
||||
else
|
||||
out:write(line)
|
||||
end
|
||||
if pc >= 0 and band(funcbc(func, pc), 0xff) < 16 then -- ORDER BC
|
||||
out:write(bcline(func, pc+1, recprefix)) -- Write JMP for cond.
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Dump taken trace exits.
|
||||
local function dump_texit(tr, ex, ngpr, nfpr, ...)
|
||||
out:write("---- TRACE ", tr, " exit ", ex, "\n")
|
||||
if dumpmode.X then
|
||||
local regs = {...}
|
||||
if jit.arch == "x64" then
|
||||
for i=1,ngpr do
|
||||
out:write(format(" %016x", regs[i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,ngpr do
|
||||
out:write(format(" %08x", regs[i]))
|
||||
if i % 8 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
if jit.arch == "mips" or jit.arch == "mipsel" then
|
||||
for i=1,nfpr,2 do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 8 == 7 then out:write("\n") end
|
||||
end
|
||||
else
|
||||
for i=1,nfpr do
|
||||
out:write(format(" %+17.14g", regs[ngpr+i]))
|
||||
if i % 4 == 0 then out:write("\n") end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_texit)
|
||||
jit.attach(dump_record)
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(opt, outfile)
|
||||
if active then dumpoff() end
|
||||
|
||||
local 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.
|
||||
|
||||
Vendored
+167
@@ -0,0 +1,167 @@
|
||||
----------------------------------------------------------------------------
|
||||
-- Verbose mode of the LuaJIT compiler.
|
||||
--
|
||||
-- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
|
||||
-- Released under the MIT license. See Copyright Notice in luajit.h
|
||||
----------------------------------------------------------------------------
|
||||
--
|
||||
-- This module shows verbose information about the progress of the
|
||||
-- JIT compiler. It prints one line for each generated trace. This module
|
||||
-- is useful to see which code has been compiled or where the compiler
|
||||
-- punts and falls back to the interpreter.
|
||||
--
|
||||
-- Example usage:
|
||||
--
|
||||
-- luajit -jv -e "for i=1,1000 do for j=1,1000 do end end"
|
||||
-- luajit -jv=myapp.out myapp.lua
|
||||
--
|
||||
-- Default output is to stderr. To redirect the output to a file, pass a
|
||||
-- filename as an argument (use '-' for stdout) or set the environment
|
||||
-- variable LUAJIT_VERBOSEFILE. The file is overwritten every time the
|
||||
-- module is started.
|
||||
--
|
||||
-- The output from the first example should look like this:
|
||||
--
|
||||
-- [TRACE 1 (command line):1 loop]
|
||||
-- [TRACE 2 (1/3) (command line):1 -> 1]
|
||||
--
|
||||
-- The first number in each line is the internal trace number. Next are
|
||||
-- the file name ('(command line)') and the line number (':1') where the
|
||||
-- trace has started. Side traces also show the parent trace number and
|
||||
-- the exit number where they are attached to in parentheses ('(1/3)').
|
||||
-- An arrow at the end shows where the trace links to ('-> 1'), unless
|
||||
-- it loops to itself.
|
||||
--
|
||||
-- In this case the inner loop gets hot and is traced first, generating
|
||||
-- a root trace. Then the last exit from the 1st trace gets hot, too,
|
||||
-- and triggers generation of the 2nd trace. The side trace follows the
|
||||
-- path along the outer loop and *around* the inner loop, back to its
|
||||
-- start, and then links to the 1st trace. Yes, this may seem unusual,
|
||||
-- if you know how traditional compilers work. Trace compilers are full
|
||||
-- of surprises like this -- have fun! :-)
|
||||
--
|
||||
-- Aborted traces are shown like this:
|
||||
--
|
||||
-- [TRACE --- foo.lua:44 -- leaving loop in root trace at foo:lua:50]
|
||||
--
|
||||
-- Don't worry -- trace aborts are quite common, even in programs which
|
||||
-- can be fully compiled. The compiler may retry several times until it
|
||||
-- finds a suitable trace.
|
||||
--
|
||||
-- Of course this doesn't work with features that are not-yet-implemented
|
||||
-- (NYI error messages). The VM simply falls back to the interpreter. This
|
||||
-- may not matter at all if the particular trace is not very high up in
|
||||
-- the CPU usage profile. Oh, and the interpreter is quite fast, too.
|
||||
--
|
||||
-- Also check out the -jdump module, which prints all the gory details.
|
||||
--
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Cache some library functions and objects.
|
||||
local jit = require("jit")
|
||||
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
|
||||
local jutil = require("jit.util")
|
||||
local vmdef = require("jit.vmdef")
|
||||
local funcinfo, traceinfo = jutil.funcinfo, jutil.traceinfo
|
||||
local type, format = type, string.format
|
||||
local stdout, stderr = io.stdout, io.stderr
|
||||
|
||||
-- Active flag and output file handle.
|
||||
local active, out
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
local startloc, startex
|
||||
|
||||
local function fmtfunc(func, pc)
|
||||
local fi = funcinfo(func, pc)
|
||||
if fi.loc then
|
||||
return fi.loc
|
||||
elseif fi.ffid then
|
||||
return vmdef.ffnames[fi.ffid]
|
||||
elseif fi.addr then
|
||||
return format("C:%x", fi.addr)
|
||||
else
|
||||
return "(?)"
|
||||
end
|
||||
end
|
||||
|
||||
-- Format trace error message.
|
||||
local function fmterr(err, info)
|
||||
if type(err) == "number" then
|
||||
if type(info) == "function" then info = fmtfunc(info) end
|
||||
err = format(vmdef.traceerr[err], info)
|
||||
end
|
||||
return err
|
||||
end
|
||||
|
||||
-- Dump trace states.
|
||||
local function dump_trace(what, tr, func, pc, otr, oex)
|
||||
if what == "start" then
|
||||
startloc = fmtfunc(func, pc)
|
||||
startex = otr and "("..otr.."/"..oex..") " or ""
|
||||
else
|
||||
if what == "abort" then
|
||||
local loc = fmtfunc(func, pc)
|
||||
if loc ~= startloc then
|
||||
out:write(format("[TRACE --- %s%s -- %s at %s]\n",
|
||||
startex, startloc, fmterr(otr, oex), loc))
|
||||
else
|
||||
out:write(format("[TRACE --- %s%s -- %s]\n",
|
||||
startex, startloc, fmterr(otr, oex)))
|
||||
end
|
||||
elseif what == "stop" then
|
||||
local info = traceinfo(tr)
|
||||
local link, ltype = info.link, info.linktype
|
||||
if ltype == "interpreter" then
|
||||
out:write(format("[TRACE %3s %s%s -- fallback to interpreter]\n",
|
||||
tr, startex, startloc))
|
||||
elseif link == tr or link == 0 then
|
||||
out:write(format("[TRACE %3s %s%s %s]\n",
|
||||
tr, startex, startloc, ltype))
|
||||
elseif ltype == "root" then
|
||||
out:write(format("[TRACE %3s %s%s -> %d]\n",
|
||||
tr, startex, startloc, link))
|
||||
else
|
||||
out:write(format("[TRACE %3s %s%s -> %d %s]\n",
|
||||
tr, startex, startloc, link, ltype))
|
||||
end
|
||||
else
|
||||
out:write(format("[TRACE %s]\n", what))
|
||||
end
|
||||
out:flush()
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
-- Detach dump handlers.
|
||||
local function dumpoff()
|
||||
if active then
|
||||
active = false
|
||||
jit.attach(dump_trace)
|
||||
if out and out ~= stdout and out ~= stderr then out:close() end
|
||||
out = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Open the output file and attach dump handlers.
|
||||
local function dumpon(outfile)
|
||||
if active then dumpoff() end
|
||||
if not outfile then outfile = os.getenv("LUAJIT_VERBOSEFILE") end
|
||||
if outfile then
|
||||
out = outfile == "-" and stdout or assert(io.open(outfile, "w"))
|
||||
else
|
||||
out = stderr
|
||||
end
|
||||
jit.attach(dump_trace, "trace")
|
||||
active = true
|
||||
end
|
||||
|
||||
-- Public module functions.
|
||||
module(...)
|
||||
|
||||
on = dumpon
|
||||
off = dumpoff
|
||||
start = dumpon -- For -j command line option.
|
||||
|
||||
Vendored
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
** $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);
|
||||
|
||||
/* From Lua 5.2. */
|
||||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname);
|
||||
LUALIB_API int luaL_execresult(lua_State *L, int stat);
|
||||
LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename,
|
||||
const char *mode);
|
||||
LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz,
|
||||
const char *name, const char *mode);
|
||||
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
|
||||
int level);
|
||||
|
||||
|
||||
/*
|
||||
** ===============================================================
|
||||
** 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
|
||||
Vendored
+356
@@ -0,0 +1,356 @@
|
||||
/*
|
||||
** Auxiliary library for the Lua/C API.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major parts taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_aux_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
#if LJ_TARGET_POSIX
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
/* -- I/O error handling -------------------------------------------------- */
|
||||
|
||||
LUALIB_API int luaL_fileresult(lua_State *L, int stat, const char *fname)
|
||||
{
|
||||
if (stat) {
|
||||
setboolV(L->top++, 1);
|
||||
return 1;
|
||||
} else {
|
||||
int en = errno; /* Lua API calls may change this value. */
|
||||
setnilV(L->top++);
|
||||
if (fname)
|
||||
lua_pushfstring(L, "%s: %s", fname, strerror(en));
|
||||
else
|
||||
lua_pushfstring(L, "%s", strerror(en));
|
||||
setintV(L->top++, en);
|
||||
lj_trace_abort(G(L));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API int luaL_execresult(lua_State *L, int stat)
|
||||
{
|
||||
if (stat != -1) {
|
||||
#if LJ_TARGET_POSIX
|
||||
if (WIFSIGNALED(stat)) {
|
||||
stat = WTERMSIG(stat);
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "signal");
|
||||
} else {
|
||||
if (WIFEXITED(stat))
|
||||
stat = WEXITSTATUS(stat);
|
||||
if (stat == 0)
|
||||
setboolV(L->top++, 1);
|
||||
else
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "exit");
|
||||
}
|
||||
#else
|
||||
if (stat == 0)
|
||||
setboolV(L->top++, 1);
|
||||
else
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "exit");
|
||||
#endif
|
||||
setintV(L->top++, stat);
|
||||
return 3;
|
||||
}
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
}
|
||||
|
||||
/* -- Module registration ------------------------------------------------- */
|
||||
|
||||
LUALIB_API const char *luaL_findtable(lua_State *L, int idx,
|
||||
const char *fname, int szhint)
|
||||
{
|
||||
const char *e;
|
||||
lua_pushvalue(L, idx);
|
||||
do {
|
||||
e = strchr(fname, '.');
|
||||
if (e == NULL) e = fname + strlen(fname);
|
||||
lua_pushlstring(L, fname, (size_t)(e - fname));
|
||||
lua_rawget(L, -2);
|
||||
if (lua_isnil(L, -1)) { /* no such field? */
|
||||
lua_pop(L, 1); /* remove this nil */
|
||||
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
|
||||
lua_pushlstring(L, fname, (size_t)(e - fname));
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -4); /* set new table into field */
|
||||
} else if (!lua_istable(L, -1)) { /* field has a non-table value? */
|
||||
lua_pop(L, 2); /* remove table and value */
|
||||
return fname; /* return problematic part of the name */
|
||||
}
|
||||
lua_remove(L, -2); /* remove previous table */
|
||||
fname = e + 1;
|
||||
} while (*e == '.');
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int libsize(const luaL_Reg *l)
|
||||
{
|
||||
int size = 0;
|
||||
for (; l->name; l++) size++;
|
||||
return size;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_openlib(lua_State *L, const char *libname,
|
||||
const luaL_Reg *l, int nup)
|
||||
{
|
||||
lj_lib_checkfpu(L);
|
||||
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
|
||||
|
||||
/* Convert a stack index to an absolute index. */
|
||||
#define abs_index(L, i) \
|
||||
((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||
|
||||
LUALIB_API int luaL_ref(lua_State *L, int t)
|
||||
{
|
||||
int ref;
|
||||
t = abs_index(L, t);
|
||||
if (lua_isnil(L, -1)) {
|
||||
lua_pop(L, 1); /* remove from stack */
|
||||
return LUA_REFNIL; /* `nil' has a unique fixed reference */
|
||||
}
|
||||
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
|
||||
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
|
||||
lua_pop(L, 1); /* remove it from stack */
|
||||
if (ref != 0) { /* any free element? */
|
||||
lua_rawgeti(L, t, ref); /* remove it from list */
|
||||
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
|
||||
} else { /* no free elements */
|
||||
ref = (int)lua_objlen(L, t);
|
||||
ref++; /* create new reference */
|
||||
}
|
||||
lua_rawseti(L, t, ref);
|
||||
return ref;
|
||||
}
|
||||
|
||||
LUALIB_API void luaL_unref(lua_State *L, int t, int ref)
|
||||
{
|
||||
if (ref >= 0) {
|
||||
t = abs_index(L, t);
|
||||
lua_rawgeti(L, t, FREELIST_REF);
|
||||
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
|
||||
lua_pushinteger(L, ref);
|
||||
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Default allocator and panic function -------------------------------- */
|
||||
|
||||
static int panic(lua_State *L)
|
||||
{
|
||||
const char *s = lua_tostring(L, -1);
|
||||
fputs("PANIC: unprotected error in call to Lua API (", stderr);
|
||||
fputs(s ? s : "?", stderr);
|
||||
fputc(')', stderr); fputc('\n', stderr);
|
||||
fflush(stderr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef LUAJIT_USE_SYSMALLOC
|
||||
|
||||
#if LJ_64
|
||||
#error "Must use builtin allocator for 64 bit target"
|
||||
#endif
|
||||
|
||||
static void *mem_alloc(void *ud, void *ptr, size_t osize, size_t nsize)
|
||||
{
|
||||
(void)ud;
|
||||
(void)osize;
|
||||
if (nsize == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
return realloc(ptr, nsize);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate(void)
|
||||
{
|
||||
lua_State *L = lua_newstate(mem_alloc, NULL);
|
||||
if (L) G(L)->panic = panic;
|
||||
return L;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "lj_alloc.h"
|
||||
|
||||
LUALIB_API lua_State *luaL_newstate(void)
|
||||
{
|
||||
lua_State *L;
|
||||
void *ud = lj_alloc_create();
|
||||
if (ud == NULL) return NULL;
|
||||
#if LJ_64
|
||||
L = lj_state_newstate(lj_alloc_f, ud);
|
||||
#else
|
||||
L = lua_newstate(lj_alloc_f, ud);
|
||||
#endif
|
||||
if (L) G(L)->panic = panic;
|
||||
return L;
|
||||
}
|
||||
|
||||
#if LJ_64
|
||||
LUA_API lua_State *lua_newstate(lua_Alloc f, void *ud)
|
||||
{
|
||||
UNUSED(f); UNUSED(ud);
|
||||
fputs("Must use luaL_newstate() for 64 bit target\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Vendored
+683
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
** Base and coroutine library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_base_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_state.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#endif
|
||||
#include "lj_bc.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_char.h"
|
||||
#include "lj_strscan.h"
|
||||
#include "lj_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("trace")
|
||||
LJLIB_PUSH("cdata")
|
||||
LJLIB_PUSH("table")
|
||||
LJLIB_PUSH(top-9) /* userdata */
|
||||
LJLIB_PUSH("number")
|
||||
LJLIB_ASM_(type) LJLIB_REC(.)
|
||||
/* Recycle the lj_lib_checkany(L, 1) from assert. */
|
||||
|
||||
/* -- Base library: iterators --------------------------------------------- */
|
||||
|
||||
/* This solves a circular dependency problem -- change FF_next_N as needed. */
|
||||
LJ_STATIC_ASSERT((int)FF_next == FF_next_N);
|
||||
|
||||
LJLIB_ASM(next)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
#if LJ_52 || LJ_HASFFI
|
||||
static int ffh_pairs(lua_State *L, MMS mm)
|
||||
{
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
cTValue *mo = lj_meta_lookup(L, o, mm);
|
||||
if ((LJ_52 || tviscdata(o)) && !tvisnil(mo)) {
|
||||
L->top = o+1; /* Only keep one argument. */
|
||||
copyTV(L, L->base-1, mo); /* Replace callable. */
|
||||
return FFH_TAILCALL;
|
||||
} else {
|
||||
if (!tvistab(o)) lj_err_argt(L, 1, LUA_TTABLE);
|
||||
setfuncV(L, o-1, funcV(lj_lib_upvalue(L, 1)));
|
||||
if (mm == MM_pairs) setnilV(o+1); else setintV(o+1, 0);
|
||||
return FFH_RES(3);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define ffh_pairs(L, mm) (lj_lib_checktab(L, 1), FFH_UNREACHABLE)
|
||||
#endif
|
||||
|
||||
LJLIB_PUSH(lastcl)
|
||||
LJLIB_ASM(pairs)
|
||||
{
|
||||
return ffh_pairs(L, MM_pairs);
|
||||
}
|
||||
|
||||
LJLIB_NOREGUV LJLIB_ASM(ipairs_aux) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checktab(L, 1);
|
||||
lj_lib_checkint(L, 2);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(lastcl)
|
||||
LJLIB_ASM(ipairs) LJLIB_REC(.)
|
||||
{
|
||||
return ffh_pairs(L, MM_ipairs);
|
||||
}
|
||||
|
||||
/* -- Base library: getters and setters ----------------------------------- */
|
||||
|
||||
LJLIB_ASM_(getmetatable) LJLIB_REC(.)
|
||||
/* Recycle the lj_lib_checkany(L, 1) from assert. */
|
||||
|
||||
LJLIB_ASM(setmetatable) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
GCtab *mt = lj_lib_checktabornil(L, 2);
|
||||
if (!tvisnil(lj_meta_lookup(L, L->base, MM_metatable)))
|
||||
lj_err_caller(L, LJ_ERR_PROTMT);
|
||||
setgcref(t->metatable, obj2gco(mt));
|
||||
if (mt) { lj_gc_objbarriert(L, t, mt); }
|
||||
settabV(L, L->base-1, 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_debug_frame(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_debug_frame(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;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_CF(rawlen) LJLIB_REC(.)
|
||||
{
|
||||
cTValue *o = L->base;
|
||||
int32_t len;
|
||||
if (L->top > o && tvisstr(o))
|
||||
len = (int32_t)strV(o)->len;
|
||||
else
|
||||
len = (int32_t)lj_tab_len(lj_lib_checktab(L, 1));
|
||||
setintV(L->top-1, len);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
LJLIB_CF(unpack)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n, i = lj_lib_optint(L, 2, 1);
|
||||
int32_t e = (L->base+3-1 < L->top && !tvisnil(L->base+3-1)) ?
|
||||
lj_lib_checkint(L, 3) : (int32_t)lj_tab_len(t);
|
||||
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) LJLIB_REC(.)
|
||||
{
|
||||
int32_t n = (int32_t)(L->top - L->base);
|
||||
if (n >= 1 && tvisstr(L->base) && *strVdata(L->base) == '#') {
|
||||
setintV(L->top-1, n-1);
|
||||
return 1;
|
||||
} else {
|
||||
int32_t i = lj_lib_checkint(L, 1);
|
||||
if (i < 0) i = n + i; else if (i > n) i = n;
|
||||
if (i < 1)
|
||||
lj_err_arg(L, 1, LJ_ERR_IDXRNG);
|
||||
return n - i;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Base library: conversions ------------------------------------------- */
|
||||
|
||||
LJLIB_ASM(tonumber) LJLIB_REC(.)
|
||||
{
|
||||
int32_t base = lj_lib_optint(L, 2, 10);
|
||||
if (base == 10) {
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
if (lj_strscan_numberobj(o)) {
|
||||
copyTV(L, L->base-1, o);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
#if LJ_HASFFI
|
||||
if (tviscdata(o)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = lj_ctype_rawref(cts, cdataV(o)->ctypeid);
|
||||
if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
|
||||
if (ctype_isnum(ct->info) || ctype_iscomplex(ct->info)) {
|
||||
if (LJ_DUALNUM && ctype_isinteger_or_bool(ct->info) &&
|
||||
ct->size <= 4 && !(ct->size == 4 && (ct->info & CTF_UNSIGNED))) {
|
||||
int32_t i;
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o, 0);
|
||||
setintV(L->base-1, i);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_DOUBLE),
|
||||
(uint8_t *)&(L->base-1)->n, o, 0);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} 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_char_isspace((unsigned char)(*ep))) ep++;
|
||||
if (*ep == '\0') {
|
||||
if (LJ_DUALNUM && LJ_LIKELY(ul < 0x80000000u))
|
||||
setintV(L->base-1, (int32_t)ul);
|
||||
else
|
||||
setnumV(L->base-1, (lua_Number)ul);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
setnilV(L->base-1);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
|
||||
LJLIB_PUSH("nil")
|
||||
LJLIB_PUSH("false")
|
||||
LJLIB_PUSH("true")
|
||||
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_TAILCALL;
|
||||
} else {
|
||||
GCstr *s;
|
||||
if (tvisnumber(o)) {
|
||||
s = lj_str_fromnumber(L, o);
|
||||
} else if (tvispri(o)) {
|
||||
s = strV(lj_lib_upvalue(L, -(int32_t)itype(o)));
|
||||
} else {
|
||||
if (tvisfunc(o) && isffunc(funcV(o)))
|
||||
lua_pushfstring(L, "function: builtin#%d", funcV(o)->c.ffid);
|
||||
else
|
||||
lua_pushfstring(L, "%s: %p", lj_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: throw and catch errors -------------------------------- */
|
||||
|
||||
LJLIB_CF(error)
|
||||
{
|
||||
int32_t level = lj_lib_optint(L, 2, 1);
|
||||
lua_settop(L, 1);
|
||||
if (lua_isstring(L, 1) && level > 0) {
|
||||
luaL_where(L, level);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_concat(L, 2);
|
||||
}
|
||||
return lua_error(L);
|
||||
}
|
||||
|
||||
LJLIB_ASM(pcall) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
lj_lib_checkfunc(L, 2); /* For xpcall only. */
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
LJLIB_ASM_(xpcall) LJLIB_REC(.)
|
||||
|
||||
/* -- Base library: load Lua code ----------------------------------------- */
|
||||
|
||||
static int load_aux(lua_State *L, int status, int envarg)
|
||||
{
|
||||
if (status == 0) {
|
||||
if (tvistab(L->base+envarg-1)) {
|
||||
GCfunc *fn = funcV(L->top-1);
|
||||
GCtab *t = tabV(L->base+envarg-1);
|
||||
setgcref(fn->c.env, obj2gco(t));
|
||||
lj_gc_objbarrier(L, fn, t);
|
||||
}
|
||||
return 1;
|
||||
} else {
|
||||
setnilV(L->top-2);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(loadfile)
|
||||
{
|
||||
GCstr *fname = lj_lib_optstr(L, 1);
|
||||
GCstr *mode = lj_lib_optstr(L, 2);
|
||||
int status;
|
||||
lua_settop(L, 3); /* Ensure env arg exists. */
|
||||
status = luaL_loadfilex(L, fname ? strdata(fname) : NULL,
|
||||
mode ? strdata(mode) : NULL);
|
||||
return load_aux(L, status, 3);
|
||||
}
|
||||
|
||||
static const char *reader_func(lua_State *L, void *ud, size_t *size)
|
||||
{
|
||||
UNUSED(ud);
|
||||
luaL_checkstack(L, 2, "too many nested functions");
|
||||
copyTV(L, L->top++, L->base);
|
||||
lua_call(L, 0, 1); /* Call user-supplied function. */
|
||||
L->top--;
|
||||
if (tvisnil(L->top)) {
|
||||
*size = 0;
|
||||
return NULL;
|
||||
} else if (tvisstr(L->top) || tvisnumber(L->top)) {
|
||||
copyTV(L, L->base+4, L->top); /* Anchor string in reserved stack slot. */
|
||||
return lua_tolstring(L, 5, size);
|
||||
} else {
|
||||
lj_err_caller(L, LJ_ERR_RDRSTR);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(load)
|
||||
{
|
||||
GCstr *name = lj_lib_optstr(L, 2);
|
||||
GCstr *mode = lj_lib_optstr(L, 3);
|
||||
int status;
|
||||
if (L->base < L->top && (tvisstr(L->base) || tvisnumber(L->base))) {
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
lua_settop(L, 4); /* Ensure env arg exists. */
|
||||
status = luaL_loadbufferx(L, strdata(s), s->len, strdata(name ? name : s),
|
||||
mode ? strdata(mode) : NULL);
|
||||
} else {
|
||||
lj_lib_checkfunc(L, 1);
|
||||
lua_settop(L, 5); /* Reserve a slot for the string from the reader. */
|
||||
status = lua_loadx(L, reader_func, NULL, name ? strdata(name) : "=(load)",
|
||||
mode ? strdata(mode) : NULL);
|
||||
}
|
||||
return load_aux(L, status, 4);
|
||||
}
|
||||
|
||||
LJLIB_CF(loadstring)
|
||||
{
|
||||
return lj_cf_load(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(dofile)
|
||||
{
|
||||
GCstr *fname = lj_lib_optstr(L, 1);
|
||||
setnilV(L->top);
|
||||
L->top = L->base+1;
|
||||
if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != 0)
|
||||
lua_error(L);
|
||||
lua_call(L, 0, LUA_MULTRET);
|
||||
return (int)(L->top - L->base) - 1;
|
||||
}
|
||||
|
||||
/* -- Base library: GC control -------------------------------------------- */
|
||||
|
||||
LJLIB_CF(gcinfo)
|
||||
{
|
||||
setintV(L->top++, (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, (lua_Number)G(L)->gc.total/1024.0);
|
||||
} else {
|
||||
int res = lua_gc(L, opt, data);
|
||||
if (opt == LUA_GCSTEP)
|
||||
setboolV(L->top, res);
|
||||
else
|
||||
setintV(L->top, res);
|
||||
}
|
||||
L->top++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* -- Base library: miscellaneous functions ------------------------------- */
|
||||
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds weak table. */
|
||||
LJLIB_CF(newproxy)
|
||||
{
|
||||
lua_settop(L, 1);
|
||||
lua_newuserdata(L, 0);
|
||||
if (lua_toboolean(L, 1) == 0) { /* newproxy(): without metatable. */
|
||||
return 1;
|
||||
} else if (lua_isboolean(L, 1)) { /* newproxy(true): with metatable. */
|
||||
lua_newtable(L);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_pushboolean(L, 1);
|
||||
lua_rawset(L, lua_upvalueindex(1)); /* Remember mt in weak table. */
|
||||
} else { /* newproxy(proxy): inherit metatable. */
|
||||
int validproxy = 0;
|
||||
if (lua_getmetatable(L, 1)) {
|
||||
lua_rawget(L, lua_upvalueindex(1));
|
||||
validproxy = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
if (!validproxy)
|
||||
lj_err_arg(L, 1, LJ_ERR_NOPROXY);
|
||||
lua_getmetatable(L, 1);
|
||||
}
|
||||
lua_setmetatable(L, 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH("tostring")
|
||||
LJLIB_CF(print)
|
||||
{
|
||||
ptrdiff_t i, nargs = L->top - L->base;
|
||||
cTValue *tv = lj_tab_getstr(tabref(L->env), strV(lj_lib_upvalue(L, 1)));
|
||||
int shortcut;
|
||||
if (tv && !tvisnil(tv)) {
|
||||
copyTV(L, L->top++, tv);
|
||||
} else {
|
||||
setstrV(L, L->top++, strV(lj_lib_upvalue(L, 1)));
|
||||
lua_gettable(L, LUA_GLOBALSINDEX);
|
||||
tv = L->top-1;
|
||||
}
|
||||
shortcut = (tvisfunc(tv) && funcV(tv)->c.ffid == FF_tostring);
|
||||
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 && tvisint(o)) {
|
||||
char buf[LJ_STR_INTBUF];
|
||||
char *p = lj_str_bufint(buf, intV(o));
|
||||
size = (size_t)(buf+LJ_STR_INTBUF-p);
|
||||
str = p;
|
||||
} else if (shortcut && tvisnum(o)) {
|
||||
char buf[LJ_STR_NUMBUF];
|
||||
size = lj_str_bufnum(buf, o);
|
||||
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 > tvref(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 LJ_52
|
||||
int ismain = lua_pushthread(L);
|
||||
setboolV(L->top++, ismain);
|
||||
return 2;
|
||||
#else
|
||||
if (lua_pushthread(L))
|
||||
setnilV(L->top++);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(coroutine_create)
|
||||
{
|
||||
lua_State *L1;
|
||||
if (!(L->base < L->top && tvisfunc(L->base)))
|
||||
lj_err_argt(L, 1, LUA_TFUNCTION);
|
||||
L1 = lua_newthread(L);
|
||||
setfuncV(L, L1->top++, funcV(L->base));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_ASM(coroutine_yield)
|
||||
{
|
||||
lj_err_caller(L, LJ_ERR_CYIELD);
|
||||
return FFH_UNREACHABLE;
|
||||
}
|
||||
|
||||
static int ffh_resume(lua_State *L, lua_State *co, int wrap)
|
||||
{
|
||||
if (co->cframe != NULL || co->status > LUA_YIELD ||
|
||||
(co->status == 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));
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(coroutine_resume)
|
||||
{
|
||||
if (!(L->top > L->base && tvisthread(L->base)))
|
||||
lj_err_arg(L, 1, LJ_ERR_NOCORO);
|
||||
return ffh_resume(L, threadV(L->base), 0);
|
||||
}
|
||||
|
||||
LJLIB_NOREG LJLIB_ASM(coroutine_wrap_aux)
|
||||
{
|
||||
return ffh_resume(L, threadV(lj_lib_upvalue(L, 1)), 1);
|
||||
}
|
||||
|
||||
/* Inline declarations. */
|
||||
LJ_ASMF void lj_ff_coroutine_wrap_aux(void);
|
||||
#if !(LJ_TARGET_MIPS && defined(ljamalg_c))
|
||||
LJ_FUNCA_NORET void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L,
|
||||
lua_State *co);
|
||||
#endif
|
||||
|
||||
/* Error handler, called from assembler VM. */
|
||||
void LJ_FASTCALL lj_ffh_coroutine_wrap_err(lua_State *L, lua_State *co)
|
||||
{
|
||||
co->top--; copyTV(L, L->top, co->top); L->top++;
|
||||
if (tvisstr(L->top-1))
|
||||
lj_err_callermsg(L, strVdata(L->top-1));
|
||||
else
|
||||
lj_err_run(L);
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn);
|
||||
|
||||
LJLIB_CF(coroutine_wrap)
|
||||
{
|
||||
lj_cf_coroutine_create(L);
|
||||
lj_lib_pushcc(L, lj_ffh_coroutine_wrap_aux, FF_coroutine_wrap_aux, 1);
|
||||
setpc_wrap_aux(L, funcV(L->top-1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* Fix the PC of wrap_aux. Really ugly workaround. */
|
||||
static void setpc_wrap_aux(lua_State *L, GCfunc *fn)
|
||||
{
|
||||
setmref(fn->c.pc, &L2GG(L)->bcff[lj_lib_init_coroutine[1]+2]);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void newproxy_weaktable(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
GCtab *t = lj_tab_new(L, 0, 1);
|
||||
settabV(L, L->top++, t);
|
||||
setgcref(t->metatable, obj2gco(t));
|
||||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")),
|
||||
lj_str_newlit(L, "kv"));
|
||||
t->nomm = (uint8_t)(~(1u<<MM_mode));
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_base(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: Table and value are the same. */
|
||||
GCtab *env = tabref(L->env);
|
||||
settabV(L, lj_tab_setstr(L, env, lj_str_newlit(L, "_G")), env);
|
||||
lua_pushliteral(L, LUA_VERSION); /* top-3. */
|
||||
newproxy_weaktable(L); /* top-2. */
|
||||
LJ_LIB_REG(L, "_G", base);
|
||||
LJ_LIB_REG(L, LUA_COLIBNAME, coroutine);
|
||||
return 2;
|
||||
}
|
||||
|
||||
Vendored
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
** Bit manipulation library.
|
||||
** Copyright (C) 2005-2013 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_checknumber(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_checknumber(L, 1);
|
||||
lj_lib_checkbit(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_checknumber(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_lib_checkbit(L, 1);
|
||||
int32_t i, n = L->base+1 >= L->top ? 8 : lj_lib_checkbit(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, LUA_BITLIBNAME, bit);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+405
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
** Debug library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_debug_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_debug.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_debug
|
||||
|
||||
LJLIB_CF(debug_getregistry)
|
||||
{
|
||||
copyTV(L, L->top++, registry(L));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getmetatable)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
if (!lua_getmetatable(L, 1)) {
|
||||
setnilV(L->top-1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setmetatable)
|
||||
{
|
||||
lj_lib_checktabornil(L, 2);
|
||||
L->top = L->base+2;
|
||||
lua_setmetatable(L, 1);
|
||||
#if !LJ_52
|
||||
setboolV(L->top-1, 1);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getfenv)
|
||||
{
|
||||
lj_lib_checkany(L, 1);
|
||||
lua_getfenv(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setfenv)
|
||||
{
|
||||
lj_lib_checktab(L, 2);
|
||||
L->top = L->base+2;
|
||||
if (!lua_setfenv(L, 1))
|
||||
lj_err_caller(L, LJ_ERR_SETFENV);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void settabss(lua_State *L, const char *i, const char *v)
|
||||
{
|
||||
lua_pushstring(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
static void settabsi(lua_State *L, const char *i, int v)
|
||||
{
|
||||
lua_pushinteger(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
static void settabsb(lua_State *L, const char *i, int v)
|
||||
{
|
||||
lua_pushboolean(L, v);
|
||||
lua_setfield(L, -2, i);
|
||||
}
|
||||
|
||||
static lua_State *getthread(lua_State *L, int *arg)
|
||||
{
|
||||
if (L->base < L->top && tvisthread(L->base)) {
|
||||
*arg = 1;
|
||||
return threadV(L->base);
|
||||
} else {
|
||||
*arg = 0;
|
||||
return L;
|
||||
}
|
||||
}
|
||||
|
||||
static void treatstackoption(lua_State *L, lua_State *L1, const char *fname)
|
||||
{
|
||||
if (L == L1) {
|
||||
lua_pushvalue(L, -2);
|
||||
lua_remove(L, -3);
|
||||
}
|
||||
else
|
||||
lua_xmove(L1, L, 1);
|
||||
lua_setfield(L, -2, fname);
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getinfo)
|
||||
{
|
||||
lj_Debug ar;
|
||||
int arg, opt_f = 0, opt_L = 0;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *options = luaL_optstring(L, arg+2, "flnSu");
|
||||
if (lua_isnumber(L, arg+1)) {
|
||||
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), (lua_Debug *)&ar)) {
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
}
|
||||
} else if (L->base+arg < L->top && tvisfunc(L->base+arg)) {
|
||||
options = lua_pushfstring(L, ">%s", options);
|
||||
setfuncV(L1, L1->top++, funcV(L->base+arg));
|
||||
} else {
|
||||
lj_err_arg(L, arg+1, LJ_ERR_NOFUNCL);
|
||||
}
|
||||
if (!lj_debug_getinfo(L1, options, &ar, 1))
|
||||
lj_err_arg(L, arg+2, LJ_ERR_INVOPT);
|
||||
lua_createtable(L, 0, 16); /* Create result table. */
|
||||
for (; *options; options++) {
|
||||
switch (*options) {
|
||||
case 'S':
|
||||
settabss(L, "source", ar.source);
|
||||
settabss(L, "short_src", ar.short_src);
|
||||
settabsi(L, "linedefined", ar.linedefined);
|
||||
settabsi(L, "lastlinedefined", ar.lastlinedefined);
|
||||
settabss(L, "what", ar.what);
|
||||
break;
|
||||
case 'l':
|
||||
settabsi(L, "currentline", ar.currentline);
|
||||
break;
|
||||
case 'u':
|
||||
settabsi(L, "nups", ar.nups);
|
||||
settabsi(L, "nparams", ar.nparams);
|
||||
settabsb(L, "isvararg", ar.isvararg);
|
||||
break;
|
||||
case 'n':
|
||||
settabss(L, "name", ar.name);
|
||||
settabss(L, "namewhat", ar.namewhat);
|
||||
break;
|
||||
case 'f': opt_f = 1; break;
|
||||
case 'L': opt_L = 1; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (opt_L) treatstackoption(L, L1, "activelines");
|
||||
if (opt_f) treatstackoption(L, L1, "func");
|
||||
return 1; /* Return result table. */
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getlocal)
|
||||
{
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
const char *name;
|
||||
int slot = lj_lib_checkint(L, arg+2);
|
||||
if (tvisfunc(L->base+arg)) {
|
||||
L->top = L->base+arg+1;
|
||||
lua_pushstring(L, lua_getlocal(L, NULL, slot));
|
||||
return 1;
|
||||
}
|
||||
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar))
|
||||
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG);
|
||||
name = lua_getlocal(L1, &ar, slot);
|
||||
if (name) {
|
||||
lua_xmove(L1, L, 1);
|
||||
lua_pushstring(L, name);
|
||||
lua_pushvalue(L, -2);
|
||||
return 2;
|
||||
} else {
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setlocal)
|
||||
{
|
||||
int arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
lua_Debug ar;
|
||||
TValue *tv;
|
||||
if (!lua_getstack(L1, lj_lib_checkint(L, arg+1), &ar))
|
||||
lj_err_arg(L, arg+1, LJ_ERR_LVLRNG);
|
||||
tv = lj_lib_checkany(L, arg+3);
|
||||
copyTV(L1, L1->top++, tv);
|
||||
lua_pushstring(L, lua_setlocal(L1, &ar, lj_lib_checkint(L, arg+2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int debug_getupvalue(lua_State *L, int get)
|
||||
{
|
||||
int32_t n = lj_lib_checkint(L, 2);
|
||||
const char *name;
|
||||
lj_lib_checkfunc(L, 1);
|
||||
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
|
||||
if (name) {
|
||||
lua_pushstring(L, name);
|
||||
if (!get) return 1;
|
||||
copyTV(L, L->top, L->top-2);
|
||||
L->top++;
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_getupvalue)
|
||||
{
|
||||
return debug_getupvalue(L, 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setupvalue)
|
||||
{
|
||||
lj_lib_checkany(L, 3);
|
||||
return debug_getupvalue(L, 0);
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_upvalueid)
|
||||
{
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 1);
|
||||
int32_t n = lj_lib_checkint(L, 2) - 1;
|
||||
if ((uint32_t)n >= fn->l.nupvalues)
|
||||
lj_err_arg(L, 2, LJ_ERR_IDXRNG);
|
||||
setlightudV(L->top-1, isluafunc(fn) ? (void *)gcref(fn->l.uvptr[n]) :
|
||||
(void *)&fn->c.upvalue[n]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_upvaluejoin)
|
||||
{
|
||||
GCfunc *fn[2];
|
||||
GCRef *p[2];
|
||||
int i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
int32_t n;
|
||||
fn[i] = lj_lib_checkfunc(L, 2*i+1);
|
||||
if (!isluafunc(fn[i]))
|
||||
lj_err_arg(L, 2*i+1, LJ_ERR_NOLFUNC);
|
||||
n = lj_lib_checkint(L, 2*i+2) - 1;
|
||||
if ((uint32_t)n >= fn[i]->l.nupvalues)
|
||||
lj_err_arg(L, 2*i+2, LJ_ERR_IDXRNG);
|
||||
p[i] = &fn[i]->l.uvptr[n];
|
||||
}
|
||||
setgcrefr(*p[0], *p[1]);
|
||||
lj_gc_objbarrier(L, fn[0], gcref(*p[1]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_CF(debug_getuservalue)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (o < L->top && tvisudata(o))
|
||||
settabV(L, o, tabref(udataV(o)->env));
|
||||
else
|
||||
setnilV(o);
|
||||
L->top = o+1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(debug_setuservalue)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (!(o < L->top && tvisudata(o)))
|
||||
lj_err_argt(L, 1, LUA_TUSERDATA);
|
||||
if (!(o+1 < L->top && tvistab(o+1)))
|
||||
lj_err_argt(L, 2, LUA_TTABLE);
|
||||
L->top = o+2;
|
||||
lua_setfenv(L, 1);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
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 arg;
|
||||
lua_State *L1 = getthread(L, &arg);
|
||||
const char *msg = lua_tostring(L, arg+1);
|
||||
if (msg == NULL && L->top > L->base+arg)
|
||||
L->top = L->base+arg+1;
|
||||
else
|
||||
luaL_traceback(L, L1, msg, lj_lib_optint(L, arg+2, (L == L1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_debug(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_DBLIBNAME, debug);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+850
@@ -0,0 +1,850 @@
|
||||
/*
|
||||
** FFI library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lib_ffi_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cparse.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_carith.h"
|
||||
#include "lj_ccall.h"
|
||||
#include "lj_ccallback.h"
|
||||
#include "lj_clib.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* -- C type checks ------------------------------------------------------- */
|
||||
|
||||
/* Check first argument for a C type and returns its ID. */
|
||||
static CTypeID ffi_checkctype(lua_State *L, CTState *cts, TValue *param)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (!(o < L->top)) {
|
||||
err_argtype:
|
||||
lj_err_argtype(L, 1, "C type");
|
||||
}
|
||||
if (tvisstr(o)) { /* Parse an abstract C type declaration. */
|
||||
GCstr *s = strV(o);
|
||||
CPState cp;
|
||||
int errcode;
|
||||
cp.L = L;
|
||||
cp.cts = cts;
|
||||
cp.srcname = strdata(s);
|
||||
cp.p = strdata(s);
|
||||
cp.param = param;
|
||||
cp.mode = CPARSE_MODE_ABSTRACT|CPARSE_MODE_NOIMPLICIT;
|
||||
errcode = lj_cparse(&cp);
|
||||
if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */
|
||||
return cp.val.id;
|
||||
} else {
|
||||
GCcdata *cd;
|
||||
if (!tviscdata(o)) goto err_argtype;
|
||||
if (param && param < L->top) lj_err_arg(L, 1, LJ_ERR_FFI_NUMPARAM);
|
||||
cd = cdataV(o);
|
||||
return cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) : cd->ctypeid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check argument for C data and return it. */
|
||||
static GCcdata *ffi_checkcdata(lua_State *L, int narg)
|
||||
{
|
||||
TValue *o = L->base + narg-1;
|
||||
if (!(o < L->top && tviscdata(o)))
|
||||
lj_err_argt(L, narg, LUA_TCDATA);
|
||||
return cdataV(o);
|
||||
}
|
||||
|
||||
/* Convert argument to C pointer. */
|
||||
static void *ffi_checkptr(lua_State *L, int narg, CTypeID id)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
TValue *o = L->base + narg-1;
|
||||
void *p;
|
||||
if (o >= L->top)
|
||||
lj_err_arg(L, narg, LJ_ERR_NOVAL);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, id), (uint8_t *)&p, o, CCF_ARG(narg));
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Convert argument to int32_t. */
|
||||
static int32_t ffi_checkint(lua_State *L, int narg)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
TValue *o = L->base + narg-1;
|
||||
int32_t i;
|
||||
if (o >= L->top)
|
||||
lj_err_arg(L, narg, LJ_ERR_NOVAL);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_INT32), (uint8_t *)&i, o,
|
||||
CCF_ARG(narg));
|
||||
return i;
|
||||
}
|
||||
|
||||
/* -- C type metamethods -------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi_meta
|
||||
|
||||
/* Handle ctype __index/__newindex metamethods. */
|
||||
static int ffi_index_meta(lua_State *L, CTState *cts, CType *ct, MMS mm)
|
||||
{
|
||||
CTypeID id = ctype_typeid(cts, ct);
|
||||
cTValue *tv = lj_ctype_meta(cts, id, mm);
|
||||
TValue *base = L->base;
|
||||
if (!tv) {
|
||||
const char *s;
|
||||
err_index:
|
||||
s = strdata(lj_ctype_repr(L, id, NULL));
|
||||
if (tvisstr(L->base+1)) {
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADMEMBER, s, strVdata(L->base+1));
|
||||
} else {
|
||||
const char *key = tviscdata(L->base+1) ?
|
||||
strdata(lj_ctype_repr(L, cdataV(L->base+1)->ctypeid, NULL)) :
|
||||
lj_typename(L->base+1);
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADIDXW, s, key);
|
||||
}
|
||||
}
|
||||
if (!tvisfunc(tv)) {
|
||||
if (mm == MM_index) {
|
||||
cTValue *o = lj_meta_tget(L, tv, base+1);
|
||||
if (o) {
|
||||
if (tvisnil(o)) goto err_index;
|
||||
copyTV(L, L->top-1, o);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
TValue *o = lj_meta_tset(L, tv, base+1);
|
||||
if (o) {
|
||||
copyTV(L, o, base+2);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
tv = L->top-1;
|
||||
}
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTInfo qual = 0;
|
||||
CType *ct;
|
||||
uint8_t *p;
|
||||
TValue *o = L->base;
|
||||
if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
|
||||
if ((qual & 1))
|
||||
return ffi_index_meta(L, cts, ct, MM_index);
|
||||
if (lj_cdata_get(cts, ct, L->top-1, p))
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___newindex) LJLIB_REC(cdata_index 1)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTInfo qual = 0;
|
||||
CType *ct;
|
||||
uint8_t *p;
|
||||
TValue *o = L->base;
|
||||
if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
|
||||
if ((qual & 1)) {
|
||||
if ((qual & CTF_CONST))
|
||||
lj_err_caller(L, LJ_ERR_FFI_WRCONST);
|
||||
return ffi_index_meta(L, cts, ct, MM_newindex);
|
||||
}
|
||||
lj_cdata_set(cts, ct, p, o+2, qual);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Common handler for cdata arithmetic. */
|
||||
static int ffi_arith(lua_State *L)
|
||||
{
|
||||
MMS mm = (MMS)(curr_func(L)->c.ffid - (int)FF_ffi_meta___eq + (int)MM_eq);
|
||||
return lj_carith_op(L, mm);
|
||||
}
|
||||
|
||||
/* The following functions must be in contiguous ORDER MM. */
|
||||
LJLIB_CF(ffi_meta___eq) LJLIB_REC(cdata_arith MM_eq)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___len) LJLIB_REC(cdata_arith MM_len)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___lt) LJLIB_REC(cdata_arith MM_lt)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___le) LJLIB_REC(cdata_arith MM_le)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___concat) LJLIB_REC(cdata_arith MM_concat)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static int lj_cf_ffi_new(lua_State *L);
|
||||
|
||||
LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
CTypeID id = cd->ctypeid;
|
||||
CType *ct;
|
||||
cTValue *tv;
|
||||
MMS mm = MM_call;
|
||||
if (cd->ctypeid == CTID_CTYPEID) {
|
||||
id = *(CTypeID *)cdataptr(cd);
|
||||
mm = MM_new;
|
||||
} else {
|
||||
int ret = lj_ccall_func(L, cd);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
}
|
||||
/* Handle ctype __call/__new metamethod. */
|
||||
ct = ctype_raw(cts, id);
|
||||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
|
||||
tv = lj_ctype_meta(cts, id, mm);
|
||||
if (tv)
|
||||
return lj_meta_tailcall(L, tv);
|
||||
else if (mm == MM_call)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADCALL, strdata(lj_ctype_repr(L, id, NULL)));
|
||||
return lj_cf_ffi_new(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___add) LJLIB_REC(cdata_arith MM_add)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___sub) LJLIB_REC(cdata_arith MM_sub)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___mul) LJLIB_REC(cdata_arith MM_mul)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___div) LJLIB_REC(cdata_arith MM_div)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___mod) LJLIB_REC(cdata_arith MM_mod)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___pow) LJLIB_REC(cdata_arith MM_pow)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___unm) LJLIB_REC(cdata_arith MM_unm)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
/* End of contiguous ORDER MM. */
|
||||
|
||||
LJLIB_CF(ffi_meta___tostring)
|
||||
{
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
const char *msg = "cdata<%s>: %p";
|
||||
CTypeID id = cd->ctypeid;
|
||||
void *p = cdataptr(cd);
|
||||
if (id == CTID_CTYPEID) {
|
||||
msg = "ctype<%s>";
|
||||
id = *(CTypeID *)p;
|
||||
} else {
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
if (ctype_isref(ct->info)) {
|
||||
p = *(void **)p;
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
}
|
||||
if (ctype_iscomplex(ct->info)) {
|
||||
setstrV(L, L->top-1, lj_ctype_repr_complex(L, cdataptr(cd), ct->size));
|
||||
goto checkgc;
|
||||
} else if (ct->size == 8 && ctype_isinteger(ct->info)) {
|
||||
setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd),
|
||||
(ct->info & CTF_UNSIGNED)));
|
||||
goto checkgc;
|
||||
} else if (ctype_isfunc(ct->info)) {
|
||||
p = *(void **)p;
|
||||
} else if (ctype_isenum(ct->info)) {
|
||||
msg = "cdata<%s>: %d";
|
||||
p = (void *)(uintptr_t)*(uint32_t **)p;
|
||||
} else {
|
||||
if (ctype_isptr(ct->info)) {
|
||||
p = cdata_getptr(p, ct->size);
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
}
|
||||
if (ctype_isstruct(ct->info) || ctype_isvector(ct->info)) {
|
||||
/* Handle ctype __tostring metamethod. */
|
||||
cTValue *tv = lj_ctype_meta(cts, ctype_typeid(cts, ct), MM_tostring);
|
||||
if (tv)
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
}
|
||||
}
|
||||
lj_str_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), p);
|
||||
checkgc:
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ffi_pairs(lua_State *L, MMS mm)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkcdata(L, 1)->ctypeid;
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
cTValue *tv;
|
||||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
|
||||
tv = lj_ctype_meta(cts, id, mm);
|
||||
if (!tv)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADMM, strdata(lj_ctype_repr(L, id, NULL)),
|
||||
strdata(mmname_str(G(L), mm)));
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___pairs)
|
||||
{
|
||||
return ffi_pairs(L, MM_pairs);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___ipairs)
|
||||
{
|
||||
return ffi_pairs(L, MM_ipairs);
|
||||
}
|
||||
|
||||
LJLIB_PUSH("ffi") LJLIB_SET(__metatable)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- C library metamethods ----------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi_clib
|
||||
|
||||
/* Index C library by a name. */
|
||||
static TValue *ffi_clib_index(lua_State *L)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
CLibrary *cl;
|
||||
if (!(o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB))
|
||||
lj_err_argt(L, 1, LUA_TUSERDATA);
|
||||
cl = (CLibrary *)uddata(udataV(o));
|
||||
if (!(o+1 < L->top && tvisstr(o+1)))
|
||||
lj_err_argt(L, 2, LUA_TSTRING);
|
||||
return lj_clib_index(L, cl, strV(o+1));
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_clib___index) LJLIB_REC(clib_index 1)
|
||||
{
|
||||
TValue *tv = ffi_clib_index(L);
|
||||
if (tviscdata(tv)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCcdata *cd = cdataV(tv);
|
||||
CType *s = ctype_get(cts, cd->ctypeid);
|
||||
if (ctype_isextern(s->info)) {
|
||||
CTypeID sid = ctype_cid(s->info);
|
||||
void *sp = *(void **)cdataptr(cd);
|
||||
CType *ct = ctype_raw(cts, sid);
|
||||
if (lj_cconv_tv_ct(cts, ct, sid, L->top-1, sp))
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
copyTV(L, L->top-1, tv);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_clib___newindex) LJLIB_REC(clib_index 0)
|
||||
{
|
||||
TValue *tv = ffi_clib_index(L);
|
||||
TValue *o = L->base+2;
|
||||
if (o < L->top && tviscdata(tv)) {
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCcdata *cd = cdataV(tv);
|
||||
CType *d = ctype_get(cts, cd->ctypeid);
|
||||
if (ctype_isextern(d->info)) {
|
||||
CTInfo qual = 0;
|
||||
for (;;) { /* Skip attributes and collect qualifiers. */
|
||||
d = ctype_child(cts, d);
|
||||
if (!ctype_isattrib(d->info)) break;
|
||||
if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size;
|
||||
}
|
||||
if (!((d->info|qual) & CTF_CONST)) {
|
||||
lj_cconv_ct_tv(cts, d, *(void **)cdataptr(cd), o, 0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
lj_err_caller(L, LJ_ERR_FFI_WRCONST);
|
||||
return 0; /* unreachable */
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_clib___gc)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (o < L->top && tvisudata(o) && udataV(o)->udtype == UDTYPE_FFI_CLIB)
|
||||
lj_clib_unload((CLibrary *)uddata(udataV(o)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- Callback function metamethods --------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi_callback
|
||||
|
||||
static int ffi_callback_set(lua_State *L, GCfunc *fn)
|
||||
{
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, cd->ctypeid);
|
||||
if (ctype_isptr(ct->info) && (LJ_32 || ct->size == 8)) {
|
||||
MSize slot = lj_ccallback_ptr2slot(cts, *(void **)cdataptr(cd));
|
||||
if (slot < cts->cb.sizeid && cts->cb.cbid[slot] != 0) {
|
||||
GCtab *t = cts->miscmap;
|
||||
TValue *tv = lj_tab_setint(L, t, (int32_t)slot);
|
||||
if (fn) {
|
||||
setfuncV(L, tv, fn);
|
||||
lj_gc_anybarriert(L, t);
|
||||
} else {
|
||||
setnilV(tv);
|
||||
cts->cb.cbid[slot] = 0;
|
||||
cts->cb.topid = slot < cts->cb.topid ? slot : cts->cb.topid;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
lj_err_caller(L, LJ_ERR_FFI_BADCBACK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_callback_free)
|
||||
{
|
||||
return ffi_callback_set(L, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_callback_set)
|
||||
{
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 2);
|
||||
return ffi_callback_set(L, fn);
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-1) LJLIB_SET(__index)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- FFI library functions ----------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_ffi
|
||||
|
||||
LJLIB_CF(ffi_cdef)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
CPState cp;
|
||||
int errcode;
|
||||
cp.L = L;
|
||||
cp.cts = ctype_cts(L);
|
||||
cp.srcname = strdata(s);
|
||||
cp.p = strdata(s);
|
||||
cp.param = L->base+1;
|
||||
cp.mode = CPARSE_MODE_MULTI|CPARSE_MODE_DIRECT;
|
||||
errcode = lj_cparse(&cp);
|
||||
if (errcode) lj_err_throw(L, errcode); /* Propagate errors. */
|
||||
lj_gc_check(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_new) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
CTSize sz;
|
||||
CTInfo info = lj_ctype_info(cts, id, &sz);
|
||||
TValue *o = L->base+1;
|
||||
GCcdata *cd;
|
||||
if ((info & CTF_VLA)) {
|
||||
o++;
|
||||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2));
|
||||
}
|
||||
if (sz == CTSIZE_INVALID)
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE);
|
||||
if (!(info & CTF_VLA) && ctype_align(info) <= CT_MEMALIGN)
|
||||
cd = lj_cdata_new(cts, id, sz);
|
||||
else
|
||||
cd = lj_cdata_newv(cts, id, sz, ctype_align(info));
|
||||
setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */
|
||||
lj_cconv_ct_init(cts, ct, sz, cdataptr(cd),
|
||||
o, (MSize)(L->top - o)); /* Initialize cdata. */
|
||||
if (ctype_isstruct(ct->info)) {
|
||||
/* Handle ctype __gc metamethod. Use the fast lookup here. */
|
||||
cTValue *tv = lj_tab_getinth(cts->miscmap, -(int32_t)id);
|
||||
if (tv && tvistab(tv) && (tv = lj_meta_fast(L, tabV(tv), MM_gc))) {
|
||||
GCtab *t = cts->finalizer;
|
||||
if (gcref(t->metatable)) {
|
||||
/* Add to finalizer table, if still enabled. */
|
||||
copyTV(L, lj_tab_set(L, t, o-1), tv);
|
||||
lj_gc_anybarriert(L, t);
|
||||
cd->marked |= LJ_GC_CDATA_FIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
L->top = o; /* Only return the cdata itself. */
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_cast) LJLIB_REC(ffi_new)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CType *d = ctype_raw(cts, id);
|
||||
TValue *o = lj_lib_checkany(L, 2);
|
||||
L->top = o+1; /* Make sure this is the last item on the stack. */
|
||||
if (!(ctype_isnum(d->info) || ctype_isptr(d->info) || ctype_isenum(d->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
if (!(tviscdata(o) && cdataV(o)->ctypeid == id)) {
|
||||
GCcdata *cd = lj_cdata_new(cts, id, d->size);
|
||||
lj_cconv_ct_tv(cts, d, cdataptr(cd), o, CCF_CAST);
|
||||
setcdataV(L, o, cd);
|
||||
lj_gc_check(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_typeof) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, L->base+1);
|
||||
GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
|
||||
*(CTypeID *)cdataptr(cd) = id;
|
||||
setcdataV(L, L->top-1, cd);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_istype) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id1 = ffi_checkctype(L, cts, NULL);
|
||||
TValue *o = lj_lib_checkany(L, 2);
|
||||
int b = 0;
|
||||
if (tviscdata(o)) {
|
||||
GCcdata *cd = cdataV(o);
|
||||
CTypeID id2 = cd->ctypeid == CTID_CTYPEID ? *(CTypeID *)cdataptr(cd) :
|
||||
cd->ctypeid;
|
||||
CType *ct1 = lj_ctype_rawref(cts, id1);
|
||||
CType *ct2 = lj_ctype_rawref(cts, id2);
|
||||
if (ct1 == ct2) {
|
||||
b = 1;
|
||||
} else if (ctype_type(ct1->info) == ctype_type(ct2->info) &&
|
||||
ct1->size == ct2->size) {
|
||||
if (ctype_ispointer(ct1->info))
|
||||
b = lj_cconv_compatptr(cts, ct1, ct2, CCF_IGNQUAL);
|
||||
else if (ctype_isnum(ct1->info) || ctype_isvoid(ct1->info))
|
||||
b = (((ct1->info ^ ct2->info) & ~(CTF_QUAL|CTF_LONG)) == 0);
|
||||
} else if (ctype_isstruct(ct1->info) && ctype_isptr(ct2->info) &&
|
||||
ct1 == ctype_rawchild(cts, ct2)) {
|
||||
b = 1;
|
||||
}
|
||||
}
|
||||
setboolV(L->top-1, b);
|
||||
setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_sizeof) LJLIB_REC(ffi_xof FF_ffi_sizeof)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CTSize sz;
|
||||
if (LJ_UNLIKELY(tviscdata(L->base) && cdataisv(cdataV(L->base)))) {
|
||||
sz = cdatavlen(cdataV(L->base));
|
||||
} else {
|
||||
CType *ct = lj_ctype_rawref(cts, id);
|
||||
if (ctype_isvltype(ct->info))
|
||||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2));
|
||||
else
|
||||
sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_INVALID;
|
||||
if (LJ_UNLIKELY(sz == CTSIZE_INVALID)) {
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
setintV(L->top-1, (int32_t)sz);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_alignof) LJLIB_REC(ffi_xof FF_ffi_alignof)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
CTSize sz = 0;
|
||||
CTInfo info = lj_ctype_info(cts, id, &sz);
|
||||
setintV(L->top-1, 1 << ctype_align(info));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_offsetof) LJLIB_REC(ffi_xof FF_ffi_offsetof)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
GCstr *name = lj_lib_checkstr(L, 2);
|
||||
CType *ct = lj_ctype_rawref(cts, id);
|
||||
CTSize ofs;
|
||||
if (ctype_isstruct(ct->info) && ct->size != CTSIZE_INVALID) {
|
||||
CType *fct = lj_ctype_getfield(cts, ct, name, &ofs);
|
||||
if (fct) {
|
||||
setintV(L->top-1, ofs);
|
||||
if (ctype_isfield(fct->info)) {
|
||||
return 1;
|
||||
} else if (ctype_isbitfield(fct->info)) {
|
||||
setintV(L->top++, ctype_bitpos(fct->info));
|
||||
setintV(L->top++, ctype_bitbsz(fct->info));
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_errno) LJLIB_REC(.)
|
||||
{
|
||||
int err = errno;
|
||||
if (L->top > L->base)
|
||||
errno = ffi_checkint(L, 1);
|
||||
setintV(L->top++, err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_string) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
TValue *o = lj_lib_checkany(L, 1);
|
||||
const char *p;
|
||||
size_t len;
|
||||
if (o+1 < L->top) {
|
||||
len = (size_t)ffi_checkint(L, 2);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CVOID), (uint8_t *)&p, o,
|
||||
CCF_ARG(1));
|
||||
} else {
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_P_CCHAR), (uint8_t *)&p, o,
|
||||
CCF_ARG(1));
|
||||
len = strlen(p);
|
||||
}
|
||||
L->top = o+1; /* Make sure this is the last item on the stack. */
|
||||
setstrV(L, o, lj_str_new(L, p, len));
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_copy) LJLIB_REC(.)
|
||||
{
|
||||
void *dp = ffi_checkptr(L, 1, CTID_P_VOID);
|
||||
void *sp = ffi_checkptr(L, 2, CTID_P_CVOID);
|
||||
TValue *o = L->base+1;
|
||||
CTSize len;
|
||||
if (tvisstr(o) && o+1 >= L->top)
|
||||
len = strV(o)->len+1; /* Copy Lua string including trailing '\0'. */
|
||||
else
|
||||
len = (CTSize)ffi_checkint(L, 3);
|
||||
memcpy(dp, sp, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_fill) LJLIB_REC(.)
|
||||
{
|
||||
void *dp = ffi_checkptr(L, 1, CTID_P_VOID);
|
||||
CTSize len = (CTSize)ffi_checkint(L, 2);
|
||||
int32_t fill = 0;
|
||||
if (L->base+2 < L->top && !tvisnil(L->base+2)) fill = ffi_checkint(L, 3);
|
||||
memset(dp, fill, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define H_(le, be) LJ_ENDIAN_SELECT(0x##le, 0x##be)
|
||||
|
||||
/* Test ABI string. */
|
||||
LJLIB_CF(ffi_abi) LJLIB_REC(.)
|
||||
{
|
||||
GCstr *s = lj_lib_checkstr(L, 1);
|
||||
int b = 0;
|
||||
switch (s->hash) {
|
||||
#if LJ_64
|
||||
case H_(849858eb,ad35fd06): b = 1; break; /* 64bit */
|
||||
#else
|
||||
case H_(662d3c79,d0e22477): b = 1; break; /* 32bit */
|
||||
#endif
|
||||
#if LJ_ARCH_HASFPU
|
||||
case H_(e33ee463,e33ee463): b = 1; break; /* fpu */
|
||||
#endif
|
||||
#if LJ_ABI_SOFTFP
|
||||
case H_(61211a23,c2e8c81c): b = 1; break; /* softfp */
|
||||
#else
|
||||
case H_(539417a8,8ce0812f): b = 1; break; /* hardfp */
|
||||
#endif
|
||||
#if LJ_ABI_EABI
|
||||
case H_(2182df8f,f2ed1152): b = 1; break; /* eabi */
|
||||
#endif
|
||||
#if LJ_ABI_WIN
|
||||
case H_(4ab624a8,4ab624a8): b = 1; break; /* win */
|
||||
#endif
|
||||
case H_(3af93066,1f001464): b = 1; break; /* le/be */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setboolV(L->top-1, b);
|
||||
setboolV(&G(L)->tmptv2, b); /* Remember for trace recorder. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
#undef H_
|
||||
|
||||
LJLIB_PUSH(top-8) LJLIB_SET(!) /* Store reference to miscmap table. */
|
||||
|
||||
LJLIB_CF(ffi_metatype)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts, NULL);
|
||||
GCtab *mt = lj_lib_checktab(L, 2);
|
||||
GCtab *t = cts->miscmap;
|
||||
CType *ct = ctype_get(cts, id); /* Only allow raw types. */
|
||||
TValue *tv;
|
||||
GCcdata *cd;
|
||||
if (!(ctype_isstruct(ct->info) || ctype_iscomplex(ct->info) ||
|
||||
ctype_isvector(ct->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
tv = lj_tab_setinth(L, t, -(int32_t)id);
|
||||
if (!tvisnil(tv))
|
||||
lj_err_caller(L, LJ_ERR_PROTMT);
|
||||
settabV(L, tv, mt);
|
||||
lj_gc_anybarriert(L, t);
|
||||
cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
|
||||
*(CTypeID *)cdataptr(cd) = id;
|
||||
setcdataV(L, L->top-1, cd);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to finalizer table. */
|
||||
|
||||
LJLIB_CF(ffi_gc) LJLIB_REC(.)
|
||||
{
|
||||
GCcdata *cd = ffi_checkcdata(L, 1);
|
||||
TValue *fin = lj_lib_checkany(L, 2);
|
||||
CTState *cts = ctype_cts(L);
|
||||
GCtab *t = cts->finalizer;
|
||||
CType *ct = ctype_raw(cts, cd->ctypeid);
|
||||
if (!(ctype_isptr(ct->info) || ctype_isstruct(ct->info) ||
|
||||
ctype_isrefarray(ct->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
if (gcref(t->metatable)) { /* Update finalizer table, if still enabled. */
|
||||
copyTV(L, lj_tab_set(L, t, L->base), fin);
|
||||
lj_gc_anybarriert(L, t);
|
||||
if (!tvisnil(fin))
|
||||
cd->marked |= LJ_GC_CDATA_FIN;
|
||||
else
|
||||
cd->marked &= ~LJ_GC_CDATA_FIN;
|
||||
}
|
||||
L->top = L->base+1; /* Pass through the cdata object. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-5) LJLIB_SET(!) /* Store clib metatable in func environment. */
|
||||
|
||||
LJLIB_CF(ffi_load)
|
||||
{
|
||||
GCstr *name = lj_lib_checkstr(L, 1);
|
||||
int global = (L->base+1 < L->top && tvistruecond(L->base+1));
|
||||
lj_clib_load(L, tabref(curr_func(L)->c.env), name, global);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-4) LJLIB_SET(C)
|
||||
LJLIB_PUSH(top-3) LJLIB_SET(os)
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(arch)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Create special weak-keyed finalizer table. */
|
||||
static GCtab *ffi_finalizer(lua_State *L)
|
||||
{
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
GCtab *t = lj_tab_new(L, 0, 1);
|
||||
settabV(L, L->top++, t);
|
||||
setgcref(t->metatable, obj2gco(t));
|
||||
setstrV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "__mode")),
|
||||
lj_str_newlit(L, "K"));
|
||||
t->nomm = (uint8_t)(~(1u<<MM_mode));
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Register FFI module as loaded. */
|
||||
static void ffi_register_module(lua_State *L)
|
||||
{
|
||||
cTValue *tmp = lj_tab_getstr(tabV(registry(L)), lj_str_newlit(L, "_LOADED"));
|
||||
if (tmp && tvistab(tmp)) {
|
||||
GCtab *t = tabV(tmp);
|
||||
copyTV(L, lj_tab_setstr(L, t, lj_str_newlit(L, LUA_FFILIBNAME)), L->top-1);
|
||||
lj_gc_anybarriert(L, t);
|
||||
}
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_ffi(lua_State *L)
|
||||
{
|
||||
CTState *cts = lj_ctype_init(L);
|
||||
settabV(L, L->top++, (cts->miscmap = lj_tab_new(L, 0, 1)));
|
||||
cts->finalizer = ffi_finalizer(L);
|
||||
LJ_LIB_REG(L, NULL, ffi_meta);
|
||||
/* NOBARRIER: basemt is a GC root. */
|
||||
setgcref(basemt_it(G(L), LJ_TCDATA), obj2gco(tabV(L->top-1)));
|
||||
LJ_LIB_REG(L, NULL, ffi_clib);
|
||||
LJ_LIB_REG(L, NULL, ffi_callback);
|
||||
/* NOBARRIER: the key is new and lj_tab_newkey() handles the barrier. */
|
||||
settabV(L, lj_tab_setstr(L, cts->miscmap, &cts->g->strempty), tabV(L->top-1));
|
||||
L->top--;
|
||||
lj_clib_default(L, tabV(L->top-1)); /* Create ffi.C default namespace. */
|
||||
lua_pushliteral(L, LJ_OS_NAME);
|
||||
lua_pushliteral(L, LJ_ARCH_NAME);
|
||||
LJ_LIB_REG(L, NULL, ffi); /* Note: no global "ffi" created! */
|
||||
ffi_register_module(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
** Library initialization.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major parts taken verbatim from the Lua interpreter.
|
||||
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_init_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_arch.h"
|
||||
|
||||
static const luaL_Reg lj_lib_load[] = {
|
||||
{ "", luaopen_base },
|
||||
{ LUA_LOADLIBNAME, luaopen_package },
|
||||
{ LUA_TABLIBNAME, luaopen_table },
|
||||
{ LUA_IOLIBNAME, luaopen_io },
|
||||
{ LUA_OSLIBNAME, luaopen_os },
|
||||
{ LUA_STRLIBNAME, luaopen_string },
|
||||
{ LUA_MATHLIBNAME, luaopen_math },
|
||||
{ LUA_DBLIBNAME, luaopen_debug },
|
||||
{ LUA_BITLIBNAME, luaopen_bit },
|
||||
{ LUA_JITLIBNAME, luaopen_jit },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg lj_lib_preload[] = {
|
||||
#if LJ_HASFFI
|
||||
{ LUA_FFILIBNAME, luaopen_ffi },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
LUALIB_API void luaL_openlibs(lua_State *L)
|
||||
{
|
||||
const luaL_Reg *lib;
|
||||
for (lib = lj_lib_load; lib->func; lib++) {
|
||||
lua_pushcfunction(L, lib->func);
|
||||
lua_pushstring(L, lib->name);
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD",
|
||||
sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1);
|
||||
for (lib = lj_lib_preload; lib->func; lib++) {
|
||||
lua_pushcfunction(L, lib->func);
|
||||
lua_setfield(L, -2, lib->name);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
Vendored
+539
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
** I/O library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2011 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define lib_io_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_ff.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* Userdata payload for I/O file. */
|
||||
typedef struct IOFileUD {
|
||||
FILE *fp; /* File handle. */
|
||||
uint32_t type; /* File type. */
|
||||
} IOFileUD;
|
||||
|
||||
#define IOFILE_TYPE_FILE 0 /* Regular file. */
|
||||
#define IOFILE_TYPE_PIPE 1 /* Pipe. */
|
||||
#define IOFILE_TYPE_STDF 2 /* Standard file handle. */
|
||||
#define IOFILE_TYPE_MASK 3
|
||||
|
||||
#define IOFILE_FLAG_CLOSE 4 /* Close after io.lines() iterator. */
|
||||
|
||||
#define IOSTDF_UD(L, id) (&gcref(G(L)->gcroot[(id)])->ud)
|
||||
#define IOSTDF_IOF(L, id) ((IOFileUD *)uddata(IOSTDF_UD(L, (id))))
|
||||
|
||||
/* -- Open/close helpers -------------------------------------------------- */
|
||||
|
||||
static IOFileUD *io_tofilep(lua_State *L)
|
||||
{
|
||||
if (!(L->base < L->top && tvisudata(L->base) &&
|
||||
udataV(L->base)->udtype == UDTYPE_IO_FILE))
|
||||
lj_err_argtype(L, 1, "FILE*");
|
||||
return (IOFileUD *)uddata(udataV(L->base));
|
||||
}
|
||||
|
||||
static IOFileUD *io_tofile(lua_State *L)
|
||||
{
|
||||
IOFileUD *iof = io_tofilep(L);
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOCLFL);
|
||||
return iof;
|
||||
}
|
||||
|
||||
static FILE *io_stdfile(lua_State *L, ptrdiff_t id)
|
||||
{
|
||||
IOFileUD *iof = IOSTDF_IOF(L, id);
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOSTDCL);
|
||||
return iof->fp;
|
||||
}
|
||||
|
||||
static IOFileUD *io_file_new(lua_State *L)
|
||||
{
|
||||
IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
|
||||
GCudata *ud = udataV(L->top-1);
|
||||
ud->udtype = UDTYPE_IO_FILE;
|
||||
/* NOBARRIER: The GCudata is new (marked white). */
|
||||
setgcrefr(ud->metatable, curr_func(L)->c.env);
|
||||
iof->fp = NULL;
|
||||
iof->type = IOFILE_TYPE_FILE;
|
||||
return iof;
|
||||
}
|
||||
|
||||
static IOFileUD *io_file_open(lua_State *L, const char *mode)
|
||||
{
|
||||
const char *fname = strdata(lj_lib_checkstr(L, 1));
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
iof->fp = fopen(fname, mode);
|
||||
if (iof->fp == NULL)
|
||||
luaL_argerror(L, 1, lj_str_pushf(L, "%s: %s", fname, strerror(errno)));
|
||||
return iof;
|
||||
}
|
||||
|
||||
static int io_file_close(lua_State *L, IOFileUD *iof)
|
||||
{
|
||||
int ok;
|
||||
if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_FILE) {
|
||||
ok = (fclose(iof->fp) == 0);
|
||||
} else if ((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_PIPE) {
|
||||
int stat = -1;
|
||||
#if LJ_TARGET_POSIX
|
||||
stat = pclose(iof->fp);
|
||||
#elif LJ_TARGET_WINDOWS
|
||||
stat = _pclose(iof->fp);
|
||||
#else
|
||||
lua_assert(0);
|
||||
return 0;
|
||||
#endif
|
||||
#if LJ_52
|
||||
iof->fp = NULL;
|
||||
return luaL_execresult(L, stat);
|
||||
#else
|
||||
ok = (stat != -1);
|
||||
#endif
|
||||
} else {
|
||||
lua_assert((iof->type & IOFILE_TYPE_MASK) == IOFILE_TYPE_STDF);
|
||||
setnilV(L->top++);
|
||||
lua_pushliteral(L, "cannot close standard file");
|
||||
return 2;
|
||||
}
|
||||
iof->fp = NULL;
|
||||
return luaL_fileresult(L, ok, NULL);
|
||||
}
|
||||
|
||||
/* -- Read/write helpers -------------------------------------------------- */
|
||||
|
||||
static int io_file_readnum(lua_State *L, FILE *fp)
|
||||
{
|
||||
lua_Number d;
|
||||
if (fscanf(fp, LUA_NUMBER_SCAN, &d) == 1) {
|
||||
if (LJ_DUALNUM) {
|
||||
int32_t i = lj_num2int(d);
|
||||
if (d == (lua_Number)i && !tvismzero((cTValue *)&d)) {
|
||||
setintV(L->top++, i);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
setnumV(L->top++, d);
|
||||
return 1;
|
||||
} else {
|
||||
setnilV(L->top++);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int io_file_readline(lua_State *L, FILE *fp, MSize chop)
|
||||
{
|
||||
MSize m = LUAL_BUFFERSIZE, n = 0, ok = 0;
|
||||
char *buf;
|
||||
for (;;) {
|
||||
buf = lj_str_needbuf(L, &G(L)->tmpbuf, m);
|
||||
if (fgets(buf+n, m-n, fp) == NULL) break;
|
||||
n += (MSize)strlen(buf+n);
|
||||
ok |= n;
|
||||
if (n && buf[n-1] == '\n') { n -= chop; break; }
|
||||
if (n >= m - 64) m += m;
|
||||
}
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
|
||||
lj_gc_check(L);
|
||||
return (int)ok;
|
||||
}
|
||||
|
||||
static void io_file_readall(lua_State *L, FILE *fp)
|
||||
{
|
||||
MSize m, n;
|
||||
for (m = LUAL_BUFFERSIZE, n = 0; ; m += m) {
|
||||
char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, m);
|
||||
n += (MSize)fread(buf+n, 1, m-n, fp);
|
||||
if (n != m) {
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
|
||||
lj_gc_check(L);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int io_file_readlen(lua_State *L, FILE *fp, MSize m)
|
||||
{
|
||||
if (m) {
|
||||
char *buf = lj_str_needbuf(L, &G(L)->tmpbuf, m);
|
||||
MSize n = (MSize)fread(buf, 1, m, fp);
|
||||
setstrV(L, L->top++, lj_str_new(L, buf, (size_t)n));
|
||||
lj_gc_check(L);
|
||||
return (n > 0 || m == 0);
|
||||
} else {
|
||||
int c = getc(fp);
|
||||
ungetc(c, fp);
|
||||
setstrV(L, L->top++, &G(L)->strempty);
|
||||
return (c != EOF);
|
||||
}
|
||||
}
|
||||
|
||||
static int io_file_read(lua_State *L, FILE *fp, int start)
|
||||
{
|
||||
int ok, n, nargs = (int)(L->top - L->base) - start;
|
||||
clearerr(fp);
|
||||
if (nargs == 0) {
|
||||
ok = io_file_readline(L, fp, 1);
|
||||
n = start+1; /* Return 1 result. */
|
||||
} else {
|
||||
/* The results plus the buffers go on top of the args. */
|
||||
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
|
||||
ok = 1;
|
||||
for (n = start; nargs-- && ok; n++) {
|
||||
if (tvisstr(L->base+n)) {
|
||||
const char *p = strVdata(L->base+n);
|
||||
if (p[0] != '*')
|
||||
lj_err_arg(L, n+1, LJ_ERR_INVOPT);
|
||||
if (p[1] == 'n')
|
||||
ok = io_file_readnum(L, fp);
|
||||
else if ((p[1] & ~0x20) == 'L')
|
||||
ok = io_file_readline(L, fp, (p[1] == 'l'));
|
||||
else if (p[1] == 'a')
|
||||
io_file_readall(L, fp);
|
||||
else
|
||||
lj_err_arg(L, n+1, LJ_ERR_INVFMT);
|
||||
} else if (tvisnumber(L->base+n)) {
|
||||
ok = io_file_readlen(L, fp, (MSize)lj_lib_checkint(L, n+1));
|
||||
} else {
|
||||
lj_err_arg(L, n+1, LJ_ERR_INVOPT);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ferror(fp))
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
if (!ok)
|
||||
setnilV(L->top-1); /* Replace last result with nil. */
|
||||
return n - start;
|
||||
}
|
||||
|
||||
static int io_file_write(lua_State *L, 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 (tvisint(tv)) {
|
||||
char buf[LJ_STR_INTBUF];
|
||||
char *p = lj_str_bufint(buf, intV(tv));
|
||||
size_t len = (size_t)(buf+LJ_STR_INTBUF-p);
|
||||
status = status && (fwrite(p, 1, len, fp) == len);
|
||||
} else if (tvisnum(tv)) {
|
||||
status = status && (fprintf(fp, LUA_NUMBER_FMT, numV(tv)) > 0);
|
||||
} else {
|
||||
lj_err_argt(L, (int)(tv - L->base) + 1, LUA_TSTRING);
|
||||
}
|
||||
}
|
||||
if (LJ_52 && status) {
|
||||
L->top = L->base+1;
|
||||
if (start == 0)
|
||||
setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_OUTPUT));
|
||||
return 1;
|
||||
}
|
||||
return luaL_fileresult(L, status, NULL);
|
||||
}
|
||||
|
||||
static int io_file_iter(lua_State *L)
|
||||
{
|
||||
GCfunc *fn = curr_func(L);
|
||||
IOFileUD *iof = uddata(udataV(&fn->c.upvalue[0]));
|
||||
int n = fn->c.nupvalues - 1;
|
||||
if (iof->fp == NULL)
|
||||
lj_err_caller(L, LJ_ERR_IOCLFL);
|
||||
L->top = L->base;
|
||||
if (n) { /* Copy upvalues with options to stack. */
|
||||
if (n > LUAI_MAXCSTACK)
|
||||
lj_err_caller(L, LJ_ERR_STKOV);
|
||||
lj_state_checkstack(L, (MSize)n);
|
||||
memcpy(L->top, &fn->c.upvalue[1], n*sizeof(TValue));
|
||||
L->top += n;
|
||||
}
|
||||
n = io_file_read(L, iof->fp, 0);
|
||||
if (ferror(iof->fp))
|
||||
lj_err_callermsg(L, strVdata(L->top-2));
|
||||
if (tvisnil(L->base) && (iof->type & IOFILE_FLAG_CLOSE)) {
|
||||
io_file_close(L, iof); /* Return values are ignored. */
|
||||
return 0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* -- I/O file methods ---------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_io_method
|
||||
|
||||
LJLIB_CF(io_method_close)
|
||||
{
|
||||
IOFileUD *iof = L->base < L->top ? io_tofile(L) :
|
||||
IOSTDF_IOF(L, GCROOT_IO_OUTPUT);
|
||||
return io_file_close(L, iof);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_read)
|
||||
{
|
||||
return io_file_read(L, io_tofile(L)->fp, 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_write) LJLIB_REC(io_write 0)
|
||||
{
|
||||
return io_file_write(L, io_tofile(L)->fp, 1);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_flush) LJLIB_REC(io_flush 0)
|
||||
{
|
||||
return luaL_fileresult(L, fflush(io_tofile(L)->fp) == 0, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_seek)
|
||||
{
|
||||
FILE *fp = io_tofile(L)->fp;
|
||||
int opt = lj_lib_checkopt(L, 2, 1, "\3set\3cur\3end");
|
||||
int64_t ofs = 0;
|
||||
cTValue *o;
|
||||
int res;
|
||||
if (opt == 0) opt = SEEK_SET;
|
||||
else if (opt == 1) opt = SEEK_CUR;
|
||||
else if (opt == 2) opt = SEEK_END;
|
||||
o = L->base+2;
|
||||
if (o < L->top) {
|
||||
if (tvisint(o))
|
||||
ofs = (int64_t)intV(o);
|
||||
else if (tvisnum(o))
|
||||
ofs = (int64_t)numV(o);
|
||||
else if (!tvisnil(o))
|
||||
lj_err_argt(L, 3, LUA_TNUMBER);
|
||||
}
|
||||
#if LJ_TARGET_POSIX
|
||||
res = fseeko(fp, ofs, opt);
|
||||
#elif _MSC_VER >= 1400
|
||||
res = _fseeki64(fp, ofs, opt);
|
||||
#elif defined(__MINGW32__)
|
||||
res = fseeko64(fp, ofs, opt);
|
||||
#else
|
||||
res = fseek(fp, (long)ofs, opt);
|
||||
#endif
|
||||
if (res)
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
#if LJ_TARGET_POSIX
|
||||
ofs = ftello(fp);
|
||||
#elif _MSC_VER >= 1400
|
||||
ofs = _ftelli64(fp);
|
||||
#elif defined(__MINGW32__)
|
||||
ofs = ftello64(fp);
|
||||
#else
|
||||
ofs = (int64_t)ftell(fp);
|
||||
#endif
|
||||
setint64V(L->top-1, ofs);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_setvbuf)
|
||||
{
|
||||
FILE *fp = io_tofile(L)->fp;
|
||||
int opt = lj_lib_checkopt(L, 2, -1, "\4full\4line\2no");
|
||||
size_t sz = (size_t)lj_lib_optint(L, 3, LUAL_BUFFERSIZE);
|
||||
if (opt == 0) opt = _IOFBF;
|
||||
else if (opt == 1) opt = _IOLBF;
|
||||
else if (opt == 2) opt = _IONBF;
|
||||
return luaL_fileresult(L, setvbuf(fp, NULL, opt, sz) == 0, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method_lines)
|
||||
{
|
||||
io_tofile(L);
|
||||
lua_pushcclosure(L, io_file_iter, (int)(L->top - L->base));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method___gc)
|
||||
{
|
||||
IOFileUD *iof = io_tofilep(L);
|
||||
if (iof->fp != NULL && (iof->type & IOFILE_TYPE_MASK) != IOFILE_TYPE_STDF)
|
||||
io_file_close(L, iof);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_method___tostring)
|
||||
{
|
||||
IOFileUD *iof = io_tofilep(L);
|
||||
if (iof->fp != NULL)
|
||||
lua_pushfstring(L, "file (%p)", iof->fp);
|
||||
else
|
||||
lua_pushliteral(L, "file (closed)");
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-1) LJLIB_SET(__index)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- I/O library functions ----------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_io
|
||||
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(!) /* Set environment. */
|
||||
|
||||
LJLIB_CF(io_open)
|
||||
{
|
||||
const char *fname = strdata(lj_lib_checkstr(L, 1));
|
||||
GCstr *s = lj_lib_optstr(L, 2);
|
||||
const char *mode = s ? strdata(s) : "r";
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
iof->fp = fopen(fname, mode);
|
||||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_popen)
|
||||
{
|
||||
#if LJ_TARGET_POSIX || LJ_TARGET_WINDOWS
|
||||
const char *fname = strdata(lj_lib_checkstr(L, 1));
|
||||
GCstr *s = lj_lib_optstr(L, 2);
|
||||
const char *mode = s ? strdata(s) : "r";
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
iof->type = IOFILE_TYPE_PIPE;
|
||||
#if LJ_TARGET_POSIX
|
||||
fflush(NULL);
|
||||
iof->fp = popen(fname, mode);
|
||||
#else
|
||||
iof->fp = _popen(fname, mode);
|
||||
#endif
|
||||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, fname);
|
||||
#else
|
||||
return luaL_error(L, LUA_QL("popen") " not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(io_tmpfile)
|
||||
{
|
||||
IOFileUD *iof = io_file_new(L);
|
||||
#if LJ_TARGET_PS3
|
||||
iof->fp = NULL; errno = ENOSYS;
|
||||
#else
|
||||
iof->fp = tmpfile();
|
||||
#endif
|
||||
return iof->fp != NULL ? 1 : luaL_fileresult(L, 0, NULL);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_close)
|
||||
{
|
||||
return lj_cf_io_method_close(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_read)
|
||||
{
|
||||
return io_file_read(L, io_stdfile(L, GCROOT_IO_INPUT), 0);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_write) LJLIB_REC(io_write GCROOT_IO_OUTPUT)
|
||||
{
|
||||
return io_file_write(L, io_stdfile(L, GCROOT_IO_OUTPUT), 0);
|
||||
}
|
||||
|
||||
LJLIB_CF(io_flush) LJLIB_REC(io_flush GCROOT_IO_OUTPUT)
|
||||
{
|
||||
return luaL_fileresult(L, fflush(io_stdfile(L, GCROOT_IO_OUTPUT)) == 0, NULL);
|
||||
}
|
||||
|
||||
static int io_std_getset(lua_State *L, ptrdiff_t id, const char *mode)
|
||||
{
|
||||
if (L->base < L->top && !tvisnil(L->base)) {
|
||||
if (tvisudata(L->base)) {
|
||||
io_tofile(L);
|
||||
L->top = L->base+1;
|
||||
} else {
|
||||
io_file_open(L, mode);
|
||||
}
|
||||
/* NOBARRIER: The standard I/O handles are GC roots. */
|
||||
setgcref(G(L)->gcroot[id], gcV(L->top-1));
|
||||
} else {
|
||||
setudataV(L, L->top++, IOSTDF_UD(L, id));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_input)
|
||||
{
|
||||
return io_std_getset(L, GCROOT_IO_INPUT, "r");
|
||||
}
|
||||
|
||||
LJLIB_CF(io_output)
|
||||
{
|
||||
return io_std_getset(L, GCROOT_IO_OUTPUT, "w");
|
||||
}
|
||||
|
||||
LJLIB_CF(io_lines)
|
||||
{
|
||||
if (L->base == L->top) setnilV(L->top++);
|
||||
if (!tvisnil(L->base)) { /* io.lines(fname) */
|
||||
IOFileUD *iof = io_file_open(L, "r");
|
||||
iof->type = IOFILE_TYPE_FILE|IOFILE_FLAG_CLOSE;
|
||||
L->top--;
|
||||
setudataV(L, L->base, udataV(L->top));
|
||||
} else { /* io.lines() iterates over stdin. */
|
||||
setudataV(L, L->base, IOSTDF_UD(L, GCROOT_IO_INPUT));
|
||||
}
|
||||
lua_pushcclosure(L, io_file_iter, (int)(L->top - L->base));
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(io_type)
|
||||
{
|
||||
cTValue *o = lj_lib_checkany(L, 1);
|
||||
if (!(tvisudata(o) && udataV(o)->udtype == UDTYPE_IO_FILE))
|
||||
setnilV(L->top++);
|
||||
else if (((IOFileUD *)uddata(udataV(o)))->fp != NULL)
|
||||
lua_pushliteral(L, "file");
|
||||
else
|
||||
lua_pushliteral(L, "closed file");
|
||||
return 1;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static GCobj *io_std_new(lua_State *L, FILE *fp, const char *name)
|
||||
{
|
||||
IOFileUD *iof = (IOFileUD *)lua_newuserdata(L, sizeof(IOFileUD));
|
||||
GCudata *ud = udataV(L->top-1);
|
||||
ud->udtype = UDTYPE_IO_FILE;
|
||||
/* NOBARRIER: The GCudata is new (marked white). */
|
||||
setgcref(ud->metatable, gcV(L->top-3));
|
||||
iof->fp = fp;
|
||||
iof->type = IOFILE_TYPE_STDF;
|
||||
lua_setfield(L, -2, name);
|
||||
return obj2gco(ud);
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_io(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, NULL, io_method);
|
||||
copyTV(L, L->top, L->top-1); L->top++;
|
||||
lua_setfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
|
||||
LJ_LIB_REG(L, LUA_IOLIBNAME, io);
|
||||
setgcref(G(L)->gcroot[GCROOT_IO_INPUT], io_std_new(L, stdin, "stdin"));
|
||||
setgcref(G(L)->gcroot[GCROOT_IO_OUTPUT], io_std_new(L, stdout, "stdout"));
|
||||
io_std_new(L, stderr, "stderr");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+663
@@ -0,0 +1,663 @@
|
||||
/*
|
||||
** JIT library.
|
||||
** Copyright (C) 2005-2013 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_debug.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASJIT
|
||||
#include "lj_ir.h"
|
||||
#include "lj_jit.h"
|
||||
#include "lj_ircall.h"
|
||||
#include "lj_iropt.h"
|
||||
#include "lj_target.h"
|
||||
#endif
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_vm.h"
|
||||
#include "lj_vmevent.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
#include "luajit.h"
|
||||
|
||||
/* -- jit.* functions ----------------------------------------------------- */
|
||||
|
||||
#define LJLIB_MODULE_jit
|
||||
|
||||
static int setjitmode(lua_State *L, int mode)
|
||||
{
|
||||
int idx = 0;
|
||||
if (L->base == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */
|
||||
mode |= LUAJIT_MODE_ENGINE;
|
||||
} else {
|
||||
/* jit.on/off/flush(func|proto, nil|true|false) */
|
||||
if (tvisfunc(L->base) || tvisproto(L->base))
|
||||
idx = 1;
|
||||
else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */
|
||||
goto err;
|
||||
if (L->base+1 < L->top && tvisbool(L->base+1))
|
||||
mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC;
|
||||
else
|
||||
mode |= LUAJIT_MODE_FUNC;
|
||||
}
|
||||
if (luaJIT_setmode(L, idx, mode) != 1) {
|
||||
if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE)
|
||||
lj_err_caller(L, LJ_ERR_NOJIT);
|
||||
err:
|
||||
lj_err_argt(L, 1, LUA_TFUNCTION);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_on)
|
||||
{
|
||||
return setjitmode(L, LUAJIT_MODE_ON);
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_off)
|
||||
{
|
||||
return setjitmode(L, LUAJIT_MODE_OFF);
|
||||
}
|
||||
|
||||
LJLIB_CF(jit_flush)
|
||||
{
|
||||
#if LJ_HASJIT
|
||||
if (L->base < L->top && !tvisnil(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 (int)(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. */
|
||||
const uint8_t *p = (const uint8_t *)strdata(s);
|
||||
uint32_t h = s->len;
|
||||
while (*p) h = h ^ (lj_rol(h, 6) + *p++);
|
||||
lua_pushvalue(L, 1);
|
||||
lua_rawseti(L, -2, VMEVENT_HASHIDX(h));
|
||||
G(L)->vmevmask = VMEVENT_NOCACHE; /* Invalidate cache. */
|
||||
} else { /* Detach if no event given. */
|
||||
setnilV(L->top++);
|
||||
while (lua_next(L, -2)) {
|
||||
L->top--;
|
||||
if (tvisfunc(L->top) && funcV(L->top) == fn) {
|
||||
setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-5) LJLIB_SET(os)
|
||||
LJLIB_PUSH(top-4) LJLIB_SET(arch)
|
||||
LJLIB_PUSH(top-3) LJLIB_SET(version_num)
|
||||
LJLIB_PUSH(top-2) LJLIB_SET(version)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- jit.util.* functions ------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_jit_util
|
||||
|
||||
/* -- Reflection API for Lua functions ------------------------------------ */
|
||||
|
||||
/* Return prototype of first argument (Lua function or prototype object) */
|
||||
static GCproto *check_Lproto(lua_State *L, int nolua)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
if (L->top > o) {
|
||||
if (tvisproto(o)) {
|
||||
return protoV(o);
|
||||
} else if (tvisfunc(o)) {
|
||||
if (isluafunc(funcV(o)))
|
||||
return funcproto(funcV(o));
|
||||
else if (nolua)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
lj_err_argt(L, 1, LUA_TFUNCTION);
|
||||
return NULL; /* unreachable */
|
||||
}
|
||||
|
||||
static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val)
|
||||
{
|
||||
setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val);
|
||||
}
|
||||
|
||||
/* local info = jit.util.funcinfo(func [,pc]) */
|
||||
LJLIB_CF(jit_util_funcinfo)
|
||||
{
|
||||
GCproto *pt = check_Lproto(L, 1);
|
||||
if (pt) {
|
||||
BCPos pc = (BCPos)lj_lib_optint(L, 2, 0);
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 16); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
setintfield(L, t, "linedefined", pt->firstline);
|
||||
setintfield(L, t, "lastlinedefined", pt->firstline + pt->numline);
|
||||
setintfield(L, t, "stackslots", pt->framesize);
|
||||
setintfield(L, t, "params", pt->numparams);
|
||||
setintfield(L, t, "bytecodes", (int32_t)pt->sizebc);
|
||||
setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc);
|
||||
setintfield(L, t, "nconsts", (int32_t)pt->sizekn);
|
||||
setintfield(L, t, "upvalues", (int32_t)pt->sizeuv);
|
||||
if (pc < pt->sizebc)
|
||||
setintfield(L, t, "currentline", lj_debug_line(pt, pc));
|
||||
lua_pushboolean(L, (pt->flags & PROTO_VARARG));
|
||||
lua_setfield(L, -2, "isvararg");
|
||||
lua_pushboolean(L, (pt->flags & PROTO_CHILD));
|
||||
lua_setfield(L, -2, "children");
|
||||
setstrV(L, L->top++, proto_chunkname(pt));
|
||||
lua_setfield(L, -2, "source");
|
||||
lj_debug_pushloc(L, pt, pc);
|
||||
lua_setfield(L, -2, "loc");
|
||||
} else {
|
||||
GCfunc *fn = funcV(L->base);
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
if (!iscfunc(fn))
|
||||
setintfield(L, t, "ffid", fn->c.ffid);
|
||||
setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")),
|
||||
(intptr_t)(void *)fn->c.f);
|
||||
setintfield(L, t, "upvalues", fn->c.nupvalues);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* local ins, m = jit.util.funcbc(func, pc) */
|
||||
LJLIB_CF(jit_util_funcbc)
|
||||
{
|
||||
GCproto *pt = check_Lproto(L, 0);
|
||||
BCPos pc = (BCPos)lj_lib_checkint(L, 2);
|
||||
if (pc < pt->sizebc) {
|
||||
BCIns ins = proto_bc(pt)[pc];
|
||||
BCOp op = bc_op(ins);
|
||||
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);
|
||||
ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2);
|
||||
if (idx >= 0) {
|
||||
if (idx < (ptrdiff_t)pt->sizekn) {
|
||||
copyTV(L, L->top-1, proto_knumtv(pt, idx));
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
if (~idx < (ptrdiff_t)pt->sizekgc) {
|
||||
GCobj *gc = proto_kgc(pt, idx);
|
||||
setgcV(L, L->top-1, gc, ~gc->gch.gct);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local name = jit.util.funcuvname(func, idx) */
|
||||
LJLIB_CF(jit_util_funcuvname)
|
||||
{
|
||||
GCproto *pt = check_Lproto(L, 0);
|
||||
uint32_t idx = (uint32_t)lj_lib_checkint(L, 2);
|
||||
if (idx < pt->sizeuv) {
|
||||
setstrV(L, L->top-1, lj_str_newz(L, lj_debug_uvname(pt, idx)));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* -- Reflection API for traces ------------------------------------------- */
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
/* Check trace argument. Must not throw for non-existent trace numbers. */
|
||||
static GCtrace *jit_checktrace(lua_State *L)
|
||||
{
|
||||
TraceNo tr = (TraceNo)lj_lib_checkint(L, 1);
|
||||
jit_State *J = L2J(L);
|
||||
if (tr > 0 && tr < J->sizetrace)
|
||||
return traceref(J, tr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Names of link types. ORDER LJ_TRLINK */
|
||||
static const char *const jit_trlinkname[] = {
|
||||
"none", "root", "loop", "tail-recursion", "up-recursion", "down-recursion",
|
||||
"interpreter", "return"
|
||||
};
|
||||
|
||||
/* local info = jit.util.traceinfo(tr) */
|
||||
LJLIB_CF(jit_util_traceinfo)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
if (T) {
|
||||
GCtab *t;
|
||||
lua_createtable(L, 0, 8); /* Increment hash size if fields are added. */
|
||||
t = tabV(L->top-1);
|
||||
setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1);
|
||||
setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk);
|
||||
setintfield(L, t, "link", T->link);
|
||||
setintfield(L, t, "nexit", T->nsnap);
|
||||
setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype]));
|
||||
lua_setfield(L, -2, "linktype");
|
||||
/* There are many more fields. Add them only when needed. */
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */
|
||||
LJLIB_CF(jit_util_traceir)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
|
||||
if (T && ref >= REF_BIAS && ref < T->nins) {
|
||||
IRIns *ir = &T->ir[ref];
|
||||
int32_t m = lj_ir_mode[ir->o];
|
||||
setintV(L->top-2, m);
|
||||
setintV(L->top-1, ir->ot);
|
||||
setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0));
|
||||
setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0));
|
||||
setintV(L->top++, ir->prev);
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local k, t [, slot] = jit.util.tracek(tr, idx) */
|
||||
LJLIB_CF(jit_util_tracek)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS;
|
||||
if (T && ref >= T->nk && ref < REF_BIAS) {
|
||||
IRIns *ir = &T->ir[ref];
|
||||
int32_t slot = -1;
|
||||
if (ir->o == IR_KSLOT) {
|
||||
slot = ir->op2;
|
||||
ir = &T->ir[ir->op1];
|
||||
}
|
||||
lj_ir_kvalue(L, L->top-2, ir);
|
||||
setintV(L->top-1, (int32_t)irt_type(ir->t));
|
||||
if (slot == -1)
|
||||
return 2;
|
||||
setintV(L->top++, slot);
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local snap = jit.util.tracesnap(tr, sn) */
|
||||
LJLIB_CF(jit_util_tracesnap)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
SnapNo sn = (SnapNo)lj_lib_checkint(L, 2);
|
||||
if (T && sn < T->nsnap) {
|
||||
SnapShot *snap = &T->snap[sn];
|
||||
SnapEntry *map = &T->snapmap[snap->mapofs];
|
||||
MSize n, nent = snap->nent;
|
||||
GCtab *t;
|
||||
lua_createtable(L, nent+2, 0);
|
||||
t = tabV(L->top-1);
|
||||
setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS);
|
||||
setintV(lj_tab_setint(L, t, 1), (int32_t)snap->nslots);
|
||||
for (n = 0; n < nent; n++)
|
||||
setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]);
|
||||
setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local mcode, addr, loop = jit.util.tracemc(tr) */
|
||||
LJLIB_CF(jit_util_tracemc)
|
||||
{
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
if (T && T->mcode != NULL) {
|
||||
setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode));
|
||||
setintptrV(L->top++, (intptr_t)(void *)T->mcode);
|
||||
setintV(L->top++, T->mcloop);
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local addr = jit.util.traceexitstub([tr,] exitno) */
|
||||
LJLIB_CF(jit_util_traceexitstub)
|
||||
{
|
||||
#ifdef EXITSTUBS_PER_GROUP
|
||||
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1);
|
||||
jit_State *J = L2J(L);
|
||||
if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) {
|
||||
setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno));
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
if (L->top > L->base+1) { /* Don't throw for one-argument variant. */
|
||||
GCtrace *T = jit_checktrace(L);
|
||||
ExitNo exitno = (ExitNo)lj_lib_checkint(L, 2);
|
||||
ExitNo maxexit = T->root ? T->nsnap+1 : T->nsnap;
|
||||
if (T && T->mcode != NULL && exitno < maxexit) {
|
||||
setintptrV(L->top-1, (intptr_t)(void *)exitstub_trace_addr(T, exitno));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* local addr = jit.util.ircalladdr(idx) */
|
||||
LJLIB_CF(jit_util_ircalladdr)
|
||||
{
|
||||
uint32_t idx = (uint32_t)lj_lib_checkint(L, 1);
|
||||
if (idx < IRCALL__MAX) {
|
||||
setintptrV(L->top-1, (intptr_t)(void *)lj_ir_callinfo[idx].func);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- jit.opt module ------------------------------------------------------ */
|
||||
|
||||
#if LJ_HASJIT
|
||||
|
||||
#define LJLIB_MODULE_jit_opt
|
||||
|
||||
/* Parse optimization level. */
|
||||
static int jitopt_level(jit_State *J, const char *str)
|
||||
{
|
||||
if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') {
|
||||
uint32_t flags;
|
||||
if (str[0] == '0') flags = JIT_F_OPT_0;
|
||||
else if (str[0] == '1') flags = JIT_F_OPT_1;
|
||||
else if (str[0] == '2') flags = JIT_F_OPT_2;
|
||||
else flags = JIT_F_OPT_3;
|
||||
J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags;
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
/* Parse optimization flag. */
|
||||
static int jitopt_flag(jit_State *J, const char *str)
|
||||
{
|
||||
const char *lst = JIT_F_OPTSTRING;
|
||||
uint32_t opt;
|
||||
int set = 1;
|
||||
if (str[0] == '+') {
|
||||
str++;
|
||||
} else if (str[0] == '-') {
|
||||
str++;
|
||||
set = 0;
|
||||
} else if (str[0] == 'n' && str[1] == 'o') {
|
||||
str += str[2] == '-' ? 3 : 2;
|
||||
set = 0;
|
||||
}
|
||||
for (opt = JIT_F_OPT_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. */
|
||||
}
|
||||
|
||||
/* 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;
|
||||
lua_assert(len != 0);
|
||||
if (strncmp(str, lst+1, len) == 0 && str[len] == '=') {
|
||||
int32_t n = 0;
|
||||
const char *p = &str[len+1];
|
||||
while (*p >= '0' && *p <= '9')
|
||||
n = n*10 + (*p++ - '0');
|
||||
if (*p) return 0; /* Malformed number. */
|
||||
J->param[i] = n;
|
||||
if (i == JIT_P_hotloop)
|
||||
lj_dispatch_init_hotcount(J2G(J));
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
lst += 1+len;
|
||||
}
|
||||
return 0; /* No match. */
|
||||
}
|
||||
|
||||
/* jit.opt.start(flags...) */
|
||||
LJLIB_CF(jit_opt_start)
|
||||
{
|
||||
jit_State *J = L2J(L);
|
||||
int nargs = (int)(L->top - L->base);
|
||||
if (nargs == 0) {
|
||||
J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT;
|
||||
} else {
|
||||
int i;
|
||||
for (i = 1; i <= nargs; i++) {
|
||||
const char *str = strdata(lj_lib_checkstr(L, i));
|
||||
if (!jitopt_level(J, str) &&
|
||||
!jitopt_flag(J, str) &&
|
||||
!jitopt_param(J, str))
|
||||
lj_err_callerv(L, LJ_ERR_JITOPT, str);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
#endif
|
||||
|
||||
/* -- JIT 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
|
||||
};
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_ARM && LJ_TARGET_LINUX
|
||||
#include <sys/utsname.h>
|
||||
#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
|
||||
#define JIT_F_SSE2 2
|
||||
#endif
|
||||
flags |= ((features[3] >> 15)&1) * JIT_F_CMOV;
|
||||
flags |= ((features[3] >> 26)&1) * JIT_F_SSE2;
|
||||
#if LJ_HASJIT
|
||||
flags |= ((features[2] >> 0)&1) * JIT_F_SSE3;
|
||||
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
|
||||
}
|
||||
/* Check for required instruction set support on x86 (unnecessary on x64). */
|
||||
#if LJ_TARGET_X86
|
||||
#if !defined(LUAJIT_CPU_NOCMOV)
|
||||
if (!(flags & JIT_F_CMOV))
|
||||
luaL_error(L, "CPU not supported");
|
||||
#endif
|
||||
#if defined(LUAJIT_CPU_SSE2)
|
||||
if (!(flags & JIT_F_SSE2))
|
||||
luaL_error(L, "CPU does not support SSE2 (recompile without -DLUAJIT_CPU_SSE2)");
|
||||
#endif
|
||||
#endif
|
||||
#elif LJ_TARGET_ARM
|
||||
#if LJ_HASJIT
|
||||
int ver = LJ_ARCH_VERSION; /* Compile-time ARM CPU detection. */
|
||||
#if LJ_TARGET_LINUX
|
||||
if (ver < 70) { /* Runtime ARM CPU detection. */
|
||||
struct utsname ut;
|
||||
uname(&ut);
|
||||
if (strncmp(ut.machine, "armv", 4) == 0) {
|
||||
if (ut.machine[4] >= '7')
|
||||
ver = 70;
|
||||
else if (ut.machine[4] == '6')
|
||||
ver = 60;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
flags |= ver >= 70 ? JIT_F_ARMV7 :
|
||||
ver >= 61 ? JIT_F_ARMV6T2_ :
|
||||
ver >= 60 ? JIT_F_ARMV6_ : 0;
|
||||
flags |= LJ_ARCH_HASFPU == 0 ? 0 : ver >= 70 ? JIT_F_VFPV3 : JIT_F_VFPV2;
|
||||
#endif
|
||||
#elif LJ_TARGET_PPC
|
||||
#if LJ_HASJIT
|
||||
#if LJ_ARCH_SQRT
|
||||
flags |= JIT_F_SQRT;
|
||||
#endif
|
||||
#if LJ_ARCH_ROUND
|
||||
flags |= JIT_F_ROUND;
|
||||
#endif
|
||||
#endif
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
/* Nothing to do. */
|
||||
#elif LJ_TARGET_MIPS
|
||||
#if LJ_HASJIT
|
||||
/* Compile-time MIPS CPU detection. */
|
||||
#if LJ_ARCH_VERSION >= 20
|
||||
flags |= JIT_F_MIPS32R2;
|
||||
#endif
|
||||
/* Runtime MIPS CPU detection. */
|
||||
#if defined(__GNUC__)
|
||||
if (!(flags & JIT_F_MIPS32R2)) {
|
||||
int x;
|
||||
/* On MIPS32R1 rotr is treated as srl. rotr r2,r2,1 -> srl r2,r2,1. */
|
||||
__asm__("li $2, 1\n\t.long 0x00221042\n\tmove %0, $2" : "=r"(x) : : "$2");
|
||||
if (x) flags |= JIT_F_MIPS32R2; /* Either 0x80000000 (R2) or 0 (R1). */
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#error "Missing CPU detection for this architecture"
|
||||
#endif
|
||||
UNUSED(L);
|
||||
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);
|
||||
#if LJ_TARGET_X86
|
||||
/* Silently turn off the JIT compiler on CPUs without SSE2. */
|
||||
if ((flags & JIT_F_SSE2))
|
||||
#endif
|
||||
J->flags = flags | JIT_F_ON | JIT_F_OPT_DEFAULT;
|
||||
memcpy(J->param, jit_param_default, sizeof(J->param));
|
||||
lj_dispatch_update(G(L));
|
||||
#else
|
||||
UNUSED(flags);
|
||||
#endif
|
||||
}
|
||||
|
||||
LUALIB_API int luaopen_jit(lua_State *L)
|
||||
{
|
||||
lua_pushliteral(L, LJ_OS_NAME);
|
||||
lua_pushliteral(L, LJ_ARCH_NAME);
|
||||
lua_pushinteger(L, LUAJIT_VERSION_NUM);
|
||||
lua_pushliteral(L, LUAJIT_VERSION);
|
||||
LJ_LIB_REG(L, LUA_JITLIBNAME, jit);
|
||||
#ifndef LUAJIT_DISABLE_JITUTIL
|
||||
LJ_LIB_REG(L, "jit.util", jit_util);
|
||||
#endif
|
||||
#if LJ_HASJIT
|
||||
LJ_LIB_REG(L, "jit.opt", jit_opt);
|
||||
#endif
|
||||
L->top -= 2;
|
||||
jit_init(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+233
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
** Math library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define lib_math_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_lib.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_math
|
||||
|
||||
LJLIB_ASM(math_abs) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknumber(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_floor) LJLIB_REC(math_round IRFPM_FLOOR)
|
||||
LJLIB_ASM_(math_ceil) LJLIB_REC(math_round IRFPM_CEIL)
|
||||
|
||||
LJLIB_ASM(math_sqrt) LJLIB_REC(math_unary IRFPM_SQRT)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_log10) LJLIB_REC(math_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_REC(math_htrig IRCALL_sinh)
|
||||
LJLIB_ASM_(math_cosh) LJLIB_REC(math_htrig IRCALL_cosh)
|
||||
LJLIB_ASM_(math_tanh) LJLIB_REC(math_htrig IRCALL_tanh)
|
||||
LJLIB_ASM_(math_frexp)
|
||||
LJLIB_ASM_(math_modf) LJLIB_REC(.)
|
||||
|
||||
LJLIB_ASM(math_log) LJLIB_REC(math_log)
|
||||
{
|
||||
double x = lj_lib_checknum(L, 1);
|
||||
if (L->base+1 < L->top) {
|
||||
double y = lj_lib_checknum(L, 2);
|
||||
#ifdef LUAJIT_NO_LOG2
|
||||
x = log(x); y = 1.0 / log(y);
|
||||
#else
|
||||
x = lj_vm_log2(x); y = 1.0 / lj_vm_log2(y);
|
||||
#endif
|
||||
setnumV(L->base-1, x*y); /* Do NOT join the expression to x / y. */
|
||||
return FFH_RES(1);
|
||||
}
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
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(.)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
lj_lib_checknum(L, 2);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_pow) LJLIB_REC(.)
|
||||
LJLIB_ASM_(math_fmod)
|
||||
|
||||
LJLIB_ASM(math_ldexp) LJLIB_REC(.)
|
||||
{
|
||||
lj_lib_checknum(L, 1);
|
||||
#if LJ_DUALNUM && !LJ_TARGET_X86ORX64
|
||||
lj_lib_checkint(L, 2);
|
||||
#else
|
||||
lj_lib_checknum(L, 2);
|
||||
#endif
|
||||
return FFH_RETRY;
|
||||
}
|
||||
|
||||
LJLIB_ASM(math_min) LJLIB_REC(math_minmax IR_MIN)
|
||||
{
|
||||
int i = 0;
|
||||
do { lj_lib_checknumber(L, ++i); } while (L->base+i < L->top);
|
||||
return FFH_RETRY;
|
||||
}
|
||||
LJLIB_ASM_(math_max) LJLIB_REC(math_minmax IR_MAX)
|
||||
|
||||
LJLIB_PUSH(3.14159265358979323846) LJLIB_SET(pi)
|
||||
LJLIB_PUSH(1e310) LJLIB_SET(huge)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* This implements a Tausworthe PRNG with period 2^223. Based on:
|
||||
** Tables of maximally-equidistributed combined LFSR generators,
|
||||
** Pierre L'Ecuyer, 1991, table 3, 1st entry.
|
||||
** Full-period ME-CF generator with L=64, J=4, k=223, N1=49.
|
||||
*/
|
||||
|
||||
/* PRNG state. */
|
||||
struct RandomState {
|
||||
uint64_t gen[4]; /* State of the 4 LFSR generators. */
|
||||
int valid; /* State is valid. */
|
||||
};
|
||||
|
||||
/* 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 = rs->gen[i]; \
|
||||
z = (((z<<q)^z) >> (k-s)) ^ ((z&((uint64_t)(int64_t)-1 << (64-k)))<<s); \
|
||||
r ^= z; rs->gen[i] = z;
|
||||
|
||||
/* PRNG step function. Returns a double in the range 1.0 <= d < 2.0. */
|
||||
LJ_NOINLINE uint64_t LJ_FASTCALL lj_math_random_step(RandomState *rs)
|
||||
{
|
||||
uint64_t z, r = 0;
|
||||
TW223_GEN(0, 63, 31, 18)
|
||||
TW223_GEN(1, 58, 19, 28)
|
||||
TW223_GEN(2, 55, 24, 7)
|
||||
TW223_GEN(3, 47, 21, 8)
|
||||
return (r & U64x(000fffff,ffffffff)) | U64x(3ff00000,00000000);
|
||||
}
|
||||
|
||||
/* PRNG initialization function. */
|
||||
static void random_init(RandomState *rs, double d)
|
||||
{
|
||||
uint32_t r = 0x11090601; /* 64-k[i] as four 8 bit constants. */
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
U64double u;
|
||||
uint32_t m = 1u << (r&255);
|
||||
r >>= 8;
|
||||
u.d = d = d * 3.14159265358979323846 + 2.7182818284590452354;
|
||||
if (u.u64 < m) u.u64 += m; /* Ensure k[i] MSB of gen[i] are non-zero. */
|
||||
rs->gen[i] = u.u64;
|
||||
}
|
||||
rs->valid = 1;
|
||||
for (i = 0; i < 10; i++)
|
||||
lj_math_random_step(rs);
|
||||
}
|
||||
|
||||
/* PRNG extract function. */
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with RandomState. */
|
||||
LJLIB_CF(math_random) LJLIB_REC(.)
|
||||
{
|
||||
int n = (int)(L->top - L->base);
|
||||
RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
|
||||
U64double u;
|
||||
double d;
|
||||
if (LJ_UNLIKELY(!rs->valid)) random_init(rs, 0.0);
|
||||
u.u64 = lj_math_random_step(rs);
|
||||
d = u.d - 1.0;
|
||||
if (n > 0) {
|
||||
#if LJ_DUALNUM
|
||||
int isint = 1;
|
||||
double r1;
|
||||
lj_lib_checknumber(L, 1);
|
||||
if (tvisint(L->base)) {
|
||||
r1 = (lua_Number)intV(L->base);
|
||||
} else {
|
||||
isint = 0;
|
||||
r1 = numV(L->base);
|
||||
}
|
||||
#else
|
||||
double r1 = lj_lib_checknum(L, 1);
|
||||
#endif
|
||||
if (n == 1) {
|
||||
d = lj_vm_floor(d*r1) + 1.0; /* d is an int in range [1, r1] */
|
||||
} else {
|
||||
#if LJ_DUALNUM
|
||||
double r2;
|
||||
lj_lib_checknumber(L, 2);
|
||||
if (tvisint(L->base+1)) {
|
||||
r2 = (lua_Number)intV(L->base+1);
|
||||
} else {
|
||||
isint = 0;
|
||||
r2 = numV(L->base+1);
|
||||
}
|
||||
#else
|
||||
double r2 = lj_lib_checknum(L, 2);
|
||||
#endif
|
||||
d = lj_vm_floor(d*(r2-r1+1.0)) + r1; /* d is an int in range [r1, r2] */
|
||||
}
|
||||
#if LJ_DUALNUM
|
||||
if (isint) {
|
||||
setintV(L->top-1, lj_num2int(d));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
} /* else: d is a double in range [0, 1] */
|
||||
setnumV(L->top++, d);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* PRNG seed function. */
|
||||
LJLIB_PUSH(top-2) /* Upvalue holds userdata with RandomState. */
|
||||
LJLIB_CF(math_randomseed)
|
||||
{
|
||||
RandomState *rs = (RandomState *)(uddata(udataV(lj_lib_upvalue(L, 1))));
|
||||
random_init(rs, lj_lib_checknum(L, 1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_math(lua_State *L)
|
||||
{
|
||||
RandomState *rs;
|
||||
rs = (RandomState *)lua_newuserdata(L, sizeof(RandomState));
|
||||
rs->valid = 0; /* Use lazy initialization to save some time on startup. */
|
||||
LJ_LIB_REG(L, LUA_MATHLIBNAME, math);
|
||||
#if defined(LUA_COMPAT_MOD) && !LJ_52
|
||||
lua_getfield(L, -1, "fmod");
|
||||
lua_setfield(L, -2, "mod");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+280
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
** OS library.
|
||||
** Copyright (C) 2005-2013 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"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
#if LJ_TARGET_POSIX
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#define LJLIB_MODULE_os
|
||||
|
||||
LJLIB_CF(os_execute)
|
||||
{
|
||||
#if LJ_TARGET_CONSOLE
|
||||
#if LJ_52
|
||||
errno = ENOSYS;
|
||||
return luaL_fileresult(L, 0, NULL);
|
||||
#else
|
||||
lua_pushinteger(L, -1);
|
||||
return 1;
|
||||
#endif
|
||||
#else
|
||||
const char *cmd = luaL_optstring(L, 1, NULL);
|
||||
int stat = system(cmd);
|
||||
#if LJ_52
|
||||
if (cmd)
|
||||
return luaL_execresult(L, stat);
|
||||
setboolV(L->top++, 1);
|
||||
#else
|
||||
setintV(L->top++, stat);
|
||||
#endif
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(os_remove)
|
||||
{
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
return luaL_fileresult(L, remove(filename) == 0, filename);
|
||||
}
|
||||
|
||||
LJLIB_CF(os_rename)
|
||||
{
|
||||
const char *fromname = luaL_checkstring(L, 1);
|
||||
const char *toname = luaL_checkstring(L, 2);
|
||||
return luaL_fileresult(L, rename(fromname, toname) == 0, fromname);
|
||||
}
|
||||
|
||||
LJLIB_CF(os_tmpname)
|
||||
{
|
||||
#if LJ_TARGET_PS3
|
||||
lj_err_caller(L, LJ_ERR_OSUNIQF);
|
||||
return 0;
|
||||
#else
|
||||
#if LJ_TARGET_POSIX
|
||||
char buf[15+1];
|
||||
int fp;
|
||||
strcpy(buf, "/tmp/lua_XXXXXX");
|
||||
fp = mkstemp(buf);
|
||||
if (fp != -1)
|
||||
close(fp);
|
||||
else
|
||||
lj_err_caller(L, LJ_ERR_OSUNIQF);
|
||||
#else
|
||||
char buf[L_tmpnam];
|
||||
if (tmpnam(buf) == NULL)
|
||||
lj_err_caller(L, LJ_ERR_OSUNIQF);
|
||||
#endif
|
||||
lua_pushstring(L, buf);
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
LJLIB_CF(os_getenv)
|
||||
{
|
||||
#if LJ_TARGET_CONSOLE
|
||||
lua_pushnil(L);
|
||||
#else
|
||||
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(os_exit)
|
||||
{
|
||||
int status;
|
||||
if (L->base < L->top && tvisbool(L->base))
|
||||
status = boolV(L->base) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
else
|
||||
status = lj_lib_optint(L, 1, EXIT_SUCCESS);
|
||||
if (L->base+1 < L->top && tvistruecond(L->base+1))
|
||||
lua_close(L);
|
||||
exit(status);
|
||||
return 0; /* Unreachable. */
|
||||
}
|
||||
|
||||
LJLIB_CF(os_clock)
|
||||
{
|
||||
setnumV(L->top++, ((lua_Number)clock())*(1.0/(lua_Number)CLOCKS_PER_SEC));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void setfield(lua_State *L, const char *key, int value)
|
||||
{
|
||||
lua_pushinteger(L, value);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
static void setboolfield(lua_State *L, const char *key, int value)
|
||||
{
|
||||
if (value < 0) /* undefined? */
|
||||
return; /* does not set field */
|
||||
lua_pushboolean(L, value);
|
||||
lua_setfield(L, -2, key);
|
||||
}
|
||||
|
||||
static int getboolfield(lua_State *L, const char *key)
|
||||
{
|
||||
int res;
|
||||
lua_getfield(L, -1, key);
|
||||
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int getfield(lua_State *L, const char *key, int d)
|
||||
{
|
||||
int res;
|
||||
lua_getfield(L, -1, key);
|
||||
if (lua_isnumber(L, -1)) {
|
||||
res = (int)lua_tointeger(L, -1);
|
||||
} else {
|
||||
if (d < 0)
|
||||
lj_err_callerv(L, LJ_ERR_OSDATEF, key);
|
||||
res = d;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
LJLIB_CF(os_date)
|
||||
{
|
||||
const char *s = luaL_optstring(L, 1, "%c");
|
||||
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
|
||||
struct tm *stm;
|
||||
#if LJ_TARGET_POSIX
|
||||
struct tm rtm;
|
||||
#endif
|
||||
if (*s == '!') { /* UTC? */
|
||||
s++; /* Skip '!' */
|
||||
#if LJ_TARGET_POSIX
|
||||
stm = gmtime_r(&t, &rtm);
|
||||
#else
|
||||
stm = gmtime(&t);
|
||||
#endif
|
||||
} else {
|
||||
#if LJ_TARGET_POSIX
|
||||
stm = localtime_r(&t, &rtm);
|
||||
#else
|
||||
stm = localtime(&t);
|
||||
#endif
|
||||
}
|
||||
if (stm == NULL) { /* Invalid date? */
|
||||
setnilV(L->top-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, LUA_OSLIBNAME, os);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+605
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
** Package library.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
**
|
||||
** Major portions taken verbatim or adapted from the Lua interpreter.
|
||||
** Copyright (C) 1994-2012 Lua.org, PUC-Rio. See Copyright Notice in lua.h
|
||||
*/
|
||||
|
||||
#define lib_package_c
|
||||
#define LUA_LIB
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_lib.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* Error codes for ll_loadfunc. */
|
||||
#define PACKAGE_ERR_LIB 1
|
||||
#define PACKAGE_ERR_FUNC 2
|
||||
#define PACKAGE_ERR_LOAD 3
|
||||
|
||||
/* Redefined in platform specific part. */
|
||||
#define PACKAGE_LIB_FAIL "open"
|
||||
#define setprogdir(L) ((void)0)
|
||||
|
||||
/* Symbol name prefixes. */
|
||||
#define SYMPREFIX_CF "luaopen_%s"
|
||||
#define SYMPREFIX_BC "luaJIT_BC_%s"
|
||||
|
||||
#if LJ_TARGET_DLOPEN
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
static void ll_unloadlib(void *lib)
|
||||
{
|
||||
dlclose(lib);
|
||||
}
|
||||
|
||||
static void *ll_load(lua_State *L, const char *path, int gl)
|
||||
{
|
||||
void *lib = dlopen(path, RTLD_NOW | (gl ? RTLD_GLOBAL : RTLD_LOCAL));
|
||||
if (lib == NULL) lua_pushstring(L, dlerror());
|
||||
return lib;
|
||||
}
|
||||
|
||||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
|
||||
{
|
||||
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
|
||||
if (f == NULL) lua_pushstring(L, dlerror());
|
||||
return f;
|
||||
}
|
||||
|
||||
static const char *ll_bcsym(void *lib, const char *sym)
|
||||
{
|
||||
#if defined(RTLD_DEFAULT)
|
||||
if (lib == NULL) lib = RTLD_DEFAULT;
|
||||
#elif LJ_TARGET_OSX || LJ_TARGET_BSD
|
||||
if (lib == NULL) lib = (void *)(intptr_t)-2;
|
||||
#endif
|
||||
return (const char *)dlsym(lib, sym);
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#ifndef WINVER
|
||||
#define WINVER 0x0500
|
||||
#endif
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
|
||||
#define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
|
||||
BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
|
||||
#endif
|
||||
|
||||
#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, int gl)
|
||||
{
|
||||
HINSTANCE lib = LoadLibraryA(path);
|
||||
if (lib == NULL) pusherror(L);
|
||||
UNUSED(gl);
|
||||
return lib;
|
||||
}
|
||||
|
||||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
|
||||
{
|
||||
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
|
||||
if (f == NULL) pusherror(L);
|
||||
return f;
|
||||
}
|
||||
|
||||
static const char *ll_bcsym(void *lib, const char *sym)
|
||||
{
|
||||
if (lib) {
|
||||
return (const char *)GetProcAddress((HINSTANCE)lib, sym);
|
||||
} else {
|
||||
HINSTANCE h = GetModuleHandleA(NULL);
|
||||
const char *p = (const char *)GetProcAddress(h, sym);
|
||||
if (p == NULL && GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
|
||||
(const char *)ll_bcsym, &h))
|
||||
p = (const char *)GetProcAddress(h, sym);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#undef PACKAGE_LIB_FAIL
|
||||
#define PACKAGE_LIB_FAIL "absent"
|
||||
|
||||
#define DLMSG "dynamic libraries not enabled; no support for target OS"
|
||||
|
||||
static void ll_unloadlib(void *lib)
|
||||
{
|
||||
UNUSED(lib);
|
||||
}
|
||||
|
||||
static void *ll_load(lua_State *L, const char *path, int gl)
|
||||
{
|
||||
UNUSED(path); UNUSED(gl);
|
||||
lua_pushliteral(L, DLMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static lua_CFunction ll_sym(lua_State *L, void *lib, const char *sym)
|
||||
{
|
||||
UNUSED(lib); UNUSED(sym);
|
||||
lua_pushliteral(L, DLMSG);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *ll_bcsym(void *lib, const char *sym)
|
||||
{
|
||||
UNUSED(lib); UNUSED(sym);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void **ll_register(lua_State *L, const char *path)
|
||||
{
|
||||
void **plib;
|
||||
lua_pushfstring(L, "LOADLIB: %s", path);
|
||||
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
|
||||
if (!lua_isnil(L, -1)) { /* is there an entry? */
|
||||
plib = (void **)lua_touserdata(L, -1);
|
||||
} else { /* no entry yet; create one */
|
||||
lua_pop(L, 1);
|
||||
plib = (void **)lua_newuserdata(L, sizeof(void *));
|
||||
*plib = NULL;
|
||||
luaL_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 const char *mksymname(lua_State *L, const char *modname,
|
||||
const char *prefix)
|
||||
{
|
||||
const char *funcname;
|
||||
const char *mark = strchr(modname, *LUA_IGMARK);
|
||||
if (mark) modname = mark + 1;
|
||||
funcname = luaL_gsub(L, modname, ".", "_");
|
||||
funcname = lua_pushfstring(L, prefix, funcname);
|
||||
lua_remove(L, -2); /* remove 'gsub' result */
|
||||
return funcname;
|
||||
}
|
||||
|
||||
static int ll_loadfunc(lua_State *L, const char *path, const char *name, int r)
|
||||
{
|
||||
void **reg = ll_register(L, path);
|
||||
if (*reg == NULL) *reg = ll_load(L, path, (*name == '*'));
|
||||
if (*reg == NULL) {
|
||||
return PACKAGE_ERR_LIB; /* Unable to load library. */
|
||||
} else if (*name == '*') { /* Only load library into global namespace. */
|
||||
lua_pushboolean(L, 1);
|
||||
return 0;
|
||||
} else {
|
||||
const char *sym = r ? name : mksymname(L, name, SYMPREFIX_CF);
|
||||
lua_CFunction f = ll_sym(L, *reg, sym);
|
||||
if (f) {
|
||||
lua_pushcfunction(L, f);
|
||||
return 0;
|
||||
}
|
||||
if (!r) {
|
||||
const char *bcdata = ll_bcsym(*reg, mksymname(L, name, SYMPREFIX_BC));
|
||||
lua_pop(L, 1);
|
||||
if (bcdata) {
|
||||
if (luaL_loadbuffer(L, bcdata, ~(size_t)0, name) != 0)
|
||||
return PACKAGE_ERR_LOAD;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return PACKAGE_ERR_FUNC; /* Unable to find function. */
|
||||
}
|
||||
}
|
||||
|
||||
static int lj_cf_package_loadlib(lua_State *L)
|
||||
{
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
const char *init = luaL_checkstring(L, 2);
|
||||
int st = ll_loadfunc(L, path, init, 1);
|
||||
if (st == 0) { /* no errors? */
|
||||
return 1; /* return the loaded function */
|
||||
} else { /* error; error message is on stack top */
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2);
|
||||
lua_pushstring(L, (st == PACKAGE_ERR_LIB) ? PACKAGE_LIB_FAIL : "init");
|
||||
return 3; /* return nil, error message, and where */
|
||||
}
|
||||
}
|
||||
|
||||
static int lj_cf_package_unloadlib(lua_State *L)
|
||||
{
|
||||
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
|
||||
if (*lib) ll_unloadlib(*lib);
|
||||
*lib = NULL; /* mark library as closed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static int readable(const char *filename)
|
||||
{
|
||||
FILE *f = fopen(filename, "r"); /* try to open file */
|
||||
if (f == NULL) return 0; /* open failed */
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const char *pushnexttemplate(lua_State *L, const char *path)
|
||||
{
|
||||
const char *l;
|
||||
while (*path == *LUA_PATHSEP) path++; /* skip separators */
|
||||
if (*path == '\0') return NULL; /* no more templates */
|
||||
l = strchr(path, *LUA_PATHSEP); /* find next separator */
|
||||
if (l == NULL) l = path + strlen(path);
|
||||
lua_pushlstring(L, path, (size_t)(l - path)); /* template */
|
||||
return l;
|
||||
}
|
||||
|
||||
static const char *searchpath (lua_State *L, const char *name,
|
||||
const char *path, const char *sep,
|
||||
const char *dirsep)
|
||||
{
|
||||
luaL_Buffer msg; /* to build error message */
|
||||
luaL_buffinit(L, &msg);
|
||||
if (*sep != '\0') /* non-empty separator? */
|
||||
name = luaL_gsub(L, name, sep, dirsep); /* replace it by 'dirsep' */
|
||||
while ((path = pushnexttemplate(L, path)) != NULL) {
|
||||
const char *filename = luaL_gsub(L, lua_tostring(L, -1),
|
||||
LUA_PATH_MARK, name);
|
||||
lua_remove(L, -2); /* remove path template */
|
||||
if (readable(filename)) /* does file exist and is readable? */
|
||||
return filename; /* return that file name */
|
||||
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
|
||||
lua_remove(L, -2); /* remove file name */
|
||||
luaL_addvalue(&msg); /* concatenate error msg. entry */
|
||||
}
|
||||
luaL_pushresult(&msg); /* create error message */
|
||||
return NULL; /* not found */
|
||||
}
|
||||
|
||||
static int lj_cf_package_searchpath(lua_State *L)
|
||||
{
|
||||
const char *f = searchpath(L, luaL_checkstring(L, 1),
|
||||
luaL_checkstring(L, 2),
|
||||
luaL_optstring(L, 3, "."),
|
||||
luaL_optstring(L, 4, LUA_DIRSEP));
|
||||
if (f != NULL) {
|
||||
return 1;
|
||||
} else { /* error message is on top of the stack */
|
||||
lua_pushnil(L);
|
||||
lua_insert(L, -2);
|
||||
return 2; /* return nil + error message */
|
||||
}
|
||||
}
|
||||
|
||||
static const char *findfile(lua_State *L, const char *name,
|
||||
const char *pname)
|
||||
{
|
||||
const char *path;
|
||||
lua_getfield(L, LUA_ENVIRONINDEX, pname);
|
||||
path = lua_tostring(L, -1);
|
||||
if (path == NULL)
|
||||
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
|
||||
return searchpath(L, name, path, ".", LUA_DIRSEP);
|
||||
}
|
||||
|
||||
static void loaderror(lua_State *L, const char *filename)
|
||||
{
|
||||
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
|
||||
lua_tostring(L, 1), filename, lua_tostring(L, -1));
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_lua(lua_State *L)
|
||||
{
|
||||
const char *filename;
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
filename = findfile(L, name, "path");
|
||||
if (filename == NULL) return 1; /* library not found in this path */
|
||||
if (luaL_loadfile(L, filename) != 0)
|
||||
loaderror(L, filename);
|
||||
return 1; /* library loaded successfully */
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_c(lua_State *L)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
const char *filename = findfile(L, name, "cpath");
|
||||
if (filename == NULL) return 1; /* library not found in this path */
|
||||
if (ll_loadfunc(L, filename, name, 0) != 0)
|
||||
loaderror(L, filename);
|
||||
return 1; /* library loaded successfully */
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_croot(lua_State *L)
|
||||
{
|
||||
const char *filename;
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
const char *p = strchr(name, '.');
|
||||
int st;
|
||||
if (p == NULL) return 0; /* is root */
|
||||
lua_pushlstring(L, name, (size_t)(p - name));
|
||||
filename = findfile(L, lua_tostring(L, -1), "cpath");
|
||||
if (filename == NULL) return 1; /* root not found */
|
||||
if ((st = ll_loadfunc(L, filename, name, 0)) != 0) {
|
||||
if (st != PACKAGE_ERR_FUNC) loaderror(L, filename); /* real error */
|
||||
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
|
||||
name, filename);
|
||||
return 1; /* function not found */
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lj_cf_package_loader_preload(lua_State *L)
|
||||
{
|
||||
const char *name = luaL_checkstring(L, 1);
|
||||
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
|
||||
if (!lua_istable(L, -1))
|
||||
luaL_error(L, LUA_QL("package.preload") " must be a table");
|
||||
lua_getfield(L, -1, name);
|
||||
if (lua_isnil(L, -1)) { /* Not found? */
|
||||
const char *bcname = mksymname(L, name, SYMPREFIX_BC);
|
||||
const char *bcdata = ll_bcsym(NULL, bcname);
|
||||
if (bcdata == NULL || luaL_loadbuffer(L, bcdata, ~(size_t)0, name) != 0)
|
||||
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
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 */
|
||||
}
|
||||
lj_lib_checkfpu(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static void setfenv(lua_State *L)
|
||||
{
|
||||
lua_Debug ar;
|
||||
if (lua_getstack(L, 1, &ar) == 0 ||
|
||||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
|
||||
lua_iscfunction(L, -1))
|
||||
luaL_error(L, LUA_QL("module") " not called from a Lua function");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_setfenv(L, -2);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static void dooptions(lua_State *L, int n)
|
||||
{
|
||||
int i;
|
||||
for (i = 2; i <= n; i++) {
|
||||
lua_pushvalue(L, i); /* get option (a function) */
|
||||
lua_pushvalue(L, -2); /* module */
|
||||
lua_call(L, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void modinit(lua_State *L, const char *modname)
|
||||
{
|
||||
const char *dot;
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "_M"); /* module._M = module */
|
||||
lua_pushstring(L, modname);
|
||||
lua_setfield(L, -2, "_NAME");
|
||||
dot = strrchr(modname, '.'); /* look for last dot in module name */
|
||||
if (dot == NULL) dot = modname; else dot++;
|
||||
/* set _PACKAGE as package name (full module name minus last part) */
|
||||
lua_pushlstring(L, modname, (size_t)(dot - modname));
|
||||
lua_setfield(L, -2, "_PACKAGE");
|
||||
}
|
||||
|
||||
static int lj_cf_package_module(lua_State *L)
|
||||
{
|
||||
const char *modname = luaL_checkstring(L, 1);
|
||||
int 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, int noenv)
|
||||
{
|
||||
#if LJ_TARGET_CONSOLE
|
||||
const char *path = NULL;
|
||||
UNUSED(envname);
|
||||
#else
|
||||
const char *path = getenv(envname);
|
||||
#endif
|
||||
if (path == NULL || noenv) {
|
||||
lua_pushstring(L, def);
|
||||
} else {
|
||||
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
|
||||
LUA_PATHSEP AUXMARK LUA_PATHSEP);
|
||||
luaL_gsub(L, path, AUXMARK, def);
|
||||
lua_remove(L, -2);
|
||||
}
|
||||
setprogdir(L);
|
||||
lua_setfield(L, -2, fieldname);
|
||||
}
|
||||
|
||||
static const luaL_Reg package_lib[] = {
|
||||
{ "loadlib", lj_cf_package_loadlib },
|
||||
{ "searchpath", lj_cf_package_searchpath },
|
||||
{ "seeall", lj_cf_package_seeall },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const luaL_Reg package_global[] = {
|
||||
{ "module", lj_cf_package_module },
|
||||
{ "require", lj_cf_package_require },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const lua_CFunction package_loaders[] =
|
||||
{
|
||||
lj_cf_package_loader_preload,
|
||||
lj_cf_package_loader_lua,
|
||||
lj_cf_package_loader_c,
|
||||
lj_cf_package_loader_croot,
|
||||
NULL
|
||||
};
|
||||
|
||||
LUALIB_API int luaopen_package(lua_State *L)
|
||||
{
|
||||
int i;
|
||||
int noenv;
|
||||
luaL_newmetatable(L, "_LOADLIB");
|
||||
lj_lib_pushcf(L, lj_cf_package_unloadlib, 1);
|
||||
lua_setfield(L, -2, "__gc");
|
||||
luaL_register(L, LUA_LOADLIBNAME, package_lib);
|
||||
lua_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++) {
|
||||
lj_lib_pushcf(L, package_loaders[i], 1);
|
||||
lua_rawseti(L, -2, i+1);
|
||||
}
|
||||
lua_setfield(L, -2, "loaders");
|
||||
lua_getfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
|
||||
noenv = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT, noenv);
|
||||
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT, noenv);
|
||||
lua_pushliteral(L, LUA_PATH_CONFIG);
|
||||
lua_setfield(L, -2, "config");
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 16);
|
||||
lua_setfield(L, -2, "loaded");
|
||||
luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", 4);
|
||||
lua_setfield(L, -2, "preload");
|
||||
lua_pushvalue(L, LUA_GLOBALSINDEX);
|
||||
luaL_register(L, NULL, package_global);
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+940
@@ -0,0 +1,940 @@
|
||||
/*
|
||||
** String library.
|
||||
** Copyright (C) 2005-2013 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_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_bcdump.h"
|
||||
#include "lj_char.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 = (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 k = lj_lib_checkint(L, 2);
|
||||
GCstr *sep = lj_lib_optstr(L, 3);
|
||||
int32_t len = (int32_t)s->len;
|
||||
global_State *g = G(L);
|
||||
int64_t tlen;
|
||||
const char *src;
|
||||
char *buf;
|
||||
if (k <= 0) {
|
||||
empty:
|
||||
setstrV(L, L->base-1, &g->strempty);
|
||||
return FFH_RES(1);
|
||||
}
|
||||
if (sep) {
|
||||
tlen = (int64_t)len + sep->len;
|
||||
if (tlen > LJ_MAX_STR)
|
||||
lj_err_caller(L, LJ_ERR_STROV);
|
||||
tlen *= k;
|
||||
if (tlen > LJ_MAX_STR)
|
||||
lj_err_caller(L, LJ_ERR_STROV);
|
||||
} else {
|
||||
tlen = (int64_t)k * len;
|
||||
if (tlen > LJ_MAX_STR)
|
||||
lj_err_caller(L, LJ_ERR_STROV);
|
||||
}
|
||||
if (tlen == 0) goto empty;
|
||||
buf = lj_str_needbuf(L, &g->tmpbuf, (MSize)tlen);
|
||||
src = strdata(s);
|
||||
if (sep) {
|
||||
tlen -= sep->len; /* Ignore trailing separator. */
|
||||
if (k > 1) { /* Paste one string and one separator. */
|
||||
int32_t i;
|
||||
i = 0; while (i < len) *buf++ = src[i++];
|
||||
src = strdata(sep); len = sep->len;
|
||||
i = 0; while (i < len) *buf++ = src[i++];
|
||||
src = g->tmpbuf.buf; len += s->len; k--; /* Now copy that k-1 times. */
|
||||
}
|
||||
}
|
||||
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->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)
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
static int writer_buf(lua_State *L, const void *p, size_t size, void *b)
|
||||
{
|
||||
luaL_addlstring((luaL_Buffer *)b, (const char *)p, size);
|
||||
UNUSED(L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(string_dump)
|
||||
{
|
||||
GCfunc *fn = lj_lib_checkfunc(L, 1);
|
||||
int strip = L->base+1 < L->top && tvistruecond(L->base+1);
|
||||
luaL_Buffer b;
|
||||
L->top = L->base+1;
|
||||
luaL_buffinit(L, &b);
|
||||
if (!isluafunc(fn) || lj_bcwrite(L, funcproto(fn), writer_buf, &b, strip))
|
||||
lj_err_caller(L, LJ_ERR_STRDUMP);
|
||||
luaL_pushresult(&b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* macro to `unsign' a character */
|
||||
#define uchar(c) ((unsigned char)(c))
|
||||
|
||||
#define CAP_UNFINISHED (-1)
|
||||
#define CAP_POSITION (-2)
|
||||
|
||||
typedef struct MatchState {
|
||||
const char *src_init; /* init of source string */
|
||||
const char *src_end; /* end (`\0') of source string */
|
||||
lua_State *L;
|
||||
int level; /* total number of captures (finished or unfinished) */
|
||||
int depth;
|
||||
struct {
|
||||
const char *init;
|
||||
ptrdiff_t len;
|
||||
} capture[LUA_MAXCAPTURES];
|
||||
} MatchState;
|
||||
|
||||
#define L_ESC '%'
|
||||
#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_CHAR_ALPHA,0,LJ_CHAR_CNTRL,LJ_CHAR_DIGIT,0,0,LJ_CHAR_GRAPH,0,0,0,0,
|
||||
LJ_CHAR_LOWER,0,0,0,LJ_CHAR_PUNCT,0,0,LJ_CHAR_SPACE,0,
|
||||
LJ_CHAR_UPPER,0,LJ_CHAR_ALNUM,LJ_CHAR_XDIGIT,0,0,0,0,0,0,0
|
||||
};
|
||||
|
||||
static int match_class(int c, int cl)
|
||||
{
|
||||
if ((cl & 0xc0) == 0x40) {
|
||||
int t = match_class_map[(cl&0x1f)];
|
||||
if (t) {
|
||||
t = lj_char_isa(c, t);
|
||||
return (cl & 0x20) ? t : !t;
|
||||
}
|
||||
if (cl == 'z') return c == 0;
|
||||
if (cl == 'Z') return c != 0;
|
||||
}
|
||||
return (cl == c);
|
||||
}
|
||||
|
||||
static int matchbracketclass(int c, const char *p, const char *ec)
|
||||
{
|
||||
int sig = 1;
|
||||
if (*(p+1) == '^') {
|
||||
sig = 0;
|
||||
p++; /* skip the `^' */
|
||||
}
|
||||
while (++p < ec) {
|
||||
if (*p == L_ESC) {
|
||||
p++;
|
||||
if (match_class(c, uchar(*p)))
|
||||
return sig;
|
||||
}
|
||||
else if ((*(p+1) == '-') && (p+2 < ec)) {
|
||||
p+=2;
|
||||
if (uchar(*(p-2)) <= c && c <= uchar(*p))
|
||||
return sig;
|
||||
}
|
||||
else if (uchar(*p) == c) return sig;
|
||||
}
|
||||
return !sig;
|
||||
}
|
||||
|
||||
static int singlematch(int c, const char *p, const char *ep)
|
||||
{
|
||||
switch (*p) {
|
||||
case '.': return 1; /* matches any char */
|
||||
case L_ESC: return match_class(c, uchar(*(p+1)));
|
||||
case '[': return matchbracketclass(c, p, ep-1);
|
||||
default: return (uchar(*p) == c);
|
||||
}
|
||||
}
|
||||
|
||||
static const char *match(MatchState *ms, const char *s, const char *p);
|
||||
|
||||
static const char *matchbalance(MatchState *ms, const char *s, const char *p)
|
||||
{
|
||||
if (*p == 0 || *(p+1) == 0)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATU);
|
||||
if (*s != *p) {
|
||||
return NULL;
|
||||
} else {
|
||||
int b = *p;
|
||||
int e = *(p+1);
|
||||
int cont = 1;
|
||||
while (++s < ms->src_end) {
|
||||
if (*s == e) {
|
||||
if (--cont == 0) return s+1;
|
||||
} else if (*s == b) {
|
||||
cont++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL; /* string ends out of balance */
|
||||
}
|
||||
|
||||
static const char *max_expand(MatchState *ms, const char *s,
|
||||
const char *p, const char *ep)
|
||||
{
|
||||
ptrdiff_t i = 0; /* counts maximum expand for item */
|
||||
while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))
|
||||
i++;
|
||||
/* keeps trying to match with the maximum repetitions */
|
||||
while (i>=0) {
|
||||
const char *res = match(ms, (s+i), ep+1);
|
||||
if (res) return res;
|
||||
i--; /* else didn't match; reduce 1 repetition to try again */
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *min_expand(MatchState *ms, const char *s,
|
||||
const char *p, const char *ep)
|
||||
{
|
||||
for (;;) {
|
||||
const char *res = match(ms, s, ep+1);
|
||||
if (res != NULL)
|
||||
return res;
|
||||
else if (s<ms->src_end && singlematch(uchar(*s), p, ep))
|
||||
s++; /* try with one more repetition */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *start_capture(MatchState *ms, const char *s,
|
||||
const char *p, int what)
|
||||
{
|
||||
const char *res;
|
||||
int level = ms->level;
|
||||
if (level >= LUA_MAXCAPTURES) lj_err_caller(ms->L, LJ_ERR_STRCAPN);
|
||||
ms->capture[level].init = s;
|
||||
ms->capture[level].len = what;
|
||||
ms->level = level+1;
|
||||
if ((res=match(ms, s, p)) == NULL) /* match failed? */
|
||||
ms->level--; /* undo capture */
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *end_capture(MatchState *ms, const char *s,
|
||||
const char *p)
|
||||
{
|
||||
int l = capture_to_close(ms);
|
||||
const char *res;
|
||||
ms->capture[l].len = s - ms->capture[l].init; /* close capture */
|
||||
if ((res = match(ms, s, p)) == NULL) /* match failed? */
|
||||
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *match_capture(MatchState *ms, const char *s, int l)
|
||||
{
|
||||
size_t len;
|
||||
l = check_capture(ms, l);
|
||||
len = (size_t)ms->capture[l].len;
|
||||
if ((size_t)(ms->src_end-s) >= len &&
|
||||
memcmp(ms->capture[l].init, s, len) == 0)
|
||||
return s+len;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char *match(MatchState *ms, const char *s, const char *p)
|
||||
{
|
||||
if (++ms->depth > LJ_MAX_XLEVEL)
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATX);
|
||||
init: /* using goto's to optimize tail recursion */
|
||||
switch (*p) {
|
||||
case '(': /* start capture */
|
||||
if (*(p+1) == ')') /* position capture? */
|
||||
s = start_capture(ms, s, p+2, CAP_POSITION);
|
||||
else
|
||||
s = start_capture(ms, s, p+1, CAP_UNFINISHED);
|
||||
break;
|
||||
case ')': /* end capture */
|
||||
s = end_capture(ms, s, p+1);
|
||||
break;
|
||||
case L_ESC:
|
||||
switch (*(p+1)) {
|
||||
case 'b': /* balanced string? */
|
||||
s = matchbalance(ms, s, p+2);
|
||||
if (s == NULL) break;
|
||||
p+=4;
|
||||
goto init; /* else s = match(ms, s, p+4); */
|
||||
case 'f': { /* frontier? */
|
||||
const char *ep; char previous;
|
||||
p += 2;
|
||||
if (*p != '[')
|
||||
lj_err_caller(ms->L, LJ_ERR_STRPATB);
|
||||
ep = classend(ms, p); /* points to what is next */
|
||||
previous = (s == ms->src_init) ? '\0' : *(s-1);
|
||||
if (matchbracketclass(uchar(previous), p, ep-1) ||
|
||||
!matchbracketclass(uchar(*s), p, ep-1)) { s = NULL; break; }
|
||||
p=ep;
|
||||
goto init; /* else s = match(ms, s, ep); */
|
||||
}
|
||||
default:
|
||||
if (lj_char_isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */
|
||||
s = match_capture(ms, s, uchar(*(p+1)));
|
||||
if (s == NULL) break;
|
||||
p+=2;
|
||||
goto init; /* else s = match(ms, s, p+2) */
|
||||
}
|
||||
goto dflt; /* case default */
|
||||
}
|
||||
break;
|
||||
case '\0': /* end of pattern */
|
||||
break; /* match succeeded */
|
||||
case '$':
|
||||
/* is the `$' the last char in pattern? */
|
||||
if (*(p+1) != '\0') goto dflt;
|
||||
if (s != ms->src_end) s = NULL; /* check end of string */
|
||||
break;
|
||||
default: dflt: { /* it is a pattern item */
|
||||
const char *ep = classend(ms, p); /* points to what is next */
|
||||
int m = s<ms->src_end && singlematch(uchar(*s), p, ep);
|
||||
switch (*ep) {
|
||||
case '?': { /* optional */
|
||||
const char *res;
|
||||
if (m && ((res=match(ms, s+1, ep+1)) != NULL)) {
|
||||
s = res;
|
||||
break;
|
||||
}
|
||||
p=ep+1;
|
||||
goto init; /* else s = match(ms, s, ep+1); */
|
||||
}
|
||||
case '*': /* 0 or more repetitions */
|
||||
s = max_expand(ms, s, p, ep);
|
||||
break;
|
||||
case '+': /* 1 or more repetitions */
|
||||
s = (m ? max_expand(ms, s+1, p, ep) : NULL);
|
||||
break;
|
||||
case '-': /* 0 or more repetitions (minimum) */
|
||||
s = min_expand(ms, s, p, ep);
|
||||
break;
|
||||
default:
|
||||
if (m) { s++; p=ep; goto init; } /* else s = match(ms, s+1, ep); */
|
||||
s = NULL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
ms->depth--;
|
||||
return s;
|
||||
}
|
||||
|
||||
static 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) {
|
||||
#if LJ_52
|
||||
setnilV(L->top-1);
|
||||
return 1;
|
||||
#else
|
||||
init = (ptrdiff_t)l1;
|
||||
#endif
|
||||
}
|
||||
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 = ms.depth = 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 = ms.depth = 0;
|
||||
if ((e = match(&ms, src, p)) != NULL) {
|
||||
int32_t pos = (int32_t)(e - s);
|
||||
if (e == src) pos++; /* Ensure progress for empty match. */
|
||||
tvpos->u32.lo = (uint32_t)pos;
|
||||
return push_captures(&ms, src, e);
|
||||
}
|
||||
}
|
||||
return 0; /* not found */
|
||||
}
|
||||
|
||||
LJLIB_CF(string_gmatch)
|
||||
{
|
||||
lj_lib_checkstr(L, 1);
|
||||
lj_lib_checkstr(L, 2);
|
||||
L->top = L->base+3;
|
||||
(L->top-1)->u64 = 0;
|
||||
lj_lib_pushcc(L, lj_cf_string_gmatch_aux, FF_string_gmatch_aux, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void add_s(MatchState *ms, luaL_Buffer *b, const char *s, const char *e)
|
||||
{
|
||||
size_t l, i;
|
||||
const char *news = lua_tolstring(ms->L, 3, &l);
|
||||
for (i = 0; i < l; i++) {
|
||||
if (news[i] != L_ESC) {
|
||||
luaL_addchar(b, news[i]);
|
||||
} else {
|
||||
i++; /* skip ESC */
|
||||
if (!lj_char_isdigit(uchar(news[i]))) {
|
||||
luaL_addchar(b, news[i]);
|
||||
} else if (news[i] == '0') {
|
||||
luaL_addlstring(b, s, (size_t)(e - s));
|
||||
} else {
|
||||
push_onecapture(ms, news[i] - '1', s, e);
|
||||
luaL_addvalue(b); /* add capture to accumulated result */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void add_value(MatchState *ms, luaL_Buffer *b,
|
||||
const char *s, const char *e)
|
||||
{
|
||||
lua_State *L = ms->L;
|
||||
switch (lua_type(L, 3)) {
|
||||
case LUA_TNUMBER:
|
||||
case LUA_TSTRING: {
|
||||
add_s(ms, b, s, e);
|
||||
return;
|
||||
}
|
||||
case LUA_TFUNCTION: {
|
||||
int n;
|
||||
lua_pushvalue(L, 3);
|
||||
n = push_captures(ms, s, e);
|
||||
lua_call(L, n, 1);
|
||||
break;
|
||||
}
|
||||
case LUA_TTABLE: {
|
||||
push_onecapture(ms, 0, s, e);
|
||||
lua_gettable(L, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!lua_toboolean(L, -1)) { /* nil or false? */
|
||||
lua_pop(L, 1);
|
||||
lua_pushlstring(L, s, (size_t)(e - s)); /* keep original text */
|
||||
} else if (!lua_isstring(L, -1)) {
|
||||
lj_err_callerv(L, LJ_ERR_STRGSRV, luaL_typename(L, -1));
|
||||
}
|
||||
luaL_addvalue(b); /* add result to accumulator */
|
||||
}
|
||||
|
||||
LJLIB_CF(string_gsub)
|
||||
{
|
||||
size_t srcl;
|
||||
const char *src = luaL_checklstring(L, 1, &srcl);
|
||||
const char *p = luaL_checkstring(L, 2);
|
||||
int tr = lua_type(L, 3);
|
||||
int max_s = luaL_optint(L, 4, (int)(srcl+1));
|
||||
int anchor = (*p == '^') ? (p++, 1) : 0;
|
||||
int n = 0;
|
||||
MatchState ms;
|
||||
luaL_Buffer b;
|
||||
if (!(tr == LUA_TNUMBER || tr == LUA_TSTRING ||
|
||||
tr == LUA_TFUNCTION || tr == LUA_TTABLE))
|
||||
lj_err_arg(L, 3, LJ_ERR_NOSFT);
|
||||
luaL_buffinit(L, &b);
|
||||
ms.L = L;
|
||||
ms.src_init = src;
|
||||
ms.src_end = src+srcl;
|
||||
while (n < max_s) {
|
||||
const char *e;
|
||||
ms.level = ms.depth = 0;
|
||||
e = match(&ms, src, p);
|
||||
if (e) {
|
||||
n++;
|
||||
add_value(&ms, &b, src, e);
|
||||
}
|
||||
if (e && e>src) /* non empty match? */
|
||||
src = e; /* skip it */
|
||||
else if (src < ms.src_end)
|
||||
luaL_addchar(&b, *src++);
|
||||
else
|
||||
break;
|
||||
if (anchor)
|
||||
break;
|
||||
}
|
||||
luaL_addlstring(&b, src, (size_t)(ms.src_end-src));
|
||||
luaL_pushresult(&b);
|
||||
lua_pushinteger(L, n); /* number of substitutions */
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
/* 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--) {
|
||||
uint32_t c = uchar(*s);
|
||||
if (c == '"' || c == '\\' || c == '\n') {
|
||||
luaL_addchar(b, '\\');
|
||||
} else if (lj_char_iscntrl(c)) { /* This can only be 0-31 or 127. */
|
||||
uint32_t d;
|
||||
luaL_addchar(b, '\\');
|
||||
if (c >= 100 || lj_char_isdigit(uchar(s[1]))) {
|
||||
luaL_addchar(b, '0'+(c >= 100)); if (c >= 100) c -= 100;
|
||||
goto tens;
|
||||
} else if (c >= 10) {
|
||||
tens:
|
||||
d = (c * 205) >> 11; c -= d * 10; luaL_addchar(b, '0'+d);
|
||||
}
|
||||
c += '0';
|
||||
}
|
||||
luaL_addchar(b, c);
|
||||
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_char_isdigit(uchar(*p))) p++; /* skip width */
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
if (*p == '.') {
|
||||
p++;
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* skip precision */
|
||||
if (lj_char_isdigit(uchar(*p))) p++; /* (2 digits at most) */
|
||||
}
|
||||
if (lj_char_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';
|
||||
}
|
||||
|
||||
static unsigned LUA_INTFRM_T num2intfrm(lua_State *L, int arg)
|
||||
{
|
||||
if (sizeof(LUA_INTFRM_T) == 4) {
|
||||
return (LUA_INTFRM_T)lj_lib_checkbit(L, arg);
|
||||
} else {
|
||||
cTValue *o;
|
||||
lj_lib_checknumber(L, arg);
|
||||
o = L->base+arg-1;
|
||||
if (tvisint(o))
|
||||
return (LUA_INTFRM_T)intV(o);
|
||||
else
|
||||
return (LUA_INTFRM_T)numV(o);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned LUA_INTFRM_T num2uintfrm(lua_State *L, int arg)
|
||||
{
|
||||
if (sizeof(LUA_INTFRM_T) == 4) {
|
||||
return (unsigned LUA_INTFRM_T)lj_lib_checkbit(L, arg);
|
||||
} else {
|
||||
cTValue *o;
|
||||
lj_lib_checknumber(L, arg);
|
||||
o = L->base+arg-1;
|
||||
if (tvisint(o))
|
||||
return (unsigned LUA_INTFRM_T)intV(o);
|
||||
else if ((int32_t)o->u32.hi < 0)
|
||||
return (unsigned LUA_INTFRM_T)(LUA_INTFRM_T)numV(o);
|
||||
else
|
||||
return (unsigned LUA_INTFRM_T)numV(o);
|
||||
}
|
||||
}
|
||||
|
||||
static GCstr *meta_tostring(lua_State *L, int arg)
|
||||
{
|
||||
TValue *o = L->base+arg-1;
|
||||
cTValue *mo;
|
||||
lua_assert(o < L->top); /* Caller already checks for existence. */
|
||||
if (LJ_LIKELY(tvisstr(o)))
|
||||
return strV(o);
|
||||
if (!tvisnil(mo = lj_meta_lookup(L, o, MM_tostring))) {
|
||||
copyTV(L, L->top++, mo);
|
||||
copyTV(L, L->top++, o);
|
||||
lua_call(L, 1, 1);
|
||||
L->top--;
|
||||
if (tvisstr(L->top))
|
||||
return strV(L->top);
|
||||
o = L->base+arg-1;
|
||||
copyTV(L, o, L->top);
|
||||
}
|
||||
if (tvisnumber(o)) {
|
||||
return lj_str_fromnumber(L, o);
|
||||
} else if (tvisnil(o)) {
|
||||
return lj_str_newlit(L, "nil");
|
||||
} else if (tvisfalse(o)) {
|
||||
return lj_str_newlit(L, "false");
|
||||
} else if (tvistrue(o)) {
|
||||
return lj_str_newlit(L, "true");
|
||||
} else {
|
||||
if (tvisfunc(o) && isffunc(funcV(o)))
|
||||
lj_str_pushf(L, "function: builtin#%d", funcV(o)->c.ffid);
|
||||
else
|
||||
lj_str_pushf(L, "%s: %p", lj_typename(o), lua_topointer(L, arg));
|
||||
L->top--;
|
||||
return strV(L->top);
|
||||
}
|
||||
}
|
||||
|
||||
LJLIB_CF(string_format)
|
||||
{
|
||||
int arg = 1, top = (int)(L->top - L->base);
|
||||
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 */
|
||||
if (++arg > top)
|
||||
luaL_argerror(L, arg, lj_obj_typename[0]);
|
||||
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, num2intfrm(L, arg));
|
||||
break;
|
||||
case 'o': case 'u': case 'x': case 'X':
|
||||
addintlen(form);
|
||||
sprintf(buff, form, num2uintfrm(L, arg));
|
||||
break;
|
||||
case 'e': case 'E': case 'f': case 'g': case 'G': case 'a': case 'A': {
|
||||
TValue tv;
|
||||
tv.n = lj_lib_checknum(L, arg);
|
||||
if (LJ_UNLIKELY((tv.u32.hi << 1) >= 0xffe00000)) {
|
||||
/* Canonicalize output of non-finite values. */
|
||||
char *p, nbuf[LJ_STR_NUMBUF];
|
||||
size_t len = lj_str_bufnum(nbuf, &tv);
|
||||
if (strfrmt[-1] < 'a') {
|
||||
nbuf[len-3] = nbuf[len-3] - 0x20;
|
||||
nbuf[len-2] = nbuf[len-2] - 0x20;
|
||||
nbuf[len-1] = nbuf[len-1] - 0x20;
|
||||
}
|
||||
nbuf[len] = '\0';
|
||||
for (p = form; *p < 'A' && *p != '.'; p++) ;
|
||||
*p++ = 's'; *p = '\0';
|
||||
sprintf(buff, form, nbuf);
|
||||
break;
|
||||
}
|
||||
sprintf(buff, form, (double)tv.n);
|
||||
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 = meta_tostring(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;
|
||||
global_State *g;
|
||||
LJ_LIB_REG(L, LUA_STRLIBNAME, string);
|
||||
#if defined(LUA_COMPAT_GFIND) && !LJ_52
|
||||
lua_getfield(L, -1, "gmatch");
|
||||
lua_setfield(L, -2, "gfind");
|
||||
#endif
|
||||
mt = lj_tab_new(L, 0, 1);
|
||||
/* NOBARRIER: basemt is a GC root. */
|
||||
g = G(L);
|
||||
setgcref(basemt_it(g, LJ_TSTR), obj2gco(mt));
|
||||
settabV(L, lj_tab_setstr(L, mt, mmname_str(g, MM_index)), tabV(L->top-1));
|
||||
mt->nomm = (uint8_t)(~(1u<<MM_index));
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+300
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
** Table library.
|
||||
** Copyright (C) 2005-2013 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;
|
||||
ptrdiff_t i;
|
||||
for (i = (ptrdiff_t)t->asize - 1; i >= 0; i--)
|
||||
if (!tvisnil(&array[i])) {
|
||||
m = (lua_Number)(int32_t)i;
|
||||
break;
|
||||
}
|
||||
node = noderef(t->node);
|
||||
for (i = (ptrdiff_t)t->hmask; i >= 0; i--)
|
||||
if (!tvisnil(&node[i].val) && tvisnumber(&node[i].key)) {
|
||||
lua_Number n = numberVnum(&node[i].key);
|
||||
if (n > m) m = n;
|
||||
}
|
||||
setnumV(L->top-1, m);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_insert) LJLIB_REC(.)
|
||||
{
|
||||
GCtab *t = lj_lib_checktab(L, 1);
|
||||
int32_t n, i = (int32_t)lj_tab_len(t) + 1;
|
||||
int nargs = (int)((char *)L->top - (char *)L->base);
|
||||
if (nargs != 2*sizeof(TValue)) {
|
||||
if (nargs != 3*sizeof(TValue))
|
||||
lj_err_caller(L, LJ_ERR_TABINS);
|
||||
/* NOBARRIER: This just moves existing elements around. */
|
||||
for (n = lj_lib_checkint(L, 2); i > n; i--) {
|
||||
/* The set may invalidate the get pointer, so need to do it first! */
|
||||
TValue *dst = lj_tab_setint(L, t, i);
|
||||
cTValue *src = lj_tab_getint(t, i-1);
|
||||
if (src) {
|
||||
copyTV(L, dst, src);
|
||||
} else {
|
||||
setnilV(dst);
|
||||
}
|
||||
}
|
||||
i = n;
|
||||
}
|
||||
{
|
||||
TValue *dst = lj_tab_setint(L, t, i);
|
||||
copyTV(L, dst, L->top-1); /* Set new value. */
|
||||
lj_gc_barriert(L, t, dst);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
LJLIB_CF(table_remove) LJLIB_REC(.)
|
||||
{
|
||||
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)) /* Nothing to remove? */
|
||||
return 0;
|
||||
lua_rawgeti(L, 1, pos); /* Get previous value. */
|
||||
/* 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)); /* Remove (last) value. */
|
||||
return 1; /* Return previous value. */
|
||||
}
|
||||
|
||||
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 && !tvisnil(L->base+3)) ?
|
||||
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) || tvisnumber(o)))
|
||||
lj_err_callerv(L, LJ_ERR_TABCAT, lj_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;
|
||||
}
|
||||
|
||||
#if LJ_52
|
||||
LJLIB_PUSH("n")
|
||||
LJLIB_CF(table_pack)
|
||||
{
|
||||
TValue *array, *base = L->base;
|
||||
MSize i, n = (uint32_t)(L->top - base);
|
||||
GCtab *t = lj_tab_new(L, n ? n+1 : 0, 1);
|
||||
/* NOBARRIER: The table is new (marked white). */
|
||||
setintV(lj_tab_setstr(L, t, strV(lj_lib_upvalue(L, 1))), (int32_t)n);
|
||||
for (array = tvref(t->array) + 1, i = 0; i < n; i++)
|
||||
copyTV(L, &array[i], &base[i]);
|
||||
settabV(L, base, t);
|
||||
L->top = base+1;
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
LUALIB_API int luaopen_table(lua_State *L)
|
||||
{
|
||||
LJ_LIB_REG(L, LUA_TABLIBNAME, table);
|
||||
#if LJ_52
|
||||
lua_getglobal(L, "unpack");
|
||||
lua_setfield(L, -2, "unpack");
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
Vendored
+26
@@ -0,0 +1,26 @@
|
||||
# Valgrind suppression file for LuaJIT 2.0.
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr4
|
||||
fun:lj_str_cmp
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr1
|
||||
fun:lj_str_cmp
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr4
|
||||
fun:lj_str_new
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Addr1
|
||||
fun:lj_str_new
|
||||
}
|
||||
{
|
||||
Optimized string compare
|
||||
Memcheck:Cond
|
||||
fun:lj_str_new
|
||||
}
|
||||
Vendored
+1388
File diff suppressed because it is too large
Load Diff
Vendored
+17
@@ -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
|
||||
Vendored
+1200
File diff suppressed because it is too large
Load Diff
Vendored
+419
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
** Target architecture selection.
|
||||
** Copyright (C) 2005-2013 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
|
||||
#define LUAJIT_ARCH_ARM 3
|
||||
#define LUAJIT_ARCH_arm 3
|
||||
#define LUAJIT_ARCH_PPC 4
|
||||
#define LUAJIT_ARCH_ppc 4
|
||||
#define LUAJIT_ARCH_PPCSPE 5
|
||||
#define LUAJIT_ARCH_ppcspe 5
|
||||
#define LUAJIT_ARCH_MIPS 6
|
||||
#define LUAJIT_ARCH_mips 6
|
||||
|
||||
/* Target OS. */
|
||||
#define LUAJIT_OS_OTHER 0
|
||||
#define LUAJIT_OS_WINDOWS 1
|
||||
#define LUAJIT_OS_LINUX 2
|
||||
#define LUAJIT_OS_OSX 3
|
||||
#define LUAJIT_OS_BSD 4
|
||||
#define LUAJIT_OS_POSIX 5
|
||||
|
||||
/* 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
|
||||
#elif defined(__arm__) || defined(__arm) || defined(__ARM__) || defined(__ARM)
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_ARM
|
||||
#elif defined(__ppc__) || defined(__ppc) || defined(__PPC__) || defined(__PPC) || defined(__powerpc__) || defined(__powerpc) || defined(__POWERPC__) || defined(__POWERPC) || defined(_M_PPC)
|
||||
#ifdef __NO_FPRS__
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_PPCSPE
|
||||
#else
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_PPC
|
||||
#endif
|
||||
#elif defined(__mips__) || defined(__mips) || defined(__MIPS__) || defined(__MIPS)
|
||||
#define LUAJIT_TARGET LUAJIT_ARCH_MIPS
|
||||
#else
|
||||
#error "No support for this architecture (yet)"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Select native OS if no target OS defined. */
|
||||
#ifndef LUAJIT_OS
|
||||
|
||||
#if defined(_WIN32) && !defined(_XBOX_VER)
|
||||
#define LUAJIT_OS LUAJIT_OS_WINDOWS
|
||||
#elif defined(__linux__)
|
||||
#define LUAJIT_OS LUAJIT_OS_LINUX
|
||||
#elif defined(__MACH__) && defined(__APPLE__)
|
||||
#define LUAJIT_OS LUAJIT_OS_OSX
|
||||
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || \
|
||||
defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
#define LUAJIT_OS LUAJIT_OS_BSD
|
||||
#elif (defined(__sun__) && defined(__svr4__)) || defined(__CYGWIN__)
|
||||
#define LUAJIT_OS LUAJIT_OS_POSIX
|
||||
#else
|
||||
#define LUAJIT_OS LUAJIT_OS_OTHER
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Set target OS properties. */
|
||||
#if LUAJIT_OS == LUAJIT_OS_WINDOWS
|
||||
#define LJ_OS_NAME "Windows"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_LINUX
|
||||
#define LJ_OS_NAME "Linux"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_OSX
|
||||
#define LJ_OS_NAME "OSX"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_BSD
|
||||
#define LJ_OS_NAME "BSD"
|
||||
#elif LUAJIT_OS == LUAJIT_OS_POSIX
|
||||
#define LJ_OS_NAME "POSIX"
|
||||
#else
|
||||
#define LJ_OS_NAME "Other"
|
||||
#endif
|
||||
|
||||
#define LJ_TARGET_WINDOWS (LUAJIT_OS == LUAJIT_OS_WINDOWS)
|
||||
#define LJ_TARGET_LINUX (LUAJIT_OS == LUAJIT_OS_LINUX)
|
||||
#define LJ_TARGET_OSX (LUAJIT_OS == LUAJIT_OS_OSX)
|
||||
#define LJ_TARGET_IOS (LJ_TARGET_OSX && LUAJIT_TARGET == LUAJIT_ARCH_ARM)
|
||||
#define LJ_TARGET_POSIX (LUAJIT_OS > LUAJIT_OS_WINDOWS)
|
||||
#define LJ_TARGET_DLOPEN LJ_TARGET_POSIX
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#define LJ_TARGET_PS3 1
|
||||
#define LJ_TARGET_CONSOLE 1
|
||||
#endif
|
||||
|
||||
#if _XBOX_VER >= 200
|
||||
#define LJ_TARGET_XBOX360 1
|
||||
#define LJ_TARGET_CONSOLE 1
|
||||
#endif
|
||||
|
||||
#define LJ_NUMMODE_SINGLE 0 /* Single-number mode only. */
|
||||
#define LJ_NUMMODE_SINGLE_DUAL 1 /* Default to single-number mode. */
|
||||
#define LJ_NUMMODE_DUAL 2 /* Dual-number mode only. */
|
||||
#define LJ_NUMMODE_DUAL_SINGLE 3 /* Default to dual-number mode. */
|
||||
|
||||
/* Set target architecture properties. */
|
||||
#if LUAJIT_TARGET == LUAJIT_ARCH_X86
|
||||
|
||||
#define LJ_ARCH_NAME "x86"
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#if LJ_TARGET_WINDOWS || __CYGWIN__
|
||||
#define LJ_ABI_WIN 1
|
||||
#else
|
||||
#define LJ_ABI_WIN 0
|
||||
#endif
|
||||
#define LJ_TARGET_X86 1
|
||||
#define LJ_TARGET_X86ORX64 1
|
||||
#define LJ_TARGET_EHRETREG 0
|
||||
#define LJ_TARGET_MASKSHIFT 1
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNALIGNED 1
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_X64
|
||||
|
||||
#define LJ_ARCH_NAME "x64"
|
||||
#define LJ_ARCH_BITS 64
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#define LJ_ABI_WIN LJ_TARGET_WINDOWS
|
||||
#define LJ_TARGET_X64 1
|
||||
#define LJ_TARGET_X86ORX64 1
|
||||
#define LJ_TARGET_EHRETREG 0
|
||||
#define LJ_TARGET_JUMPRANGE 31 /* +-2^31 = +-2GB */
|
||||
#define LJ_TARGET_MASKSHIFT 1
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNALIGNED 1
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE_DUAL
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_ARM
|
||||
|
||||
#define LJ_ARCH_NAME "arm"
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#if !defined(LJ_ARCH_HASFPU) && __SOFTFP__
|
||||
#define LJ_ARCH_HASFPU 0
|
||||
#endif
|
||||
#if !defined(LJ_ABI_SOFTFP) && !__ARM_PCS_VFP
|
||||
#define LJ_ABI_SOFTFP 1
|
||||
#endif
|
||||
#define LJ_ABI_EABI 1
|
||||
#define LJ_TARGET_ARM 1
|
||||
#define LJ_TARGET_EHRETREG 0
|
||||
#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */
|
||||
#define LJ_TARGET_MASKSHIFT 0
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL
|
||||
|
||||
#if __ARM_ARCH_7__ || __ARM_ARCH_7A__ || __ARM_ARCH_7R__ || __ARM_ARCH_7S__
|
||||
#define LJ_ARCH_VERSION 70
|
||||
#elif __ARM_ARCH_6T2__
|
||||
#define LJ_ARCH_VERSION 61
|
||||
#elif __ARM_ARCH_6__ || __ARM_ARCH_6J__ || __ARM_ARCH_6K__ || __ARM_ARCH_6Z__ || __ARM_ARCH_6ZK__
|
||||
#define LJ_ARCH_VERSION 60
|
||||
#else
|
||||
#define LJ_ARCH_VERSION 50
|
||||
#endif
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_PPC
|
||||
|
||||
#define LJ_ARCH_NAME "ppc"
|
||||
#if _LP64
|
||||
#define LJ_ARCH_BITS 64
|
||||
#else
|
||||
#define LJ_ARCH_BITS 32
|
||||
#endif
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_BE
|
||||
#define LJ_TARGET_PPC 1
|
||||
#define LJ_TARGET_EHRETREG 3
|
||||
#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */
|
||||
#define LJ_TARGET_MASKSHIFT 0
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_DUAL_SINGLE
|
||||
|
||||
#if _ARCH_PWR7
|
||||
#define LJ_ARCH_VERSION 70
|
||||
#elif _ARCH_PWR6
|
||||
#define LJ_ARCH_VERSION 60
|
||||
#elif _ARCH_PWR5X
|
||||
#define LJ_ARCH_VERSION 51
|
||||
#elif _ARCH_PWR5
|
||||
#define LJ_ARCH_VERSION 50
|
||||
#elif _ARCH_PWR4
|
||||
#define LJ_ARCH_VERSION 40
|
||||
#else
|
||||
#define LJ_ARCH_VERSION 0
|
||||
#endif
|
||||
#if __PPC64__ || __powerpc64__ || LJ_TARGET_CONSOLE
|
||||
#define LJ_ARCH_PPC64 1
|
||||
#define LJ_ARCH_NOFFI 1
|
||||
#endif
|
||||
#if _ARCH_PPCSQ
|
||||
#define LJ_ARCH_SQRT 1
|
||||
#endif
|
||||
#if _ARCH_PWR5X
|
||||
#define LJ_ARCH_ROUND 1
|
||||
#endif
|
||||
#if __PPU__
|
||||
#define LJ_ARCH_CELL 1
|
||||
#endif
|
||||
#if LJ_TARGET_XBOX360
|
||||
#define LJ_ARCH_XENON 1
|
||||
#endif
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_PPCSPE
|
||||
|
||||
#define LJ_ARCH_NAME "ppcspe"
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_BE
|
||||
#ifndef LJ_ABI_SOFTFP
|
||||
#define LJ_ABI_SOFTFP 1
|
||||
#endif
|
||||
#define LJ_ABI_EABI 1
|
||||
#define LJ_TARGET_PPCSPE 1
|
||||
#define LJ_TARGET_EHRETREG 3
|
||||
#define LJ_TARGET_JUMPRANGE 25 /* +-2^25 = +-32MB */
|
||||
#define LJ_TARGET_MASKSHIFT 0
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 1 /* Want only IR_BROL. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE
|
||||
#define LJ_ARCH_NOFFI 1 /* NYI: comparisons, calls. */
|
||||
#define LJ_ARCH_NOJIT 1
|
||||
|
||||
#elif LUAJIT_TARGET == LUAJIT_ARCH_MIPS
|
||||
|
||||
#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL)
|
||||
#define LJ_ARCH_NAME "mipsel"
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_LE
|
||||
#else
|
||||
#define LJ_ARCH_NAME "mips"
|
||||
#define LJ_ARCH_ENDIAN LUAJIT_BE
|
||||
#endif
|
||||
#define LJ_ARCH_BITS 32
|
||||
#define LJ_TARGET_MIPS 1
|
||||
#define LJ_TARGET_EHRETREG 4
|
||||
#define LJ_TARGET_JUMPRANGE 27 /* 2*2^27 = 256MB-aligned region */
|
||||
#define LJ_TARGET_MASKSHIFT 1
|
||||
#define LJ_TARGET_MASKROT 1
|
||||
#define LJ_TARGET_UNIFYROT 2 /* Want only IR_BROR. */
|
||||
#define LJ_ARCH_NUMMODE LJ_NUMMODE_SINGLE
|
||||
|
||||
#if _MIPS_ARCH_MIPS32R2
|
||||
#define LJ_ARCH_VERSION 20
|
||||
#else
|
||||
#define LJ_ARCH_VERSION 10
|
||||
#endif
|
||||
|
||||
#else
|
||||
#error "No target architecture defined"
|
||||
#endif
|
||||
|
||||
#ifndef LJ_PAGESIZE
|
||||
#define LJ_PAGESIZE 4096
|
||||
#endif
|
||||
|
||||
/* Check for minimum required compiler versions. */
|
||||
#if defined(__GNUC__)
|
||||
#if LJ_TARGET_X86
|
||||
#if (__GNUC__ < 3) || ((__GNUC__ == 3) && __GNUC_MINOR__ < 4)
|
||||
#error "Need at least GCC 3.4 or newer"
|
||||
#endif
|
||||
#elif LJ_TARGET_X64
|
||||
#if __GNUC__ < 4
|
||||
#error "Need at least GCC 4.0 or newer"
|
||||
#endif
|
||||
#elif LJ_TARGET_ARM
|
||||
#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 2)
|
||||
#error "Need at least GCC 4.2 or newer"
|
||||
#endif
|
||||
#elif !LJ_TARGET_PS3
|
||||
#if (__GNUC__ < 4) || ((__GNUC__ == 4) && __GNUC_MINOR__ < 3)
|
||||
#error "Need at least GCC 4.3 or newer"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Check target-specific constraints. */
|
||||
#ifndef _BUILDVM_H
|
||||
#if LJ_TARGET_X64
|
||||
#if __USING_SJLJ_EXCEPTIONS__
|
||||
#error "Need a C compiler with native exception handling on x64"
|
||||
#endif
|
||||
#elif LJ_TARGET_ARM
|
||||
#if defined(__ARMEB__)
|
||||
#error "No support for big-endian ARM"
|
||||
#endif
|
||||
#if __ARM_ARCH_6M__ || __ARM_ARCH_7M__ || __ARM_ARCH_7EM__
|
||||
#error "No support for Cortex-M CPUs"
|
||||
#endif
|
||||
#if !(__ARM_EABI__ || LJ_TARGET_IOS)
|
||||
#error "Only ARM EABI or iOS 3.0+ ABI is supported"
|
||||
#endif
|
||||
#elif LJ_TARGET_PPC || LJ_TARGET_PPCSPE
|
||||
#if defined(_SOFT_FLOAT) || defined(_SOFT_DOUBLE)
|
||||
#error "No support for PowerPC CPUs without double-precision FPU"
|
||||
#endif
|
||||
#if defined(_LITTLE_ENDIAN)
|
||||
#error "No support for little-endian PowerPC"
|
||||
#endif
|
||||
#if defined(_LP64)
|
||||
#error "No support for PowerPC 64 bit mode"
|
||||
#endif
|
||||
#elif LJ_TARGET_MIPS
|
||||
#if defined(__mips_soft_float)
|
||||
#error "No support for MIPS CPUs without FPU"
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Enable or disable the dual-number mode for the VM. */
|
||||
#if (LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE && LUAJIT_NUMMODE == 2) || \
|
||||
(LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL && LUAJIT_NUMMODE == 1)
|
||||
#error "No support for this number mode on this architecture"
|
||||
#endif
|
||||
#if LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL || \
|
||||
(LJ_ARCH_NUMMODE == LJ_NUMMODE_DUAL_SINGLE && LUAJIT_NUMMODE != 1) || \
|
||||
(LJ_ARCH_NUMMODE == LJ_NUMMODE_SINGLE_DUAL && LUAJIT_NUMMODE == 2)
|
||||
#define LJ_DUALNUM 1
|
||||
#else
|
||||
#define LJ_DUALNUM 0
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_IOS || LJ_TARGET_CONSOLE
|
||||
/* Runtime code generation is restricted on iOS. Complain to Apple, not me. */
|
||||
/* Ditto for the consoles. Complain to Sony or MS, not me. */
|
||||
#ifndef LUAJIT_ENABLE_JIT
|
||||
#define LJ_OS_NOJIT 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Disable or enable the JIT compiler. */
|
||||
#if defined(LUAJIT_DISABLE_JIT) || defined(LJ_ARCH_NOJIT) || defined(LJ_OS_NOJIT)
|
||||
#define LJ_HASJIT 0
|
||||
#else
|
||||
#define LJ_HASJIT 1
|
||||
#endif
|
||||
|
||||
/* Disable or enable the FFI extension. */
|
||||
#if defined(LUAJIT_DISABLE_FFI) || defined(LJ_ARCH_NOFFI)
|
||||
#define LJ_HASFFI 0
|
||||
#else
|
||||
#define LJ_HASFFI 1
|
||||
#endif
|
||||
|
||||
#ifndef LJ_ARCH_HASFPU
|
||||
#define LJ_ARCH_HASFPU 1
|
||||
#endif
|
||||
#ifndef LJ_ABI_SOFTFP
|
||||
#define LJ_ABI_SOFTFP 0
|
||||
#endif
|
||||
#define LJ_SOFTFP (!LJ_ARCH_HASFPU)
|
||||
|
||||
#if LJ_ARCH_ENDIAN == LUAJIT_BE
|
||||
#define LJ_LE 0
|
||||
#define LJ_BE 1
|
||||
#define LJ_ENDIAN_SELECT(le, be) be
|
||||
#define LJ_ENDIAN_LOHI(lo, hi) hi lo
|
||||
#else
|
||||
#define LJ_LE 1
|
||||
#define LJ_BE 0
|
||||
#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
|
||||
#else
|
||||
#define LJ_32 0
|
||||
#define LJ_64 1
|
||||
#endif
|
||||
|
||||
#ifndef LJ_TARGET_UNALIGNED
|
||||
#define LJ_TARGET_UNALIGNED 0
|
||||
#endif
|
||||
|
||||
/* Various workarounds for embedded operating systems. */
|
||||
#if (defined(__ANDROID__) && !defined(LJ_TARGET_X86ORX64)) || defined(__symbian__) || LJ_TARGET_XBOX360
|
||||
#define LUAJIT_NO_LOG2
|
||||
#endif
|
||||
#if defined(__symbian__)
|
||||
#define LUAJIT_NO_EXP2
|
||||
#endif
|
||||
|
||||
#if defined(LUAJIT_NO_UNWIND) || defined(__symbian__) || LJ_TARGET_IOS || LJ_TARGET_PS3
|
||||
#define LJ_NO_UNWIND 1
|
||||
#endif
|
||||
|
||||
/* Compatibility with Lua 5.1 vs. 5.2. */
|
||||
#ifdef LUAJIT_ENABLE_LUA52COMPAT
|
||||
#define LJ_52 1
|
||||
#else
|
||||
#define LJ_52 0
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+1912
File diff suppressed because it is too large
Load Diff
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
** IR assembler (SSA IR -> machine code).
|
||||
** Copyright (C) 2005-2013 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, GCtrace *T);
|
||||
LJ_FUNC void lj_asm_patchexit(jit_State *J, GCtrace *T, ExitNo exitno,
|
||||
MCode *target);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+2358
File diff suppressed because it is too large
Load Diff
Vendored
+1976
File diff suppressed because it is too large
Load Diff
Vendored
+2166
File diff suppressed because it is too large
Load Diff
Vendored
+2793
File diff suppressed because it is too large
Load Diff
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
** Bytecode instruction modes.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_bc_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_bc.h"
|
||||
|
||||
/* Bytecode offsets and bytecode instruction modes. */
|
||||
#include "lj_bcdef.h"
|
||||
|
||||
Vendored
+261
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
** Bytecode instruction format.
|
||||
** Copyright (C) 2005-2013 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) ((BCOp)((i)&0xff))
|
||||
#define bc_a(i) ((BCReg)(((i)>>8)&0xff))
|
||||
#define bc_b(i) ((BCReg)((i)>>24))
|
||||
#define bc_c(i) ((BCReg)(((i)>>16)&0xff))
|
||||
#define bc_d(i) ((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)] = (uint8_t)(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)] = (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) \
|
||||
(((BCIns)(o))|((BCIns)(a)<<8)|((BCIns)(b)<<24)|((BCIns)(c)<<16))
|
||||
#define BCINS_AD(o, a, d) \
|
||||
(((BCIns)(o))|((BCIns)(a)<<8)|((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, ___) \
|
||||
_(KCDATA, dst, ___, cdata, ___) \
|
||||
_(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) \
|
||||
_(ITERN, base, lit, lit, call) \
|
||||
_(VARG, base, lit, lit, ___) \
|
||||
_(ISNEXT, base, ___, jump, ___) \
|
||||
\
|
||||
/* 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, ___) \
|
||||
\
|
||||
/* Function headers. I/J = interp/JIT, F/V/C = fixarg/vararg/C func. */ \
|
||||
_(FUNCF, rbase, ___, ___, ___) \
|
||||
_(IFUNCF, rbase, ___, ___, ___) \
|
||||
_(JFUNCF, rbase, ___, lit, ___) \
|
||||
_(FUNCV, rbase, ___, ___, ___) \
|
||||
_(IFUNCV, rbase, ___, ___, ___) \
|
||||
_(JFUNCV, rbase, ___, lit, ___) \
|
||||
_(FUNCC, rbase, ___, ___, ___) \
|
||||
_(FUNCCW, rbase, ___, ___, ___)
|
||||
|
||||
/* 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);
|
||||
LJ_STATIC_ASSERT((int)BC_FUNCF + 1 == (int)BC_IFUNCF);
|
||||
LJ_STATIC_ASSERT((int)BC_FUNCF + 2 == (int)BC_JFUNCF);
|
||||
LJ_STATIC_ASSERT((int)BC_FUNCV + 1 == (int)BC_IFUNCV);
|
||||
LJ_STATIC_ASSERT((int)BC_FUNCV + 2 == (int)BC_JFUNCV);
|
||||
|
||||
/* This solves a circular dependency problem, change as needed. */
|
||||
#define FF_next_N 4
|
||||
|
||||
/* 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, BCMcdata,
|
||||
BCM_max
|
||||
} BCMode;
|
||||
#define BCM___ BCMnone
|
||||
|
||||
#define bcmode_a(op) ((BCMode)(lj_bc_mode[op] & 7))
|
||||
#define bcmode_b(op) ((BCMode)((lj_bc_mode[op]>>3) & 15))
|
||||
#define bcmode_c(op) ((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) ((MMS)(lj_bc_mode[op]>>11))
|
||||
|
||||
#define BCMODE(name, ma, mb, mc, mm) \
|
||||
(BCM##ma|(BCM##mb<<3)|(BCM##mc<<7)|(MM_##mm<<11)),
|
||||
#define BCMODE_FF 0
|
||||
|
||||
static LJ_AINLINE int bc_isret(BCOp op)
|
||||
{
|
||||
return (op == BC_RETM || op == BC_RET || op == BC_RET0 || op == BC_RET1);
|
||||
}
|
||||
|
||||
LJ_DATA const uint16_t lj_bc_mode[];
|
||||
LJ_DATA const uint16_t lj_bc_ofs[];
|
||||
|
||||
#endif
|
||||
Vendored
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
** Bytecode dump definitions.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_BCDUMP_H
|
||||
#define _LJ_BCDUMP_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_lex.h"
|
||||
|
||||
/* -- Bytecode dump format ------------------------------------------------ */
|
||||
|
||||
/*
|
||||
** dump = header proto+ 0U
|
||||
** header = ESC 'L' 'J' versionB flagsU [namelenU nameB*]
|
||||
** proto = lengthU pdata
|
||||
** pdata = phead bcinsW* uvdataH* kgc* knum* [debugB*]
|
||||
** phead = flagsB numparamsB framesizeB numuvB numkgcU numknU numbcU
|
||||
** [debuglenU [firstlineU numlineU]]
|
||||
** kgc = kgctypeU { ktab | (loU hiU) | (rloU rhiU iloU ihiU) | strB* }
|
||||
** knum = intU0 | (loU1 hiU)
|
||||
** ktab = narrayU nhashU karray* khash*
|
||||
** karray = ktabk
|
||||
** khash = ktabk ktabk
|
||||
** ktabk = ktabtypeU { intU | (loU hiU) | strB* }
|
||||
**
|
||||
** B = 8 bit, H = 16 bit, W = 32 bit, U = ULEB128 of W, U0/U1 = ULEB128 of W+1
|
||||
*/
|
||||
|
||||
/* Bytecode dump header. */
|
||||
#define BCDUMP_HEAD1 0x1b
|
||||
#define BCDUMP_HEAD2 0x4c
|
||||
#define BCDUMP_HEAD3 0x4a
|
||||
|
||||
/* If you perform *any* kind of private modifications to the bytecode itself
|
||||
** or to the dump format, you *must* set BCDUMP_VERSION to 0x80 or higher.
|
||||
*/
|
||||
#define BCDUMP_VERSION 1
|
||||
|
||||
/* Compatibility flags. */
|
||||
#define BCDUMP_F_BE 0x01
|
||||
#define BCDUMP_F_STRIP 0x02
|
||||
#define BCDUMP_F_FFI 0x04
|
||||
|
||||
#define BCDUMP_F_KNOWN (BCDUMP_F_FFI*2-1)
|
||||
|
||||
/* Type codes for the GC constants of a prototype. Plus length for strings. */
|
||||
enum {
|
||||
BCDUMP_KGC_CHILD, BCDUMP_KGC_TAB, BCDUMP_KGC_I64, BCDUMP_KGC_U64,
|
||||
BCDUMP_KGC_COMPLEX, BCDUMP_KGC_STR
|
||||
};
|
||||
|
||||
/* Type codes for the keys/values of a constant table. */
|
||||
enum {
|
||||
BCDUMP_KTAB_NIL, BCDUMP_KTAB_FALSE, BCDUMP_KTAB_TRUE,
|
||||
BCDUMP_KTAB_INT, BCDUMP_KTAB_NUM, BCDUMP_KTAB_STR
|
||||
};
|
||||
|
||||
/* -- Bytecode reader/writer ---------------------------------------------- */
|
||||
|
||||
LJ_FUNC int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer,
|
||||
void *data, int strip);
|
||||
LJ_FUNC GCproto *lj_bcread(LexState *ls);
|
||||
|
||||
#endif
|
||||
Vendored
+476
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
** Bytecode reader.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_bcread_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_bc.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lualib.h"
|
||||
#endif
|
||||
#include "lj_lex.h"
|
||||
#include "lj_bcdump.h"
|
||||
#include "lj_state.h"
|
||||
|
||||
/* Reuse some lexer fields for our own purposes. */
|
||||
#define bcread_flags(ls) ls->level
|
||||
#define bcread_swap(ls) \
|
||||
((bcread_flags(ls) & BCDUMP_F_BE) != LJ_BE*BCDUMP_F_BE)
|
||||
#define bcread_oldtop(L, ls) restorestack(L, ls->lastline)
|
||||
#define bcread_savetop(L, ls, top) \
|
||||
ls->lastline = (BCLine)savestack(L, (top))
|
||||
|
||||
/* -- Input buffer handling ----------------------------------------------- */
|
||||
|
||||
/* Throw reader error. */
|
||||
static LJ_NOINLINE void bcread_error(LexState *ls, ErrMsg em)
|
||||
{
|
||||
lua_State *L = ls->L;
|
||||
const char *name = ls->chunkarg;
|
||||
if (*name == BCDUMP_HEAD1) name = "(binary)";
|
||||
else if (*name == '@' || *name == '=') name++;
|
||||
lj_str_pushf(L, "%s: %s", name, err2msg(em));
|
||||
lj_err_throw(L, LUA_ERRSYNTAX);
|
||||
}
|
||||
|
||||
/* Resize input buffer. */
|
||||
static void bcread_resize(LexState *ls, MSize len)
|
||||
{
|
||||
if (ls->sb.sz < len) {
|
||||
MSize sz = ls->sb.sz * 2;
|
||||
while (len > sz) sz = sz * 2;
|
||||
lj_str_resizebuf(ls->L, &ls->sb, sz);
|
||||
/* Caveat: this may change ls->sb.buf which may affect ls->p. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Refill buffer if needed. */
|
||||
static LJ_NOINLINE void bcread_fill(LexState *ls, MSize len, int need)
|
||||
{
|
||||
lua_assert(len != 0);
|
||||
if (len > LJ_MAX_MEM || ls->current < 0)
|
||||
bcread_error(ls, LJ_ERR_BCBAD);
|
||||
do {
|
||||
const char *buf;
|
||||
size_t size;
|
||||
if (ls->n) { /* Copy remainder to buffer. */
|
||||
if (ls->sb.n) { /* Move down in buffer. */
|
||||
lua_assert(ls->p + ls->n == ls->sb.buf + ls->sb.n);
|
||||
if (ls->n != ls->sb.n)
|
||||
memmove(ls->sb.buf, ls->p, ls->n);
|
||||
} else { /* Copy from buffer provided by reader. */
|
||||
bcread_resize(ls, len);
|
||||
memcpy(ls->sb.buf, ls->p, ls->n);
|
||||
}
|
||||
ls->p = ls->sb.buf;
|
||||
}
|
||||
ls->sb.n = ls->n;
|
||||
buf = ls->rfunc(ls->L, ls->rdata, &size); /* Get more data from reader. */
|
||||
if (buf == NULL || size == 0) { /* EOF? */
|
||||
if (need) bcread_error(ls, LJ_ERR_BCBAD);
|
||||
ls->current = -1; /* Only bad if we get called again. */
|
||||
break;
|
||||
}
|
||||
if (ls->sb.n) { /* Append to buffer. */
|
||||
MSize n = ls->sb.n + (MSize)size;
|
||||
bcread_resize(ls, n < len ? len : n);
|
||||
memcpy(ls->sb.buf + ls->sb.n, buf, size);
|
||||
ls->n = ls->sb.n = n;
|
||||
ls->p = ls->sb.buf;
|
||||
} else { /* Return buffer provided by reader. */
|
||||
ls->n = (MSize)size;
|
||||
ls->p = buf;
|
||||
}
|
||||
} while (ls->n < len);
|
||||
}
|
||||
|
||||
/* Need a certain number of bytes. */
|
||||
static LJ_AINLINE void bcread_need(LexState *ls, MSize len)
|
||||
{
|
||||
if (LJ_UNLIKELY(ls->n < len))
|
||||
bcread_fill(ls, len, 1);
|
||||
}
|
||||
|
||||
/* Want to read up to a certain number of bytes, but may need less. */
|
||||
static LJ_AINLINE void bcread_want(LexState *ls, MSize len)
|
||||
{
|
||||
if (LJ_UNLIKELY(ls->n < len))
|
||||
bcread_fill(ls, len, 0);
|
||||
}
|
||||
|
||||
#define bcread_dec(ls) check_exp(ls->n > 0, ls->n--)
|
||||
#define bcread_consume(ls, len) check_exp(ls->n >= (len), ls->n -= (len))
|
||||
|
||||
/* Return memory block from buffer. */
|
||||
static uint8_t *bcread_mem(LexState *ls, MSize len)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)ls->p;
|
||||
bcread_consume(ls, len);
|
||||
ls->p = (char *)p + len;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Copy memory block from buffer. */
|
||||
static void bcread_block(LexState *ls, void *q, MSize len)
|
||||
{
|
||||
memcpy(q, bcread_mem(ls, len), len);
|
||||
}
|
||||
|
||||
/* Read byte from buffer. */
|
||||
static LJ_AINLINE uint32_t bcread_byte(LexState *ls)
|
||||
{
|
||||
bcread_dec(ls);
|
||||
return (uint32_t)(uint8_t)*ls->p++;
|
||||
}
|
||||
|
||||
/* Read ULEB128 value from buffer. */
|
||||
static uint32_t bcread_uleb128(LexState *ls)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)ls->p;
|
||||
uint32_t v = *p++;
|
||||
if (LJ_UNLIKELY(v >= 0x80)) {
|
||||
int sh = 0;
|
||||
v &= 0x7f;
|
||||
do {
|
||||
v |= ((*p & 0x7f) << (sh += 7));
|
||||
bcread_dec(ls);
|
||||
} while (*p++ >= 0x80);
|
||||
}
|
||||
bcread_dec(ls);
|
||||
ls->p = (char *)p;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* Read top 32 bits of 33 bit ULEB128 value from buffer. */
|
||||
static uint32_t bcread_uleb128_33(LexState *ls)
|
||||
{
|
||||
const uint8_t *p = (const uint8_t *)ls->p;
|
||||
uint32_t v = (*p++ >> 1);
|
||||
if (LJ_UNLIKELY(v >= 0x40)) {
|
||||
int sh = -1;
|
||||
v &= 0x3f;
|
||||
do {
|
||||
v |= ((*p & 0x7f) << (sh += 7));
|
||||
bcread_dec(ls);
|
||||
} while (*p++ >= 0x80);
|
||||
}
|
||||
bcread_dec(ls);
|
||||
ls->p = (char *)p;
|
||||
return v;
|
||||
}
|
||||
|
||||
/* -- Bytecode reader ----------------------------------------------------- */
|
||||
|
||||
/* Read debug info of a prototype. */
|
||||
static void bcread_dbg(LexState *ls, GCproto *pt, MSize sizedbg)
|
||||
{
|
||||
void *lineinfo = (void *)proto_lineinfo(pt);
|
||||
bcread_block(ls, lineinfo, sizedbg);
|
||||
/* Swap lineinfo if the endianess differs. */
|
||||
if (bcread_swap(ls) && pt->numline >= 256) {
|
||||
MSize i, n = pt->sizebc-1;
|
||||
if (pt->numline < 65536) {
|
||||
uint16_t *p = (uint16_t *)lineinfo;
|
||||
for (i = 0; i < n; i++) p[i] = (uint16_t)((p[i] >> 8)|(p[i] << 8));
|
||||
} else {
|
||||
uint32_t *p = (uint32_t *)lineinfo;
|
||||
for (i = 0; i < n; i++) p[i] = lj_bswap(p[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find pointer to varinfo. */
|
||||
static const void *bcread_varinfo(GCproto *pt)
|
||||
{
|
||||
const uint8_t *p = proto_uvinfo(pt);
|
||||
MSize n = pt->sizeuv;
|
||||
if (n) while (*p++ || --n) ;
|
||||
return p;
|
||||
}
|
||||
|
||||
/* Read a single constant key/value of a template table. */
|
||||
static void bcread_ktabk(LexState *ls, TValue *o)
|
||||
{
|
||||
MSize tp = bcread_uleb128(ls);
|
||||
if (tp >= BCDUMP_KTAB_STR) {
|
||||
MSize len = tp - BCDUMP_KTAB_STR;
|
||||
const char *p = (const char *)bcread_mem(ls, len);
|
||||
setstrV(ls->L, o, lj_str_new(ls->L, p, len));
|
||||
} else if (tp == BCDUMP_KTAB_INT) {
|
||||
setintV(o, (int32_t)bcread_uleb128(ls));
|
||||
} else if (tp == BCDUMP_KTAB_NUM) {
|
||||
o->u32.lo = bcread_uleb128(ls);
|
||||
o->u32.hi = bcread_uleb128(ls);
|
||||
} else {
|
||||
lua_assert(tp <= BCDUMP_KTAB_TRUE);
|
||||
setitype(o, ~tp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a template table. */
|
||||
static GCtab *bcread_ktab(LexState *ls)
|
||||
{
|
||||
MSize narray = bcread_uleb128(ls);
|
||||
MSize nhash = bcread_uleb128(ls);
|
||||
GCtab *t = lj_tab_new(ls->L, narray, hsize2hbits(nhash));
|
||||
if (narray) { /* Read array entries. */
|
||||
MSize i;
|
||||
TValue *o = tvref(t->array);
|
||||
for (i = 0; i < narray; i++, o++)
|
||||
bcread_ktabk(ls, o);
|
||||
}
|
||||
if (nhash) { /* Read hash entries. */
|
||||
MSize i;
|
||||
for (i = 0; i < nhash; i++) {
|
||||
TValue key;
|
||||
bcread_ktabk(ls, &key);
|
||||
lua_assert(!tvisnil(&key));
|
||||
bcread_ktabk(ls, lj_tab_set(ls->L, t, &key));
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Read GC constants of a prototype. */
|
||||
static void bcread_kgc(LexState *ls, GCproto *pt, MSize sizekgc)
|
||||
{
|
||||
MSize i;
|
||||
GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc;
|
||||
for (i = 0; i < sizekgc; i++, kr++) {
|
||||
MSize tp = bcread_uleb128(ls);
|
||||
if (tp >= BCDUMP_KGC_STR) {
|
||||
MSize len = tp - BCDUMP_KGC_STR;
|
||||
const char *p = (const char *)bcread_mem(ls, len);
|
||||
setgcref(*kr, obj2gco(lj_str_new(ls->L, p, len)));
|
||||
} else if (tp == BCDUMP_KGC_TAB) {
|
||||
setgcref(*kr, obj2gco(bcread_ktab(ls)));
|
||||
#if LJ_HASFFI
|
||||
} else if (tp != BCDUMP_KGC_CHILD) {
|
||||
CTypeID id = tp == BCDUMP_KGC_COMPLEX ? CTID_COMPLEX_DOUBLE :
|
||||
tp == BCDUMP_KGC_I64 ? CTID_INT64 : CTID_UINT64;
|
||||
CTSize sz = tp == BCDUMP_KGC_COMPLEX ? 16 : 8;
|
||||
GCcdata *cd = lj_cdata_new_(ls->L, id, sz);
|
||||
TValue *p = (TValue *)cdataptr(cd);
|
||||
setgcref(*kr, obj2gco(cd));
|
||||
p[0].u32.lo = bcread_uleb128(ls);
|
||||
p[0].u32.hi = bcread_uleb128(ls);
|
||||
if (tp == BCDUMP_KGC_COMPLEX) {
|
||||
p[1].u32.lo = bcread_uleb128(ls);
|
||||
p[1].u32.hi = bcread_uleb128(ls);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
lua_State *L = ls->L;
|
||||
lua_assert(tp == BCDUMP_KGC_CHILD);
|
||||
if (L->top <= bcread_oldtop(L, ls)) /* Stack underflow? */
|
||||
bcread_error(ls, LJ_ERR_BCBAD);
|
||||
L->top--;
|
||||
setgcref(*kr, obj2gco(protoV(L->top)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read number constants of a prototype. */
|
||||
static void bcread_knum(LexState *ls, GCproto *pt, MSize sizekn)
|
||||
{
|
||||
MSize i;
|
||||
TValue *o = mref(pt->k, TValue);
|
||||
for (i = 0; i < sizekn; i++, o++) {
|
||||
int isnum = (ls->p[0] & 1);
|
||||
uint32_t lo = bcread_uleb128_33(ls);
|
||||
if (isnum) {
|
||||
o->u32.lo = lo;
|
||||
o->u32.hi = bcread_uleb128(ls);
|
||||
} else {
|
||||
setintV(o, lo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read bytecode instructions. */
|
||||
static void bcread_bytecode(LexState *ls, GCproto *pt, MSize sizebc)
|
||||
{
|
||||
BCIns *bc = proto_bc(pt);
|
||||
bc[0] = BCINS_AD((pt->flags & PROTO_VARARG) ? BC_FUNCV : BC_FUNCF,
|
||||
pt->framesize, 0);
|
||||
bcread_block(ls, bc+1, (sizebc-1)*(MSize)sizeof(BCIns));
|
||||
/* Swap bytecode instructions if the endianess differs. */
|
||||
if (bcread_swap(ls)) {
|
||||
MSize i;
|
||||
for (i = 1; i < sizebc; i++) bc[i] = lj_bswap(bc[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read upvalue refs. */
|
||||
static void bcread_uv(LexState *ls, GCproto *pt, MSize sizeuv)
|
||||
{
|
||||
if (sizeuv) {
|
||||
uint16_t *uv = proto_uv(pt);
|
||||
bcread_block(ls, uv, sizeuv*2);
|
||||
/* Swap upvalue refs if the endianess differs. */
|
||||
if (bcread_swap(ls)) {
|
||||
MSize i;
|
||||
for (i = 0; i < sizeuv; i++)
|
||||
uv[i] = (uint16_t)((uv[i] >> 8)|(uv[i] << 8));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read a prototype. */
|
||||
static GCproto *bcread_proto(LexState *ls)
|
||||
{
|
||||
GCproto *pt;
|
||||
MSize framesize, numparams, flags, sizeuv, sizekgc, sizekn, sizebc, sizept;
|
||||
MSize ofsk, ofsuv, ofsdbg;
|
||||
MSize sizedbg = 0;
|
||||
BCLine firstline = 0, numline = 0;
|
||||
MSize len, startn;
|
||||
|
||||
/* Read length. */
|
||||
if (ls->n > 0 && ls->p[0] == 0) { /* Shortcut EOF. */
|
||||
ls->n--; ls->p++;
|
||||
return NULL;
|
||||
}
|
||||
bcread_want(ls, 5);
|
||||
len = bcread_uleb128(ls);
|
||||
if (!len) return NULL; /* EOF */
|
||||
bcread_need(ls, len);
|
||||
startn = ls->n;
|
||||
|
||||
/* Read prototype header. */
|
||||
flags = bcread_byte(ls);
|
||||
numparams = bcread_byte(ls);
|
||||
framesize = bcread_byte(ls);
|
||||
sizeuv = bcread_byte(ls);
|
||||
sizekgc = bcread_uleb128(ls);
|
||||
sizekn = bcread_uleb128(ls);
|
||||
sizebc = bcread_uleb128(ls) + 1;
|
||||
if (!(bcread_flags(ls) & BCDUMP_F_STRIP)) {
|
||||
sizedbg = bcread_uleb128(ls);
|
||||
if (sizedbg) {
|
||||
firstline = bcread_uleb128(ls);
|
||||
numline = bcread_uleb128(ls);
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate total size of prototype including all colocated arrays. */
|
||||
sizept = (MSize)sizeof(GCproto) +
|
||||
sizebc*(MSize)sizeof(BCIns) +
|
||||
sizekgc*(MSize)sizeof(GCRef);
|
||||
sizept = (sizept + (MSize)sizeof(TValue)-1) & ~((MSize)sizeof(TValue)-1);
|
||||
ofsk = sizept; sizept += sizekn*(MSize)sizeof(TValue);
|
||||
ofsuv = sizept; sizept += ((sizeuv+1)&~1)*2;
|
||||
ofsdbg = sizept; sizept += sizedbg;
|
||||
|
||||
/* Allocate prototype object and initialize its fields. */
|
||||
pt = (GCproto *)lj_mem_newgco(ls->L, (MSize)sizept);
|
||||
pt->gct = ~LJ_TPROTO;
|
||||
pt->numparams = (uint8_t)numparams;
|
||||
pt->framesize = (uint8_t)framesize;
|
||||
pt->sizebc = sizebc;
|
||||
setmref(pt->k, (char *)pt + ofsk);
|
||||
setmref(pt->uv, (char *)pt + ofsuv);
|
||||
pt->sizekgc = 0; /* Set to zero until fully initialized. */
|
||||
pt->sizekn = sizekn;
|
||||
pt->sizept = sizept;
|
||||
pt->sizeuv = (uint8_t)sizeuv;
|
||||
pt->flags = (uint8_t)flags;
|
||||
pt->trace = 0;
|
||||
setgcref(pt->chunkname, obj2gco(ls->chunkname));
|
||||
|
||||
/* Close potentially uninitialized gap between bc and kgc. */
|
||||
*(uint32_t *)((char *)pt + ofsk - sizeof(GCRef)*(sizekgc+1)) = 0;
|
||||
|
||||
/* Read bytecode instructions and upvalue refs. */
|
||||
bcread_bytecode(ls, pt, sizebc);
|
||||
bcread_uv(ls, pt, sizeuv);
|
||||
|
||||
/* Read constants. */
|
||||
bcread_kgc(ls, pt, sizekgc);
|
||||
pt->sizekgc = sizekgc;
|
||||
bcread_knum(ls, pt, sizekn);
|
||||
|
||||
/* Read and initialize debug info. */
|
||||
pt->firstline = firstline;
|
||||
pt->numline = numline;
|
||||
if (sizedbg) {
|
||||
MSize sizeli = (sizebc-1) << (numline < 256 ? 0 : numline < 65536 ? 1 : 2);
|
||||
setmref(pt->lineinfo, (char *)pt + ofsdbg);
|
||||
setmref(pt->uvinfo, (char *)pt + ofsdbg + sizeli);
|
||||
bcread_dbg(ls, pt, sizedbg);
|
||||
setmref(pt->varinfo, bcread_varinfo(pt));
|
||||
} else {
|
||||
setmref(pt->lineinfo, NULL);
|
||||
setmref(pt->uvinfo, NULL);
|
||||
setmref(pt->varinfo, NULL);
|
||||
}
|
||||
|
||||
if (len != startn - ls->n)
|
||||
bcread_error(ls, LJ_ERR_BCBAD);
|
||||
return pt;
|
||||
}
|
||||
|
||||
/* Read and check header of bytecode dump. */
|
||||
static int bcread_header(LexState *ls)
|
||||
{
|
||||
uint32_t flags;
|
||||
bcread_want(ls, 3+5+5);
|
||||
if (bcread_byte(ls) != BCDUMP_HEAD2 ||
|
||||
bcread_byte(ls) != BCDUMP_HEAD3 ||
|
||||
bcread_byte(ls) != BCDUMP_VERSION) return 0;
|
||||
bcread_flags(ls) = flags = bcread_uleb128(ls);
|
||||
if ((flags & ~(BCDUMP_F_KNOWN)) != 0) return 0;
|
||||
if ((flags & BCDUMP_F_FFI)) {
|
||||
#if LJ_HASFFI
|
||||
lua_State *L = ls->L;
|
||||
if (!ctype_ctsG(G(L))) {
|
||||
ptrdiff_t oldtop = savestack(L, L->top);
|
||||
luaopen_ffi(L); /* Load FFI library on-demand. */
|
||||
L->top = restorestack(L, oldtop);
|
||||
}
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
if ((flags & BCDUMP_F_STRIP)) {
|
||||
ls->chunkname = lj_str_newz(ls->L, ls->chunkarg);
|
||||
} else {
|
||||
MSize len = bcread_uleb128(ls);
|
||||
bcread_need(ls, len);
|
||||
ls->chunkname = lj_str_new(ls->L, (const char *)bcread_mem(ls, len), len);
|
||||
}
|
||||
return 1; /* Ok. */
|
||||
}
|
||||
|
||||
/* Read a bytecode dump. */
|
||||
GCproto *lj_bcread(LexState *ls)
|
||||
{
|
||||
lua_State *L = ls->L;
|
||||
lua_assert(ls->current == BCDUMP_HEAD1);
|
||||
bcread_savetop(L, ls, L->top);
|
||||
lj_str_resetbuf(&ls->sb);
|
||||
/* Check for a valid bytecode dump header. */
|
||||
if (!bcread_header(ls))
|
||||
bcread_error(ls, LJ_ERR_BCFMT);
|
||||
for (;;) { /* Process all prototypes in the bytecode dump. */
|
||||
GCproto *pt = bcread_proto(ls);
|
||||
if (!pt) break;
|
||||
setprotoV(L, L->top, pt);
|
||||
incr_top(L);
|
||||
}
|
||||
if ((int32_t)ls->n > 0 || L->top-1 != bcread_oldtop(L, ls))
|
||||
bcread_error(ls, LJ_ERR_BCBAD);
|
||||
/* Pop off last prototype. */
|
||||
L->top--;
|
||||
return protoV(L->top);
|
||||
}
|
||||
|
||||
Vendored
+396
@@ -0,0 +1,396 @@
|
||||
/*
|
||||
** Bytecode writer.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#define lj_bcwrite_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_bc.h"
|
||||
#if LJ_HASFFI
|
||||
#include "lj_ctype.h"
|
||||
#endif
|
||||
#if LJ_HASJIT
|
||||
#include "lj_dispatch.h"
|
||||
#include "lj_jit.h"
|
||||
#endif
|
||||
#include "lj_bcdump.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/* Context for bytecode writer. */
|
||||
typedef struct BCWriteCtx {
|
||||
SBuf sb; /* Output buffer. */
|
||||
lua_State *L; /* Lua state. */
|
||||
GCproto *pt; /* Root prototype. */
|
||||
lua_Writer wfunc; /* Writer callback. */
|
||||
void *wdata; /* Writer callback data. */
|
||||
int strip; /* Strip debug info. */
|
||||
int status; /* Status from writer callback. */
|
||||
} BCWriteCtx;
|
||||
|
||||
/* -- Output buffer handling ---------------------------------------------- */
|
||||
|
||||
/* Resize buffer if needed. */
|
||||
static LJ_NOINLINE void bcwrite_resize(BCWriteCtx *ctx, MSize len)
|
||||
{
|
||||
MSize sz = ctx->sb.sz * 2;
|
||||
while (ctx->sb.n + len > sz) sz = sz * 2;
|
||||
lj_str_resizebuf(ctx->L, &ctx->sb, sz);
|
||||
}
|
||||
|
||||
/* Need a certain amount of buffer space. */
|
||||
static LJ_AINLINE void bcwrite_need(BCWriteCtx *ctx, MSize len)
|
||||
{
|
||||
if (LJ_UNLIKELY(ctx->sb.n + len > ctx->sb.sz))
|
||||
bcwrite_resize(ctx, len);
|
||||
}
|
||||
|
||||
/* Add memory block to buffer. */
|
||||
static void bcwrite_block(BCWriteCtx *ctx, const void *p, MSize len)
|
||||
{
|
||||
uint8_t *q = (uint8_t *)(ctx->sb.buf + ctx->sb.n);
|
||||
MSize i;
|
||||
ctx->sb.n += len;
|
||||
for (i = 0; i < len; i++) q[i] = ((uint8_t *)p)[i];
|
||||
}
|
||||
|
||||
/* Add byte to buffer. */
|
||||
static LJ_AINLINE void bcwrite_byte(BCWriteCtx *ctx, uint8_t b)
|
||||
{
|
||||
ctx->sb.buf[ctx->sb.n++] = b;
|
||||
}
|
||||
|
||||
/* Add ULEB128 value to buffer. */
|
||||
static void bcwrite_uleb128(BCWriteCtx *ctx, uint32_t v)
|
||||
{
|
||||
MSize n = ctx->sb.n;
|
||||
uint8_t *p = (uint8_t *)ctx->sb.buf;
|
||||
for (; v >= 0x80; v >>= 7)
|
||||
p[n++] = (uint8_t)((v & 0x7f) | 0x80);
|
||||
p[n++] = (uint8_t)v;
|
||||
ctx->sb.n = n;
|
||||
}
|
||||
|
||||
/* -- Bytecode writer ----------------------------------------------------- */
|
||||
|
||||
/* Write a single constant key/value of a template table. */
|
||||
static void bcwrite_ktabk(BCWriteCtx *ctx, cTValue *o, int narrow)
|
||||
{
|
||||
bcwrite_need(ctx, 1+10);
|
||||
if (tvisstr(o)) {
|
||||
const GCstr *str = strV(o);
|
||||
MSize len = str->len;
|
||||
bcwrite_need(ctx, 5+len);
|
||||
bcwrite_uleb128(ctx, BCDUMP_KTAB_STR+len);
|
||||
bcwrite_block(ctx, strdata(str), len);
|
||||
} else if (tvisint(o)) {
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_INT);
|
||||
bcwrite_uleb128(ctx, intV(o));
|
||||
} else if (tvisnum(o)) {
|
||||
if (!LJ_DUALNUM && narrow) { /* Narrow number constants to integers. */
|
||||
lua_Number num = numV(o);
|
||||
int32_t k = lj_num2int(num);
|
||||
if (num == (lua_Number)k) { /* -0 is never a constant. */
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_INT);
|
||||
bcwrite_uleb128(ctx, k);
|
||||
return;
|
||||
}
|
||||
}
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_NUM);
|
||||
bcwrite_uleb128(ctx, o->u32.lo);
|
||||
bcwrite_uleb128(ctx, o->u32.hi);
|
||||
} else {
|
||||
lua_assert(tvispri(o));
|
||||
bcwrite_byte(ctx, BCDUMP_KTAB_NIL+~itype(o));
|
||||
}
|
||||
}
|
||||
|
||||
/* Write a template table. */
|
||||
static void bcwrite_ktab(BCWriteCtx *ctx, const GCtab *t)
|
||||
{
|
||||
MSize narray = 0, nhash = 0;
|
||||
if (t->asize > 0) { /* Determine max. length of array part. */
|
||||
ptrdiff_t i;
|
||||
TValue *array = tvref(t->array);
|
||||
for (i = (ptrdiff_t)t->asize-1; i >= 0; i--)
|
||||
if (!tvisnil(&array[i]))
|
||||
break;
|
||||
narray = (MSize)(i+1);
|
||||
}
|
||||
if (t->hmask > 0) { /* Count number of used hash slots. */
|
||||
MSize i, hmask = t->hmask;
|
||||
Node *node = noderef(t->node);
|
||||
for (i = 0; i <= hmask; i++)
|
||||
nhash += !tvisnil(&node[i].val);
|
||||
}
|
||||
/* Write number of array slots and hash slots. */
|
||||
bcwrite_uleb128(ctx, narray);
|
||||
bcwrite_uleb128(ctx, nhash);
|
||||
if (narray) { /* Write array entries (may contain nil). */
|
||||
MSize i;
|
||||
TValue *o = tvref(t->array);
|
||||
for (i = 0; i < narray; i++, o++)
|
||||
bcwrite_ktabk(ctx, o, 1);
|
||||
}
|
||||
if (nhash) { /* Write hash entries. */
|
||||
MSize i = nhash;
|
||||
Node *node = noderef(t->node) + t->hmask;
|
||||
for (;; node--)
|
||||
if (!tvisnil(&node->val)) {
|
||||
bcwrite_ktabk(ctx, &node->key, 0);
|
||||
bcwrite_ktabk(ctx, &node->val, 1);
|
||||
if (--i == 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write GC constants of a prototype. */
|
||||
static void bcwrite_kgc(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize i, sizekgc = pt->sizekgc;
|
||||
GCRef *kr = mref(pt->k, GCRef) - (ptrdiff_t)sizekgc;
|
||||
for (i = 0; i < sizekgc; i++, kr++) {
|
||||
GCobj *o = gcref(*kr);
|
||||
MSize tp, need = 1;
|
||||
/* Determine constant type and needed size. */
|
||||
if (o->gch.gct == ~LJ_TSTR) {
|
||||
tp = BCDUMP_KGC_STR + gco2str(o)->len;
|
||||
need = 5+gco2str(o)->len;
|
||||
} else if (o->gch.gct == ~LJ_TPROTO) {
|
||||
lua_assert((pt->flags & PROTO_CHILD));
|
||||
tp = BCDUMP_KGC_CHILD;
|
||||
#if LJ_HASFFI
|
||||
} else if (o->gch.gct == ~LJ_TCDATA) {
|
||||
CTypeID id = gco2cd(o)->ctypeid;
|
||||
need = 1+4*5;
|
||||
if (id == CTID_INT64) {
|
||||
tp = BCDUMP_KGC_I64;
|
||||
} else if (id == CTID_UINT64) {
|
||||
tp = BCDUMP_KGC_U64;
|
||||
} else {
|
||||
lua_assert(id == CTID_COMPLEX_DOUBLE);
|
||||
tp = BCDUMP_KGC_COMPLEX;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
lua_assert(o->gch.gct == ~LJ_TTAB);
|
||||
tp = BCDUMP_KGC_TAB;
|
||||
need = 1+2*5;
|
||||
}
|
||||
/* Write constant type. */
|
||||
bcwrite_need(ctx, need);
|
||||
bcwrite_uleb128(ctx, tp);
|
||||
/* Write constant data (if any). */
|
||||
if (tp >= BCDUMP_KGC_STR) {
|
||||
bcwrite_block(ctx, strdata(gco2str(o)), gco2str(o)->len);
|
||||
} else if (tp == BCDUMP_KGC_TAB) {
|
||||
bcwrite_ktab(ctx, gco2tab(o));
|
||||
#if LJ_HASFFI
|
||||
} else if (tp != BCDUMP_KGC_CHILD) {
|
||||
cTValue *p = (TValue *)cdataptr(gco2cd(o));
|
||||
bcwrite_uleb128(ctx, p[0].u32.lo);
|
||||
bcwrite_uleb128(ctx, p[0].u32.hi);
|
||||
if (tp == BCDUMP_KGC_COMPLEX) {
|
||||
bcwrite_uleb128(ctx, p[1].u32.lo);
|
||||
bcwrite_uleb128(ctx, p[1].u32.hi);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write number constants of a prototype. */
|
||||
static void bcwrite_knum(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize i, sizekn = pt->sizekn;
|
||||
cTValue *o = mref(pt->k, TValue);
|
||||
bcwrite_need(ctx, 10*sizekn);
|
||||
for (i = 0; i < sizekn; i++, o++) {
|
||||
int32_t k;
|
||||
if (tvisint(o)) {
|
||||
k = intV(o);
|
||||
goto save_int;
|
||||
} else {
|
||||
/* Write a 33 bit ULEB128 for the int (lsb=0) or loword (lsb=1). */
|
||||
if (!LJ_DUALNUM) { /* Narrow number constants to integers. */
|
||||
lua_Number num = numV(o);
|
||||
k = lj_num2int(num);
|
||||
if (num == (lua_Number)k) { /* -0 is never a constant. */
|
||||
save_int:
|
||||
bcwrite_uleb128(ctx, 2*(uint32_t)k | ((uint32_t)k & 0x80000000u));
|
||||
if (k < 0) {
|
||||
char *p = &ctx->sb.buf[ctx->sb.n-1];
|
||||
*p = (*p & 7) | ((k>>27) & 0x18);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bcwrite_uleb128(ctx, 1+(2*o->u32.lo | (o->u32.lo & 0x80000000u)));
|
||||
if (o->u32.lo >= 0x80000000u) {
|
||||
char *p = &ctx->sb.buf[ctx->sb.n-1];
|
||||
*p = (*p & 7) | ((o->u32.lo>>27) & 0x18);
|
||||
}
|
||||
bcwrite_uleb128(ctx, o->u32.hi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Write bytecode instructions. */
|
||||
static void bcwrite_bytecode(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize nbc = pt->sizebc-1; /* Omit the [JI]FUNC* header. */
|
||||
#if LJ_HASJIT
|
||||
uint8_t *p = (uint8_t *)&ctx->sb.buf[ctx->sb.n];
|
||||
#endif
|
||||
bcwrite_block(ctx, proto_bc(pt)+1, nbc*(MSize)sizeof(BCIns));
|
||||
#if LJ_HASJIT
|
||||
/* Unpatch modified bytecode containing ILOOP/JLOOP etc. */
|
||||
if ((pt->flags & PROTO_ILOOP) || pt->trace) {
|
||||
jit_State *J = L2J(ctx->L);
|
||||
MSize i;
|
||||
for (i = 0; i < nbc; i++, p += sizeof(BCIns)) {
|
||||
BCOp op = (BCOp)p[LJ_ENDIAN_SELECT(0, 3)];
|
||||
if (op == BC_IFORL || op == BC_IITERL || op == BC_ILOOP ||
|
||||
op == BC_JFORI) {
|
||||
p[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_IFORL+BC_FORL);
|
||||
} else if (op == BC_JFORL || op == BC_JITERL || op == BC_JLOOP) {
|
||||
BCReg rd = p[LJ_ENDIAN_SELECT(2, 1)] + (p[LJ_ENDIAN_SELECT(3, 0)] << 8);
|
||||
BCIns ins = traceref(J, rd)->startins;
|
||||
p[LJ_ENDIAN_SELECT(0, 3)] = (uint8_t)(op-BC_JFORL+BC_FORL);
|
||||
p[LJ_ENDIAN_SELECT(2, 1)] = bc_c(ins);
|
||||
p[LJ_ENDIAN_SELECT(3, 0)] = bc_b(ins);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Write prototype. */
|
||||
static void bcwrite_proto(BCWriteCtx *ctx, GCproto *pt)
|
||||
{
|
||||
MSize sizedbg = 0;
|
||||
|
||||
/* Recursively write children of prototype. */
|
||||
if ((pt->flags & PROTO_CHILD)) {
|
||||
ptrdiff_t i, n = pt->sizekgc;
|
||||
GCRef *kr = mref(pt->k, GCRef) - 1;
|
||||
for (i = 0; i < n; i++, kr--) {
|
||||
GCobj *o = gcref(*kr);
|
||||
if (o->gch.gct == ~LJ_TPROTO)
|
||||
bcwrite_proto(ctx, gco2pt(o));
|
||||
}
|
||||
}
|
||||
|
||||
/* Start writing the prototype info to a buffer. */
|
||||
lj_str_resetbuf(&ctx->sb);
|
||||
ctx->sb.n = 5; /* Leave room for final size. */
|
||||
bcwrite_need(ctx, 4+6*5+(pt->sizebc-1)*(MSize)sizeof(BCIns)+pt->sizeuv*2);
|
||||
|
||||
/* Write prototype header. */
|
||||
bcwrite_byte(ctx, (pt->flags & (PROTO_CHILD|PROTO_VARARG|PROTO_FFI)));
|
||||
bcwrite_byte(ctx, pt->numparams);
|
||||
bcwrite_byte(ctx, pt->framesize);
|
||||
bcwrite_byte(ctx, pt->sizeuv);
|
||||
bcwrite_uleb128(ctx, pt->sizekgc);
|
||||
bcwrite_uleb128(ctx, pt->sizekn);
|
||||
bcwrite_uleb128(ctx, pt->sizebc-1);
|
||||
if (!ctx->strip) {
|
||||
if (proto_lineinfo(pt))
|
||||
sizedbg = pt->sizept - (MSize)((char *)proto_lineinfo(pt) - (char *)pt);
|
||||
bcwrite_uleb128(ctx, sizedbg);
|
||||
if (sizedbg) {
|
||||
bcwrite_uleb128(ctx, pt->firstline);
|
||||
bcwrite_uleb128(ctx, pt->numline);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write bytecode instructions and upvalue refs. */
|
||||
bcwrite_bytecode(ctx, pt);
|
||||
bcwrite_block(ctx, proto_uv(pt), pt->sizeuv*2);
|
||||
|
||||
/* Write constants. */
|
||||
bcwrite_kgc(ctx, pt);
|
||||
bcwrite_knum(ctx, pt);
|
||||
|
||||
/* Write debug info, if not stripped. */
|
||||
if (sizedbg) {
|
||||
bcwrite_need(ctx, sizedbg);
|
||||
bcwrite_block(ctx, proto_lineinfo(pt), sizedbg);
|
||||
}
|
||||
|
||||
/* Pass buffer to writer function. */
|
||||
if (ctx->status == 0) {
|
||||
MSize n = ctx->sb.n - 5;
|
||||
MSize nn = (lj_fls(n)+8)*9 >> 6;
|
||||
ctx->sb.n = 5 - nn;
|
||||
bcwrite_uleb128(ctx, n); /* Fill in final size. */
|
||||
lua_assert(ctx->sb.n == 5);
|
||||
ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf+5-nn, nn+n, ctx->wdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Write header of bytecode dump. */
|
||||
static void bcwrite_header(BCWriteCtx *ctx)
|
||||
{
|
||||
GCstr *chunkname = proto_chunkname(ctx->pt);
|
||||
const char *name = strdata(chunkname);
|
||||
MSize len = chunkname->len;
|
||||
lj_str_resetbuf(&ctx->sb);
|
||||
bcwrite_need(ctx, 5+5+len);
|
||||
bcwrite_byte(ctx, BCDUMP_HEAD1);
|
||||
bcwrite_byte(ctx, BCDUMP_HEAD2);
|
||||
bcwrite_byte(ctx, BCDUMP_HEAD3);
|
||||
bcwrite_byte(ctx, BCDUMP_VERSION);
|
||||
bcwrite_byte(ctx, (ctx->strip ? BCDUMP_F_STRIP : 0) +
|
||||
(LJ_BE ? BCDUMP_F_BE : 0) +
|
||||
((ctx->pt->flags & PROTO_FFI) ? BCDUMP_F_FFI : 0));
|
||||
if (!ctx->strip) {
|
||||
bcwrite_uleb128(ctx, len);
|
||||
bcwrite_block(ctx, name, len);
|
||||
}
|
||||
ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf, ctx->sb.n, ctx->wdata);
|
||||
}
|
||||
|
||||
/* Write footer of bytecode dump. */
|
||||
static void bcwrite_footer(BCWriteCtx *ctx)
|
||||
{
|
||||
if (ctx->status == 0) {
|
||||
uint8_t zero = 0;
|
||||
ctx->status = ctx->wfunc(ctx->L, &zero, 1, ctx->wdata);
|
||||
}
|
||||
}
|
||||
|
||||
/* Protected callback for bytecode writer. */
|
||||
static TValue *cpwriter(lua_State *L, lua_CFunction dummy, void *ud)
|
||||
{
|
||||
BCWriteCtx *ctx = (BCWriteCtx *)ud;
|
||||
UNUSED(dummy);
|
||||
lj_str_resizebuf(L, &ctx->sb, 1024); /* Avoids resize for most prototypes. */
|
||||
bcwrite_header(ctx);
|
||||
bcwrite_proto(ctx, ctx->pt);
|
||||
bcwrite_footer(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Write bytecode for a prototype. */
|
||||
int lj_bcwrite(lua_State *L, GCproto *pt, lua_Writer writer, void *data,
|
||||
int strip)
|
||||
{
|
||||
BCWriteCtx ctx;
|
||||
int status;
|
||||
ctx.L = L;
|
||||
ctx.pt = pt;
|
||||
ctx.wfunc = writer;
|
||||
ctx.wdata = data;
|
||||
ctx.strip = strip;
|
||||
ctx.status = 0;
|
||||
lj_str_initbuf(&ctx.sb);
|
||||
status = lj_vm_cpcall(L, NULL, &ctx, cpwriter);
|
||||
if (status == 0) status = ctx.status;
|
||||
lj_str_freebuf(G(ctx.L), &ctx.sb);
|
||||
return status;
|
||||
}
|
||||
|
||||
Vendored
+351
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
** C data arithmetic.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_carith.h"
|
||||
|
||||
/* -- C data arithmetic --------------------------------------------------- */
|
||||
|
||||
/* Binary operands of an operator converted to ctypes. */
|
||||
typedef struct CDArith {
|
||||
uint8_t *p[2];
|
||||
CType *ct[2];
|
||||
} CDArith;
|
||||
|
||||
/* Check arguments for arithmetic metamethods. */
|
||||
static int carith_checkarg(lua_State *L, CTState *cts, CDArith *ca)
|
||||
{
|
||||
TValue *o = L->base;
|
||||
int ok = 1;
|
||||
MSize i;
|
||||
if (o+1 >= L->top)
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
for (i = 0; i < 2; i++, o++) {
|
||||
if (tviscdata(o)) {
|
||||
GCcdata *cd = cdataV(o);
|
||||
CTypeID id = (CTypeID)cd->ctypeid;
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
uint8_t *p = (uint8_t *)cdataptr(cd);
|
||||
if (ctype_isptr(ct->info)) {
|
||||
p = (uint8_t *)cdata_getptr(p, ct->size);
|
||||
if (ctype_isref(ct->info)) ct = ctype_rawchild(cts, ct);
|
||||
} else if (ctype_isfunc(ct->info)) {
|
||||
p = (uint8_t *)*(void **)p;
|
||||
ct = ctype_get(cts,
|
||||
lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR));
|
||||
}
|
||||
if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
|
||||
ca->ct[i] = ct;
|
||||
ca->p[i] = p;
|
||||
} else if (tvisint(o)) {
|
||||
ca->ct[i] = ctype_get(cts, CTID_INT32);
|
||||
ca->p[i] = (uint8_t *)&o->i;
|
||||
} else if (tvisnum(o)) {
|
||||
ca->ct[i] = ctype_get(cts, CTID_DOUBLE);
|
||||
ca->p[i] = (uint8_t *)&o->n;
|
||||
} else if (tvisnil(o)) {
|
||||
ca->ct[i] = ctype_get(cts, CTID_P_VOID);
|
||||
ca->p[i] = (uint8_t *)0;
|
||||
} else if (tvisstr(o)) {
|
||||
TValue *o2 = i == 0 ? o+1 : o-1;
|
||||
CType *ct = ctype_raw(cts, cdataV(o2)->ctypeid);
|
||||
ca->ct[i] = NULL;
|
||||
ca->p[i] = NULL;
|
||||
ok = 0;
|
||||
if (ctype_isenum(ct->info)) {
|
||||
CTSize ofs;
|
||||
CType *cct = lj_ctype_getfield(cts, ct, strV(o), &ofs);
|
||||
if (cct && ctype_isconstval(cct->info)) {
|
||||
ca->ct[i] = ctype_child(cts, cct);
|
||||
ca->p[i] = (uint8_t *)&cct->size; /* Assumes ct does not grow. */
|
||||
ok = 1;
|
||||
} else {
|
||||
ca->ct[1-i] = ct; /* Use enum to improve error message. */
|
||||
ca->p[1-i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ca->ct[i] = NULL;
|
||||
ca->p[i] = NULL;
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* Pointer arithmetic. */
|
||||
static int carith_ptr(lua_State *L, CTState *cts, CDArith *ca, MMS mm)
|
||||
{
|
||||
CType *ctp = ca->ct[0];
|
||||
uint8_t *pp = ca->p[0];
|
||||
ptrdiff_t idx;
|
||||
CTSize sz;
|
||||
CTypeID id;
|
||||
GCcdata *cd;
|
||||
if (ctype_isptr(ctp->info) || ctype_isrefarray(ctp->info)) {
|
||||
if ((mm == MM_sub || mm == MM_eq || mm == MM_lt || mm == MM_le) &&
|
||||
(ctype_isptr(ca->ct[1]->info) || ctype_isrefarray(ca->ct[1]->info))) {
|
||||
uint8_t *pp2 = ca->p[1];
|
||||
if (mm == MM_eq) { /* Pointer equality. Incompatible pointers are ok. */
|
||||
setboolV(L->top-1, (pp == pp2));
|
||||
return 1;
|
||||
}
|
||||
if (!lj_cconv_compatptr(cts, ctp, ca->ct[1], CCF_IGNQUAL))
|
||||
return 0;
|
||||
if (mm == MM_sub) { /* Pointer difference. */
|
||||
intptr_t diff;
|
||||
sz = lj_ctype_size(cts, ctype_cid(ctp->info)); /* Element size. */
|
||||
if (sz == 0 || sz == CTSIZE_INVALID)
|
||||
return 0;
|
||||
diff = ((intptr_t)pp - (intptr_t)pp2) / (int32_t)sz;
|
||||
/* All valid pointer differences on x64 are in (-2^47, +2^47),
|
||||
** which fits into a double without loss of precision.
|
||||
*/
|
||||
setintptrV(L->top-1, (int32_t)diff);
|
||||
return 1;
|
||||
} else if (mm == MM_lt) { /* Pointer comparison (unsigned). */
|
||||
setboolV(L->top-1, ((uintptr_t)pp < (uintptr_t)pp2));
|
||||
return 1;
|
||||
} else {
|
||||
lua_assert(mm == MM_le);
|
||||
setboolV(L->top-1, ((uintptr_t)pp <= (uintptr_t)pp2));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (!((mm == MM_add || mm == MM_sub) && ctype_isnum(ca->ct[1]->info)))
|
||||
return 0;
|
||||
lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ca->ct[1],
|
||||
(uint8_t *)&idx, ca->p[1], 0);
|
||||
if (mm == MM_sub) idx = -idx;
|
||||
} else if (mm == MM_add && ctype_isnum(ctp->info) &&
|
||||
(ctype_isptr(ca->ct[1]->info) || ctype_isrefarray(ca->ct[1]->info))) {
|
||||
/* Swap pointer and index. */
|
||||
ctp = ca->ct[1]; pp = ca->p[1];
|
||||
lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ca->ct[0],
|
||||
(uint8_t *)&idx, ca->p[0], 0);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
sz = lj_ctype_size(cts, ctype_cid(ctp->info)); /* Element size. */
|
||||
if (sz == CTSIZE_INVALID)
|
||||
return 0;
|
||||
pp += idx*(int32_t)sz; /* Compute pointer + index. */
|
||||
id = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(ctp->info)),
|
||||
CTSIZE_PTR);
|
||||
cd = lj_cdata_new(cts, id, CTSIZE_PTR);
|
||||
*(uint8_t **)cdataptr(cd) = pp;
|
||||
setcdataV(L, L->top-1, cd);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 64 bit integer arithmetic. */
|
||||
static int carith_int64(lua_State *L, CTState *cts, CDArith *ca, MMS mm)
|
||||
{
|
||||
if (ctype_isnum(ca->ct[0]->info) && ca->ct[0]->size <= 8 &&
|
||||
ctype_isnum(ca->ct[1]->info) && ca->ct[1]->size <= 8) {
|
||||
CTypeID id = (((ca->ct[0]->info & CTF_UNSIGNED) && ca->ct[0]->size == 8) ||
|
||||
((ca->ct[1]->info & CTF_UNSIGNED) && ca->ct[1]->size == 8)) ?
|
||||
CTID_UINT64 : CTID_INT64;
|
||||
CType *ct = ctype_get(cts, id);
|
||||
GCcdata *cd;
|
||||
uint64_t u0, u1, *up;
|
||||
lj_cconv_ct_ct(cts, ct, ca->ct[0], (uint8_t *)&u0, ca->p[0], 0);
|
||||
if (mm != MM_unm)
|
||||
lj_cconv_ct_ct(cts, ct, ca->ct[1], (uint8_t *)&u1, ca->p[1], 0);
|
||||
switch (mm) {
|
||||
case MM_eq:
|
||||
setboolV(L->top-1, (u0 == u1));
|
||||
return 1;
|
||||
case MM_lt:
|
||||
setboolV(L->top-1,
|
||||
id == CTID_INT64 ? ((int64_t)u0 < (int64_t)u1) : (u0 < u1));
|
||||
return 1;
|
||||
case MM_le:
|
||||
setboolV(L->top-1,
|
||||
id == CTID_INT64 ? ((int64_t)u0 <= (int64_t)u1) : (u0 <= u1));
|
||||
return 1;
|
||||
default: break;
|
||||
}
|
||||
cd = lj_cdata_new(cts, id, 8);
|
||||
up = (uint64_t *)cdataptr(cd);
|
||||
setcdataV(L, L->top-1, cd);
|
||||
switch (mm) {
|
||||
case MM_add: *up = u0 + u1; break;
|
||||
case MM_sub: *up = u0 - u1; break;
|
||||
case MM_mul: *up = u0 * u1; break;
|
||||
case MM_div:
|
||||
if (id == CTID_INT64)
|
||||
*up = (uint64_t)lj_carith_divi64((int64_t)u0, (int64_t)u1);
|
||||
else
|
||||
*up = lj_carith_divu64(u0, u1);
|
||||
break;
|
||||
case MM_mod:
|
||||
if (id == CTID_INT64)
|
||||
*up = (uint64_t)lj_carith_modi64((int64_t)u0, (int64_t)u1);
|
||||
else
|
||||
*up = lj_carith_modu64(u0, u1);
|
||||
break;
|
||||
case MM_pow:
|
||||
if (id == CTID_INT64)
|
||||
*up = (uint64_t)lj_carith_powi64((int64_t)u0, (int64_t)u1);
|
||||
else
|
||||
*up = lj_carith_powu64(u0, u1);
|
||||
break;
|
||||
case MM_unm: *up = (uint64_t)-(int64_t)u0; break;
|
||||
default: lua_assert(0); break;
|
||||
}
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle ctype arithmetic metamethods. */
|
||||
static int lj_carith_meta(lua_State *L, CTState *cts, CDArith *ca, MMS mm)
|
||||
{
|
||||
cTValue *tv = NULL;
|
||||
if (tviscdata(L->base)) {
|
||||
CTypeID id = cdataV(L->base)->ctypeid;
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
|
||||
tv = lj_ctype_meta(cts, id, mm);
|
||||
}
|
||||
if (!tv && L->base+1 < L->top && tviscdata(L->base+1)) {
|
||||
CTypeID id = cdataV(L->base+1)->ctypeid;
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
|
||||
tv = lj_ctype_meta(cts, id, mm);
|
||||
}
|
||||
if (!tv) {
|
||||
const char *repr[2];
|
||||
int i, isenum = -1, isstr = -1;
|
||||
if (mm == MM_eq) { /* Equality checks never raise an error. */
|
||||
setboolV(L->top-1, 0);
|
||||
return 1;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (ca->ct[i] && tviscdata(L->base+i)) {
|
||||
if (ctype_isenum(ca->ct[i]->info)) isenum = i;
|
||||
repr[i] = strdata(lj_ctype_repr(L, ctype_typeid(cts, ca->ct[i]), NULL));
|
||||
} else {
|
||||
if (tvisstr(&L->base[i])) isstr = i;
|
||||
repr[i] = lj_typename(&L->base[i]);
|
||||
}
|
||||
}
|
||||
if ((isenum ^ isstr) == 1)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADCONV, repr[isstr], repr[isenum]);
|
||||
lj_err_callerv(L, mm == MM_len ? LJ_ERR_FFI_BADLEN :
|
||||
mm == MM_concat ? LJ_ERR_FFI_BADCONCAT :
|
||||
mm < MM_add ? LJ_ERR_FFI_BADCOMP : LJ_ERR_FFI_BADARITH,
|
||||
repr[0], repr[1]);
|
||||
}
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
/* Arithmetic operators for cdata. */
|
||||
int lj_carith_op(lua_State *L, MMS mm)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CDArith ca;
|
||||
if (carith_checkarg(L, cts, &ca)) {
|
||||
if (carith_int64(L, cts, &ca, mm) || carith_ptr(L, cts, &ca, mm)) {
|
||||
copyTV(L, &G(L)->tmptv2, L->top-1); /* Remember for trace recorder. */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return lj_carith_meta(L, cts, &ca, mm);
|
||||
}
|
||||
|
||||
/* -- 64 bit integer arithmetic helpers ----------------------------------- */
|
||||
|
||||
#if LJ_32 && LJ_HASJIT
|
||||
/* Signed/unsigned 64 bit multiplication. */
|
||||
int64_t lj_carith_mul64(int64_t a, int64_t b)
|
||||
{
|
||||
return a * b;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Unsigned 64 bit division. */
|
||||
uint64_t lj_carith_divu64(uint64_t a, uint64_t b)
|
||||
{
|
||||
if (b == 0) return U64x(80000000,00000000);
|
||||
return a / b;
|
||||
}
|
||||
|
||||
/* Signed 64 bit division. */
|
||||
int64_t lj_carith_divi64(int64_t a, int64_t b)
|
||||
{
|
||||
if (b == 0 || (a == (int64_t)U64x(80000000,00000000) && b == -1))
|
||||
return U64x(80000000,00000000);
|
||||
return a / b;
|
||||
}
|
||||
|
||||
/* Unsigned 64 bit modulo. */
|
||||
uint64_t lj_carith_modu64(uint64_t a, uint64_t b)
|
||||
{
|
||||
if (b == 0) return U64x(80000000,00000000);
|
||||
return a % b;
|
||||
}
|
||||
|
||||
/* Signed 64 bit modulo. */
|
||||
int64_t lj_carith_modi64(int64_t a, int64_t b)
|
||||
{
|
||||
if (b == 0) return U64x(80000000,00000000);
|
||||
if (a == (int64_t)U64x(80000000,00000000) && b == -1) return 0;
|
||||
return a % b;
|
||||
}
|
||||
|
||||
/* Unsigned 64 bit x^k. */
|
||||
uint64_t lj_carith_powu64(uint64_t x, uint64_t k)
|
||||
{
|
||||
uint64_t y;
|
||||
if (k == 0)
|
||||
return 1;
|
||||
for (; (k & 1) == 0; k >>= 1) x *= x;
|
||||
y = x;
|
||||
if ((k >>= 1) != 0) {
|
||||
for (;;) {
|
||||
x *= x;
|
||||
if (k == 1) break;
|
||||
if (k & 1) y *= x;
|
||||
k >>= 1;
|
||||
}
|
||||
y *= x;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
/* Signed 64 bit x^k. */
|
||||
int64_t lj_carith_powi64(int64_t x, int64_t k)
|
||||
{
|
||||
if (k == 0)
|
||||
return 1;
|
||||
if (k < 0) {
|
||||
if (x == 0)
|
||||
return U64x(7fffffff,ffffffff);
|
||||
else if (x == 1)
|
||||
return 1;
|
||||
else if (x == -1)
|
||||
return (k & 1) ? -1 : 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
return (int64_t)lj_carith_powu64((uint64_t)x, (uint64_t)k);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
** C data arithmetic.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CARITH_H
|
||||
#define _LJ_CARITH_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
LJ_FUNC int lj_carith_op(lua_State *L, MMS mm);
|
||||
|
||||
#if LJ_32 && LJ_HASJIT
|
||||
LJ_FUNC int64_t lj_carith_mul64(int64_t x, int64_t k);
|
||||
#endif
|
||||
LJ_FUNC uint64_t lj_carith_divu64(uint64_t a, uint64_t b);
|
||||
LJ_FUNC int64_t lj_carith_divi64(int64_t a, int64_t b);
|
||||
LJ_FUNC uint64_t lj_carith_modu64(uint64_t a, uint64_t b);
|
||||
LJ_FUNC int64_t lj_carith_modi64(int64_t a, int64_t b);
|
||||
LJ_FUNC uint64_t lj_carith_powu64(uint64_t x, uint64_t k);
|
||||
LJ_FUNC int64_t lj_carith_powi64(int64_t x, int64_t k);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+899
@@ -0,0 +1,899 @@
|
||||
/*
|
||||
** FFI C call handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_ccall.h"
|
||||
#include "lj_trace.h"
|
||||
|
||||
/* Target-specific handling of register arguments. */
|
||||
#if LJ_TARGET_X86
|
||||
/* -- x86 calling conventions --------------------------------------------- */
|
||||
|
||||
#if LJ_ABI_WIN
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs bigger than 8 by reference (on stack only). */ \
|
||||
cc->retref = (sz > 8); \
|
||||
if (cc->retref) cc->stack[nsp++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET CCALL_HANDLE_STRUCTRET
|
||||
|
||||
#else
|
||||
|
||||
#if LJ_TARGET_OSX
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs of size 1, 2, 4 or 8 in registers. */ \
|
||||
cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); \
|
||||
if (cc->retref) { \
|
||||
if (ngpr < maxgpr) \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
else \
|
||||
cc->stack[nsp++] = (GPRArg)dp; \
|
||||
} else { /* Struct with single FP field ends up in FPR. */ \
|
||||
cc->resx87 = ccall_classify_struct(cts, ctr); \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
if (cc->resx87) sp = (uint8_t *)&cc->fpr[0]; \
|
||||
memcpy(dp, sp, ctr->size);
|
||||
|
||||
#else
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference (in reg or on stack). */ \
|
||||
if (ngpr < maxgpr) \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
else \
|
||||
cc->stack[nsp++] = (GPRArg)dp;
|
||||
|
||||
#endif
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Return complex float in GPRs and complex double by reference. */ \
|
||||
cc->retref = (sz > 8); \
|
||||
if (cc->retref) { \
|
||||
if (ngpr < maxgpr) \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
else \
|
||||
cc->stack[nsp++] = (GPRArg)dp; \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (!cc->retref) \
|
||||
*(int64_t *)dp = *(int64_t *)sp; /* Copy complex float from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
ngpr = maxgpr; /* Pass all structs by value on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
isfp = 1; /* Pass complex by value on stack. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (!isfp) { /* Only non-FP values may be passed in registers. */ \
|
||||
if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \
|
||||
if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} else if (ngpr + 1 <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64 && LJ_ABI_WIN
|
||||
/* -- Windows/x64 calling conventions ------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs of size 1, 2, 4 or 8 in a GPR. */ \
|
||||
cc->retref = !(sz == 1 || sz == 2 || sz == 4 || sz == 8); \
|
||||
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET CCALL_HANDLE_STRUCTRET
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (!cc->retref) \
|
||||
*(int64_t *)dp = *(int64_t *)sp; /* Copy complex float from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
/* Pass structs of size 1, 2, 4 or 8 in a GPR by value. */ \
|
||||
if (!(sz == 1 || sz == 2 || sz == 4 || sz == 8)) { \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; /* Pass all other structs by reference. */ \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex float in a GPR and complex double by reference. */ \
|
||||
if (sz != 2*sizeof(float)) { \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; \
|
||||
}
|
||||
|
||||
/* Windows/x64 argument registers are strictly positional (use ngpr). */
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (ngpr < maxgpr) { dp = &cc->fpr[ngpr++]; nfpr = ngpr; goto done; } \
|
||||
} else { \
|
||||
if (ngpr < maxgpr) { dp = &cc->gpr[ngpr++]; goto done; } \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64
|
||||
/* -- POSIX/x64 calling conventions --------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
int rcl[2]; rcl[0] = rcl[1] = 0; \
|
||||
if (ccall_classify_struct(cts, ctr, rcl, 0)) { \
|
||||
cc->retref = 1; /* Return struct by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp; \
|
||||
} else { \
|
||||
cc->retref = 0; /* Return small structs in registers. */ \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
int rcl[2]; rcl[0] = rcl[1] = 0; \
|
||||
ccall_classify_struct(cts, ctr, rcl, 0); \
|
||||
ccall_struct_ret(cc, rcl, dp, ctr->size);
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in one or two FPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPR. */ \
|
||||
*(int64_t *)dp = cc->fpr[0].l[0]; \
|
||||
} else { /* Copy non-contiguous complex double from FPRs. */ \
|
||||
((int64_t *)dp)[0] = cc->fpr[0].l[0]; \
|
||||
((int64_t *)dp)[1] = cc->fpr[1].l[0]; \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
int rcl[2]; rcl[0] = rcl[1] = 0; \
|
||||
if (!ccall_classify_struct(cts, d, rcl, 0)) { \
|
||||
cc->nsp = nsp; cc->ngpr = ngpr; cc->nfpr = nfpr; \
|
||||
if (ccall_struct_arg(cc, cts, d, rcl, o, narg)) goto err_nyi; \
|
||||
nsp = cc->nsp; ngpr = cc->ngpr; nfpr = cc->nfpr; \
|
||||
continue; \
|
||||
} /* Pass all other structs by value on stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
isfp = 2; /* Pass complex in FPRs or on stack. Needs postprocessing. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp) { /* Try to pass argument in FPRs. */ \
|
||||
if (nfpr + n <= CCALL_NARG_FPR) { \
|
||||
dp = &cc->fpr[nfpr]; \
|
||||
nfpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
/* Note that reordering is explicitly allowed in the x64 ABI. */ \
|
||||
if (n <= 2 && ngpr + n <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
/* -- ARM calling conventions --------------------------------------------- */
|
||||
|
||||
#if LJ_ABI_SOFTFP
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
/* Return structs of size <= 4 in a GPR. */ \
|
||||
cc->retref = !(sz <= 4); \
|
||||
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
cc->retref = 1; /* Return all complex values by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
UNUSED(dp); /* Nothing to do. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
/* Pass all structs by value in registers and/or on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG_FP1
|
||||
#define CCALL_HANDLE_REGARG_FP2
|
||||
|
||||
#else
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = !ccall_classify_struct(cts, ctr, ct); \
|
||||
if (cc->retref) cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
if (ccall_classify_struct(cts, ctr, ct) > 1) sp = (uint8_t *)&cc->fpr[0]; \
|
||||
memcpy(dp, sp, ctr->size);
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
if (!(ct->info & CTF_VARARG)) cc->retref = 0; /* Return complex in FPRs. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (!(ct->info & CTF_VARARG)) memcpy(dp, &cc->fpr[0], ctr->size);
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
isfp = (ccall_classify_struct(cts, d, ct) > 1);
|
||||
/* Pass all structs by value in registers and/or on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
isfp = 1; /* Pass complex by value in FPRs or on stack. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG_FP1 \
|
||||
if (isfp && !(ct->info & CTF_VARARG)) { \
|
||||
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \
|
||||
if (nfpr + (n >> 1) <= CCALL_NARG_FPR) { \
|
||||
dp = &cc->fpr[nfpr]; \
|
||||
nfpr += (n >> 1); \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (sz > 1 && fprodd != nfpr) fprodd = 0; \
|
||||
if (fprodd) { \
|
||||
if (2*nfpr+n <= 2*CCALL_NARG_FPR+1) { \
|
||||
dp = (void *)&cc->fpr[fprodd-1].f[1]; \
|
||||
nfpr += (n >> 1); \
|
||||
if ((n & 1)) fprodd = 0; else fprodd = nfpr-1; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (2*nfpr+n <= 2*CCALL_NARG_FPR) { \
|
||||
dp = (void *)&cc->fpr[nfpr]; \
|
||||
nfpr += (n >> 1); \
|
||||
if ((n & 1)) fprodd = ++nfpr; else fprodd = 0; \
|
||||
goto done; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
fprodd = 0; /* No reordering after the first FP value is on stack. */ \
|
||||
} else {
|
||||
|
||||
#define CCALL_HANDLE_REGARG_FP2 }
|
||||
|
||||
#endif
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
CCALL_HANDLE_REGARG_FP1 \
|
||||
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) { \
|
||||
if (ngpr < maxgpr) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
} \
|
||||
if (ngpr < maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
if (ngpr + n > maxgpr) { \
|
||||
nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \
|
||||
if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \
|
||||
ngpr = maxgpr; \
|
||||
} else { \
|
||||
ngpr += n; \
|
||||
} \
|
||||
goto done; \
|
||||
} CCALL_HANDLE_REGARG_FP2
|
||||
|
||||
#define CCALL_HANDLE_RET \
|
||||
if ((ct->info & CTF_VARARG)) sp = (uint8_t *)&cc->gpr[0];
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
/* -- PPC calling conventions --------------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in 2 or 4 GPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; /* Pass all structs by reference. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp) { /* Try to pass argument in FPRs. */ \
|
||||
if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
dp = &cc->fpr[nfpr]; \
|
||||
nfpr += 1; \
|
||||
d = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \
|
||||
goto done; \
|
||||
} \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
if (n > 1) { \
|
||||
lua_assert(n == 2 || n == 4); /* int64_t or complex (float). */ \
|
||||
if (ctype_isinteger(d->info)) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align int64_t to regpair. */ \
|
||||
else if (ngpr + n > maxgpr) \
|
||||
ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
ctr = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */
|
||||
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
/* -- PPC/SPE calling conventions ----------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in 2 or 4 GPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
memcpy(dp, sp, ctr->size); /* Copy complex from GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
rp = cdataptr(lj_cdata_new(cts, did, sz)); \
|
||||
sz = CTSIZE_PTR; /* Pass all structs by reference. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
/* PPC/SPE has a softfp ABI. */
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (n > 1) { /* Doesn't fit in a single GPR? */ \
|
||||
lua_assert(n == 2 || n == 4); /* int64_t, double or complex (float). */ \
|
||||
if (n == 2) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Only align 64 bit value to regpair. */ \
|
||||
else if (ngpr + n > maxgpr) \
|
||||
ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
/* -- MIPS calling conventions -------------------------------------------- */
|
||||
|
||||
#define CCALL_HANDLE_STRUCTRET \
|
||||
cc->retref = 1; /* Return all structs by reference. */ \
|
||||
cc->gpr[ngpr++] = (GPRArg)dp;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET \
|
||||
/* Complex values are returned in 1 or 2 FPRs. */ \
|
||||
cc->retref = 0;
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXRET2 \
|
||||
if (ctr->size == 2*sizeof(float)) { /* Copy complex float from FPRs. */ \
|
||||
((float *)dp)[0] = cc->fpr[0].f; \
|
||||
((float *)dp)[1] = cc->fpr[1].f; \
|
||||
} else { /* Copy complex double from FPRs. */ \
|
||||
((double *)dp)[0] = cc->fpr[0].d; \
|
||||
((double *)dp)[1] = cc->fpr[1].d; \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_STRUCTARG \
|
||||
/* Pass all structs by value in registers and/or on the stack. */
|
||||
|
||||
#define CCALL_HANDLE_COMPLEXARG \
|
||||
/* Pass complex by value in 2 or 4 GPRs. */
|
||||
|
||||
#define CCALL_HANDLE_REGARG \
|
||||
if (isfp && nfpr < CCALL_NARG_FPR && !(ct->info & CTF_VARARG)) { \
|
||||
/* Try to pass argument in FPRs. */ \
|
||||
dp = n == 1 ? (void *)&cc->fpr[nfpr].f : (void *)&cc->fpr[nfpr].d; \
|
||||
nfpr++; ngpr += n; \
|
||||
goto done; \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
nfpr = CCALL_NARG_FPR; \
|
||||
if ((d->info & CTF_ALIGN) > CTALIGN_PTR) \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
if (ngpr < maxgpr) { \
|
||||
dp = &cc->gpr[ngpr]; \
|
||||
if (ngpr + n > maxgpr) { \
|
||||
nsp += ngpr + n - maxgpr; /* Assumes contiguous gpr/stack fields. */ \
|
||||
if (nsp > CCALL_MAXSTACK) goto err_nyi; /* Too many arguments. */ \
|
||||
ngpr = maxgpr; \
|
||||
} else { \
|
||||
ngpr += n; \
|
||||
} \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CCALL_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
sp = (uint8_t *)&cc->fpr[0].f;
|
||||
|
||||
#else
|
||||
#error "Missing calling convention definitions for this architecture"
|
||||
#endif
|
||||
|
||||
#ifndef CCALL_HANDLE_STRUCTRET2
|
||||
#define CCALL_HANDLE_STRUCTRET2 \
|
||||
memcpy(dp, sp, ctr->size); /* Copy struct return value from GPRs. */
|
||||
#endif
|
||||
|
||||
/* -- x86 OSX ABI struct classification ----------------------------------- */
|
||||
|
||||
#if LJ_TARGET_X86 && LJ_TARGET_OSX
|
||||
|
||||
/* Check for struct with single FP field. */
|
||||
static int ccall_classify_struct(CTState *cts, CType *ct)
|
||||
{
|
||||
CTSize sz = ct->size;
|
||||
if (!(sz == sizeof(float) || sz == sizeof(double))) return 0;
|
||||
if ((ct->info & CTF_UNION)) return 0;
|
||||
while (ct->sib) {
|
||||
ct = ctype_get(cts, ct->sib);
|
||||
if (ctype_isfield(ct->info)) {
|
||||
CType *sct = ctype_rawchild(cts, ct);
|
||||
if (ctype_isfp(sct->info)) {
|
||||
if (sct->size == sz)
|
||||
return (sz >> 2); /* Return 1 for float or 2 for double. */
|
||||
} else if (ctype_isstruct(sct->info)) {
|
||||
if (sct->size)
|
||||
return ccall_classify_struct(cts, sct);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else if (ctype_isbitfield(ct->info)) {
|
||||
break;
|
||||
} else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
|
||||
CType *sct = ctype_rawchild(cts, ct);
|
||||
if (sct->size)
|
||||
return ccall_classify_struct(cts, sct);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* -- x64 struct classification ------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_X64 && !LJ_ABI_WIN
|
||||
|
||||
/* Register classes for x64 struct classification. */
|
||||
#define CCALL_RCL_INT 1
|
||||
#define CCALL_RCL_SSE 2
|
||||
#define CCALL_RCL_MEM 4
|
||||
/* NYI: classify vectors. */
|
||||
|
||||
static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs);
|
||||
|
||||
/* Classify a C type. */
|
||||
static void ccall_classify_ct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
|
||||
{
|
||||
if (ctype_isarray(ct->info)) {
|
||||
CType *cct = ctype_rawchild(cts, ct);
|
||||
CTSize eofs, esz = cct->size, asz = ct->size;
|
||||
for (eofs = 0; eofs < asz; eofs += esz)
|
||||
ccall_classify_ct(cts, cct, rcl, ofs+eofs);
|
||||
} else if (ctype_isstruct(ct->info)) {
|
||||
ccall_classify_struct(cts, ct, rcl, ofs);
|
||||
} else {
|
||||
int cl = ctype_isfp(ct->info) ? CCALL_RCL_SSE : CCALL_RCL_INT;
|
||||
lua_assert(ctype_hassize(ct->info));
|
||||
if ((ofs & (ct->size-1))) cl = CCALL_RCL_MEM; /* Unaligned. */
|
||||
rcl[(ofs >= 8)] |= cl;
|
||||
}
|
||||
}
|
||||
|
||||
/* Recursively classify a struct based on its fields. */
|
||||
static int ccall_classify_struct(CTState *cts, CType *ct, int *rcl, CTSize ofs)
|
||||
{
|
||||
if (ct->size > 16) return CCALL_RCL_MEM; /* Too big, gets memory class. */
|
||||
while (ct->sib) {
|
||||
CTSize fofs;
|
||||
ct = ctype_get(cts, ct->sib);
|
||||
fofs = ofs+ct->size;
|
||||
if (ctype_isfield(ct->info))
|
||||
ccall_classify_ct(cts, ctype_rawchild(cts, ct), rcl, fofs);
|
||||
else if (ctype_isbitfield(ct->info))
|
||||
rcl[(fofs >= 8)] |= CCALL_RCL_INT; /* NYI: unaligned bitfields? */
|
||||
else if (ctype_isxattrib(ct->info, CTA_SUBTYPE))
|
||||
ccall_classify_struct(cts, ctype_rawchild(cts, ct), rcl, fofs);
|
||||
}
|
||||
return ((rcl[0]|rcl[1]) & CCALL_RCL_MEM); /* Memory class? */
|
||||
}
|
||||
|
||||
/* Try to split up a small struct into registers. */
|
||||
static int ccall_struct_reg(CCallState *cc, GPRArg *dp, int *rcl)
|
||||
{
|
||||
MSize ngpr = cc->ngpr, nfpr = cc->nfpr;
|
||||
uint32_t i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
lua_assert(!(rcl[i] & CCALL_RCL_MEM));
|
||||
if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */
|
||||
if (ngpr >= CCALL_NARG_GPR) return 1; /* Register overflow. */
|
||||
cc->gpr[ngpr++] = dp[i];
|
||||
} else if ((rcl[i] & CCALL_RCL_SSE)) {
|
||||
if (nfpr >= CCALL_NARG_FPR) return 1; /* Register overflow. */
|
||||
cc->fpr[nfpr++].l[0] = dp[i];
|
||||
}
|
||||
}
|
||||
cc->ngpr = ngpr; cc->nfpr = nfpr;
|
||||
return 0; /* Ok. */
|
||||
}
|
||||
|
||||
/* Pass a small struct argument. */
|
||||
static int ccall_struct_arg(CCallState *cc, CTState *cts, CType *d, int *rcl,
|
||||
TValue *o, int narg)
|
||||
{
|
||||
GPRArg dp[2];
|
||||
dp[0] = dp[1] = 0;
|
||||
/* Convert to temp. struct. */
|
||||
lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, CCF_ARG(narg));
|
||||
if (ccall_struct_reg(cc, dp, rcl)) { /* Register overflow? Pass on stack. */
|
||||
MSize nsp = cc->nsp, n = rcl[1] ? 2 : 1;
|
||||
if (nsp + n > CCALL_MAXSTACK) return 1; /* Too many arguments. */
|
||||
cc->nsp = nsp + n;
|
||||
memcpy(&cc->stack[nsp], dp, n*CTSIZE_PTR);
|
||||
}
|
||||
return 0; /* Ok. */
|
||||
}
|
||||
|
||||
/* Combine returned small struct. */
|
||||
static void ccall_struct_ret(CCallState *cc, int *rcl, uint8_t *dp, CTSize sz)
|
||||
{
|
||||
GPRArg sp[2];
|
||||
MSize ngpr = 0, nfpr = 0;
|
||||
uint32_t i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
if ((rcl[i] & CCALL_RCL_INT)) { /* Integer class takes precedence. */
|
||||
sp[i] = cc->gpr[ngpr++];
|
||||
} else if ((rcl[i] & CCALL_RCL_SSE)) {
|
||||
sp[i] = cc->fpr[nfpr++].l[0];
|
||||
}
|
||||
}
|
||||
memcpy(dp, sp, sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* -- ARM hard-float ABI struct classification ---------------------------- */
|
||||
|
||||
#if LJ_TARGET_ARM && !LJ_ABI_SOFTFP
|
||||
|
||||
/* Classify a struct based on its fields. */
|
||||
static unsigned int ccall_classify_struct(CTState *cts, CType *ct, CType *ctf)
|
||||
{
|
||||
CTSize sz = ct->size;
|
||||
unsigned int r = 0, n = 0, isu = (ct->info & CTF_UNION);
|
||||
if ((ctf->info & CTF_VARARG)) goto noth;
|
||||
while (ct->sib) {
|
||||
CType *sct;
|
||||
ct = ctype_get(cts, ct->sib);
|
||||
if (ctype_isfield(ct->info)) {
|
||||
sct = ctype_rawchild(cts, ct);
|
||||
if (ctype_isfp(sct->info)) {
|
||||
r |= sct->size;
|
||||
if (!isu) n++; else if (n == 0) n = 1;
|
||||
} else if (ctype_iscomplex(sct->info)) {
|
||||
r |= (sct->size >> 1);
|
||||
if (!isu) n += 2; else if (n < 2) n = 2;
|
||||
} else if (ctype_isstruct(sct->info)) {
|
||||
goto substruct;
|
||||
} else {
|
||||
goto noth;
|
||||
}
|
||||
} else if (ctype_isbitfield(ct->info)) {
|
||||
goto noth;
|
||||
} else if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
|
||||
sct = ctype_rawchild(cts, ct);
|
||||
substruct:
|
||||
if (sct->size > 0) {
|
||||
unsigned int s = ccall_classify_struct(cts, sct, ctf);
|
||||
if (s <= 1) goto noth;
|
||||
r |= (s & 255);
|
||||
if (!isu) n += (s >> 8); else if (n < (s >>8)) n = (s >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((r == 4 || r == 8) && n <= 4)
|
||||
return r + (n << 8);
|
||||
noth: /* Not a homogeneous float/double aggregate. */
|
||||
return (sz <= 4); /* Return structs of size <= 4 in a GPR. */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* -- Common C call handling ---------------------------------------------- */
|
||||
|
||||
/* Infer the destination CTypeID for a vararg argument. */
|
||||
CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o)
|
||||
{
|
||||
if (tvisnumber(o)) {
|
||||
return CTID_DOUBLE;
|
||||
} else if (tviscdata(o)) {
|
||||
CTypeID id = cdataV(o)->ctypeid;
|
||||
CType *s = ctype_get(cts, id);
|
||||
if (ctype_isrefarray(s->info)) {
|
||||
return lj_ctype_intern(cts,
|
||||
CTINFO(CT_PTR, CTALIGN_PTR|ctype_cid(s->info)), CTSIZE_PTR);
|
||||
} else if (ctype_isstruct(s->info) || ctype_isfunc(s->info)) {
|
||||
/* NYI: how to pass a struct by value in a vararg argument? */
|
||||
return lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|id), CTSIZE_PTR);
|
||||
} else if (ctype_isfp(s->info) && s->size == sizeof(float)) {
|
||||
return CTID_DOUBLE;
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
} else if (tvisstr(o)) {
|
||||
return CTID_P_CCHAR;
|
||||
} else if (tvisbool(o)) {
|
||||
return CTID_BOOL;
|
||||
} else {
|
||||
return CTID_P_VOID;
|
||||
}
|
||||
}
|
||||
|
||||
/* Setup arguments for C call. */
|
||||
static int ccall_set_args(lua_State *L, CTState *cts, CType *ct,
|
||||
CCallState *cc)
|
||||
{
|
||||
int gcsteps = 0;
|
||||
TValue *o, *top = L->top;
|
||||
CTypeID fid;
|
||||
CType *ctr;
|
||||
MSize maxgpr, ngpr = 0, nsp = 0, narg;
|
||||
#if CCALL_NARG_FPR
|
||||
MSize nfpr = 0;
|
||||
#if LJ_TARGET_ARM
|
||||
MSize fprodd = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Clear unused regs to get some determinism in case of misdeclaration. */
|
||||
memset(cc->gpr, 0, sizeof(cc->gpr));
|
||||
#if CCALL_NUM_FPR
|
||||
memset(cc->fpr, 0, sizeof(cc->fpr));
|
||||
#endif
|
||||
|
||||
#if LJ_TARGET_X86
|
||||
/* x86 has several different calling conventions. */
|
||||
cc->resx87 = 0;
|
||||
switch (ctype_cconv(ct->info)) {
|
||||
case CTCC_FASTCALL: maxgpr = 2; break;
|
||||
case CTCC_THISCALL: maxgpr = 1; break;
|
||||
default: maxgpr = 0; break;
|
||||
}
|
||||
#else
|
||||
maxgpr = CCALL_NARG_GPR;
|
||||
#endif
|
||||
|
||||
/* Perform required setup for some result types. */
|
||||
ctr = ctype_rawchild(cts, ct);
|
||||
if (ctype_isvector(ctr->info)) {
|
||||
if (!(CCALL_VECTOR_REG && (ctr->size == 8 || ctr->size == 16)))
|
||||
goto err_nyi;
|
||||
} else if (ctype_iscomplex(ctr->info) || ctype_isstruct(ctr->info)) {
|
||||
/* Preallocate cdata object and anchor it after arguments. */
|
||||
CTSize sz = ctr->size;
|
||||
GCcdata *cd = lj_cdata_new(cts, ctype_cid(ct->info), sz);
|
||||
void *dp = cdataptr(cd);
|
||||
setcdataV(L, L->top++, cd);
|
||||
if (ctype_isstruct(ctr->info)) {
|
||||
CCALL_HANDLE_STRUCTRET
|
||||
} else {
|
||||
CCALL_HANDLE_COMPLEXRET
|
||||
}
|
||||
#if LJ_TARGET_X86
|
||||
} else if (ctype_isfp(ctr->info)) {
|
||||
cc->resx87 = ctr->size == sizeof(float) ? 1 : 2;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Skip initial attributes. */
|
||||
fid = ct->sib;
|
||||
while (fid) {
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
if (!ctype_isattrib(ctf->info)) break;
|
||||
fid = ctf->sib;
|
||||
}
|
||||
|
||||
/* Walk through all passed arguments. */
|
||||
for (o = L->base+1, narg = 1; o < top; o++, narg++) {
|
||||
CTypeID did;
|
||||
CType *d;
|
||||
CTSize sz;
|
||||
MSize n, isfp = 0, isva = 0;
|
||||
void *dp, *rp = NULL;
|
||||
|
||||
if (fid) { /* Get argument type from field. */
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
fid = ctf->sib;
|
||||
lua_assert(ctype_isfield(ctf->info));
|
||||
did = ctype_cid(ctf->info);
|
||||
} else {
|
||||
if (!(ct->info & CTF_VARARG))
|
||||
lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too many arguments. */
|
||||
did = lj_ccall_ctid_vararg(cts, o); /* Infer vararg type. */
|
||||
isva = 1;
|
||||
}
|
||||
d = ctype_raw(cts, did);
|
||||
sz = d->size;
|
||||
|
||||
/* Find out how (by value/ref) and where (GPR/FPR) to pass an argument. */
|
||||
if (ctype_isnum(d->info)) {
|
||||
if (sz > 8) goto err_nyi;
|
||||
if ((d->info & CTF_FP))
|
||||
isfp = 1;
|
||||
} else if (ctype_isvector(d->info)) {
|
||||
if (CCALL_VECTOR_REG && (sz == 8 || sz == 16))
|
||||
isfp = 1;
|
||||
else
|
||||
goto err_nyi;
|
||||
} else if (ctype_isstruct(d->info)) {
|
||||
CCALL_HANDLE_STRUCTARG
|
||||
} else if (ctype_iscomplex(d->info)) {
|
||||
CCALL_HANDLE_COMPLEXARG
|
||||
} else {
|
||||
sz = CTSIZE_PTR;
|
||||
}
|
||||
sz = (sz + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1);
|
||||
n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */
|
||||
|
||||
CCALL_HANDLE_REGARG /* Handle register arguments. */
|
||||
|
||||
/* Otherwise pass argument on stack. */
|
||||
if (CCALL_ALIGN_STACKARG && !rp && (d->info & CTF_ALIGN) > CTALIGN_PTR) {
|
||||
MSize align = (1u << ctype_align(d->info-CTALIGN_PTR)) -1;
|
||||
nsp = (nsp + align) & ~align; /* Align argument on stack. */
|
||||
}
|
||||
if (nsp + n > CCALL_MAXSTACK) { /* Too many arguments. */
|
||||
err_nyi:
|
||||
lj_err_caller(L, LJ_ERR_FFI_NYICALL);
|
||||
}
|
||||
dp = &cc->stack[nsp];
|
||||
nsp += n;
|
||||
isva = 0;
|
||||
|
||||
done:
|
||||
if (rp) { /* Pass by reference. */
|
||||
gcsteps++;
|
||||
*(void **)dp = rp;
|
||||
dp = rp;
|
||||
}
|
||||
lj_cconv_ct_tv(cts, d, (uint8_t *)dp, o, CCF_ARG(narg));
|
||||
/* Extend passed integers to 32 bits at least. */
|
||||
if (ctype_isinteger_or_bool(d->info) && d->size < 4) {
|
||||
if (d->info & CTF_UNSIGNED)
|
||||
*(uint32_t *)dp = d->size == 1 ? (uint32_t)*(uint8_t *)dp :
|
||||
(uint32_t)*(uint16_t *)dp;
|
||||
else
|
||||
*(int32_t *)dp = d->size == 1 ? (int32_t)*(int8_t *)dp :
|
||||
(int32_t)*(int16_t *)dp;
|
||||
}
|
||||
#if LJ_TARGET_X64 && LJ_ABI_WIN
|
||||
if (isva) { /* Windows/x64 mirrors varargs in both register sets. */
|
||||
if (nfpr == ngpr)
|
||||
cc->gpr[ngpr-1] = cc->fpr[ngpr-1].l[0];
|
||||
else
|
||||
cc->fpr[ngpr-1].l[0] = cc->gpr[ngpr-1];
|
||||
}
|
||||
#else
|
||||
UNUSED(isva);
|
||||
#endif
|
||||
#if LJ_TARGET_X64 && !LJ_ABI_WIN
|
||||
if (isfp == 2 && n == 2 && (uint8_t *)dp == (uint8_t *)&cc->fpr[nfpr-2]) {
|
||||
cc->fpr[nfpr-1].d[0] = cc->fpr[nfpr-2].d[1]; /* Split complex double. */
|
||||
cc->fpr[nfpr-2].d[1] = 0;
|
||||
}
|
||||
#else
|
||||
UNUSED(isfp);
|
||||
#endif
|
||||
}
|
||||
if (fid) lj_err_caller(L, LJ_ERR_FFI_NUMARG); /* Too few arguments. */
|
||||
|
||||
#if LJ_TARGET_X64 || LJ_TARGET_PPC
|
||||
cc->nfpr = nfpr; /* Required for vararg functions. */
|
||||
#endif
|
||||
cc->nsp = nsp;
|
||||
cc->spadj = (CCALL_SPS_FREE + CCALL_SPS_EXTRA)*CTSIZE_PTR;
|
||||
if (nsp > CCALL_SPS_FREE)
|
||||
cc->spadj += (((nsp-CCALL_SPS_FREE)*CTSIZE_PTR + 15u) & ~15u);
|
||||
return gcsteps;
|
||||
}
|
||||
|
||||
/* Get results from C call. */
|
||||
static int ccall_get_results(lua_State *L, CTState *cts, CType *ct,
|
||||
CCallState *cc, int *ret)
|
||||
{
|
||||
CType *ctr = ctype_rawchild(cts, ct);
|
||||
uint8_t *sp = (uint8_t *)&cc->gpr[0];
|
||||
if (ctype_isvoid(ctr->info)) {
|
||||
*ret = 0; /* Zero results. */
|
||||
return 0; /* No additional GC step. */
|
||||
}
|
||||
*ret = 1; /* One result. */
|
||||
if (ctype_isstruct(ctr->info)) {
|
||||
/* Return cdata object which is already on top of stack. */
|
||||
if (!cc->retref) {
|
||||
void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
|
||||
CCALL_HANDLE_STRUCTRET2
|
||||
}
|
||||
return 1; /* One GC step. */
|
||||
}
|
||||
if (ctype_iscomplex(ctr->info)) {
|
||||
/* Return cdata object which is already on top of stack. */
|
||||
void *dp = cdataptr(cdataV(L->top-1)); /* Use preallocated object. */
|
||||
CCALL_HANDLE_COMPLEXRET2
|
||||
return 1; /* One GC step. */
|
||||
}
|
||||
if (LJ_BE && ctype_isinteger_or_bool(ctr->info) && ctr->size < CTSIZE_PTR)
|
||||
sp += (CTSIZE_PTR - ctr->size);
|
||||
#if CCALL_NUM_FPR
|
||||
if (ctype_isfp(ctr->info) || ctype_isvector(ctr->info))
|
||||
sp = (uint8_t *)&cc->fpr[0];
|
||||
#endif
|
||||
#ifdef CCALL_HANDLE_RET
|
||||
CCALL_HANDLE_RET
|
||||
#endif
|
||||
/* No reference types end up here, so there's no need for the CTypeID. */
|
||||
lua_assert(!(ctype_isrefarray(ctr->info) || ctype_isstruct(ctr->info)));
|
||||
return lj_cconv_tv_ct(cts, ctr, 0, L->top-1, sp);
|
||||
}
|
||||
|
||||
/* Call C function. */
|
||||
int lj_ccall_func(lua_State *L, GCcdata *cd)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CType *ct = ctype_raw(cts, cd->ctypeid);
|
||||
CTSize sz = CTSIZE_PTR;
|
||||
if (ctype_isptr(ct->info)) {
|
||||
sz = ct->size;
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
}
|
||||
if (ctype_isfunc(ct->info)) {
|
||||
CCallState cc;
|
||||
int gcsteps, ret;
|
||||
cc.func = (void (*)(void))cdata_getptr(cdataptr(cd), sz);
|
||||
gcsteps = ccall_set_args(L, cts, ct, &cc);
|
||||
ct = (CType *)((intptr_t)ct-(intptr_t)cts->tab);
|
||||
cts->cb.slot = ~0u;
|
||||
lj_vm_ffi_call(&cc);
|
||||
if (cts->cb.slot != ~0u) { /* Blacklist function that called a callback. */
|
||||
TValue tv;
|
||||
setlightudV(&tv, (void *)cc.func);
|
||||
setboolV(lj_tab_set(L, cts->miscmap, &tv), 1);
|
||||
}
|
||||
ct = (CType *)((intptr_t)ct+(intptr_t)cts->tab); /* May be reallocated. */
|
||||
gcsteps += ccall_get_results(L, cts, ct, &cc, &ret);
|
||||
#if LJ_TARGET_X86 && LJ_ABI_WIN
|
||||
/* Automatically detect __stdcall and fix up C function declaration. */
|
||||
if (cc.spadj && ctype_cconv(ct->info) == CTCC_CDECL) {
|
||||
CTF_INSERT(ct->info, CCONV, CTCC_STDCALL);
|
||||
lj_trace_abort(G(L));
|
||||
}
|
||||
#endif
|
||||
while (gcsteps-- > 0)
|
||||
lj_gc_check(L);
|
||||
return ret;
|
||||
}
|
||||
return -1; /* Not a function. */
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+171
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
** FFI C call handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CCALL_H
|
||||
#define _LJ_CCALL_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ctype.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
/* -- C calling conventions ----------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_X86ORX64
|
||||
|
||||
#if LJ_TARGET_X86
|
||||
#define CCALL_NARG_GPR 2 /* For fastcall arguments. */
|
||||
#define CCALL_NARG_FPR 0
|
||||
#define CCALL_NRET_GPR 2
|
||||
#define CCALL_NRET_FPR 1 /* For FP results on x87 stack. */
|
||||
#define CCALL_ALIGN_STACKARG 0 /* Don't align argument on stack. */
|
||||
#elif LJ_ABI_WIN
|
||||
#define CCALL_NARG_GPR 4
|
||||
#define CCALL_NARG_FPR 4
|
||||
#define CCALL_NRET_GPR 1
|
||||
#define CCALL_NRET_FPR 1
|
||||
#define CCALL_SPS_EXTRA 4
|
||||
#else
|
||||
#define CCALL_NARG_GPR 6
|
||||
#define CCALL_NARG_FPR 8
|
||||
#define CCALL_NRET_GPR 2
|
||||
#define CCALL_NRET_FPR 2
|
||||
#define CCALL_VECTOR_REG 1 /* Pass vectors in registers. */
|
||||
#endif
|
||||
|
||||
#define CCALL_SPS_FREE 1
|
||||
#define CCALL_ALIGN_CALLSTATE 16
|
||||
|
||||
typedef LJ_ALIGN(16) union FPRArg {
|
||||
double d[2];
|
||||
float f[4];
|
||||
uint8_t b[16];
|
||||
uint16_t s[8];
|
||||
int i[4];
|
||||
int64_t l[2];
|
||||
} FPRArg;
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
#define CCALL_NARG_GPR 4
|
||||
#define CCALL_NRET_GPR 2 /* For softfp double. */
|
||||
#if LJ_ABI_SOFTFP
|
||||
#define CCALL_NARG_FPR 0
|
||||
#define CCALL_NRET_FPR 0
|
||||
#else
|
||||
#define CCALL_NARG_FPR 8
|
||||
#define CCALL_NRET_FPR 4
|
||||
#endif
|
||||
#define CCALL_SPS_FREE 0
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
typedef union FPRArg {
|
||||
double d;
|
||||
float f[2];
|
||||
} FPRArg;
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#define CCALL_NARG_GPR 8
|
||||
#define CCALL_NARG_FPR 8
|
||||
#define CCALL_NRET_GPR 4 /* For complex double. */
|
||||
#define CCALL_NRET_FPR 1
|
||||
#define CCALL_SPS_EXTRA 4
|
||||
#define CCALL_SPS_FREE 0
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
typedef double FPRArg;
|
||||
|
||||
#elif LJ_TARGET_PPCSPE
|
||||
|
||||
#define CCALL_NARG_GPR 8
|
||||
#define CCALL_NARG_FPR 0
|
||||
#define CCALL_NRET_GPR 4 /* For softfp complex double. */
|
||||
#define CCALL_NRET_FPR 0
|
||||
#define CCALL_SPS_FREE 0 /* NYI */
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
#define CCALL_NARG_GPR 4
|
||||
#define CCALL_NARG_FPR 2
|
||||
#define CCALL_NRET_GPR 2
|
||||
#define CCALL_NRET_FPR 2
|
||||
#define CCALL_SPS_EXTRA 7
|
||||
#define CCALL_SPS_FREE 1
|
||||
|
||||
typedef intptr_t GPRArg;
|
||||
typedef union FPRArg {
|
||||
double d;
|
||||
struct { LJ_ENDIAN_LOHI(float f; , float g;) };
|
||||
} FPRArg;
|
||||
|
||||
#else
|
||||
#error "Missing calling convention definitions for this architecture"
|
||||
#endif
|
||||
|
||||
#ifndef CCALL_SPS_EXTRA
|
||||
#define CCALL_SPS_EXTRA 0
|
||||
#endif
|
||||
#ifndef CCALL_VECTOR_REG
|
||||
#define CCALL_VECTOR_REG 0
|
||||
#endif
|
||||
#ifndef CCALL_ALIGN_STACKARG
|
||||
#define CCALL_ALIGN_STACKARG 1
|
||||
#endif
|
||||
#ifndef CCALL_ALIGN_CALLSTATE
|
||||
#define CCALL_ALIGN_CALLSTATE 8
|
||||
#endif
|
||||
|
||||
#define CCALL_NUM_GPR \
|
||||
(CCALL_NARG_GPR > CCALL_NRET_GPR ? CCALL_NARG_GPR : CCALL_NRET_GPR)
|
||||
#define CCALL_NUM_FPR \
|
||||
(CCALL_NARG_FPR > CCALL_NRET_FPR ? CCALL_NARG_FPR : CCALL_NRET_FPR)
|
||||
|
||||
/* Check against constants in lj_ctype.h. */
|
||||
LJ_STATIC_ASSERT(CCALL_NUM_GPR <= CCALL_MAX_GPR);
|
||||
LJ_STATIC_ASSERT(CCALL_NUM_FPR <= CCALL_MAX_FPR);
|
||||
|
||||
#define CCALL_MAXSTACK 32
|
||||
|
||||
/* -- C call state -------------------------------------------------------- */
|
||||
|
||||
typedef LJ_ALIGN(CCALL_ALIGN_CALLSTATE) struct CCallState {
|
||||
void (*func)(void); /* Pointer to called function. */
|
||||
uint32_t spadj; /* Stack pointer adjustment. */
|
||||
uint8_t nsp; /* Number of stack slots. */
|
||||
uint8_t retref; /* Return value by reference. */
|
||||
#if LJ_TARGET_X64
|
||||
uint8_t ngpr; /* Number of arguments in GPRs. */
|
||||
uint8_t nfpr; /* Number of arguments in FPRs. */
|
||||
#elif LJ_TARGET_X86
|
||||
uint8_t resx87; /* Result on x87 stack: 1:float, 2:double. */
|
||||
#elif LJ_TARGET_PPC
|
||||
uint8_t nfpr; /* Number of arguments in FPRs. */
|
||||
#endif
|
||||
#if LJ_32
|
||||
int32_t align1;
|
||||
#endif
|
||||
#if CCALL_NUM_FPR
|
||||
FPRArg fpr[CCALL_NUM_FPR]; /* Arguments/results in FPRs. */
|
||||
#endif
|
||||
GPRArg gpr[CCALL_NUM_GPR]; /* Arguments/results in GPRs. */
|
||||
GPRArg stack[CCALL_MAXSTACK]; /* Stack slots. */
|
||||
} CCallState;
|
||||
|
||||
/* -- C call handling ----------------------------------------------------- */
|
||||
|
||||
/* Really belongs to lj_vm.h. */
|
||||
LJ_ASMF void LJ_FASTCALL lj_vm_ffi_call(CCallState *cc);
|
||||
|
||||
LJ_FUNC CTypeID lj_ccall_ctid_vararg(CTState *cts, cTValue *o);
|
||||
LJ_FUNC int lj_ccall_func(lua_State *L, GCcdata *cd);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+641
@@ -0,0 +1,641 @@
|
||||
/*
|
||||
** FFI C callback handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_state.h"
|
||||
#include "lj_frame.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_ccall.h"
|
||||
#include "lj_ccallback.h"
|
||||
#include "lj_target.h"
|
||||
#include "lj_mcode.h"
|
||||
#include "lj_trace.h"
|
||||
#include "lj_vm.h"
|
||||
|
||||
/* -- Target-specific handling of callback slots -------------------------- */
|
||||
|
||||
#define CALLBACK_MCODE_SIZE (LJ_PAGESIZE * LJ_NUM_CBPAGE)
|
||||
|
||||
#if LJ_OS_NOJIT
|
||||
|
||||
/* Disabled callback support. */
|
||||
#define CALLBACK_SLOT2OFS(slot) (0*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (0*(ofs))
|
||||
#define CALLBACK_MAX_SLOT 0
|
||||
|
||||
#elif LJ_TARGET_X86ORX64
|
||||
|
||||
#define CALLBACK_MCODE_HEAD (LJ_64 ? 8 : 0)
|
||||
#define CALLBACK_MCODE_GROUP (-2+1+2+5+(LJ_64 ? 6 : 5))
|
||||
|
||||
#define CALLBACK_SLOT2OFS(slot) \
|
||||
(CALLBACK_MCODE_HEAD + CALLBACK_MCODE_GROUP*((slot)/32) + 4*(slot))
|
||||
|
||||
static MSize CALLBACK_OFS2SLOT(MSize ofs)
|
||||
{
|
||||
MSize group;
|
||||
ofs -= CALLBACK_MCODE_HEAD;
|
||||
group = ofs / (32*4 + CALLBACK_MCODE_GROUP);
|
||||
return (ofs % (32*4 + CALLBACK_MCODE_GROUP))/4 + group*32;
|
||||
}
|
||||
|
||||
#define CALLBACK_MAX_SLOT \
|
||||
(((CALLBACK_MCODE_SIZE-CALLBACK_MCODE_HEAD)/(CALLBACK_MCODE_GROUP+4*32))*32)
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
#define CALLBACK_MCODE_HEAD 32
|
||||
#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8)
|
||||
#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE))
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#define CALLBACK_MCODE_HEAD 24
|
||||
#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8)
|
||||
#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE))
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
#define CALLBACK_MCODE_HEAD 24
|
||||
#define CALLBACK_SLOT2OFS(slot) (CALLBACK_MCODE_HEAD + 8*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (((ofs)-CALLBACK_MCODE_HEAD)/8)
|
||||
#define CALLBACK_MAX_SLOT (CALLBACK_OFS2SLOT(CALLBACK_MCODE_SIZE))
|
||||
|
||||
#else
|
||||
|
||||
/* Missing support for this architecture. */
|
||||
#define CALLBACK_SLOT2OFS(slot) (0*(slot))
|
||||
#define CALLBACK_OFS2SLOT(ofs) (0*(ofs))
|
||||
#define CALLBACK_MAX_SLOT 0
|
||||
|
||||
#endif
|
||||
|
||||
/* Convert callback slot number to callback function pointer. */
|
||||
static void *callback_slot2ptr(CTState *cts, MSize slot)
|
||||
{
|
||||
return (uint8_t *)cts->cb.mcode + CALLBACK_SLOT2OFS(slot);
|
||||
}
|
||||
|
||||
/* Convert callback function pointer to slot number. */
|
||||
MSize lj_ccallback_ptr2slot(CTState *cts, void *p)
|
||||
{
|
||||
uintptr_t ofs = (uintptr_t)((uint8_t *)p -(uint8_t *)cts->cb.mcode);
|
||||
if (ofs < CALLBACK_MCODE_SIZE) {
|
||||
MSize slot = CALLBACK_OFS2SLOT((MSize)ofs);
|
||||
if (CALLBACK_SLOT2OFS(slot) == (MSize)ofs)
|
||||
return slot;
|
||||
}
|
||||
return ~0u; /* Not a known callback function pointer. */
|
||||
}
|
||||
|
||||
/* Initialize machine code for callback function pointers. */
|
||||
#if LJ_OS_NOJIT
|
||||
/* Disabled callback support. */
|
||||
#define callback_mcode_init(g, p) UNUSED(p)
|
||||
#elif LJ_TARGET_X86ORX64
|
||||
static void callback_mcode_init(global_State *g, uint8_t *page)
|
||||
{
|
||||
uint8_t *p = page;
|
||||
uint8_t *target = (uint8_t *)(void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
#if LJ_64
|
||||
*(void **)p = target; p += 8;
|
||||
#endif
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
/* mov al, slot; jmp group */
|
||||
*p++ = XI_MOVrib | RID_EAX; *p++ = (uint8_t)slot;
|
||||
if ((slot & 31) == 31 || slot == CALLBACK_MAX_SLOT-1) {
|
||||
/* push ebp/rbp; mov ah, slot>>8; mov ebp, &g. */
|
||||
*p++ = XI_PUSH + RID_EBP;
|
||||
*p++ = XI_MOVrib | (RID_EAX+4); *p++ = (uint8_t)(slot >> 8);
|
||||
*p++ = XI_MOVri | RID_EBP;
|
||||
*(int32_t *)p = i32ptr(g); p += 4;
|
||||
#if LJ_64
|
||||
/* jmp [rip-pageofs] where lj_vm_ffi_callback is stored. */
|
||||
*p++ = XI_GROUP5; *p++ = XM_OFS0 + (XOg_JMP<<3) + RID_EBP;
|
||||
*(int32_t *)p = (int32_t)(page-(p+4)); p += 4;
|
||||
#else
|
||||
/* jmp lj_vm_ffi_callback. */
|
||||
*p++ = XI_JMP; *(int32_t *)p = target-(p+4); p += 4;
|
||||
#endif
|
||||
} else {
|
||||
*p++ = XI_JMPs; *p++ = (uint8_t)((2+2)*(31-(slot&31)) - 2);
|
||||
}
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_ARM
|
||||
static void callback_mcode_init(global_State *g, uint32_t *page)
|
||||
{
|
||||
uint32_t *p = page;
|
||||
void *target = (void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
/* This must match with the saveregs macro in buildvm_arm.dasc. */
|
||||
*p++ = ARMI_SUB|ARMF_D(RID_R12)|ARMF_N(RID_R12)|ARMF_M(RID_PC);
|
||||
*p++ = ARMI_PUSH|ARMF_N(RID_SP)|RSET_RANGE(RID_R4,RID_R11+1)|RID2RSET(RID_LR);
|
||||
*p++ = ARMI_SUB|ARMI_K12|ARMF_D(RID_R12)|ARMF_N(RID_R12)|CALLBACK_MCODE_HEAD;
|
||||
*p++ = ARMI_STR|ARMI_LS_P|ARMI_LS_W|ARMF_D(RID_R12)|ARMF_N(RID_SP)|(CFRAME_SIZE-4*9);
|
||||
*p++ = ARMI_LDR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_R12)|ARMF_N(RID_PC);
|
||||
*p++ = ARMI_LDR|ARMI_LS_P|ARMI_LS_U|ARMF_D(RID_PC)|ARMF_N(RID_PC);
|
||||
*p++ = u32ptr(g);
|
||||
*p++ = u32ptr(target);
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
*p++ = ARMI_MOV|ARMF_D(RID_R12)|ARMF_M(RID_PC);
|
||||
*p = ARMI_B | ((page-p-2) & 0x00ffffffu);
|
||||
p++;
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_PPC
|
||||
static void callback_mcode_init(global_State *g, uint32_t *page)
|
||||
{
|
||||
uint32_t *p = page;
|
||||
void *target = (void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
*p++ = PPCI_LIS | PPCF_T(RID_TMP) | (u32ptr(target) >> 16);
|
||||
*p++ = PPCI_LIS | PPCF_T(RID_R12) | (u32ptr(g) >> 16);
|
||||
*p++ = PPCI_ORI | PPCF_A(RID_TMP)|PPCF_T(RID_TMP) | (u32ptr(target) & 0xffff);
|
||||
*p++ = PPCI_ORI | PPCF_A(RID_R12)|PPCF_T(RID_R12) | (u32ptr(g) & 0xffff);
|
||||
*p++ = PPCI_MTCTR | PPCF_T(RID_TMP);
|
||||
*p++ = PPCI_BCTR;
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
*p++ = PPCI_LI | PPCF_T(RID_R11) | slot;
|
||||
*p = PPCI_B | (((page-p) & 0x00ffffffu) << 2);
|
||||
p++;
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#elif LJ_TARGET_MIPS
|
||||
static void callback_mcode_init(global_State *g, uint32_t *page)
|
||||
{
|
||||
uint32_t *p = page;
|
||||
void *target = (void *)lj_vm_ffi_callback;
|
||||
MSize slot;
|
||||
*p++ = MIPSI_SW | MIPSF_T(RID_R1)|MIPSF_S(RID_SP) | 0;
|
||||
*p++ = MIPSI_LUI | MIPSF_T(RID_R3) | (u32ptr(target) >> 16);
|
||||
*p++ = MIPSI_LUI | MIPSF_T(RID_R2) | (u32ptr(g) >> 16);
|
||||
*p++ = MIPSI_ORI | MIPSF_T(RID_R3)|MIPSF_S(RID_R3) |(u32ptr(target)&0xffff);
|
||||
*p++ = MIPSI_JR | MIPSF_S(RID_R3);
|
||||
*p++ = MIPSI_ORI | MIPSF_T(RID_R2)|MIPSF_S(RID_R2) | (u32ptr(g)&0xffff);
|
||||
for (slot = 0; slot < CALLBACK_MAX_SLOT; slot++) {
|
||||
*p = MIPSI_B | ((page-p-1) & 0x0000ffffu);
|
||||
p++;
|
||||
*p++ = MIPSI_LI | MIPSF_T(RID_R1) | slot;
|
||||
}
|
||||
lua_assert(p - page <= CALLBACK_MCODE_SIZE);
|
||||
}
|
||||
#else
|
||||
/* Missing support for this architecture. */
|
||||
#define callback_mcode_init(g, p) UNUSED(p)
|
||||
#endif
|
||||
|
||||
/* -- Machine code management --------------------------------------------- */
|
||||
|
||||
#if LJ_TARGET_WINDOWS
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
#elif LJ_TARGET_POSIX
|
||||
|
||||
#include <sys/mman.h>
|
||||
#ifndef MAP_ANONYMOUS
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* Allocate and initialize area for callback function pointers. */
|
||||
static void callback_mcode_new(CTState *cts)
|
||||
{
|
||||
size_t sz = (size_t)CALLBACK_MCODE_SIZE;
|
||||
void *p;
|
||||
if (CALLBACK_MAX_SLOT == 0)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
#if LJ_TARGET_WINDOWS
|
||||
p = VirtualAlloc(NULL, sz, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||
if (!p)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
#elif LJ_TARGET_POSIX
|
||||
p = mmap(NULL, sz, (PROT_READ|PROT_WRITE), MAP_PRIVATE|MAP_ANONYMOUS,
|
||||
-1, 0);
|
||||
if (p == MAP_FAILED)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
#else
|
||||
/* Fallback allocator. Fails if memory is not executable by default. */
|
||||
p = lj_mem_new(cts->L, sz);
|
||||
#endif
|
||||
cts->cb.mcode = p;
|
||||
callback_mcode_init(cts->g, p);
|
||||
lj_mcode_sync(p, (char *)p + sz);
|
||||
#if LJ_TARGET_WINDOWS
|
||||
{
|
||||
DWORD oprot;
|
||||
VirtualProtect(p, sz, PAGE_EXECUTE_READ, &oprot);
|
||||
}
|
||||
#elif LJ_TARGET_POSIX
|
||||
mprotect(p, sz, (PROT_READ|PROT_EXEC));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Free area for callback function pointers. */
|
||||
void lj_ccallback_mcode_free(CTState *cts)
|
||||
{
|
||||
size_t sz = (size_t)CALLBACK_MCODE_SIZE;
|
||||
void *p = cts->cb.mcode;
|
||||
if (p == NULL) return;
|
||||
#if LJ_TARGET_WINDOWS
|
||||
VirtualFree(p, 0, MEM_RELEASE);
|
||||
UNUSED(sz);
|
||||
#elif LJ_TARGET_POSIX
|
||||
munmap(p, sz);
|
||||
#else
|
||||
lj_mem_free(cts->g, p, sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* -- C callback entry ---------------------------------------------------- */
|
||||
|
||||
/* Target-specific handling of register arguments. Similar to lj_ccall.c. */
|
||||
#if LJ_TARGET_X86
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (!isfp) { /* Only non-FP values may be passed in registers. */ \
|
||||
if (n > 1) { /* Anything > 32 bit is passed on the stack. */ \
|
||||
if (!LJ_ABI_WIN) ngpr = maxgpr; /* Prevent reordering. */ \
|
||||
} else if (ngpr + 1 <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64 && LJ_ABI_WIN
|
||||
|
||||
/* Windows/x64 argument registers are strictly positional (use ngpr). */
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (ngpr < maxgpr) { sp = &cts->cb.fpr[ngpr++]; UNUSED(nfpr); goto done; } \
|
||||
} else { \
|
||||
if (ngpr < maxgpr) { sp = &cts->cb.gpr[ngpr++]; goto done; } \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_X64
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (nfpr + n <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr]; \
|
||||
nfpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#elif LJ_TARGET_ARM
|
||||
|
||||
#if LJ_ABI_SOFTFP
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG_FP1 UNUSED(isfp);
|
||||
#define CALLBACK_HANDLE_REGARG_FP2
|
||||
|
||||
#else
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG_FP1 \
|
||||
if (isfp) { \
|
||||
if (n == 1) { \
|
||||
if (fprodd) { \
|
||||
sp = &cts->cb.fpr[fprodd-1]; \
|
||||
fprodd = 0; \
|
||||
goto done; \
|
||||
} else if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr++]; \
|
||||
fprodd = nfpr; \
|
||||
goto done; \
|
||||
} \
|
||||
} else { \
|
||||
if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr++]; \
|
||||
goto done; \
|
||||
} \
|
||||
} \
|
||||
fprodd = 0; /* No reordering after the first FP value is on stack. */ \
|
||||
} else {
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG_FP2 }
|
||||
|
||||
#endif
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
CALLBACK_HANDLE_REGARG_FP1 \
|
||||
if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} CALLBACK_HANDLE_REGARG_FP2
|
||||
|
||||
#elif LJ_TARGET_PPC
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp) { \
|
||||
if (nfpr + 1 <= CCALL_NARG_FPR) { \
|
||||
sp = &cts->cb.fpr[nfpr++]; \
|
||||
cta = ctype_get(cts, CTID_DOUBLE); /* FPRs always hold doubles. */ \
|
||||
goto done; \
|
||||
} \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
if (n > 1) { \
|
||||
lua_assert(ctype_isinteger(cta->info) && n == 2); /* int64_t. */ \
|
||||
ngpr = (ngpr + 1u) & ~1u; /* Align int64_t to regpair. */ \
|
||||
} \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CALLBACK_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
*(double *)dp = *(float *)dp; /* FPRs always hold doubles. */
|
||||
|
||||
#elif LJ_TARGET_MIPS
|
||||
|
||||
#define CALLBACK_HANDLE_REGARG \
|
||||
if (isfp && nfpr < CCALL_NARG_FPR) { /* Try to pass argument in FPRs. */ \
|
||||
sp = (void *)((uint8_t *)&cts->cb.fpr[nfpr] + ((LJ_BE && n==1) ? 4 : 0)); \
|
||||
nfpr++; ngpr += n; \
|
||||
goto done; \
|
||||
} else { /* Try to pass argument in GPRs. */ \
|
||||
nfpr = CCALL_NARG_FPR; \
|
||||
if (n > 1) ngpr = (ngpr + 1u) & ~1u; /* Align to regpair. */ \
|
||||
if (ngpr + n <= maxgpr) { \
|
||||
sp = &cts->cb.gpr[ngpr]; \
|
||||
ngpr += n; \
|
||||
goto done; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CALLBACK_HANDLE_RET \
|
||||
if (ctype_isfp(ctr->info) && ctr->size == sizeof(float)) \
|
||||
((float *)dp)[1] = *(float *)dp;
|
||||
|
||||
#else
|
||||
#error "Missing calling convention definitions for this architecture"
|
||||
#endif
|
||||
|
||||
/* Convert and push callback arguments to Lua stack. */
|
||||
static void callback_conv_args(CTState *cts, lua_State *L)
|
||||
{
|
||||
TValue *o = L->top;
|
||||
intptr_t *stack = cts->cb.stack;
|
||||
MSize slot = cts->cb.slot;
|
||||
CTypeID id = 0, rid, fid;
|
||||
CType *ct;
|
||||
GCfunc *fn;
|
||||
MSize ngpr = 0, nsp = 0, maxgpr = CCALL_NARG_GPR;
|
||||
#if CCALL_NARG_FPR
|
||||
MSize nfpr = 0;
|
||||
#if LJ_TARGET_ARM
|
||||
MSize fprodd = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (slot < cts->cb.sizeid && (id = cts->cb.cbid[slot]) != 0) {
|
||||
ct = ctype_get(cts, id);
|
||||
rid = ctype_cid(ct->info);
|
||||
fn = funcV(lj_tab_getint(cts->miscmap, (int32_t)slot));
|
||||
} else { /* Must set up frame first, before throwing the error. */
|
||||
ct = NULL;
|
||||
rid = 0;
|
||||
fn = (GCfunc *)L;
|
||||
}
|
||||
o->u32.lo = LJ_CONT_FFI_CALLBACK; /* Continuation returns from callback. */
|
||||
o->u32.hi = rid; /* Return type. x86: +(spadj<<16). */
|
||||
o++;
|
||||
setframe_gc(o, obj2gco(fn));
|
||||
setframe_ftsz(o, (int)((char *)(o+1) - (char *)L->base) + FRAME_CONT);
|
||||
L->top = L->base = ++o;
|
||||
if (!ct)
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_BADCBACK);
|
||||
if (isluafunc(fn))
|
||||
setcframe_pc(L->cframe, proto_bc(funcproto(fn))+1);
|
||||
lj_state_checkstack(L, LUA_MINSTACK); /* May throw. */
|
||||
o = L->base; /* Might have been reallocated. */
|
||||
|
||||
#if LJ_TARGET_X86
|
||||
/* x86 has several different calling conventions. */
|
||||
switch (ctype_cconv(ct->info)) {
|
||||
case CTCC_FASTCALL: maxgpr = 2; break;
|
||||
case CTCC_THISCALL: maxgpr = 1; break;
|
||||
default: maxgpr = 0; break;
|
||||
}
|
||||
#endif
|
||||
|
||||
fid = ct->sib;
|
||||
while (fid) {
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
if (!ctype_isattrib(ctf->info)) {
|
||||
CType *cta;
|
||||
void *sp;
|
||||
CTSize sz;
|
||||
int isfp;
|
||||
MSize n;
|
||||
lua_assert(ctype_isfield(ctf->info));
|
||||
cta = ctype_rawchild(cts, ctf);
|
||||
isfp = ctype_isfp(cta->info);
|
||||
sz = (cta->size + CTSIZE_PTR-1) & ~(CTSIZE_PTR-1);
|
||||
n = sz / CTSIZE_PTR; /* Number of GPRs or stack slots needed. */
|
||||
|
||||
CALLBACK_HANDLE_REGARG /* Handle register arguments. */
|
||||
|
||||
/* Otherwise pass argument on stack. */
|
||||
if (CCALL_ALIGN_STACKARG && LJ_32 && sz == 8)
|
||||
nsp = (nsp + 1) & ~1u; /* Align 64 bit argument on stack. */
|
||||
sp = &stack[nsp];
|
||||
nsp += n;
|
||||
|
||||
done:
|
||||
if (LJ_BE && cta->size < CTSIZE_PTR)
|
||||
sp = (void *)((uint8_t *)sp + CTSIZE_PTR-cta->size);
|
||||
lj_cconv_tv_ct(cts, cta, 0, o++, sp);
|
||||
}
|
||||
fid = ctf->sib;
|
||||
}
|
||||
L->top = o;
|
||||
#if LJ_TARGET_X86
|
||||
/* Store stack adjustment for returns from non-cdecl callbacks. */
|
||||
if (ctype_cconv(ct->info) != CTCC_CDECL)
|
||||
(L->base-2)->u32.hi |= (nsp << (16+2));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Convert Lua object to callback result. */
|
||||
static void callback_conv_result(CTState *cts, lua_State *L, TValue *o)
|
||||
{
|
||||
CType *ctr = ctype_raw(cts, (uint16_t)(L->base-2)->u32.hi);
|
||||
#if LJ_TARGET_X86
|
||||
cts->cb.gpr[2] = 0;
|
||||
#endif
|
||||
if (!ctype_isvoid(ctr->info)) {
|
||||
uint8_t *dp = (uint8_t *)&cts->cb.gpr[0];
|
||||
#if CCALL_NUM_FPR
|
||||
if (ctype_isfp(ctr->info))
|
||||
dp = (uint8_t *)&cts->cb.fpr[0];
|
||||
#endif
|
||||
lj_cconv_ct_tv(cts, ctr, dp, o, 0);
|
||||
#ifdef CALLBACK_HANDLE_RET
|
||||
CALLBACK_HANDLE_RET
|
||||
#endif
|
||||
/* Extend returned integers to (at least) 32 bits. */
|
||||
if (ctype_isinteger_or_bool(ctr->info) && ctr->size < 4) {
|
||||
if (ctr->info & CTF_UNSIGNED)
|
||||
*(uint32_t *)dp = ctr->size == 1 ? (uint32_t)*(uint8_t *)dp :
|
||||
(uint32_t)*(uint16_t *)dp;
|
||||
else
|
||||
*(int32_t *)dp = ctr->size == 1 ? (int32_t)*(int8_t *)dp :
|
||||
(int32_t)*(int16_t *)dp;
|
||||
}
|
||||
#if LJ_TARGET_X86
|
||||
if (ctype_isfp(ctr->info))
|
||||
cts->cb.gpr[2] = ctr->size == sizeof(float) ? 1 : 2;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Enter callback. */
|
||||
lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf)
|
||||
{
|
||||
lua_State *L = cts->L;
|
||||
global_State *g = cts->g;
|
||||
lua_assert(L != NULL);
|
||||
if (gcref(g->jit_L)) {
|
||||
setstrV(L, L->top++, lj_err_str(L, LJ_ERR_FFI_BADCBACK));
|
||||
if (g->panic) g->panic(L);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
lj_trace_abort(g); /* Never record across callback. */
|
||||
/* Setup C frame. */
|
||||
cframe_prev(cf) = L->cframe;
|
||||
setcframe_L(cf, L);
|
||||
cframe_errfunc(cf) = -1;
|
||||
cframe_nres(cf) = 0;
|
||||
L->cframe = cf;
|
||||
callback_conv_args(cts, L);
|
||||
return L; /* Now call the function on this stack. */
|
||||
}
|
||||
|
||||
/* Leave callback. */
|
||||
void LJ_FASTCALL lj_ccallback_leave(CTState *cts, TValue *o)
|
||||
{
|
||||
lua_State *L = cts->L;
|
||||
GCfunc *fn;
|
||||
TValue *obase = L->base;
|
||||
L->base = L->top; /* Keep continuation frame for throwing errors. */
|
||||
if (o >= L->base) {
|
||||
/* PC of RET* is lost. Point to last line for result conv. errors. */
|
||||
fn = curr_func(L);
|
||||
if (isluafunc(fn)) {
|
||||
GCproto *pt = funcproto(fn);
|
||||
setcframe_pc(L->cframe, proto_bc(pt)+pt->sizebc+1);
|
||||
}
|
||||
}
|
||||
callback_conv_result(cts, L, o);
|
||||
/* Finally drop C frame and continuation frame. */
|
||||
L->cframe = cframe_prev(L->cframe);
|
||||
L->top -= 2;
|
||||
L->base = obase;
|
||||
cts->cb.slot = 0; /* Blacklist C function that called the callback. */
|
||||
}
|
||||
|
||||
/* -- C callback management ----------------------------------------------- */
|
||||
|
||||
/* Get an unused slot in the callback slot table. */
|
||||
static MSize callback_slot_new(CTState *cts, CType *ct)
|
||||
{
|
||||
CTypeID id = ctype_typeid(cts, ct);
|
||||
CTypeID1 *cbid = cts->cb.cbid;
|
||||
MSize top;
|
||||
for (top = cts->cb.topid; top < cts->cb.sizeid; top++)
|
||||
if (LJ_LIKELY(cbid[top] == 0))
|
||||
goto found;
|
||||
#if CALLBACK_MAX_SLOT
|
||||
if (top >= CALLBACK_MAX_SLOT)
|
||||
#endif
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_CBACKOV);
|
||||
if (!cts->cb.mcode)
|
||||
callback_mcode_new(cts);
|
||||
lj_mem_growvec(cts->L, cbid, cts->cb.sizeid, CALLBACK_MAX_SLOT, CTypeID1);
|
||||
cts->cb.cbid = cbid;
|
||||
memset(cbid+top, 0, (cts->cb.sizeid-top)*sizeof(CTypeID1));
|
||||
found:
|
||||
cbid[top] = id;
|
||||
cts->cb.topid = top+1;
|
||||
return top;
|
||||
}
|
||||
|
||||
/* Check for function pointer and supported argument/result types. */
|
||||
static CType *callback_checkfunc(CTState *cts, CType *ct)
|
||||
{
|
||||
int narg = 0;
|
||||
if (!ctype_isptr(ct->info) || (LJ_64 && ct->size != CTSIZE_PTR))
|
||||
return NULL;
|
||||
ct = ctype_rawchild(cts, ct);
|
||||
if (ctype_isfunc(ct->info)) {
|
||||
CType *ctr = ctype_rawchild(cts, ct);
|
||||
CTypeID fid = ct->sib;
|
||||
if (!(ctype_isvoid(ctr->info) || ctype_isenum(ctr->info) ||
|
||||
ctype_isptr(ctr->info) || (ctype_isnum(ctr->info) && ctr->size <= 8)))
|
||||
return NULL;
|
||||
if ((ct->info & CTF_VARARG))
|
||||
return NULL;
|
||||
while (fid) {
|
||||
CType *ctf = ctype_get(cts, fid);
|
||||
if (!ctype_isattrib(ctf->info)) {
|
||||
CType *cta;
|
||||
lua_assert(ctype_isfield(ctf->info));
|
||||
cta = ctype_rawchild(cts, ctf);
|
||||
if (!(ctype_isenum(cta->info) || ctype_isptr(cta->info) ||
|
||||
(ctype_isnum(cta->info) && cta->size <= 8)) ||
|
||||
++narg >= LUA_MINSTACK-3)
|
||||
return NULL;
|
||||
}
|
||||
fid = ctf->sib;
|
||||
}
|
||||
return ct;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create a new callback and return the callback function pointer. */
|
||||
void *lj_ccallback_new(CTState *cts, CType *ct, GCfunc *fn)
|
||||
{
|
||||
ct = callback_checkfunc(cts, ct);
|
||||
if (ct) {
|
||||
MSize slot = callback_slot_new(cts, ct);
|
||||
GCtab *t = cts->miscmap;
|
||||
setfuncV(cts->L, lj_tab_setint(cts->L, t, (int32_t)slot), fn);
|
||||
lj_gc_anybarriert(cts->L, t);
|
||||
return callback_slot2ptr(cts, slot);
|
||||
}
|
||||
return NULL; /* Bad conversion. */
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
** FFI C callback handling.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CCALLBACK_H
|
||||
#define _LJ_CCALLBACK_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ctype.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
/* Really belongs to lj_vm.h. */
|
||||
LJ_ASMF void lj_vm_ffi_callback(void);
|
||||
|
||||
LJ_FUNC MSize lj_ccallback_ptr2slot(CTState *cts, void *p);
|
||||
LJ_FUNCA lua_State * LJ_FASTCALL lj_ccallback_enter(CTState *cts, void *cf);
|
||||
LJ_FUNCA void LJ_FASTCALL lj_ccallback_leave(CTState *cts, TValue *o);
|
||||
LJ_FUNC void *lj_ccallback_new(CTState *cts, CType *ct, GCfunc *fn);
|
||||
LJ_FUNC void lj_ccallback_mcode_free(CTState *cts);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+751
@@ -0,0 +1,751 @@
|
||||
/*
|
||||
** C type conversions.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_err.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cdata.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_ccallback.h"
|
||||
|
||||
/* -- Conversion errors --------------------------------------------------- */
|
||||
|
||||
/* Bad conversion. */
|
||||
LJ_NORET static void cconv_err_conv(CTState *cts, CType *d, CType *s,
|
||||
CTInfo flags)
|
||||
{
|
||||
const char *dst = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, d), NULL));
|
||||
const char *src;
|
||||
if ((flags & CCF_FROMTV))
|
||||
src = lj_obj_typename[1+(ctype_isnum(s->info) ? LUA_TNUMBER :
|
||||
ctype_isarray(s->info) ? LUA_TSTRING : LUA_TNIL)];
|
||||
else
|
||||
src = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, s), NULL));
|
||||
if (CCF_GETARG(flags))
|
||||
lj_err_argv(cts->L, CCF_GETARG(flags), LJ_ERR_FFI_BADCONV, src, dst);
|
||||
else
|
||||
lj_err_callerv(cts->L, LJ_ERR_FFI_BADCONV, src, dst);
|
||||
}
|
||||
|
||||
/* Bad conversion from TValue. */
|
||||
LJ_NORET static void cconv_err_convtv(CTState *cts, CType *d, TValue *o,
|
||||
CTInfo flags)
|
||||
{
|
||||
const char *dst = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, d), NULL));
|
||||
const char *src = lj_typename(o);
|
||||
if (CCF_GETARG(flags))
|
||||
lj_err_argv(cts->L, CCF_GETARG(flags), LJ_ERR_FFI_BADCONV, src, dst);
|
||||
else
|
||||
lj_err_callerv(cts->L, LJ_ERR_FFI_BADCONV, src, dst);
|
||||
}
|
||||
|
||||
/* Initializer overflow. */
|
||||
LJ_NORET static void cconv_err_initov(CTState *cts, CType *d)
|
||||
{
|
||||
const char *dst = strdata(lj_ctype_repr(cts->L, ctype_typeid(cts, d), NULL));
|
||||
lj_err_callerv(cts->L, LJ_ERR_FFI_INITOV, dst);
|
||||
}
|
||||
|
||||
/* -- C type compatibility checks ----------------------------------------- */
|
||||
|
||||
/* Get raw type and qualifiers for a child type. Resolves enums, too. */
|
||||
static CType *cconv_childqual(CTState *cts, CType *ct, CTInfo *qual)
|
||||
{
|
||||
ct = ctype_child(cts, ct);
|
||||
for (;;) {
|
||||
if (ctype_isattrib(ct->info)) {
|
||||
if (ctype_attrib(ct->info) == CTA_QUAL) *qual |= ct->size;
|
||||
} else if (!ctype_isenum(ct->info)) {
|
||||
break;
|
||||
}
|
||||
ct = ctype_child(cts, ct);
|
||||
}
|
||||
*qual |= (ct->info & CTF_QUAL);
|
||||
return ct;
|
||||
}
|
||||
|
||||
/* Check for compatible types when converting to a pointer.
|
||||
** Note: these checks are more relaxed than what C99 mandates.
|
||||
*/
|
||||
int lj_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags)
|
||||
{
|
||||
if (!((flags & CCF_CAST) || d == s)) {
|
||||
CTInfo dqual = 0, squal = 0;
|
||||
d = cconv_childqual(cts, d, &dqual);
|
||||
if (!ctype_isstruct(s->info))
|
||||
s = cconv_childqual(cts, s, &squal);
|
||||
if ((flags & CCF_SAME)) {
|
||||
if (dqual != squal)
|
||||
return 0; /* Different qualifiers. */
|
||||
} else if (!(flags & CCF_IGNQUAL)) {
|
||||
if ((dqual & squal) != squal)
|
||||
return 0; /* Discarded qualifiers. */
|
||||
if (ctype_isvoid(d->info) || ctype_isvoid(s->info))
|
||||
return 1; /* Converting to/from void * is always ok. */
|
||||
}
|
||||
if (ctype_type(d->info) != ctype_type(s->info) ||
|
||||
d->size != s->size)
|
||||
return 0; /* Different type or different size. */
|
||||
if (ctype_isnum(d->info)) {
|
||||
if (((d->info ^ s->info) & (CTF_BOOL|CTF_FP)))
|
||||
return 0; /* Different numeric types. */
|
||||
} else if (ctype_ispointer(d->info)) {
|
||||
/* Check child types for compatibility. */
|
||||
return lj_cconv_compatptr(cts, d, s, flags|CCF_SAME);
|
||||
} else if (ctype_isstruct(d->info)) {
|
||||
if (d != s)
|
||||
return 0; /* Must be exact same type for struct/union. */
|
||||
} else if (ctype_isfunc(d->info)) {
|
||||
/* NYI: structural equality of functions. */
|
||||
}
|
||||
}
|
||||
return 1; /* Types are compatible. */
|
||||
}
|
||||
|
||||
/* -- C type to C type conversion ----------------------------------------- */
|
||||
|
||||
/* Convert C type to C type. Caveat: expects to get the raw CType!
|
||||
**
|
||||
** Note: This is only used by the interpreter and not optimized at all.
|
||||
** The JIT compiler will do a much better job specializing for each case.
|
||||
*/
|
||||
void lj_cconv_ct_ct(CTState *cts, CType *d, CType *s,
|
||||
uint8_t *dp, uint8_t *sp, CTInfo flags)
|
||||
{
|
||||
CTSize dsize = d->size, ssize = s->size;
|
||||
CTInfo dinfo = d->info, sinfo = s->info;
|
||||
void *tmpptr;
|
||||
|
||||
lua_assert(!ctype_isenum(dinfo) && !ctype_isenum(sinfo));
|
||||
lua_assert(!ctype_isattrib(dinfo) && !ctype_isattrib(sinfo));
|
||||
|
||||
if (ctype_type(dinfo) > CT_MAYCONVERT || ctype_type(sinfo) > CT_MAYCONVERT)
|
||||
goto err_conv;
|
||||
|
||||
/* Some basic sanity checks. */
|
||||
lua_assert(!ctype_isnum(dinfo) || dsize > 0);
|
||||
lua_assert(!ctype_isnum(sinfo) || ssize > 0);
|
||||
lua_assert(!ctype_isbool(dinfo) || dsize == 1 || dsize == 4);
|
||||
lua_assert(!ctype_isbool(sinfo) || ssize == 1 || ssize == 4);
|
||||
lua_assert(!ctype_isinteger(dinfo) || (1u<<lj_fls(dsize)) == dsize);
|
||||
lua_assert(!ctype_isinteger(sinfo) || (1u<<lj_fls(ssize)) == ssize);
|
||||
|
||||
switch (cconv_idx2(dinfo, sinfo)) {
|
||||
/* Destination is a bool. */
|
||||
case CCX(B, B):
|
||||
/* Source operand is already normalized. */
|
||||
if (dsize == 1) *dp = *sp; else *(int *)dp = *sp;
|
||||
break;
|
||||
case CCX(B, I): {
|
||||
MSize i;
|
||||
uint8_t b = 0;
|
||||
for (i = 0; i < ssize; i++) b |= sp[i];
|
||||
b = (b != 0);
|
||||
if (dsize == 1) *dp = b; else *(int *)dp = b;
|
||||
break;
|
||||
}
|
||||
case CCX(B, F): {
|
||||
uint8_t b;
|
||||
if (ssize == sizeof(double)) b = (*(double *)sp != 0);
|
||||
else if (ssize == sizeof(float)) b = (*(float *)sp != 0);
|
||||
else goto err_conv; /* NYI: long double. */
|
||||
if (dsize == 1) *dp = b; else *(int *)dp = b;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Destination is an integer. */
|
||||
case CCX(I, B):
|
||||
case CCX(I, I):
|
||||
conv_I_I:
|
||||
if (dsize > ssize) { /* Zero-extend or sign-extend LSB. */
|
||||
#if LJ_LE
|
||||
uint8_t fill = (!(sinfo & CTF_UNSIGNED) && (sp[ssize-1]&0x80)) ? 0xff : 0;
|
||||
memcpy(dp, sp, ssize);
|
||||
memset(dp + ssize, fill, dsize-ssize);
|
||||
#else
|
||||
uint8_t fill = (!(sinfo & CTF_UNSIGNED) && (sp[0]&0x80)) ? 0xff : 0;
|
||||
memset(dp, fill, dsize-ssize);
|
||||
memcpy(dp + (dsize-ssize), sp, ssize);
|
||||
#endif
|
||||
} else { /* Copy LSB. */
|
||||
#if LJ_LE
|
||||
memcpy(dp, sp, dsize);
|
||||
#else
|
||||
memcpy(dp, sp + (ssize-dsize), dsize);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case CCX(I, F): {
|
||||
double n; /* Always convert via double. */
|
||||
conv_I_F:
|
||||
/* Convert source to double. */
|
||||
if (ssize == sizeof(double)) n = *(double *)sp;
|
||||
else if (ssize == sizeof(float)) n = (double)*(float *)sp;
|
||||
else goto err_conv; /* NYI: long double. */
|
||||
/* Then convert double to integer. */
|
||||
/* The conversion must exactly match the semantics of JIT-compiled code! */
|
||||
if (dsize < 4 || (dsize == 4 && !(dinfo & CTF_UNSIGNED))) {
|
||||
int32_t i = (int32_t)n;
|
||||
if (dsize == 4) *(int32_t *)dp = i;
|
||||
else if (dsize == 2) *(int16_t *)dp = (int16_t)i;
|
||||
else *(int8_t *)dp = (int8_t)i;
|
||||
} else if (dsize == 4) {
|
||||
*(uint32_t *)dp = (uint32_t)n;
|
||||
} else if (dsize == 8) {
|
||||
if (!(dinfo & CTF_UNSIGNED))
|
||||
*(int64_t *)dp = (int64_t)n;
|
||||
else
|
||||
*(uint64_t *)dp = lj_num2u64(n);
|
||||
} else {
|
||||
goto err_conv; /* NYI: conversion to >64 bit integers. */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CCX(I, C):
|
||||
s = ctype_child(cts, s);
|
||||
sinfo = s->info;
|
||||
ssize = s->size;
|
||||
goto conv_I_F; /* Just convert re. */
|
||||
case CCX(I, P):
|
||||
if (!(flags & CCF_CAST)) goto err_conv;
|
||||
sinfo = CTINFO(CT_NUM, CTF_UNSIGNED);
|
||||
goto conv_I_I;
|
||||
case CCX(I, A):
|
||||
if (!(flags & CCF_CAST)) goto err_conv;
|
||||
sinfo = CTINFO(CT_NUM, CTF_UNSIGNED);
|
||||
ssize = CTSIZE_PTR;
|
||||
tmpptr = sp;
|
||||
sp = (uint8_t *)&tmpptr;
|
||||
goto conv_I_I;
|
||||
|
||||
/* Destination is a floating-point number. */
|
||||
case CCX(F, B):
|
||||
case CCX(F, I): {
|
||||
double n; /* Always convert via double. */
|
||||
conv_F_I:
|
||||
/* First convert source to double. */
|
||||
/* The conversion must exactly match the semantics of JIT-compiled code! */
|
||||
if (ssize < 4 || (ssize == 4 && !(sinfo & CTF_UNSIGNED))) {
|
||||
int32_t i;
|
||||
if (ssize == 4) {
|
||||
i = *(int32_t *)sp;
|
||||
} else if (!(sinfo & CTF_UNSIGNED)) {
|
||||
if (ssize == 2) i = *(int16_t *)sp;
|
||||
else i = *(int8_t *)sp;
|
||||
} else {
|
||||
if (ssize == 2) i = *(uint16_t *)sp;
|
||||
else i = *(uint8_t *)sp;
|
||||
}
|
||||
n = (double)i;
|
||||
} else if (ssize == 4) {
|
||||
n = (double)*(uint32_t *)sp;
|
||||
} else if (ssize == 8) {
|
||||
if (!(sinfo & CTF_UNSIGNED)) n = (double)*(int64_t *)sp;
|
||||
else n = (double)*(uint64_t *)sp;
|
||||
} else {
|
||||
goto err_conv; /* NYI: conversion from >64 bit integers. */
|
||||
}
|
||||
/* Convert double to destination. */
|
||||
if (dsize == sizeof(double)) *(double *)dp = n;
|
||||
else if (dsize == sizeof(float)) *(float *)dp = (float)n;
|
||||
else goto err_conv; /* NYI: long double. */
|
||||
break;
|
||||
}
|
||||
case CCX(F, F): {
|
||||
double n; /* Always convert via double. */
|
||||
conv_F_F:
|
||||
if (ssize == dsize) goto copyval;
|
||||
/* Convert source to double. */
|
||||
if (ssize == sizeof(double)) n = *(double *)sp;
|
||||
else if (ssize == sizeof(float)) n = (double)*(float *)sp;
|
||||
else goto err_conv; /* NYI: long double. */
|
||||
/* Convert double to destination. */
|
||||
if (dsize == sizeof(double)) *(double *)dp = n;
|
||||
else if (dsize == sizeof(float)) *(float *)dp = (float)n;
|
||||
else goto err_conv; /* NYI: long double. */
|
||||
break;
|
||||
}
|
||||
case CCX(F, C):
|
||||
s = ctype_child(cts, s);
|
||||
sinfo = s->info;
|
||||
ssize = s->size;
|
||||
goto conv_F_F; /* Ignore im, and convert from re. */
|
||||
|
||||
/* Destination is a complex number. */
|
||||
case CCX(C, I):
|
||||
d = ctype_child(cts, d);
|
||||
dinfo = d->info;
|
||||
dsize = d->size;
|
||||
memset(dp + dsize, 0, dsize); /* Clear im. */
|
||||
goto conv_F_I; /* Convert to re. */
|
||||
case CCX(C, F):
|
||||
d = ctype_child(cts, d);
|
||||
dinfo = d->info;
|
||||
dsize = d->size;
|
||||
memset(dp + dsize, 0, dsize); /* Clear im. */
|
||||
goto conv_F_F; /* Convert to re. */
|
||||
|
||||
case CCX(C, C):
|
||||
if (dsize != ssize) { /* Different types: convert re/im separately. */
|
||||
CType *dc = ctype_child(cts, d);
|
||||
CType *sc = ctype_child(cts, s);
|
||||
lj_cconv_ct_ct(cts, dc, sc, dp, sp, flags);
|
||||
lj_cconv_ct_ct(cts, dc, sc, dp + dc->size, sp + sc->size, flags);
|
||||
return;
|
||||
}
|
||||
goto copyval; /* Otherwise this is easy. */
|
||||
|
||||
/* Destination is a vector. */
|
||||
case CCX(V, I):
|
||||
case CCX(V, F):
|
||||
case CCX(V, C): {
|
||||
CType *dc = ctype_child(cts, d);
|
||||
CTSize esize;
|
||||
/* First convert the scalar to the first element. */
|
||||
lj_cconv_ct_ct(cts, dc, s, dp, sp, flags);
|
||||
/* Then replicate it to the other elements (splat). */
|
||||
for (sp = dp, esize = dc->size; dsize > esize; dsize -= esize) {
|
||||
dp += esize;
|
||||
memcpy(dp, sp, esize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CCX(V, V):
|
||||
/* Copy same-sized vectors, even for different lengths/element-types. */
|
||||
if (dsize != ssize) goto err_conv;
|
||||
goto copyval;
|
||||
|
||||
/* Destination is a pointer. */
|
||||
case CCX(P, I):
|
||||
if (!(flags & CCF_CAST)) goto err_conv;
|
||||
dinfo = CTINFO(CT_NUM, CTF_UNSIGNED);
|
||||
goto conv_I_I;
|
||||
|
||||
case CCX(P, F):
|
||||
if (!(flags & CCF_CAST) || !(flags & CCF_FROMTV)) goto err_conv;
|
||||
/* The signed conversion is cheaper. x64 really has 47 bit pointers. */
|
||||
dinfo = CTINFO(CT_NUM, (LJ_64 && dsize == 8) ? 0 : CTF_UNSIGNED);
|
||||
goto conv_I_F;
|
||||
|
||||
case CCX(P, P):
|
||||
if (!lj_cconv_compatptr(cts, d, s, flags)) goto err_conv;
|
||||
cdata_setptr(dp, dsize, cdata_getptr(sp, ssize));
|
||||
break;
|
||||
|
||||
case CCX(P, A):
|
||||
case CCX(P, S):
|
||||
if (!lj_cconv_compatptr(cts, d, s, flags)) goto err_conv;
|
||||
cdata_setptr(dp, dsize, sp);
|
||||
break;
|
||||
|
||||
/* Destination is an array. */
|
||||
case CCX(A, A):
|
||||
if ((flags & CCF_CAST) || (d->info & CTF_VLA) || dsize != ssize ||
|
||||
d->size == CTSIZE_INVALID || !lj_cconv_compatptr(cts, d, s, flags))
|
||||
goto err_conv;
|
||||
goto copyval;
|
||||
|
||||
/* Destination is a struct/union. */
|
||||
case CCX(S, S):
|
||||
if ((flags & CCF_CAST) || (d->info & CTF_VLA) || d != s)
|
||||
goto err_conv; /* Must be exact same type. */
|
||||
copyval: /* Copy value. */
|
||||
lua_assert(dsize == ssize);
|
||||
memcpy(dp, sp, dsize);
|
||||
break;
|
||||
|
||||
default:
|
||||
err_conv:
|
||||
cconv_err_conv(cts, d, s, flags);
|
||||
}
|
||||
}
|
||||
|
||||
/* -- C type to TValue conversion ----------------------------------------- */
|
||||
|
||||
/* Convert C type to TValue. Caveat: expects to get the raw CType! */
|
||||
int lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid,
|
||||
TValue *o, uint8_t *sp)
|
||||
{
|
||||
CTInfo sinfo = s->info;
|
||||
if (ctype_isnum(sinfo)) {
|
||||
if (!ctype_isbool(sinfo)) {
|
||||
if (ctype_isinteger(sinfo) && s->size > 4) goto copyval;
|
||||
if (LJ_DUALNUM && ctype_isinteger(sinfo)) {
|
||||
int32_t i;
|
||||
lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT32), s,
|
||||
(uint8_t *)&i, sp, 0);
|
||||
if ((sinfo & CTF_UNSIGNED) && i < 0)
|
||||
setnumV(o, (lua_Number)(uint32_t)i);
|
||||
else
|
||||
setintV(o, i);
|
||||
} else {
|
||||
lj_cconv_ct_ct(cts, ctype_get(cts, CTID_DOUBLE), s,
|
||||
(uint8_t *)&o->n, sp, 0);
|
||||
/* Numbers are NOT canonicalized here! Beware of uninitialized data. */
|
||||
lua_assert(tvisnum(o));
|
||||
}
|
||||
} else {
|
||||
uint32_t b = s->size == 1 ? (*sp != 0) : (*(int *)sp != 0);
|
||||
setboolV(o, b);
|
||||
setboolV(&cts->g->tmptv2, b); /* Remember for trace recorder. */
|
||||
}
|
||||
return 0;
|
||||
} else if (ctype_isrefarray(sinfo) || ctype_isstruct(sinfo)) {
|
||||
/* Create reference. */
|
||||
setcdataV(cts->L, o, lj_cdata_newref(cts, sp, sid));
|
||||
return 1; /* Need GC step. */
|
||||
} else {
|
||||
GCcdata *cd;
|
||||
CTSize sz;
|
||||
copyval: /* Copy value. */
|
||||
sz = s->size;
|
||||
lua_assert(sz != CTSIZE_INVALID);
|
||||
/* Attributes are stripped, qualifiers are kept (but mostly ignored). */
|
||||
cd = lj_cdata_new(cts, ctype_typeid(cts, s), sz);
|
||||
setcdataV(cts->L, o, cd);
|
||||
memcpy(cdataptr(cd), sp, sz);
|
||||
return 1; /* Need GC step. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert bitfield to TValue. */
|
||||
int lj_cconv_tv_bf(CTState *cts, CType *s, TValue *o, uint8_t *sp)
|
||||
{
|
||||
CTInfo info = s->info;
|
||||
CTSize pos, bsz;
|
||||
uint32_t val;
|
||||
lua_assert(ctype_isbitfield(info));
|
||||
/* NYI: packed bitfields may cause misaligned reads. */
|
||||
switch (ctype_bitcsz(info)) {
|
||||
case 4: val = *(uint32_t *)sp; break;
|
||||
case 2: val = *(uint16_t *)sp; break;
|
||||
case 1: val = *(uint8_t *)sp; break;
|
||||
default: lua_assert(0); val = 0; break;
|
||||
}
|
||||
/* Check if a packed bitfield crosses a container boundary. */
|
||||
pos = ctype_bitpos(info);
|
||||
bsz = ctype_bitbsz(info);
|
||||
lua_assert(pos < 8*ctype_bitcsz(info));
|
||||
lua_assert(bsz > 0 && bsz <= 8*ctype_bitcsz(info));
|
||||
if (pos + bsz > 8*ctype_bitcsz(info))
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_NYIPACKBIT);
|
||||
if (!(info & CTF_BOOL)) {
|
||||
CTSize shift = 32 - bsz;
|
||||
if (!(info & CTF_UNSIGNED)) {
|
||||
setintV(o, (int32_t)(val << (shift-pos)) >> shift);
|
||||
} else {
|
||||
val = (val << (shift-pos)) >> shift;
|
||||
if (!LJ_DUALNUM || (int32_t)val < 0)
|
||||
setnumV(o, (lua_Number)(uint32_t)val);
|
||||
else
|
||||
setintV(o, (int32_t)val);
|
||||
}
|
||||
} else {
|
||||
lua_assert(bsz == 1);
|
||||
setboolV(o, (val >> pos) & 1);
|
||||
}
|
||||
return 0; /* No GC step needed. */
|
||||
}
|
||||
|
||||
/* -- TValue to C type conversion ----------------------------------------- */
|
||||
|
||||
/* Convert table to array. */
|
||||
static void cconv_array_tab(CTState *cts, CType *d,
|
||||
uint8_t *dp, GCtab *t, CTInfo flags)
|
||||
{
|
||||
int32_t i;
|
||||
CType *dc = ctype_rawchild(cts, d); /* Array element type. */
|
||||
CTSize size = d->size, esize = dc->size, ofs = 0;
|
||||
for (i = 0; ; i++) {
|
||||
TValue *tv = (TValue *)lj_tab_getint(t, i);
|
||||
if (!tv || tvisnil(tv)) {
|
||||
if (i == 0) continue; /* Try again for 1-based tables. */
|
||||
break; /* Stop at first nil. */
|
||||
}
|
||||
if (ofs >= size)
|
||||
cconv_err_initov(cts, d);
|
||||
lj_cconv_ct_tv(cts, dc, dp + ofs, tv, flags);
|
||||
ofs += esize;
|
||||
}
|
||||
if (size != CTSIZE_INVALID) { /* Only fill up arrays with known size. */
|
||||
if (ofs == esize) { /* Replicate a single element. */
|
||||
for (; ofs < size; ofs += esize) memcpy(dp + ofs, dp, esize);
|
||||
} else { /* Otherwise fill the remainder with zero. */
|
||||
memset(dp + ofs, 0, size - ofs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert table to sub-struct/union. */
|
||||
static void cconv_substruct_tab(CTState *cts, CType *d, uint8_t *dp,
|
||||
GCtab *t, int32_t *ip, CTInfo flags)
|
||||
{
|
||||
CTypeID id = d->sib;
|
||||
while (id) {
|
||||
CType *df = ctype_get(cts, id);
|
||||
id = df->sib;
|
||||
if (ctype_isfield(df->info) || ctype_isbitfield(df->info)) {
|
||||
TValue *tv;
|
||||
int32_t i = *ip, iz = i;
|
||||
if (!gcref(df->name)) continue; /* Ignore unnamed fields. */
|
||||
if (i >= 0) {
|
||||
retry:
|
||||
tv = (TValue *)lj_tab_getint(t, i);
|
||||
if (!tv || tvisnil(tv)) {
|
||||
if (i == 0) { i = 1; goto retry; } /* 1-based tables. */
|
||||
if (iz == 0) { *ip = i = -1; goto tryname; } /* Init named fields. */
|
||||
break; /* Stop at first nil. */
|
||||
}
|
||||
*ip = i + 1;
|
||||
} else {
|
||||
tryname:
|
||||
tv = (TValue *)lj_tab_getstr(t, gco2str(gcref(df->name)));
|
||||
if (!tv || tvisnil(tv)) continue;
|
||||
}
|
||||
if (ctype_isfield(df->info))
|
||||
lj_cconv_ct_tv(cts, ctype_rawchild(cts, df), dp+df->size, tv, flags);
|
||||
else
|
||||
lj_cconv_bf_tv(cts, df, dp+df->size, tv);
|
||||
if ((d->info & CTF_UNION)) break;
|
||||
} else if (ctype_isxattrib(df->info, CTA_SUBTYPE)) {
|
||||
cconv_substruct_tab(cts, ctype_rawchild(cts, df),
|
||||
dp+df->size, t, ip, flags);
|
||||
} /* Ignore all other entries in the chain. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert table to struct/union. */
|
||||
static void cconv_struct_tab(CTState *cts, CType *d,
|
||||
uint8_t *dp, GCtab *t, CTInfo flags)
|
||||
{
|
||||
int32_t i = 0;
|
||||
memset(dp, 0, d->size); /* Much simpler to clear the struct first. */
|
||||
cconv_substruct_tab(cts, d, dp, t, &i, flags);
|
||||
}
|
||||
|
||||
/* Convert TValue to C type. Caveat: expects to get the raw CType! */
|
||||
void lj_cconv_ct_tv(CTState *cts, CType *d,
|
||||
uint8_t *dp, TValue *o, CTInfo flags)
|
||||
{
|
||||
CTypeID sid = CTID_P_VOID;
|
||||
CType *s;
|
||||
void *tmpptr;
|
||||
uint8_t tmpbool, *sp = (uint8_t *)&tmpptr;
|
||||
if (LJ_LIKELY(tvisint(o))) {
|
||||
sp = (uint8_t *)&o->i;
|
||||
sid = CTID_INT32;
|
||||
flags |= CCF_FROMTV;
|
||||
} else if (LJ_LIKELY(tvisnum(o))) {
|
||||
sp = (uint8_t *)&o->n;
|
||||
sid = CTID_DOUBLE;
|
||||
flags |= CCF_FROMTV;
|
||||
} else if (tviscdata(o)) {
|
||||
sp = cdataptr(cdataV(o));
|
||||
sid = cdataV(o)->ctypeid;
|
||||
s = ctype_get(cts, sid);
|
||||
if (ctype_isref(s->info)) { /* Resolve reference for value. */
|
||||
lua_assert(s->size == CTSIZE_PTR);
|
||||
sp = *(void **)sp;
|
||||
sid = ctype_cid(s->info);
|
||||
}
|
||||
s = ctype_raw(cts, sid);
|
||||
if (ctype_isfunc(s->info)) {
|
||||
sid = lj_ctype_intern(cts, CTINFO(CT_PTR, CTALIGN_PTR|sid), CTSIZE_PTR);
|
||||
} else {
|
||||
if (ctype_isenum(s->info)) s = ctype_child(cts, s);
|
||||
goto doconv;
|
||||
}
|
||||
} else if (tvisstr(o)) {
|
||||
GCstr *str = strV(o);
|
||||
if (ctype_isenum(d->info)) { /* Match string against enum constant. */
|
||||
CTSize ofs;
|
||||
CType *cct = lj_ctype_getfield(cts, d, str, &ofs);
|
||||
if (!cct || !ctype_isconstval(cct->info))
|
||||
goto err_conv;
|
||||
lua_assert(d->size == 4);
|
||||
sp = (uint8_t *)&cct->size;
|
||||
sid = ctype_cid(cct->info);
|
||||
} else if (ctype_isrefarray(d->info)) { /* Copy string to array. */
|
||||
CType *dc = ctype_rawchild(cts, d);
|
||||
CTSize sz = str->len+1;
|
||||
if (!ctype_isinteger(dc->info) || dc->size != 1)
|
||||
goto err_conv;
|
||||
if (d->size != 0 && d->size < sz)
|
||||
sz = d->size;
|
||||
memcpy(dp, strdata(str), sz);
|
||||
return;
|
||||
} else { /* Otherwise pass it as a const char[]. */
|
||||
sp = (uint8_t *)strdata(str);
|
||||
sid = CTID_A_CCHAR;
|
||||
flags |= CCF_FROMTV;
|
||||
}
|
||||
} else if (tvistab(o)) {
|
||||
if (ctype_isarray(d->info)) {
|
||||
cconv_array_tab(cts, d, dp, tabV(o), flags);
|
||||
return;
|
||||
} else if (ctype_isstruct(d->info)) {
|
||||
cconv_struct_tab(cts, d, dp, tabV(o), flags);
|
||||
return;
|
||||
} else {
|
||||
goto err_conv;
|
||||
}
|
||||
} else if (tvisbool(o)) {
|
||||
tmpbool = boolV(o);
|
||||
sp = &tmpbool;
|
||||
sid = CTID_BOOL;
|
||||
} else if (tvisnil(o)) {
|
||||
tmpptr = (void *)0;
|
||||
flags |= CCF_FROMTV;
|
||||
} else if (tvisudata(o)) {
|
||||
GCudata *ud = udataV(o);
|
||||
tmpptr = uddata(ud);
|
||||
if (ud->udtype == UDTYPE_IO_FILE)
|
||||
tmpptr = *(void **)tmpptr;
|
||||
} else if (tvislightud(o)) {
|
||||
tmpptr = lightudV(o);
|
||||
} else if (tvisfunc(o)) {
|
||||
void *p = lj_ccallback_new(cts, d, funcV(o));
|
||||
if (p) {
|
||||
*(void **)dp = p;
|
||||
return;
|
||||
}
|
||||
goto err_conv;
|
||||
} else {
|
||||
err_conv:
|
||||
cconv_err_convtv(cts, d, o, flags);
|
||||
}
|
||||
s = ctype_get(cts, sid);
|
||||
doconv:
|
||||
if (ctype_isenum(d->info)) d = ctype_child(cts, d);
|
||||
lj_cconv_ct_ct(cts, d, s, dp, sp, flags);
|
||||
}
|
||||
|
||||
/* Convert TValue to bitfield. */
|
||||
void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o)
|
||||
{
|
||||
CTInfo info = d->info;
|
||||
CTSize pos, bsz;
|
||||
uint32_t val, mask;
|
||||
lua_assert(ctype_isbitfield(info));
|
||||
if ((info & CTF_BOOL)) {
|
||||
uint8_t tmpbool;
|
||||
lua_assert(ctype_bitbsz(info) == 1);
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, CTID_BOOL), &tmpbool, o, 0);
|
||||
val = tmpbool;
|
||||
} else {
|
||||
CTypeID did = (info & CTF_UNSIGNED) ? CTID_UINT32 : CTID_INT32;
|
||||
lj_cconv_ct_tv(cts, ctype_get(cts, did), (uint8_t *)&val, o, 0);
|
||||
}
|
||||
pos = ctype_bitpos(info);
|
||||
bsz = ctype_bitbsz(info);
|
||||
lua_assert(pos < 8*ctype_bitcsz(info));
|
||||
lua_assert(bsz > 0 && bsz <= 8*ctype_bitcsz(info));
|
||||
/* Check if a packed bitfield crosses a container boundary. */
|
||||
if (pos + bsz > 8*ctype_bitcsz(info))
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_NYIPACKBIT);
|
||||
mask = ((1u << bsz) - 1u) << pos;
|
||||
val = (val << pos) & mask;
|
||||
/* NYI: packed bitfields may cause misaligned reads/writes. */
|
||||
switch (ctype_bitcsz(info)) {
|
||||
case 4: *(uint32_t *)dp = (*(uint32_t *)dp & ~mask) | (uint32_t)val; break;
|
||||
case 2: *(uint16_t *)dp = (*(uint16_t *)dp & ~mask) | (uint16_t)val; break;
|
||||
case 1: *(uint8_t *)dp = (*(uint8_t *)dp & ~mask) | (uint8_t)val; break;
|
||||
default: lua_assert(0); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- Initialize C type with TValues -------------------------------------- */
|
||||
|
||||
/* Initialize an array with TValues. */
|
||||
static void cconv_array_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp,
|
||||
TValue *o, MSize len)
|
||||
{
|
||||
CType *dc = ctype_rawchild(cts, d); /* Array element type. */
|
||||
CTSize ofs, esize = dc->size;
|
||||
MSize i;
|
||||
if (len*esize > sz)
|
||||
cconv_err_initov(cts, d);
|
||||
for (i = 0, ofs = 0; i < len; i++, ofs += esize)
|
||||
lj_cconv_ct_tv(cts, dc, dp + ofs, o + i, 0);
|
||||
if (ofs == esize) { /* Replicate a single element. */
|
||||
for (; ofs < sz; ofs += esize) memcpy(dp + ofs, dp, esize);
|
||||
} else { /* Otherwise fill the remainder with zero. */
|
||||
memset(dp + ofs, 0, sz - ofs);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a sub-struct/union with TValues. */
|
||||
static void cconv_substruct_init(CTState *cts, CType *d, uint8_t *dp,
|
||||
TValue *o, MSize len, MSize *ip)
|
||||
{
|
||||
CTypeID id = d->sib;
|
||||
while (id) {
|
||||
CType *df = ctype_get(cts, id);
|
||||
id = df->sib;
|
||||
if (ctype_isfield(df->info) || ctype_isbitfield(df->info)) {
|
||||
MSize i = *ip;
|
||||
if (!gcref(df->name)) continue; /* Ignore unnamed fields. */
|
||||
if (i >= len) break;
|
||||
*ip = i + 1;
|
||||
if (ctype_isfield(df->info))
|
||||
lj_cconv_ct_tv(cts, ctype_rawchild(cts, df), dp+df->size, o + i, 0);
|
||||
else
|
||||
lj_cconv_bf_tv(cts, df, dp+df->size, o + i);
|
||||
if ((d->info & CTF_UNION)) break;
|
||||
} else if (ctype_isxattrib(df->info, CTA_SUBTYPE)) {
|
||||
cconv_substruct_init(cts, ctype_rawchild(cts, df),
|
||||
dp+df->size, o, len, ip);
|
||||
} /* Ignore all other entries in the chain. */
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a struct/union with TValues. */
|
||||
static void cconv_struct_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp,
|
||||
TValue *o, MSize len)
|
||||
{
|
||||
MSize i = 0;
|
||||
memset(dp, 0, sz); /* Much simpler to clear the struct first. */
|
||||
cconv_substruct_init(cts, d, dp, o, len, &i);
|
||||
if (i < len)
|
||||
cconv_err_initov(cts, d);
|
||||
}
|
||||
|
||||
/* Check whether to use a multi-value initializer.
|
||||
** This is true if an aggregate is to be initialized with a value.
|
||||
** Valarrays are treated as values here so ct_tv handles (V|C, I|F).
|
||||
*/
|
||||
int lj_cconv_multi_init(CTState *cts, CType *d, TValue *o)
|
||||
{
|
||||
if (!(ctype_isrefarray(d->info) || ctype_isstruct(d->info)))
|
||||
return 0; /* Destination is not an aggregate. */
|
||||
if (tvistab(o) || (tvisstr(o) && !ctype_isstruct(d->info)))
|
||||
return 0; /* Initializer is not a value. */
|
||||
if (tviscdata(o) && lj_ctype_rawref(cts, cdataV(o)->ctypeid) == d)
|
||||
return 0; /* Source and destination are identical aggregates. */
|
||||
return 1; /* Otherwise the initializer is a value. */
|
||||
}
|
||||
|
||||
/* Initialize C type with TValues. Caveat: expects to get the raw CType! */
|
||||
void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz,
|
||||
uint8_t *dp, TValue *o, MSize len)
|
||||
{
|
||||
if (len == 0)
|
||||
memset(dp, 0, sz);
|
||||
else if (len == 1 && !lj_cconv_multi_init(cts, d, o))
|
||||
lj_cconv_ct_tv(cts, d, dp, o, 0);
|
||||
else if (ctype_isarray(d->info)) /* Also handles valarray init with len>1. */
|
||||
cconv_array_init(cts, d, sz, dp, o, len);
|
||||
else if (ctype_isstruct(d->info))
|
||||
cconv_struct_init(cts, d, sz, dp, o, len);
|
||||
else
|
||||
cconv_err_initov(cts, d);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
** C type conversions.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CCONV_H
|
||||
#define _LJ_CCONV_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_ctype.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
/* Compressed C type index. ORDER CCX. */
|
||||
enum {
|
||||
CCX_B, /* Bool. */
|
||||
CCX_I, /* Integer. */
|
||||
CCX_F, /* Floating-point number. */
|
||||
CCX_C, /* Complex. */
|
||||
CCX_V, /* Vector. */
|
||||
CCX_P, /* Pointer. */
|
||||
CCX_A, /* Refarray. */
|
||||
CCX_S /* Struct/union. */
|
||||
};
|
||||
|
||||
/* Convert C type info to compressed C type index. ORDER CT. ORDER CCX. */
|
||||
static LJ_AINLINE uint32_t cconv_idx(CTInfo info)
|
||||
{
|
||||
uint32_t idx = ((info >> 26) & 15u); /* Dispatch bits. */
|
||||
lua_assert(ctype_type(info) <= CT_MAYCONVERT);
|
||||
#if LJ_64
|
||||
idx = ((U64x(f436fff5,fff7f021) >> 4*idx) & 15u);
|
||||
#else
|
||||
idx = (((idx < 8 ? 0xfff7f021u : 0xf436fff5) >> 4*(idx & 7u)) & 15u);
|
||||
#endif
|
||||
lua_assert(idx < 8);
|
||||
return idx;
|
||||
}
|
||||
|
||||
#define cconv_idx2(dinfo, sinfo) \
|
||||
((cconv_idx((dinfo)) << 3) + cconv_idx((sinfo)))
|
||||
|
||||
#define CCX(dst, src) ((CCX_##dst << 3) + CCX_##src)
|
||||
|
||||
/* Conversion flags. */
|
||||
#define CCF_CAST 0x00000001u
|
||||
#define CCF_FROMTV 0x00000002u
|
||||
#define CCF_SAME 0x00000004u
|
||||
#define CCF_IGNQUAL 0x00000008u
|
||||
|
||||
#define CCF_ARG_SHIFT 8
|
||||
#define CCF_ARG(n) ((n) << CCF_ARG_SHIFT)
|
||||
#define CCF_GETARG(f) ((f) >> CCF_ARG_SHIFT)
|
||||
|
||||
LJ_FUNC int lj_cconv_compatptr(CTState *cts, CType *d, CType *s, CTInfo flags);
|
||||
LJ_FUNC void lj_cconv_ct_ct(CTState *cts, CType *d, CType *s,
|
||||
uint8_t *dp, uint8_t *sp, CTInfo flags);
|
||||
LJ_FUNC int lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid,
|
||||
TValue *o, uint8_t *sp);
|
||||
LJ_FUNC int lj_cconv_tv_bf(CTState *cts, CType *s, TValue *o, uint8_t *sp);
|
||||
LJ_FUNC void lj_cconv_ct_tv(CTState *cts, CType *d,
|
||||
uint8_t *dp, TValue *o, CTInfo flags);
|
||||
LJ_FUNC void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o);
|
||||
LJ_FUNC int lj_cconv_multi_init(CTState *cts, CType *d, TValue *o);
|
||||
LJ_FUNC void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz,
|
||||
uint8_t *dp, TValue *o, MSize len);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+285
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
** C data management.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#include "lj_obj.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
#include "lj_gc.h"
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cconv.h"
|
||||
#include "lj_cdata.h"
|
||||
|
||||
/* -- C data allocation --------------------------------------------------- */
|
||||
|
||||
/* Allocate a new C data object holding a reference to another object. */
|
||||
GCcdata *lj_cdata_newref(CTState *cts, const void *p, CTypeID id)
|
||||
{
|
||||
CTypeID refid = lj_ctype_intern(cts, CTINFO_REF(id), CTSIZE_PTR);
|
||||
GCcdata *cd = lj_cdata_new(cts, refid, CTSIZE_PTR);
|
||||
*(const void **)cdataptr(cd) = p;
|
||||
return cd;
|
||||
}
|
||||
|
||||
/* Allocate variable-sized or specially aligned C data object. */
|
||||
GCcdata *lj_cdata_newv(CTState *cts, CTypeID id, CTSize sz, CTSize align)
|
||||
{
|
||||
global_State *g;
|
||||
MSize extra = sizeof(GCcdataVar) + sizeof(GCcdata) +
|
||||
(align > CT_MEMALIGN ? (1u<<align) - (1u<<CT_MEMALIGN) : 0);
|
||||
char *p = lj_mem_newt(cts->L, extra + sz, char);
|
||||
uintptr_t adata = (uintptr_t)p + sizeof(GCcdataVar) + sizeof(GCcdata);
|
||||
uintptr_t almask = (1u << align) - 1u;
|
||||
GCcdata *cd = (GCcdata *)(((adata + almask) & ~almask) - sizeof(GCcdata));
|
||||
lua_assert((char *)cd - p < 65536);
|
||||
cdatav(cd)->offset = (uint16_t)((char *)cd - p);
|
||||
cdatav(cd)->extra = extra;
|
||||
cdatav(cd)->len = sz;
|
||||
g = cts->g;
|
||||
setgcrefr(cd->nextgc, g->gc.root);
|
||||
setgcref(g->gc.root, obj2gco(cd));
|
||||
newwhite(g, obj2gco(cd));
|
||||
cd->marked |= 0x80;
|
||||
cd->gct = ~LJ_TCDATA;
|
||||
cd->ctypeid = id;
|
||||
return cd;
|
||||
}
|
||||
|
||||
/* Free a C data object. */
|
||||
void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd)
|
||||
{
|
||||
if (LJ_UNLIKELY(cd->marked & LJ_GC_CDATA_FIN)) {
|
||||
GCobj *root;
|
||||
makewhite(g, obj2gco(cd));
|
||||
markfinalized(obj2gco(cd));
|
||||
if ((root = gcref(g->gc.mmudata)) != NULL) {
|
||||
setgcrefr(cd->nextgc, root->gch.nextgc);
|
||||
setgcref(root->gch.nextgc, obj2gco(cd));
|
||||
setgcref(g->gc.mmudata, obj2gco(cd));
|
||||
} else {
|
||||
setgcref(cd->nextgc, obj2gco(cd));
|
||||
setgcref(g->gc.mmudata, obj2gco(cd));
|
||||
}
|
||||
} else if (LJ_LIKELY(!cdataisv(cd))) {
|
||||
CType *ct = ctype_raw(ctype_ctsG(g), cd->ctypeid);
|
||||
CTSize sz = ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR;
|
||||
lua_assert(ctype_hassize(ct->info) || ctype_isfunc(ct->info) ||
|
||||
ctype_isextern(ct->info));
|
||||
lj_mem_free(g, cd, sizeof(GCcdata) + sz);
|
||||
} else {
|
||||
lj_mem_free(g, memcdatav(cd), sizecdatav(cd));
|
||||
}
|
||||
}
|
||||
|
||||
TValue * LJ_FASTCALL lj_cdata_setfin(lua_State *L, GCcdata *cd)
|
||||
{
|
||||
global_State *g = G(L);
|
||||
GCtab *t = ctype_ctsG(g)->finalizer;
|
||||
if (gcref(t->metatable)) {
|
||||
/* Add cdata to finalizer table, if still enabled. */
|
||||
TValue *tv, tmp;
|
||||
setcdataV(L, &tmp, cd);
|
||||
lj_gc_anybarriert(L, t);
|
||||
tv = lj_tab_set(L, t, &tmp);
|
||||
cd->marked |= LJ_GC_CDATA_FIN;
|
||||
return tv;
|
||||
} else {
|
||||
/* Otherwise return dummy TValue. */
|
||||
return &g->tmptv;
|
||||
}
|
||||
}
|
||||
|
||||
/* -- C data indexing ----------------------------------------------------- */
|
||||
|
||||
/* Index C data by a TValue. Return CType and pointer. */
|
||||
CType *lj_cdata_index(CTState *cts, GCcdata *cd, cTValue *key, uint8_t **pp,
|
||||
CTInfo *qual)
|
||||
{
|
||||
uint8_t *p = (uint8_t *)cdataptr(cd);
|
||||
CType *ct = ctype_get(cts, cd->ctypeid);
|
||||
ptrdiff_t idx;
|
||||
|
||||
/* Resolve reference for cdata object. */
|
||||
if (ctype_isref(ct->info)) {
|
||||
lua_assert(ct->size == CTSIZE_PTR);
|
||||
p = *(uint8_t **)p;
|
||||
ct = ctype_child(cts, ct);
|
||||
}
|
||||
|
||||
collect_attrib:
|
||||
/* Skip attributes and collect qualifiers. */
|
||||
while (ctype_isattrib(ct->info)) {
|
||||
if (ctype_attrib(ct->info) == CTA_QUAL) *qual |= ct->size;
|
||||
ct = ctype_child(cts, ct);
|
||||
}
|
||||
lua_assert(!ctype_isref(ct->info)); /* Interning rejects refs to refs. */
|
||||
|
||||
if (tvisint(key)) {
|
||||
idx = (ptrdiff_t)intV(key);
|
||||
goto integer_key;
|
||||
} else if (tvisnum(key)) { /* Numeric key. */
|
||||
idx = LJ_64 ? (ptrdiff_t)numV(key) : (ptrdiff_t)lj_num2int(numV(key));
|
||||
integer_key:
|
||||
if (ctype_ispointer(ct->info)) {
|
||||
CTSize sz = lj_ctype_size(cts, ctype_cid(ct->info)); /* Element size. */
|
||||
if (sz != CTSIZE_INVALID) {
|
||||
if (ctype_isptr(ct->info)) {
|
||||
p = (uint8_t *)cdata_getptr(p, ct->size);
|
||||
} else if ((ct->info & (CTF_VECTOR|CTF_COMPLEX))) {
|
||||
if ((ct->info & CTF_COMPLEX)) idx &= 1;
|
||||
*qual |= CTF_CONST; /* Valarray elements are constant. */
|
||||
}
|
||||
*pp = p + idx*(int32_t)sz;
|
||||
return ct;
|
||||
}
|
||||
}
|
||||
} else if (tviscdata(key)) { /* Integer cdata key. */
|
||||
GCcdata *cdk = cdataV(key);
|
||||
CType *ctk = ctype_raw(cts, cdk->ctypeid);
|
||||
if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk);
|
||||
if (ctype_isinteger(ctk->info)) {
|
||||
lj_cconv_ct_ct(cts, ctype_get(cts, CTID_INT_PSZ), ctk,
|
||||
(uint8_t *)&idx, cdataptr(cdk), 0);
|
||||
goto integer_key;
|
||||
}
|
||||
} else if (tvisstr(key)) { /* String key. */
|
||||
GCstr *name = strV(key);
|
||||
if (ctype_isstruct(ct->info)) {
|
||||
CTSize ofs;
|
||||
CType *fct = lj_ctype_getfieldq(cts, ct, name, &ofs, qual);
|
||||
if (fct) {
|
||||
*pp = p + ofs;
|
||||
return fct;
|
||||
}
|
||||
} else if (ctype_iscomplex(ct->info)) {
|
||||
if (name->len == 2) {
|
||||
*qual |= CTF_CONST; /* Complex fields are constant. */
|
||||
if (strdata(name)[0] == 'r' && strdata(name)[1] == 'e') {
|
||||
*pp = p;
|
||||
return ct;
|
||||
} else if (strdata(name)[0] == 'i' && strdata(name)[1] == 'm') {
|
||||
*pp = p + (ct->size >> 1);
|
||||
return ct;
|
||||
}
|
||||
}
|
||||
} else if (cd->ctypeid == CTID_CTYPEID) {
|
||||
/* Allow indexing a (pointer to) struct constructor to get constants. */
|
||||
CType *sct = ctype_raw(cts, *(CTypeID *)p);
|
||||
if (ctype_isptr(sct->info))
|
||||
sct = ctype_rawchild(cts, sct);
|
||||
if (ctype_isstruct(sct->info)) {
|
||||
CTSize ofs;
|
||||
CType *fct = lj_ctype_getfield(cts, sct, name, &ofs);
|
||||
if (fct && ctype_isconstval(fct->info))
|
||||
return fct;
|
||||
}
|
||||
ct = sct; /* Allow resolving metamethods for constructors, too. */
|
||||
}
|
||||
}
|
||||
if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */
|
||||
if (ctype_isstruct(ctype_rawchild(cts, ct)->info)) {
|
||||
p = (uint8_t *)cdata_getptr(p, ct->size);
|
||||
ct = ctype_child(cts, ct);
|
||||
goto collect_attrib;
|
||||
}
|
||||
}
|
||||
*qual |= 1; /* Lookup failed. */
|
||||
return ct; /* But return the resolved raw type. */
|
||||
}
|
||||
|
||||
/* -- C data getters ------------------------------------------------------ */
|
||||
|
||||
/* Get constant value and convert to TValue. */
|
||||
static void cdata_getconst(CTState *cts, TValue *o, CType *ct)
|
||||
{
|
||||
CType *ctt = ctype_child(cts, ct);
|
||||
lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4);
|
||||
/* Constants are already zero-extended/sign-extended to 32 bits. */
|
||||
if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0)
|
||||
setnumV(o, (lua_Number)(uint32_t)ct->size);
|
||||
else
|
||||
setintV(o, (int32_t)ct->size);
|
||||
}
|
||||
|
||||
/* Get C data value and convert to TValue. */
|
||||
int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp)
|
||||
{
|
||||
CTypeID sid;
|
||||
|
||||
if (ctype_isconstval(s->info)) {
|
||||
cdata_getconst(cts, o, s);
|
||||
return 0; /* No GC step needed. */
|
||||
} else if (ctype_isbitfield(s->info)) {
|
||||
return lj_cconv_tv_bf(cts, s, o, sp);
|
||||
}
|
||||
|
||||
/* Get child type of pointer/array/field. */
|
||||
lua_assert(ctype_ispointer(s->info) || ctype_isfield(s->info));
|
||||
sid = ctype_cid(s->info);
|
||||
s = ctype_get(cts, sid);
|
||||
|
||||
/* Resolve reference for field. */
|
||||
if (ctype_isref(s->info)) {
|
||||
lua_assert(s->size == CTSIZE_PTR);
|
||||
sp = *(uint8_t **)sp;
|
||||
sid = ctype_cid(s->info);
|
||||
s = ctype_get(cts, sid);
|
||||
}
|
||||
|
||||
/* Skip attributes. */
|
||||
while (ctype_isattrib(s->info))
|
||||
s = ctype_child(cts, s);
|
||||
|
||||
return lj_cconv_tv_ct(cts, s, sid, o, sp);
|
||||
}
|
||||
|
||||
/* -- C data setters ------------------------------------------------------ */
|
||||
|
||||
/* Convert TValue and set C data value. */
|
||||
void lj_cdata_set(CTState *cts, CType *d, uint8_t *dp, TValue *o, CTInfo qual)
|
||||
{
|
||||
if (ctype_isconstval(d->info)) {
|
||||
goto err_const;
|
||||
} else if (ctype_isbitfield(d->info)) {
|
||||
if (((d->info|qual) & CTF_CONST)) goto err_const;
|
||||
lj_cconv_bf_tv(cts, d, dp, o);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get child type of pointer/array/field. */
|
||||
lua_assert(ctype_ispointer(d->info) || ctype_isfield(d->info));
|
||||
d = ctype_child(cts, d);
|
||||
|
||||
/* Resolve reference for field. */
|
||||
if (ctype_isref(d->info)) {
|
||||
lua_assert(d->size == CTSIZE_PTR);
|
||||
dp = *(uint8_t **)dp;
|
||||
d = ctype_child(cts, d);
|
||||
}
|
||||
|
||||
/* Skip attributes and collect qualifiers. */
|
||||
for (;;) {
|
||||
if (ctype_isattrib(d->info)) {
|
||||
if (ctype_attrib(d->info) == CTA_QUAL) qual |= d->size;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
d = ctype_child(cts, d);
|
||||
}
|
||||
|
||||
lua_assert(ctype_hassize(d->info) && !ctype_isvoid(d->info));
|
||||
|
||||
if (((d->info|qual) & CTF_CONST)) {
|
||||
err_const:
|
||||
lj_err_caller(cts->L, LJ_ERR_FFI_WRCONST);
|
||||
}
|
||||
|
||||
lj_cconv_ct_tv(cts, d, dp, o, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
Vendored
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
** C data management.
|
||||
** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CDATA_H
|
||||
#define _LJ_CDATA_H
|
||||
|
||||
#include "lj_obj.h"
|
||||
#include "lj_gc.h"
|
||||
#include "lj_ctype.h"
|
||||
|
||||
#if LJ_HASFFI
|
||||
|
||||
/* Get C data pointer. */
|
||||
static LJ_AINLINE void *cdata_getptr(void *p, CTSize sz)
|
||||
{
|
||||
if (LJ_64 && sz == 4) { /* Support 32 bit pointers on 64 bit targets. */
|
||||
return ((void *)(uintptr_t)*(uint32_t *)p);
|
||||
} else {
|
||||
lua_assert(sz == CTSIZE_PTR);
|
||||
return *(void **)p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set C data pointer. */
|
||||
static LJ_AINLINE void cdata_setptr(void *p, CTSize sz, const void *v)
|
||||
{
|
||||
if (LJ_64 && sz == 4) { /* Support 32 bit pointers on 64 bit targets. */
|
||||
*(uint32_t *)p = (uint32_t)(uintptr_t)v;
|
||||
} else {
|
||||
lua_assert(sz == CTSIZE_PTR);
|
||||
*(void **)p = (void *)v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate fixed-size C data object. */
|
||||
static LJ_AINLINE GCcdata *lj_cdata_new(CTState *cts, CTypeID id, CTSize sz)
|
||||
{
|
||||
GCcdata *cd;
|
||||
#ifdef LUA_USE_ASSERT
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
lua_assert((ctype_hassize(ct->info) ? ct->size : CTSIZE_PTR) == sz);
|
||||
#endif
|
||||
cd = (GCcdata *)lj_mem_newgco(cts->L, sizeof(GCcdata) + sz);
|
||||
cd->gct = ~LJ_TCDATA;
|
||||
cd->ctypeid = ctype_check(cts, id);
|
||||
return cd;
|
||||
}
|
||||
|
||||
/* Variant which works without a valid CTState. */
|
||||
static LJ_AINLINE GCcdata *lj_cdata_new_(lua_State *L, CTypeID id, CTSize sz)
|
||||
{
|
||||
GCcdata *cd = (GCcdata *)lj_mem_newgco(L, sizeof(GCcdata) + sz);
|
||||
cd->gct = ~LJ_TCDATA;
|
||||
cd->ctypeid = id;
|
||||
return cd;
|
||||
}
|
||||
|
||||
LJ_FUNC GCcdata *lj_cdata_newref(CTState *cts, const void *pp, CTypeID id);
|
||||
LJ_FUNC GCcdata *lj_cdata_newv(CTState *cts, CTypeID id, CTSize sz,
|
||||
CTSize align);
|
||||
|
||||
LJ_FUNC void LJ_FASTCALL lj_cdata_free(global_State *g, GCcdata *cd);
|
||||
LJ_FUNCA TValue * LJ_FASTCALL lj_cdata_setfin(lua_State *L, GCcdata *cd);
|
||||
|
||||
LJ_FUNC CType *lj_cdata_index(CTState *cts, GCcdata *cd, cTValue *key,
|
||||
uint8_t **pp, CTInfo *qual);
|
||||
LJ_FUNC int lj_cdata_get(CTState *cts, CType *s, TValue *o, uint8_t *sp);
|
||||
LJ_FUNC void lj_cdata_set(CTState *cts, CType *d, uint8_t *dp, TValue *o,
|
||||
CTInfo qual);
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
** Character types.
|
||||
** 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().
|
||||
**
|
||||
** This table is hardcoded for ASCII. Identifiers 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 character types for UTF-8 strings, please use
|
||||
** an add-on library such as slnunicode: http://luaforge.net/projects/sln/
|
||||
*/
|
||||
|
||||
#define lj_char_c
|
||||
#define LUA_CORE
|
||||
|
||||
#include "lj_char.h"
|
||||
|
||||
LJ_DATADEF const uint8_t lj_char_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
|
||||
};
|
||||
|
||||
Vendored
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
** Character types.
|
||||
** Donated to the public domain.
|
||||
*/
|
||||
|
||||
#ifndef _LJ_CHAR_H
|
||||
#define _LJ_CHAR_H
|
||||
|
||||
#include "lj_def.h"
|
||||
|
||||
#define LJ_CHAR_CNTRL 0x01
|
||||
#define LJ_CHAR_SPACE 0x02
|
||||
#define LJ_CHAR_PUNCT 0x04
|
||||
#define LJ_CHAR_DIGIT 0x08
|
||||
#define LJ_CHAR_XDIGIT 0x10
|
||||
#define LJ_CHAR_UPPER 0x20
|
||||
#define LJ_CHAR_LOWER 0x40
|
||||
#define LJ_CHAR_IDENT 0x80
|
||||
#define LJ_CHAR_ALPHA (LJ_CHAR_LOWER|LJ_CHAR_UPPER)
|
||||
#define LJ_CHAR_ALNUM (LJ_CHAR_ALPHA|LJ_CHAR_DIGIT)
|
||||
#define LJ_CHAR_GRAPH (LJ_CHAR_ALNUM|LJ_CHAR_PUNCT)
|
||||
|
||||
/* Only pass -1 or 0..255 to these macros. Never pass a signed char! */
|
||||
#define lj_char_isa(c, t) ((lj_char_bits+1)[(c)] & t)
|
||||
#define lj_char_iscntrl(c) lj_char_isa((c), LJ_CHAR_CNTRL)
|
||||
#define lj_char_isspace(c) lj_char_isa((c), LJ_CHAR_SPACE)
|
||||
#define lj_char_ispunct(c) lj_char_isa((c), LJ_CHAR_PUNCT)
|
||||
#define lj_char_isdigit(c) lj_char_isa((c), LJ_CHAR_DIGIT)
|
||||
#define lj_char_isxdigit(c) lj_char_isa((c), LJ_CHAR_XDIGIT)
|
||||
#define lj_char_isupper(c) lj_char_isa((c), LJ_CHAR_UPPER)
|
||||
#define lj_char_islower(c) lj_char_isa((c), LJ_CHAR_LOWER)
|
||||
#define lj_char_isident(c) lj_char_isa((c), LJ_CHAR_IDENT)
|
||||
#define lj_char_isalpha(c) lj_char_isa((c), LJ_CHAR_ALPHA)
|
||||
#define lj_char_isalnum(c) lj_char_isa((c), LJ_CHAR_ALNUM)
|
||||
#define lj_char_isgraph(c) lj_char_isa((c), LJ_CHAR_GRAPH)
|
||||
|
||||
#define lj_char_toupper(c) ((c) - (lj_char_islower(c) >> 1))
|
||||
#define lj_char_tolower(c) ((c) + lj_char_isupper(c))
|
||||
|
||||
LJ_DATA const uint8_t lj_char_bits[257];
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user