RELEASE LuaJIT-2.0.0-beta1

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

8
src/.gitignore vendored Normal file
View File

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

326
src/Makefile Normal file
View File

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

139
src/Makefile.dep Normal file
View File

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

438
src/buildvm.c Normal file
View File

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

106
src/buildvm.h Normal file
View File

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

220
src/buildvm_asm.c Normal file
View File

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

206
src/buildvm_fold.c Normal file
View File

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

365
src/buildvm_lib.c Normal file
View File

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

303
src/buildvm_peobj.c Normal file
View File

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

3592
src/buildvm_x86.dasc Normal file

File diff suppressed because it is too large Load Diff

159
src/lauxlib.h Normal file
View File

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

438
src/lib_aux.c Normal file
View File

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

560
src/lib_base.c Normal file
View File

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

74
src/lib_bit.c Normal file
View File

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

366
src/lib_debug.c Normal file
View File

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

37
src/lib_init.c Normal file
View File

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

538
src/lib_io.c Normal file
View File

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

589
src/lib_jit.c Normal file
View File

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

188
src/lib_math.c Normal file
View File

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

249
src/lib_os.c Normal file
View File

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

508
src/lib_package.c Normal file
View File

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

790
src/lib_string.c Normal file
View File

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

276
src/lib_table.c Normal file
View File

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

6
src/lj.supp Normal file
View File

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

1232
src/lj_alloc.c Normal file

File diff suppressed because it is too large Load Diff

17
src/lj_alloc.h Normal file
View File

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

1046
src/lj_api.c Normal file

File diff suppressed because it is too large Load Diff

88
src/lj_arch.h Normal file
View File

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

3324
src/lj_asm.c Normal file

File diff suppressed because it is too large Load Diff

17
src/lj_asm.h Normal file
View File

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

17
src/lj_bc.c Normal file
View File

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

235
src/lj_bc.h Normal file
View File

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

44
src/lj_ctype.c Normal file
View File

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

40
src/lj_ctype.h Normal file
View File

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

226
src/lj_def.h Normal file
View File

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

284
src/lj_dispatch.c Normal file
View File

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

64
src/lj_dispatch.h Normal file
View File

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

763
src/lj_err.c Normal file
View File

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

40
src/lj_err.h Normal file
View File

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

134
src/lj_errmsg.h Normal file
View File

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

18
src/lj_ff.h Normal file
View File

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

84
src/lj_frame.h Normal file
View File

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

185
src/lj_func.c Normal file
View File

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

25
src/lj_func.h Normal file
View File

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

800
src/lj_gc.c Normal file
View File

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

102
src/lj_gc.h Normal file
View File

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

739
src/lj_gdbjit.c Normal file
View File

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

22
src/lj_gdbjit.h Normal file
View File

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

461
src/lj_ir.c Normal file
View File

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

429
src/lj_ir.h Normal file
View File

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

128
src/lj_iropt.h Normal file
View File

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

279
src/lj_jit.h Normal file
View File

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

393
src/lj_lex.c Normal file
View File

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

63
src/lj_lex.h Normal file
View File

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

216
src/lj_lib.c Normal file
View File

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

84
src/lj_lib.h Normal file
View File

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

260
src/lj_mcode.c Normal file
View File

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

23
src/lj_mcode.h Normal file
View File

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

358
src/lj_meta.c Normal file
View File

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

33
src/lj_meta.h Normal file
View File

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

41
src/lj_obj.c Normal file
View File

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

676
src/lj_obj.h Normal file
View File

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

79
src/lj_opt_dce.c Normal file
View File

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

1415
src/lj_opt_fold.c Normal file

File diff suppressed because it is too large Load Diff

358
src/lj_opt_loop.c Normal file
View File

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

550
src/lj_opt_mem.c Normal file
View File

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

430
src/lj_opt_narrow.c Normal file
View File

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

2198
src/lj_parse.c Normal file

File diff suppressed because it is too large Load Diff

15
src/lj_parse.h Normal file
View File

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

2136
src/lj_record.c Normal file

File diff suppressed because it is too large Load Diff

17
src/lj_record.h Normal file
View File

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

286
src/lj_snap.c Normal file
View File

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

19
src/lj_snap.h Normal file
View File

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

255
src/lj_state.c Normal file
View File

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

31
src/lj_state.h Normal file
View File

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

301
src/lj_str.c Normal file
View File

@@ -0,0 +1,301 @@
/*
** String handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <stdio.h>
#define lj_str_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_state.h"
#include "lj_ctype.h"
/* -- String interning ---------------------------------------------------- */
/* Ordered compare of strings. Assumes string data is 4-byte aligned. */
int32_t lj_str_cmp(GCstr *a, GCstr *b)
{
MSize i, n = a->len > b->len ? b->len : a->len;
for (i = 0; i < n; i += 4) {
/* Note: innocuous access up to end of string + 3. */
uint32_t va = *(const uint32_t *)(strdata(a)+i);
uint32_t vb = *(const uint32_t *)(strdata(b)+i);
if (va != vb) {
#if LJ_ARCH_ENDIAN == LUAJIT_LE
va = lj_bswap(va); vb = lj_bswap(vb);
#endif
i -= n;
if ((int32_t)i >= -3) {
va >>= 32+(i<<3); vb >>= 32+(i<<3);
if (va == vb) break;
}
return (int32_t)(va - vb);
}
}
return (int32_t)(a->len - b->len);
}
/* Resize the string hash table (grow and shrink). */
void lj_str_resize(lua_State *L, MSize newmask)
{
global_State *g = G(L);
GCRef *newhash;
MSize i;
if (g->gc.state == GCSsweepstring || newmask >= LJ_MAX_STRTAB-1)
return; /* No resizing during GC traversal or if already too big. */
newhash = lj_mem_newvec(L, newmask+1, GCRef);
memset(newhash, 0, (newmask+1)*sizeof(GCRef));
for (i = g->strmask; i != ~(MSize)0; i--) { /* Rehash old table. */
GCobj *p = gcref(g->strhash[i]);
while (p) { /* Follow each hash chain and reinsert all strings. */
MSize h = gco2str(p)->hash & newmask;
GCobj *next = gcnext(p);
/* NOBARRIER: The string table is a GC root. */
setgcrefr(p->gch.nextgc, newhash[h]);
setgcref(newhash[h], p);
p = next;
}
}
lj_mem_freevec(g, g->strhash, g->strmask+1, GCstr *);
g->strmask = newmask;
g->strhash = newhash;
}
/* Intern a string and return string object. */
GCstr *lj_str_new(lua_State *L, const char *str, size_t lenx)
{
global_State *g;
GCstr *s;
GCobj *o;
MSize len = (MSize)lenx;
MSize h = len;
MSize step = (len>>5)+1; /* Partial hash. */
MSize l1;
if (lenx >= LJ_MAX_STR)
lj_err_msg(L, LJ_ERR_STROV);
for (l1 = len; l1 >= step; l1 -= step) /* Compute string hash. */
h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
/* Check if the string has already been interned. */
g = G(L);
for (o = gcref(g->strhash[h & g->strmask]); o != NULL; o = gcnext(o)) {
GCstr *tso = gco2str(o);
if (tso->len == len && (memcmp(str, strdata(tso), len) == 0)) {
if (isdead(g, o)) flipwhite(o); /* Resurrect if dead. */
return tso; /* Return existing string. */
}
}
/* Nope, create a new string. */
s = lj_mem_newt(L, sizeof(GCstr)+len+1, GCstr);
newwhite(g, s);
s->gct = ~LJ_TSTR;
s->len = len;
s->hash = h;
s->reserved = 0;
memcpy(strdatawr(s), str, len);
strdatawr(s)[len] = '\0'; /* Zero-terminate string. */
/* Add it to string hash table. */
h &= g->strmask;
s->nextgc = g->strhash[h];
/* NOBARRIER: The string table is a GC root. */
setgcref(g->strhash[h], obj2gco(s));
if (g->strnum++ > g->strmask) /* Allow a 100% load factor. */
lj_str_resize(L, (g->strmask<<1)+1); /* Grow string table. */
return s; /* Return newly interned string. */
}
void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s)
{
g->strnum--;
lj_mem_free(g, s, sizestring(s));
}
/* -- Type conversions ---------------------------------------------------- */
/* Convert string to number. */
int lj_str_numconv(const char *s, TValue *n)
{
lua_Number sign = 1;
const uint8_t *p = (const uint8_t *)s;
while (lj_ctype_isspace(*p)) p++;
if (*p == '-') { p++; sign = -1; } else if (*p == '+') { p++; }
if ((uint32_t)(*p - '0') < 10) {
uint32_t k = (uint32_t)(*p++ - '0');
if (k == 0 && ((*p & ~0x20) == 'X')) {
p++;
while (lj_ctype_isxdigit(*p)) {
if (k >= 0x10000000) goto parsedbl;
k = (k << 4) + (*p & 15u);
if (!lj_ctype_isdigit(*p)) k += 9;
p++;
}
} else {
while ((uint32_t)(*p - '0') < 10) {
if (k >= 0x19999999) goto parsedbl;
k = k * 10u + (uint32_t)(*p++ - '0');
}
}
while (LJ_UNLIKELY(lj_ctype_isspace(*p))) p++;
if (LJ_LIKELY(*p == '\0')) {
setnumV(n, sign * cast_num(k));
return 1;
}
}
parsedbl:
{
TValue tv;
char *endptr;
setnumV(&tv, lua_str2number(s, &endptr));
if (endptr == s) return 0; /* conversion failed */
if (LJ_UNLIKELY(*endptr != '\0')) {
while (lj_ctype_isspace((uint8_t)*endptr)) endptr++;
if (*endptr != '\0') return 0; /* invalid trailing characters? */
}
if (LJ_LIKELY(!tvisnan(&tv)))
setnumV(n, numV(&tv));
else
setnanV(n); /* Canonicalize injected NaNs. */
return 1;
}
}
/* Convert number to string. */
GCstr *lj_str_fromnum(lua_State *L, const lua_Number *np)
{
char s[LUAI_MAXNUMBER2STR];
lua_Number n = *np;
size_t len = (size_t)lua_number2str(s, n);
return lj_str_new(L, s, len);
}
/* Convert integer to string. */
GCstr *lj_str_fromint(lua_State *L, int32_t k)
{
char s[1+10];
char *p = s+sizeof(s);
uint32_t i = (uint32_t)(k < 0 ? -k : k);
do { *--p = (char)('0' + i % 10); } while (i /= 10);
if (k < 0) *--p = '-';
return lj_str_new(L, p, (size_t)(s+sizeof(s)-p));
}
/* -- String formatting --------------------------------------------------- */
static void addstr(lua_State *L, SBuf *sb, const char *str, MSize len)
{
char *p;
MSize i;
if (sb->n + len > sb->sz) {
MSize sz = sb->sz * 2;
while (sb->n + len > sz) sz = sz * 2;
lj_str_resizebuf(L, sb, sz);
}
p = sb->buf + sb->n;
sb->n += len;
for (i = 0; i < len; i++) p[i] = str[i];
}
static void addchar(lua_State *L, SBuf *sb, int c)
{
if (sb->n + 1 > sb->sz) {
MSize sz = sb->sz * 2;
lj_str_resizebuf(L, sb, sz);
}
sb->buf[sb->n++] = cast(char, c);
}
/* Push formatted message as a string object to Lua stack. va_list variant. */
const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp)
{
SBuf *sb = &G(L)->tmpbuf;
lj_str_needbuf(L, sb, (MSize)strlen(fmt));
lj_str_resetbuf(sb);
for (;;) {
const char *e = strchr(fmt, '%');
if (e == NULL) break;
addstr(L, sb, fmt, (MSize)(e-fmt));
/* This function only handles %s, %c, %d, %f and %p formats. */
switch (e[1]) {
case 's': {
const char *s = va_arg(argp, char *);
if (s == NULL) s = "(null)";
addstr(L, sb, s, (MSize)strlen(s));
break;
}
case 'c':
addchar(L, sb, va_arg(argp, int));
break;
case 'd': {
char buff[1+10];
char *p = buff+sizeof(buff);
int32_t k = va_arg(argp, int32_t);
uint32_t i = (uint32_t)(k < 0 ? -k : k);
do { *--p = (char)('0' + i % 10); } while (i /= 10);
if (k < 0) *--p = '-';
addstr(L, sb, p, (MSize)(buff+sizeof(buff)-p));
break;
}
case 'f': {
char buff[LUAI_MAXNUMBER2STR];
lua_Number n = cast_num(va_arg(argp, LUAI_UACNUMBER));
MSize len = (MSize)lua_number2str(buff, n);
addstr(L, sb, buff, len);
break;
}
case 'p': {
#define FMTP_CHARS (2*sizeof(ptrdiff_t))
char buff[2+FMTP_CHARS];
ptrdiff_t p = (ptrdiff_t)(va_arg(argp, void *));
int i;
buff[0] = '0';
buff[1] = 'x';
for (i = 2+FMTP_CHARS-1; i >= 2; i--, p >>= 4)
buff[i] = "0123456789abcdef"[(p & 15)];
addstr(L, sb, buff, 2+FMTP_CHARS);
break;
}
case '%':
addchar(L, sb, '%');
break;
default:
addchar(L, sb, '%');
addchar(L, sb, e[1]);
break;
}
fmt = e+2;
}
addstr(L, sb, fmt, (MSize)strlen(fmt));
setstrV(L, L->top, lj_str_new(L, sb->buf, sb->n));
incr_top(L);
return strVdata(L->top - 1);
}
/* Push formatted message as a string object to Lua stack. Vararg variant. */
const char *lj_str_pushf(lua_State *L, const char *fmt, ...)
{
const char *msg;
va_list argp;
va_start(argp, fmt);
msg = lj_str_pushvf(L, fmt, argp);
va_end(argp);
return msg;
}
/* -- Buffer handling ----------------------------------------------------- */
char *lj_str_needbuf(lua_State *L, SBuf *sb, MSize sz)
{
if (sz > sb->sz) {
if (sz < LJ_MIN_SBUF) sz = LJ_MIN_SBUF;
lj_str_resizebuf(L, sb, sz);
}
return sb->buf;
}

45
src/lj_str.h Normal file
View File

@@ -0,0 +1,45 @@
/*
** String handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_STR_H
#define _LJ_STR_H
#include <stdarg.h>
#include "lj_obj.h"
/* String interning. */
LJ_FUNCA int32_t lj_str_cmp(GCstr *a, GCstr *b);
LJ_FUNC void lj_str_resize(lua_State *L, MSize newmask);
LJ_FUNCA GCstr *lj_str_new(lua_State *L, const char *str, size_t len);
LJ_FUNC void LJ_FASTCALL lj_str_free(global_State *g, GCstr *s);
#define lj_str_newz(L, s) (lj_str_new(L, s, strlen(s)))
#define lj_str_newlit(L, s) (lj_str_new(L, "" s, sizeof(s)-1))
/* Type conversions. */
LJ_FUNCA int lj_str_numconv(const char *s, TValue *n);
LJ_FUNCA GCstr *lj_str_fromnum(lua_State *L, const lua_Number *np);
LJ_FUNCA GCstr *lj_str_fromint(lua_State *L, int32_t k);
/* String formatting. */
LJ_FUNC const char *lj_str_pushvf(lua_State *L, const char *fmt, va_list argp);
LJ_FUNC const char *lj_str_pushf(lua_State *L, const char *fmt, ...)
#if defined(__GNUC__)
__attribute__ ((format (printf, 2, 3)))
#endif
;
/* Resizable string buffers. Struct definition in lj_obj.h. */
LJ_FUNC char *lj_str_needbuf(lua_State *L, SBuf *sb, MSize sz);
#define lj_str_initbuf(L, sb) ((sb)->buf = NULL, (sb)->sz = 0)
#define lj_str_resetbuf(sb) ((sb)->n = 0)
#define lj_str_resizebuf(L, sb, size) \
((sb)->buf = (char *)lj_mem_realloc(L, (sb)->buf, (sb)->sz, (size)), \
(sb)->sz = (size))
#define lj_str_freebuf(g, sb) lj_mem_free(g, (void *)(sb)->buf, (sb)->sz)
#endif

