mirror of https://github.com/lukechilds/node.git
Timothy J Fontaine
11 years ago
219 changed files with 115294 additions and 461 deletions
@ -1,2 +1,14 @@ |
|||
obj/* |
|||
/wrk |
|||
*.o |
|||
*.a |
|||
wrk |
|||
deps/luajit/src/host/buildvm |
|||
deps/luajit/src/host/buildvm_arch.h |
|||
deps/luajit/src/host/minilua |
|||
deps/luajit/src/jit/vmdef.lua |
|||
deps/luajit/src/lj_bcdef.h |
|||
deps/luajit/src/lj_ffdef.h |
|||
deps/luajit/src/lj_folddef.h |
|||
deps/luajit/src/lj_libdef.h |
|||
deps/luajit/src/lj_recdef.h |
|||
deps/luajit/src/lj_vm.s |
|||
deps/luajit/src/luajit |
|||
|
@ -1,35 +1,64 @@ |
|||
CFLAGS := -std=c99 -Wall -O2 -pthread |
|||
LDFLAGS := -pthread |
|||
LIBS := -lm |
|||
ifeq ($(shell uname),SunOS) |
|||
LIBS += -lnsl -lsocket -lresolv |
|||
CFLAGS := -std=c99 -Wall -O2 -D_REENTRANT |
|||
LIBS := -lpthread -lm -lcrypto -lssl |
|||
|
|||
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 aprintf.c stats.c units.c ae.c zmalloc.c http_parser.c tinymt64.c |
|||
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)) |
|||
OBJ := $(patsubst %.c,$(ODIR)/%.o,$(SRC)) $(ODIR)/bytecode.o |
|||
|
|||
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) |
|||
@echo LINK $(BIN) |
|||
@$(CC) $(LDFLAGS) -o $@ $^ $(LIBS) |
|||
|
|||
$(OBJ): config.h Makefile | $(ODIR) |
|||
$(OBJ): config.h Makefile $(LDIR)/libluajit.a | $(ODIR) |
|||
|
|||
$(ODIR): |
|||
@mkdir $@ |
|||
@mkdir -p $@ |
|||
|
|||
$(ODIR)/bytecode.o: src/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 |
|||
|
@ -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 |
|||
|
|||
=============================================================================== |
@ -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 |
|||
|
|||
##############################################################################
|
@ -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 |
|||
|
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
File diff suppressed because it is too large
@ -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> |
@ -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> |
@ -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> |
@ -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> |
After Width: | Height: | Size: 1.3 KiB |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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 |
|||
|
File diff suppressed because it is too large
@ -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 |
|||
|
@ -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 |
|||
|
|||
------------------------------------------------------------------------------ |
|||
|
@ -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 |
|||
|
File diff suppressed because it is too large
@ -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 */ |
@ -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") |
@ -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 |
|||
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,88 @@ |
|||
.TH luajit 1 "" "" "LuaJIT documentation" |
|||
.SH NAME |
|||
luajit \- Just-In-Time Compiler for the Lua Language |
|||
\fB |
|||
.SH SYNOPSIS |
|||
.B luajit |
|||
[\fIoptions\fR]... [\fIscript\fR [\fIargs\fR]...] |
|||
.SH "WEB SITE" |
|||
.IR 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) |
@ -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} |
@ -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" |
|||
|
|||
##############################################################################
|
@ -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 |
@ -0,0 +1,4 @@ |
|||
The files in this directory are only used during the build process of LuaJIT. |
|||
For cross-compilation, they must be executed on the host, not on the target. |
|||
|
|||
These files should NOT be installed! |
@ -0,0 +1,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; |
|||
} |
|||
|
@ -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 |
@ -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"); |
|||
} |
|||
|
@ -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); |
|||
} |
|||
|
@ -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"); |
|||
} |
|||
} |
|||
|
@ -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 |
@ -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) |
File diff suppressed because it is too large
@ -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. |
|||
|
@ -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. |
|||
|
@ -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_ |
|||
|
@ -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_ |
|||
|
@ -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 |
|||
|
@ -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_ |
|||
|
@ -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 |
|||
|
@ -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_ |
|||
|
@ -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. |
|||
|
@ -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. |
|||
|
@ -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 |
@ -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 |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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 |
@ -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); |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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 |
|||
} |
File diff suppressed because it is too large
@ -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 |
File diff suppressed because it is too large
@ -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 |
File diff suppressed because it is too large
@ -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 |
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -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" |
|||
|
@ -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 |
@ -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 |
@ -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); |
|||
} |
|||
|
@ -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; |
|||
} |
|||
|
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
|||
}; |
|||
|
@ -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 changed in this diff
Loading…
Reference in new issue