RELEASE LuaJIT-2.0.0-beta1
This commit is contained in:
8
src/.gitignore
vendored
Normal file
8
src/.gitignore
vendored
Normal 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
326
src/Makefile
Normal 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
139
src/Makefile.dep
Normal 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
438
src/buildvm.c
Normal 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
106
src/buildvm.h
Normal 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
220
src/buildvm_asm.c
Normal 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
206
src/buildvm_fold.c
Normal 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
365
src/buildvm_lib.c
Normal 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
303
src/buildvm_peobj.c
Normal 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
3592
src/buildvm_x86.dasc
Normal file
File diff suppressed because it is too large
Load Diff
159
src/lauxlib.h
Normal file
159
src/lauxlib.h
Normal 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
438
src/lib_aux.c
Normal 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
560
src/lib_base.c
Normal 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
74
src/lib_bit.c
Normal 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
366
src/lib_debug.c
Normal 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
37
src/lib_init.c
Normal 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
538
src/lib_io.c
Normal 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
589
src/lib_jit.c
Normal 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
188
src/lib_math.c
Normal 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
249
src/lib_os.c
Normal 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
508
src/lib_package.c
Normal 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
790
src/lib_string.c
Normal 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
276
src/lib_table.c
Normal 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
6
src/lj.supp
Normal 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
1232
src/lj_alloc.c
Normal file
File diff suppressed because it is too large
Load Diff
17
src/lj_alloc.h
Normal file
17
src/lj_alloc.h
Normal 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
1046
src/lj_api.c
Normal file
File diff suppressed because it is too large
Load Diff
88
src/lj_arch.h
Normal file
88
src/lj_arch.h
Normal 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
3324
src/lj_asm.c
Normal file
File diff suppressed because it is too large
Load Diff
17
src/lj_asm.h
Normal file
17
src/lj_asm.h
Normal 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
17
src/lj_bc.c
Normal 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
235
src/lj_bc.h
Normal 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
44
src/lj_ctype.c
Normal 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
40
src/lj_ctype.h
Normal 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
226
src/lj_def.h
Normal 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
284
src/lj_dispatch.c
Normal 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
64
src/lj_dispatch.h
Normal 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
763
src/lj_err.c
Normal 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
40
src/lj_err.h
Normal 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
134
src/lj_errmsg.h
Normal 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
18
src/lj_ff.h
Normal 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
84
src/lj_frame.h
Normal 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
185
src/lj_func.c
Normal 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
25
src/lj_func.h
Normal 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
800
src/lj_gc.c
Normal 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
102
src/lj_gc.h
Normal 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
739
src/lj_gdbjit.c
Normal 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
22
src/lj_gdbjit.h
Normal 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
461
src/lj_ir.c
Normal 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
429
src/lj_ir.h
Normal 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
128
src/lj_iropt.h
Normal 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
279
src/lj_jit.h
Normal 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
393
src/lj_lex.c
Normal 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
63
src/lj_lex.h
Normal 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
216
src/lj_lib.c
Normal 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
84
src/lj_lib.h
Normal 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
260
src/lj_mcode.c
Normal 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
23
src/lj_mcode.h
Normal 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
358
src/lj_meta.c
Normal 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
33
src/lj_meta.h
Normal 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
41
src/lj_obj.c
Normal 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
676
src/lj_obj.h
Normal 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
79
src/lj_opt_dce.c
Normal 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
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
358
src/lj_opt_loop.c
Normal 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
550
src/lj_opt_mem.c
Normal 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
430
src/lj_opt_narrow.c
Normal 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
2198
src/lj_parse.c
Normal file
File diff suppressed because it is too large
Load Diff
15
src/lj_parse.h
Normal file
15
src/lj_parse.h
Normal 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
2136
src/lj_record.c
Normal file
File diff suppressed because it is too large
Load Diff
17
src/lj_record.h
Normal file
17
src/lj_record.h
Normal 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
286
src/lj_snap.c
Normal 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
19
src/lj_snap.h
Normal 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
255
src/lj_state.c
Normal 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
31
src/lj_state.h
Normal 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
301
src/lj_str.c
Normal 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
45
src/lj_str.h
Normal 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
618
src/lj_tab.c
Normal 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
41
src/lj_tab.h
Normal 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
132
src/lj_target.h
Normal 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
257
src/lj_target_x86.h
Normal 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
591
src/lj_trace.c
Normal 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
52
src/lj_trace.h
Normal 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
59
src/lj_traceerr.h
Normal 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
33
src/lj_udata.c
Normal 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
14
src/lj_udata.h
Normal 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
66
src/lj_vm.h
Normal 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
56
src/lj_vmevent.c
Normal 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
49
src/lj_vmevent.h
Normal 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
70
src/ljamalg.c
Normal 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
388
src/lua.h
Normal 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
9
src/lua.hpp
Normal 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
133
src/luaconf.h
Normal 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
519
src/luajit.c
Normal 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
68
src/luajit.h
Normal 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
41
src/lualib.h
Normal 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
53
src/msvcbuild.bat
Normal 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
|
||||
Reference in New Issue
Block a user