618
src/lj_tab.c Normal file
View File

@@ -0,0 +1,618 @@
/*
** Table handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#define lj_tab_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_tab.h"
/* -- Object hashing ------------------------------------------------------ */
/* Hash values are masked with the table hash mask and used as an index. */
#define hashmask(t, x) (&noderef(t->node)[(x) & t->hmask])
/* String hashes are precomputed when they are interned. */
#define hashstr(t, s) hashmask(t, (s)->hash)
#define hashnum(t, o) hashrot(t, (o)->u32.lo, (o)->u32.hi&0x7fffffff)
#define hashgcref(t, r) hashrot(t, gcrefu(r), gcrefu(r)-0x04c11db7)
/* Scramble the bits of numbers and pointers. */
static LJ_AINLINE Node *hashrot(const GCtab *t, uint32_t lo, uint32_t hi)
{
lo ^= hi; hi = lj_rol(hi, 14);
lo -= hi; hi = lj_rol(hi, 5);
hi ^= lo; hi -= lj_rol(lo, 27);
return hashmask(t, hi);
}
/* Hash an arbitrary key and return its anchor position in the hash table. */
static Node *hashkey(const GCtab *t, cTValue *key)
{
if (tvisstr(key))
return hashstr(t, strV(key));
else if (tvisnum(key))
return hashnum(t, key);
else if (tvisbool(key))
return hashmask(t, boolV(key));
else
return hashgcref(t, key->gcr);
/* Only hash 32 bits of lightuserdata on a 64 bit CPU. Good enough? */
}
/* -- Table creation and destruction -------------------------------------- */
/* Create new hash part for table. */
static LJ_AINLINE void newhpart(lua_State *L, GCtab *t, uint32_t hbits)
{
uint32_t hsize;
Node *node;
lua_assert(hbits != 0);
if (hbits > LJ_MAX_HBITS)
lj_err_msg(L, LJ_ERR_TABOV);
hsize = 1u << hbits;
node = lj_mem_newvec(L, hsize, Node);
setmref(t->node, node);
t->hmask = hsize-1;
setmref(t->lastfree, &node[hsize]);
}
/*
** Q: Why all of these copies of t->hmask, t->node etc. to local variables?
** A: Because alias analysis for C is _really_ tough.
** Even state-of-the-art C compilers won't produce good code without this.
*/
/* Clear hash part of table. */
static LJ_AINLINE void clearhpart(GCtab *t)
{
uint32_t i, hmask = t->hmask;
Node *node = noderef(t->node);
lua_assert(t->hmask != 0);
for (i = 0; i <= hmask; i++) {
Node *n = &node[i];
setmref(n->next, NULL);
setnilV(&n->key);
setnilV(&n->val);
}
}
/* Clear array part of table. */
static LJ_AINLINE void clearapart(GCtab *t)
{
uint32_t i, asize = t->asize;
TValue *array = tvref(t->array);
for (i = 0; i < asize; i++)
setnilV(&array[i]);
}
/* Create a new table. Note: the slots are not initialized (yet). */
static GCtab *newtab(lua_State *L, uint32_t asize, uint32_t hbits)
{
GCtab *t;
global_State *g;
/* First try to colocate the array part. */
if (LJ_MAX_COLOSIZE && asize > 0 && asize <= LJ_MAX_COLOSIZE) {
/* This is ugly. (sizeof(GCtab)&7) != 0. So prepend the colocated array. */
TValue *array = lj_mem_newt(L, sizetabcolo(asize), TValue);
t = cast(GCtab *, array + asize);
g = G(L);
setgcrefr(t->nextgc, g->gc.root);
setgcref(g->gc.root, obj2gco(t));
newwhite(g, t);
t->gct = ~LJ_TTAB;
t->nomm = cast_byte(~0);
t->colo = (int8_t)asize;
setmref(t->array, array);
setgcrefnull(t->metatable);
t->asize = asize;
t->hmask = 0;
setmref(t->node, &g->nilnode);
setmref(t->lastfree, &g->nilnode);
} else { /* Otherwise separately allocate the array part. */
t = lj_mem_newobj(L, GCtab);
t->gct = ~LJ_TTAB;
t->nomm = cast_byte(~0);
t->colo = 0;
setmref(t->array, NULL);
setgcrefnull(t->metatable);
t->asize = 0; /* In case the array allocation fails. */
t->hmask = 0;
g = G(L);
setmref(t->node, &g->nilnode);
setmref(t->lastfree, &g->nilnode);
if (asize > 0) {
if (asize > LJ_MAX_ASIZE)
lj_err_msg(L, LJ_ERR_TABOV);
setmref(t->array, lj_mem_newvec(L, asize, TValue));
t->asize = asize;
}
}
if (hbits)
newhpart(L, t, hbits);
return t;
}
/* Create a new table.
**
** IMPORTANT NOTE: The API differs from lua_createtable()!
**
** The array size is non-inclusive. E.g. asize=128 creates array slots
** for 0..127, but not for 128. If you need slots 1..128, pass asize=129
** (slot 0 is wasted in this case).
**
** The hash size is given in hash bits. hbits=0 means no hash part.
** hbits=1 creates 2 hash slots, hbits=2 creates 4 hash slots and so on.
*/
GCtab *lj_tab_new(lua_State *L, uint32_t asize, uint32_t hbits)
{
GCtab *t = newtab(L, asize, hbits);
clearapart(t);
if (t->hmask > 0) clearhpart(t);
return t;
}
/* Duplicate a table. */
GCtab *lj_tab_dup(lua_State *L, const GCtab *kt)
{
GCtab *t;
uint32_t asize, hmask;
t = newtab(L, kt->asize, kt->hmask > 0 ? lj_fls(kt->hmask)+1 : 0);
lua_assert(kt->asize == t->asize && kt->hmask == t->hmask);
t->nomm = 0; /* Keys with metamethod names may be present. */
asize = kt->asize;
if (asize > 0) {
TValue *array = tvref(t->array);
TValue *karray = tvref(kt->array);
if (asize < 64) { /* An inlined loop beats memcpy for < 512 bytes. */
uint32_t i;
for (i = 0; i < asize; i++)
copyTV(L, &array[i], &karray[i]);
} else {
memcpy(array, karray, asize*sizeof(TValue));
}
}
hmask = kt->hmask;
if (hmask > 0) {
uint32_t i;
Node *node = noderef(t->node);
Node *knode = noderef(kt->node);
ptrdiff_t d = (char *)node - (char *)knode;
setmref(t->lastfree, (Node *)((char *)noderef(kt->lastfree) + d));
for (i = 0; i <= hmask; i++) {
Node *kn = &knode[i];
Node *n = &node[i];
Node *next = nextnode(kn);
copyTV(L, &n->val, &kn->val);
copyTV(L, &n->key, &kn->key);
setmref(n->next, next == NULL? next : (Node *)((char *)next + d));
}
}
return t;
}
/* Free a table. */
void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t)
{
if (t->hmask > 0)
lj_mem_freevec(g, noderef(t->node), t->hmask+1, Node);
if (LJ_MAX_COLOSIZE && t->colo) {
ptrdiff_t n;
if (t->colo < 0 && t->asize > 0) /* Array part was separated. */
lj_mem_freevec(g, tvref(t->array), t->asize, TValue);
n = t->colo & 0x7f;
lj_mem_free(g, (TValue *)t - n, sizetabcolo((uint32_t)n));
} else {
if (t->asize > 0)
lj_mem_freevec(g, tvref(t->array), t->asize, TValue);
lj_mem_freet(g, t);
}
}
/* -- Table resizing ------------------------------------------------------ */
/* Resize a table to fit the new array/hash part sizes. */
static void resizetab(lua_State *L, GCtab *t, uint32_t asize, uint32_t hbits)
{
Node *oldnode = noderef(t->node);
uint32_t oldasize = t->asize;
uint32_t oldhmask = t->hmask;
if (asize > oldasize) { /* Array part grows? */
TValue *array;
uint32_t i;
if (asize > LJ_MAX_ASIZE)
lj_err_msg(L, LJ_ERR_TABOV);
if (LJ_MAX_COLOSIZE && t->colo > 0) {
/* A colocated array must be separated and copied. */
TValue *oarray = tvref(t->array);
array = lj_mem_newvec(L, asize, TValue);
t->colo = (int8_t)(t->colo | 0x80); /* Mark as separated (colo < 0). */
for (i = 0; i < oldasize; i++)
copyTV(L, &array[i], &oarray[i]);
} else {
array = (TValue *)lj_mem_realloc(L, tvref(t->array),
oldasize*sizeof(TValue), asize*sizeof(TValue));
}
setmref(t->array, array);
t->asize = asize;
for (i = oldasize; i < asize; i++) /* Clear newly allocated slots. */
setnilV(&array[i]);
}
/* Create new (empty) hash part. */
if (hbits) {
newhpart(L, t, hbits);
clearhpart(t);
} else {
global_State *g = G(L);
setmref(t->node, &g->nilnode);
setmref(t->lastfree, &g->nilnode);
t->hmask = 0;
}
if (asize < oldasize) { /* Array part shrinks? */
TValue *array = tvref(t->array);
uint32_t i;
t->asize = asize; /* Note: This 'shrinks' even colocated arrays. */
for (i = asize; i < oldasize; i++) /* Reinsert old array values. */
if (!tvisnil(&array[i]))
copyTV(L, lj_tab_setinth(L, t, (int32_t)i), &array[i]);
/* Physically shrink only separated arrays. */
if (LJ_MAX_COLOSIZE && t->colo <= 0)
setmref(t->array, lj_mem_realloc(L, array,
oldasize*sizeof(TValue), asize*sizeof(TValue)));
}
if (oldhmask > 0) { /* Reinsert pairs from old hash part. */
global_State *g;
uint32_t i;
for (i = 0; i <= oldhmask; i++) {
Node *n = &oldnode[i];
if (!tvisnil(&n->val))
copyTV(L, lj_tab_set(L, t, &n->key), &n->val);
}
g = G(L);
lj_mem_freevec(g, oldnode, oldhmask+1, Node);
}
}
static uint32_t countint(cTValue *key, uint32_t *bins)
{
if (tvisnum(key)) {
lua_Number nk = numV(key);
int32_t k = lj_num2int(nk);
if ((uint32_t)k < LJ_MAX_ASIZE && nk == cast_num(k)) {
bins[(k > 2 ? lj_fls((uint32_t)(k-1)) : 0)]++;
return 1;
}
}
return 0;
}
static uint32_t countarray(const GCtab *t, uint32_t *bins)
{
uint32_t na, b, i;
if (t->asize == 0) return 0;
for (na = i = b = 0; b < LJ_MAX_ABITS; b++) {
uint32_t n, top = 2u << b;
TValue *array;
if (top >= t->asize) {
top = t->asize-1;
if (i > top)
break;
}
array = tvref(t->array);
for (n = 0; i <= top; i++)
if (!tvisnil(&array[i]))
n++;
bins[b] += n;
na += n;
}
return na;
}
static uint32_t counthash(const GCtab *t, uint32_t *bins, uint32_t *narray)
{
uint32_t total, na, i, hmask = t->hmask;
Node *node = noderef(t->node);
for (total = na = 0, i = 0; i <= hmask; i++) {
Node *n = &node[i];
if (!tvisnil(&n->val)) {
na += countint(&n->key, bins);
total++;
}
}
*narray += na;
return total;
}
static uint32_t bestasize(uint32_t bins[], uint32_t *narray)
{
uint32_t b, sum, na = 0, sz = 0, nn = *narray;
for (b = 0, sum = 0; (1u<<b) <= nn && sum != nn; b++)
if (bins[b] > 0 && (sum += bins[b]) >= (1u<<b)) {
sz = (2u<<b)+1;
na = sum;
}
*narray = sz;
return na;
}
static void rehashtab(lua_State *L, GCtab *t, cTValue *ek)
{
uint32_t bins[LJ_MAX_ABITS];
uint32_t total, asize, na, i;
for (i = 0; i < LJ_MAX_ABITS; i++) bins[i] = 0;
asize = countarray(t, bins);
total = 1 + asize + counthash(t, bins, &asize);
asize += countint(ek, bins);
na = bestasize(bins, &asize);
total -= na;
resizetab(L, t, asize, hsize2hbits(total));
}
void lj_tab_reasize(lua_State *L, GCtab *t, uint32_t nasize)
{
resizetab(L, t, nasize+1, t->hmask > 0 ? lj_fls(t->hmask)+1 : 0);
}
/* -- Table getters ------------------------------------------------------- */
cTValue *lj_tab_getinth(GCtab *t, int32_t key)
{
TValue k;
Node *n;
k.n = cast_num(key);
n = hashnum(t, &k);
do {
if (tvisnum(&n->key) && n->key.n == k.n)
return &n->val;
} while ((n = nextnode(n)));
return NULL;
}
cTValue *lj_tab_getstr(GCtab *t, GCstr *key)
{
Node *n = hashstr(t, key);
do {
if (tvisstr(&n->key) && strV(&n->key) == key)
return &n->val;
} while ((n = nextnode(n)));
return NULL;
}
cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key)
{
if (tvisstr(key)) {
cTValue *tv = lj_tab_getstr(t, strV(key));
if (tv)
return tv;
} else if (tvisnum(key)) {
lua_Number nk = numV(key);
int32_t k = lj_num2int(nk);
if (nk == cast_num(k)) {
cTValue *tv = lj_tab_getint(t, k);
if (tv)
return tv;
} else {
goto genlookup; /* Else use the generic lookup. */
}
} else if (!tvisnil(key)) {
Node *n;
genlookup:
n = hashkey(t, key);
do {
if (lj_obj_equal(&n->key, key))
return &n->val;
} while ((n = nextnode(n)));
}
return niltv(L);
}
/* -- Table setters ------------------------------------------------------- */
static Node *getfreepos(GCtab *t)
{
Node *node = noderef(t->node);
Node *lastfree = noderef(t->lastfree);
while (lastfree > node) {
lastfree--;
setmref(t->lastfree, lastfree);
if (tvisnil(&lastfree->key))
return lastfree;
}
return NULL; /* could not find a free place */
}
/*
** inserts a new key into a hash table; first, check whether key's main
** position is free. If not, check whether colliding node is in its main
** position or not: if it is not, move colliding node to an empty place and
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
*/
TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key)
{
Node *mp = hashkey(t, key);
if (!tvisnil(&mp->val) || t->hmask == 0) {
Node *othern;
Node *n = getfreepos(t); /* get a free place */
if (n == NULL) { /* cannot find a free place? */
rehashtab(L, t, key); /* grow table */
return lj_tab_set(L, t, key); /* re-insert key into grown table */
}
lua_assert(n != &G(L)->nilnode);
othern = hashkey(t, &mp->key);
if (othern != mp) { /* is colliding node out of its main position? */
/* yes; move colliding node into free position */
while (noderef(othern->next) != mp)
othern = nextnode(othern); /* find previous */
setmref(othern->next, n); /* redo the chain with `n' in place of `mp' */
*n = *mp; /* copy colliding node into free pos. (mp->next also goes) */
setmref(mp->next, NULL); /* now `mp' is free */
setnilV(&mp->val);
} else { /* colliding node is in its own main position */
/* new node will go into free position */
setmrefr(n->next, mp->next); /* chain new position */
setmref(mp->next, n);
mp = n;
}
}
mp->key.u64 = key->u64;
if (LJ_UNLIKELY(tvismzero(&mp->key)))
mp->key.u64 = 0;
lj_gc_barriert(L, t, key);
lua_assert(tvisnil(&mp->val));
return &mp->val;
}
TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key)
{
TValue k;
Node *n;
k.n = cast_num(key);
n = hashnum(t, &k);
do {
if (tvisnum(&n->key) && n->key.n == k.n)
return &n->val;
} while ((n = nextnode(n)));
return lj_tab_newkey(L, t, &k);
}
TValue *lj_tab_setstr(lua_State *L, GCtab *t, GCstr *key)
{
TValue k;
Node *n = hashstr(t, key);
do {
if (tvisstr(&n->key) && strV(&n->key) == key)
return &n->val;
} while ((n = nextnode(n)));
setstrV(L, &k, key);
return lj_tab_newkey(L, t, &k);
}
TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key)
{
Node *n;
t->nomm = 0; /* Invalidate negative metamethod cache. */
if (tvisstr(key)) {
return lj_tab_setstr(L, t, strV(key));
} else if (tvisnum(key)) {
lua_Number nk = numV(key);
int32_t k = lj_num2int(nk);
if (nk == cast_num(k))
return lj_tab_setint(L, t, k);
if (tvisnan(key))
lj_err_msg(L, LJ_ERR_NANIDX);
/* Else use the generic lookup. */
} else if (tvisnil(key)) {
lj_err_msg(L, LJ_ERR_NILIDX);
}
n = hashkey(t, key);
do {
if (lj_obj_equal(&n->key, key))
return &n->val;
} while ((n = nextnode(n)));
return lj_tab_newkey(L, t, key);
}
/* -- Table traversal ----------------------------------------------------- */
/* Get the traversal index of a key. */
static uint32_t keyindex(lua_State *L, GCtab *t, cTValue *key)
{
if (tvisnum(key)) {
lua_Number nk = numV(key);
int32_t k = lj_num2int(nk);
if ((uint32_t)k < t->asize && nk == cast_num(k))
return (uint32_t)k; /* Array key indexes: [0..t->asize-1] */
}
if (!tvisnil(key)) {
Node *n = hashkey(t, key);
do {
if (lj_obj_equal(&n->key, key) ||
(itype(&n->key) == LJ_TDEADKEY && tvisgcv(key) &&
gcV(&n->key) == gcV(key)))
return t->asize + (uint32_t)(n - noderef(t->node));
/* Hash key indexes: [t->asize..t->asize+t->nmask] */
} while ((n = nextnode(n)));
lj_err_msg(L, LJ_ERR_NEXTIDX);
return 0; /* unreachable */
}
return ~0u; /* A nil key starts the traversal. */
}
/* Advance to the next step in a table traversal. */
int lj_tab_next(lua_State *L, GCtab *t, TValue *key)
{
uint32_t i = keyindex(L, t, key); /* Find predecessor key index. */
for (i++; i < t->asize; i++) /* First traverse the array keys. */
if (!tvisnil(arrayslot(t, i))) {
setintV(key, i);
copyTV(L, key+1, arrayslot(t, i));
return 1;
}
for (i -= t->asize; i <= t->hmask; i++) { /* Then traverse the hash keys. */
Node *n = &noderef(t->node)[i];
if (!tvisnil(&n->val)) {
copyTV(L, key, &n->key);
copyTV(L, key+1, &n->val);
return 1;
}
}
return 0; /* End of traversal. */
}
/* -- Table length calculation -------------------------------------------- */
static MSize unbound_search(GCtab *t, MSize j)
{
cTValue *tv;
MSize i = j; /* i is zero or a present index */
j++;
/* find `i' and `j' such that i is present and j is not */
while ((tv = lj_tab_getint(t, cast(int32_t, j))) && !tvisnil(tv)) {
i = j;
j *= 2;
if (j > (MSize)(INT_MAX-2)) { /* overflow? */
/* table was built with bad purposes: resort to linear search */
i = 1;
while ((tv = lj_tab_getint(t, cast(int32_t, i))) && !tvisnil(tv)) i++;
return i - 1;
}
}
/* now do a binary search between them */
while (j - i > 1) {
MSize m = (i+j)/2;
cTValue *tvb = lj_tab_getint(t, cast(int32_t, m));
if (tvb && !tvisnil(tvb)) i = m; else j = m;
}
return i;
}
/*
** Try to find a boundary in table `t'. A `boundary' is an integer index
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
MSize lj_tab_len(GCtab *t)
{
MSize j = (MSize)t->asize;
if (j > 1 && tvisnil(arrayslot(t, j-1))) {
MSize i = 1;
while (j - i > 1) {
MSize m = (i+j)/2;
if (tvisnil(arrayslot(t, m-1))) j = m; else i = m;
}
return i-1;
}
if (j) j--;
if (t->hmask <= 0)
return j;
return unbound_search(t, j);
}

41
src/lj_tab.h Normal file
View File

@@ -0,0 +1,41 @@
/*
** Table handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_TAB_H
#define _LJ_TAB_H
#include "lj_obj.h"
#define hsize2hbits(s) ((s) ? ((s)==1 ? 1 : 1+lj_fls((uint32_t)((s)-1))) : 0)
LJ_FUNCA GCtab *lj_tab_new(lua_State *L, uint32_t asize, uint32_t hbits);
LJ_FUNCA GCtab *lj_tab_dup(lua_State *L, const GCtab *kt);
LJ_FUNC void LJ_FASTCALL lj_tab_free(global_State *g, GCtab *t);
LJ_FUNCA void lj_tab_reasize(lua_State *L, GCtab *t, uint32_t nasize);
/* Caveat: all getters except lj_tab_get() can return NULL! */
LJ_FUNCA cTValue *lj_tab_getinth(GCtab *t, int32_t key);
LJ_FUNC cTValue *lj_tab_getstr(GCtab *t, GCstr *key);
LJ_FUNCA cTValue *lj_tab_get(lua_State *L, GCtab *t, cTValue *key);
/* Caveat: all setters require a write barrier for the stored value. */
LJ_FUNCA TValue *lj_tab_newkey(lua_State *L, GCtab *t, cTValue *key);
LJ_FUNC TValue *lj_tab_setinth(lua_State *L, GCtab *t, int32_t key);
LJ_FUNC TValue *lj_tab_setstr(lua_State *L, GCtab *t, GCstr *key);
LJ_FUNC TValue *lj_tab_set(lua_State *L, GCtab *t, cTValue *key);
#define inarray(t, key) ((MSize)(key) < (MSize)(t)->asize)
#define arrayslot(t, i) (&tvref((t)->array)[(i)])
#define lj_tab_getint(t, key) \
(inarray((t), (key)) ? arrayslot((t), (key)) : lj_tab_getinth((t), (key)))
#define lj_tab_setint(L, t, key) \
(inarray((t), (key)) ? arrayslot((t), (key)) : lj_tab_setinth(L, (t), (key)))
LJ_FUNCA int lj_tab_next(lua_State *L, GCtab *t, TValue *key);
LJ_FUNCA MSize lj_tab_len(GCtab *t);
#endif

132
src/lj_target.h Normal file
View File

@@ -0,0 +1,132 @@
/*
** Definitions for target CPU.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_TARGET_H
#define _LJ_TARGET_H
#include "lj_def.h"
#include "lj_arch.h"
/* -- Registers and spill slots ------------------------------------------- */
/* Register type (uint8_t in ir->r). */
typedef uint32_t Reg;
/* The hi-bit is NOT set for an allocated register. This means the value
** can be directly used without masking. The hi-bit is set for a register
** allocation hint or for RID_INIT.
*/
#define RID_NONE 0x80
#define RID_MASK 0x7f
#define RID_INIT (RID_NONE|RID_MASK)
#define ra_noreg(r) ((r) & RID_NONE)
#define ra_hasreg(r) (!((r) & RID_NONE))
/* The ra_hashint() macro assumes a previous test for ra_noreg(). */
#define ra_hashint(r) ((r) != RID_INIT)
#define ra_gethint(r) ((Reg)((r) & RID_MASK))
#define ra_sethint(rr, r) rr = (uint8_t)((r)|RID_NONE)
#define ra_samehint(r1, r2) (ra_gethint((r1)^(r2)) == 0)
/* Spill slot 0 means no spill slot has been allocated. */
#define SPS_NONE 0
#define ra_hasspill(s) ((s) != SPS_NONE)
/* Combined register and spill slot (uint16_t in ir->prev). */
typedef uint32_t RegSP;
#define REGSP(r, s) ((r) + ((s) << 8))
#define REGSP_HINT(r) ((r)|RID_NONE)
#define REGSP_INIT REGSP(RID_INIT, 0)
#define regsp_reg(rs) ((rs) & 255)
#define regsp_spill(rs) ((rs) >> 8)
#define regsp_used(rs) \
(((rs) & ~REGSP(RID_MASK, 0)) != REGSP(RID_NONE, 0))
/* -- Register sets ------------------------------------------------------- */
/* Bitset for registers. 32 registers suffice right now.
** Note that one set holds bits for both GPRs and FPRs.
*/
typedef uint32_t RegSet;
#define RID2RSET(r) (((RegSet)1) << (r))
#define RSET_EMPTY 0
#define RSET_RANGE(lo, hi) ((RID2RSET((hi)-(lo))-1) << (lo))
#define rset_test(rs, r) (((rs) >> (r)) & 1)
#define rset_set(rs, r) (rs |= RID2RSET(r))
#define rset_clear(rs, r) (rs &= ~RID2RSET(r))
#define rset_exclude(rs, r) (rs & ~RID2RSET(r))
#define rset_picktop(rs) ((Reg)lj_fls(rs))
#define rset_pickbot(rs) ((Reg)lj_ffs(rs))
/* -- Register allocation cost -------------------------------------------- */
/* The register allocation heuristic keeps track of the cost for allocating
** a specific register:
**
** A free register (obviously) has a cost of 0 and a 1-bit in the free mask.
**
** An already allocated register has the (non-zero) IR reference in the lowest
** bits and the result of a blended cost-model in the higher bits.
**
** The allocator first checks the free mask for a hit. Otherwise an (unrolled)
** linear search for the minimum cost is used. The search doesn't need to
** keep track of the position of the minimum, which makes it very fast.
** The lowest bits of the minimum cost show the desired IR reference whose
** register is the one to evict.
**
** Without the cost-model this degenerates to the standard heuristics for
** (reverse) linear-scan register allocation. Since code generation is done
** in reverse, a live interval extends from the last use to the first def.
** For an SSA IR the IR reference is the first (and only) def and thus
** trivially marks the end of the interval. The LSRA heuristics says to pick
** the register whose live interval has the furthest extent, i.e. the lowest
** IR reference in our case.
**
** A cost-model should take into account other factors, like spill-cost and
** restore- or rematerialization-cost, which depend on the kind of instruction.
** E.g. constants have zero spill costs, variant instructions have higher
** costs than invariants and PHIs should preferably never be spilled.
**
** Here's a first cut at simple, but effective blended cost-model for R-LSRA:
** - Due to careful design of the IR, constants already have lower IR
** references than invariants and invariants have lower IR references
** than variants.
** - The cost in the upper 16 bits is the sum of the IR reference and a
** weighted score. The score currently only takes into account whether
** the IRT_ISPHI bit is set in the instruction type.
** - The PHI weight is the minimum distance (in IR instructions) a PHI
** reference has to be further apart from a non-PHI reference to be spilled.
** - It should be a power of two (for speed) and must be between 2 and 32768.
** Good values for the PHI weight seem to be between 40 and 150.
** - Further study is required.
*/
#define REGCOST_PHI_WEIGHT 64
/* Cost for allocating a specific register. */
typedef uint32_t RegCost;
/* Note: assumes 16 bit IRRef1. */
#define REGCOST(cost, ref) ((RegCost)(ref) + ((RegCost)(cost) << 16))
#define regcost_ref(rc) ((IRRef1)(rc))
#define REGCOST_T(t) \
((RegCost)((t)&IRT_ISPHI) * (((RegCost)(REGCOST_PHI_WEIGHT)<<16)/IRT_ISPHI))
#define REGCOST_REF_T(ref, t) (REGCOST((ref), (ref)) + REGCOST_T((t)))
/* -- Target-specific definitions ----------------------------------------- */
#if LJ_TARGET_X86ORX64
#include "lj_target_x86.h"
#else
#error "Missing include for target CPU"
#endif
#endif

257
src/lj_target_x86.h Normal file
View File

@@ -0,0 +1,257 @@
/*
** Definitions for x86 and x64 CPUs.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_TARGET_X86_H
#define _LJ_TARGET_X86_H
/* -- Registers IDs ------------------------------------------------------- */
#if LJ_64
#define GPRDEF(_) \
_(EAX) _(ECX) _(EDX) _(EBX) _(ESP) _(EBP) _(ESI) _(EDI) \
_(R8D) _(R9D) _(R10D) _(R11D) _(R12D) _(R13D) _(R14D) _(R15D)
#define FPRDEF(_) \
_(XMM0) _(XMM1) _(XMM2) _(XMM3) _(XMM4) _(XMM5) _(XMM6) _(XMM7) \
_(XMM8) _(XMM9) _(XMM10) _(XMM11) _(XMM12) _(XMM13) _(XMM14) _(XMM15)
#else
#define GPRDEF(_) \
_(EAX) _(ECX) _(EDX) _(EBX) _(ESP) _(EBP) _(ESI) _(EDI)
#define FPRDEF(_) \
_(XMM0) _(XMM1) _(XMM2) _(XMM3) _(XMM4) _(XMM5) _(XMM6) _(XMM7)
#endif
#define RIDENUM(name) RID_##name,
enum {
GPRDEF(RIDENUM) /* General-purpose registers (GPRs). */
FPRDEF(RIDENUM) /* Floating-point registers (FPRs). */
RID_MAX,
RID_MRM = RID_MAX, /* Pseudo-id for ModRM operand. */
/* Calling conventions. */
RID_RET = RID_EAX,
/* These definitions must match with the *.dasc file(s): */
RID_BASE = RID_EDX, /* Interpreter BASE. */
RID_PC = RID_ESI, /* Interpreter PC. */
RID_DISPATCH = RID_EBX, /* Interpreter DISPATCH table. */
/* Register ranges [min, max) and number of registers. */
RID_MIN_GPR = RID_EAX,
RID_MIN_FPR = RID_XMM0,
RID_MAX_GPR = RID_MIN_FPR,
RID_MAX_FPR = RID_MAX,
RID_NUM_GPR = RID_MAX_GPR - RID_MIN_GPR,
RID_NUM_FPR = RID_MAX_FPR - RID_MIN_FPR,
};
/* -- Register sets ------------------------------------------------------- */
/* Make use of all registers, except the stack pointer. */
#define RSET_GPR (RSET_RANGE(RID_MIN_GPR, RID_MAX_GPR)-RID2RSET(RID_ESP))
#define RSET_FPR (RSET_RANGE(RID_MIN_FPR, RID_MAX_FPR))
#define RSET_ALL (RSET_GPR|RSET_FPR)
#if LJ_64
/* Note: this requires the use of FORCE_REX! */
#define RSET_GPR8 RSET_GPR
#else
#define RSET_GPR8 (RSET_RANGE(RID_EAX, RID_EBX+1))
#endif
/* ABI-specific register sets. */
#define RSET_ACD (RID2RSET(RID_EAX)|RID2RSET(RID_ECX)|RID2RSET(RID_EDX))
#if LJ_64
#ifdef _WIN64
/* Windows x64 ABI. */
#define RSET_SCRATCH \
(RSET_ACD|RSET_RANGE(RID_R8D, RID_R11D+1)|RSET_RANGE(RID_XMM0, RID_XMM5+1))
#else
/* The rest of the civilized x64 world has a common ABI. */
#define RSET_SCRATCH \
(RSET_ACD|RSET_RANGE(RID_ESI, RID_R11D+1)|RSET_FPR)
#endif
#else
/* Common x86 ABI. */
#define RSET_SCRATCH (RSET_ACD|RSET_FPR)
#endif
#if LJ_64
/* Prefer the low 8 regs of each type to reduce REX prefixes. */
#undef rset_picktop
#define rset_picktop(rs) (lj_fls(lj_bswap(rs)) ^ 0x18)
#endif
/* -- Spill slots --------------------------------------------------------- */
/* Stack layout for the compiled machine code (after stack adjustment). */
enum {
SPS_TEMP1, /* Temps (3*dword) for calls and asm_x87load. */
SPS_TEMP2,
SPS_TEMP3,
SPS_FIRST, /* First spill slot for general use. */
/* This definition must match with the *.dasc file(s). */
SPS_FIXED = 6 /* Available fixed spill slots in interpreter frame. */
};
/* Spill slots are 32 bit wide. An even/odd pair is used for FPRs. */
#define sps_scale(slot) (4 * (int32_t)(slot))
#define sps_adjust(as) (sps_scale((as->evenspill-SPS_FIXED+3)&~3))
/* -- Exit state ---------------------------------------------------------- */
/* This definition must match with the *.dasc file(s). */
typedef struct {
lua_Number fpr[RID_NUM_FPR]; /* Floating-point registers. */
int32_t gpr[RID_NUM_GPR]; /* General-purpose registers. */
int32_t spill[256]; /* Spill slots. */
} ExitState;
/* -- x86 ModRM operand encoding ------------------------------------------ */
typedef enum {
XM_OFS0 = 0x00, XM_OFS8 = 0x40, XM_OFS32 = 0x80, XM_REG = 0xc0,
XM_SCALE1 = 0x00, XM_SCALE2 = 0x40, XM_SCALE4 = 0x80, XM_SCALE8 = 0xc0,
XM_MASK = 0xc0
} x86Mode;
/* Structure to hold variable ModRM operand. */
typedef struct {
int32_t ofs; /* Offset. */
uint8_t base; /* Base register or RID_NONE. */
uint8_t idx; /* Index register or RID_NONE. */
uint8_t scale; /* Index scale (XM_SCALE1 .. XM_SCALE8). */
} x86ModRM;
/* -- Opcodes ------------------------------------------------------------- */
/* Macros to construct variable-length x86 opcodes. -(len+1) is in LSB. */
#define XO_(o) ((uint32_t)(0x0000fe + (0x##o<<24)))
#define XO_FPU(a,b) ((uint32_t)(0x00fd + (0x##a<<16)+(0x##b<<24)))
#define XO_0f(o) ((uint32_t)(0x0f00fd + (0x##o<<24)))
#define XO_66(o) ((uint32_t)(0x6600fd + (0x##o<<24)))
#define XO_660f(o) ((uint32_t)(0x0f66fc + (0x##o<<24)))
#define XO_f20f(o) ((uint32_t)(0x0ff2fc + (0x##o<<24)))
#define XO_f30f(o) ((uint32_t)(0x0ff3fc + (0x##o<<24)))
/* This list of x86 opcodes is not intended to be complete. Opcodes are only
** included when needed. Take a look at DynASM or jit.dis_x86 to see the
** whole mess.
*/
typedef enum {
/* Fixed length opcodes. XI_* prefix. */
XI_NOP = 0x90,
XI_CALL = 0xe8,
XI_JMP = 0xe9,
XI_JMPs = 0xeb,
XI_JCCs = 0x70, /* Really 7x. */
XI_JCCn = 0x80, /* Really 0f8x. */
XI_LEA = 0x8d,
XI_MOVri = 0xb8, /* Really b8+r. */
XI_ARITHib = 0x80,
XI_ARITHi = 0x81,
XI_ARITHi8 = 0x83,
XI_PUSHi8 = 0x6a,
XI_TEST = 0x85,
XI_MOVmi = 0xc7,
XI_BSWAP = 0xc8, /* Really 0fc8+r. */
/* Note: little-endian byte-order! */
XI_FLDZ = 0xeed9,
XI_FLD1 = 0xe8d9,
XI_FLDLG2 = 0xecd9,
XI_FLDLN2 = 0xedd9,
XI_FPOP = 0xd8dd, /* Really fstp st0. */
XI_FPOP1 = 0xd9dd, /* Really fstp st1. */
XI_FRNDINT = 0xfcd9,
XI_FSIN = 0xfed9,
XI_FCOS = 0xffd9,
XI_FPTAN = 0xf2d9,
XI_FPATAN = 0xf3d9,
XI_FSCALE = 0xfdd9,
XI_FYL2X = 0xf1d9,
/* Variable-length opcodes. XO_* prefix. */
XO_MOV = XO_(8b),
XO_MOVto = XO_(89),
XO_MOVtow = XO_66(89),
XO_MOVtob = XO_(88),
XO_MOVmi = XO_(c7),
XO_MOVmib = XO_(c6),
XO_LEA = XO_(8d),
XO_ARITHib = XO_(80),
XO_ARITHi = XO_(81),
XO_ARITHi8 = XO_(83),
XO_SHIFTi = XO_(c1),
XO_SHIFT1 = XO_(d1),
XO_SHIFTcl = XO_(d3),
XO_IMULi8 = XO_(6b),
XO_CMP = XO_(3b),
XO_TEST = XO_(85),
XO_GROUP3b = XO_(f6),
XO_GROUP3 = XO_(f7),
XO_MOVZXb = XO_0f(b6),
XO_MOVZXw = XO_0f(b7),
XO_MOVSXb = XO_0f(be),
XO_MOVSXw = XO_0f(bf),
XO_MOVSD = XO_f20f(10),
XO_MOVSDto = XO_f20f(11),
XO_MOVLPD = XO_660f(12),
XO_MOVAPS = XO_0f(28),
XO_XORPS = XO_0f(57),
XO_ANDPS = XO_0f(54),
XO_ADDSD = XO_f20f(58),
XO_SUBSD = XO_f20f(5c),
XO_MULSD = XO_f20f(59),
XO_DIVSD = XO_f20f(5e),
XO_SQRTSD = XO_f20f(51),
XO_MINSD = XO_f20f(5d),
XO_MAXSD = XO_f20f(5f),
XO_ROUNDSD = 0x0b3a0ffc, /* Really 66 0f 3a 0b. See asm_fpmath. */
XO_UCOMISD = XO_660f(2e),
XO_CVTSI2SD = XO_f20f(2a),
XO_CVTSD2SI = XO_f20f(2d),
XO_CVTTSD2SI= XO_f20f(2c),
XO_MOVDto = XO_660f(7e),
XO_FLDq = XO_(dd), XOg_FLDq = 0,
XO_FILDd = XO_(db), XOg_FILDd = 0,
XO_FSTPq = XO_(dd), XOg_FSTPq = 3,
XO_FISTPq = XO_(df), XOg_FISTPq = 7,
} x86Op;
/* x86 opcode groups. */
typedef uint32_t x86Group;
#define XG_(i8, i, g) ((x86Group)(((i8) << 16) + ((i) << 8) + (g)))
#define XG_ARITHi(g) XG_(XI_ARITHi8, XI_ARITHi, g)
#define XO_ARITH(a) ((x86Op)(0x030000fe + ((a)<<27)))
typedef enum {
XOg_ADD, XOg_OR, XOg_ADC, XOg_SBB, XOg_AND, XOg_SUB, XOg_XOR, XOg_CMP
} x86Arith;
typedef enum {
XOg_ROL, XOg_ROR, XOg_RCL, XOg_RCR, XOg_SHL, XOg_SHR, XOg_SAL, XOg_SAR
} x86Shift;
typedef enum {
XOg_TEST, XOg_TEST_, XOg_NOT, XOg_NEG, XOg_MUL, XOg_IMUL, XOg_DIV, XOg_IDIV
} x86Group3;
/* x86 condition codes. */
typedef enum {
CC_O, CC_NO, CC_B, CC_NB, CC_E, CC_NE, CC_BE, CC_NBE,
CC_S, CC_NS, CC_P, CC_NP, CC_L, CC_NL, CC_LE, CC_NLE,
CC_C = CC_B, CC_NAE = CC_C, CC_NC = CC_NB, CC_AE = CC_NB,
CC_Z = CC_E, CC_NZ = CC_NE, CC_NA = CC_BE, CC_A = CC_NBE,
CC_PE = CC_P, CC_PO = CC_NP, CC_NGE = CC_L, CC_GE = CC_NL,
CC_NG = CC_LE, CC_G = CC_NLE
} x86CC;
#endif

591
src/lj_trace.c Normal file
View File

@@ -0,0 +1,591 @@
/*
** Trace management.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_trace_c
#define LUA_CORE
#include "lj_obj.h"
#if LJ_HASJIT
#include "lj_gc.h"
#include "lj_err.h"
#include "lj_str.h"
#include "lj_frame.h"
#include "lj_state.h"
#include "lj_bc.h"
#include "lj_ir.h"
#include "lj_jit.h"
#include "lj_iropt.h"
#include "lj_mcode.h"
#include "lj_trace.h"
#include "lj_snap.h"
#include "lj_gdbjit.h"
#include "lj_record.h"
#include "lj_asm.h"
#include "lj_dispatch.h"
#include "lj_vm.h"
#include "lj_vmevent.h"
#include "lj_target.h"
/* -- Error handling ------------------------------------------------------ */
/* Synchronous abort with error message. */
void lj_trace_err(jit_State *J, TraceError e)
{
setnilV(&J->errinfo); /* No error info. */
setintV(J->L->top++, (int32_t)e);
lj_err_throw(J->L, LUA_ERRRUN);
}
/* Synchronous abort with error message and error info. */
void lj_trace_err_info(jit_State *J, TraceError e)
{
setintV(J->L->top++, (int32_t)e);
lj_err_throw(J->L, LUA_ERRRUN);
}
/* -- Trace management ---------------------------------------------------- */
/* The current trace is first assembled in J->cur. The variable length
** arrays point to shared, growable buffers (J->irbuf etc.). The trace is
** kept in this state until a new trace needs to be created. Then the current
** trace and its data structures are copied to a new (compact) Trace object.
*/
/* Find a free trace number. */
static TraceNo trace_findfree(jit_State *J)
{
MSize osz, lim;
if (J->freetrace == 0)
J->freetrace = 1;
for (; J->freetrace < J->sizetrace; J->freetrace++)
if (J->trace[J->freetrace] == NULL)
return J->freetrace++;
/* Need to grow trace array. */
lim = (MSize)J->param[JIT_P_maxtrace] + 1;
if (lim < 2) lim = 2; else if (lim > 65535) lim = 65535;
osz = J->sizetrace;
if (osz >= lim)
return 0; /* Too many traces. */
lj_mem_growvec(J->L, J->trace, J->sizetrace, lim, Trace *);
while (osz < J->sizetrace)
J->trace[osz++] = NULL;
return J->freetrace;
}
#define TRACE_COPYELEM(field, szfield, tp) \
T2->field = (tp *)p; \
memcpy(p, T->field, T->szfield*sizeof(tp)); \
p += T->szfield*sizeof(tp);
/* Save a trace by copying and compacting it. */
static Trace *trace_save(jit_State *J, Trace *T)
{
size_t sztr = ((sizeof(Trace)+7)&~7);
size_t szins = (T->nins-T->nk)*sizeof(IRIns);
size_t sz = sztr + szins +
T->nsnap*sizeof(SnapShot) +
T->nsnapmap*sizeof(IRRef2);
Trace *T2 = lj_mem_newt(J->L, (MSize)sz, Trace);
char *p = (char *)T2 + sztr;
memcpy(T2, T, sizeof(Trace));
T2->ir = (IRIns *)p - T->nk;
memcpy(p, T->ir+T->nk, szins);
p += szins;
TRACE_COPYELEM(snap, nsnap, SnapShot)
TRACE_COPYELEM(snapmap, nsnapmap, IRRef2)
lj_gc_barriertrace(J2G(J), T);
return T2;
}
/* Free a trace. */
static void trace_free(jit_State *J, TraceNo traceno)
{
lua_assert(traceno != 0);
if (traceno < J->freetrace)
J->freetrace = traceno;
lj_gdbjit_deltrace(J, J->trace[traceno]);
if (traceno == J->curtrace) {
lua_assert(J->trace[traceno] == &J->cur);
J->trace[traceno] = NULL;
J->curtrace = 0;
} else {
Trace *T = J->trace[traceno];
lua_assert(T != NULL && T != &J->cur);
J->trace[traceno] = NULL;
lj_mem_free(J2G(J), T,
((sizeof(Trace)+7)&~7) + (T->nins-T->nk)*sizeof(IRIns) +
T->nsnap*sizeof(SnapShot) + T->nsnapmap*sizeof(IRRef2));
}
}
/* Free all traces associated with a prototype. No unpatching needed. */
void lj_trace_freeproto(global_State *g, GCproto *pt)
{
jit_State *J = G2J(g);
TraceNo traceno;
/* Free all root traces. */
for (traceno = pt->trace; traceno != 0; ) {
TraceNo side, nextroot = J->trace[traceno]->nextroot;
/* Free all side traces. */
for (side = J->trace[traceno]->nextside; side != 0; ) {
TraceNo next = J->trace[side]->nextside;
trace_free(J, side);
side = next;
}
/* Now free the trace itself. */
trace_free(J, traceno);
traceno = nextroot;
}
}
/* Re-enable compiling a prototype by unpatching any modified bytecode. */
void lj_trace_reenableproto(GCproto *pt)
{
if ((pt->flags & PROTO_HAS_ILOOP)) {
BCIns *bc = pt->bc;
BCPos i, sizebc = pt->sizebc;;
pt->flags &= ~PROTO_HAS_ILOOP;
for (i = 0; i < sizebc; i++) {
BCOp op = bc_op(bc[i]);
if (op == BC_IFORL || op == BC_IITERL || op == BC_ILOOP)
setbc_op(&bc[i], (int)op+(int)BC_LOOP-(int)BC_ILOOP);
}
}
}
/* Unpatch the bytecode modified by a root trace. */
static void trace_unpatch(jit_State *J, Trace *T)
{
BCOp op = bc_op(T->startins);
uint32_t pcofs = T->snap[0].mapofs + T->snap[0].nslots;
BCIns *pc = ((BCIns *)(uintptr_t)T->snapmap[pcofs]) - 1;
switch (op) {
case BC_FORL:
lua_assert(bc_op(*pc) == BC_JFORI);
setbc_op(pc, BC_FORI); /* Unpatch JFORI, too. */
pc += bc_j(*pc);
lua_assert(bc_op(*pc) == BC_JFORL && J->trace[bc_d(*pc)] == T);
*pc = T->startins;
break;
case BC_LOOP:
lua_assert(bc_op(*pc) == BC_JLOOP && J->trace[bc_d(*pc)] == T);
*pc = T->startins;
break;
case BC_ITERL:
lua_assert(bc_op(*pc) == BC_JMP);
pc += bc_j(*pc)+2;
lua_assert(bc_op(*pc) == BC_JITERL && J->trace[bc_d(*pc)] == T);
*pc = T->startins;
break;
case BC_CALL:
lj_trace_err(J, LJ_TRERR_NYILNKF);
break;
case BC_JMP: /* No need to unpatch branches in parent traces (yet). */
default:
lua_assert(0);
break;
}
}
/* Flush a root trace and any attached side traces. */
void lj_trace_flush(jit_State *J, TraceNo traceno)
{
Trace *T = NULL;
GCproto *pt;
if (traceno > 0 && traceno <= J->sizetrace)
T = J->trace[traceno];
if (T == NULL)
return;
pt = &gcref(T->startpt)->pt;
if (T->root == 0 && pt != NULL) {
TraceNo side;
/* First unpatch any modified bytecode. */
trace_unpatch(J, T);
/* Unlink root trace from chain anchored in prototype. */
if (pt->trace == traceno) { /* Trace is first in chain. Easy. */
pt->trace = T->nextroot;
} else { /* Otherwise search in chain of root traces. */
Trace *T2 = J->trace[pt->trace];
while (T2->nextroot != traceno) {
lua_assert(T2->nextroot != 0);
T2 = J->trace[T2->nextroot];
}
T2->nextroot = T->nextroot; /* Unlink from chain. */
}
/* Free all side traces. */
for (side = T->nextside; side != 0; ) {
TraceNo next = J->trace[side]->nextside;
trace_free(J, side);
side = next;
}
/* Now free the trace itself. */
trace_free(J, traceno);
} /* Flush for non-root traces is currently ignored. */
}
/* Flush all traces associated with a prototype. */
void lj_trace_flushproto(global_State *g, GCproto *pt)
{
while (pt->trace != 0)
lj_trace_flush(G2J(g), pt->trace);
}
/* Flush all traces. */
int lj_trace_flushall(lua_State *L)
{
jit_State *J = L2J(L);
ptrdiff_t i;
if ((J2G(J)->hookmask & HOOK_GC))
return 1;
for (i = (ptrdiff_t)J->sizetrace-1; i > 0; i--)
lj_trace_flush(J, (TraceNo)i);
#ifdef LUA_USE_ASSERT
for (i = 0; i < (ptrdiff_t)J->sizetrace; i++)
lua_assert(J->trace[i] == NULL);
#endif
J->freetrace = 0;
/* Free the whole machine code and invalidate all exit stub groups. */
lj_mcode_free(J);
memset(J->exitstubgroup, 0, sizeof(J->exitstubgroup));
lj_vmevent_send(L, TRACE,
setstrV(L, L->top++, lj_str_newlit(L, "flush"));
);
return 0;
}
/* Free everything associated with the JIT compiler state. */
void lj_trace_freestate(global_State *g)
{
jit_State *J = G2J(g);
#ifdef LUA_USE_ASSERT
{ /* This assumes all traces have already been freed. */
ptrdiff_t i;
for (i = 0; i < (ptrdiff_t)J->sizetrace; i++)
lua_assert(J->trace[i] == NULL);
}
#endif
lj_mcode_free(J);
lj_ir_knum_freeall(J);
lj_mem_freevec(g, J->snapmapbuf, J->sizesnapmap, IRRef2);
lj_mem_freevec(g, J->snapbuf, J->sizesnap, SnapShot);
lj_mem_freevec(g, J->irbuf + J->irbotlim, J->irtoplim - J->irbotlim, IRIns);
lj_mem_freevec(g, J->trace, J->sizetrace, Trace *);
}
/* -- Trace compiler state machine ---------------------------------------- */
/* Penalize a bytecode instruction by bumping its hot counter. */
static void hotpenalty(jit_State *J, const BCIns *pc, TraceError e)
{
uint32_t i, val = HOTCOUNT_MIN_PENALTY;
for (i = 0; i < PENALTY_SLOTS; i++)
if (J->penalty[i].pc == pc) {
val = ((uint32_t)J->penalty[i].val << 1) + 1;
if (val > HOTCOUNT_MAX_PENALTY) val = HOTCOUNT_MAX_PENALTY;
goto setpenalty;
}
i = J->penaltyslot;
J->penaltyslot = (J->penaltyslot + 1) & (PENALTY_SLOTS-1);
J->penalty[i].pc = pc;
setpenalty:
J->penalty[i].val = (uint16_t)val;
J->penalty[i].reason = e;
hotcount_set(J2GG(J), pc+1, val);
}
/* Start tracing. */
static void trace_start(jit_State *J)
{
lua_State *L;
if (J->curtrace != 0 && J->trace[J->curtrace] == &J->cur) {
J->trace[J->curtrace] = trace_save(J, &J->cur); /* Save current trace. */
J->curtrace = 0;
}
if ((J->pt->flags & PROTO_NO_JIT)) { /* JIT disabled for this proto? */
if (J->parent == 0) {
if (J->pc >= J->pt->bc) {
/* Lazy bytecode patching to disable hotcount events. */
setbc_op(J->pc, (int)bc_op(*J->pc)+(int)BC_ILOOP-(int)BC_LOOP);
J->pt->flags |= PROTO_HAS_ILOOP;
} else {
/* NYI: lazy closure patching to disable hotcall events. */
lua_assert(0);
}
}
J->state = LJ_TRACE_IDLE; /* Silently ignored. */
return;
}
/* Get a new trace number. */
J->curtrace = trace_findfree(J);
if (LJ_UNLIKELY(J->curtrace == 0)) { /* No free trace? */
lua_assert((J2G(J)->hookmask & HOOK_GC) == 0);
lj_trace_flushall(J->L);
J->state = LJ_TRACE_IDLE; /* Silently ignored. */
return;
}
J->trace[J->curtrace] = &J->cur;
/* Setup enough of the current trace to be able to send the vmevent. */
memset(&J->cur, 0, sizeof(Trace));
J->cur.nins = J->cur.nk = REF_BASE;
J->cur.ir = J->irbuf;
J->cur.snap = J->snapbuf;
J->cur.snapmap = J->snapmapbuf;
/* J->cur.nsnapmap = 0; */
J->mergesnap = 0;
J->needsnap = 0;
J->guardemit.irt = 0;
L = J->L;
lj_vmevent_send(L, TRACE,
setstrV(L, L->top++, lj_str_newlit(L, "start"));
setintV(L->top++, J->curtrace);
setfuncV(L, L->top++, J->fn);
setintV(L->top++, J->pc - J->pt->bc + 1);
if (J->parent) {
setintV(L->top++, J->parent);
setintV(L->top++, J->exitno);
}
);
lj_record_setup(J);
}
/* Stop tracing. */
static void trace_stop(jit_State *J)
{
BCIns *pc = (BCIns *)J->startpc; /* Not const here. */
BCOp op = bc_op(J->cur.startins);
GCproto *pt = &gcref(J->cur.startpt)->pt;
lua_State *L;
switch (op) {
case BC_FORL:
setbc_op(pc+bc_j(J->cur.startins), BC_JFORI); /* Patch FORI, too. */
/* fallthrough */
case BC_LOOP:
case BC_ITERL:
/* Patch bytecode of starting instruction in root trace. */
setbc_op(pc, (int)op+(int)BC_JLOOP-(int)BC_LOOP);
setbc_d(pc, J->curtrace);
/* Add to root trace chain in prototype. */
J->cur.nextroot = pt->trace;
pt->trace = (TraceNo1)J->curtrace;
break;
case BC_CALL:
lj_trace_err(J, LJ_TRERR_NYILNKF);
break;
case BC_JMP:
/* Patch exit branch in parent to side trace entry. */
lua_assert(J->parent != 0 && J->cur.root != 0);
lj_asm_patchexit(J, J->trace[J->parent], J->exitno, J->cur.mcode);
/* Avoid compiling a side trace twice (stack resizing uses parent exit). */
J->trace[J->parent]->snap[J->exitno].count = SNAPCOUNT_DONE;
/* Add to side trace chain in root trace. */
{
Trace *root = J->trace[J->cur.root];
root->nchild++;
J->cur.nextside = root->nextside;
root->nextside = (TraceNo1)J->curtrace;
}
break;
default:
lua_assert(0);
break;
}
/* Commit new mcode only after all patching is done. */
lj_mcode_commit(J, J->cur.mcode);
lj_gdbjit_addtrace(J, &J->cur, J->curtrace);
L = J->L;
lj_vmevent_send(L, TRACE,
setstrV(L, L->top++, lj_str_newlit(L, "stop"));
setintV(L->top++, J->curtrace);
);
}
/* Abort tracing. */
static int trace_abort(jit_State *J)
{
lua_State *L = J->L;
TraceError e = LJ_TRERR_RECERR;
lj_mcode_abort(J);
if (tvisnum(L->top-1))
e = (TraceError)lj_num2int(numV(L->top-1));
if (e == LJ_TRERR_MCODELM) {
J->state = LJ_TRACE_ASM;
return 1; /* Retry ASM with new MCode area. */
}
if (J->parent == 0)
hotpenalty(J, J->startpc, e); /* Penalize starting instruction. */
if (J->curtrace) { /* Is there anything to abort? */
ptrdiff_t errobj = savestack(L, L->top-1); /* Stack may be resized. */
lj_vmevent_send(L, TRACE,
setstrV(L, L->top++, lj_str_newlit(L, "abort"));
setintV(L->top++, J->curtrace);
setfuncV(L, L->top++, J->fn);
setintV(L->top++, J->pc - J->pt->bc + 1);
copyTV(L, L->top++, restorestack(L, errobj));
copyTV(L, L->top++, &J->errinfo);
);
/* Drop aborted trace after the vmevent (which may still access it). */
J->trace[J->curtrace] = NULL;
if (J->curtrace < J->freetrace)
J->freetrace = J->curtrace;
J->curtrace = 0;
}
L->top--; /* Remove error object */
if (e == LJ_TRERR_MCODEAL)
lj_trace_flushall(L);
return 0;
}
/* State machine for the trace compiler. Protected callback. */
static TValue *trace_state(lua_State *L, lua_CFunction dummy, void *ud)
{
jit_State *J = (jit_State *)ud;
UNUSED(dummy);
do {
switch (J->state) {
case LJ_TRACE_START:
J->state = LJ_TRACE_RECORD; /* trace_start() may change state. */
trace_start(J);
lj_dispatch_update(J2G(J));
break;
case LJ_TRACE_RECORD:
setvmstate(J2G(J), RECORD);
lj_vmevent_send(L, RECORD,
setintV(L->top++, J->curtrace);
setfuncV(L, L->top++, J->fn);
setintV(L->top++, J->pc - J->pt->bc + 1);
setintV(L->top++, J->framedepth);
if (bcmode_mm(bc_op(*J->pc)) == MM_call) {
cTValue *o = &L->base[bc_a(*J->pc)];
if (bc_op(*J->pc) == BC_ITERC) o -= 3;
copyTV(L, L->top++, o);
}
);
lj_record_ins(J);
break;
case LJ_TRACE_END:
J->loopref = 0;
if ((J->flags & JIT_F_OPT_LOOP) && J->cur.link == J->curtrace) {
setvmstate(J2G(J), OPT);
lj_opt_dce(J);
if (lj_opt_loop(J)) { /* Loop optimization failed? */
J->loopref = J->cur.nins;
J->state = LJ_TRACE_RECORD; /* Try to continue recording. */
break;
}
J->loopref = J->chain[IR_LOOP]; /* Needed by assembler. */
}
J->state = LJ_TRACE_ASM;
break;
case LJ_TRACE_ASM:
setvmstate(J2G(J), ASM);
lj_asm_trace(J, &J->cur);
trace_stop(J);
setvmstate(J2G(J), INTERP);
J->state = LJ_TRACE_IDLE;
lj_dispatch_update(J2G(J));
return NULL;
default: /* Trace aborted asynchronously. */
setintV(L->top++, (int32_t)LJ_TRERR_RECERR);
/* fallthrough */
case LJ_TRACE_ERR:
if (trace_abort(J))
break; /* Retry. */
setvmstate(J2G(J), INTERP);
J->state = LJ_TRACE_IDLE;
lj_dispatch_update(J2G(J));
return NULL;
}
} while (J->state > LJ_TRACE_RECORD);
return NULL;
}
/* -- Event handling ------------------------------------------------------ */
/* A bytecode instruction is about to be executed. Record it. */
void lj_trace_ins(jit_State *J)
{
while (lj_vm_cpcall(J->L, trace_state, NULL, (void *)J) != 0)
J->state = LJ_TRACE_ERR;
}
/* Start recording a new trace. */
static void trace_new(jit_State *J)
{
/* Only start a new trace if not inside __gc call or vmevent. */
if (!(J2G(J)->hookmask & (HOOK_GC|HOOK_VMEVENT))) {
lua_assert(J->state == LJ_TRACE_IDLE);
J->state = LJ_TRACE_START;
J->fn = curr_func(J->L);
J->pt = funcproto(J->fn);
lj_trace_ins(J);
}
}
/* A hotcount triggered. Start recording a root trace. */
void lj_trace_hot(jit_State *J, const BCIns *pc)
{
lua_State *L = J->L;
L->top = curr_topL(L); /* Only called from Lua and NRESULTS is not used. */
hotcount_set(J2GG(J), pc, J->param[JIT_P_hotloop]+1); /* Reset hotcount. */
J->parent = 0; /* Root trace. */
J->exitno = 0;
J->pc = pc-1; /* The interpreter bytecode PC is offset by 1. */
trace_new(J);
}
/* A trace exited. Restore interpreter state and check for hot exits. */
void *lj_trace_exit(jit_State *J, void *exptr)
{
lua_State *L = J->L;
void *cf;
/* Restore interpreter state. */
lj_snap_restore(J, exptr);
cf = cframe_raw(L->cframe);
cframe_pc(cf) = J->pc;
lj_vmevent_send(L, TEXIT,
ExitState *ex = (ExitState *)exptr;
uint32_t i;
lj_state_checkstack(L, 4+RID_NUM_GPR+RID_NUM_FPR+LUA_MINSTACK);
setintV(L->top++, J->parent);
setintV(L->top++, J->exitno);
setintV(L->top++, RID_NUM_GPR);
setintV(L->top++, RID_NUM_FPR);
for (i = 0; i < RID_NUM_GPR; i++)
setintV(L->top++, ex->gpr[i]);
for (i = 0; i < RID_NUM_FPR; i++) {
setnumV(L->top, ex->fpr[i]);
if (LJ_UNLIKELY(tvisnan(L->top)))
setnanV(L->top);
L->top++;
}
);
{ /* Check for a hot exit. */
SnapShot *snap = &J->trace[J->parent]->snap[J->exitno];
if (snap->count != SNAPCOUNT_DONE &&
++snap->count >= J->param[JIT_P_hotexit])
trace_new(J); /* Start recording a side trace. */
}
return cf; /* Return the interpreter C frame. */
}
#endif

52
src/lj_trace.h Normal file
View File

@@ -0,0 +1,52 @@
/*
** Trace management.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_TRACE_H
#define _LJ_TRACE_H
#if LJ_HASJIT
#include "lj_obj.h"
#include "lj_jit.h"
#include "lj_dispatch.h"
/* Trace errors. */
typedef enum {
#define TREDEF(name, msg) LJ_TRERR_##name,
#include "lj_traceerr.h"
LJ_TRERR__MAX
} TraceError;
LJ_FUNC_NORET void lj_trace_err(jit_State *J, TraceError e);
LJ_FUNC_NORET void lj_trace_err_info(jit_State *J, TraceError e);
/* Trace management. */
LJ_FUNC void lj_trace_freeproto(global_State *g, GCproto *pt);
LJ_FUNC void lj_trace_reenableproto(GCproto *pt);
LJ_FUNC void lj_trace_flushproto(global_State *g, GCproto *pt);
LJ_FUNC void lj_trace_flush(jit_State *J, TraceNo traceno);
LJ_FUNC int lj_trace_flushall(lua_State *L);
LJ_FUNC void lj_trace_freestate(global_State *g);
/* Event handling. */
LJ_FUNC void lj_trace_ins(jit_State *J);
LJ_FUNCA void lj_trace_hot(jit_State *J, const BCIns *pc);
LJ_FUNCA void *lj_trace_exit(jit_State *J, void *exptr);
/* Signal asynchronous abort of trace or end of trace. */
#define lj_trace_abort(g) (G2J(g)->state &= ~LJ_TRACE_ACTIVE)
#define lj_trace_end(J) (J->state = LJ_TRACE_END)
#else
#define lj_trace_flushall(L) (UNUSED(L), 0)
#define lj_trace_freestate(g) UNUSED(g)
#define lj_trace_freeproto(g, pt) (UNUSED(g), UNUSED(pt), (void)0)
#define lj_trace_abort(g) UNUSED(g)
#define lj_trace_end(J) UNUSED(J)
#endif
#endif

59
src/lj_traceerr.h Normal file
View File

@@ -0,0 +1,59 @@
/*
** Trace compiler error messages.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
/* This file may be included multiple times with different TREDEF macros. */
/* Recording. */
TREDEF(RECERR, "error thrown or hook called during recording")
TREDEF(TRACEOV, "trace too long")
TREDEF(STACKOV, "trace too deep")
TREDEF(SNAPOV, "too many snapshots")
TREDEF(NYIBC, "NYI: bytecode %d")
/* Recording loop ops. */
TREDEF(LLEAVE, "leaving loop in root trace")
TREDEF(LINNER, "inner loop in root trace")
TREDEF(LUNROLL, "loop unroll limit reached")
TREDEF(LBLACKL, "blacklisted loop")
/* Recording calls/returns. */
TREDEF(BADTYPE, "bad argument type")
TREDEF(CJITOFF, "call to JIT-disabled function")
TREDEF(CUNROLL, "call unroll limit reached")
TREDEF(NYIRECU, "NYI: recursive calls")
TREDEF(NYILNKF, "NYI: linking/patching function calls")
TREDEF(NYIVF, "NYI: vararg function")
TREDEF(NYICF, "NYI: C function %p")
TREDEF(NYIFF, "NYI: FastFunc %s")
TREDEF(NYIFFU, "NYI: unsupported variant of FastFunc %s")
TREDEF(NYIRETL, "NYI: return to lower frame")
/* Recording indexed load/store. */
TREDEF(STORENN, "store with nil or NaN key")
TREDEF(NOMM, "missing metamethod")
TREDEF(IDXLOOP, "looping index lookup")
TREDEF(NYITMIX, "NYI: mixed sparse/dense table")
/* Optimizations. */
TREDEF(GFAIL, "guard would always fail")
TREDEF(PHIOV, "too many PHIs")
TREDEF(TYPEINS, "persistent type instability")
/* Assembler. */
TREDEF(MCODEAL, "failed to allocate mcode memory")
TREDEF(MCODEOV, "machine code too long")
TREDEF(MCODELM, "hit mcode limit (retrying)")
TREDEF(SPILLOV, "too many spill slots")
TREDEF(BADRA, "inconsistent register allocation")
TREDEF(NYIIR, "NYI: cannot assemble IR instruction %d")
TREDEF(NYIPHI, "NYI: PHI shuffling too complex")
TREDEF(NYICOAL, "NYI: register coalescing too complex")
TREDEF(NYIGCF, "NYI: gcstep sync with frames")
#undef TREDEF
/* Detecting unused error messages:
awk -F, '/^TREDEF/ { gsub(/TREDEF./, ""); printf "grep -q LJ_TRERR_%s *.[ch] || echo %s\n", $1, $1}' lj_traceerr.h | sh
*/

33
src/lj_udata.c Normal file
View File

@@ -0,0 +1,33 @@
/*
** Userdata handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#define lj_udata_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_gc.h"
#include "lj_udata.h"
GCudata *lj_udata_new(lua_State *L, MSize sz, GCtab *env)
{
GCudata *ud = lj_mem_newt(L, sizeof(GCudata) + sz, GCudata);
global_State *g = G(L);
newwhite(g, ud); /* Not finalized. */
ud->gct = ~LJ_TUDATA;
ud->len = sz;
/* NOBARRIER: The GCudata is new (marked white). */
setgcrefnull(ud->metatable);
setgcref(ud->env, obj2gco(env));
/* Chain to userdata list (after main thread). */
setgcrefr(ud->nextgc, mainthread(g)->nextgc);
setgcref(mainthread(g)->nextgc, obj2gco(ud));
return ud;
}
void LJ_FASTCALL lj_udata_free(global_State *g, GCudata *ud)
{
lj_mem_free(g, ud, sizeudata(ud));
}

14
src/lj_udata.h Normal file
View File

@@ -0,0 +1,14 @@
/*
** Userdata handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_UDATA_H
#define _LJ_UDATA_H
#include "lj_obj.h"
LJ_FUNC GCudata *lj_udata_new(lua_State *L, MSize sz, GCtab *env);
LJ_FUNC void LJ_FASTCALL lj_udata_free(global_State *g, GCudata *ud);
#endif

66
src/lj_vm.h Normal file
View File

@@ -0,0 +1,66 @@
/*
** Assembler VM interface definitions.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_VM_H
#define _LJ_VM_H
#include "lj_obj.h"
/* Entry points for ASM parts of VM. */
LJ_ASMF void lj_vm_call(lua_State *L, TValue *base, int nres1);
LJ_ASMF int lj_vm_pcall(lua_State *L, TValue *base, int nres1, ptrdiff_t ef);
typedef TValue *(*lua_CPFunction)(lua_State *L, lua_CFunction func, void *ud);
LJ_ASMF int lj_vm_cpcall(lua_State *L, lua_CPFunction cp, lua_CFunction func,
void *ud);
LJ_ASMF int lj_vm_resume(lua_State *L, TValue *base, int nres1, ptrdiff_t ef);
LJ_ASMF_NORET void lj_vm_unwind_c(void *cframe, int errcode);
LJ_ASMF_NORET void lj_vm_unwind_ff(void *cframe);
/* Miscellaneous functions. */
#if LJ_TARGET_X86ORX64
LJ_ASMF int lj_vm_cpuid(uint32_t f, uint32_t res[4]);
#endif
LJ_ASMF double lj_vm_foldarith(double x, double y, int op);
LJ_ASMF double lj_vm_foldfpm(double x, int op);
/* Dispatch targets for recording and hooks. */
LJ_ASMF void lj_vm_record(void);
LJ_ASMF void lj_vm_hook(void);
/* Trace exit handling. */
LJ_ASMF void lj_vm_exit_handler(void);
LJ_ASMF void lj_vm_exit_interp(void);
/* Handlers callable from compiled code. */
LJ_ASMF void lj_vm_floor(void);
LJ_ASMF void lj_vm_ceil(void);
LJ_ASMF void lj_vm_trunc(void);
LJ_ASMF void lj_vm_exp(void);
LJ_ASMF void lj_vm_exp2(void);
LJ_ASMF void lj_vm_pow(void);
LJ_ASMF void lj_vm_powi(void);
/* Call gates for functions. */
LJ_ASMF void lj_gate_lf(void);
LJ_ASMF void lj_gate_lv(void);
LJ_ASMF void lj_gate_c(void);
/* Continuations for metamethods. */
LJ_ASMF void lj_cont_cat(void); /* Continue with concatenation. */
LJ_ASMF void lj_cont_ra(void); /* Store result in RA from instruction. */
LJ_ASMF void lj_cont_nop(void); /* Do nothing, just continue execution. */
LJ_ASMF void lj_cont_condt(void); /* Branch if result is true. */
LJ_ASMF void lj_cont_condf(void); /* Branch if result is false. */
/* Start of the ASM code. */
LJ_ASMF void lj_vm_asm_begin(void);
/* Opcode handler offsets, relative to lj_vm_asm_begin. */
LJ_ASMF const uint16_t lj_vm_op_ofs[];
#define makeasmfunc(ofs) \
((ASMFunction)((char *)lj_vm_asm_begin + (ofs)))
#endif

56
src/lj_vmevent.c Normal file
View File

@@ -0,0 +1,56 @@
/*
** VM event handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#include <stdio.h>
#define lj_vmevent_c
#define LUA_CORE
#include "lj_obj.h"
#include "lj_str.h"
#include "lj_tab.h"
#include "lj_state.h"
#include "lj_dispatch.h"
#include "lj_vm.h"
#include "lj_vmevent.h"
ptrdiff_t lj_vmevent_prepare(lua_State *L, VMEvent ev)
{
global_State *g = G(L);
GCstr *s = lj_str_newlit(L, LJ_VMEVENTS_REGKEY);
cTValue *tv = lj_tab_getstr(tabV(registry(L)), s);
if (tvistab(tv)) {
int hash = VMEVENT_HASH(ev);
tv = lj_tab_getint(tabV(tv), hash);
if (tv && tvisfunc(tv)) {
lj_state_checkstack(L, LUA_MINSTACK);
setfuncV(L, L->top++, funcV(tv));
return savestack(L, L->top);
}
}
g->vmevmask &= ~VMEVENT_MASK(ev); /* No handler: cache this fact. */
return 0;
}
void lj_vmevent_call(lua_State *L, ptrdiff_t argbase)
{
global_State *g = G(L);
uint8_t oldmask = g->vmevmask;
uint8_t oldh = hook_save(g);
int status;
g->vmevmask = 0; /* Disable all events. */
hook_vmevent(g);
status = lj_vm_pcall(L, restorestack(L, argbase), 0+1, 0);
if (LJ_UNLIKELY(status)) {
/* Really shouldn't use stderr here, but where else to complain? */
L->top--;
fprintf(stderr, "VM handler failed: %s\n",
tvisstr(L->top) ? strVdata(L->top) : "?");
}
hook_restore(g, oldh);
if (g->vmevmask != VMEVENT_NOCACHE)
g->vmevmask = oldmask; /* Restore event mask, but not if not modified. */
}

49
src/lj_vmevent.h Normal file
View File

@@ -0,0 +1,49 @@
/*
** VM event handling.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LJ_VMEVENT_H
#define _LJ_VMEVENT_H
#include "lj_obj.h"
/* Registry key for VM event handler table. */
#define LJ_VMEVENTS_REGKEY "_VMEVENTS"
#define LJ_VMEVENTS_HSIZE 4
#define VMEVENT_MASK(ev) ((uint8_t)1 << ((int)(ev) & 7))
#define VMEVENT_HASH(ev) ((int)(ev) & ~7)
#define VMEVENT_HASHIDX(h) ((int)(h) << 3)
#define VMEVENT_NOCACHE 255
#define VMEVENT_DEF(name, hash) \
LJ_VMEVENT_##name##_, \
LJ_VMEVENT_##name = ((LJ_VMEVENT_##name##_) & 7)|((hash) << 3)
/* VM event IDs. */
typedef enum {
VMEVENT_DEF(BC, 0x0000140b),
VMEVENT_DEF(TRACE, 0x10ea574d),
VMEVENT_DEF(RECORD, 0x5698231c),
VMEVENT_DEF(TEXIT, 0x12d984a7),
LJ_VMEVENT__MAX
} VMEvent;
#ifdef LUAJIT_DISABLE_VMEVENT
#define lj_vmevent_send(L, ev, args) UNUSED(L)
#else
#define lj_vmevent_send(L, ev, args) \
if (G(L)->vmevmask & VMEVENT_MASK(LJ_VMEVENT_##ev)) { \
ptrdiff_t argbase = lj_vmevent_prepare(L, LJ_VMEVENT_##ev); \
if (argbase) { \
args \
lj_vmevent_call(L, argbase); \
} \
}
LJ_FUNC ptrdiff_t lj_vmevent_prepare(lua_State *L, VMEvent ev);
LJ_FUNC void lj_vmevent_call(lua_State *L, ptrdiff_t argbase);
#endif
#endif

70
src/ljamalg.c Normal file
View File

@@ -0,0 +1,70 @@
/*
** LuaJIT core and libraries amalgamation.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
/*
+--------------------------------------------------------------------------+
| WARNING: Compiling the amalgamation needs a lot of virtual memory |
| (around 160 MB with GCC 4.x)! If you don't have enough physical memory |
| your machine will start swapping to disk and the compile will not finish |
| within a reasonable amount of time. |
| So either compile on a bigger machine or use the non-amalgamated build. |
+--------------------------------------------------------------------------+
*/
#define ljamalg_c
#define LUA_CORE
/* To get the mremap prototype. Must be defind before any system includes. */
#if defined(__linux__) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE
#endif
#include "lua.h"
#include "lauxlib.h"
#include "lj_gc.c"
#include "lj_err.c"
#include "lj_ctype.c"
#include "lj_bc.c"
#include "lj_obj.c"
#include "lj_str.c"
#include "lj_tab.c"
#include "lj_func.c"
#include "lj_udata.c"
#include "lj_meta.c"
#include "lj_state.c"
#include "lj_dispatch.c"
#include "lj_vmevent.c"
#include "lj_api.c"
#include "lj_lex.c"
#include "lj_parse.c"
#include "lj_lib.c"
#include "lj_ir.c"
#include "lj_opt_mem.c"
#include "lj_opt_fold.c"
#include "lj_opt_narrow.c"
#include "lj_opt_dce.c"
#include "lj_opt_loop.c"
#include "lj_mcode.c"
#include "lj_snap.c"
#include "lj_record.c"
#include "lj_asm.c"
#include "lj_trace.c"
#include "lj_gdbjit.c"
#include "lj_alloc.c"
#include "lib_aux.c"
#include "lib_base.c"
#include "lib_math.c"
#include "lib_string.c"
#include "lib_table.c"
#include "lib_io.c"
#include "lib_os.c"
#include "lib_package.c"
#include "lib_debug.c"
#include "lib_bit.c"
#include "lib_jit.c"
#include "lib_init.c"

388
src/lua.h Normal file
View File

@@ -0,0 +1,388 @@
/*
** $Id: lua.h,v 1.218.1.5 2008/08/06 13:30:12 roberto Exp $
** Lua - An Extensible Extension Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
*/
#ifndef lua_h
#define lua_h
#include <stdarg.h>
#include <stddef.h>
#include "luaconf.h"
#define LUA_VERSION "Lua 5.1"
#define LUA_RELEASE "Lua 5.1.4"
#define LUA_VERSION_NUM 501
#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes"
/* mark for precompiled code (`<esc>Lua') */
#define LUA_SIGNATURE "\033Lua"
/* option for multiple returns in `lua_pcall' and `lua_call' */
#define LUA_MULTRET (-1)
/*
** pseudo-indices
*/
#define LUA_REGISTRYINDEX (-10000)
#define LUA_ENVIRONINDEX (-10001)
#define LUA_GLOBALSINDEX (-10002)
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
/* thread status; 0 is OK */
#define LUA_YIELD 1
#define LUA_ERRRUN 2
#define LUA_ERRSYNTAX 3
#define LUA_ERRMEM 4
#define LUA_ERRERR 5
typedef struct lua_State lua_State;
typedef int (*lua_CFunction) (lua_State *L);
/*
** functions that read/write blocks when loading/dumping Lua chunks
*/
typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);
typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);
/*
** prototype for memory-allocation functions
*/
typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
/*
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
/* minimum Lua stack available to a C function */
#define LUA_MINSTACK 20
/*
** generic extra include file
*/
#if defined(LUA_USER_H)
#include LUA_USER_H
#endif
/* type of numbers in Lua */
typedef LUA_NUMBER lua_Number;
/* type for integer functions */
typedef LUA_INTEGER lua_Integer;
/*
** state manipulation
*/
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);
/*
** basic stack manipulation
*/
LUA_API int (lua_gettop) (lua_State *L);
LUA_API void (lua_settop) (lua_State *L, int idx);
LUA_API void (lua_pushvalue) (lua_State *L, int idx);
LUA_API void (lua_remove) (lua_State *L, int idx);
LUA_API void (lua_insert) (lua_State *L, int idx);
LUA_API void (lua_replace) (lua_State *L, int idx);
LUA_API int (lua_checkstack) (lua_State *L, int sz);
LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n);
/*
** access functions (stack -> C)
*/
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
LUA_API const char *(lua_typename) (lua_State *L, int tp);
LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2);
LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2);
LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx);
LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_objlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);
/*
** push functions (C -> stack)
*/
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l);
LUA_API void (lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,
va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
/*
** get functions (Lua -> stack)
*/
LUA_API void (lua_gettable) (lua_State *L, int idx);
LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawget) (lua_State *L, int idx);
LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n);
LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API void (lua_getfenv) (lua_State *L, int idx);
/*
** set functions (stack -> Lua)
*/
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, int n);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API int (lua_setfenv) (lua_State *L, int idx);
/*
** `load' and `call' functions (load and run Lua code)
*/
LUA_API void (lua_call) (lua_State *L, int nargs, int nresults);
LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);
LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
const char *chunkname);
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
/*
** coroutine functions
*/
LUA_API int (lua_yield) (lua_State *L, int nresults);
LUA_API int (lua_resume) (lua_State *L, int narg);
LUA_API int (lua_status) (lua_State *L);
/*
** garbage-collection function and options
*/
#define LUA_GCSTOP 0
#define LUA_GCRESTART 1
#define LUA_GCCOLLECT 2
#define LUA_GCCOUNT 3
#define LUA_GCCOUNTB 4
#define LUA_GCSTEP 5
#define LUA_GCSETPAUSE 6
#define LUA_GCSETSTEPMUL 7
LUA_API int (lua_gc) (lua_State *L, int what, int data);
/*
** miscellaneous functions
*/
LUA_API int (lua_error) (lua_State *L);
LUA_API int (lua_next) (lua_State *L, int idx);
LUA_API void (lua_concat) (lua_State *L, int n);
LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);
LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define lua_pop(L,n) lua_settop(L, -(n)-1)
#define lua_newtable(L) lua_createtable(L, 0, 0)
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
#define lua_strlen(L,i) lua_objlen(L, (i))
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD)
#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE)
#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0)
#define lua_pushliteral(L, s) \
lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
/*
** compatibility macros and functions
*/
#define lua_open() luaL_newstate()
#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX)
#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0)
#define lua_Chunkreader lua_Reader
#define lua_Chunkwriter lua_Writer
/* hack */
LUA_API void lua_setlevel (lua_State *from, lua_State *to);
/*
** {======================================================================
** Debug API
** =======================================================================
*/
/*
** Event codes
*/
#define LUA_HOOKCALL 0
#define LUA_HOOKRET 1
#define LUA_HOOKLINE 2
#define LUA_HOOKCOUNT 3
#define LUA_HOOKTAILRET 4
/*
** Event masks
*/
#define LUA_MASKCALL (1 << LUA_HOOKCALL)
#define LUA_MASKRET (1 << LUA_HOOKRET)
#define LUA_MASKLINE (1 << LUA_HOOKLINE)
#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT)
typedef struct lua_Debug lua_Debug; /* activation record */
/* Functions to be called by the debuger in specific events */
typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);
LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);
LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);
LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);
LUA_API lua_Hook lua_gethook (lua_State *L);
LUA_API int lua_gethookmask (lua_State *L);
LUA_API int lua_gethookcount (lua_State *L);
struct lua_Debug {
int event;
const char *name; /* (n) */
const char *namewhat; /* (n) `global', `local', `field', `method' */
const char *what; /* (S) `Lua', `C', `main', `tail' */
const char *source; /* (S) */
int currentline; /* (l) */
int nups; /* (u) number of upvalues */
int linedefined; /* (S) */
int lastlinedefined; /* (S) */
char short_src[LUA_IDSIZE]; /* (S) */
/* private part */
int i_ci; /* active function */
};
/* }====================================================================== */
/******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. 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.
******************************************************************************/
#endif

9
src/lua.hpp Normal file
View File

@@ -0,0 +1,9 @@
// lua.hpp
// Lua header files for C++
// <<extern "C">> not supplied automatically because Lua also compiles as C++
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

133
src/luaconf.h Normal file
View File

@@ -0,0 +1,133 @@
/*
** Configuration header.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef luaconf_h
#define luaconf_h
#include <limits.h>
#include <stddef.h>
/* Try to determine supported features for a couple of standard platforms. */
#if defined(_WIN32)
#define LUA_USE_WIN
#define LUA_DL_DLL
#elif defined(__linux__) || defined(__solaris__) || defined(__CYGWIN__) || \
defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
(defined(__MACH__) && defined(__APPLE__))
#define LUA_USE_POSIX
#define LUA_DL_DLOPEN
#endif
/* Default path for loading Lua and C modules with require(). */
#ifdef LUA_USE_WIN
/*
** In Windows, any exclamation mark ('!') in the path is replaced by the
** path of the directory of the executable file of the current process.
*/
#define LUA_LDIR "!\\lua\\"
#define LUA_CDIR "!\\"
#define LUA_PATH_DEFAULT \
".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;"
#define LUA_CPATH_DEFAULT \
".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll"
#else
#define LUA_ROOT "/usr/local/"
#define LUA_JDIR LUA_ROOT "share/luajit-2.0.0-beta1/"
#define LUA_LDIR LUA_ROOT "share/lua/5.1/"
#define LUA_CDIR LUA_ROOT "lib/lua/5.1/"
#define LUA_PATH_DEFAULT \
"./?.lua;" LUA_JDIR"?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;"
#define LUA_CPATH_DEFAULT \
"./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so"
#endif
/* Environment variable names for path overrides and initialization code. */
#define LUA_PATH "LUA_PATH"
#define LUA_CPATH "LUA_CPATH"
#define LUA_INIT "LUA_INIT"
/* Special file system characters. */
#ifdef LUA_USE_WIN
#define LUA_DIRSEP "\\"
#else
#define LUA_DIRSEP "/"
#endif
#define LUA_PATHSEP ";"
#define LUA_PATH_MARK "?"
#define LUA_EXECDIR "!"
#define LUA_IGMARK "-"
#define LUA_PATH_CONFIG \
LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" \
LUA_EXECDIR "\n" LUA_IGMARK
/* Quoting in error messages. */
#define LUA_QL(x) "'" x "'"
#define LUA_QS LUA_QL("%s")
/* Various tunables. */
#define LUAI_MAXSTACK 65500 /* Max. # of stack slots for a thread (<64K). */
#define LUAI_MAXCSTACK 8000 /* Max. # of stack slots for a C func (<10K). */
#define LUAI_GCPAUSE 200 /* Pause GC until memory is at 200%. */
#define LUAI_GCMUL 200 /* Run GC at 200% of allocation speed. */
#define LUA_MAXCAPTURES 32 /* Max. pattern captures. */
/* Compatibility with older library function names. */
#define LUA_COMPAT_MOD /* OLD: math.mod, NEW: math.fmod */
#define LUA_COMPAT_GFIND /* OLD: string.gfind, NEW: string.gmatch */
/* Configuration for the frontend (the luajit executable). */
#if defined(luajit_c)
#define LUA_PROGNAME "luajit" /* Fallback frontend name. */
#define LUA_PROMPT "> " /* Interactive prompt. */
#define LUA_PROMPT2 ">> " /* Continuation prompt. */
#define LUA_MAXINPUT 512 /* Max. input line length. */
#endif
/* Note: changing the following defines breaks the Lua 5.1 ABI. */
#define LUA_INTEGER ptrdiff_t
#define LUA_IDSIZE 60 /* Size of lua_Debug.short_src. */
#define LUAL_BUFFERSIZE BUFSIZ /* Size of lauxlib and io.* buffers. */
/* The following defines are here only for compatibility with luaconf.h
** from the standard Lua distribution. They must not be changed for LuaJIT.
*/
#define LUA_NUMBER_DOUBLE
#define LUA_NUMBER double
#define LUAI_UACNUMBER double
#define LUA_NUMBER_SCAN "%lf"
#define LUA_NUMBER_FMT "%.14g"
#define lua_number2str(s, n) sprintf((s), LUA_NUMBER_FMT, (n))
#define LUAI_MAXNUMBER2STR 32
#define lua_str2number(s, p) strtod((s), (p))
#define LUA_INTFRMLEN "l"
#define LUA_INTFRM_T long
/* Linkage of public API functions. */
#if defined(LUA_BUILD_AS_DLL)
#if defined(LUA_CORE) || defined(LUA_LIB)
#define LUA_API __declspec(dllexport)
#else
#define LUA_API __declspec(dllimport)
#endif
#else
#define LUA_API extern
#endif
#define LUALIB_API LUA_API
/* Support for internal assertions. */
#if defined(LUA_USE_ASSERT) || defined(LUA_USE_APICHECK)
#include <assert.h>
#endif
#ifdef LUA_USE_ASSERT
#define lua_assert(x) assert(x)
#endif
#ifdef LUA_USE_APICHECK
#define luai_apicheck(L, o) { (void)L; assert(o); }
#else
#define luai_apicheck(L, o) { (void)L; }
#endif
#endif

519
src/luajit.c Normal file
View File

@@ -0,0 +1,519 @@
/*
** LuaJIT frontend. Runs commands, scripts, read-eval-print (REPL) etc.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
**
** Major portions taken verbatim or adapted from the Lua interpreter.
** Copyright (C) 1994-2008 Lua.org, PUC-Rio. See Copyright Notice in lua.h
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define luajit_c
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "luajit.h"
#if defined(LUA_USE_POSIX)
#include <unistd.h>
#define lua_stdin_is_tty() isatty(0)
#elif defined(LUA_USE_WIN)
#include <io.h>
#ifdef __BORLANDC__
#define lua_stdin_is_tty() isatty(_fileno(stdin))
#else
#define lua_stdin_is_tty() _isatty(_fileno(stdin))
#endif
#else
#define lua_stdin_is_tty() 1
#endif
static lua_State *globalL = NULL;
static const char *progname = LUA_PROGNAME;
static void lstop(lua_State *L, lua_Debug *ar)
{
(void)ar; /* unused arg. */
lua_sethook(L, NULL, 0, 0);
/* Avoid luaL_error -- a C hook doesn't add an extra frame. */
luaL_where(L, 0);
lua_pushfstring(L, "%sinterrupted!", lua_tostring(L, -1));
lua_error(L);
}
static void laction(int i)
{
signal(i, SIG_DFL); /* if another SIGINT happens before lstop,
terminate process (default action) */
lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);
}
static void print_usage(void)
{
fprintf(stderr,
"usage: %s [options] [script [args]].\n"
"Available options are:\n"
" -e stat execute string " LUA_QL("stat") "\n"
" -l name require library " LUA_QL("name") "\n"
" -j cmd perform LuaJIT control command\n"
" -O[lvl] set LuaJIT optimization level\n"
" -i enter interactive mode after executing " LUA_QL("script") "\n"
" -v show version information\n"
" -- stop handling options\n"
" - execute stdin and stop handling options\n"
,
progname);
fflush(stderr);
}
static void l_message(const char *pname, const char *msg)
{
if (pname) fprintf(stderr, "%s: ", pname);
fprintf(stderr, "%s\n", msg);
fflush(stderr);
}
static int report(lua_State *L, int status)
{
if (status && !lua_isnil(L, -1)) {
const char *msg = lua_tostring(L, -1);
if (msg == NULL) msg = "(error object is not a string)";
l_message(progname, msg);
lua_pop(L, 1);
}
return status;
}
static int traceback(lua_State *L)
{
if (!lua_isstring(L, 1)) /* 'message' not a string? */
return 1; /* keep it intact */
lua_getfield(L, LUA_GLOBALSINDEX, "debug");
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
return 1;
}
lua_getfield(L, -1, "traceback");
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2);
return 1;
}
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
return 1;
}
static int docall(lua_State *L, int narg, int clear)
{
int status;
int base = lua_gettop(L) - narg; /* function index */
lua_pushcfunction(L, traceback); /* push traceback function */
lua_insert(L, base); /* put it under chunk and args */
signal(SIGINT, laction);
status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
signal(SIGINT, SIG_DFL);
lua_remove(L, base); /* remove traceback function */
/* force a complete garbage collection in case of errors */
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
return status;
}
static void print_version(void)
{
fprintf(stderr,
LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n");
}
static void print_jit_status(lua_State *L)
{
int n;
const char *s;
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, "jit"); /* Get jit.* module table. */
lua_remove(L, -2);
lua_getfield(L, -1, "status");
lua_remove(L, -2);
n = lua_gettop(L);
lua_call(L, 0, LUA_MULTRET);
fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stderr);
for (n++; (s = lua_tostring(L, n)); n++)
fprintf(stderr, " %s", s);
fputs("\n", stdout);
}
static int getargs(lua_State *L, char **argv, int n)
{
int narg;
int i;
int argc = 0;
while (argv[argc]) argc++; /* count total number of arguments */
narg = argc - (n + 1); /* number of arguments to the script */
luaL_checkstack(L, narg + 3, "too many arguments to script");
for (i = n+1; i < argc; i++)
lua_pushstring(L, argv[i]);
lua_createtable(L, narg, n + 1);
for (i = 0; i < argc; i++) {
lua_pushstring(L, argv[i]);
lua_rawseti(L, -2, i - n);
}
return narg;
}
static int dofile(lua_State *L, const char *name)
{
int status = luaL_loadfile(L, name) || docall(L, 0, 1);
return report(L, status);
}
static int dostring(lua_State *L, const char *s, const char *name)
{
int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
return report(L, status);
}
static int dolibrary(lua_State *L, const char *name)
{
lua_getglobal(L, "require");
lua_pushstring(L, name);
return report(L, docall(L, 1, 1));
}
static void write_prompt(lua_State *L, int firstline)
{
const char *p;
lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
p = lua_tostring(L, -1);
if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2;
fputs(p, stdout);
fflush(stdout);
lua_pop(L, 1); /* remove global */
}
static int incomplete(lua_State *L, int status)
{
if (status == LUA_ERRSYNTAX) {
size_t lmsg;
const char *msg = lua_tolstring(L, -1, &lmsg);
const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
if (strstr(msg, LUA_QL("<eof>")) == tp) {
lua_pop(L, 1);
return 1;
}
}
return 0; /* else... */
}
static int pushline(lua_State *L, int firstline)
{
char buf[LUA_MAXINPUT];
write_prompt(L, firstline);
if (fgets(buf, LUA_MAXINPUT, stdin)) {
size_t len = strlen(buf);
if (len > 0 && buf[len-1] == '\n')
buf[len-1] = '\0';
if (firstline && buf[0] == '=')
lua_pushfstring(L, "return %s", buf+1);
else
lua_pushstring(L, buf);
return 1;
}
return 0;
}
static int loadline(lua_State *L)
{
int status;
lua_settop(L, 0);
if (!pushline(L, 1))
return -1; /* no input */
for (;;) { /* repeat until gets a complete line */
status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
if (!incomplete(L, status)) break; /* cannot try to add lines? */
if (!pushline(L, 0)) /* no more input? */
return -1;
lua_pushliteral(L, "\n"); /* add a new line... */
lua_insert(L, -2); /* ...between the two lines */
lua_concat(L, 3); /* join them */
}
lua_remove(L, 1); /* remove line */
return status;
}
static void dotty(lua_State *L)
{
int status;
const char *oldprogname = progname;
progname = NULL;
while ((status = loadline(L)) != -1) {
if (status == 0) status = docall(L, 0, 0);
report(L, status);
if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */
lua_getglobal(L, "print");
lua_insert(L, 1);
if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
l_message(progname,
lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)",
lua_tostring(L, -1)));
}
}
lua_settop(L, 0); /* clear stack */
fputs("\n", stdout);
fflush(stdout);
progname = oldprogname;
}
static int handle_script(lua_State *L, char **argv, int n)
{
int status;
const char *fname;
int narg = getargs(L, argv, n); /* collect arguments */
lua_setglobal(L, "arg");
fname = argv[n];
if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)
fname = NULL; /* stdin */
status = luaL_loadfile(L, fname);
lua_insert(L, -(narg+1));
if (status == 0)
status = docall(L, narg, 0);
else
lua_pop(L, narg);
return report(L, status);
}
/* Load add-on module. */
static int loadjitmodule(lua_State *L, const char *notfound)
{
lua_getglobal(L, "require");
lua_pushliteral(L, "jit.");
lua_pushvalue(L, -3);
lua_concat(L, 2);
if (lua_pcall(L, 1, 1, 0)) {
const char *msg = lua_tostring(L, -1);
if (msg && !strncmp(msg, "module ", 7)) {
err:
l_message(progname, notfound);
return 1;
} else {
return report(L, 1);
}
}
lua_getfield(L, -1, "start");
if (lua_isnil(L, -1)) goto err;
lua_remove(L, -2); /* Drop module table. */
return 0;
}
/* Run command with options. */
static int runcmdopt(lua_State *L, const char *opt)
{
int narg = 0;
if (opt && *opt) {
for (;;) { /* Split arguments. */
const char *p = strchr(opt, ',');
narg++;
if (!p) break;
if (p == opt)
lua_pushnil(L);
else
lua_pushlstring(L, opt, (size_t)(p - opt));
opt = p + 1;
}
if (*opt)
lua_pushstring(L, opt);
else
lua_pushnil(L);
}
return report(L, lua_pcall(L, narg, 0, 0));
}
/* JIT engine control command: try jit library first or load add-on module. */
static int dojitcmd(lua_State *L, const char *cmd)
{
const char *opt = strchr(cmd, '=');
lua_pushlstring(L, cmd, opt ? (size_t)(opt - cmd) : strlen(cmd));
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, "jit"); /* Get jit.* module table. */
lua_remove(L, -2);
lua_pushvalue(L, -2);
lua_gettable(L, -2); /* Lookup library function. */
if (!lua_isfunction(L, -1)) {
lua_pop(L, 2); /* Drop non-function and jit.* table, keep module name. */
if (loadjitmodule(L, "unknown luaJIT command"))
return 1;
} else {
lua_remove(L, -2); /* Drop jit.* table. */
}
lua_remove(L, -2); /* Drop module name. */
return runcmdopt(L, opt ? opt+1 : opt);
}
/* Optimization flags. */
static int dojitopt(lua_State *L, const char *opt)
{
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, -1, "jit.opt"); /* Get jit.opt.* module table. */
lua_remove(L, -2);
lua_getfield(L, -1, "start");
lua_remove(L, -2);
return runcmdopt(L, opt);
}
/* check that argument has no extra characters at the end */
#define notail(x) {if ((x)[2] != '\0') return -1;}
static int collectargs(char **argv, int *pi, int *pv, int *pe)
{
int i;
for (i = 1; argv[i] != NULL; i++) {
if (argv[i][0] != '-') /* not an option? */
return i;
switch (argv[i][1]) { /* option */
case '-':
notail(argv[i]);
return (argv[i+1] != NULL ? i+1 : 0);
case '\0':
return i;
case 'i':
notail(argv[i]);
*pi = 1; /* go through */
case 'v':
notail(argv[i]);
*pv = 1;
break;
case 'e':
*pe = 1; /* go through */
case 'j': /* LuaJIT extension */
case 'l':
if (argv[i][2] == '\0') {
i++;
if (argv[i] == NULL) return -1;
}
break;
case 'O': break; /* LuaJIT extension */
default: return -1; /* invalid option */
}
}
return 0;
}
static int runargs(lua_State *L, char **argv, int n)
{
int i;
for (i = 1; i < n; i++) {
if (argv[i] == NULL) continue;
lua_assert(argv[i][0] == '-');
switch (argv[i][1]) { /* option */
case 'e': {
const char *chunk = argv[i] + 2;
if (*chunk == '\0') chunk = argv[++i];
lua_assert(chunk != NULL);
if (dostring(L, chunk, "=(command line)") != 0)
return 1;
break;
}
case 'l': {
const char *filename = argv[i] + 2;
if (*filename == '\0') filename = argv[++i];
lua_assert(filename != NULL);
if (dolibrary(L, filename))
return 1; /* stop if file fails */
break;
}
case 'j': { /* LuaJIT extension */
const char *cmd = argv[i] + 2;
if (*cmd == '\0') cmd = argv[++i];
lua_assert(cmd != NULL);
if (dojitcmd(L, cmd))
return 1;
break;
}
case 'O': /* LuaJIT extension */
if (dojitopt(L, argv[i] + 2))
return 1;
break;
default: break;
}
}
return 0;
}
static int handle_luainit(lua_State *L)
{
const char *init = getenv(LUA_INIT);
if (init == NULL)
return 0; /* status OK */
else if (init[0] == '@')
return dofile(L, init+1);
else
return dostring(L, init, "=" LUA_INIT);
}
struct Smain {
int argc;
char **argv;
int status;
};
static int pmain(lua_State *L)
{
struct Smain *s = (struct Smain *)lua_touserdata(L, 1);
char **argv = s->argv;
int script;
int has_i = 0, has_v = 0, has_e = 0;
globalL = L;
if (argv[0] && argv[0][0]) progname = argv[0];
LUAJIT_VERSION_SYM(); /* linker-enforced version check */
lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */
luaL_openlibs(L); /* open libraries */
lua_gc(L, LUA_GCRESTART, 0);
s->status = handle_luainit(L);
if (s->status != 0) return 0;
script = collectargs(argv, &has_i, &has_v, &has_e);
if (script < 0) { /* invalid args? */
print_usage();
s->status = 1;
return 0;
}
if (has_v) print_version();
s->status = runargs(L, argv, (script > 0) ? script : s->argc);
if (s->status != 0) return 0;
if (script)
s->status = handle_script(L, argv, script);
if (s->status != 0) return 0;
if (has_i) {
print_jit_status(L);
dotty(L);
} else if (script == 0 && !has_e && !has_v) {
if (lua_stdin_is_tty()) {
print_version();
print_jit_status(L);
dotty(L);
} else {
dofile(L, NULL); /* executes stdin as a file */
}
}
return 0;
}
int main(int argc, char **argv)
{
int status;
struct Smain s;
lua_State *L = lua_open(); /* create state */
if (L == NULL) {
l_message(argv[0], "cannot create state: not enough memory");
return EXIT_FAILURE;
}
s.argc = argc;
s.argv = argv;
status = lua_cpcall(L, pmain, &s);
report(L, status);
lua_close(L);
return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
}

68
src/luajit.h Normal file
View File

@@ -0,0 +1,68 @@
/*
** LuaJIT -- a Just-In-Time Compiler for Lua. http://luajit.org/
**
** Copyright (C) 2005-2009 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 ]
*/
#ifndef _LUAJIT_H
#define _LUAJIT_H
#include "lua.h"
#define LUAJIT_VERSION "LuaJIT 2.0.0-beta1"
#define LUAJIT_VERSION_NUM 20000 /* Version 2.0.0 = 02.00.00. */
#define LUAJIT_VERSION_SYM luaJIT_version_2_0_0_beta1
#define LUAJIT_COPYRIGHT "Copyright (C) 2005-2009 Mike Pall"
#define LUAJIT_URL "http://luajit.org/"
/* Modes for luaJIT_setmode. */
#define LUAJIT_MODE_MASK 0x00ff
enum {
LUAJIT_MODE_ENGINE, /* Set mode for whole JIT engine. */
LUAJIT_MODE_DEBUG, /* Set debug mode (idx = level). */
LUAJIT_MODE_FUNC, /* Change mode for a function. */
LUAJIT_MODE_ALLFUNC, /* Recurse into subroutine protos. */
LUAJIT_MODE_ALLSUBFUNC, /* Change only the subroutines. */
LUAJIT_MODE_TRACE, /* Flush a compiled trace. */
LUAJIT_MODE_MAX
};
/* Flags or'ed in to the mode. */
#define LUAJIT_MODE_OFF 0x0000 /* Disable JIT compilation. */
#define LUAJIT_MODE_ON 0x0100 /* (Re-)enable JIT compilation. */
#define LUAJIT_MODE_FLUSH 0x0200 /* Flush JIT-compiled code. */
/* LuaJIT public C API. */
/* Control the JIT engine. */
LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode);
/* Enforce (dynamic) linker error for version mismatches. Call from main. */
LUA_API void LUAJIT_VERSION_SYM(void);
#endif

41
src/lualib.h Normal file
View File

@@ -0,0 +1,41 @@
/*
** Standard library header.
** Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
*/
#ifndef _LUALIB_H
#define _LUALIB_H
#include "lua.h"
#define LUA_FILEHANDLE "FILE*"
#define LUA_COLIBNAME "coroutine"
#define LUA_MATHLIBNAME "math"
#define LUA_STRLIBNAME "string"
#define LUA_TABLIBNAME "table"
#define LUA_IOLIBNAME "io"
#define LUA_OSLIBNAME "os"
#define LUA_LOADLIBNAME "package"
#define LUA_DBLIBNAME "debug"
#define LUA_BITLIBNAME "bit"
#define LUA_JITLIBNAME "jit"
LUALIB_API int luaopen_base(lua_State *L);
LUALIB_API int luaopen_math(lua_State *L);
LUALIB_API int luaopen_string(lua_State *L);
LUALIB_API int luaopen_table(lua_State *L);
LUALIB_API int luaopen_io(lua_State *L);
LUALIB_API int luaopen_os(lua_State *L);
LUALIB_API int luaopen_package(lua_State *L);
LUALIB_API int luaopen_debug(lua_State *L);
LUALIB_API int luaopen_bit(lua_State *L);
LUALIB_API int luaopen_jit(lua_State *L);
LUALIB_API void luaL_openlibs(lua_State *L);
#ifndef lua_assert
#define lua_assert(x) ((void)0)
#endif
#endif

53
src/msvcbuild.bat Normal file
View File

@@ -0,0 +1,53 @@
@rem Script to build LuaJIT with MSVC.
@rem Copyright (C) 2005-2009 Mike Pall. See Copyright Notice in luajit.h
@rem
@rem Open a "Visual Studio .NET Command Prompt", cd to this directory
@rem and run this script.
@if not defined INCLUDE goto :FAIL
@setlocal
@set LJCOMPILE=cl /nologo /c /MD /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE
@set LJLINK=link /nologo
@set LJMT=mt /nologo
@set DASMDIR=..\dynasm
@set DASM=lua %DASMDIR%\dynasm.lua
@set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c
if not exist buildvm_x86.h^
%DASM% -LN -o buildvm_x86.h buildvm_x86.dasc
%LJCOMPILE% /I "." /I %DASMDIR% buildvm*.c
%LJLINK% /out:buildvm.exe buildvm*.obj
if exist buildvm.exe.manifest^
%LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe
buildvm -m peobj -o lj_vm.obj
buildvm -m ffdef -o lj_ffdef.h %ALL_LIB%
buildvm -m libdef -o lj_libdef.h %ALL_LIB%
buildvm -m recdef -o lj_recdef.h %ALL_LIB%
buildvm -m vmdef -o ..\lib\vmdef.lua %ALL_LIB%
buildvm -m folddef -o lj_folddef.h lj_opt_fold.c
@if "%1"=="amalg" goto :AMALGDLL
%LJCOMPILE% /DLUA_BUILD_AS_DLL lj_*.c lib_*.c
%LJLINK% /DLL /out:lua51.dll lj_*.obj lib_*.obj
@goto :MTDLL
:AMALGDLL
%LJCOMPILE% /DLUA_BUILD_AS_DLL ljamalg.c
%LJLINK% /DLL /out:lua51.dll ljamalg.obj lj_vm.obj
:MTDLL
if exist lua51.dll.manifest^
%LJMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
%LJCOMPILE% luajit.c
%LJLINK% /out:luajit.exe luajit.obj lua51.lib
if exist luajit.exe.manifest^
%LJMT% -manifest luajit.exe.manifest -outputresource:luajit.exe
del *.obj *.manifest buildvm.exe
@goto :END
:FAIL
@echo You must open a "Visual Studio .NET Command Prompt" to run this script
:END