aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgus knight <waddlesplash@gmail.com>2015-07-27 16:03:25 -0400
committerAugustin Cavalier <waddlesplash@gmail.com>2015-07-27 16:03:25 -0400
commit47e06c6d4e542e47fcbad69a78c2436a854a0779 (patch)
treed979fb8f3372966c0ef3031c4edeaa8f017241d9 /src
parent694d0fdade8bff3bc03466675350f596b2f4f8ed (diff)
downloadtinycc-47e06c6d4e542e47fcbad69a78c2436a854a0779.tar.gz
tinycc-47e06c6d4e542e47fcbad69a78c2436a854a0779.tar.bz2
Reorganize the source tree.
* Documentation is now in "docs". * Source code is now in "src". * Misc. fixes here and there so that everything still works. I think I got everything in this commit, but I only tested this on Linux (Make) and Windows (CMake), so I might've messed something up on other platforms...
Diffstat (limited to 'src')
-rw-r--r--src/Makefile421
-rw-r--r--src/arm/arm-gen.c2162
-rw-r--r--src/arm/arm64-gen.c1846
-rw-r--r--src/cil/il-gen.c655
-rw-r--r--src/cil/il-opcodes.h251
-rw-r--r--src/coff.h446
-rw-r--r--src/config.h.in7
-rw-r--r--src/conftest.c77
-rw-r--r--src/elf.h3228
-rw-r--r--src/libtcc.c2322
-rw-r--r--src/libtcc.h104
-rw-r--r--src/stab.def234
-rw-r--r--src/stab.h17
-rw-r--r--src/tcc.c365
-rw-r--r--src/tcc.h1498
-rw-r--r--src/tccasm.c1130
-rw-r--r--src/tcccoff.c924
-rw-r--r--src/tccelf.c3504
-rw-r--r--src/tccgen.c6463
-rw-r--r--src/tcclib.h80
-rw-r--r--src/tccpe.c1890
-rw-r--r--src/tccpp.c3430
-rw-r--r--src/tccrun.c772
-rw-r--r--src/tcctok.h335
-rw-r--r--src/tms320c67/c67-gen.c2559
-rw-r--r--src/x86/i386-asm.c1502
-rw-r--r--src/x86/i386-asm.h474
-rw-r--r--src/x86/i386-gen.c1144
-rw-r--r--src/x86/i386-tok.h244
-rw-r--r--src/x86/x86_64-asm.h449
-rw-r--r--src/x86/x86_64-gen.c2360
31 files changed, 40893 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..a098a76
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,421 @@
+#
+# Tiny C Compiler Makefile
+#
+
+TOP ?= .
+include $(TOP)/../config.mak
+VPATH = $(top_srcdir)
+
+CPPFLAGS += -I$(TOP) # for config.h
+
+ifneq (-$(findstring gcc,$(CC))-,-gcc-)
+ifeq (-$(findstring clang,$(CC))-,-clang-)
+# make clang accept gnuisms in libtcc1.c
+CFLAGS+=-fheinous-gnu-extensions
+endif
+endif
+
+CPPFLAGS_P=$(CPPFLAGS) -DCONFIG_TCC_STATIC
+CFLAGS_P=$(CFLAGS) -pg -static
+LIBS_P=
+LDFLAGS_P=$(LDFLAGS)
+
+ifdef CONFIG_WIN64
+CONFIG_WIN32=yes
+endif
+
+ifndef CONFIG_WIN32
+LIBS=-lm
+ifndef CONFIG_NOLDL
+LIBS+=-ldl
+endif
+endif
+
+# make libtcc as static or dynamic library?
+ifdef DISABLE_STATIC
+ifndef CONFIG_WIN32
+LIBTCC=libtcc.so.1.0
+else
+LIBTCC=libtcc.dll
+LIBTCC_DLL=yes
+LIBTCC_EXTRA=libtcc.def libtcc.a
+endif
+LINK_LIBTCC=-Wl,-rpath,"$(libdir)"
+ifdef DISABLE_RPATH
+LINK_LIBTCC=
+endif
+else
+LIBTCC=libtcc.a
+LINK_LIBTCC=
+endif
+
+CONFIG_$(ARCH) = yes
+NATIVE_DEFINES_$(CONFIG_i386) += -DTCC_TARGET_I386
+NATIVE_DEFINES_$(CONFIG_x86-64) += -DTCC_TARGET_X86_64
+NATIVE_DEFINES_$(CONFIG_WIN32) += -DTCC_TARGET_PE
+NATIVE_DEFINES_$(CONFIG_uClibc) += -DTCC_UCLIBC
+NATIVE_DEFINES_$(CONFIG_arm) += -DTCC_TARGET_ARM
+NATIVE_DEFINES_$(CONFIG_arm_eabihf) += -DTCC_ARM_EABI -DTCC_ARM_HARDFLOAT
+NATIVE_DEFINES_$(CONFIG_arm_eabi) += -DTCC_ARM_EABI
+NATIVE_DEFINES_$(CONFIG_arm_vfp) += -DTCC_ARM_VFP
+NATIVE_DEFINES_$(CONFIG_arm64) += -DTCC_TARGET_ARM64
+NATIVE_DEFINES += $(NATIVE_DEFINES_yes)
+
+ifeq ($(TOP),.)
+
+PROGS=tcc$(EXESUF)
+I386_CROSS = i386-linux-gnu-tcc$(EXESUF)
+WIN32_CROSS = i386-win-mingw32-tcc$(EXESUF)
+WIN64_CROSS = x86_64-win-mingw32-tcc$(EXESUF)
+WINCE_CROSS = arm-win-mingw32ce-tcc$(EXESUF)
+X64_CROSS = x86_64-linux-gnu-tcc$(EXESUF)
+ARM_FPA_CROSS = arm-linux-fpa-tcc$(EXESUF)
+ARM_FPA_LD_CROSS = arm-linux-fpa-ld-tcc$(EXESUF)
+ARM_VFP_CROSS = arm-linux-gnu-tcc$(EXESUF)
+ARM_EABI_CROSS = arm-linux-gnueabi-tcc$(EXESUF)
+ARM_EABIHF_CROSS = arm-linux-gnueabihf-tcc$(EXESUF)
+ARM_CROSS = $(ARM_FPA_CROSS) $(ARM_FPA_LD_CROSS) $(ARM_VFP_CROSS) $(ARM_EABI_CROSS)
+ARM64_CROSS = arm64-tcc$(EXESUF)
+C67_CROSS = c67-tcc$(EXESUF)
+
+# Legacy symlinks for cross compilers
+$(I386_CROSS)_LINK = i386-tcc$(EXESUF)
+$(WIN32_CROSS)_LINK = i386-win-tcc$(EXESUF)
+$(WIN64_CROSS)_LINK = x86_64-win-tcc$(EXESUF)
+$(WINCE_CROSS)_LINK = arm-win-tcc$(EXESUF)
+$(X64_CROSS)_LINK = x86_64-tcc$(EXESUF)
+$(ARM_FPA_CROSS)_LINK = arm-fpa-tcc$(EXESUF)
+$(ARM_FPA_LD_CROSS)_LINK = arm-fpa-ld-tcc$(EXESUF)
+$(ARM_VFP_CROSS)_LINK = arm-vfp-tcc$(EXESUF)
+$(ARM_EABI_CROSS)_LINK = arm-eabi-tcc$(EXESUF)
+
+ifeq ($(TARGETOS),Windows)
+ifeq ($(ARCH),i386)
+PROGS:=$($(WIN32_CROSS)_LINK)
+$($(WIN32_CROSS)_LINK)_TCC = yes
+endif
+ifeq ($(ARCH),x86-64)
+PROGS:=$($(WIN64_CROSS)_LINK)
+$($(WIN64_CROSS)_LINK)_TCC = yes
+endif
+endif
+
+ifeq ($(TARGETOS),Linux)
+ifeq ($(ARCH),i386)
+PROGS:=$($(I386_CROSS)_LINK)
+$($(I386_CROSS)_LINK)_TCC = yes
+endif
+ifeq ($(ARCH),x86-64)
+PROGS:=$($(X64_CROSS)_LINK)
+$($(X64_CROSS)_LINK)_TCC = yes
+endif
+endif
+
+CORE_FILES = tcc.c libtcc.c tccpp.c tccgen.c tccelf.c tccasm.c tccrun.c
+CORE_FILES += tcc.h config.h libtcc.h tcctok.h
+I386_FILES = $(CORE_FILES) x86/i386-gen.c x86/i386-asm.c x86/i386-asm.h x86/i386-tok.h
+WIN32_FILES = $(CORE_FILES) x86/i386-gen.c x86/i386-asm.c x86/i386-asm.h x86/i386-tok.h tccpe.c
+WIN64_FILES = $(CORE_FILES) x86/x86_64-gen.c x86/i386-asm.c x86/x86_64-asm.h tccpe.c
+WINCE_FILES = $(CORE_FILES) arm/arm-gen.c tccpe.c
+X86_64_FILES = $(CORE_FILES) x86/x86_64-gen.c x86/i386-asm.c x86/x86_64-asm.h
+ARM_FILES = $(CORE_FILES) arm/arm-gen.c
+ARM64_FILES = $(CORE_FILES) arm/arm64-gen.c
+C67_FILES = $(CORE_FILES) tms320c67/c67-gen.c tcccoff.c
+
+ifdef CONFIG_WIN64
+PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF)
+NATIVE_FILES=$(WIN64_FILES)
+PROGS_CROSS=$(WIN32_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1_CROSS=lib/i386-win/libtcc1.a
+LIBTCC1=libtcc1.a
+else ifdef CONFIG_WIN32
+PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF)
+NATIVE_FILES=$(WIN32_FILES)
+PROGS_CROSS=$(WIN64_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1_CROSS=lib/x86_64-win/libtcc1.a
+LIBTCC1=libtcc1.a
+else ifeq ($(ARCH),i386)
+NATIVE_FILES=$(I386_FILES)
+PROGS_CROSS=$($(X64_CROSS)_LINK) $($(WIN32_CROSS)_LINK) $($(WIN64_CROSS)_LINK) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a lib/x86_64/libtcc1.a \
+ lib/arm64/libtcc1.a
+LIBTCC1=libtcc1.a
+else ifeq ($(ARCH),x86-64)
+ifeq ($(TARGETOS),Darwin)
+NATIVE_FILES=$(X86_64_FILES)
+PROGS_CROSS=$($(I386_CROSS)_LINK) $($(WIN32_CROSS)_LINK) $($(WIN64_CROSS)_LINK) $(ARM_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a lib/x86_64/libtcc1.a
+LIBTCC1=libtcc1.a
+else
+NATIVE_FILES=$(X86_64_FILES)
+PROGS_CROSS=$($(I386_CROSS)_LINK) $($(WIN32_CROSS)_LINK) $($(WIN64_CROSS)_LINK) $(ARM_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a lib/x86_64/libtcc1.a \
+ lib/arm64/libtcc1.a
+LIBTCC1=libtcc1.a
+endif
+else ifeq ($(ARCH),arm)
+NATIVE_FILES=$(ARM_FILES)
+PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(ARM64_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1=libtcc1.a
+LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a
+else ifeq ($(ARCH),arm64)
+NATIVE_FILES=$(ARM64_FILES)
+PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(ARM_CROSS) $(C67_CROSS) $(WINCE_CROSS)
+LIBTCC1=libtcc1.a
+LIBTCC1_CROSS=lib/i386-win/libtcc1.a lib/x86_64-win/libtcc1.a lib/i386/libtcc1.a
+endif
+PROGS_CROSS_LINK=$(foreach PROG_CROSS,$(PROGS_CROSS),$($(PROG_CROSS)_LINK))
+
+ifeq ($(TARGETOS),Darwin)
+PROGS+=tiny_libmaker$(EXESUF)
+endif
+
+TCCLIBS = $(LIBTCC1) $(LIBTCC) $(LIBTCC_EXTRA)
+TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info
+
+ifdef CONFIG_CROSS
+PROGS+=$(PROGS_CROSS)
+TCCLIBS+=$(LIBTCC1_CROSS)
+endif
+
+all: $(PROGS) $(TCCLIBS) $(TCCDOCS)
+
+# Host Tiny C Compiler
+tcc$(EXESUF): tcc.o $(LIBTCC)
+ $(CC) -o $@ $^ $(LIBS) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LINK_LIBTCC)
+
+# Cross Tiny C Compilers
+%-tcc$(EXESUF): tcc.c
+ $(CC) -o $@ $< -DONE_SOURCE $(if $($@_TCC),$(NATIVE_DEFINES),$(DEFINES)) $(CPPFLAGS) $(CFLAGS) $(LIBS) $(LDFLAGS)
+ $(if $($@_LINK),ln -sf $@ $($@_LINK))
+ $(if $($@_TCC),ln -sf $@ tcc$(EXESUF))
+
+# profiling version
+tcc_p$(EXESUF): $(NATIVE_FILES)
+ $(CC) -o $@ $< -DONE_SOURCE $(NATIVE_DEFINES) $(CPPFLAGS_P) $(CFLAGS_P) $(LIBS_P) $(LDFLAGS_P)
+
+$(I386_CROSS) $($(I386_CROSS)_LINK): DEFINES = -DTCC_TARGET_I386
+$(X64_CROSS) $($(X64_CROSS)_LINK): DEFINES = -DTCC_TARGET_X86_64
+$(WIN32_CROSS) $($(WIN32_CROSS)_LINK): DEFINES = -DTCC_TARGET_I386 -DTCC_TARGET_PE \
+ -DCONFIG_TCCDIR="\"$(tccdir)/win32\"" \
+ -DCONFIG_TCC_LIBPATHS="\"{B}/lib/32;{B}/lib\""
+$(WIN64_CROSS) $($(WIN64_CROSS)_LINK): DEFINES = -DTCC_TARGET_X86_64 -DTCC_TARGET_PE \
+ -DCONFIG_TCCDIR="\"$(tccdir)/win32\"" \
+ -DCONFIG_TCC_LIBPATHS="\"{B}/lib/64;{B}/lib\""
+$(WINCE_CROSS): DEFINES = -DTCC_TARGET_PE
+$(C67_CROSS): DEFINES = -DTCC_TARGET_C67
+$(ARM_FPA_CROSS): DEFINES = -DTCC_TARGET_ARM
+$(ARM_FPA_LD_CROSS)$(EXESUF): DEFINES = -DTCC_TARGET_ARM -DLDOUBLE_SIZE=12
+$(ARM_VFP_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_VFP
+$(ARM_EABI_CROSS): DEFINES = -DTCC_TARGET_ARM -DTCC_ARM_EABI -DTCC_ARM_VFP
+$(ARM64_CROSS): DEFINES = -DTCC_TARGET_ARM64
+
+$(I386_CROSS) $($(I386_CROSS)_LINK): $(I386_FILES)
+$(X64_CROSS) $($(X64_CROSS)_LINK): $(X86_64_FILES)
+$(WIN32_CROSS) $($(WIN32_CROSS)_LINK): $(WIN32_FILES)
+$(WIN64_CROSS) $($(WIN64_CROSS)_LINK): $(WIN64_FILES)
+$(WINCE_CROSS) $($(WINCE_CROSS)_LINK): $(WINCE_FILES)
+$(C67_CROSS) $($(C67_CROSS)_LINK): $(C67_FILES)
+$(ARM_FPA_CROSS) $(ARM_FPA_LD_CROSS) $(ARM_VFP_CROSS) $(ARM_EABI_CROSS): $(ARM_FILES)
+$($(ARM_FPA_CROSS)_LINK) $($(ARM_FPA_LD_CROSS)_LINK) $($(ARM_VFP_CROSS)_LINK) $($(ARM_EABI_CROSS)_LINK): $(ARM_FILES)
+$(ARM64_CROSS): $(ARM64_FILES)
+
+# libtcc generation and test
+ifndef ONE_SOURCE
+LIBTCC_OBJ = $(filter-out tcc.o,$(patsubst %.c,%.o,$(filter %.c,$(NATIVE_FILES))))
+LIBTCC_INC = $(filter %.h,$(CORE_FILES)) $(filter-out $(CORE_FILES),$(NATIVE_FILES))
+else
+LIBTCC_OBJ = libtcc.o
+LIBTCC_INC = $(NATIVE_FILES)
+libtcc.o : NATIVE_DEFINES += -DONE_SOURCE
+endif
+
+$(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC)
+ $(CC) -o $@ -c $< $(NATIVE_DEFINES) $(CPPFLAGS) $(CFLAGS)
+
+ifndef LIBTCC_DLL
+libtcc.a: $(LIBTCC_OBJ)
+ $(AR) rcs $@ $^
+endif
+
+libtcc.so.1.0: $(LIBTCC_OBJ)
+ $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS)
+
+libtcc.so.1.0: CFLAGS+=-fPIC
+
+ifdef LIBTCC_DLL
+libtcc.dll libtcc.def libtcc.a: $(LIBTCC_OBJ)
+ $(CC) -shared $^ -o $@ $(LDFLAGS) -Wl,--output-def,libtcc.def,--out-implib,libtcc.a
+endif
+
+# windows utilities
+tiny_impdef$(EXESUF): ../win32/tools/tiny_impdef.c
+ $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
+tiny_libmaker$(EXESUF): ../win32/tools/tiny_libmaker.c
+ $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS)
+
+# TinyCC runtime libraries
+libtcc1.a : FORCE
+ $(MAKE) -C ../lib native
+ if test ! -d $(ARCH); then mkdir $(ARCH); fi
+ if test ! -L $(ARCH)/$@; then ln -sf ../$@ $(ARCH)/$@; fi
+lib/%/libtcc1.a : FORCE $(PROGS_CROSS)
+ $(MAKE) -C ../lib cross TARGET=$*
+
+FORCE:
+
+# install
+TCC_INCLUDES = stdarg.h stddef.h stdbool.h float.h varargs.h
+INSTALL=install
+ifdef STRIP_BINARIES
+INSTALLBIN=$(INSTALL) -s
+else
+INSTALLBIN=$(INSTALL)
+endif
+
+install-strip: install
+ strip $(foreach PROG,$(PROGS),"$(bindir)"/$(PROG))
+
+ifndef CONFIG_WIN32
+install: $(PROGS) $(TCCLIBS) $(TCCDOCS)
+ mkdir -p "$(bindir)"
+ $(INSTALLBIN) -m755 $(PROGS) "$(bindir)"
+ cp -P tcc$(EXESUF) "$(bindir)"
+ mkdir -p "$(mandir)/man1"
+ -$(INSTALL) -m644 tcc.1 "$(mandir)/man1"
+ mkdir -p "$(infodir)"
+ -$(INSTALL) -m644 tcc-doc.info "$(infodir)"
+ mkdir -p "$(tccdir)"
+ mkdir -p "$(tccdir)/include"
+ifneq ($(LIBTCC1),)
+ mkdir -p "$(tccdir)/$(ARCH)"
+ $(INSTALL) -m644 $(LIBTCC1) "$(tccdir)/$(ARCH)"
+endif
+ $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) $(top_srcdir)/tcclib.h "$(tccdir)/include"
+ mkdir -p "$(libdir)"
+ $(INSTALL) -m644 $(LIBTCC) "$(libdir)"
+ifdef DISABLE_STATIC
+ ln -sf "$(ln_libdir)/libtcc.so.1.0" "$(libdir)/libtcc.so.1"
+ ln -sf "$(ln_libdir)/libtcc.so.1.0" "$(libdir)/libtcc.so"
+endif
+ mkdir -p "$(includedir)"
+ $(INSTALL) -m644 $(top_srcdir)/libtcc.h "$(includedir)"
+ mkdir -p "$(docdir)"
+ -$(INSTALL) -m644 tcc-doc.html "$(docdir)"
+ifdef CONFIG_CROSS
+ mkdir -p "$(tccdir)/win32/lib/32"
+ mkdir -p "$(tccdir)/win32/lib/64"
+ mkdir -p "$(tccdir)/i386"
+ mkdir -p "$(tccdir)/x86-64"
+ifneq ($(HOST_OS),Darwin)
+ mkdir -p "$(tccdir)/arm64"
+ $(INSTALL) -m644 lib/arm64/libtcc1.a "$(tccdir)/arm64"
+endif
+ $(INSTALL) -m644 lib/i386/libtcc1.a "$(tccdir)/i386"
+ $(INSTALL) -m644 lib/x86_64/libtcc1.a "$(tccdir)/x86-64"
+ $(INSTALL) -m644 $(top_srcdir)/win32/lib/*.def "$(tccdir)/win32/lib"
+ $(INSTALL) -m644 lib/i386-win/libtcc1.a "$(tccdir)/win32/lib/32"
+ $(INSTALL) -m644 lib/x86_64-win/libtcc1.a "$(tccdir)/win32/lib/64"
+ cp -r $(top_srcdir)/win32/include/. "$(tccdir)/win32/include"
+ cp -r "$(tccdir)/include" "$(tccdir)/win32"
+endif
+
+uninstall:
+ rm -fv $(foreach P,$(PROGS),"$(bindir)/$P")
+ rm -fv "$(bindir)/tcc$(EXESUF)"
+ rm -fv $(foreach P,$(LIBTCC1),"$(tccdir)/$P")
+ rm -fv $(foreach P,$(TCC_INCLUDES),"$(tccdir)/include/$P")
+ rm -fv "$(mandir)/man1/tcc.1" "$(infodir)/tcc-doc.info"
+ rm -fv "$(libdir)/$(LIBTCC)" "$(includedir)/libtcc.h"
+ rm -fv "$(libdir)/libtcc.so*"
+ rm -rv "$(tccdir)"
+ rm -rv "$(docdir)"
+else
+# on windows
+install: $(PROGS) $(TCCLIBS) $(TCCDOCS)
+ mkdir -p "$(tccdir)"
+ mkdir -p "$(tccdir)/lib"
+ mkdir -p "$(tccdir)/include"
+ mkdir -p "$(tccdir)/examples"
+ mkdir -p "$(tccdir)/doc"
+ mkdir -p "$(tccdir)/libtcc"
+ $(INSTALLBIN) -m755 $(PROGS) "$(tccdir)"
+ $(INSTALLBIN) -m755 tcc.exe "$(tccdir)"
+ $(INSTALL) -m644 $(LIBTCC1) $(top_srcdir)/win32/lib/*.def "$(tccdir)/lib"
+ cp -r $(top_srcdir)/win32/include/. "$(tccdir)/include"
+ cp -r $(top_srcdir)/win32/examples/. "$(tccdir)/examples"
+ $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) $(top_srcdir)/tcclib.h "$(tccdir)/include"
+ $(INSTALL) -m644 tcc-doc.html $(top_srcdir)/win32/tcc-win32.txt "$(tccdir)/doc"
+ $(INSTALL) -m644 $(top_srcdir)/libtcc.h $(LIBTCC_EXTRA) "$(tccdir)/libtcc"
+ $(INSTALL) -m644 $(LIBTCC) "$(tccdir)"
+ifdef CONFIG_CROSS
+ mkdir -p "$(tccdir)/lib/32"
+ mkdir -p "$(tccdir)/lib/64"
+ -$(INSTALL) -m644 lib/i386-win/libtcc1.a "$(tccdir)/lib/32"
+ -$(INSTALL) -m644 lib/x86_64-win/libtcc1.a "$(tccdir)/lib/64"
+endif
+
+uninstall:
+ rm -rfv "$(tccdir)/*"
+endif
+
+# documentation and man page
+tcc-doc.html: ../docs/tcc-doc.texi
+ -makeinfo --no-split --html --number-sections -o $@ $<
+
+tcc.1: ../docs/tcc-doc.texi
+ -$(top_srcdir)/texi2pod.pl $< tcc.pod
+ -pod2man --section=1 --center="Tiny C Compiler" --release=`cat $(top_srcdir)/../VERSION` tcc.pod > $@
+
+tcc-doc.info: ../docs/tcc-doc.texi
+ -makeinfo $<
+
+# in tests subdir
+export LIBTCC1
+
+%est:
+ $(MAKE) -C ../tests $@ 'PROGS_CROSS=$(PROGS_CROSS)'
+
+clean:
+ rm -vf $(PROGS) tcc_p$(EXESUF) tcc.pod *~ *.o *.a *.so* *.out *.log \
+ *.exe a.out tags TAGS libtcc_test$(EXESUF) tcc$(EXESUF)
+ -rm -r $(ARCH) arm64
+ifeq ($(HOST_OS),Linux)
+ -rm -r ./C:
+endif
+ -rm *-tcc$(EXESUF)
+ $(MAKE) -C ../tests $@
+ifneq ($(LIBTCC1),)
+ $(MAKE) -C ../lib $@
+endif
+
+distclean: clean
+ rm -vf config.h config.mak config.texi tcc.1 tcc-doc.info tcc-doc.html
+
+config.mak:
+ @echo "Please run ./configure."
+ @exit 1
+
+tags:
+ ctags $(top_srcdir)/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[chS]
+
+TAGS:
+ ctags -e $(top_srcdir)/*.[ch] $(top_srcdir)/include/*.h $(top_srcdir)/lib/*.[chS]
+
+# create release tarball from *current* git branch (including tcc-doc.html
+# and converting two files to CRLF)
+TCC-VERSION := tcc-$(shell cat $(top_srcdir)/../VERSION)
+tar: tcc-doc.html
+ mkdir $(TCC-VERSION)
+ ( cd $(TCC-VERSION) && git --git-dir ../.git checkout -f )
+ cp tcc-doc.html $(TCC-VERSION)
+ for f in tcc-win32.txt build-tcc.bat ; do \
+ cat win32/$$f | sed 's,\(.*\),\1\r,g' > $(TCC-VERSION)/win32/$$f ; \
+ done
+ tar cjf $(TCC-VERSION).tar.bz2 $(TCC-VERSION)
+ rm -rf $(TCC-VERSION)
+ git reset
+
+.PHONY: all clean tar tags TAGS distclean install uninstall FORCE
+
+endif # ifeq ($(TOP),.)
diff --git a/src/arm/arm-gen.c b/src/arm/arm-gen.c
new file mode 100644
index 0000000..f31fb88
--- /dev/null
+++ b/src/arm/arm-gen.c
@@ -0,0 +1,2162 @@
+/*
+ * ARMv4 code generator for TCC
+ *
+ * Copyright (c) 2003 Daniel Glöckner
+ * Copyright (c) 2012 Thomas Preud'homme
+ *
+ * Based on i386-gen.c by Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef TARGET_DEFS_ONLY
+
+#if defined(TCC_ARM_EABI) && !defined(TCC_ARM_VFP)
+#error "Currently TinyCC only supports float computation with VFP instructions"
+#endif
+
+/* number of available registers */
+#ifdef TCC_ARM_VFP
+#define NB_REGS 13
+#else
+#define NB_REGS 9
+#endif
+
+typedef int RegArgs;
+
+#ifndef TCC_ARM_VERSION
+#define TCC_ARM_VERSION 5
+#endif
+
+/* a register can belong to several classes. The classes must be
+ sorted from more general to more precise (see gv2() code which does
+ assumptions on it). */
+#define RC_INT 0x0001 /* generic integer register */
+#define RC_FLOAT 0x0002 /* generic float register */
+#define RC_R0 0x0004
+#define RC_R1 0x0008
+#define RC_R2 0x0010
+#define RC_R3 0x0020
+#define RC_R12 0x0040
+#define RC_F0 0x0080
+#define RC_F1 0x0100
+#define RC_F2 0x0200
+#define RC_F3 0x0400
+#ifdef TCC_ARM_VFP
+#define RC_F4 0x0800
+#define RC_F5 0x1000
+#define RC_F6 0x2000
+#define RC_F7 0x4000
+#endif
+#define RC_IRET RC_R0 /* function return: integer register */
+#define RC_LRET RC_R1 /* function return: second integer register */
+#define RC_FRET RC_F0 /* function return: float register */
+
+/* pretty names for the registers */
+enum {
+ TREG_R0 = 0,
+ TREG_R1,
+ TREG_R2,
+ TREG_R3,
+ TREG_R12,
+ TREG_F0,
+ TREG_F1,
+ TREG_F2,
+ TREG_F3,
+#ifdef TCC_ARM_VFP
+ TREG_F4,
+ TREG_F5,
+ TREG_F6,
+ TREG_F7,
+#endif
+};
+
+#ifdef TCC_ARM_VFP
+#define T2CPR(t) (((t)&VT_BTYPE) != VT_FLOAT ? 0x100 : 0)
+#endif
+
+/* return registers for function */
+#define REG_IRET TREG_R0 /* single word int return register */
+#define REG_LRET TREG_R1 /* second word return register (for long long) */
+#define REG_FRET TREG_F0 /* float return register */
+
+#ifdef TCC_ARM_EABI
+#define TOK___divdi3 TOK___aeabi_ldivmod
+#define TOK___moddi3 TOK___aeabi_ldivmod
+#define TOK___udivdi3 TOK___aeabi_uldivmod
+#define TOK___umoddi3 TOK___aeabi_uldivmod
+#endif
+
+/* defined if function parameters must be evaluated in reverse order */
+#define INVERT_FUNC_PARAMS
+
+/* defined if structures are passed as pointers. Otherwise structures
+ are directly pushed on stack. */
+/* #define FUNC_STRUCT_PARAM_AS_PTR */
+
+/* pointer size, in bytes */
+#define PTR_SIZE 4
+
+/* long double size and alignment, in bytes */
+#ifdef TCC_ARM_VFP
+#define LDOUBLE_SIZE 8
+#endif
+
+#ifndef LDOUBLE_SIZE
+#define LDOUBLE_SIZE 8
+#endif
+
+#ifdef TCC_ARM_EABI
+#define LDOUBLE_ALIGN 8
+#else
+#define LDOUBLE_ALIGN 4
+#endif
+
+/* maximum alignment (for aligned attribute support) */
+#define MAX_ALIGN 8
+
+#define CHAR_IS_UNSIGNED
+
+/******************************************************/
+/* ELF defines */
+
+#define EM_TCC_TARGET EM_ARM
+
+/* relocation type for 32 bit data relocation */
+#define R_DATA_32 R_ARM_ABS32
+#define R_DATA_PTR R_ARM_ABS32
+#define R_JMP_SLOT R_ARM_JUMP_SLOT
+#define R_COPY R_ARM_COPY
+
+#define ELF_START_ADDR 0x00008000
+#define ELF_PAGE_SIZE 0x1000
+
+enum float_abi {
+ ARM_SOFTFP_FLOAT,
+ ARM_HARD_FLOAT,
+};
+
+/******************************************************/
+#else /* ! TARGET_DEFS_ONLY */
+/******************************************************/
+#include "../tcc.h"
+
+enum float_abi float_abi;
+
+ST_DATA const int reg_classes[NB_REGS] = {
+ /* r0 */ RC_INT | RC_R0,
+ /* r1 */ RC_INT | RC_R1,
+ /* r2 */ RC_INT | RC_R2,
+ /* r3 */ RC_INT | RC_R3,
+ /* r12 */ RC_INT | RC_R12,
+ /* f0 */ RC_FLOAT | RC_F0,
+ /* f1 */ RC_FLOAT | RC_F1,
+ /* f2 */ RC_FLOAT | RC_F2,
+ /* f3 */ RC_FLOAT | RC_F3,
+#ifdef TCC_ARM_VFP
+ /* d4/s8 */ RC_FLOAT | RC_F4,
+ /* d5/s10 */ RC_FLOAT | RC_F5,
+ /* d6/s12 */ RC_FLOAT | RC_F6,
+ /* d7/s14 */ RC_FLOAT | RC_F7,
+#endif
+};
+
+static int func_sub_sp_offset, last_itod_magic;
+static int leaffunc;
+
+#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
+static CType float_type, double_type, func_float_type, func_double_type;
+ST_FUNC void arm_init(struct TCCState* s)
+{
+ float_type.t = VT_FLOAT;
+ double_type.t = VT_DOUBLE;
+ func_float_type.t = VT_FUNC;
+ func_float_type.ref =
+ sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD);
+ func_double_type.t = VT_FUNC;
+ func_double_type.ref =
+ sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD);
+
+ float_abi = s->float_abi;
+#ifndef TCC_ARM_HARDFLOAT
+ tcc_warning("soft float ABI currently not supported: default to softfp");
+#endif
+}
+#else
+#define func_float_type func_old_type
+#define func_double_type func_old_type
+#define func_ldouble_type func_old_type
+ST_FUNC void arm_init(struct TCCState* s)
+{
+#if !defined(TCC_ARM_VFP)
+ tcc_warning("Support for FPA is deprecated and will be removed in next"
+ " release");
+#endif
+#if !defined(TCC_ARM_EABI)
+ tcc_warning("Support for OABI is deprecated and will be removed in next"
+ " release");
+#endif
+}
+#endif
+
+static int two2mask(int a, int b)
+{
+ return (reg_classes[a] | reg_classes[b]) & ~(RC_INT | RC_FLOAT);
+}
+
+static int regmask(int r)
+{
+ return reg_classes[r] & ~(RC_INT | RC_FLOAT);
+}
+
+/******************************************************/
+
+#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
+char* default_elfinterp(struct TCCState* s)
+{
+ if (s->float_abi == ARM_HARD_FLOAT)
+ return "/lib/ld-linux-armhf.so.3";
+ else
+ return "/lib/ld-linux.so.3";
+}
+#endif
+
+void o(uint32_t i)
+{
+ /* this is a good place to start adding big-endian support*/
+ int ind1;
+
+ ind1 = ind + 4;
+ if (!cur_text_section)
+ tcc_error("compiler error! This happens f.ex. if the compiler\n"
+ "can't evaluate constant expressions outside of a function.");
+ if (ind1 > cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ cur_text_section->data[ind++] = i & 255;
+ i >>= 8;
+ cur_text_section->data[ind++] = i & 255;
+ i >>= 8;
+ cur_text_section->data[ind++] = i & 255;
+ i >>= 8;
+ cur_text_section->data[ind++] = i;
+}
+
+static uint32_t stuff_const(uint32_t op, uint32_t c)
+{
+ int try_neg = 0;
+ uint32_t nc = 0, negop = 0;
+
+ switch (op & 0x1F00000) {
+ case 0x800000: // add
+ case 0x400000: // sub
+ try_neg = 1;
+ negop = op ^ 0xC00000;
+ nc = -c;
+ break;
+ case 0x1A00000: // mov
+ case 0x1E00000: // mvn
+ try_neg = 1;
+ negop = op ^ 0x400000;
+ nc = ~c;
+ break;
+ case 0x200000: // xor
+ if (c == ~0)
+ return (op & 0xF010F000) | ((op >> 16) & 0xF) | 0x1E00000;
+ break;
+ case 0x0: // and
+ if (c == ~0)
+ return (op & 0xF010F000) | ((op >> 16) & 0xF) | 0x1A00000;
+ case 0x1C00000: // bic
+ try_neg = 1;
+ negop = op ^ 0x1C00000;
+ nc = ~c;
+ break;
+ case 0x1800000: // orr
+ if (c == ~0)
+ return (op & 0xFFF0FFFF) | 0x1E00000;
+ break;
+ }
+ do {
+ uint32_t m;
+ int i;
+ if (c < 256) /* catch undefined <<32 */
+ return op | c;
+ for (i = 2; i < 32; i += 2) {
+ m = (0xff >> i) | (0xff << (32 - i));
+ if (!(c & ~m))
+ return op | (i << 7) | (c << i) | (c >> (32 - i));
+ }
+ op = negop;
+ c = nc;
+ } while (try_neg--);
+ return 0;
+}
+
+// only add,sub
+void stuff_const_harder(uint32_t op, uint32_t v)
+{
+ uint32_t x;
+ x = stuff_const(op, v);
+ if (x)
+ o(x);
+ else {
+ uint32_t a[16], nv, no, o2, n2;
+ int i, j, k;
+ a[0] = 0xff;
+ o2 = (op & 0xfff0ffff) | ((op & 0xf000) << 4);
+ ;
+ for (i = 1; i < 16; i++)
+ a[i] = (a[i - 1] >> 2) | (a[i - 1] << 30);
+ for (i = 0; i < 12; i++)
+ for (j = i < 4 ? i + 12 : 15; j >= i + 4; j--)
+ if ((v & (a[i] | a[j])) == v) {
+ o(stuff_const(op, v & a[i]));
+ o(stuff_const(o2, v & a[j]));
+ return;
+ }
+ no = op ^ 0xC00000;
+ n2 = o2 ^ 0xC00000;
+ nv = -v;
+ for (i = 0; i < 12; i++)
+ for (j = i < 4 ? i + 12 : 15; j >= i + 4; j--)
+ if ((nv & (a[i] | a[j])) == nv) {
+ o(stuff_const(no, nv & a[i]));
+ o(stuff_const(n2, nv & a[j]));
+ return;
+ }
+ for (i = 0; i < 8; i++)
+ for (j = i + 4; j < 12; j++)
+ for (k = i < 4 ? i + 12 : 15; k >= j + 4; k--)
+ if ((v & (a[i] | a[j] | a[k])) == v) {
+ o(stuff_const(op, v & a[i]));
+ o(stuff_const(o2, v & a[j]));
+ o(stuff_const(o2, v & a[k]));
+ return;
+ }
+ no = op ^ 0xC00000;
+ nv = -v;
+ for (i = 0; i < 8; i++)
+ for (j = i + 4; j < 12; j++)
+ for (k = i < 4 ? i + 12 : 15; k >= j + 4; k--)
+ if ((nv & (a[i] | a[j] | a[k])) == nv) {
+ o(stuff_const(no, nv & a[i]));
+ o(stuff_const(n2, nv & a[j]));
+ o(stuff_const(n2, nv & a[k]));
+ return;
+ }
+ o(stuff_const(op, v & a[0]));
+ o(stuff_const(o2, v & a[4]));
+ o(stuff_const(o2, v & a[8]));
+ o(stuff_const(o2, v & a[12]));
+ }
+}
+
+ST_FUNC uint32_t encbranch(int pos, int addr, int fail)
+{
+ addr -= pos + 8;
+ addr /= 4;
+ if (addr >= 0x1000000 || addr < -0x1000000) {
+ if (fail)
+ tcc_error("FIXME: function bigger than 32MB");
+ return 0;
+ }
+ return 0x0A000000 | (addr & 0xffffff);
+}
+
+int decbranch(int pos)
+{
+ int x;
+ x = *(uint32_t*)(cur_text_section->data + pos);
+ x &= 0x00ffffff;
+ if (x & 0x800000)
+ x -= 0x1000000;
+ return x * 4 + pos + 8;
+}
+
+/* output a symbol and patch all calls to it */
+void gsym_addr(int t, int a)
+{
+ uint32_t* x;
+ int lt;
+ while (t) {
+ x = (uint32_t*)(cur_text_section->data + t);
+ t = decbranch(lt = t);
+ if (a == lt + 4)
+ *x = 0xE1A00000; // nop
+ else {
+ *x &= 0xff000000;
+ *x |= encbranch(lt, a, 1);
+ }
+ }
+}
+
+void gsym(int t)
+{
+ gsym_addr(t, ind);
+}
+
+#ifdef TCC_ARM_VFP
+static uint32_t vfpr(int r)
+{
+ if (r < TREG_F0 || r > TREG_F7)
+ tcc_error("compiler error! register %i is no vfp register", r);
+ return r - 5;
+}
+#else
+static uint32_t fpr(int r)
+{
+ if (r < TREG_F0 || r > TREG_F3)
+ tcc_error("compiler error! register %i is no fpa register", r);
+ return r - 5;
+}
+#endif
+
+static uint32_t intr(int r)
+{
+ if (r == 4)
+ return 12;
+ if ((r < 0 || r > 4) && r != 14)
+ tcc_error("compiler error! register %i is no int register", r);
+ return r;
+}
+
+static void calcaddr(uint32_t* base, int* off, int* sgn, int maxoff,
+ unsigned shift)
+{
+ if (*off > maxoff || *off & ((1 << shift) - 1)) {
+ uint32_t x, y;
+ x = 0xE280E000;
+ if (*sgn)
+ x = 0xE240E000;
+ x |= (*base) << 16;
+ *base = 14; // lr
+ y = stuff_const(x, *off & ~maxoff);
+ if (y) {
+ o(y);
+ *off &= maxoff;
+ return;
+ }
+ y = stuff_const(x, (*off + maxoff) & ~maxoff);
+ if (y) {
+ o(y);
+ *sgn = !*sgn;
+ *off = ((*off + maxoff) & ~maxoff) - *off;
+ return;
+ }
+ stuff_const_harder(x, *off & ~maxoff);
+ *off &= maxoff;
+ }
+}
+
+static uint32_t mapcc(int cc)
+{
+ switch (cc) {
+ case TOK_ULT:
+ return 0x30000000; /* CC/LO */
+ case TOK_UGE:
+ return 0x20000000; /* CS/HS */
+ case TOK_EQ:
+ return 0x00000000; /* EQ */
+ case TOK_NE:
+ return 0x10000000; /* NE */
+ case TOK_ULE:
+ return 0x90000000; /* LS */
+ case TOK_UGT:
+ return 0x80000000; /* HI */
+ case TOK_Nset:
+ return 0x40000000; /* MI */
+ case TOK_Nclear:
+ return 0x50000000; /* PL */
+ case TOK_LT:
+ return 0xB0000000; /* LT */
+ case TOK_GE:
+ return 0xA0000000; /* GE */
+ case TOK_LE:
+ return 0xD0000000; /* LE */
+ case TOK_GT:
+ return 0xC0000000; /* GT */
+ }
+ tcc_error("unexpected condition code");
+ return 0xE0000000; /* AL */
+}
+
+static int negcc(int cc)
+{
+ switch (cc) {
+ case TOK_ULT:
+ return TOK_UGE;
+ case TOK_UGE:
+ return TOK_ULT;
+ case TOK_EQ:
+ return TOK_NE;
+ case TOK_NE:
+ return TOK_EQ;
+ case TOK_ULE:
+ return TOK_UGT;
+ case TOK_UGT:
+ return TOK_ULE;
+ case TOK_Nset:
+ return TOK_Nclear;
+ case TOK_Nclear:
+ return TOK_Nset;
+ case TOK_LT:
+ return TOK_GE;
+ case TOK_GE:
+ return TOK_LT;
+ case TOK_LE:
+ return TOK_GT;
+ case TOK_GT:
+ return TOK_LE;
+ }
+ tcc_error("unexpected condition code");
+ return TOK_NE;
+}
+
+/* load 'r' from value 'sv' */
+void load(int r, SValue* sv)
+{
+ int v, ft, fc, fr, sign;
+ uint32_t op;
+ SValue v1;
+
+ fr = sv->r;
+ ft = sv->type.t;
+ fc = sv->c.ul;
+
+ if (fc >= 0)
+ sign = 0;
+ else {
+ sign = 1;
+ fc = -fc;
+ }
+
+ v = fr & VT_VALMASK;
+ if (fr & VT_LVAL) {
+ uint32_t base = 0xB; // fp
+ if (v == VT_LLOCAL) {
+ v1.type.t = VT_PTR;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = sv->c.ul;
+ load(base = 14 /* lr */, &v1);
+ fc = sign = 0;
+ v = VT_LOCAL;
+ } else if (v == VT_CONST) {
+ v1.type.t = VT_PTR;
+ v1.r = fr & ~VT_LVAL;
+ v1.c.ul = sv->c.ul;
+ v1.sym = sv->sym;
+ load(base = 14, &v1);
+ fc = sign = 0;
+ v = VT_LOCAL;
+ } else if (v < VT_CONST) {
+ base = intr(v);
+ fc = sign = 0;
+ v = VT_LOCAL;
+ }
+ if (v == VT_LOCAL) {
+ if (is_float(ft)) {
+ calcaddr(&base, &fc, &sign, 1020, 2);
+#ifdef TCC_ARM_VFP
+ op = 0xED100A00; /* flds */
+ if (!sign)
+ op |= 0x800000;
+ if ((ft & VT_BTYPE) != VT_FLOAT)
+ op |= 0x100; /* flds -> fldd */
+ o(op | (vfpr(r) << 12) | (fc >> 2) | (base << 16));
+#else
+ op = 0xED100100;
+ if (!sign)
+ op |= 0x800000;
+#if LDOUBLE_SIZE == 8
+ if ((ft & VT_BTYPE) != VT_FLOAT)
+ op |= 0x8000;
+#else
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ op |= 0x8000;
+ else if ((ft & VT_BTYPE) == VT_LDOUBLE)
+ op |= 0x400000;
+#endif
+ o(op | (fpr(r) << 12) | (fc >> 2) | (base << 16));
+#endif
+ } else if ((ft & (VT_BTYPE | VT_UNSIGNED)) == VT_BYTE ||
+ (ft & VT_BTYPE) == VT_SHORT) {
+ calcaddr(&base, &fc, &sign, 255, 0);
+ op = 0xE1500090;
+ if ((ft & VT_BTYPE) == VT_SHORT)
+ op |= 0x20;
+ if ((ft & VT_UNSIGNED) == 0)
+ op |= 0x40;
+ if (!sign)
+ op |= 0x800000;
+ o(op | (intr(r) << 12) | (base << 16) | ((fc & 0xf0) << 4) |
+ (fc & 0xf));
+ } else {
+ calcaddr(&base, &fc, &sign, 4095, 0);
+ op = 0xE5100000;
+ if (!sign)
+ op |= 0x800000;
+ if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL)
+ op |= 0x400000;
+ o(op | (intr(r) << 12) | fc | (base << 16));
+ }
+ return;
+ }
+ } else {
+ if (v == VT_CONST) {
+ op = stuff_const(0xE3A00000 | (intr(r) << 12), sv->c.ul);
+ if (fr & VT_SYM || !op) {
+ o(0xE59F0000 | (intr(r) << 12));
+ o(0xEA000000);
+ if (fr & VT_SYM)
+ greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32);
+ o(sv->c.ul);
+ } else
+ o(op);
+ return;
+ } else if (v == VT_LOCAL) {
+ op = stuff_const(0xE28B0000 | (intr(r) << 12), sv->c.ul);
+ if (fr & VT_SYM || !op) {
+ o(0xE59F0000 | (intr(r) << 12));
+ o(0xEA000000);
+ if (fr & VT_SYM) // needed ?
+ greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32);
+ o(sv->c.ul);
+ o(0xE08B0000 | (intr(r) << 12) | intr(r));
+ } else
+ o(op);
+ return;
+ } else if (v == VT_CMP) {
+ o(mapcc(sv->c.ul) | 0x3A00001 | (intr(r) << 12));
+ o(mapcc(negcc(sv->c.ul)) | 0x3A00000 | (intr(r) << 12));
+ return;
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ int t;
+ t = v & 1;
+ o(0xE3A00000 | (intr(r) << 12) | t);
+ o(0xEA000000);
+ gsym(sv->c.ul);
+ o(0xE3A00000 | (intr(r) << 12) | (t ^ 1));
+ return;
+ } else if (v < VT_CONST) {
+ if (is_float(ft))
+#ifdef TCC_ARM_VFP
+ o(0xEEB00A40 | (vfpr(r) << 12) | vfpr(v) |
+ T2CPR(ft)); /* fcpyX */
+#else
+ o(0xEE008180 | (fpr(r) << 12) | fpr(v));
+#endif
+ else
+ o(0xE1A00000 | (intr(r) << 12) | intr(v));
+ return;
+ }
+ }
+ tcc_error("load unimplemented!");
+}
+
+/* store register 'r' in lvalue 'v' */
+void store(int r, SValue* sv)
+{
+ SValue v1;
+ int v, ft, fc, fr, sign;
+ uint32_t op;
+
+ fr = sv->r;
+ ft = sv->type.t;
+ fc = sv->c.ul;
+
+ if (fc >= 0)
+ sign = 0;
+ else {
+ sign = 1;
+ fc = -fc;
+ }
+
+ v = fr & VT_VALMASK;
+ if (fr & VT_LVAL || fr == VT_LOCAL) {
+ uint32_t base = 0xb;
+ if (v < VT_CONST) {
+ base = intr(v);
+ v = VT_LOCAL;
+ fc = sign = 0;
+ } else if (v == VT_CONST) {
+ v1.type.t = ft;
+ v1.r = fr & ~VT_LVAL;
+ v1.c.ul = sv->c.ul;
+ v1.sym = sv->sym;
+ load(base = 14, &v1);
+ fc = sign = 0;
+ v = VT_LOCAL;
+ }
+ if (v == VT_LOCAL) {
+ if (is_float(ft)) {
+ calcaddr(&base, &fc, &sign, 1020, 2);
+#ifdef TCC_ARM_VFP
+ op = 0xED000A00; /* fsts */
+ if (!sign)
+ op |= 0x800000;
+ if ((ft & VT_BTYPE) != VT_FLOAT)
+ op |= 0x100; /* fsts -> fstd */
+ o(op | (vfpr(r) << 12) | (fc >> 2) | (base << 16));
+#else
+ op = 0xED000100;
+ if (!sign)
+ op |= 0x800000;
+#if LDOUBLE_SIZE == 8
+ if ((ft & VT_BTYPE) != VT_FLOAT)
+ op |= 0x8000;
+#else
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ op |= 0x8000;
+ if ((ft & VT_BTYPE) == VT_LDOUBLE)
+ op |= 0x400000;
+#endif
+ o(op | (fpr(r) << 12) | (fc >> 2) | (base << 16));
+#endif
+ return;
+ } else if ((ft & VT_BTYPE) == VT_SHORT) {
+ calcaddr(&base, &fc, &sign, 255, 0);
+ op = 0xE14000B0;
+ if (!sign)
+ op |= 0x800000;
+ o(op | (intr(r) << 12) | (base << 16) | ((fc & 0xf0) << 4) |
+ (fc & 0xf));
+ } else {
+ calcaddr(&base, &fc, &sign, 4095, 0);
+ op = 0xE5000000;
+ if (!sign)
+ op |= 0x800000;
+ if ((ft & VT_BTYPE) == VT_BYTE || (ft & VT_BTYPE) == VT_BOOL)
+ op |= 0x400000;
+ o(op | (intr(r) << 12) | fc | (base << 16));
+ }
+ return;
+ }
+ }
+ tcc_error("store unimplemented");
+}
+
+static void gadd_sp(int val)
+{
+ stuff_const_harder(0xE28DD000, val);
+}
+
+/* 'is_jmp' is '1' if it is a jump */
+static void gcall_or_jmp(int is_jmp)
+{
+ int r;
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ uint32_t x;
+ /* constant case */
+ x = encbranch(ind, ind + vtop->c.ul, 0);
+ if (x) {
+ if (vtop->r & VT_SYM) {
+ /* relocation case */
+ greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24);
+ } else
+ put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24,
+ 0);
+ o(x | (is_jmp ? 0xE0000000 : 0xE1000000));
+ } else {
+ if (!is_jmp)
+ o(0xE28FE004); // add lr,pc,#4
+ o(0xE51FF004); // ldr pc,[pc,#-4]
+ if (vtop->r & VT_SYM)
+ greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32);
+ o(vtop->c.ul);
+ }
+ } else {
+ /* otherwise, indirect call */
+ r = gv(RC_INT);
+ if (!is_jmp)
+ o(0xE1A0E00F); // mov lr,pc
+ o(0xE1A0F000 | intr(r)); // mov pc,r
+ }
+}
+
+/* Return whether a structure is an homogeneous float aggregate or not.
+ The answer is true if all the elements of the structure are of the same
+ primitive float type and there is less than 4 elements.
+
+ type: the type corresponding to the structure to be tested */
+static int is_hgen_float_aggr(CType* type)
+{
+ if ((type->t & VT_BTYPE) == VT_STRUCT) {
+ struct Sym* ref;
+ int btype, nb_fields = 0;
+
+ ref = type->ref->next;
+ btype = ref->type.t & VT_BTYPE;
+ if (btype == VT_FLOAT || btype == VT_DOUBLE) {
+ for (; ref && btype == (ref->type.t & VT_BTYPE);
+ ref = ref->next, nb_fields++)
+ ;
+ return !ref && nb_fields <= 4;
+ }
+ }
+ return 0;
+}
+
+struct avail_regs {
+ signed char
+ avail[3]; /* 3 holes max with only float and double alignments */
+ int first_hole; /* first available hole */
+ int last_hole; /* last available hole (none if equal to first_hole) */
+ int first_free_reg; /* next free register in the sequence, hole excluded */
+};
+
+#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 }
+
+/* Find suitable registers for a VFP Co-Processor Register Candidate (VFP CPRC
+ param) according to the rules described in the procedure call standard for
+ the ARM architecture (AAPCS). If found, the registers are assigned to this
+ VFP CPRC parameter. Registers are allocated in sequence unless a hole exists
+ and the parameter is a single float.
+
+ avregs: opaque structure to keep track of available VFP co-processor regs
+ align: alignment contraints for the param, as returned by type_size()
+ size: size of the parameter, as returned by type_size() */
+int assign_vfpreg(struct avail_regs* avregs, int align, int size)
+{
+ int first_reg = 0;
+
+ if (avregs->first_free_reg == -1)
+ return -1;
+ if (align >> 3) { /* double alignment */
+ first_reg = avregs->first_free_reg;
+ /* alignment contraint not respected so use next reg and record hole */
+ if (first_reg & 1)
+ avregs->avail[avregs->last_hole++] = first_reg++;
+ } else { /* no special alignment (float or array of float) */
+ /* if single float and a hole is available, assign the param to it */
+ if (size == 4 && avregs->first_hole != avregs->last_hole)
+ return avregs->avail[avregs->first_hole++];
+ else
+ first_reg = avregs->first_free_reg;
+ }
+ if (first_reg + size / 4 <= 16) {
+ avregs->first_free_reg = first_reg + size / 4;
+ return first_reg;
+ }
+ avregs->first_free_reg = -1;
+ return -1;
+}
+
+/* Returns whether all params need to be passed in core registers or not.
+ This is the case for function part of the runtime ABI. */
+int floats_in_core_regs(SValue* sval)
+{
+ if (!sval->sym)
+ return 0;
+
+ switch (sval->sym->v) {
+ case TOK___floatundisf:
+ case TOK___floatundidf:
+ case TOK___fixunssfdi:
+ case TOK___fixunsdfdi:
+#ifndef TCC_ARM_VFP
+ case TOK___fixunsxfdi:
+#endif
+ case TOK___floatdisf:
+ case TOK___floatdidf:
+ case TOK___fixsfdi:
+ case TOK___fixdfdi:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+ST_FUNC int regargs_nregs(RegArgs* args)
+{
+ return *args;
+}
+
+/* Return the number of registers needed to return the struct, or 0 if
+ returning via struct pointer. */
+ST_FUNC int gfunc_sret(CType* vt, int variadic, CType* ret, int* ret_align,
+ int* regsize, RegArgs* args)
+{
+#ifdef TCC_ARM_EABI
+ int size, align;
+ size = type_size(vt, &align);
+ if (float_abi == ARM_HARD_FLOAT && !variadic &&
+ (is_float(vt->t) || is_hgen_float_aggr(vt))) {
+ *ret_align = 8;
+ *regsize = 8;
+ ret->ref = NULL;
+ ret->t = VT_DOUBLE;
+ *args = (size + 7) >> 3;
+ } else if (size <= 4) {
+ *ret_align = 4;
+ *regsize = 4;
+ ret->ref = NULL;
+ ret->t = VT_INT;
+ *args = 1;
+ } else
+ *args = 0;
+#else
+ *args = 0;
+#endif
+
+ return *args != 0;
+}
+
+/* Parameters are classified according to how they are copied to their final
+ destination for the function call. Because the copying is performed class
+ after class according to the order in the union below, it is important that
+ some constraints about the order of the members of this union are respected:
+ - CORE_STRUCT_CLASS must come after STACK_CLASS;
+ - CORE_CLASS must come after STACK_CLASS, CORE_STRUCT_CLASS and
+ VFP_STRUCT_CLASS;
+ - VFP_STRUCT_CLASS must come after VFP_CLASS.
+ See the comment for the main loop in copy_params() for the reason. */
+enum reg_class {
+ STACK_CLASS = 0,
+ CORE_STRUCT_CLASS,
+ VFP_CLASS,
+ VFP_STRUCT_CLASS,
+ CORE_CLASS,
+ NB_CLASSES
+};
+
+struct param_plan {
+ int start; /* first reg or addr used depending on the class */
+ int end; /* last reg used or next free addr depending on the class */
+ SValue* sval; /* pointer to SValue on the value stack */
+ struct param_plan* prev; /* previous element in this class */
+};
+
+struct plan {
+ struct param_plan* pplans; /* array of all the param plans */
+ struct param_plan*
+ clsplans[NB_CLASSES]; /* per class lists of param plans */
+};
+
+#define add_param_plan(plan, pplan, class) \
+ do { \
+ pplan.prev = plan->clsplans[class]; \
+ plan->pplans[plan##_nb] = pplan; \
+ plan->clsplans[class] = &plan->pplans[plan##_nb++]; \
+ } while (0)
+
+/* Assign parameters to registers and stack with alignment according to the
+ rules in the procedure call standard for the ARM architecture (AAPCS).
+ The overall assignment is recorded in an array of per parameter structures
+ called parameter plans. The parameter plans are also further organized in a
+ number of linked lists, one per class of parameter (see the comment for the
+ definition of union reg_class).
+
+ nb_args: number of parameters of the function for which a call is generated
+ float_abi: float ABI in use for this function call
+ plan: the structure where the overall assignment is recorded
+ todo: a bitmap that record which core registers hold a parameter
+
+ Returns the amount of stack space needed for parameter passing
+
+ Note: this function allocated an array in plan->pplans with tcc_malloc. It
+ is the responsibility of the caller to free this array once used (ie not
+ before copy_params). */
+static int assign_regs(int nb_args, int float_abi, struct plan* plan, int* todo)
+{
+ int i, size, align;
+ int ncrn /* next core register number */,
+ nsaa /* next stacked argument address*/;
+ int plan_nb = 0;
+ struct param_plan pplan;
+ struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
+
+ ncrn = nsaa = 0;
+ *todo = 0;
+ plan->pplans = tcc_malloc(nb_args * sizeof(*plan->pplans));
+ memset(plan->clsplans, 0, sizeof(plan->clsplans));
+ for (i = nb_args; i--;) {
+ int j, start_vfpreg = 0;
+ CType type = vtop[-i].type;
+ type.t &= ~VT_ARRAY;
+ size = type_size(&type, &align);
+ size = (size + 3) & ~3;
+ align = (align + 3) & ~3;
+ switch (vtop[-i].type.t & VT_BTYPE) {
+ case VT_STRUCT:
+ case VT_FLOAT:
+ case VT_DOUBLE:
+ case VT_LDOUBLE:
+ if (float_abi == ARM_HARD_FLOAT) {
+ int is_hfa = 0; /* Homogeneous float aggregate */
+
+ if (is_float(vtop[-i].type.t) ||
+ (is_hfa = is_hgen_float_aggr(&vtop[-i].type))) {
+ int end_vfpreg;
+
+ start_vfpreg = assign_vfpreg(&avregs, align, size);
+ end_vfpreg = start_vfpreg + ((size - 1) >> 2);
+ if (start_vfpreg >= 0) {
+ pplan = (struct param_plan){start_vfpreg, end_vfpreg,
+ &vtop[-i]};
+ if (is_hfa)
+ add_param_plan(plan, pplan, VFP_STRUCT_CLASS);
+ else
+ add_param_plan(plan, pplan, VFP_CLASS);
+ continue;
+ } else
+ break;
+ }
+ }
+ ncrn = (ncrn + (align - 1) / 4) & ~((align / 4) - 1);
+ if (ncrn + size / 4 <= 4 || (ncrn < 4 && start_vfpreg != -1)) {
+ /* The parameter is allocated both in core register and on
+ * stack. As
+ * such, it can be of either class: it would either be the last of
+ * CORE_STRUCT_CLASS or the first of STACK_CLASS. */
+ for (j = ncrn; j < 4 && j < ncrn + size / 4; j++)
+ *todo |= (1 << j);
+ pplan = (struct param_plan){ncrn, j, &vtop[-i]};
+ add_param_plan(plan, pplan, CORE_STRUCT_CLASS);
+ ncrn += size / 4;
+ if (ncrn > 4)
+ nsaa = (ncrn - 4) * 4;
+ } else {
+ ncrn = 4;
+ break;
+ }
+ continue;
+ default:
+ if (ncrn < 4) {
+ int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG;
+
+ if (is_long) {
+ ncrn = (ncrn + 1) & -2;
+ if (ncrn == 4)
+ break;
+ }
+ pplan = (struct param_plan){ncrn, ncrn, &vtop[-i]};
+ ncrn++;
+ if (is_long)
+ pplan.end = ncrn++;
+ add_param_plan(plan, pplan, CORE_CLASS);
+ continue;
+ }
+ }
+ nsaa = (nsaa + (align - 1)) & ~(align - 1);
+ pplan = (struct param_plan){nsaa, nsaa + size, &vtop[-i]};
+ add_param_plan(plan, pplan, STACK_CLASS);
+ nsaa += size; /* size already rounded up before */
+ }
+ return nsaa;
+}
+
+#undef add_param_plan
+
+/* Copy parameters to their final destination (core reg, VFP reg or stack) for
+ function call.
+
+ nb_args: number of parameters the function take
+ plan: the overall assignment plan for parameters
+ todo: a bitmap indicating what core reg will hold a parameter
+
+ Returns the number of SValue added by this function on the value stack */
+static int copy_params(int nb_args, struct plan* plan, int todo)
+{
+ int size, align, r, i, nb_extra_sval = 0;
+ struct param_plan* pplan;
+
+ /* Several constraints require parameters to be copied in a specific order:
+ - structures are copied to the stack before being loaded in a reg;
+ - floats loaded to an odd numbered VFP reg are first copied to the
+ preceding even numbered VFP reg and then moved to the next VFP reg.
+
+ It is thus important that:
+ - structures assigned to core regs must be copied after parameters
+ assigned to the stack but before structures assigned to VFP regs
+ because a structure can lie partly in core registers and partly on
+ the stack;
+ - parameters assigned to the stack and all structures be copied before
+ parameters assigned to a core reg since copying a parameter to the
+ stack require using a core reg;
+ - parameters assigned to VFP regs be copied before structures assigned to
+ VFP regs as the copy might use an even numbered VFP reg that already
+ holds part of a structure. */
+ for (i = 0; i < NB_CLASSES; i++) {
+ for (pplan = plan->clsplans[i]; pplan; pplan = pplan->prev) {
+ vpushv(pplan->sval);
+ pplan->sval->r = pplan->sval->r2 = VT_CONST; /* disable entry */
+ switch (i) {
+ case STACK_CLASS:
+ case CORE_STRUCT_CLASS:
+ case VFP_STRUCT_CLASS:
+ if ((pplan->sval->type.t & VT_BTYPE) == VT_STRUCT) {
+ int padding = 0;
+ size = type_size(&pplan->sval->type, &align);
+ /* align to stack align size */
+ size = (size + 3) & ~3;
+ if (i == STACK_CLASS && pplan->prev)
+ padding = pplan->start - pplan->prev->end;
+ size += padding; /* Add padding if any */
+ /* allocate the necessary size on stack */
+ gadd_sp(-size);
+ /* generate structure store */
+ r = get_reg(RC_INT);
+ o(0xE28D0000 | (intr(r) << 12) |
+ padding); /* add r, sp, padding */
+ vset(&vtop->type, r | VT_LVAL, 0);
+ vswap();
+ vstore(); /* memcpy to current sp + potential padding */
+
+ /* Homogeneous float aggregate are loaded to VFP registers
+ immediately since there is no way of loading data in
+ multiple non consecutive VFP registers as what is done
+ for other structures (see the use of todo). */
+ if (i == VFP_STRUCT_CLASS) {
+ int first = pplan->start, nb = pplan->end - first + 1;
+ /* vpop.32 {pplan->start, ..., pplan->end} */
+ o(0xECBD0A00 | (first & 1) << 22 | (first >> 1) << 12 |
+ nb);
+ /* No need to write the register used to a SValue since
+ VFP regs cannot be used for gcall_or_jmp */
+ }
+ } else {
+ if (is_float(pplan->sval->type.t)) {
+#ifdef TCC_ARM_VFP
+ r = vfpr(gv(RC_FLOAT)) << 12;
+ if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT)
+ size = 4;
+ else {
+ size = 8;
+ r |= 0x101; /* vpush.32 -> vpush.64 */
+ }
+ o(0xED2D0A01 + r); /* vpush */
+#else
+ r = fpr(gv(RC_FLOAT)) << 12;
+ if ((pplan->sval->type.t & VT_BTYPE) == VT_FLOAT)
+ size = 4;
+ else if ((pplan->sval->type.t & VT_BTYPE) == VT_DOUBLE)
+ size = 8;
+ else
+ size = LDOUBLE_SIZE;
+
+ if (size == 12)
+ r |= 0x400000;
+ else if (size == 8)
+ r |= 0x8000;
+
+ o(0xED2D0100 | r | (size >> 2)); /* some kind of vpush for FPA */
+#endif
+ } else {
+ /* simple type (currently always same size) */
+ /* XXX: implicit cast ? */
+ size = 4;
+ if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) {
+ lexpand_nr();
+ size = 8;
+ r = gv(RC_INT);
+ o(0xE52D0004 | (intr(r) << 12)); /* push r */
+ vtop--;
+ }
+ r = gv(RC_INT);
+ o(0xE52D0004 | (intr(r) << 12)); /* push r */
+ }
+ if (i == STACK_CLASS && pplan->prev)
+ gadd_sp(pplan->prev->end -
+ pplan->start); /* Add padding if any */
+ }
+ break;
+
+ case VFP_CLASS:
+ gv(regmask(TREG_F0 + (pplan->start >> 1)));
+ if (pplan->start &
+ 1) { /* Must be in upper part of double register */
+ o(0xEEF00A40 | ((pplan->start >> 1) << 12) |
+ (pplan->start >> 1)); /* vmov.f32 s(n+1), sn */
+ vtop->r =
+ VT_CONST; /* avoid being saved on stack by gv for next
+ float */
+ }
+ break;
+
+ case CORE_CLASS:
+ if ((pplan->sval->type.t & VT_BTYPE) == VT_LLONG) {
+ lexpand_nr();
+ gv(regmask(pplan->end));
+ pplan->sval->r2 = vtop->r;
+ vtop--;
+ }
+ gv(regmask(pplan->start));
+ /* Mark register as used so that gcall_or_jmp use another one
+ (regs >=4 are free as never used to pass parameters) */
+ pplan->sval->r = vtop->r;
+ break;
+ }
+ vtop--;
+ }
+ }
+
+ /* Manually free remaining registers since next parameters are loaded
+ * manually, without the help of gv(int). */
+ save_regs(nb_args);
+
+ if (todo) {
+ o(0xE8BD0000 | todo); /* pop {todo} */
+ for (pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan;
+ pplan = pplan->prev) {
+ int r;
+ pplan->sval->r = pplan->start;
+ /* An SValue can only pin 2 registers at best (r and r2) but a
+ structure can occupy more than 2 registers. Thus, we need to
+ push on the value stack some fake parameter to have on SValue
+ for each registers used by a structure (r2 is not used). */
+ for (r = pplan->start + 1; r <= pplan->end; r++) {
+ if (todo & (1 << r)) {
+ nb_extra_sval++;
+ vpushi(0);
+ vtop->r = r;
+ }
+ }
+ }
+ }
+ return nb_extra_sval;
+}
+
+/* Generate function call. The function address is pushed first, then
+ all the parameters in call order. This functions pops all the
+ parameters and the function address. */
+void gfunc_call(int nb_args)
+{
+ int r, args_size;
+ int def_float_abi = float_abi;
+ int todo;
+ struct plan plan;
+
+#ifdef TCC_ARM_EABI
+ int variadic;
+
+ if (float_abi == ARM_HARD_FLOAT) {
+ variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS);
+ if (variadic || floats_in_core_regs(&vtop[-nb_args]))
+ float_abi = ARM_SOFTFP_FLOAT;
+ }
+#endif
+ /* cannot let cpu flags if other instruction are generated. Also avoid
+ leaving VT_JMP anywhere except on the top of the stack because it
+ would complicate the code generator. */
+ r = vtop->r & VT_VALMASK;
+ if (r == VT_CMP || (r & ~1) == VT_JMP)
+ gv(RC_INT);
+
+ args_size = assign_regs(nb_args, float_abi, &plan, &todo);
+
+#ifdef TCC_ARM_EABI
+ if (args_size & 7) { /* Stack must be 8 byte aligned at fct call for EABI */
+ args_size = (args_size + 7) & ~7;
+ o(0xE24DD004); /* sub sp, sp, #4 */
+ }
+#endif
+
+ nb_args += copy_params(nb_args, &plan, todo);
+ tcc_free(plan.pplans);
+
+ /* Move fct SValue on top as required by gcall_or_jmp */
+ vrotb(nb_args + 1);
+ gcall_or_jmp(0);
+ if (args_size)
+ gadd_sp(args_size); /* pop all parameters passed on the stack */
+#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
+ if (float_abi == ARM_SOFTFP_FLOAT && is_float(vtop->type.ref->type.t)) {
+ if ((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) {
+ o(0xEE000A10); /*vmov s0, r0 */
+ } else {
+ o(0xEE000B10); /* vmov.32 d0[0], r0 */
+ o(0xEE201B10); /* vmov.32 d0[1], r1 */
+ }
+ }
+#endif
+ vtop -= nb_args + 1; /* Pop all params and fct address from value stack */
+ leaffunc =
+ 0; /* we are calling a function, so we aren't in a leaf function */
+ float_abi = def_float_abi;
+}
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(CType* func_type)
+{
+ Sym* sym, *sym2;
+ int n, nf, size, align, rs, struct_ret = 0;
+ int addr, pn, sn; /* pn=core, sn=stack */
+ CType ret_type;
+ RegArgs dummy;
+
+#ifdef TCC_ARM_EABI
+ struct avail_regs avregs = AVAIL_REGS_INITIALIZER;
+#endif
+
+ sym = func_type->ref;
+ func_vt = sym->type;
+ func_var = (func_type->ref->c == FUNC_ELLIPSIS);
+
+ n = nf = 0;
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT &&
+ !gfunc_sret(&func_vt, func_var, &ret_type, &align, &rs, &dummy)) {
+ n++;
+ struct_ret = 1;
+ func_vc = 12; /* Offset from fp of the place to store the result */
+ }
+ for (sym2 = sym->next; sym2 && (n < 4 || nf < 16); sym2 = sym2->next) {
+ size = type_size(&sym2->type, &align);
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT && !func_var &&
+ (is_float(sym2->type.t) || is_hgen_float_aggr(&sym2->type))) {
+ int tmpnf = assign_vfpreg(&avregs, align, size);
+ tmpnf += (size + 3) / 4;
+ nf = (tmpnf > nf) ? tmpnf : nf;
+ } else
+#endif
+ if (n < 4)
+ n += (size + 3) / 4;
+ }
+ o(0xE1A0C00D); /* mov ip,sp */
+ if (func_var)
+ n = 4;
+ if (n) {
+ if (n > 4)
+ n = 4;
+#ifdef TCC_ARM_EABI
+ n = (n + 1) & -2;
+#endif
+ o(0xE92D0000 | ((1 << n) - 1)); /* save r0-r4 on stack if needed */
+ }
+ if (nf) {
+ if (nf > 16)
+ nf = 16;
+ nf = (nf + 1) & -2; /* nf => HARDFLOAT => EABI */
+ o(0xED2D0A00 | nf); /* save s0-s15 on stack if needed */
+ }
+ o(0xE92D5800); /* save fp, ip, lr */
+ o(0xE1A0B00D); /* mov fp, sp */
+ func_sub_sp_offset = ind;
+ o(0xE1A00000); /* nop, leave space for stack adjustment in epilog */
+
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT) {
+ func_vc += nf * 4;
+ avregs = AVAIL_REGS_INITIALIZER;
+ }
+#endif
+ pn = struct_ret, sn = 0;
+ while ((sym = sym->next)) {
+ CType* type;
+ type = &sym->type;
+ size = type_size(type, &align);
+ size = (size + 3) >> 2;
+ align = (align + 3) & ~3;
+#ifdef TCC_ARM_EABI
+ if (float_abi == ARM_HARD_FLOAT && !func_var &&
+ (is_float(sym->type.t) || is_hgen_float_aggr(&sym->type))) {
+ int fpn = assign_vfpreg(&avregs, align, size << 2);
+ if (fpn >= 0)
+ addr = fpn * 4;
+ else
+ goto from_stack;
+ } else
+#endif
+ if (pn < 4) {
+#ifdef TCC_ARM_EABI
+ pn = (pn + (align - 1) / 4) & -(align / 4);
+#endif
+ addr = (nf + pn) * 4;
+ pn += size;
+ if (!sn && pn > 4)
+ sn = (pn - 4);
+ } else {
+#ifdef TCC_ARM_EABI
+ from_stack:
+ sn = (sn + (align - 1) / 4) & -(align / 4);
+#endif
+ addr = (n + nf + sn) * 4;
+ sn += size;
+ }
+ sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t),
+ addr + 12);
+ }
+ last_itod_magic = 0;
+ leaffunc = 1;
+ loc = 0;
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+ uint32_t x;
+ int diff;
+/* Copy float return value to core register if base standard is used and
+ float computation is made with VFP */
+#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP)
+ if ((float_abi == ARM_SOFTFP_FLOAT || func_var) && is_float(func_vt.t)) {
+ if ((func_vt.t & VT_BTYPE) == VT_FLOAT)
+ o(0xEE100A10); /* fmrs r0, s0 */
+ else {
+ o(0xEE100B10); /* fmrdl r0, d0 */
+ o(0xEE301B10); /* fmrdh r1, d0 */
+ }
+ }
+#endif
+ o(0xE89BA800); /* restore fp, sp, pc */
+ diff = (-loc + 3) & -4;
+#ifdef TCC_ARM_EABI
+ if (!leaffunc)
+ diff = ((diff + 11) & -8) - 4;
+#endif
+ if (diff > 0) {
+ x = stuff_const(0xE24BD000, diff); /* sub sp,fp,# */
+ if (x)
+ *(uint32_t*)(cur_text_section->data + func_sub_sp_offset) = x;
+ else {
+ int addr;
+ addr = ind;
+ o(0xE59FC004); /* ldr ip,[pc+4] */
+ o(0xE04BD00C); /* sub sp,fp,ip */
+ o(0xE1A0F00E); /* mov pc,lr */
+ o(diff);
+ *(uint32_t*)(cur_text_section->data + func_sub_sp_offset) =
+ 0xE1000000 | encbranch(func_sub_sp_offset, addr, 1);
+ }
+ }
+}
+
+/* generate a jump to a label */
+int gjmp(int t)
+{
+ int r;
+ r = ind;
+ o(0xE0000000 | encbranch(r, t, 1));
+ return r;
+}
+
+/* generate a jump to a fixed address */
+void gjmp_addr(int a)
+{
+ gjmp(a);
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+int gtst(int inv, int t)
+{
+ int v, r;
+ uint32_t op;
+ v = vtop->r & VT_VALMASK;
+ r = ind;
+ if (v == VT_CMP) {
+ op = mapcc(inv ? negcc(vtop->c.i) : vtop->c.i);
+ op |= encbranch(r, t, 1);
+ o(op);
+ t = r;
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ if ((v & 1) == inv) {
+ if (!vtop->c.i)
+ vtop->c.i = t;
+ else {
+ uint32_t* x;
+ int p, lp;
+ if (t) {
+ p = vtop->c.i;
+ do {
+ p = decbranch(lp = p);
+ } while (p);
+ x = (uint32_t*)(cur_text_section->data + lp);
+ *x &= 0xff000000;
+ *x |= encbranch(lp, t, 1);
+ }
+ t = vtop->c.i;
+ }
+ } else {
+ t = gjmp(t);
+ gsym(vtop->c.i);
+ }
+ }
+ vtop--;
+ return t;
+}
+
+/* generate an integer binary operation */
+void gen_opi(int op)
+{
+ int c, func = 0;
+ uint32_t opc = 0, r, fr;
+ unsigned short retreg = REG_IRET;
+
+ c = 0;
+ switch (op) {
+ case '+':
+ opc = 0x8;
+ c = 1;
+ break;
+ case TOK_ADDC1: /* add with carry generation */
+ opc = 0x9;
+ c = 1;
+ break;
+ case '-':
+ opc = 0x4;
+ c = 1;
+ break;
+ case TOK_SUBC1: /* sub with carry generation */
+ opc = 0x5;
+ c = 1;
+ break;
+ case TOK_ADDC2: /* add with carry use */
+ opc = 0xA;
+ c = 1;
+ break;
+ case TOK_SUBC2: /* sub with carry use */
+ opc = 0xC;
+ c = 1;
+ break;
+ case '&':
+ opc = 0x0;
+ c = 1;
+ break;
+ case '^':
+ opc = 0x2;
+ c = 1;
+ break;
+ case '|':
+ opc = 0x18;
+ c = 1;
+ break;
+ case '*':
+ gv2(RC_INT, RC_INT);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ o(0xE0000090 | (intr(r) << 16) | (intr(r) << 8) | intr(fr));
+ return;
+ case TOK_SHL:
+ opc = 0;
+ c = 2;
+ break;
+ case TOK_SHR:
+ opc = 1;
+ c = 2;
+ break;
+ case TOK_SAR:
+ opc = 2;
+ c = 2;
+ break;
+ case '/':
+ case TOK_PDIV:
+ func = TOK___divsi3;
+ c = 3;
+ break;
+ case TOK_UDIV:
+ func = TOK___udivsi3;
+ c = 3;
+ break;
+ case '%':
+#ifdef TCC_ARM_EABI
+ func = TOK___aeabi_idivmod;
+ retreg = REG_LRET;
+#else
+ func = TOK___modsi3;
+#endif
+ c = 3;
+ break;
+ case TOK_UMOD:
+#ifdef TCC_ARM_EABI
+ func = TOK___aeabi_uidivmod;
+ retreg = REG_LRET;
+#else
+ func = TOK___umodsi3;
+#endif
+ c = 3;
+ break;
+ case TOK_UMULL:
+ gv2(RC_INT, RC_INT);
+ r = intr(vtop[-1].r2 = get_reg(RC_INT));
+ c = vtop[-1].r;
+ vtop[-1].r = get_reg_ex(RC_INT, regmask(c));
+ vtop--;
+ o(0xE0800090 | (r << 16) | (intr(vtop->r) << 12) | (intr(c) << 8) |
+ intr(vtop[1].r));
+ return;
+ default:
+ opc = 0x15;
+ c = 1;
+ break;
+ }
+ switch (c) {
+ case 1:
+ if ((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ if (opc == 4 || opc == 5 || opc == 0xc) {
+ vswap();
+ opc |= 2; // sub -> rsb
+ }
+ }
+ if ((vtop->r & VT_VALMASK) == VT_CMP ||
+ (vtop->r & (VT_VALMASK & ~1)) == VT_JMP)
+ gv(RC_INT);
+ vswap();
+ c = intr(gv(RC_INT));
+ vswap();
+ opc = 0xE0000000 | (opc << 20) | (c << 16);
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ uint32_t x;
+ x = stuff_const(opc | 0x2000000, vtop->c.i);
+ if (x) {
+ r = intr(vtop[-1].r = get_reg_ex(RC_INT, regmask(vtop[-1].r)));
+ o(x | (r << 12));
+ goto done;
+ }
+ }
+ fr = intr(gv(RC_INT));
+ r = intr(vtop[-1].r =
+ get_reg_ex(RC_INT, two2mask(vtop->r, vtop[-1].r)));
+ o(opc | (r << 12) | fr);
+ done:
+ vtop--;
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ }
+ break;
+ case 2:
+ opc = 0xE1A00000 | (opc << 5);
+ if ((vtop->r & VT_VALMASK) == VT_CMP ||
+ (vtop->r & (VT_VALMASK & ~1)) == VT_JMP)
+ gv(RC_INT);
+ vswap();
+ r = intr(gv(RC_INT));
+ vswap();
+ opc |= r;
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ fr = intr(vtop[-1].r = get_reg_ex(RC_INT, regmask(vtop[-1].r)));
+ c = vtop->c.i & 0x1f;
+ o(opc | (c << 7) | (fr << 12));
+ } else {
+ fr = intr(gv(RC_INT));
+ c = intr(vtop[-1].r =
+ get_reg_ex(RC_INT, two2mask(vtop->r, vtop[-1].r)));
+ o(opc | (c << 12) | (fr << 8) | 0x10);
+ }
+ vtop--;
+ break;
+ case 3:
+ vpush_global_sym(&func_old_type, func);
+ vrott(3);
+ gfunc_call(2);
+ vpushi(0);
+ vtop->r = retreg;
+ break;
+ default:
+ tcc_error("gen_opi %i unimplemented!", op);
+ }
+}
+
+#ifdef TCC_ARM_VFP
+static int is_zero(int i)
+{
+ if ((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+ return 0;
+ if (vtop[i].type.t == VT_FLOAT)
+ return (vtop[i].c.f == 0.f);
+ else if (vtop[i].type.t == VT_DOUBLE)
+ return (vtop[i].c.d == 0.0);
+ return (vtop[i].c.ld == 0.l);
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ * two operands are guaranted to have the same floating point type */
+void gen_opf(int op)
+{
+ uint32_t x;
+ int fneg = 0, r;
+ x = 0xEE000A00 | T2CPR(vtop->type.t);
+ switch (op) {
+ case '+':
+ if (is_zero(-1))
+ vswap();
+ if (is_zero(0)) {
+ vtop--;
+ return;
+ }
+ x |= 0x300000;
+ break;
+ case '-':
+ x |= 0x300040;
+ if (is_zero(0)) {
+ vtop--;
+ return;
+ }
+ if (is_zero(-1)) {
+ x |= 0x810000; /* fsubX -> fnegX */
+ vswap();
+ vtop--;
+ fneg = 1;
+ }
+ break;
+ case '*':
+ x |= 0x200000;
+ break;
+ case '/':
+ x |= 0x800000;
+ break;
+ default:
+ if (op < TOK_ULT || op > TOK_GT) {
+ tcc_error("unknown fp op %x!", op);
+ return;
+ }
+ if (is_zero(-1)) {
+ vswap();
+ switch(op) {
+ case TOK_LT: op = TOK_GT; break;
+ case TOK_GE: op = TOK_ULE; break;
+ case TOK_LE: op = TOK_GE; break;
+ case TOK_GT: op = TOK_ULT; break;
+ }
+ }
+ x |= 0xB40040; /* fcmpX */
+ if (op != TOK_EQ && op != TOK_NE)
+ x |= 0x80; /* fcmpX -> fcmpeX */
+ if (is_zero(0)) {
+ vtop--;
+ o(x | 0x10000 |
+ (vfpr(gv(RC_FLOAT)) << 12)); /* fcmp(e)X -> fcmp(e)zX */
+ } else {
+ x |= vfpr(gv(RC_FLOAT));
+ vswap();
+ o(x | (vfpr(gv(RC_FLOAT)) << 12));
+ vtop--;
+ }
+ o(0xEEF1FA10); /* fmstat */
+
+ switch(op) {
+ case TOK_LE: op = TOK_ULE; break;
+ case TOK_LT: op = TOK_ULT; break;
+ case TOK_UGE: op = TOK_GE; break;
+ case TOK_UGT: op = TOK_GT; break;
+ }
+
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ return;
+ }
+ r = gv(RC_FLOAT);
+ x |= vfpr(r);
+ r = regmask(r);
+ if (!fneg) {
+ int r2;
+ vswap();
+ r2 = gv(RC_FLOAT);
+ x |= vfpr(r2) << 16;
+ r |= regmask(r2);
+ }
+ vtop->r = get_reg_ex(RC_FLOAT, r);
+ if (!fneg)
+ vtop--;
+ o(x | (vfpr(vtop->r) << 12));
+}
+
+#else
+static uint32_t is_fconst()
+{
+ long double f;
+ uint32_t r;
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+ return 0;
+ if (vtop->type.t == VT_FLOAT)
+ f = vtop->c.f;
+ else if (vtop->type.t == VT_DOUBLE)
+ f = vtop->c.d;
+ else
+ f = vtop->c.ld;
+ if (!ieee_finite(f))
+ return 0;
+ r = 0x8;
+ if (f < 0.0) {
+ r = 0x18;
+ f = -f;
+ }
+ if (f == 0.0)
+ return r;
+ if (f == 1.0)
+ return r | 1;
+ if (f == 2.0)
+ return r | 2;
+ if (f == 3.0)
+ return r | 3;
+ if (f == 4.0)
+ return r | 4;
+ if (f == 5.0)
+ return r | 5;
+ if (f == 0.5)
+ return r | 6;
+ if (f == 10.0)
+ return r | 7;
+ return 0;
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ two operands are guaranted to have the same floating point type */
+void gen_opf(int op)
+{
+ uint32_t x, r, r2, c1, c2;
+ // fputs("gen_opf\n",stderr);
+ vswap();
+ c1 = is_fconst();
+ vswap();
+ c2 = is_fconst();
+ x = 0xEE000100;
+#if LDOUBLE_SIZE == 8
+ if ((vtop->type.t & VT_BTYPE) != VT_FLOAT)
+ x |= 0x80;
+#else
+ if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
+ x |= 0x80;
+ else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE)
+ x |= 0x80000;
+#endif
+ switch (op) {
+ case '+':
+ if (!c2) {
+ vswap();
+ c2 = c1;
+ }
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ if (c2) {
+ if (c2 > 0xf)
+ x |= 0x200000; // suf
+ r2 = c2 & 0xf;
+ } else {
+ r2 = fpr(gv(RC_FLOAT));
+ }
+ break;
+ case '-':
+ if (c2) {
+ if (c2 <= 0xf)
+ x |= 0x200000; // suf
+ r2 = c2 & 0xf;
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ } else if (c1 && c1 <= 0xf) {
+ x |= 0x300000; // rsf
+ r2 = c1;
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ } else {
+ x |= 0x200000; // suf
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ r2 = fpr(gv(RC_FLOAT));
+ }
+ break;
+ case '*':
+ if (!c2 || c2 > 0xf) {
+ vswap();
+ c2 = c1;
+ }
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ if (c2 && c2 <= 0xf)
+ r2 = c2;
+ else
+ r2 = fpr(gv(RC_FLOAT));
+ x |= 0x100000; // muf
+ break;
+ case '/':
+ if (c2 && c2 <= 0xf) {
+ x |= 0x400000; // dvf
+ r2 = c2;
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ } else if (c1 && c1 <= 0xf) {
+ x |= 0x500000; // rdf
+ r2 = c1;
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ } else {
+ x |= 0x400000; // dvf
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ r2 = fpr(gv(RC_FLOAT));
+ }
+ break;
+ default:
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ x |= 0xd0f110; // cmfe
+ /* bug (intention?) in Linux FPU emulator
+ doesn't set carry if equal */
+ switch (op) {
+ case TOK_ULT:
+ case TOK_UGE:
+ case TOK_ULE:
+ case TOK_UGT:
+ tcc_error("unsigned comparison on floats?");
+ break;
+ case TOK_LT:
+ op = TOK_Nset;
+ break;
+ case TOK_LE:
+ op = TOK_ULE; /* correct in unordered case only if AC bit in
+ FPSR set */
+ break;
+ case TOK_EQ:
+ case TOK_NE:
+ x &= ~0x400000; // cmfe -> cmf
+ break;
+ }
+ if (c1 && !c2) {
+ c2 = c1;
+ vswap();
+ switch (op) {
+ case TOK_Nset:
+ op = TOK_GT;
+ break;
+ case TOK_GE:
+ op = TOK_ULE;
+ break;
+ case TOK_ULE:
+ op = TOK_GE;
+ break;
+ case TOK_GT:
+ op = TOK_Nset;
+ break;
+ }
+ }
+ vswap();
+ r = fpr(gv(RC_FLOAT));
+ vswap();
+ if (c2) {
+ if (c2 > 0xf)
+ x |= 0x200000;
+ r2 = c2 & 0xf;
+ } else {
+ r2 = fpr(gv(RC_FLOAT));
+ }
+ vtop[-1].r = VT_CMP;
+ vtop[-1].c.i = op;
+ } else {
+ tcc_error("unknown fp op %x!", op);
+ return;
+ }
+ }
+ if (vtop[-1].r == VT_CMP)
+ c1 = 15;
+ else {
+ c1 = vtop->r;
+ if (r2 & 0x8)
+ c1 = vtop[-1].r;
+ vtop[-1].r = get_reg_ex(RC_FLOAT, two2mask(vtop[-1].r, c1));
+ c1 = fpr(vtop[-1].r);
+ }
+ vtop--;
+ o(x | (r << 16) | (c1 << 12) | r2);
+}
+#endif
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+ and 'long long' cases. */
+ST_FUNC void gen_cvt_itof1(int t)
+{
+ uint32_t r, r2;
+ int bt;
+ bt = vtop->type.t & VT_BTYPE;
+ if (bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) {
+#ifndef TCC_ARM_VFP
+ uint32_t dsize = 0;
+#endif
+ r = intr(gv(RC_INT));
+#ifdef TCC_ARM_VFP
+ r2 = vfpr(vtop->r = get_reg(RC_FLOAT));
+ o(0xEE000A10 | (r << 12) | (r2 << 16)); /* fmsr */
+ r2 |= r2 << 12;
+ if (!(vtop->type.t & VT_UNSIGNED))
+ r2 |= 0x80; /* fuitoX -> fsituX */
+ o(0xEEB80A40 | r2 | T2CPR(t)); /* fYitoX*/
+#else
+ r2 = fpr(vtop->r = get_reg(RC_FLOAT));
+ if ((t & VT_BTYPE) != VT_FLOAT)
+ dsize = 0x80; /* flts -> fltd */
+ o(0xEE000110 | dsize | (r2 << 16) | (r << 12)); /* flts */
+ if ((vtop->type.t & (VT_UNSIGNED | VT_BTYPE)) ==
+ (VT_UNSIGNED | VT_INT)) {
+ uint32_t off = 0;
+ o(0xE3500000 | (r << 12)); /* cmp */
+ r = fpr(get_reg(RC_FLOAT));
+ if (last_itod_magic) {
+ off = ind + 8 - last_itod_magic;
+ off /= 4;
+ if (off > 255)
+ off = 0;
+ }
+ o(0xBD1F0100 | (r << 12) | off); /* ldflts */
+ if (!off) {
+ o(0xEA000000); /* b */
+ last_itod_magic = ind;
+ o(0x4F800000); /* 4294967296.0f */
+ }
+ o(0xBE000100 | dsize | (r2 << 16) | (r2 << 12) | r); /* adflt */
+ }
+#endif
+ return;
+ } else if (bt == VT_LLONG) {
+ int func;
+ CType* func_type = 0;
+ if ((t & VT_BTYPE) == VT_FLOAT) {
+ func_type = &func_float_type;
+ if (vtop->type.t & VT_UNSIGNED)
+ func = TOK___floatundisf;
+ else
+ func = TOK___floatdisf;
+#if LDOUBLE_SIZE != 8
+ } else if ((t & VT_BTYPE) == VT_LDOUBLE) {
+ func_type = &func_ldouble_type;
+ if (vtop->type.t & VT_UNSIGNED)
+ func = TOK___floatundixf;
+ else
+ func = TOK___floatdixf;
+ } else if ((t & VT_BTYPE) == VT_DOUBLE) {
+#else
+ } else if ((t & VT_BTYPE) == VT_DOUBLE ||
+ (t & VT_BTYPE) == VT_LDOUBLE) {
+#endif
+ func_type = &func_double_type;
+ if (vtop->type.t & VT_UNSIGNED)
+ func = TOK___floatundidf;
+ else
+ func = TOK___floatdidf;
+ }
+ if (func_type) {
+ vpush_global_sym(func_type, func);
+ vswap();
+ gfunc_call(1);
+ vpushi(0);
+ vtop->r = TREG_F0;
+ return;
+ }
+ }
+ tcc_error("unimplemented gen_cvt_itof %x!", vtop->type.t);
+}
+
+/* convert fp to int 't' type */
+void gen_cvt_ftoi(int t)
+{
+ uint32_t r, r2;
+ int u, func = 0;
+ u = t & VT_UNSIGNED;
+ t &= VT_BTYPE;
+ r2 = vtop->type.t & VT_BTYPE;
+ if (t == VT_INT) {
+#ifdef TCC_ARM_VFP
+ r = vfpr(gv(RC_FLOAT));
+ u = u ? 0 : 0x10000;
+ o(0xEEBC0AC0 | (r << 12) | r | T2CPR(r2) | u); /* ftoXizY */
+ r2 = intr(vtop->r = get_reg(RC_INT));
+ o(0xEE100A10 | (r << 16) | (r2 << 12));
+ return;
+#else
+ if (u) {
+ if (r2 == VT_FLOAT)
+ func = TOK___fixunssfsi;
+#if LDOUBLE_SIZE != 8
+ else if (r2 == VT_LDOUBLE)
+ func = TOK___fixunsxfsi;
+ else if (r2 == VT_DOUBLE)
+#else
+ else if (r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
+#endif
+ func = TOK___fixunsdfsi;
+ } else {
+ r = fpr(gv(RC_FLOAT));
+ r2 = intr(vtop->r = get_reg(RC_INT));
+ o(0xEE100170 | (r2 << 12) | r);
+ return;
+ }
+#endif
+ } else if (t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1
+ if (r2 == VT_FLOAT)
+ func = TOK___fixsfdi;
+#if LDOUBLE_SIZE != 8
+ else if (r2 == VT_LDOUBLE)
+ func = TOK___fixxfdi;
+ else if (r2 == VT_DOUBLE)
+#else
+ else if (r2 == VT_LDOUBLE || r2 == VT_DOUBLE)
+#endif
+ func = TOK___fixdfdi;
+ }
+ if (func) {
+ vpush_global_sym(&func_old_type, func);
+ vswap();
+ gfunc_call(1);
+ vpushi(0);
+ if (t == VT_LLONG)
+ vtop->r2 = REG_LRET;
+ vtop->r = REG_IRET;
+ return;
+ }
+ tcc_error("unimplemented gen_cvt_ftoi!");
+}
+
+/* convert from one floating point type to another */
+void gen_cvt_ftof(int t)
+{
+#ifdef TCC_ARM_VFP
+ if (((vtop->type.t & VT_BTYPE) == VT_FLOAT) !=
+ ((t & VT_BTYPE) == VT_FLOAT)) {
+ uint32_t r = vfpr(gv(RC_FLOAT));
+ o(0xEEB70AC0 | (r << 12) | r | T2CPR(vtop->type.t));
+ }
+#else
+ /* all we have to do on i386 and FPA ARM is to put the float in a register
+ */
+ gv(RC_FLOAT);
+#endif
+}
+
+/* computed goto support */
+void ggoto(void)
+{
+ gcall_or_jmp(1);
+ vtop--;
+}
+
+/* Save the stack pointer onto the stack and return the location of its address
+ */
+ST_FUNC void gen_vla_sp_save(int addr)
+{
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* Restore the SP from a location on the stack */
+ST_FUNC void gen_vla_sp_restore(int addr)
+{
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* Subtract from the stack pointer, and push the resulting value onto the stack
+ */
+ST_FUNC void gen_vla_alloc(CType* type, int align)
+{
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* end of ARM code generator */
+/*************************************************************/
+#endif
+/*************************************************************/
diff --git a/src/arm/arm64-gen.c b/src/arm/arm64-gen.c
new file mode 100644
index 0000000..fe3d7aa
--- /dev/null
+++ b/src/arm/arm64-gen.c
@@ -0,0 +1,1846 @@
+/*
+ * A64 code generator for TCC
+ *
+ * Copyright (c) 2014-2015 Edmund Grimley Evans
+ *
+ * Copying and distribution of this file, with or without modification,
+ * are permitted in any medium without royalty provided the copyright
+ * notice and this notice are preserved. This file is offered as-is,
+ * without any warranty.
+ */
+
+#ifdef TARGET_DEFS_ONLY
+
+// Number of registers available to allocator:
+#define NB_REGS 28 // x0-x18, x30, v0-v7
+
+typedef int RegArgs;
+
+#define TREG_R(x) (x) // x = 0..18
+#define TREG_R30 19
+#define TREG_F(x) (x + 20) // x = 0..7
+
+// Register classes sorted from more general to more precise:
+#define RC_INT (1 << 0)
+#define RC_FLOAT (1 << 1)
+#define RC_R(x) (1 << (2 + (x))) // x = 0..18
+#define RC_R30 (1 << 21)
+#define RC_F(x) (1 << (22 + (x))) // x = 0..7
+
+#define RC_IRET (RC_R(0)) // int return register class
+#define RC_FRET (RC_F(0)) // float return register class
+
+#define REG_IRET (TREG_R(0)) // int return register number
+#define REG_FRET (TREG_F(0)) // float return register number
+
+#define PTR_SIZE 8
+
+#define LDOUBLE_SIZE 16
+#define LDOUBLE_ALIGN 16
+
+#define MAX_ALIGN 16
+
+#define CHAR_IS_UNSIGNED
+
+/******************************************************/
+/* ELF defines */
+
+#define EM_TCC_TARGET EM_AARCH64
+
+#define R_DATA_32 R_AARCH64_ABS32
+#define R_DATA_PTR R_AARCH64_ABS64
+#define R_JMP_SLOT R_AARCH64_JUMP_SLOT
+#define R_COPY R_AARCH64_COPY
+
+#define ELF_START_ADDR 0x00400000
+#define ELF_PAGE_SIZE 0x1000
+
+/******************************************************/
+#else /* ! TARGET_DEFS_ONLY */
+/******************************************************/
+#include "../tcc.h"
+#include <assert.h>
+
+ST_DATA const int reg_classes[NB_REGS] = {
+ RC_INT | RC_R(0),
+ RC_INT | RC_R(1),
+ RC_INT | RC_R(2),
+ RC_INT | RC_R(3),
+ RC_INT | RC_R(4),
+ RC_INT | RC_R(5),
+ RC_INT | RC_R(6),
+ RC_INT | RC_R(7),
+ RC_INT | RC_R(8),
+ RC_INT | RC_R(9),
+ RC_INT | RC_R(10),
+ RC_INT | RC_R(11),
+ RC_INT | RC_R(12),
+ RC_INT | RC_R(13),
+ RC_INT | RC_R(14),
+ RC_INT | RC_R(15),
+ RC_INT | RC_R(16),
+ RC_INT | RC_R(17),
+ RC_INT | RC_R(18),
+ RC_R30, // not in RC_INT as we make special use of x30
+ RC_FLOAT | RC_F(0),
+ RC_FLOAT | RC_F(1),
+ RC_FLOAT | RC_F(2),
+ RC_FLOAT | RC_F(3),
+ RC_FLOAT | RC_F(4),
+ RC_FLOAT | RC_F(5),
+ RC_FLOAT | RC_F(6),
+ RC_FLOAT | RC_F(7)
+};
+
+#define IS_FREG(x) ((x) >= TREG_F(0))
+
+static uint32_t intr(int r)
+{
+ assert(TREG_R(0) <= r && r <= TREG_R30);
+ return r < TREG_R30 ? r : 30;
+}
+
+static uint32_t fltr(int r)
+{
+ assert(TREG_F(0) <= r && r <= TREG_F(7));
+ return r - TREG_F(0);
+}
+
+// Add an instruction to text section:
+ST_FUNC void o(unsigned int c)
+{
+ int ind1 = ind + 4;
+ if (ind1 > cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ *(uint32_t *)(cur_text_section->data + ind) = c;
+ ind = ind1;
+}
+
+static int arm64_encode_bimm64(uint64_t x)
+{
+ int neg = x & 1;
+ int rep, pos, len;
+
+ if (neg)
+ x = ~x;
+ if (!x)
+ return -1;
+
+ if (x >> 2 == (x & (((uint64_t)1 << (64 - 2)) - 1)))
+ rep = 2, x &= ((uint64_t)1 << 2) - 1;
+ else if (x >> 4 == (x & (((uint64_t)1 << (64 - 4)) - 1)))
+ rep = 4, x &= ((uint64_t)1 << 4) - 1;
+ else if (x >> 8 == (x & (((uint64_t)1 << (64 - 8)) - 1)))
+ rep = 8, x &= ((uint64_t)1 << 8) - 1;
+ else if (x >> 16 == (x & (((uint64_t)1 << (64 - 16)) - 1)))
+ rep = 16, x &= ((uint64_t)1 << 16) - 1;
+ else if (x >> 32 == (x & (((uint64_t)1 << (64 - 32)) - 1)))
+ rep = 32, x &= ((uint64_t)1 << 32) - 1;
+ else
+ rep = 64;
+
+ pos = 0;
+ if (!(x & (((uint64_t)1 << 32) - 1))) x >>= 32, pos += 32;
+ if (!(x & (((uint64_t)1 << 16) - 1))) x >>= 16, pos += 16;
+ if (!(x & (((uint64_t)1 << 8) - 1))) x >>= 8, pos += 8;
+ if (!(x & (((uint64_t)1 << 4) - 1))) x >>= 4, pos += 4;
+ if (!(x & (((uint64_t)1 << 2) - 1))) x >>= 2, pos += 2;
+ if (!(x & (((uint64_t)1 << 1) - 1))) x >>= 1, pos += 1;
+
+ len = 0;
+ if (!(~x & (((uint64_t)1 << 32) - 1))) x >>= 32, len += 32;
+ if (!(~x & (((uint64_t)1 << 16) - 1))) x >>= 16, len += 16;
+ if (!(~x & (((uint64_t)1 << 8) - 1))) x >>= 8, len += 8;
+ if (!(~x & (((uint64_t)1 << 4) - 1))) x >>= 4, len += 4;
+ if (!(~x & (((uint64_t)1 << 2) - 1))) x >>= 2, len += 2;
+ if (!(~x & (((uint64_t)1 << 1) - 1))) x >>= 1, len += 1;
+
+ if (x)
+ return -1;
+ if (neg) {
+ pos = (pos + len) & (rep - 1);
+ len = rep - len;
+ }
+ return ((0x1000 & rep << 6) | (((rep - 1) ^ 31) << 1 & 63) |
+ ((rep - pos) & (rep - 1)) << 6 | (len - 1));
+}
+
+static uint32_t arm64_movi(int r, uint64_t x)
+{
+ uint64_t m = 0xffff;
+ int e;
+ if (!(x & ~m))
+ return 0x52800000 | r | x << 5; // movz w(r),#(x)
+ if (!(x & ~(m << 16)))
+ return 0x52a00000 | r | x >> 11; // movz w(r),#(x >> 16),lsl #16
+ if (!(x & ~(m << 32)))
+ return 0xd2c00000 | r | x >> 27; // movz x(r),#(x >> 32),lsl #32
+ if (!(x & ~(m << 48)))
+ return 0xd2e00000 | r | x >> 43; // movz x(r),#(x >> 48),lsl #48
+ if ((x & ~m) == m << 16)
+ return (0x12800000 | r |
+ (~x << 5 & 0x1fffe0)); // movn w(r),#(~x)
+ if ((x & ~(m << 16)) == m)
+ return (0x12a00000 | r |
+ (~x >> 11 & 0x1fffe0)); // movn w(r),#(~x >> 16),lsl #16
+ if (!~(x | m))
+ return (0x92800000 | r |
+ (~x << 5 & 0x1fffe0)); // movn x(r),#(~x)
+ if (!~(x | m << 16))
+ return (0x92a00000 | r |
+ (~x >> 11 & 0x1fffe0)); // movn x(r),#(~x >> 16),lsl #16
+ if (!~(x | m << 32))
+ return (0x92c00000 | r |
+ (~x >> 27 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32
+ if (!~(x | m << 48))
+ return (0x92e00000 | r |
+ (~x >> 43 & 0x1fffe0)); // movn x(r),#(~x >> 32),lsl #32
+ if (!(x >> 32) && (e = arm64_encode_bimm64(x | x << 32)) >= 0)
+ return 0x320003e0 | r | (uint32_t)e << 10; // movi w(r),#(x)
+ if ((e = arm64_encode_bimm64(x)) >= 0)
+ return 0xb20003e0 | r | (uint32_t)e << 10; // movi x(r),#(x)
+ return 0;
+}
+
+static void arm64_movimm(int r, uint64_t x)
+{
+ uint32_t i;
+ if ((i = arm64_movi(r, x)))
+ o(i); // a single MOV
+ else {
+ // MOVZ/MOVN and 1-3 MOVKs
+ int z = 0, m = 0;
+ uint32_t mov1 = 0xd2800000; // movz
+ uint64_t x1 = x;
+ for (i = 0; i < 64; i += 16) {
+ z += !(x >> i & 0xffff);
+ m += !(~x >> i & 0xffff);
+ }
+ if (m > z) {
+ x1 = ~x;
+ mov1 = 0x92800000; // movn
+ }
+ for (i = 0; i < 64; i += 16)
+ if (x1 >> i & 0xffff) {
+ o(mov1 | r | (x1 >> i & 0xffff) << 5 | i << 17);
+ // movz/movn x(r),#(*),lsl #(i)
+ break;
+ }
+ for (i += 16; i < 64; i += 16)
+ if (x1 >> i & 0xffff)
+ o(0xf2800000 | r | (x >> i & 0xffff) << 5 | i << 17);
+ // movk x(r),#(*),lsl #(i)
+ }
+}
+
+// Patch all branches in list pointed to by t to branch to a:
+ST_FUNC void gsym_addr(int t_, int a_)
+{
+ uint32_t t = t_;
+ uint32_t a = a_;
+ while (t) {
+ uint32_t *ptr = (uint32_t *)(cur_text_section->data + t);
+ uint32_t next = *ptr;
+ if (a - t + 0x8000000 >= 0x10000000)
+ tcc_error("branch out of range");
+ *ptr = (a - t == 4 ? 0xd503201f : // nop
+ 0x14000000 | ((a - t) >> 2 & 0x3ffffff)); // b
+ t = next;
+ }
+}
+
+// Patch all branches in list pointed to by t to branch to current location:
+ST_FUNC void gsym(int t)
+{
+ gsym_addr(t, ind);
+}
+
+static int arm64_type_size(int t)
+{
+ switch (t & VT_BTYPE) {
+ case VT_INT: return 2;
+ case VT_BYTE: return 0;
+ case VT_SHORT: return 1;
+ case VT_PTR: return 3;
+ case VT_ENUM: return 2;
+ case VT_FUNC: return 3;
+ case VT_FLOAT: return 2;
+ case VT_DOUBLE: return 3;
+ case VT_LDOUBLE: return 4;
+ case VT_BOOL: return 0;
+ case VT_LLONG: return 3;
+ }
+ assert(0);
+ return 0;
+}
+
+static void arm64_spoff(int reg, uint64_t off)
+{
+ uint32_t sub = off >> 63;
+ if (sub)
+ off = -off;
+ if (off < 4096)
+ o(0x910003e0 | sub << 30 | reg | off << 10);
+ // (add|sub) x(reg),sp,#(off)
+ else {
+ arm64_movimm(30, off); // use x30 for offset
+ o(0x8b3e63e0 | sub << 30 | reg); // (add|sub) x(reg),sp,x30
+ }
+}
+
+static void arm64_ldrx(int sg, int sz, int dst, int bas, uint64_t off)
+{
+ if (sz >= 2)
+ sg = 0;
+ if (!(off & ~(0xfff << sz)))
+ o(0x39400000 | dst | bas << 5 | off << (10 - sz) |
+ !!sg << 23 | sz << 30); // ldr(*) x(dst),[x(bas),#(off)]
+ else if (off < 256 || -off <= 256)
+ o(0x38400000 | dst | bas << 5 | (off & 511) << 12 |
+ !!sg << 23 | sz << 30); // ldur(*) x(dst),[x(bas),#(off)]
+ else {
+ arm64_movimm(30, off); // use x30 for offset
+ o(0x38206800 | dst | bas << 5 | 30 << 16 |
+ (!!sg + 1) << 22 | sz << 30); // ldr(*) x(dst),[x(bas),x30]
+ }
+}
+
+static void arm64_ldrv(int sz, int dst, int bas, uint64_t off)
+{
+ if (!(off & ~(0xfff << sz)))
+ o(0x3d400000 | dst | bas << 5 | off << (10 - sz) |
+ (sz & 4) << 21 | (sz & 3) << 30); // ldr (s|d|q)(dst),[x(bas),#(off)]
+ else if (off < 256 || -off <= 256)
+ o(0x3c400000 | dst | bas << 5 | (off & 511) << 12 |
+ (sz & 4) << 21 | (sz & 3) << 30); // ldur (s|d|q)(dst),[x(bas),#(off)]
+ else {
+ arm64_movimm(30, off); // use x30 for offset
+ o(0x3c606800 | dst | bas << 5 | 30 << 16 | sz << 30 | (sz & 4) << 21);
+ // ldr (s|d|q)(dst),[x(bas),x30]
+ }
+}
+
+static void arm64_ldrs(int reg, int size)
+{
+ // Use x30 for intermediate value in some cases.
+ switch (size) {
+ default: assert(0); break;
+ case 1:
+ arm64_ldrx(0, 0, reg, reg, 0);
+ break;
+ case 2:
+ arm64_ldrx(0, 1, reg, reg, 0);
+ break;
+ case 3:
+ arm64_ldrx(0, 1, 30, reg, 0);
+ arm64_ldrx(0, 0, reg, reg, 2);
+ o(0x2a0043c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #16
+ break;
+ case 4:
+ arm64_ldrx(0, 2, reg, reg, 0);
+ break;
+ case 5:
+ arm64_ldrx(0, 2, 30, reg, 0);
+ arm64_ldrx(0, 0, reg, reg, 4);
+ o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32
+ break;
+ case 6:
+ arm64_ldrx(0, 2, 30, reg, 0);
+ arm64_ldrx(0, 1, reg, reg, 4);
+ o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32
+ break;
+ case 7:
+ arm64_ldrx(0, 2, 30, reg, 0);
+ arm64_ldrx(0, 2, reg, reg, 3);
+ o(0x53087c00 | reg | reg << 5); // lsr w(reg), w(reg), #8
+ o(0xaa0083c0 | reg | reg << 16); // orr x(reg),x30,x(reg),lsl #32
+ break;
+ case 8:
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 9:
+ arm64_ldrx(0, 0, reg + 1, reg, 8);
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 10:
+ arm64_ldrx(0, 1, reg + 1, reg, 8);
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 11:
+ arm64_ldrx(0, 2, reg + 1, reg, 7);
+ o(0x53087c00 | (reg+1) | (reg+1) << 5); // lsr w(reg+1), w(reg+1), #8
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 12:
+ arm64_ldrx(0, 2, reg + 1, reg, 8);
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 13:
+ arm64_ldrx(0, 3, reg + 1, reg, 5);
+ o(0xd358fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #24
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 14:
+ arm64_ldrx(0, 3, reg + 1, reg, 6);
+ o(0xd350fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #16
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 15:
+ arm64_ldrx(0, 3, reg + 1, reg, 7);
+ o(0xd348fc00 | (reg+1) | (reg+1) << 5); // lsr x(reg+1), x(reg+1), #8
+ arm64_ldrx(0, 3, reg, reg, 0);
+ break;
+ case 16:
+ o(0xa9400000 | reg | (reg+1) << 10 | reg << 5);
+ // ldp x(reg),x(reg+1),[x(reg)]
+ break;
+ }
+}
+
+static void arm64_strx(int sz, int dst, int bas, uint64_t off)
+{
+ if (!(off & ~(0xfff << sz)))
+ o(0x39000000 | dst | bas << 5 | off << (10 - sz) | sz << 30);
+ // str(*) x(dst),[x(bas],#(off)]
+ else if (off < 256 || -off <= 256)
+ o(0x38000000 | dst | bas << 5 | (off & 511) << 12 | sz << 30);
+ // stur(*) x(dst),[x(bas],#(off)]
+ else {
+ arm64_movimm(30, off); // use x30 for offset
+ o(0x38206800 | dst | bas << 5 | 30 << 16 | sz << 30);
+ // str(*) x(dst),[x(bas),x30]
+ }
+}
+
+static void arm64_strv(int sz, int dst, int bas, uint64_t off)
+{
+ if (!(off & ~(0xfff << sz)))
+ o(0x3d000000 | dst | bas << 5 | off << (10 - sz) |
+ (sz & 4) << 21 | (sz & 3) << 30); // str (s|d|q)(dst),[x(bas),#(off)]
+ else if (off < 256 || -off <= 256)
+ o(0x3c000000 | dst | bas << 5 | (off & 511) << 12 |
+ (sz & 4) << 21 | (sz & 3) << 30); // stur (s|d|q)(dst),[x(bas),#(off)]
+ else {
+ arm64_movimm(30, off); // use x30 for offset
+ o(0x3c206800 | dst | bas << 5 | 30 << 16 | sz << 30 | (sz & 4) << 21);
+ // str (s|d|q)(dst),[x(bas),x30]
+ }
+}
+
+static void arm64_sym(int r, Sym *sym, unsigned long addend)
+{
+ // Currently TCC's linker does not generate COPY relocations for
+ // STT_OBJECTs when tcc is invoked with "-run". This typically
+ // results in "R_AARCH64_ADR_PREL_PG_HI21 relocation failed" when
+ // a program refers to stdin. A workaround is to avoid that
+ // relocation and use only relocations with unlimited range.
+ int avoid_adrp = 1;
+
+ if (avoid_adrp || (sym->type.t & VT_WEAK)) {
+ // (GCC uses a R_AARCH64_ABS64 in this case.)
+ greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G0_NC, addend);
+ o(0xd2800000 | r); // mov x(rt),#0,lsl #0
+ greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G1_NC, addend);
+ o(0xf2a00000 | r); // movk x(rt),#0,lsl #16
+ greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G2_NC, addend);
+ o(0xf2c00000 | r); // movk x(rt),#0,lsl #32
+ greloca(cur_text_section, sym, ind, R_AARCH64_MOVW_UABS_G3, addend);
+ o(0xf2e00000 | r); // movk x(rt),#0,lsl #48
+ }
+ else {
+ greloca(cur_text_section, sym, ind, R_AARCH64_ADR_PREL_PG_HI21, addend);
+ o(0x90000000 | r);
+ greloca(cur_text_section, sym, ind, R_AARCH64_ADD_ABS_LO12_NC, addend);
+ o(0x91000000 | r | r << 5);
+ }
+}
+
+ST_FUNC void load(int r, SValue *sv)
+{
+ int svtt = sv->type.t;
+ int svr = sv->r & ~VT_LVAL_TYPE;
+ int svrv = svr & VT_VALMASK;
+ uint64_t svcul = (int32_t)sv->c.ul;
+
+ if (svr == (VT_LOCAL | VT_LVAL)) {
+ if (IS_FREG(r))
+ arm64_ldrv(arm64_type_size(svtt), fltr(r), 29, svcul);
+ else
+ arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
+ intr(r), 29, svcul);
+ return;
+ }
+
+ if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) {
+ if (IS_FREG(r))
+ arm64_ldrv(arm64_type_size(svtt), fltr(r), intr(svrv), 0);
+ else
+ arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
+ intr(r), intr(svrv), 0);
+ return;
+ }
+
+ if (svr == (VT_CONST | VT_LVAL | VT_SYM)) {
+ arm64_sym(30, sv->sym, svcul); // use x30 for address
+ if (IS_FREG(r))
+ arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0);
+ else
+ arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
+ intr(r), 30, 0);
+ return;
+ }
+
+ if (svr == (VT_CONST | VT_SYM)) {
+ arm64_sym(intr(r), sv->sym, svcul);
+ return;
+ }
+
+ if (svr == VT_CONST) {
+ if ((svtt & VT_BTYPE) != VT_VOID)
+ arm64_movimm(intr(r), arm64_type_size(svtt) == 3 ?
+ sv->c.ull : (uint32_t)svcul);
+ return;
+ }
+
+ if (svr < VT_CONST) {
+ if (IS_FREG(r) && IS_FREG(svr))
+ if (svtt == VT_LDOUBLE)
+ o(0x4ea01c00 | fltr(r) | fltr(svr) << 5);
+ // mov v(r).16b,v(svr).16b
+ else
+ o(0x1e604000 | fltr(r) | fltr(svr) << 5); // fmov d(r),d(svr)
+ else if (!IS_FREG(r) && !IS_FREG(svr))
+ o(0xaa0003e0 | intr(r) | intr(svr) << 16); // mov x(r),x(svr)
+ else
+ assert(0);
+ return;
+ }
+
+ if (svr == VT_LOCAL) {
+ if (-svcul < 0x1000)
+ o(0xd10003a0 | intr(r) | -svcul << 10); // sub x(r),x29,#...
+ else {
+ arm64_movimm(30, -svcul); // use x30 for offset
+ o(0xcb0003a0 | intr(r) | 30 << 16); // sub x(r),x29,x30
+ }
+ return;
+ }
+
+ if (svr == VT_JMP || svr == VT_JMPI) {
+ int t = (svr == VT_JMPI);
+ arm64_movimm(intr(r), t);
+ o(0x14000002); // b .+8
+ gsym(svcul);
+ arm64_movimm(intr(r), t ^ 1);
+ return;
+ }
+
+ if (svr == (VT_LLOCAL | VT_LVAL)) {
+ arm64_ldrx(0, 3, 30, 29, svcul); // use x30 for offset
+ if (IS_FREG(r))
+ arm64_ldrv(arm64_type_size(svtt), fltr(r), 30, 0);
+ else
+ arm64_ldrx(!(svtt & VT_UNSIGNED), arm64_type_size(svtt),
+ intr(r), 30, 0);
+ return;
+ }
+
+ printf("load(%x, (%x, %x, %llx))\n", r, svtt, sv->r, (long long)svcul);
+ assert(0);
+}
+
+ST_FUNC void store(int r, SValue *sv)
+{
+ int svtt = sv->type.t;
+ int svr = sv->r & ~VT_LVAL_TYPE;
+ int svrv = svr & VT_VALMASK;
+ uint64_t svcul = (int32_t)sv->c.ul;
+
+ if (svr == (VT_LOCAL | VT_LVAL)) {
+ if (IS_FREG(r))
+ arm64_strv(arm64_type_size(svtt), fltr(r), 29, svcul);
+ else
+ arm64_strx(arm64_type_size(svtt), intr(r), 29, svcul);
+ return;
+ }
+
+ if ((svr & ~VT_VALMASK) == VT_LVAL && svrv < VT_CONST) {
+ if (IS_FREG(r))
+ arm64_strv(arm64_type_size(svtt), fltr(r), intr(svrv), 0);
+ else
+ arm64_strx(arm64_type_size(svtt), intr(r), intr(svrv), 0);
+ return;
+ }
+
+ if (svr == (VT_CONST | VT_LVAL | VT_SYM)) {
+ arm64_sym(30, sv->sym, svcul); // use x30 for address
+ if (IS_FREG(r))
+ arm64_strv(arm64_type_size(svtt), fltr(r), 30, 0);
+ else
+ arm64_strx(arm64_type_size(svtt), intr(r), 30, 0);
+ return;
+ }
+
+ printf("store(%x, (%x, %x, %llx))\n", r, svtt, sv->r, (long long)svcul);
+ assert(0);
+}
+
+static void arm64_gen_bl_or_b(int b)
+{
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ assert(!b);
+ if (vtop->r & VT_SYM)
+ greloc(cur_text_section, vtop->sym, ind, R_AARCH64_CALL26);
+ else
+ assert(0);
+ o(0x94000000); // bl .
+ }
+ else
+ o(0xd61f0000 | !b << 21 | intr(gv(RC_R30)) << 5); // br/blr
+}
+
+static int arm64_hfa_aux(CType *type, int *fsize, int num)
+{
+ if (is_float(type->t)) {
+ int a, n = type_size(type, &a);
+ if (num >= 4 || (*fsize && *fsize != n))
+ return -1;
+ *fsize = n;
+ return num + 1;
+ }
+ else if ((type->t & VT_BTYPE) == VT_STRUCT) {
+ int is_struct = 0; // rather than union
+ Sym *field;
+ for (field = type->ref->next; field; field = field->next)
+ if (field->c) {
+ is_struct = 1;
+ break;
+ }
+ if (is_struct) {
+ int num0 = num;
+ for (field = type->ref->next; field; field = field->next) {
+ if (field->c != (num - num0) * *fsize)
+ return -1;
+ num = arm64_hfa_aux(&field->type, fsize, num);
+ if (num == -1)
+ return -1;
+ }
+ if (type->ref->c != (num - num0) * *fsize)
+ return -1;
+ return num;
+ }
+ else { // union
+ int num0 = num;
+ for (field = type->ref->next; field; field = field->next) {
+ int num1 = arm64_hfa_aux(&field->type, fsize, num0);
+ if (num1 == -1)
+ return -1;
+ num = num1 < num ? num : num1;
+ }
+ if (type->ref->c != (num - num0) * *fsize)
+ return -1;
+ return num;
+ }
+ }
+ else if (type->t & VT_ARRAY) {
+ int num1;
+ if (!type->ref->c)
+ return num;
+ num1 = arm64_hfa_aux(&type->ref->type, fsize, num);
+ if (num1 == -1 || (num1 != num && type->ref->c > 4))
+ return -1;
+ num1 = num + type->ref->c * (num1 - num);
+ if (num1 > 4)
+ return -1;
+ return num1;
+ }
+ return -1;
+}
+
+static int arm64_hfa(CType *type, int *fsize)
+{
+ if ((type->t & VT_BTYPE) == VT_STRUCT || (type->t & VT_ARRAY)) {
+ int sz = 0;
+ int n = arm64_hfa_aux(type, &sz, 0);
+ if (0 < n && n <= 4) {
+ if (fsize)
+ *fsize = sz;
+ return n;
+ }
+ }
+ return 0;
+}
+
+static unsigned long arm64_pcs_aux(int n, CType **type, unsigned long *a)
+{
+ int nx = 0; // next integer register
+ int nv = 0; // next vector register
+ unsigned long ns = 32; // next stack offset
+ int i;
+
+ for (i = 0; i < n; i++) {
+ int hfa = arm64_hfa(type[i], 0);
+ int size, align;
+
+ if ((type[i]->t & VT_ARRAY) ||
+ (type[i]->t & VT_BTYPE) == VT_FUNC)
+ size = align = 8;
+ else
+ size = type_size(type[i], &align);
+
+ if (hfa)
+ // B.2
+ ;
+ else if (size > 16) {
+ // B.3: replace with pointer
+ if (nx < 8)
+ a[i] = nx++ << 1 | 1;
+ else {
+ ns = (ns + 7) & ~7;
+ a[i] = ns | 1;
+ ns += 8;
+ }
+ continue;
+ }
+ else if ((type[i]->t & VT_BTYPE) == VT_STRUCT)
+ // B.4
+ size = (size + 7) & ~7;
+
+ // C.1
+ if (is_float(type[i]->t) && nv < 8) {
+ a[i] = 16 + (nv++ << 1);
+ continue;
+ }
+
+ // C.2
+ if (hfa && nv + hfa <= 8) {
+ a[i] = 16 + (nv << 1);
+ nv += hfa;
+ continue;
+ }
+
+ // C.3
+ if (hfa) {
+ nv = 8;
+ size = (size + 7) & ~7;
+ }
+
+ // C.4
+ if (hfa || (type[i]->t & VT_BTYPE) == VT_LDOUBLE) {
+ ns = (ns + 7) & ~7;
+ ns = (ns + align - 1) & -align;
+ }
+
+ // C.5
+ if ((type[i]->t & VT_BTYPE) == VT_FLOAT)
+ size = 8;
+
+ // C.6
+ if (hfa || is_float(type[i]->t)) {
+ a[i] = ns;
+ ns += size;
+ continue;
+ }
+
+ // C.7
+ if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size <= 8 && nx < 8) {
+ a[i] = nx++ << 1;
+ continue;
+ }
+
+ // C.8
+ if (align == 16)
+ nx = (nx + 1) & ~1;
+
+ // C.9
+ if ((type[i]->t & VT_BTYPE) != VT_STRUCT && size == 16 && nx < 7) {
+ a[i] = nx << 1;
+ nx += 2;
+ continue;
+ }
+
+ // C.10
+ if ((type[i]->t & VT_BTYPE) == VT_STRUCT && size <= (8 - nx) * 8) {
+ a[i] = nx << 1;
+ nx += (size + 7) >> 3;
+ continue;
+ }
+
+ // C.11
+ nx = 8;
+
+ // C.12
+ ns = (ns + 7) & ~7;
+ ns = (ns + align - 1) & -align;
+
+ // C.13
+ if ((type[i]->t & VT_BTYPE) == VT_STRUCT) {
+ a[i] = ns;
+ ns += size;
+ continue;
+ }
+
+ // C.14
+ if (size < 8)
+ size = 8;
+
+ // C.15
+ a[i] = ns;
+ ns += size;
+ }
+
+ return ns - 32;
+}
+
+static unsigned long arm64_pcs(int n, CType **type, unsigned long *a)
+{
+ unsigned long stack;
+
+ // Return type:
+ if ((type[0]->t & VT_BTYPE) == VT_VOID)
+ a[0] = -1;
+ else {
+ arm64_pcs_aux(1, type, a);
+ assert(a[0] == 0 || a[0] == 1 || a[0] == 16);
+ }
+
+ // Argument types:
+ stack = arm64_pcs_aux(n, type + 1, a + 1);
+
+ if (0) {
+ int i;
+ for (i = 0; i <= n; i++) {
+ if (!i)
+ printf("arm64_pcs return: ");
+ else
+ printf("arm64_pcs arg %d: ", i);
+ if (a[i] == (unsigned long)-1)
+ printf("void\n");
+ else if (a[i] == 1 && !i)
+ printf("X8 pointer\n");
+ else if (a[i] < 16)
+ printf("X%lu%s\n", a[i] / 2, a[i] & 1 ? " pointer" : "");
+ else if (a[i] < 32)
+ printf("V%lu\n", a[i] / 2 - 8);
+ else
+ printf("stack %lu%s\n",
+ (a[i] - 32) & ~1, a[i] & 1 ? " pointer" : "");
+ }
+ }
+
+ return stack;
+}
+
+ST_FUNC void gfunc_call(int nb_args)
+{
+ CType *return_type;
+ CType **t;
+ unsigned long *a, *a1;
+ unsigned long stack;
+ int i;
+
+ return_type = &vtop[-nb_args].type.ref->type;
+ if ((return_type->t & VT_BTYPE) == VT_STRUCT)
+ --nb_args;
+
+ t = tcc_malloc((nb_args + 1) * sizeof(*t));
+ a = tcc_malloc((nb_args + 1) * sizeof(*a));
+ a1 = tcc_malloc((nb_args + 1) * sizeof(*a1));
+
+ t[0] = return_type;
+ for (i = 0; i < nb_args; i++)
+ t[nb_args - i] = &vtop[-i].type;
+
+ stack = arm64_pcs(nb_args, t, a);
+
+ // Allocate space for structs replaced by pointer:
+ for (i = nb_args; i; i--)
+ if (a[i] & 1) {
+ SValue *arg = &vtop[i - nb_args];
+ int align, size = type_size(&arg->type, &align);
+ assert((arg->type.t & VT_BTYPE) == VT_STRUCT);
+ stack = (stack + align - 1) & -align;
+ a1[i] = stack;
+ stack += size;
+ }
+
+ stack = (stack + 15) >> 4 << 4;
+
+ assert(stack < 0x1000);
+ if (stack)
+ o(0xd10003ff | stack << 10); // sub sp,sp,#(n)
+
+ // First pass: set all values on stack
+ for (i = nb_args; i; i--) {
+ vpushv(vtop - nb_args + i);
+
+ if (a[i] & 1) {
+ // struct replaced by pointer
+ int r = get_reg(RC_INT);
+ arm64_spoff(intr(r), a1[i]);
+ vset(&vtop->type, r | VT_LVAL, 0);
+ vswap();
+ vstore();
+ if (a[i] >= 32) {
+ // pointer on stack
+ r = get_reg(RC_INT);
+ arm64_spoff(intr(r), a1[i]);
+ arm64_strx(3, intr(r), 31, (a[i] - 32) >> 1 << 1);
+ }
+ }
+ else if (a[i] >= 32) {
+ // value on stack
+ if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+ int r = get_reg(RC_INT);
+ arm64_spoff(intr(r), a[i] - 32);
+ vset(&vtop->type, r | VT_LVAL, 0);
+ vswap();
+ vstore();
+ }
+ else if (is_float(vtop->type.t)) {
+ gv(RC_FLOAT);
+ arm64_strv(arm64_type_size(vtop[0].type.t),
+ fltr(vtop[0].r), 31, a[i] - 32);
+ }
+ else {
+ gv(RC_INT);
+ arm64_strx(arm64_type_size(vtop[0].type.t),
+ intr(vtop[0].r), 31, a[i] - 32);
+ }
+ }
+
+ --vtop;
+ }
+
+ // Second pass: assign values to registers
+ for (i = nb_args; i; i--, vtop--) {
+ if (a[i] < 16 && !(a[i] & 1)) {
+ // value in general-purpose registers
+ if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+ int align, size = type_size(&vtop->type, &align);
+ vtop->type.t = VT_PTR;
+ gaddrof();
+ gv(RC_R(a[i] / 2));
+ arm64_ldrs(a[i] / 2, size);
+ }
+ else
+ gv(RC_R(a[i] / 2));
+ }
+ else if (a[i] < 16)
+ // struct replaced by pointer in register
+ arm64_spoff(a[i] / 2, a1[i]);
+ else if (a[i] < 32) {
+ // value in floating-point registers
+ if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+ int j, sz, n = arm64_hfa(&vtop->type, &sz);
+ vtop->type.t = VT_PTR;
+ gaddrof();
+ gv(RC_R30);
+ for (j = 0; j < n; j++)
+ o(0x3d4003c0 |
+ (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
+ (a[i] / 2 - 8 + j) |
+ j << 10); // ldr ([sdq])(*),[x30,#(j * sz)]
+ }
+ else
+ gv(RC_F(a[i] / 2 - 8));
+ }
+ }
+
+ if ((return_type->t & VT_BTYPE) == VT_STRUCT) {
+ if (a[0] == 1) {
+ // indirect return: set x8 and discard the stack value
+ gv(RC_R(8));
+ --vtop;
+ }
+ else
+ // return in registers: keep the address for after the call
+ vswap();
+ }
+
+ save_regs(0);
+ arm64_gen_bl_or_b(0);
+ --vtop;
+ if (stack)
+ o(0x910003ff | stack << 10); // add sp,sp,#(n)
+
+ {
+ int rt = return_type->t;
+ int bt = rt & VT_BTYPE;
+ if (bt == VT_BYTE || bt == VT_SHORT)
+ // Promote small integers:
+ o(0x13001c00 | (bt == VT_SHORT) << 13 |
+ !!(rt & VT_UNSIGNED) << 30); // [su]xt[bh] w0,w0
+ else if (bt == VT_STRUCT && !(a[0] & 1)) {
+ // A struct was returned in registers, so write it out:
+ gv(RC_R(8));
+ --vtop;
+ if (a[0] == 0) {
+ int align, size = type_size(return_type, &align);
+ assert(size <= 16);
+ if (size > 8)
+ o(0xa9000500); // stp x0,x1,[x8]
+ else if (size)
+ arm64_strx(size > 4 ? 3 : size > 2 ? 2 : size > 1, 0, 8, 0);
+
+ }
+ else if (a[0] == 16) {
+ int j, sz, n = arm64_hfa(return_type, &sz);
+ for (j = 0; j < n; j++)
+ o(0x3d000100 |
+ (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
+ (a[i] / 2 - 8 + j) |
+ j << 10); // str ([sdq])(*),[x8,#(j * sz)]
+ }
+ }
+ }
+
+ tcc_free(a1);
+ tcc_free(a);
+ tcc_free(t);
+}
+
+static unsigned long arm64_func_va_list_stack;
+static int arm64_func_va_list_gr_offs;
+static int arm64_func_va_list_vr_offs;
+static int arm64_func_sub_sp_offset;
+
+ST_FUNC void gfunc_prolog(CType *func_type)
+{
+ int n = 0;
+ int i = 0;
+ Sym *sym;
+ CType **t;
+ unsigned long *a;
+
+ // Why doesn't the caller (gen_function) set func_vt?
+ func_vt = func_type->ref->type;
+ func_vc = 144; // offset of where x8 is stored
+
+ for (sym = func_type->ref; sym; sym = sym->next)
+ ++n;
+ t = tcc_malloc(n * sizeof(*t));
+ a = tcc_malloc(n * sizeof(*a));
+
+ for (sym = func_type->ref; sym; sym = sym->next)
+ t[i++] = &sym->type;
+
+ arm64_func_va_list_stack = arm64_pcs(n - 1, t, a);
+
+ o(0xa9b27bfd); // stp x29,x30,[sp,#-224]!
+ o(0xad0087e0); // stp q0,q1,[sp,#16]
+ o(0xad018fe2); // stp q2,q3,[sp,#48]
+ o(0xad0297e4); // stp q4,q5,[sp,#80]
+ o(0xad039fe6); // stp q6,q7,[sp,#112]
+ o(0xa90923e8); // stp x8,x8,[sp,#144]
+ o(0xa90a07e0); // stp x0,x1,[sp,#160]
+ o(0xa90b0fe2); // stp x2,x3,[sp,#176]
+ o(0xa90c17e4); // stp x4,x5,[sp,#192]
+ o(0xa90d1fe6); // stp x6,x7,[sp,#208]
+
+ arm64_func_va_list_gr_offs = -64;
+ arm64_func_va_list_vr_offs = -128;
+
+ for (i = 1, sym = func_type->ref->next; sym; i++, sym = sym->next) {
+ int off = (a[i] < 16 ? 160 + a[i] / 2 * 8 :
+ a[i] < 32 ? 16 + (a[i] - 16) / 2 * 16 :
+ 224 + ((a[i] - 32) >> 1 << 1));
+ sym_push(sym->v & ~SYM_FIELD, &sym->type,
+ (a[i] & 1 ? VT_LLOCAL : VT_LOCAL) | lvalue_type(sym->type.t),
+ off);
+
+ if (a[i] < 16) {
+ int align, size = type_size(&sym->type, &align);
+ arm64_func_va_list_gr_offs = (a[i] / 2 - 7 +
+ (!(a[i] & 1) && size > 8)) * 8;
+ }
+ else if (a[i] < 32) {
+ int hfa = arm64_hfa(&sym->type, 0);
+ arm64_func_va_list_vr_offs = (a[i] / 2 - 16 +
+ (hfa ? hfa : 1)) * 16;
+ }
+
+ // HFAs of float and double need to be written differently:
+ if (16 <= a[i] && a[i] < 32 && (sym->type.t & VT_BTYPE) == VT_STRUCT) {
+ int j, sz, k = arm64_hfa(&sym->type, &sz);
+ if (sz < 16)
+ for (j = 0; j < k; j++) {
+ o(0x3d0003e0 | -(sz & 8) << 27 | (sz & 4) << 29 |
+ ((a[i] - 16) / 2 + j) | (off / sz + j) << 10);
+ // str ([sdq])(*),[sp,#(j * sz)]
+ }
+ }
+ }
+
+ tcc_free(a);
+ tcc_free(t);
+
+ o(0x910003fd); // mov x29,sp
+ arm64_func_sub_sp_offset = ind;
+ // In gfunc_epilog these will be replaced with code to decrement SP:
+ o(0xd503201f); // nop
+ o(0xd503201f); // nop
+ loc = 0;
+}
+
+ST_FUNC void gen_va_start(void)
+{
+ int r;
+ --vtop; // we don't need the "arg"
+ gaddrof();
+ r = intr(gv(RC_INT));
+
+ if (arm64_func_va_list_stack) {
+ //xx could use add (immediate) here
+ arm64_movimm(30, arm64_func_va_list_stack + 224);
+ o(0x8b1e03be); // add x30,x29,x30
+ }
+ else
+ o(0x910383be); // add x30,x29,#224
+ o(0xf900001e | r << 5); // str x30,[x(r)]
+
+ if (arm64_func_va_list_gr_offs) {
+ if (arm64_func_va_list_stack)
+ o(0x910383be); // add x30,x29,#224
+ o(0xf900041e | r << 5); // str x30,[x(r),#8]
+ }
+
+ if (arm64_func_va_list_vr_offs) {
+ o(0x910243be); // add x30,x29,#144
+ o(0xf900081e | r << 5); // str x30,[x(r),#16]
+ }
+
+ arm64_movimm(30, arm64_func_va_list_gr_offs);
+ o(0xb900181e | r << 5); // str w30,[x(r),#24]
+
+ arm64_movimm(30, arm64_func_va_list_vr_offs);
+ o(0xb9001c1e | r << 5); // str w30,[x(r),#28]
+
+ --vtop;
+}
+
+ST_FUNC void gen_va_arg(CType *t)
+{
+ int align, size = type_size(t, &align);
+ int fsize, hfa = arm64_hfa(t, &fsize);
+ uint32_t r0, r1;
+
+ if (is_float(t->t)) {
+ hfa = 1;
+ fsize = size;
+ }
+
+ gaddrof();
+ r0 = intr(gv(RC_INT));
+ r1 = get_reg(RC_INT);
+ vtop[0].r = r1 | lvalue_type(t->t);
+ r1 = intr(r1);
+
+ if (!hfa) {
+ uint32_t n = size > 16 ? 8 : (size + 7) & -8;
+ o(0xb940181e | r0 << 5); // ldr w30,[x(r0),#24] // __gr_offs
+ if (align == 16) {
+ assert(0); // this path untested but needed for __uint128_t
+ o(0x11003fde); // add w30,w30,#15
+ o(0x121c6fde); // and w30,w30,#-16
+ }
+ o(0x310003c0 | r1 | n << 10); // adds w(r1),w30,#(n)
+ o(0x540000ad); // b.le .+20
+ o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack
+ o(0x9100001e | r1 << 5 | n << 10); // add x30,x(r1),#(n)
+ o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack
+ o(0x14000004); // b .+16
+ o(0xb9001800 | r1 | r0 << 5); // str w(r1),[x(r0),#24] // __gr_offs
+ o(0xf9400400 | r1 | r0 << 5); // ldr x(r1),[x(r0),#8] // __gr_top
+ o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw
+ if (size > 16)
+ o(0xf9400000 | r1 | r1 << 5); // ldr x(r1),[x(r1)]
+ }
+ else {
+ uint32_t rsz = hfa << 4;
+ uint32_t ssz = (size + 7) & -(uint32_t)8;
+ uint32_t b1, b2;
+ o(0xb9401c1e | r0 << 5); // ldr w30,[x(r0),#28] // __vr_offs
+ o(0x310003c0 | r1 | rsz << 10); // adds w(r1),w30,#(rsz)
+ b1 = ind; o(0x5400000d); // b.le lab1
+ o(0xf9400000 | r1 | r0 << 5); // ldr x(r1),[x(r0)] // __stack
+ if (fsize == 16) {
+ o(0x91003c00 | r1 | r1 << 5); // add x(r1),x(r1),#15
+ o(0x927cec00 | r1 | r1 << 5); // and x(r1),x(r1),#-16
+ }
+ o(0x9100001e | r1 << 5 | ssz << 10); // add x30,x(r1),#(ssz)
+ o(0xf900001e | r0 << 5); // str x30,[x(r0)] // __stack
+ b2 = ind; o(0x14000000); // b lab2
+ // lab1:
+ *(uint32_t *)(cur_text_section->data + b1) =
+ (0x5400000d | (ind - b1) << 3);
+ o(0xb9001c00 | r1 | r0 << 5); // str w(r1),[x(r0),#28] // __vr_offs
+ o(0xf9400800 | r1 | r0 << 5); // ldr x(r1),[x(r0),#16] // __vr_top
+ if (hfa == 1 || fsize == 16)
+ o(0x8b3ec000 | r1 | r1 << 5); // add x(r1),x(r1),w30,sxtw
+ else {
+ // We need to change the layout of this HFA.
+ // Get some space on the stack using global variable "loc":
+ loc = (loc - size) & -(uint32_t)align;
+ o(0x8b3ec000 | 30 | r1 << 5); // add x30,x(r1),w30,sxtw
+ arm64_movimm(r1, loc);
+ o(0x8b0003a0 | r1 | r1 << 16); // add x(r1),x29,x(r1)
+ o(0x4c402bdc | (uint32_t)fsize << 7 |
+ (uint32_t)(hfa == 2) << 15 |
+ (uint32_t)(hfa == 3) << 14); // ld1 {v28.(4s|2d),...},[x30]
+ o(0x0d00801c | r1 << 5 | (fsize == 8) << 10 |
+ (uint32_t)(hfa != 2) << 13 |
+ (uint32_t)(hfa != 3) << 21); // st(hfa) {v28.(s|d),...}[0],[x(r1)]
+ }
+ // lab2:
+ *(uint32_t *)(cur_text_section->data + b2) =
+ (0x14000000 | (ind - b2) >> 2);
+ }
+}
+
+ST_FUNC int regargs_nregs(RegArgs *args)
+{
+ return *args;
+}
+
+ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize, RegArgs *args)
+{
+ *args = 0;
+
+ return 0;
+}
+
+ST_FUNC void greturn(void)
+{
+ CType *t = &func_vt;
+ unsigned long a;
+
+ arm64_pcs(0, &t, &a);
+ switch (a) {
+ case -1:
+ break;
+ case 0:
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+ int align, size = type_size(&func_vt, &align);
+ gaddrof();
+ gv(RC_R(0));
+ arm64_ldrs(0, size);
+ }
+ else
+ gv(RC_IRET);
+ break;
+ case 1: {
+ CType type = func_vt;
+ mk_pointer(&type);
+ vset(&type, VT_LOCAL | VT_LVAL, func_vc);
+ indir();
+ vswap();
+ vstore();
+ break;
+ }
+ case 16:
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+ int j, sz, n = arm64_hfa(&vtop->type, &sz);
+ gaddrof();
+ gv(RC_R(0));
+ for (j = 0; j < n; j++)
+ o(0x3d400000 |
+ (sz & 16) << 19 | -(sz & 8) << 27 | (sz & 4) << 29 |
+ j | j << 10); // ldr ([sdq])(*),[x0,#(j * sz)]
+ }
+ else
+ gv(RC_FRET);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+ST_FUNC void gfunc_epilog(void)
+{
+ if (loc) {
+ // Insert instructions to subtract size of stack frame from SP.
+ uint32_t *ptr =
+ (uint32_t *)(cur_text_section->data + arm64_func_sub_sp_offset);
+ uint64_t diff = (-loc + 15) & ~15;
+ if (!(diff >> 24)) {
+ if (diff & 0xfff) // sub sp,sp,#(diff & 0xfff)
+ ptr[0] = 0xd10003ff | (diff & 0xfff) << 10;
+ if (diff >> 12) // sub sp,sp,#(diff >> 12),lsl #12
+ ptr[1] = 0xd14003ff | (diff >> 12) << 10;
+ }
+ else {
+ // In this case we may subtract more than necessary,
+ // but always less than 17/16 of what we were aiming for.
+ int i = 0;
+ int j = 0;
+ while (diff >> 20) {
+ diff = (diff + 0xffff) >> 16;
+ ++i;
+ }
+ while (diff >> 16) {
+ diff = (diff + 1) >> 1;
+ ++j;
+ }
+ ptr[0] = 0xd2800010 | diff << 5 | i << 21;
+ // mov x16,#(diff),lsl #(16 * i)
+ ptr[1] = 0xcb3063ff | j << 10;
+ // sub sp,sp,x16,lsl #(j)
+ }
+ }
+ o(0x910003bf); // mov sp,x29
+ o(0xa8ce7bfd); // ldp x29,x30,[sp],#224
+
+ o(0xd65f03c0); // ret
+}
+
+// Generate forward branch to label:
+ST_FUNC int gjmp(int t)
+{
+ int r = ind;
+ o(t);
+ return r;
+}
+
+// Generate branch to known address:
+ST_FUNC void gjmp_addr(int a)
+{
+ assert(a - ind + 0x8000000 < 0x10000000);
+ o(0x14000000 | ((a - ind) >> 2 & 0x3ffffff));
+}
+
+ST_FUNC int gtst(int inv, int t)
+{
+ int bt = vtop->type.t & VT_BTYPE;
+ if (bt == VT_LDOUBLE) {
+ int a, b, f = fltr(gv(RC_FLOAT));
+ a = get_reg(RC_INT);
+ vpushi(0);
+ vtop[0].r = a;
+ b = get_reg(RC_INT);
+ a = intr(a);
+ b = intr(b);
+ o(0x4e083c00 | a | f << 5); // mov x(a),v(f).d[0]
+ o(0x4e183c00 | b | f << 5); // mov x(b),v(f).d[1]
+ o(0xaa000400 | a | a << 5 | b << 16); // orr x(a),x(a),x(b),lsl #1
+ o(0xb4000040 | a | !!inv << 24); // cbz/cbnz x(a),.+8
+ --vtop;
+ }
+ else if (bt == VT_FLOAT || bt == VT_DOUBLE) {
+ int a = fltr(gv(RC_FLOAT));
+ o(0x1e202008 | a << 5 | (bt != VT_FLOAT) << 22); // fcmp
+ o(0x54000040 | !!inv); // b.eq/b.ne .+8
+ }
+ else {
+ int ll = (bt == VT_PTR || bt == VT_LLONG);
+ int a = intr(gv(RC_INT));
+ o(0x34000040 | a | !!inv << 24 | ll << 31); // cbz/cbnz wA,.+8
+ }
+ --vtop;
+ return gjmp(t);
+}
+
+static int arm64_iconst(uint64_t *val, SValue *sv)
+{
+ if ((sv->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+ return 0;
+ if (val) {
+ int t = sv->type.t & (VT_BTYPE | VT_UNSIGNED);
+ // It's crazy how TCC has all these alternatives for storing a value:
+ if (t == (VT_LLONG | VT_UNSIGNED))
+ *val = sv->c.ull;
+ else if (t == VT_LLONG)
+ *val = sv->c.ll;
+ else if (t & VT_UNSIGNED)
+ *val = sv->c.ui;
+ else
+ *val = sv->c.i;
+ }
+ return 1;
+}
+
+static int arm64_gen_opic(int op, uint32_t l, int rev, uint64_t val,
+ uint32_t x, uint32_t a)
+{
+ if (op == '-' && !rev) {
+ val = -val;
+ op = '+';
+ }
+ val = l ? val : (uint32_t)val;
+
+ switch (op) {
+
+ case '+': {
+ int s = l ? val >> 63 : val >> 31;
+ val = s ? -val : val;
+ val = l ? val : (uint32_t)val;
+ if (!(val & ~(uint64_t)0xfff))
+ o(0x11000000 | l << 31 | s << 30 | x | a << 5 | val << 10);
+ else if (!(val & ~(uint64_t)0xfff000))
+ o(0x11400000 | l << 31 | s << 30 | x | a << 5 | val >> 12 << 10);
+ else {
+ arm64_movimm(30, val); // use x30
+ o(0x0b1e0000 | l << 31 | s << 30 | x | a << 5);
+ }
+ return 1;
+ }
+
+ case '-':
+ if (!val)
+ o(0x4b0003e0 | l << 31 | x | a << 16); // neg
+ else if (val == (l ? (uint64_t)-1 : (uint32_t)-1))
+ o(0x2a2003e0 | l << 31 | x | a << 16); // mvn
+ else {
+ arm64_movimm(30, val); // use x30
+ o(0x4b0003c0 | l << 31 | x | a << 16); // sub
+ }
+ return 1;
+
+ case '^':
+ if (val == -1 || (val == 0xffffffff && !l)) {
+ o(0x2a2003e0 | l << 31 | x | a << 16); // mvn
+ return 1;
+ }
+ // fall through
+ case '&':
+ case '|': {
+ int e = arm64_encode_bimm64(l ? val : val | val << 32);
+ if (e < 0)
+ return 0;
+ o((op == '&' ? 0x12000000 :
+ op == '|' ? 0x32000000 : 0x52000000) |
+ l << 31 | x | a << 5 | (uint32_t)e << 10);
+ return 1;
+ }
+
+ case TOK_SAR:
+ case TOK_SHL:
+ case TOK_SHR: {
+ uint32_t n = 32 << l;
+ val = val & (n - 1);
+ if (rev)
+ return 0;
+ if (!val)
+ assert(0);
+ else if (op == TOK_SHL)
+ o(0x53000000 | l << 31 | l << 22 | x | a << 5 |
+ (n - val) << 16 | (n - 1 - val) << 10); // lsl
+ else
+ o(0x13000000 | (op == TOK_SHR) << 30 | l << 31 | l << 22 |
+ x | a << 5 | val << 16 | (n - 1) << 10); // lsr/asr
+ return 1;
+ }
+
+ }
+ return 0;
+}
+
+static void arm64_gen_opil(int op, uint32_t l)
+{
+ uint32_t x, a, b;
+
+ // Special treatment for operations with a constant operand:
+ {
+ uint64_t val;
+ int rev = 1;
+
+ if (arm64_iconst(0, &vtop[0])) {
+ vswap();
+ rev = 0;
+ }
+ if (arm64_iconst(&val, &vtop[-1])) {
+ gv(RC_INT);
+ a = intr(vtop[0].r);
+ --vtop;
+ x = get_reg(RC_INT);
+ ++vtop;
+ if (arm64_gen_opic(op, l, rev, val, intr(x), a)) {
+ vtop[0].r = x;
+ vswap();
+ --vtop;
+ return;
+ }
+ }
+ if (!rev)
+ vswap();
+ }
+
+ gv2(RC_INT, RC_INT);
+ assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST);
+ a = intr(vtop[-1].r);
+ b = intr(vtop[0].r);
+ vtop -= 2;
+ x = get_reg(RC_INT);
+ ++vtop;
+ vtop[0].r = x;
+ x = intr(x);
+
+ switch (op) {
+ case '%':
+ // Use x30 for quotient:
+ o(0x1ac00c00 | l << 31 | 30 | a << 5 | b << 16); // sdiv
+ o(0x1b008000 | l << 31 | x | 30 << 5 | b << 16 | a << 10); // msub
+ break;
+ case '&':
+ o(0x0a000000 | l << 31 | x | a << 5 | b << 16); // and
+ break;
+ case '*':
+ o(0x1b007c00 | l << 31 | x | a << 5 | b << 16); // mul
+ break;
+ case '+':
+ o(0x0b000000 | l << 31 | x | a << 5 | b << 16); // add
+ break;
+ case '-':
+ o(0x4b000000 | l << 31 | x | a << 5 | b << 16); // sub
+ break;
+ case '/':
+ o(0x1ac00c00 | l << 31 | x | a << 5 | b << 16); // sdiv
+ break;
+ case '^':
+ o(0x4a000000 | l << 31 | x | a << 5 | b << 16); // eor
+ break;
+ case '|':
+ o(0x2a000000 | l << 31 | x | a << 5 | b << 16); // orr
+ break;
+ case TOK_EQ:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9f17e0 | x); // cset wA,eq
+ break;
+ case TOK_GE:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9fb7e0 | x); // cset wA,ge
+ break;
+ case TOK_GT:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9fd7e0 | x); // cset wA,gt
+ break;
+ case TOK_LE:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9fc7e0 | x); // cset wA,le
+ break;
+ case TOK_LT:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9fa7e0 | x); // cset wA,lt
+ break;
+ case TOK_NE:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9f07e0 | x); // cset wA,ne
+ break;
+ case TOK_SAR:
+ o(0x1ac02800 | l << 31 | x | a << 5 | b << 16); // asr
+ break;
+ case TOK_SHL:
+ o(0x1ac02000 | l << 31 | x | a << 5 | b << 16); // lsl
+ break;
+ case TOK_SHR:
+ o(0x1ac02400 | l << 31 | x | a << 5 | b << 16); // lsr
+ break;
+ case TOK_UDIV:
+ case TOK_PDIV:
+ o(0x1ac00800 | l << 31 | x | a << 5 | b << 16); // udiv
+ break;
+ case TOK_UGE:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9f37e0 | x); // cset wA,cs
+ break;
+ case TOK_UGT:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9f97e0 | x); // cset wA,hi
+ break;
+ case TOK_ULT:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9f27e0 | x); // cset wA,cc
+ break;
+ case TOK_ULE:
+ o(0x6b00001f | l << 31 | a << 5 | b << 16); // cmp
+ o(0x1a9f87e0 | x); // cset wA,ls
+ break;
+ case TOK_UMOD:
+ // Use x30 for quotient:
+ o(0x1ac00800 | l << 31 | 30 | a << 5 | b << 16); // udiv
+ o(0x1b008000 | l << 31 | x | 30 << 5 | b << 16 | a << 10); // msub
+ break;
+ default:
+ assert(0);
+ }
+}
+
+ST_FUNC void gen_opi(int op)
+{
+ arm64_gen_opil(op, 0);
+}
+
+ST_FUNC void gen_opl(int op)
+{
+ arm64_gen_opil(op, 1);
+}
+
+ST_FUNC void gen_opf(int op)
+{
+ int x, a, b, dbl;
+
+ if (vtop[0].type.t == VT_LDOUBLE) {
+ CType type = vtop[0].type;
+ int func = 0;
+ int cond = -1;
+ switch (op) {
+ case '*': func = TOK___multf3; break;
+ case '+': func = TOK___addtf3; break;
+ case '-': func = TOK___subtf3; break;
+ case '/': func = TOK___divtf3; break;
+ case TOK_EQ: func = TOK___eqtf2; cond = 1; break;
+ case TOK_NE: func = TOK___netf2; cond = 0; break;
+ case TOK_LT: func = TOK___lttf2; cond = 10; break;
+ case TOK_GE: func = TOK___getf2; cond = 11; break;
+ case TOK_LE: func = TOK___letf2; cond = 12; break;
+ case TOK_GT: func = TOK___gttf2; cond = 13; break;
+ default: assert(0); break;
+ }
+ vpush_global_sym(&func_old_type, func);
+ vrott(3);
+ gfunc_call(2);
+ vpushi(0);
+ vtop->r = cond < 0 ? REG_FRET : REG_IRET;
+ if (cond < 0)
+ vtop->type = type;
+ else {
+ o(0x7100001f); // cmp w0,#0
+ o(0x1a9f07e0 | cond << 12); // cset w0,(cond)
+ }
+ return;
+ }
+
+ dbl = vtop[0].type.t != VT_FLOAT;
+ gv2(RC_FLOAT, RC_FLOAT);
+ assert(vtop[-1].r < VT_CONST && vtop[0].r < VT_CONST);
+ a = fltr(vtop[-1].r);
+ b = fltr(vtop[0].r);
+ vtop -= 2;
+ switch (op) {
+ case TOK_EQ: case TOK_NE:
+ case TOK_LT: case TOK_GE: case TOK_LE: case TOK_GT:
+ x = get_reg(RC_INT);
+ ++vtop;
+ vtop[0].r = x;
+ x = intr(x);
+ break;
+ default:
+ x = get_reg(RC_FLOAT);
+ ++vtop;
+ vtop[0].r = x;
+ x = fltr(x);
+ break;
+ }
+
+ switch (op) {
+ case '*':
+ o(0x1e200800 | dbl << 22 | x | a << 5 | b << 16); // fmul
+ break;
+ case '+':
+ o(0x1e202800 | dbl << 22 | x | a << 5 | b << 16); // fadd
+ break;
+ case '-':
+ o(0x1e203800 | dbl << 22 | x | a << 5 | b << 16); // fsub
+ break;
+ case '/':
+ o(0x1e201800 | dbl << 22 | x | a << 5 | b << 16); // fdiv
+ break;
+ case TOK_EQ:
+ o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
+ o(0x1a9f17e0 | x); // cset w(x),eq
+ break;
+ case TOK_GE:
+ o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
+ o(0x1a9fb7e0 | x); // cset w(x),ge
+ break;
+ case TOK_GT:
+ o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
+ o(0x1a9fd7e0 | x); // cset w(x),gt
+ break;
+ case TOK_LE:
+ o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
+ o(0x1a9f87e0 | x); // cset w(x),ls
+ break;
+ case TOK_LT:
+ o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
+ o(0x1a9f57e0 | x); // cset w(x),mi
+ break;
+ case TOK_NE:
+ o(0x1e202000 | dbl << 22 | a << 5 | b << 16); // fcmp
+ o(0x1a9f07e0 | x); // cset w(x),ne
+ break;
+ default:
+ assert(0);
+ }
+}
+
+// Generate sign extension from 32 to 64 bits:
+ST_FUNC void gen_cvt_sxtw(void)
+{
+ int r = intr(gv(RC_INT));
+ o(0x93407c00 | r | r << 5); // sxtw x(r),w(r)
+}
+
+ST_FUNC void gen_cvt_itof(int t)
+{
+ if (t == VT_LDOUBLE) {
+ int f = vtop->type.t;
+ int func = (f & VT_BTYPE) == VT_LLONG ?
+ (f & VT_UNSIGNED ? TOK___floatunditf : TOK___floatditf) :
+ (f & VT_UNSIGNED ? TOK___floatunsitf : TOK___floatsitf);
+ vpush_global_sym(&func_old_type, func);
+ vrott(2);
+ gfunc_call(1);
+ vpushi(0);
+ vtop->type.t = t;
+ vtop->r = REG_FRET;
+ return;
+ }
+ else {
+ int d, n = intr(gv(RC_INT));
+ int s = !(vtop->type.t & VT_UNSIGNED);
+ int l = ((vtop->type.t & VT_BTYPE) == VT_LLONG);
+ --vtop;
+ d = get_reg(RC_FLOAT);
+ ++vtop;
+ vtop[0].r = d;
+ o(0x1e220000 | !s << 16 | (t != VT_FLOAT) << 22 | fltr(d) |
+ l << 31 | n << 5); // [us]cvtf [sd](d),[wx](n)
+ }
+}
+
+ST_FUNC void gen_cvt_ftoi(int t)
+{
+ if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+ int func = (t & VT_BTYPE) == VT_LLONG ?
+ (t & VT_UNSIGNED ? TOK___fixunstfdi : TOK___fixtfdi) :
+ (t & VT_UNSIGNED ? TOK___fixunstfsi : TOK___fixtfsi);
+ vpush_global_sym(&func_old_type, func);
+ vrott(2);
+ gfunc_call(1);
+ vpushi(0);
+ vtop->type.t = t;
+ vtop->r = REG_IRET;
+ return;
+ }
+ else {
+ int d, n = fltr(gv(RC_FLOAT));
+ int l = ((vtop->type.t & VT_BTYPE) != VT_FLOAT);
+ --vtop;
+ d = get_reg(RC_INT);
+ ++vtop;
+ vtop[0].r = d;
+ o(0x1e380000 |
+ !!(t & VT_UNSIGNED) << 16 |
+ ((t & VT_BTYPE) == VT_LLONG) << 31 | intr(d) |
+ l << 22 | n << 5); // fcvtz[su] [wx](d),[sd](n)
+ }
+}
+
+ST_FUNC void gen_cvt_ftof(int t)
+{
+ int f = vtop[0].type.t;
+ assert(t == VT_FLOAT || t == VT_DOUBLE || t == VT_LDOUBLE);
+ assert(f == VT_FLOAT || f == VT_DOUBLE || f == VT_LDOUBLE);
+ if (t == f)
+ return;
+
+ if (t == VT_LDOUBLE || f == VT_LDOUBLE) {
+ int func = (t == VT_LDOUBLE) ?
+ (f == VT_FLOAT ? TOK___extendsftf2 : TOK___extenddftf2) :
+ (t == VT_FLOAT ? TOK___trunctfsf2 : TOK___trunctfdf2);
+ vpush_global_sym(&func_old_type, func);
+ vrott(2);
+ gfunc_call(1);
+ vpushi(0);
+ vtop->type.t = t;
+ vtop->r = REG_FRET;
+ }
+ else {
+ int x, a;
+ gv(RC_FLOAT);
+ assert(vtop[0].r < VT_CONST);
+ a = fltr(vtop[0].r);
+ --vtop;
+ x = get_reg(RC_FLOAT);
+ ++vtop;
+ vtop[0].r = x;
+ x = fltr(x);
+
+ if (f == VT_FLOAT)
+ o(0x1e22c000 | x | a << 5); // fcvt d(x),s(a)
+ else
+ o(0x1e624000 | x | a << 5); // fcvt s(x),d(a)
+ }
+}
+
+ST_FUNC void ggoto(void)
+{
+ arm64_gen_bl_or_b(1);
+ --vtop;
+}
+
+ST_FUNC void gen_clear_cache(void)
+{
+ uint32_t beg, end, dsz, isz, p, lab1, b1;
+ gv2(RC_INT, RC_INT);
+ vpushi(0);
+ vtop->r = get_reg(RC_INT);
+ vpushi(0);
+ vtop->r = get_reg(RC_INT);
+ vpushi(0);
+ vtop->r = get_reg(RC_INT);
+ beg = intr(vtop[-4].r); // x0
+ end = intr(vtop[-3].r); // x1
+ dsz = intr(vtop[-2].r); // x2
+ isz = intr(vtop[-1].r); // x3
+ p = intr(vtop[0].r); // x4
+ vtop -= 5;
+
+ o(0xd53b0020 | isz); // mrs x(isz),ctr_el0
+ o(0x52800080 | p); // mov w(p),#4
+ o(0x53104c00 | dsz | isz << 5); // ubfx w(dsz),w(isz),#16,#4
+ o(0x1ac02000 | dsz | p << 5 | dsz << 16); // lsl w(dsz),w(p),w(dsz)
+ o(0x12000c00 | isz | isz << 5); // and w(isz),w(isz),#15
+ o(0x1ac02000 | isz | p << 5 | isz << 16); // lsl w(isz),w(p),w(isz)
+ o(0x51000400 | p | dsz << 5); // sub w(p),w(dsz),#1
+ o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p)
+ b1 = ind; o(0x14000000); // b
+ lab1 = ind;
+ o(0xd50b7b20 | p); // dc cvau,x(p)
+ o(0x8b000000 | p | p << 5 | dsz << 16); // add x(p),x(p),x(dsz)
+ *(uint32_t *)(cur_text_section->data + b1) =
+ (0x14000000 | (ind - b1) >> 2);
+ o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end)
+ o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1
+ o(0xd5033b9f); // dsb ish
+ o(0x51000400 | p | isz << 5); // sub w(p),w(isz),#1
+ o(0x8a240004 | p | beg << 5 | p << 16); // bic x(p),x(beg),x(p)
+ b1 = ind; o(0x14000000); // b
+ lab1 = ind;
+ o(0xd50b7520 | p); // ic ivau,x(p)
+ o(0x8b000000 | p | p << 5 | isz << 16); // add x(p),x(p),x(isz)
+ *(uint32_t *)(cur_text_section->data + b1) =
+ (0x14000000 | (ind - b1) >> 2);
+ o(0xeb00001f | p << 5 | end << 16); // cmp x(p),x(end)
+ o(0x54ffffa3 | ((lab1 - ind) << 3 & 0xffffe0)); // b.cc lab1
+ o(0xd5033b9f); // dsb ish
+ o(0xd5033fdf); // isb
+}
+
+ST_FUNC void gen_vla_sp_save(int addr) {
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+ST_FUNC void gen_vla_sp_restore(int addr) {
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+ST_FUNC void gen_vla_alloc(CType *type, int align) {
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* end of A64 code generator */
+/*************************************************************/
+#endif
+/*************************************************************/
diff --git a/src/cil/il-gen.c b/src/cil/il-gen.c
new file mode 100644
index 0000000..a4170b0
--- /dev/null
+++ b/src/cil/il-gen.c
@@ -0,0 +1,655 @@
+/*
+ * CIL code generator for TCC
+ *
+ * Copyright (c) 2002 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* number of available registers */
+#define NB_REGS 3
+
+/* a register can belong to several classes. The classes must be
+ sorted from more general to more precise (see gv2() code which does
+ assumptions on it). */
+#define RC_ST 0x0001 /* any stack entry */
+#define RC_ST0 0x0002 /* top of stack */
+#define RC_ST1 0x0004 /* top - 1 */
+
+#define RC_INT RC_ST
+#define RC_FLOAT RC_ST
+#define RC_IRET RC_ST0 /* function return: integer register */
+#define RC_LRET RC_ST0 /* function return: second integer register */
+#define RC_FRET RC_ST0 /* function return: float register */
+
+/* pretty names for the registers */
+enum {
+ REG_ST0 = 0,
+ REG_ST1,
+ REG_ST2,
+};
+
+const int reg_classes[NB_REGS] = {
+ /* ST0 */ RC_ST | RC_ST0,
+ /* ST1 */ RC_ST | RC_ST1,
+ /* ST2 */ RC_ST,
+};
+
+/* return registers for function */
+#define REG_IRET REG_ST0 /* single word int return register */
+#define REG_LRET REG_ST0 /* second word return register (for long long) */
+#define REG_FRET REG_ST0 /* float return register */
+
+/* defined if function parameters must be evaluated in reverse order */
+/* #define INVERT_FUNC_PARAMS */
+
+/* defined if structures are passed as pointers. Otherwise structures
+ are directly pushed on stack. */
+/* #define FUNC_STRUCT_PARAM_AS_PTR */
+
+/* pointer size, in bytes */
+#define PTR_SIZE 4
+
+/* long double size and alignment, in bytes */
+#define LDOUBLE_SIZE 8
+#define LDOUBLE_ALIGN 8
+
+/* function call context */
+typedef struct GFuncContext {
+ int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */
+} GFuncContext;
+
+/******************************************************/
+/* opcode definitions */
+
+#define IL_OP_PREFIX 0xFE
+
+enum ILOPCodes {
+#define OP(name, str, n) IL_OP_ ## name = n,
+#include "il-opcodes.h"
+#undef OP
+};
+
+char *il_opcodes_str[] = {
+#define OP(name, str, n) [n] = str,
+#include "il-opcodes.h"
+#undef OP
+};
+
+/******************************************************/
+
+/* arguments variable numbers start from there */
+#define ARG_BASE 0x70000000
+
+static FILE *il_outfile;
+
+static void out_byte(int c)
+{
+ *(char *)ind++ = c;
+}
+
+static void out_le32(int c)
+{
+ out_byte(c);
+ out_byte(c >> 8);
+ out_byte(c >> 16);
+ out_byte(c >> 24);
+}
+
+static void init_outfile(void)
+{
+ if (!il_outfile) {
+ il_outfile = stdout;
+ fprintf(il_outfile,
+ ".assembly extern mscorlib\n"
+ "{\n"
+ ".ver 1:0:2411:0\n"
+ "}\n\n");
+ }
+}
+
+static void out_op1(int op)
+{
+ if (op & 0x100)
+ out_byte(IL_OP_PREFIX);
+ out_byte(op & 0xff);
+}
+
+/* output an opcode with prefix */
+static void out_op(int op)
+{
+ out_op1(op);
+ fprintf(il_outfile, " %s\n", il_opcodes_str[op]);
+}
+
+static void out_opb(int op, int c)
+{
+ out_op1(op);
+ out_byte(c);
+ fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c);
+}
+
+static void out_opi(int op, int c)
+{
+ out_op1(op);
+ out_le32(c);
+ fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c);
+}
+
+/* XXX: not complete */
+static void il_type_to_str(char *buf, int buf_size,
+ int t, const char *varstr)
+{
+ int bt;
+ Sym *s, *sa;
+ char buf1[256];
+ const char *tstr;
+
+ t = t & VT_TYPE;
+ bt = t & VT_BTYPE;
+ buf[0] = '\0';
+ if (t & VT_UNSIGNED)
+ pstrcat(buf, buf_size, "unsigned ");
+ switch(bt) {
+ case VT_VOID:
+ tstr = "void";
+ goto add_tstr;
+ case VT_BOOL:
+ tstr = "bool";
+ goto add_tstr;
+ case VT_BYTE:
+ tstr = "int8";
+ goto add_tstr;
+ case VT_SHORT:
+ tstr = "int16";
+ goto add_tstr;
+ case VT_ENUM:
+ case VT_INT:
+ case VT_LONG:
+ tstr = "int32";
+ goto add_tstr;
+ case VT_LLONG:
+ tstr = "int64";
+ goto add_tstr;
+ case VT_FLOAT:
+ tstr = "float32";
+ goto add_tstr;
+ case VT_DOUBLE:
+ case VT_LDOUBLE:
+ tstr = "float64";
+ add_tstr:
+ pstrcat(buf, buf_size, tstr);
+ break;
+ case VT_STRUCT:
+ tcc_error("structures not handled yet");
+ break;
+ case VT_FUNC:
+ s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
+ il_type_to_str(buf, buf_size, s->t, varstr);
+ pstrcat(buf, buf_size, "(");
+ sa = s->next;
+ while (sa != NULL) {
+ il_type_to_str(buf1, sizeof(buf1), sa->t, NULL);
+ pstrcat(buf, buf_size, buf1);
+ sa = sa->next;
+ if (sa)
+ pstrcat(buf, buf_size, ", ");
+ }
+ pstrcat(buf, buf_size, ")");
+ goto no_var;
+ case VT_PTR:
+ s = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
+ pstrcpy(buf1, sizeof(buf1), "*");
+ if (varstr)
+ pstrcat(buf1, sizeof(buf1), varstr);
+ il_type_to_str(buf, buf_size, s->t, buf1);
+ goto no_var;
+ }
+ if (varstr) {
+ pstrcat(buf, buf_size, " ");
+ pstrcat(buf, buf_size, varstr);
+ }
+ no_var: ;
+}
+
+
+/* patch relocation entry with value 'val' */
+void greloc_patch1(Reloc *p, int val)
+{
+}
+
+/* output a symbol and patch all calls to it */
+void gsym_addr(t, a)
+{
+}
+
+/* output jump and return symbol */
+static int out_opj(int op, int c)
+{
+ out_op1(op);
+ out_le32(0);
+ if (c == 0) {
+ c = ind - (int)cur_text_section->data;
+ }
+ fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c);
+ return c;
+}
+
+void gsym(int t)
+{
+ fprintf(il_outfile, "L%d:\n", t);
+}
+
+/* load 'r' from value 'sv' */
+void load(int r, SValue *sv)
+{
+ int v, fc, ft;
+
+ v = sv->r & VT_VALMASK;
+ fc = sv->c.i;
+ ft = sv->t;
+
+ if (sv->r & VT_LVAL) {
+ if (v == VT_LOCAL) {
+ if (fc >= ARG_BASE) {
+ fc -= ARG_BASE;
+ if (fc >= 0 && fc <= 4) {
+ out_op(IL_OP_LDARG_0 + fc);
+ } else if (fc <= 0xff) {
+ out_opb(IL_OP_LDARG_S, fc);
+ } else {
+ out_opi(IL_OP_LDARG, fc);
+ }
+ } else {
+ if (fc >= 0 && fc <= 4) {
+ out_op(IL_OP_LDLOC_0 + fc);
+ } else if (fc <= 0xff) {
+ out_opb(IL_OP_LDLOC_S, fc);
+ } else {
+ out_opi(IL_OP_LDLOC, fc);
+ }
+ }
+ } else if (v == VT_CONST) {
+ /* XXX: handle globals */
+ out_opi(IL_OP_LDSFLD, 0);
+ } else {
+ if ((ft & VT_BTYPE) == VT_FLOAT) {
+ out_op(IL_OP_LDIND_R4);
+ } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ out_op(IL_OP_LDIND_R8);
+ } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ out_op(IL_OP_LDIND_R8);
+ } else if ((ft & VT_TYPE) == VT_BYTE)
+ out_op(IL_OP_LDIND_I1);
+ else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED))
+ out_op(IL_OP_LDIND_U1);
+ else if ((ft & VT_TYPE) == VT_SHORT)
+ out_op(IL_OP_LDIND_I2);
+ else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED))
+ out_op(IL_OP_LDIND_U2);
+ else
+ out_op(IL_OP_LDIND_I4);
+ }
+ } else {
+ if (v == VT_CONST) {
+ /* XXX: handle globals */
+ if (fc >= -1 && fc <= 8) {
+ out_op(IL_OP_LDC_I4_M1 + fc + 1);
+ } else {
+ out_opi(IL_OP_LDC_I4, fc);
+ }
+ } else if (v == VT_LOCAL) {
+ if (fc >= ARG_BASE) {
+ fc -= ARG_BASE;
+ if (fc <= 0xff) {
+ out_opb(IL_OP_LDARGA_S, fc);
+ } else {
+ out_opi(IL_OP_LDARGA, fc);
+ }
+ } else {
+ if (fc <= 0xff) {
+ out_opb(IL_OP_LDLOCA_S, fc);
+ } else {
+ out_opi(IL_OP_LDLOCA, fc);
+ }
+ }
+ } else {
+ /* XXX: do it */
+ }
+ }
+}
+
+/* store register 'r' in lvalue 'v' */
+void store(int r, SValue *sv)
+{
+ int v, fc, ft;
+
+ v = sv->r & VT_VALMASK;
+ fc = sv->c.i;
+ ft = sv->t;
+ if (v == VT_LOCAL) {
+ if (fc >= ARG_BASE) {
+ fc -= ARG_BASE;
+ /* XXX: check IL arg store semantics */
+ if (fc <= 0xff) {
+ out_opb(IL_OP_STARG_S, fc);
+ } else {
+ out_opi(IL_OP_STARG, fc);
+ }
+ } else {
+ if (fc >= 0 && fc <= 4) {
+ out_op(IL_OP_STLOC_0 + fc);
+ } else if (fc <= 0xff) {
+ out_opb(IL_OP_STLOC_S, fc);
+ } else {
+ out_opi(IL_OP_STLOC, fc);
+ }
+ }
+ } else if (v == VT_CONST) {
+ /* XXX: handle globals */
+ out_opi(IL_OP_STSFLD, 0);
+ } else {
+ if ((ft & VT_BTYPE) == VT_FLOAT)
+ out_op(IL_OP_STIND_R4);
+ else if ((ft & VT_BTYPE) == VT_DOUBLE)
+ out_op(IL_OP_STIND_R8);
+ else if ((ft & VT_BTYPE) == VT_LDOUBLE)
+ out_op(IL_OP_STIND_R8);
+ else if ((ft & VT_BTYPE) == VT_BYTE)
+ out_op(IL_OP_STIND_I1);
+ else if ((ft & VT_BTYPE) == VT_SHORT)
+ out_op(IL_OP_STIND_I2);
+ else
+ out_op(IL_OP_STIND_I4);
+ }
+}
+
+/* start function call and return function call context */
+void gfunc_start(GFuncContext *c, int func_call)
+{
+ c->func_call = func_call;
+}
+
+/* push function parameter which is in (vtop->t, vtop->c). Stack entry
+ is then popped. */
+void gfunc_param(GFuncContext *c)
+{
+ if ((vtop->t & VT_BTYPE) == VT_STRUCT) {
+ tcc_error("structures passed as value not handled yet");
+ } else {
+ /* simply push on stack */
+ gv(RC_ST0);
+ }
+ vtop--;
+}
+
+/* generate function call with address in (vtop->t, vtop->c) and free function
+ context. Stack entry is popped */
+void gfunc_call(GFuncContext *c)
+{
+ char buf[1024];
+
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ /* XXX: more info needed from tcc */
+ il_type_to_str(buf, sizeof(buf), vtop->t, "xxx");
+ fprintf(il_outfile, " call %s\n", buf);
+ } else {
+ /* indirect call */
+ gv(RC_INT);
+ il_type_to_str(buf, sizeof(buf), vtop->t, NULL);
+ fprintf(il_outfile, " calli %s\n", buf);
+ }
+ vtop--;
+}
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(int t)
+{
+ int addr, u, func_call;
+ Sym *sym;
+ char buf[1024];
+
+ init_outfile();
+
+ /* XXX: pass function name to gfunc_prolog */
+ il_type_to_str(buf, sizeof(buf), t, funcname);
+ fprintf(il_outfile, ".method static %s il managed\n", buf);
+ fprintf(il_outfile, "{\n");
+ /* XXX: cannot do better now */
+ fprintf(il_outfile, " .maxstack %d\n", NB_REGS);
+ fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n");
+
+ if (!strcmp(funcname, "main"))
+ fprintf(il_outfile, " .entrypoint\n");
+
+ sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT);
+ func_call = sym->r;
+
+ addr = ARG_BASE;
+ /* if the function returns a structure, then add an
+ implicit pointer parameter */
+ func_vt = sym->t;
+ func_var = (sym->c == FUNC_ELLIPSIS);
+ if ((func_vt & VT_BTYPE) == VT_STRUCT) {
+ func_vc = addr;
+ addr++;
+ }
+ /* define parameters */
+ while ((sym = sym->next) != NULL) {
+ u = sym->t;
+ sym_push(sym->v & ~SYM_FIELD, u,
+ VT_LOCAL | lvalue_type(sym->type.t), addr);
+ addr++;
+ }
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+ out_op(IL_OP_RET);
+ fprintf(il_outfile, "}\n\n");
+}
+
+/* generate a jump to a label */
+int gjmp(int t)
+{
+ return out_opj(IL_OP_BR, t);
+}
+
+/* generate a jump to a fixed address */
+void gjmp_addr(int a)
+{
+ /* XXX: handle syms */
+ out_opi(IL_OP_BR, a);
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+int gtst(int inv, int t)
+{
+ int v, *p, c;
+
+ v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP) {
+ c = vtop->c.i ^ inv;
+ switch(c) {
+ case TOK_EQ:
+ c = IL_OP_BEQ;
+ break;
+ case TOK_NE:
+ c = IL_OP_BNE_UN;
+ break;
+ case TOK_LT:
+ c = IL_OP_BLT;
+ break;
+ case TOK_LE:
+ c = IL_OP_BLE;
+ break;
+ case TOK_GT:
+ c = IL_OP_BGT;
+ break;
+ case TOK_GE:
+ c = IL_OP_BGE;
+ break;
+ case TOK_ULT:
+ c = IL_OP_BLT_UN;
+ break;
+ case TOK_ULE:
+ c = IL_OP_BLE_UN;
+ break;
+ case TOK_UGT:
+ c = IL_OP_BGT_UN;
+ break;
+ case TOK_UGE:
+ c = IL_OP_BGE_UN;
+ break;
+ }
+ t = out_opj(c, t);
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ /* && or || optimization */
+ if ((v & 1) == inv) {
+ /* insert vtop->c jump list in t */
+ p = &vtop->c.i;
+ while (*p != 0)
+ p = (int *)*p;
+ *p = t;
+ t = vtop->c.i;
+ } else {
+ t = gjmp(t);
+ gsym(vtop->c.i);
+ }
+ }
+ vtop--;
+ return t;
+}
+
+/* generate an integer binary operation */
+void gen_opi(int op)
+{
+ gv2(RC_ST1, RC_ST0);
+ switch(op) {
+ case '+':
+ out_op(IL_OP_ADD);
+ goto std_op;
+ case '-':
+ out_op(IL_OP_SUB);
+ goto std_op;
+ case '&':
+ out_op(IL_OP_AND);
+ goto std_op;
+ case '^':
+ out_op(IL_OP_XOR);
+ goto std_op;
+ case '|':
+ out_op(IL_OP_OR);
+ goto std_op;
+ case '*':
+ out_op(IL_OP_MUL);
+ goto std_op;
+ case TOK_SHL:
+ out_op(IL_OP_SHL);
+ goto std_op;
+ case TOK_SHR:
+ out_op(IL_OP_SHR_UN);
+ goto std_op;
+ case TOK_SAR:
+ out_op(IL_OP_SHR);
+ goto std_op;
+ case '/':
+ case TOK_PDIV:
+ out_op(IL_OP_DIV);
+ goto std_op;
+ case TOK_UDIV:
+ out_op(IL_OP_DIV_UN);
+ goto std_op;
+ case '%':
+ out_op(IL_OP_REM);
+ goto std_op;
+ case TOK_UMOD:
+ out_op(IL_OP_REM_UN);
+ std_op:
+ vtop--;
+ vtop[0].r = REG_ST0;
+ break;
+ case TOK_EQ:
+ case TOK_NE:
+ case TOK_LT:
+ case TOK_LE:
+ case TOK_GT:
+ case TOK_GE:
+ case TOK_ULT:
+ case TOK_ULE:
+ case TOK_UGT:
+ case TOK_UGE:
+ vtop--;
+ vtop[0].r = VT_CMP;
+ vtop[0].c.i = op;
+ break;
+ }
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ two operands are guaranted to have the same floating point type */
+void gen_opf(int op)
+{
+ /* same as integer */
+ gen_opi(op);
+}
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+ and 'long long' cases. */
+void gen_cvt_itof(int t)
+{
+ gv(RC_ST0);
+ if (t == VT_FLOAT)
+ out_op(IL_OP_CONV_R4);
+ else
+ out_op(IL_OP_CONV_R8);
+}
+
+/* convert fp to int 't' type */
+/* XXX: handle long long case */
+void gen_cvt_ftoi(int t)
+{
+ gv(RC_ST0);
+ switch(t) {
+ case VT_INT | VT_UNSIGNED:
+ out_op(IL_OP_CONV_U4);
+ break;
+ case VT_LLONG:
+ out_op(IL_OP_CONV_I8);
+ break;
+ case VT_LLONG | VT_UNSIGNED:
+ out_op(IL_OP_CONV_U8);
+ break;
+ default:
+ out_op(IL_OP_CONV_I4);
+ break;
+ }
+}
+
+/* convert from one floating point type to another */
+void gen_cvt_ftof(int t)
+{
+ gv(RC_ST0);
+ if (t == VT_FLOAT) {
+ out_op(IL_OP_CONV_R4);
+ } else {
+ out_op(IL_OP_CONV_R8);
+ }
+}
+
+/* end of CIL code generator */
+/*************************************************************/
+
diff --git a/src/cil/il-opcodes.h b/src/cil/il-opcodes.h
new file mode 100644
index 0000000..adf35cf
--- /dev/null
+++ b/src/cil/il-opcodes.h
@@ -0,0 +1,251 @@
+/*
+ * CIL opcode definition
+ *
+ * Copyright (c) 2002 Fabrice Bellard
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+OP(NOP, "nop", 0x00)
+OP(BREAK, "break", 0x01)
+OP(LDARG_0, "ldarg.0", 0x02)
+OP(LDARG_1, "ldarg.1", 0x03)
+OP(LDARG_2, "ldarg.2", 0x04)
+OP(LDARG_3, "ldarg.3", 0x05)
+OP(LDLOC_0, "ldloc.0", 0x06)
+OP(LDLOC_1, "ldloc.1", 0x07)
+OP(LDLOC_2, "ldloc.2", 0x08)
+OP(LDLOC_3, "ldloc.3", 0x09)
+OP(STLOC_0, "stloc.0", 0x0a)
+OP(STLOC_1, "stloc.1", 0x0b)
+OP(STLOC_2, "stloc.2", 0x0c)
+OP(STLOC_3, "stloc.3", 0x0d)
+OP(LDARG_S, "ldarg.s", 0x0e)
+OP(LDARGA_S, "ldarga.s", 0x0f)
+OP(STARG_S, "starg.s", 0x10)
+OP(LDLOC_S, "ldloc.s", 0x11)
+OP(LDLOCA_S, "ldloca.s", 0x12)
+OP(STLOC_S, "stloc.s", 0x13)
+OP(LDNULL, "ldnull", 0x14)
+OP(LDC_I4_M1, "ldc.i4.m1", 0x15)
+OP(LDC_I4_0, "ldc.i4.0", 0x16)
+OP(LDC_I4_1, "ldc.i4.1", 0x17)
+OP(LDC_I4_2, "ldc.i4.2", 0x18)
+OP(LDC_I4_3, "ldc.i4.3", 0x19)
+OP(LDC_I4_4, "ldc.i4.4", 0x1a)
+OP(LDC_I4_5, "ldc.i4.5", 0x1b)
+OP(LDC_I4_6, "ldc.i4.6", 0x1c)
+OP(LDC_I4_7, "ldc.i4.7", 0x1d)
+OP(LDC_I4_8, "ldc.i4.8", 0x1e)
+OP(LDC_I4_S, "ldc.i4.s", 0x1f)
+OP(LDC_I4, "ldc.i4", 0x20)
+OP(LDC_I8, "ldc.i8", 0x21)
+OP(LDC_R4, "ldc.r4", 0x22)
+OP(LDC_R8, "ldc.r8", 0x23)
+OP(LDPTR, "ldptr", 0x24)
+OP(DUP, "dup", 0x25)
+OP(POP, "pop", 0x26)
+OP(JMP, "jmp", 0x27)
+OP(CALL, "call", 0x28)
+OP(CALLI, "calli", 0x29)
+OP(RET, "ret", 0x2a)
+OP(BR_S, "br.s", 0x2b)
+OP(BRFALSE_S, "brfalse.s", 0x2c)
+OP(BRTRUE_S, "brtrue.s", 0x2d)
+OP(BEQ_S, "beq.s", 0x2e)
+OP(BGE_S, "bge.s", 0x2f)
+OP(BGT_S, "bgt.s", 0x30)
+OP(BLE_S, "ble.s", 0x31)
+OP(BLT_S, "blt.s", 0x32)
+OP(BNE_UN_S, "bne.un.s", 0x33)
+OP(BGE_UN_S, "bge.un.s", 0x34)
+OP(BGT_UN_S, "bgt.un.s", 0x35)
+OP(BLE_UN_S, "ble.un.s", 0x36)
+OP(BLT_UN_S, "blt.un.s", 0x37)
+OP(BR, "br", 0x38)
+OP(BRFALSE, "brfalse", 0x39)
+OP(BRTRUE, "brtrue", 0x3a)
+OP(BEQ, "beq", 0x3b)
+OP(BGE, "bge", 0x3c)
+OP(BGT, "bgt", 0x3d)
+OP(BLE, "ble", 0x3e)
+OP(BLT, "blt", 0x3f)
+OP(BNE_UN, "bne.un", 0x40)
+OP(BGE_UN, "bge.un", 0x41)
+OP(BGT_UN, "bgt.un", 0x42)
+OP(BLE_UN, "ble.un", 0x43)
+OP(BLT_UN, "blt.un", 0x44)
+OP(SWITCH, "switch", 0x45)
+OP(LDIND_I1, "ldind.i1", 0x46)
+OP(LDIND_U1, "ldind.u1", 0x47)
+OP(LDIND_I2, "ldind.i2", 0x48)
+OP(LDIND_U2, "ldind.u2", 0x49)
+OP(LDIND_I4, "ldind.i4", 0x4a)
+OP(LDIND_U4, "ldind.u4", 0x4b)
+OP(LDIND_I8, "ldind.i8", 0x4c)
+OP(LDIND_I, "ldind.i", 0x4d)
+OP(LDIND_R4, "ldind.r4", 0x4e)
+OP(LDIND_R8, "ldind.r8", 0x4f)
+OP(LDIND_REF, "ldind.ref", 0x50)
+OP(STIND_REF, "stind.ref", 0x51)
+OP(STIND_I1, "stind.i1", 0x52)
+OP(STIND_I2, "stind.i2", 0x53)
+OP(STIND_I4, "stind.i4", 0x54)
+OP(STIND_I8, "stind.i8", 0x55)
+OP(STIND_R4, "stind.r4", 0x56)
+OP(STIND_R8, "stind.r8", 0x57)
+OP(ADD, "add", 0x58)
+OP(SUB, "sub", 0x59)
+OP(MUL, "mul", 0x5a)
+OP(DIV, "div", 0x5b)
+OP(DIV_UN, "div.un", 0x5c)
+OP(REM, "rem", 0x5d)
+OP(REM_UN, "rem.un", 0x5e)
+OP(AND, "and", 0x5f)
+OP(OR, "or", 0x60)
+OP(XOR, "xor", 0x61)
+OP(SHL, "shl", 0x62)
+OP(SHR, "shr", 0x63)
+OP(SHR_UN, "shr.un", 0x64)
+OP(NEG, "neg", 0x65)
+OP(NOT, "not", 0x66)
+OP(CONV_I1, "conv.i1", 0x67)
+OP(CONV_I2, "conv.i2", 0x68)
+OP(CONV_I4, "conv.i4", 0x69)
+OP(CONV_I8, "conv.i8", 0x6a)
+OP(CONV_R4, "conv.r4", 0x6b)
+OP(CONV_R8, "conv.r8", 0x6c)
+OP(CONV_U4, "conv.u4", 0x6d)
+OP(CONV_U8, "conv.u8", 0x6e)
+OP(CALLVIRT, "callvirt", 0x6f)
+OP(CPOBJ, "cpobj", 0x70)
+OP(LDOBJ, "ldobj", 0x71)
+OP(LDSTR, "ldstr", 0x72)
+OP(NEWOBJ, "newobj", 0x73)
+OP(CASTCLASS, "castclass", 0x74)
+OP(ISINST, "isinst", 0x75)
+OP(CONV_R_UN, "conv.r.un", 0x76)
+OP(ANN_DATA_S, "ann.data.s", 0x77)
+OP(UNBOX, "unbox", 0x79)
+OP(THROW, "throw", 0x7a)
+OP(LDFLD, "ldfld", 0x7b)
+OP(LDFLDA, "ldflda", 0x7c)
+OP(STFLD, "stfld", 0x7d)
+OP(LDSFLD, "ldsfld", 0x7e)
+OP(LDSFLDA, "ldsflda", 0x7f)
+OP(STSFLD, "stsfld", 0x80)
+OP(STOBJ, "stobj", 0x81)
+OP(CONV_OVF_I1_UN, "conv.ovf.i1.un", 0x82)
+OP(CONV_OVF_I2_UN, "conv.ovf.i2.un", 0x83)
+OP(CONV_OVF_I4_UN, "conv.ovf.i4.un", 0x84)
+OP(CONV_OVF_I8_UN, "conv.ovf.i8.un", 0x85)
+OP(CONV_OVF_U1_UN, "conv.ovf.u1.un", 0x86)
+OP(CONV_OVF_U2_UN, "conv.ovf.u2.un", 0x87)
+OP(CONV_OVF_U4_UN, "conv.ovf.u4.un", 0x88)
+OP(CONV_OVF_U8_UN, "conv.ovf.u8.un", 0x89)
+OP(CONV_OVF_I_UN, "conv.ovf.i.un", 0x8a)
+OP(CONV_OVF_U_UN, "conv.ovf.u.un", 0x8b)
+OP(BOX, "box", 0x8c)
+OP(NEWARR, "newarr", 0x8d)
+OP(LDLEN, "ldlen", 0x8e)
+OP(LDELEMA, "ldelema", 0x8f)
+OP(LDELEM_I1, "ldelem.i1", 0x90)
+OP(LDELEM_U1, "ldelem.u1", 0x91)
+OP(LDELEM_I2, "ldelem.i2", 0x92)
+OP(LDELEM_U2, "ldelem.u2", 0x93)
+OP(LDELEM_I4, "ldelem.i4", 0x94)
+OP(LDELEM_U4, "ldelem.u4", 0x95)
+OP(LDELEM_I8, "ldelem.i8", 0x96)
+OP(LDELEM_I, "ldelem.i", 0x97)
+OP(LDELEM_R4, "ldelem.r4", 0x98)
+OP(LDELEM_R8, "ldelem.r8", 0x99)
+OP(LDELEM_REF, "ldelem.ref", 0x9a)
+OP(STELEM_I, "stelem.i", 0x9b)
+OP(STELEM_I1, "stelem.i1", 0x9c)
+OP(STELEM_I2, "stelem.i2", 0x9d)
+OP(STELEM_I4, "stelem.i4", 0x9e)
+OP(STELEM_I8, "stelem.i8", 0x9f)
+OP(STELEM_R4, "stelem.r4", 0xa0)
+OP(STELEM_R8, "stelem.r8", 0xa1)
+OP(STELEM_REF, "stelem.ref", 0xa2)
+OP(CONV_OVF_I1, "conv.ovf.i1", 0xb3)
+OP(CONV_OVF_U1, "conv.ovf.u1", 0xb4)
+OP(CONV_OVF_I2, "conv.ovf.i2", 0xb5)
+OP(CONV_OVF_U2, "conv.ovf.u2", 0xb6)
+OP(CONV_OVF_I4, "conv.ovf.i4", 0xb7)
+OP(CONV_OVF_U4, "conv.ovf.u4", 0xb8)
+OP(CONV_OVF_I8, "conv.ovf.i8", 0xb9)
+OP(CONV_OVF_U8, "conv.ovf.u8", 0xba)
+OP(REFANYVAL, "refanyval", 0xc2)
+OP(CKFINITE, "ckfinite", 0xc3)
+OP(MKREFANY, "mkrefany", 0xc6)
+OP(ANN_CALL, "ann.call", 0xc7)
+OP(ANN_CATCH, "ann.catch", 0xc8)
+OP(ANN_DEAD, "ann.dead", 0xc9)
+OP(ANN_HOISTED, "ann.hoisted", 0xca)
+OP(ANN_HOISTED_CALL, "ann.hoisted.call", 0xcb)
+OP(ANN_LAB, "ann.lab", 0xcc)
+OP(ANN_DEF, "ann.def", 0xcd)
+OP(ANN_REF_S, "ann.ref.s", 0xce)
+OP(ANN_PHI, "ann.phi", 0xcf)
+OP(LDTOKEN, "ldtoken", 0xd0)
+OP(CONV_U2, "conv.u2", 0xd1)
+OP(CONV_U1, "conv.u1", 0xd2)
+OP(CONV_I, "conv.i", 0xd3)
+OP(CONV_OVF_I, "conv.ovf.i", 0xd4)
+OP(CONV_OVF_U, "conv.ovf.u", 0xd5)
+OP(ADD_OVF, "add.ovf", 0xd6)
+OP(ADD_OVF_UN, "add.ovf.un", 0xd7)
+OP(MUL_OVF, "mul.ovf", 0xd8)
+OP(MUL_OVF_UN, "mul.ovf.un", 0xd9)
+OP(SUB_OVF, "sub.ovf", 0xda)
+OP(SUB_OVF_UN, "sub.ovf.un", 0xdb)
+OP(ENDFINALLY, "endfinally", 0xdc)
+OP(LEAVE, "leave", 0xdd)
+OP(LEAVE_S, "leave.s", 0xde)
+OP(STIND_I, "stind.i", 0xdf)
+OP(CONV_U, "conv.u", 0xe0)
+
+/* prefix instructions. we use an opcode >= 256 to ease coding */
+
+OP(ARGLIST, "arglist", 0x100)
+OP(CEQ, "ceq", 0x101)
+OP(CGT, "cgt", 0x102)
+OP(CGT_UN, "cgt.un", 0x103)
+OP(CLT, "clt", 0x104)
+OP(CLT_UN, "clt.un", 0x105)
+OP(LDFTN, "ldftn", 0x106)
+OP(LDVIRTFTN, "ldvirtftn", 0x107)
+OP(JMPI, "jmpi", 0x108)
+OP(LDARG, "ldarg", 0x109)
+OP(LDARGA, "ldarga", 0x10a)
+OP(STARG, "starg", 0x10b)
+OP(LDLOC, "ldloc", 0x10c)
+OP(LDLOCA, "ldloca", 0x10d)
+OP(STLOC, "stloc", 0x10e)
+OP(LOCALLOC, "localloc", 0x10f)
+OP(ENDFILTER, "endfilter", 0x111)
+OP(UNALIGNED, "unaligned", 0x112)
+OP(VOLATILE, "volatile", 0x113)
+OP(TAIL, "tail", 0x114)
+OP(INITOBJ, "initobj", 0x115)
+OP(ANN_LIVE, "ann.live", 0x116)
+OP(CPBLK, "cpblk", 0x117)
+OP(INITBLK, "initblk", 0x118)
+OP(ANN_REF, "ann.ref", 0x119)
+OP(RETHROW, "rethrow", 0x11a)
+OP(SIZEOF, "sizeof", 0x11c)
+OP(REFANYTYPE, "refanytype", 0x11d)
+OP(ANN_DATA, "ann.data", 0x122)
+OP(ANN_ARG, "ann.arg", 0x123)
diff --git a/src/coff.h b/src/coff.h
new file mode 100644
index 0000000..63edfe2
--- /dev/null
+++ b/src/coff.h
@@ -0,0 +1,446 @@
+/**************************************************************************/
+/* COFF.H */
+/* COFF data structures and related definitions used by the linker */
+/**************************************************************************/
+
+/*------------------------------------------------------------------------*/
+/* COFF FILE HEADER */
+/*------------------------------------------------------------------------*/
+struct filehdr {
+ unsigned short f_magic; /* magic number */
+ unsigned short f_nscns; /* number of sections */
+ long f_timdat; /* time & date stamp */
+ long f_symptr; /* file pointer to symtab */
+ long f_nsyms; /* number of symtab entries */
+ unsigned short f_opthdr; /* sizeof(optional hdr) */
+ unsigned short f_flags; /* flags */
+ unsigned short f_TargetID; /* for C6x = 0x0099 */
+ };
+
+/*------------------------------------------------------------------------*/
+/* File header flags */
+/*------------------------------------------------------------------------*/
+#define F_RELFLG 0x01 /* relocation info stripped from file */
+#define F_EXEC 0x02 /* file is executable (no unresolved refs) */
+#define F_LNNO 0x04 /* line nunbers stripped from file */
+#define F_LSYMS 0x08 /* local symbols stripped from file */
+#define F_GSP10 0x10 /* 34010 version */
+#define F_GSP20 0x20 /* 34020 version */
+#define F_SWABD 0x40 /* bytes swabbed (in names) */
+#define F_AR16WR 0x80 /* byte ordering of an AR16WR (PDP-11) */
+#define F_LITTLE 0x100 /* byte ordering of an AR32WR (vax) */
+#define F_BIG 0x200 /* byte ordering of an AR32W (3B, maxi) */
+#define F_PATCH 0x400 /* contains "patch" list in optional header */
+#define F_NODF 0x400
+
+#define F_VERSION (F_GSP10 | F_GSP20)
+#define F_BYTE_ORDER (F_LITTLE | F_BIG)
+#define FILHDR struct filehdr
+
+/* #define FILHSZ sizeof(FILHDR) */
+#define FILHSZ 22 /* above rounds to align on 4 bytes which causes problems */
+
+#define COFF_C67_MAGIC 0x00c2
+
+/*------------------------------------------------------------------------*/
+/* Macros to recognize magic numbers */
+/*------------------------------------------------------------------------*/
+#define ISMAGIC(x) (((unsigned short)(x))==(unsigned short)magic)
+#define ISARCHIVE(x) ((((unsigned short)(x))==(unsigned short)ARTYPE))
+#define BADMAGIC(x) (((unsigned short)(x) & 0x8080) && !ISMAGIC(x))
+
+
+/*------------------------------------------------------------------------*/
+/* OPTIONAL FILE HEADER */
+/*------------------------------------------------------------------------*/
+typedef struct aouthdr {
+ short magic; /* see magic.h */
+ short vstamp; /* version stamp */
+ long tsize; /* text size in bytes, padded to FW bdry*/
+ long dsize; /* initialized data " " */
+ long bsize; /* uninitialized data " " */
+ long entrypt; /* entry pt. */
+ long text_start; /* base of text used for this file */
+ long data_start; /* base of data used for this file */
+} AOUTHDR;
+
+#define AOUTSZ sizeof(AOUTHDR)
+
+/*----------------------------------------------------------------------*/
+/* When a UNIX aout header is to be built in the optional header, */
+/* the following magic numbers can appear in that header: */
+/* */
+/* AOUT1MAGIC : default : readonly sharable text segment */
+/* AOUT2MAGIC: : writable text segment */
+/* PAGEMAGIC : : configured for paging */
+/*----------------------------------------------------------------------*/
+#define AOUT1MAGIC 0410
+#define AOUT2MAGIC 0407
+#define PAGEMAGIC 0413
+
+
+/*------------------------------------------------------------------------*/
+/* COMMON ARCHIVE FILE STRUCTURES */
+/* */
+/* ARCHIVE File Organization: */
+/* _______________________________________________ */
+/* |__________ARCHIVE_MAGIC_STRING_______________| */
+/* |__________ARCHIVE_FILE_MEMBER_1______________| */
+/* | | */
+/* | Archive File Header "ar_hdr" | */
+/* |.............................................| */
+/* | Member Contents | */
+/* | 1. External symbol directory | */
+/* | 2. Text file | */
+/* |_____________________________________________| */
+/* |________ARCHIVE_FILE_MEMBER_2________________| */
+/* | "ar_hdr" | */
+/* |.............................................| */
+/* | Member Contents (.o or text file) | */
+/* |_____________________________________________| */
+/* | . . . | */
+/* | . . . | */
+/* | . . . | */
+/* |_____________________________________________| */
+/* |________ARCHIVE_FILE_MEMBER_n________________| */
+/* | "ar_hdr" | */
+/* |.............................................| */
+/* | Member Contents | */
+/* |_____________________________________________| */
+/* */
+/*------------------------------------------------------------------------*/
+
+#define COFF_ARMAG "!<arch>\n"
+#define SARMAG 8
+#define ARFMAG "`\n"
+
+struct ar_hdr /* archive file member header - printable ascii */
+{
+ char ar_name[16]; /* file member name - `/' terminated */
+ char ar_date[12]; /* file member date - decimal */
+ char ar_uid[6]; /* file member user id - decimal */
+ char ar_gid[6]; /* file member group id - decimal */
+ char ar_mode[8]; /* file member mode - octal */
+ char ar_size[10]; /* file member size - decimal */
+ char ar_fmag[2]; /* ARFMAG - string to end header */
+};
+
+
+/*------------------------------------------------------------------------*/
+/* SECTION HEADER */
+/*------------------------------------------------------------------------*/
+struct scnhdr {
+ char s_name[8]; /* section name */
+ long s_paddr; /* physical address */
+ long s_vaddr; /* virtual address */
+ long s_size; /* section size */
+ long s_scnptr; /* file ptr to raw data for section */
+ long s_relptr; /* file ptr to relocation */
+ long s_lnnoptr; /* file ptr to line numbers */
+ unsigned int s_nreloc; /* number of relocation entries */
+ unsigned int s_nlnno; /* number of line number entries */
+ unsigned int s_flags; /* flags */
+ unsigned short s_reserved; /* reserved byte */
+ unsigned short s_page; /* memory page id */
+ };
+
+#define SCNHDR struct scnhdr
+#define SCNHSZ sizeof(SCNHDR)
+
+/*------------------------------------------------------------------------*/
+/* Define constants for names of "special" sections */
+/*------------------------------------------------------------------------*/
+/* #define _TEXT ".text" */
+#define _DATA ".data"
+#define _BSS ".bss"
+#define _CINIT ".cinit"
+#define _TV ".tv"
+
+/*------------------------------------------------------------------------*/
+/* The low 4 bits of s_flags is used as a section "type" */
+/*------------------------------------------------------------------------*/
+#define STYP_REG 0x00 /* "regular" : allocated, relocated, loaded */
+#define STYP_DSECT 0x01 /* "dummy" : not allocated, relocated, not loaded */
+#define STYP_NOLOAD 0x02 /* "noload" : allocated, relocated, not loaded */
+#define STYP_GROUP 0x04 /* "grouped" : formed of input sections */
+#define STYP_PAD 0x08 /* "padding" : not allocated, not relocated, loaded */
+#define STYP_COPY 0x10 /* "copy" : used for C init tables -
+ not allocated, relocated,
+ loaded; reloc & lineno
+ entries processed normally */
+#define STYP_TEXT 0x20 /* section contains text only */
+#define STYP_DATA 0x40 /* section contains data only */
+#define STYP_BSS 0x80 /* section contains bss only */
+
+#define STYP_ALIGN 0x100 /* align flag passed by old version assemblers */
+#define ALIGN_MASK 0x0F00 /* part of s_flags that is used for align vals */
+#define ALIGNSIZE(x) (1 << ((x & ALIGN_MASK) >> 8))
+
+
+/*------------------------------------------------------------------------*/
+/* RELOCATION ENTRIES */
+/*------------------------------------------------------------------------*/
+struct reloc
+{
+ long r_vaddr; /* (virtual) address of reference */
+ short r_symndx; /* index into symbol table */
+ unsigned short r_disp; /* additional bits for address calculation */
+ unsigned short r_type; /* relocation type */
+};
+
+#define RELOC struct reloc
+#define RELSZ 10 /* sizeof(RELOC) */
+
+/*--------------------------------------------------------------------------*/
+/* define all relocation types */
+/*--------------------------------------------------------------------------*/
+
+#define R_ABS 0 /* absolute address - no relocation */
+#define R_DIR16 01 /* UNUSED */
+#define R_REL16 02 /* UNUSED */
+#define R_DIR24 04 /* UNUSED */
+#define R_REL24 05 /* 24 bits, direct */
+#define R_DIR32 06 /* UNUSED */
+#define R_RELBYTE 017 /* 8 bits, direct */
+#define R_RELWORD 020 /* 16 bits, direct */
+#define R_RELLONG 021 /* 32 bits, direct */
+#define R_PCRBYTE 022 /* 8 bits, PC-relative */
+#define R_PCRWORD 023 /* 16 bits, PC-relative */
+#define R_PCRLONG 024 /* 32 bits, PC-relative */
+#define R_OCRLONG 030 /* GSP: 32 bits, one's complement direct */
+#define R_GSPPCR16 031 /* GSP: 16 bits, PC relative (in words) */
+#define R_GSPOPR32 032 /* GSP: 32 bits, direct big-endian */
+#define R_PARTLS16 040 /* Brahma: 16 bit offset of 24 bit address*/
+#define R_PARTMS8 041 /* Brahma: 8 bit page of 24 bit address */
+#define R_PARTLS7 050 /* DSP: 7 bit offset of 16 bit address */
+#define R_PARTMS9 051 /* DSP: 9 bit page of 16 bit address */
+#define R_REL13 052 /* DSP: 13 bits, direct */
+
+
+/*------------------------------------------------------------------------*/
+/* LINE NUMBER ENTRIES */
+/*------------------------------------------------------------------------*/
+struct lineno
+{
+ union
+ {
+ long l_symndx ; /* sym. table index of function name
+ iff l_lnno == 0 */
+ long l_paddr ; /* (physical) address of line number */
+ } l_addr ;
+ unsigned short l_lnno ; /* line number */
+};
+
+#define LINENO struct lineno
+#define LINESZ 6 /* sizeof(LINENO) */
+
+
+/*------------------------------------------------------------------------*/
+/* STORAGE CLASSES */
+/*------------------------------------------------------------------------*/
+#define C_EFCN -1 /* physical end of function */
+#define C_NULL 0
+#define C_AUTO 1 /* automatic variable */
+#define C_EXT 2 /* external symbol */
+#define C_STAT 3 /* static */
+#define C_REG 4 /* register variable */
+#define C_EXTDEF 5 /* external definition */
+#define C_LABEL 6 /* label */
+#define C_ULABEL 7 /* undefined label */
+#define C_MOS 8 /* member of structure */
+#define C_ARG 9 /* function argument */
+#define C_STRTAG 10 /* structure tag */
+#define C_MOU 11 /* member of union */
+#define C_UNTAG 12 /* union tag */
+#define C_TPDEF 13 /* type definition */
+#define C_USTATIC 14 /* undefined static */
+#define C_ENTAG 15 /* enumeration tag */
+#define C_MOE 16 /* member of enumeration */
+#define C_REGPARM 17 /* register parameter */
+#define C_FIELD 18 /* bit field */
+
+#define C_BLOCK 100 /* ".bb" or ".eb" */
+#define C_FCN 101 /* ".bf" or ".ef" */
+#define C_EOS 102 /* end of structure */
+#define C_FILE 103 /* file name */
+#define C_LINE 104 /* dummy sclass for line number entry */
+#define C_ALIAS 105 /* duplicate tag */
+#define C_HIDDEN 106 /* special storage class for external */
+ /* symbols in dmert public libraries */
+
+/*------------------------------------------------------------------------*/
+/* SYMBOL TABLE ENTRIES */
+/*------------------------------------------------------------------------*/
+
+#define SYMNMLEN 8 /* Number of characters in a symbol name */
+#define FILNMLEN 14 /* Number of characters in a file name */
+#define DIMNUM 4 /* Number of array dimensions in auxiliary entry */
+
+
+struct syment
+{
+ union
+ {
+ char _n_name[SYMNMLEN]; /* old COFF version */
+ struct
+ {
+ long _n_zeroes; /* new == 0 */
+ long _n_offset; /* offset into string table */
+ } _n_n;
+ char *_n_nptr[2]; /* allows for overlaying */
+ } _n;
+ long n_value; /* value of symbol */
+ short n_scnum; /* section number */
+ unsigned short n_type; /* type and derived type */
+ char n_sclass; /* storage class */
+ char n_numaux; /* number of aux. entries */
+};
+
+#define n_name _n._n_name
+#define n_nptr _n._n_nptr[1]
+#define n_zeroes _n._n_n._n_zeroes
+#define n_offset _n._n_n._n_offset
+
+/*------------------------------------------------------------------------*/
+/* Relocatable symbols have a section number of the */
+/* section in which they are defined. Otherwise, section */
+/* numbers have the following meanings: */
+/*------------------------------------------------------------------------*/
+#define N_UNDEF 0 /* undefined symbol */
+#define N_ABS -1 /* value of symbol is absolute */
+#define N_DEBUG -2 /* special debugging symbol */
+#define N_TV (unsigned short)-3 /* needs transfer vector (preload) */
+#define P_TV (unsigned short)-4 /* needs transfer vector (postload) */
+
+
+/*------------------------------------------------------------------------*/
+/* The fundamental type of a symbol packed into the low */
+/* 4 bits of the word. */
+/*------------------------------------------------------------------------*/
+#define _EF ".ef"
+
+#define T_NULL 0 /* no type info */
+#define T_ARG 1 /* function argument (only used by compiler) */
+#define T_CHAR 2 /* character */
+#define T_SHORT 3 /* short integer */
+#define T_INT 4 /* integer */
+#define T_LONG 5 /* long integer */
+#define T_FLOAT 6 /* floating point */
+#define T_DOUBLE 7 /* double word */
+#define T_STRUCT 8 /* structure */
+#define T_UNION 9 /* union */
+#define T_ENUM 10 /* enumeration */
+#define T_MOE 11 /* member of enumeration */
+#define T_UCHAR 12 /* unsigned character */
+#define T_USHORT 13 /* unsigned short */
+#define T_UINT 14 /* unsigned integer */
+#define T_ULONG 15 /* unsigned long */
+
+/*------------------------------------------------------------------------*/
+/* derived types are: */
+/*------------------------------------------------------------------------*/
+#define DT_NON 0 /* no derived type */
+#define DT_PTR 1 /* pointer */
+#define DT_FCN 2 /* function */
+#define DT_ARY 3 /* array */
+
+#define MKTYPE(basic, d1,d2,d3,d4,d5,d6) \
+ ((basic) | ((d1) << 4) | ((d2) << 6) | ((d3) << 8) |\
+ ((d4) << 10) | ((d5) << 12) | ((d6) << 14))
+
+/*------------------------------------------------------------------------*/
+/* type packing constants and macros */
+/*------------------------------------------------------------------------*/
+#define N_BTMASK_COFF 017
+#define N_TMASK_COFF 060
+#define N_TMASK1_COFF 0300
+#define N_TMASK2_COFF 0360
+#define N_BTSHFT_COFF 4
+#define N_TSHIFT_COFF 2
+
+#define BTYPE_COFF(x) ((x) & N_BTMASK_COFF)
+#define ISINT(x) (((x) >= T_CHAR && (x) <= T_LONG) || \
+ ((x) >= T_UCHAR && (x) <= T_ULONG) || (x) == T_ENUM)
+#define ISFLT_COFF(x) ((x) == T_DOUBLE || (x) == T_FLOAT)
+#define ISPTR_COFF(x) (((x) & N_TMASK_COFF) == (DT_PTR << N_BTSHFT_COFF))
+#define ISFCN_COFF(x) (((x) & N_TMASK_COFF) == (DT_FCN << N_BTSHFT_COFF))
+#define ISARY_COFF(x) (((x) & N_TMASK_COFF) == (DT_ARY << N_BTSHFT_COFF))
+#define ISTAG_COFF(x) ((x)==C_STRTAG || (x)==C_UNTAG || (x)==C_ENTAG)
+
+#define INCREF_COFF(x) ((((x)&~N_BTMASK_COFF)<<N_TSHIFT_COFF)|(DT_PTR<<N_BTSHFT_COFF)|(x&N_BTMASK_COFF))
+#define DECREF_COFF(x) ((((x)>>N_TSHIFT_COFF)&~N_BTMASK_COFF)|((x)&N_BTMASK_COFF))
+
+
+/*------------------------------------------------------------------------*/
+/* AUXILIARY SYMBOL ENTRY */
+/*------------------------------------------------------------------------*/
+union auxent
+{
+ struct
+ {
+ long x_tagndx; /* str, un, or enum tag indx */
+ union
+ {
+ struct
+ {
+ unsigned short x_lnno; /* declaration line number */
+ unsigned short x_size; /* str, union, array size */
+ } x_lnsz;
+ long x_fsize; /* size of function */
+ } x_misc;
+ union
+ {
+ struct /* if ISFCN, tag, or .bb */
+ {
+ long x_lnnoptr; /* ptr to fcn line # */
+ long x_endndx; /* entry ndx past block end */
+ } x_fcn;
+ struct /* if ISARY, up to 4 dimen. */
+ {
+ unsigned short x_dimen[DIMNUM];
+ } x_ary;
+ } x_fcnary;
+ unsigned short x_regcount; /* number of registers used by func */
+ } x_sym;
+ struct
+ {
+ char x_fname[FILNMLEN];
+ } x_file;
+ struct
+ {
+ long x_scnlen; /* section length */
+ unsigned short x_nreloc; /* number of relocation entries */
+ unsigned short x_nlinno; /* number of line numbers */
+ } x_scn;
+};
+
+#define SYMENT struct syment
+#define SYMESZ 18 /* sizeof(SYMENT) */
+
+#define AUXENT union auxent
+#define AUXESZ 18 /* sizeof(AUXENT) */
+
+/*------------------------------------------------------------------------*/
+/* NAMES OF "SPECIAL" SYMBOLS */
+/*------------------------------------------------------------------------*/
+#define _STEXT ".text"
+#define _ETEXT "etext"
+#define _SDATA ".data"
+#define _EDATA "edata"
+#define _SBSS ".bss"
+#define _END "end"
+#define _CINITPTR "cinit"
+
+/*--------------------------------------------------------------------------*/
+/* ENTRY POINT SYMBOLS */
+/*--------------------------------------------------------------------------*/
+#define _START "_start"
+#define _MAIN "_main"
+ /* _CSTART "_c_int00" (defined in params.h) */
+
+
+#define _TVORIG "_tvorig"
+#define _TORIGIN "_torigin"
+#define _DORIGIN "_dorigin"
+
+#define _SORIGIN "_sorigin"
diff --git a/src/config.h.in b/src/config.h.in
new file mode 100644
index 0000000..c9e1a18
--- /dev/null
+++ b/src/config.h.in
@@ -0,0 +1,7 @@
+#define CONFIG_TCCDIR "${CONFIG_TCCDIR}"
+#define TCC_VERSION "${TCC_VERSION}"
+
+#cmakedefine CONFIG_WIN32
+#cmakedefine CONFIG_WIN64
+#cmakedefine CONFIG_TCC_BCHECK
+#cmakedefine CONFIG_TCC_ASSERT
diff --git a/src/conftest.c b/src/conftest.c
new file mode 100644
index 0000000..fa07a1b
--- /dev/null
+++ b/src/conftest.c
@@ -0,0 +1,77 @@
+#include <stdio.h>
+
+/* Define architecture */
+#if defined(__i386__)
+# define TRIPLET_ARCH "i386"
+#elif defined(__x86_64__)
+# define TRIPLET_ARCH "x86_64"
+#elif defined(__arm__)
+# define TRIPLET_ARCH "arm"
+#elif defined(__aarch64__)
+# define TRIPLET_ARCH "aarch64"
+#else
+# define TRIPLET_ARCH "unknown"
+#endif
+
+/* Define OS */
+#if defined (__linux__)
+# define TRIPLET_OS "linux"
+#elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+# define TRIPLET_OS "kfreebsd"
+#elif !defined (__GNU__)
+# define TRIPLET_OS "unknown"
+#endif
+
+/* Define calling convention and ABI */
+#if defined (__ARM_EABI__)
+# if defined (__ARM_PCS_VFP)
+# define TRIPLET_ABI "gnueabihf"
+# else
+# define TRIPLET_ABI "gnueabi"
+# endif
+#else
+# define TRIPLET_ABI "gnu"
+#endif
+
+#ifdef __GNU__
+# define TRIPLET TRIPLET_ARCH "-" TRIPLET_ABI
+#else
+# define TRIPLET TRIPLET_ARCH "-" TRIPLET_OS "-" TRIPLET_ABI
+#endif
+
+#if defined(_WIN32)
+int _CRT_glob = 0;
+#endif
+
+int main(int argc, char *argv[])
+{
+ switch(argc == 2 ? argv[1][0] : 0) {
+ case 'b':
+ {
+ volatile unsigned foo = 0x01234567;
+ puts(*(unsigned char*)&foo == 0x67 ? "no" : "yes");
+ break;
+ }
+#ifdef __GNUC__
+ case 'm':
+ printf("%d\n", __GNUC_MINOR__);
+ break;
+ case 'v':
+ printf("%d\n", __GNUC__);
+ break;
+#else
+ case 'm':
+ case 'v':
+ puts("0");
+ break;
+#endif
+ case 't':
+ puts(TRIPLET);
+ break;
+ case -1:
+ /* to test -Wno-unused-result */
+ fread(NULL, 1, 1, NULL);
+ break;
+ }
+ return 0;
+}
diff --git a/src/elf.h b/src/elf.h
new file mode 100644
index 0000000..a40c736
--- /dev/null
+++ b/src/elf.h
@@ -0,0 +1,3228 @@
+/* This file defines standard ELF types, structures, and macros.
+ Copyright (C) 1995-2012 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#ifndef _ELF_H
+#define _ELF_H 1
+
+#ifndef _WIN32
+#include <inttypes.h>
+#else
+#ifndef __int8_t_defined
+#define __int8_t_defined
+typedef signed char int8_t;
+typedef short int int16_t;
+typedef int int32_t;
+typedef long long int int64_t;
+#endif
+
+typedef unsigned char uint8_t;
+typedef unsigned short int uint16_t;
+typedef unsigned int uint32_t;
+typedef unsigned long long int uint64_t;
+#endif
+
+/* Standard ELF types. */
+
+/* Type for a 16-bit quantity. */
+typedef uint16_t Elf32_Half;
+typedef uint16_t Elf64_Half;
+
+/* Types for signed and unsigned 32-bit quantities. */
+typedef uint32_t Elf32_Word;
+typedef int32_t Elf32_Sword;
+typedef uint32_t Elf64_Word;
+typedef int32_t Elf64_Sword;
+
+/* Types for signed and unsigned 64-bit quantities. */
+typedef uint64_t Elf32_Xword;
+typedef int64_t Elf32_Sxword;
+typedef uint64_t Elf64_Xword;
+typedef int64_t Elf64_Sxword;
+
+/* Type of addresses. */
+typedef uint32_t Elf32_Addr;
+typedef uint64_t Elf64_Addr;
+
+/* Type of file offsets. */
+typedef uint32_t Elf32_Off;
+typedef uint64_t Elf64_Off;
+
+/* Type for section indices, which are 16-bit quantities. */
+typedef uint16_t Elf32_Section;
+typedef uint16_t Elf64_Section;
+
+/* Type for version symbol information. */
+typedef Elf32_Half Elf32_Versym;
+typedef Elf64_Half Elf64_Versym;
+
+
+/* The ELF file header. This appears at the start of every ELF file. */
+
+#define EI_NIDENT (16)
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf32_Half e_type; /* Object file type */
+ Elf32_Half e_machine; /* Architecture */
+ Elf32_Word e_version; /* Object file version */
+ Elf32_Addr e_entry; /* Entry point virtual address */
+ Elf32_Off e_phoff; /* Program header table file offset */
+ Elf32_Off e_shoff; /* Section header table file offset */
+ Elf32_Word e_flags; /* Processor-specific flags */
+ Elf32_Half e_ehsize; /* ELF header size in bytes */
+ Elf32_Half e_phentsize; /* Program header table entry size */
+ Elf32_Half e_phnum; /* Program header table entry count */
+ Elf32_Half e_shentsize; /* Section header table entry size */
+ Elf32_Half e_shnum; /* Section header table entry count */
+ Elf32_Half e_shstrndx; /* Section header string table index */
+} Elf32_Ehdr;
+
+typedef struct
+{
+ unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
+ Elf64_Half e_type; /* Object file type */
+ Elf64_Half e_machine; /* Architecture */
+ Elf64_Word e_version; /* Object file version */
+ Elf64_Addr e_entry; /* Entry point virtual address */
+ Elf64_Off e_phoff; /* Program header table file offset */
+ Elf64_Off e_shoff; /* Section header table file offset */
+ Elf64_Word e_flags; /* Processor-specific flags */
+ Elf64_Half e_ehsize; /* ELF header size in bytes */
+ Elf64_Half e_phentsize; /* Program header table entry size */
+ Elf64_Half e_phnum; /* Program header table entry count */
+ Elf64_Half e_shentsize; /* Section header table entry size */
+ Elf64_Half e_shnum; /* Section header table entry count */
+ Elf64_Half e_shstrndx; /* Section header string table index */
+} Elf64_Ehdr;
+
+/* Fields in the e_ident array. The EI_* macros are indices into the
+ array. The macros under each EI_* macro are the values the byte
+ may have. */
+
+#define EI_MAG0 0 /* File identification byte 0 index */
+#define ELFMAG0 0x7f /* Magic number byte 0 */
+
+#define EI_MAG1 1 /* File identification byte 1 index */
+#define ELFMAG1 'E' /* Magic number byte 1 */
+
+#define EI_MAG2 2 /* File identification byte 2 index */
+#define ELFMAG2 'L' /* Magic number byte 2 */
+
+#define EI_MAG3 3 /* File identification byte 3 index */
+#define ELFMAG3 'F' /* Magic number byte 3 */
+
+/* Conglomeration of the identification bytes, for easy testing as a word. */
+#define ELFMAG "\177ELF"
+#define SELFMAG 4
+
+#define EI_CLASS 4 /* File class byte index */
+#define ELFCLASSNONE 0 /* Invalid class */
+#define ELFCLASS32 1 /* 32-bit objects */
+#define ELFCLASS64 2 /* 64-bit objects */
+#define ELFCLASSNUM 3
+
+#define EI_DATA 5 /* Data encoding byte index */
+#define ELFDATANONE 0 /* Invalid data encoding */
+#define ELFDATA2LSB 1 /* 2's complement, little endian */
+#define ELFDATA2MSB 2 /* 2's complement, big endian */
+#define ELFDATANUM 3
+
+#define EI_VERSION 6 /* File version byte index */
+ /* Value must be EV_CURRENT */
+
+#define EI_OSABI 7 /* OS ABI identification */
+#define ELFOSABI_NONE 0 /* UNIX System V ABI */
+#define ELFOSABI_SYSV 0 /* Alias. */
+#define ELFOSABI_HPUX 1 /* HP-UX */
+#define ELFOSABI_NETBSD 2 /* NetBSD. */
+#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */
+#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */
+#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
+#define ELFOSABI_AIX 7 /* IBM AIX. */
+#define ELFOSABI_IRIX 8 /* SGI Irix. */
+#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
+#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
+#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
+#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
+#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */
+#define ELFOSABI_ARM 97 /* ARM */
+#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
+
+#define EI_ABIVERSION 8 /* ABI version */
+
+#define EI_PAD 9 /* Byte index of padding bytes */
+
+/* Legal values for e_type (object file type). */
+
+#define ET_NONE 0 /* No file type */
+#define ET_REL 1 /* Relocatable file */
+#define ET_EXEC 2 /* Executable file */
+#define ET_DYN 3 /* Shared object file */
+#define ET_CORE 4 /* Core file */
+#define ET_NUM 5 /* Number of defined types */
+#define ET_LOOS 0xfe00 /* OS-specific range start */
+#define ET_HIOS 0xfeff /* OS-specific range end */
+#define ET_LOPROC 0xff00 /* Processor-specific range start */
+#define ET_HIPROC 0xffff /* Processor-specific range end */
+
+/* Legal values for e_machine (architecture). */
+
+#define EM_NONE 0 /* No machine */
+#define EM_M32 1 /* AT&T WE 32100 */
+#define EM_SPARC 2 /* SUN SPARC */
+#define EM_386 3 /* Intel 80386 */
+#define EM_68K 4 /* Motorola m68k family */
+#define EM_88K 5 /* Motorola m88k family */
+#define EM_860 7 /* Intel 80860 */
+#define EM_MIPS 8 /* MIPS R3000 big-endian */
+#define EM_S370 9 /* IBM System/370 */
+#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */
+
+#define EM_PARISC 15 /* HPPA */
+#define EM_VPP500 17 /* Fujitsu VPP500 */
+#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */
+#define EM_960 19 /* Intel 80960 */
+#define EM_PPC 20 /* PowerPC */
+#define EM_PPC64 21 /* PowerPC 64-bit */
+#define EM_S390 22 /* IBM S390 */
+
+#define EM_V800 36 /* NEC V800 series */
+#define EM_FR20 37 /* Fujitsu FR20 */
+#define EM_RH32 38 /* TRW RH-32 */
+#define EM_RCE 39 /* Motorola RCE */
+#define EM_ARM 40 /* ARM */
+#define EM_FAKE_ALPHA 41 /* Digital Alpha */
+#define EM_SH 42 /* Hitachi SH */
+#define EM_SPARCV9 43 /* SPARC v9 64-bit */
+#define EM_TRICORE 44 /* Siemens Tricore */
+#define EM_ARC 45 /* Argonaut RISC Core */
+#define EM_H8_300 46 /* Hitachi H8/300 */
+#define EM_H8_300H 47 /* Hitachi H8/300H */
+#define EM_H8S 48 /* Hitachi H8S */
+#define EM_H8_500 49 /* Hitachi H8/500 */
+#define EM_IA_64 50 /* Intel Merced */
+#define EM_MIPS_X 51 /* Stanford MIPS-X */
+#define EM_COLDFIRE 52 /* Motorola Coldfire */
+#define EM_68HC12 53 /* Motorola M68HC12 */
+#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/
+#define EM_PCP 55 /* Siemens PCP */
+#define EM_NCPU 56 /* Sony nCPU embeeded RISC */
+#define EM_NDR1 57 /* Denso NDR1 microprocessor */
+#define EM_STARCORE 58 /* Motorola Start*Core processor */
+#define EM_ME16 59 /* Toyota ME16 processor */
+#define EM_ST100 60 /* STMicroelectronic ST100 processor */
+#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/
+#define EM_X86_64 62 /* AMD x86-64 architecture */
+#define EM_PDSP 63 /* Sony DSP Processor */
+
+#define EM_FX66 66 /* Siemens FX66 microcontroller */
+#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */
+#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */
+#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */
+#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */
+#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */
+#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */
+#define EM_SVX 73 /* Silicon Graphics SVx */
+#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */
+#define EM_VAX 75 /* Digital VAX */
+#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */
+#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */
+#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */
+#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */
+#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */
+#define EM_HUANY 81 /* Harvard University machine-independent object files */
+#define EM_PRISM 82 /* SiTera Prism */
+#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */
+#define EM_FR30 84 /* Fujitsu FR30 */
+#define EM_D10V 85 /* Mitsubishi D10V */
+#define EM_D30V 86 /* Mitsubishi D30V */
+#define EM_V850 87 /* NEC v850 */
+#define EM_M32R 88 /* Mitsubishi M32R */
+#define EM_MN10300 89 /* Matsushita MN10300 */
+#define EM_MN10200 90 /* Matsushita MN10200 */
+#define EM_PJ 91 /* picoJava */
+#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */
+#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */
+#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */
+#define EM_AARCH64 183 /* ARM AARCH64 */
+#define EM_TILEPRO 188 /* Tilera TILEPro */
+#define EM_TILEGX 191 /* Tilera TILE-Gx */
+#define EM_NUM 192
+
+/* If it is necessary to assign new unofficial EM_* values, please
+ pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the
+ chances of collision with official or non-GNU unofficial values. */
+
+#define EM_ALPHA 0x9026
+#define EM_C60 0x9c60
+
+/* Legal values for e_version (version). */
+
+#define EV_NONE 0 /* Invalid ELF version */
+#define EV_CURRENT 1 /* Current version */
+#define EV_NUM 2
+
+/* Section header. */
+
+typedef struct
+{
+ Elf32_Word sh_name; /* Section name (string tbl index) */
+ Elf32_Word sh_type; /* Section type */
+ Elf32_Word sh_flags; /* Section flags */
+ Elf32_Addr sh_addr; /* Section virtual addr at execution */
+ Elf32_Off sh_offset; /* Section file offset */
+ Elf32_Word sh_size; /* Section size in bytes */
+ Elf32_Word sh_link; /* Link to another section */
+ Elf32_Word sh_info; /* Additional section information */
+ Elf32_Word sh_addralign; /* Section alignment */
+ Elf32_Word sh_entsize; /* Entry size if section holds table */
+} Elf32_Shdr;
+
+typedef struct
+{
+ Elf64_Word sh_name; /* Section name (string tbl index) */
+ Elf64_Word sh_type; /* Section type */
+ Elf64_Xword sh_flags; /* Section flags */
+ Elf64_Addr sh_addr; /* Section virtual addr at execution */
+ Elf64_Off sh_offset; /* Section file offset */
+ Elf64_Xword sh_size; /* Section size in bytes */
+ Elf64_Word sh_link; /* Link to another section */
+ Elf64_Word sh_info; /* Additional section information */
+ Elf64_Xword sh_addralign; /* Section alignment */
+ Elf64_Xword sh_entsize; /* Entry size if section holds table */
+} Elf64_Shdr;
+
+/* Special section indices. */
+
+#define SHN_UNDEF 0 /* Undefined section */
+#define SHN_LORESERVE 0xff00 /* Start of reserved indices */
+#define SHN_LOPROC 0xff00 /* Start of processor-specific */
+#define SHN_BEFORE 0xff00 /* Order section before all others
+ (Solaris). */
+#define SHN_AFTER 0xff01 /* Order section after all others
+ (Solaris). */
+#define SHN_HIPROC 0xff1f /* End of processor-specific */
+#define SHN_LOOS 0xff20 /* Start of OS-specific */
+#define SHN_HIOS 0xff3f /* End of OS-specific */
+#define SHN_ABS 0xfff1 /* Associated symbol is absolute */
+#define SHN_COMMON 0xfff2 /* Associated symbol is common */
+#define SHN_XINDEX 0xffff /* Index is in extra table. */
+#define SHN_HIRESERVE 0xffff /* End of reserved indices */
+
+/* Legal values for sh_type (section type). */
+
+#define SHT_NULL 0 /* Section header table entry unused */
+#define SHT_PROGBITS 1 /* Program data */
+#define SHT_SYMTAB 2 /* Symbol table */
+#define SHT_STRTAB 3 /* String table */
+#define SHT_RELA 4 /* Relocation entries with addends */
+#define SHT_HASH 5 /* Symbol hash table */
+#define SHT_DYNAMIC 6 /* Dynamic linking information */
+#define SHT_NOTE 7 /* Notes */
+#define SHT_NOBITS 8 /* Program space with no data (bss) */
+#define SHT_REL 9 /* Relocation entries, no addends */
+#define SHT_SHLIB 10 /* Reserved */
+#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
+#define SHT_INIT_ARRAY 14 /* Array of constructors */
+#define SHT_FINI_ARRAY 15 /* Array of destructors */
+#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */
+#define SHT_GROUP 17 /* Section group */
+#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */
+#define SHT_NUM 19 /* Number of defined types. */
+#define SHT_LOOS 0x60000000 /* Start OS-specific. */
+#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */
+#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */
+#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */
+#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */
+#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */
+#define SHT_SUNW_move 0x6ffffffa
+#define SHT_SUNW_COMDAT 0x6ffffffb
+#define SHT_SUNW_syminfo 0x6ffffffc
+#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */
+#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */
+#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */
+#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */
+#define SHT_HIOS 0x6fffffff /* End OS-specific type */
+#define SHT_LOPROC 0x70000000 /* Start of processor-specific */
+#define SHT_HIPROC 0x7fffffff /* End of processor-specific */
+#define SHT_LOUSER 0x80000000 /* Start of application-specific */
+#define SHT_HIUSER 0x8fffffff /* End of application-specific */
+
+/* Legal values for sh_flags (section flags). */
+
+#define SHF_WRITE (1 << 0) /* Writable */
+#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */
+#define SHF_EXECINSTR (1 << 2) /* Executable */
+#define SHF_MERGE (1 << 4) /* Might be merged */
+#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */
+#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */
+#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */
+#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling
+ required */
+#define SHF_GROUP (1 << 9) /* Section is member of a group. */
+#define SHF_TLS (1 << 10) /* Section hold thread-local data. */
+#define SHF_MASKOS 0x0ff00000 /* OS-specific. */
+#define SHF_MASKPROC 0xf0000000 /* Processor-specific */
+#define SHF_ORDERED (1 << 30) /* Special ordering requirement
+ (Solaris). */
+#define SHF_EXCLUDE (1 << 31) /* Section is excluded unless
+ referenced or allocated (Solaris).*/
+
+/* Section group handling. */
+#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */
+
+/* Symbol table entry. */
+
+typedef struct
+{
+ Elf32_Word st_name; /* Symbol name (string tbl index) */
+ Elf32_Addr st_value; /* Symbol value */
+ Elf32_Word st_size; /* Symbol size */
+ unsigned char st_info; /* Symbol type and binding */
+ unsigned char st_other; /* Symbol visibility */
+ Elf32_Section st_shndx; /* Section index */
+} Elf32_Sym;
+
+typedef struct
+{
+ Elf64_Word st_name; /* Symbol name (string tbl index) */
+ unsigned char st_info; /* Symbol type and binding */
+ unsigned char st_other; /* Symbol visibility */
+ Elf64_Section st_shndx; /* Section index */
+ Elf64_Addr st_value; /* Symbol value */
+ Elf64_Xword st_size; /* Symbol size */
+} Elf64_Sym;
+
+/* The syminfo section if available contains additional information about
+ every dynamic symbol. */
+
+typedef struct
+{
+ Elf32_Half si_boundto; /* Direct bindings, symbol bound to */
+ Elf32_Half si_flags; /* Per symbol flags */
+} Elf32_Syminfo;
+
+typedef struct
+{
+ Elf64_Half si_boundto; /* Direct bindings, symbol bound to */
+ Elf64_Half si_flags; /* Per symbol flags */
+} Elf64_Syminfo;
+
+/* Possible values for si_boundto. */
+#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */
+#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */
+#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */
+
+/* Possible bitmasks for si_flags. */
+#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */
+#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */
+#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */
+#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy
+ loaded */
+/* Syminfo version values. */
+#define SYMINFO_NONE 0
+#define SYMINFO_CURRENT 1
+#define SYMINFO_NUM 2
+
+
+/* How to extract and insert information held in the st_info field. */
+
+#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
+#define ELF32_ST_TYPE(val) ((val) & 0xf)
+#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
+
+/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */
+#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
+#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
+#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
+
+/* Legal values for ST_BIND subfield of st_info (symbol binding). */
+
+#define STB_LOCAL 0 /* Local symbol */
+#define STB_GLOBAL 1 /* Global symbol */
+#define STB_WEAK 2 /* Weak symbol */
+#define STB_NUM 3 /* Number of defined types. */
+#define STB_LOOS 10 /* Start of OS-specific */
+#define STB_GNU_UNIQUE 10 /* Unique symbol. */
+#define STB_HIOS 12 /* End of OS-specific */
+#define STB_LOPROC 13 /* Start of processor-specific */
+#define STB_HIPROC 15 /* End of processor-specific */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_NOTYPE 0 /* Symbol type is unspecified */
+#define STT_OBJECT 1 /* Symbol is a data object */
+#define STT_FUNC 2 /* Symbol is a code object */
+#define STT_SECTION 3 /* Symbol associated with a section */
+#define STT_FILE 4 /* Symbol's name is file name */
+#define STT_COMMON 5 /* Symbol is a common data object */
+#define STT_TLS 6 /* Symbol is thread-local data object*/
+#define STT_NUM 7 /* Number of defined types. */
+#define STT_LOOS 10 /* Start of OS-specific */
+#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */
+#define STT_HIOS 12 /* End of OS-specific */
+#define STT_LOPROC 13 /* Start of processor-specific */
+#define STT_HIPROC 15 /* End of processor-specific */
+
+
+/* Symbol table indices are found in the hash buckets and chain table
+ of a symbol hash table section. This special index value indicates
+ the end of a chain, meaning no further symbols are found in that bucket. */
+
+#define STN_UNDEF 0 /* End of a chain. */
+
+
+/* How to extract and insert information held in the st_other field. */
+
+#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
+
+/* For ELF64 the definitions are the same. */
+#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
+
+/* Symbol visibility specification encoded in the st_other field. */
+#define STV_DEFAULT 0 /* Default symbol visibility rules */
+#define STV_INTERNAL 1 /* Processor specific hidden class */
+#define STV_HIDDEN 2 /* Sym unavailable in other modules */
+#define STV_PROTECTED 3 /* Not preemptible, not exported */
+
+
+/* Relocation table entry without addend (in section of type SHT_REL). */
+
+typedef struct
+{
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+} Elf32_Rel;
+
+/* I have seen two different definitions of the Elf64_Rel and
+ Elf64_Rela structures, so we'll leave them out until Novell (or
+ whoever) gets their act together. */
+/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */
+
+typedef struct
+{
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+} Elf64_Rel;
+
+/* Relocation table entry with addend (in section of type SHT_RELA). */
+
+typedef struct
+{
+ Elf32_Addr r_offset; /* Address */
+ Elf32_Word r_info; /* Relocation type and symbol index */
+ Elf32_Sword r_addend; /* Addend */
+} Elf32_Rela;
+
+typedef struct
+{
+ Elf64_Addr r_offset; /* Address */
+ Elf64_Xword r_info; /* Relocation type and symbol index */
+ Elf64_Sxword r_addend; /* Addend */
+} Elf64_Rela;
+
+/* How to extract and insert information held in the r_info field. */
+
+#define ELF32_R_SYM(val) ((val) >> 8)
+#define ELF32_R_TYPE(val) ((val) & 0xff)
+#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
+
+#define ELF64_R_SYM(i) ((i) >> 32)
+#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
+#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
+
+/* Program segment header. */
+
+typedef struct
+{
+ Elf32_Word p_type; /* Segment type */
+ Elf32_Off p_offset; /* Segment file offset */
+ Elf32_Addr p_vaddr; /* Segment virtual address */
+ Elf32_Addr p_paddr; /* Segment physical address */
+ Elf32_Word p_filesz; /* Segment size in file */
+ Elf32_Word p_memsz; /* Segment size in memory */
+ Elf32_Word p_flags; /* Segment flags */
+ Elf32_Word p_align; /* Segment alignment */
+} Elf32_Phdr;
+
+typedef struct
+{
+ Elf64_Word p_type; /* Segment type */
+ Elf64_Word p_flags; /* Segment flags */
+ Elf64_Off p_offset; /* Segment file offset */
+ Elf64_Addr p_vaddr; /* Segment virtual address */
+ Elf64_Addr p_paddr; /* Segment physical address */
+ Elf64_Xword p_filesz; /* Segment size in file */
+ Elf64_Xword p_memsz; /* Segment size in memory */
+ Elf64_Xword p_align; /* Segment alignment */
+} Elf64_Phdr;
+
+/* Special value for e_phnum. This indicates that the real number of
+ program headers is too large to fit into e_phnum. Instead the real
+ value is in the field sh_info of section 0. */
+
+#define PN_XNUM 0xffff
+
+/* Legal values for p_type (segment type). */
+
+#define PT_NULL 0 /* Program header table entry unused */
+#define PT_LOAD 1 /* Loadable program segment */
+#define PT_DYNAMIC 2 /* Dynamic linking information */
+#define PT_INTERP 3 /* Program interpreter */
+#define PT_NOTE 4 /* Auxiliary information */
+#define PT_SHLIB 5 /* Reserved */
+#define PT_PHDR 6 /* Entry for header table itself */
+#define PT_TLS 7 /* Thread-local storage segment */
+#define PT_NUM 8 /* Number of defined types */
+#define PT_LOOS 0x60000000 /* Start of OS-specific */
+#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */
+#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */
+#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */
+#define PT_LOSUNW 0x6ffffffa
+#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */
+#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */
+#define PT_HISUNW 0x6fffffff
+#define PT_HIOS 0x6fffffff /* End of OS-specific */
+#define PT_LOPROC 0x70000000 /* Start of processor-specific */
+#define PT_HIPROC 0x7fffffff /* End of processor-specific */
+
+/* Legal values for p_flags (segment flags). */
+
+#define PF_X (1 << 0) /* Segment is executable */
+#define PF_W (1 << 1) /* Segment is writable */
+#define PF_R (1 << 2) /* Segment is readable */
+#define PF_MASKOS 0x0ff00000 /* OS-specific */
+#define PF_MASKPROC 0xf0000000 /* Processor-specific */
+
+/* Legal values for note segment descriptor types for core files. */
+
+#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */
+#define NT_FPREGSET 2 /* Contains copy of fpregset struct */
+#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */
+#define NT_PRXREG 4 /* Contains copy of prxregset struct */
+#define NT_TASKSTRUCT 4 /* Contains copy of task structure */
+#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */
+#define NT_AUXV 6 /* Contains copy of auxv array */
+#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */
+#define NT_ASRS 8 /* Contains copy of asrset struct */
+#define NT_PSTATUS 10 /* Contains copy of pstatus struct */
+#define NT_PSINFO 13 /* Contains copy of psinfo struct */
+#define NT_PRCRED 14 /* Contains copy of prcred struct */
+#define NT_UTSNAME 15 /* Contains copy of utsname struct */
+#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */
+#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */
+#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */
+#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */
+#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
+#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
+#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
+#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */
+#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */
+#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */
+#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */
+#define NT_S390_TIMER 0x301 /* s390 timer register */
+#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
+#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */
+#define NT_S390_CTRS 0x304 /* s390 control registers */
+#define NT_S390_PREFIX 0x305 /* s390 prefix register */
+#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
+#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */
+#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */
+#define NT_ARM_TLS 0x401 /* ARM TLS register */
+#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */
+#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */
+
+/* Legal values for the note segment descriptor types for object files. */
+
+#define NT_VERSION 1 /* Contains a version string. */
+
+
+/* Dynamic section entry. */
+
+typedef struct
+{
+ Elf32_Sword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf32_Word d_val; /* Integer value */
+ Elf32_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf32_Dyn;
+
+typedef struct
+{
+ Elf64_Sxword d_tag; /* Dynamic entry type */
+ union
+ {
+ Elf64_Xword d_val; /* Integer value */
+ Elf64_Addr d_ptr; /* Address value */
+ } d_un;
+} Elf64_Dyn;
+
+/* Legal values for d_tag (dynamic entry type). */
+
+#define DT_NULL 0 /* Marks end of dynamic section */
+#define DT_NEEDED 1 /* Name of needed library */
+#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */
+#define DT_PLTGOT 3 /* Processor defined value */
+#define DT_HASH 4 /* Address of symbol hash table */
+#define DT_STRTAB 5 /* Address of string table */
+#define DT_SYMTAB 6 /* Address of symbol table */
+#define DT_RELA 7 /* Address of Rela relocs */
+#define DT_RELASZ 8 /* Total size of Rela relocs */
+#define DT_RELAENT 9 /* Size of one Rela reloc */
+#define DT_STRSZ 10 /* Size of string table */
+#define DT_SYMENT 11 /* Size of one symbol table entry */
+#define DT_INIT 12 /* Address of init function */
+#define DT_FINI 13 /* Address of termination function */
+#define DT_SONAME 14 /* Name of shared object */
+#define DT_RPATH 15 /* Library search path (deprecated) */
+#define DT_SYMBOLIC 16 /* Start symbol search here */
+#define DT_REL 17 /* Address of Rel relocs */
+#define DT_RELSZ 18 /* Total size of Rel relocs */
+#define DT_RELENT 19 /* Size of one Rel reloc */
+#define DT_PLTREL 20 /* Type of reloc in PLT */
+#define DT_DEBUG 21 /* For debugging; unspecified */
+#define DT_TEXTREL 22 /* Reloc might modify .text */
+#define DT_JMPREL 23 /* Address of PLT relocs */
+#define DT_BIND_NOW 24 /* Process relocations of object */
+#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */
+#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
+#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */
+#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */
+#define DT_RUNPATH 29 /* Library search path */
+#define DT_FLAGS 30 /* Flags for the object being loaded */
+#define DT_ENCODING 32 /* Start of encoded range */
+#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/
+#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */
+#define DT_NUM 34 /* Number used */
+#define DT_LOOS 0x6000000d /* Start of OS-specific */
+#define DT_HIOS 0x6ffff000 /* End of OS-specific */
+#define DT_LOPROC 0x70000000 /* Start of processor-specific */
+#define DT_HIPROC 0x7fffffff /* End of processor-specific */
+#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */
+
+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
+ Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's
+ approach. */
+#define DT_VALRNGLO 0x6ffffd00
+#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */
+#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */
+#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */
+#define DT_CHECKSUM 0x6ffffdf8
+#define DT_PLTPADSZ 0x6ffffdf9
+#define DT_MOVEENT 0x6ffffdfa
+#define DT_MOVESZ 0x6ffffdfb
+#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */
+#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting
+ the following DT_* entry. */
+#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */
+#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */
+#define DT_VALRNGHI 0x6ffffdff
+#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */
+#define DT_VALNUM 12
+
+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+ Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+
+ If any adjustment is made to the ELF object after it has been
+ built these entries will need to be adjusted. */
+#define DT_ADDRRNGLO 0x6ffffe00
+#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */
+#define DT_TLSDESC_PLT 0x6ffffef6
+#define DT_TLSDESC_GOT 0x6ffffef7
+#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */
+#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */
+#define DT_CONFIG 0x6ffffefa /* Configuration information. */
+#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */
+#define DT_AUDIT 0x6ffffefc /* Object auditing. */
+#define DT_PLTPAD 0x6ffffefd /* PLT padding. */
+#define DT_MOVETAB 0x6ffffefe /* Move table. */
+#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */
+#define DT_ADDRRNGHI 0x6ffffeff
+#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */
+#define DT_ADDRNUM 11
+
+/* The versioning entry types. The next are defined as part of the
+ GNU extension. */
+#define DT_VERSYM 0x6ffffff0
+
+#define DT_RELACOUNT 0x6ffffff9
+#define DT_RELCOUNT 0x6ffffffa
+
+/* These were chosen by Sun. */
+#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */
+#define DT_VERDEF 0x6ffffffc /* Address of version definition
+ table */
+#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */
+#define DT_VERNEED 0x6ffffffe /* Address of table with needed
+ versions */
+#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */
+#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */
+#define DT_VERSIONTAGNUM 16
+
+/* Sun added these machine-independent extensions in the "processor-specific"
+ range. Be compatible. */
+#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */
+#define DT_FILTER 0x7fffffff /* Shared object to get values from */
+#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
+#define DT_EXTRANUM 3
+
+/* Values of `d_un.d_val' in the DT_FLAGS entry. */
+#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */
+#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */
+#define DF_TEXTREL 0x00000004 /* Object contains text relocations */
+#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */
+#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */
+
+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
+ entry in the dynamic section. */
+#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */
+#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */
+#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */
+#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/
+#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/
+#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/
+#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */
+#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */
+#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */
+#define DF_1_TRANS 0x00000200
+#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */
+#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */
+#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */
+#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/
+#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */
+#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */
+#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */
+#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */
+#define DF_1_IGNMULDEF 0x00040000
+#define DF_1_NOKSYMS 0x00080000
+#define DF_1_NOHDR 0x00100000
+#define DF_1_EDITED 0x00200000 /* Object is modified after built. */
+#define DF_1_NORELOC 0x00400000
+#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */
+#define DF_1_GLOBAUDIT 0x01000000 /* Global auditin required. */
+#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */
+
+/* Flags for the feature selection in DT_FEATURE_1. */
+#define DTF_1_PARINIT 0x00000001
+#define DTF_1_CONFEXP 0x00000002
+
+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */
+#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */
+#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not
+ generally available. */
+
+/* Version definition sections. */
+
+typedef struct
+{
+ Elf32_Half vd_version; /* Version revision */
+ Elf32_Half vd_flags; /* Version information */
+ Elf32_Half vd_ndx; /* Version Index */
+ Elf32_Half vd_cnt; /* Number of associated aux entries */
+ Elf32_Word vd_hash; /* Version name hash value */
+ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf32_Word vd_next; /* Offset in bytes to next verdef
+ entry */
+} Elf32_Verdef;
+
+typedef struct
+{
+ Elf64_Half vd_version; /* Version revision */
+ Elf64_Half vd_flags; /* Version information */
+ Elf64_Half vd_ndx; /* Version Index */
+ Elf64_Half vd_cnt; /* Number of associated aux entries */
+ Elf64_Word vd_hash; /* Version name hash value */
+ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */
+ Elf64_Word vd_next; /* Offset in bytes to next verdef
+ entry */
+} Elf64_Verdef;
+
+
+/* Legal values for vd_version (version revision). */
+#define VER_DEF_NONE 0 /* No version */
+#define VER_DEF_CURRENT 1 /* Current version */
+#define VER_DEF_NUM 2 /* Given version number */
+
+/* Legal values for vd_flags (version information flags). */
+#define VER_FLG_BASE 0x1 /* Version definition of file itself */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+
+/* Versym symbol index values. */
+#define VER_NDX_LOCAL 0 /* Symbol is local. */
+#define VER_NDX_GLOBAL 1 /* Symbol is global. */
+#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */
+#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */
+
+/* Auxialiary version information. */
+
+typedef struct
+{
+ Elf32_Word vda_name; /* Version or dependency names */
+ Elf32_Word vda_next; /* Offset in bytes to next verdaux
+ entry */
+} Elf32_Verdaux;
+
+typedef struct
+{
+ Elf64_Word vda_name; /* Version or dependency names */
+ Elf64_Word vda_next; /* Offset in bytes to next verdaux
+ entry */
+} Elf64_Verdaux;
+
+
+/* Version dependency section. */
+
+typedef struct
+{
+ Elf32_Half vn_version; /* Version of structure */
+ Elf32_Half vn_cnt; /* Number of associated aux entries */
+ Elf32_Word vn_file; /* Offset of filename for this
+ dependency */
+ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */
+ Elf32_Word vn_next; /* Offset in bytes to next verneed
+ entry */
+} Elf32_Verneed;
+
+typedef struct
+{
+ Elf64_Half vn_version; /* Version of structure */
+ Elf64_Half vn_cnt; /* Number of associated aux entries */
+ Elf64_Word vn_file; /* Offset of filename for this
+ dependency */
+ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */
+ Elf64_Word vn_next; /* Offset in bytes to next verneed
+ entry */
+} Elf64_Verneed;
+
+
+/* Legal values for vn_version (version revision). */
+#define VER_NEED_NONE 0 /* No version */
+#define VER_NEED_CURRENT 1 /* Current version */
+#define VER_NEED_NUM 2 /* Given version number */
+
+/* Auxiliary needed version information. */
+
+typedef struct
+{
+ Elf32_Word vna_hash; /* Hash value of dependency name */
+ Elf32_Half vna_flags; /* Dependency specific information */
+ Elf32_Half vna_other; /* Unused */
+ Elf32_Word vna_name; /* Dependency name string offset */
+ Elf32_Word vna_next; /* Offset in bytes to next vernaux
+ entry */
+} Elf32_Vernaux;
+
+typedef struct
+{
+ Elf64_Word vna_hash; /* Hash value of dependency name */
+ Elf64_Half vna_flags; /* Dependency specific information */
+ Elf64_Half vna_other; /* Unused */
+ Elf64_Word vna_name; /* Dependency name string offset */
+ Elf64_Word vna_next; /* Offset in bytes to next vernaux
+ entry */
+} Elf64_Vernaux;
+
+
+/* Legal values for vna_flags. */
+#define VER_FLG_WEAK 0x2 /* Weak version identifier */
+
+
+/* Auxiliary vector. */
+
+/* This vector is normally only used by the program interpreter. The
+ usual definition in an ABI supplement uses the name auxv_t. The
+ vector is not usually defined in a standard <elf.h> file, but it
+ can't hurt. We rename it to avoid conflicts. The sizes of these
+ types are an arrangement between the exec server and the program
+ interpreter, so we don't fully specify them here. */
+
+typedef struct
+{
+ uint32_t a_type; /* Entry type */
+ union
+ {
+ uint32_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf32_auxv_t;
+
+typedef struct
+{
+ uint64_t a_type; /* Entry type */
+ union
+ {
+ uint64_t a_val; /* Integer value */
+ /* We use to have pointer elements added here. We cannot do that,
+ though, since it does not work when using 32-bit definitions
+ on 64-bit platforms and vice versa. */
+ } a_un;
+} Elf64_auxv_t;
+
+/* Legal values for a_type (entry type). */
+
+#define AT_NULL 0 /* End of vector */
+#define AT_IGNORE 1 /* Entry should be ignored */
+#define AT_EXECFD 2 /* File descriptor of program */
+#define AT_PHDR 3 /* Program headers for program */
+#define AT_PHENT 4 /* Size of program header entry */
+#define AT_PHNUM 5 /* Number of program headers */
+#define AT_PAGESZ 6 /* System page size */
+#define AT_BASE 7 /* Base address of interpreter */
+#define AT_FLAGS 8 /* Flags */
+#define AT_ENTRY 9 /* Entry point of program */
+#define AT_NOTELF 10 /* Program is not ELF */
+#define AT_UID 11 /* Real uid */
+#define AT_EUID 12 /* Effective uid */
+#define AT_GID 13 /* Real gid */
+#define AT_EGID 14 /* Effective gid */
+#define AT_CLKTCK 17 /* Frequency of times() */
+
+/* Some more special a_type values describing the hardware. */
+#define AT_PLATFORM 15 /* String identifying platform. */
+#define AT_HWCAP 16 /* Machine dependent hints about
+ processor capabilities. */
+
+/* This entry gives some information about the FPU initialization
+ performed by the kernel. */
+#define AT_FPUCW 18 /* Used FPU control word. */
+
+/* Cache block sizes. */
+#define AT_DCACHEBSIZE 19 /* Data cache block size. */
+#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */
+#define AT_UCACHEBSIZE 21 /* Unified cache block size. */
+
+/* A special ignored value for PPC, used by the kernel to control the
+ interpretation of the AUXV. Must be > 16. */
+#define AT_IGNOREPPC 22 /* Entry should be ignored. */
+
+#define AT_SECURE 23 /* Boolean, was exec setuid-like? */
+
+#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/
+
+#define AT_RANDOM 25 /* Address of 16 random bytes. */
+
+#define AT_EXECFN 31 /* Filename of executable. */
+
+/* Pointer to the global system page used for system calls and other
+ nice things. */
+#define AT_SYSINFO 32
+#define AT_SYSINFO_EHDR 33
+
+/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains
+ log2 of line size; mask those to get cache size. */
+#define AT_L1I_CACHESHAPE 34
+#define AT_L1D_CACHESHAPE 35
+#define AT_L2_CACHESHAPE 36
+#define AT_L3_CACHESHAPE 37
+
+/* Note section contents. Each entry in the note section begins with
+ a header of a fixed form. */
+
+typedef struct
+{
+ Elf32_Word n_namesz; /* Length of the note's name. */
+ Elf32_Word n_descsz; /* Length of the note's descriptor. */
+ Elf32_Word n_type; /* Type of the note. */
+} Elf32_Nhdr;
+
+typedef struct
+{
+ Elf64_Word n_namesz; /* Length of the note's name. */
+ Elf64_Word n_descsz; /* Length of the note's descriptor. */
+ Elf64_Word n_type; /* Type of the note. */
+} Elf64_Nhdr;
+
+/* Known names of notes. */
+
+/* Solaris entries in the note section have this name. */
+#define ELF_NOTE_SOLARIS "SUNW Solaris"
+
+/* Note entries for GNU systems have this name. */
+#define ELF_NOTE_GNU "GNU"
+
+
+/* Defined types of notes for Solaris. */
+
+/* Value of descriptor (one word) is desired pagesize for the binary. */
+#define ELF_NOTE_PAGESIZE_HINT 1
+
+
+/* Defined note types for GNU systems. */
+
+/* ABI information. The descriptor consists of words:
+ word 0: OS descriptor
+ word 1: major version of the ABI
+ word 2: minor version of the ABI
+ word 3: subminor version of the ABI
+*/
+#define NT_GNU_ABI_TAG 1
+#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */
+
+/* Known OSes. These values can appear in word 0 of an
+ NT_GNU_ABI_TAG note section entry. */
+#define ELF_NOTE_OS_LINUX 0
+#define ELF_NOTE_OS_GNU 1
+#define ELF_NOTE_OS_SOLARIS2 2
+#define ELF_NOTE_OS_FREEBSD 3
+
+/* Synthetic hwcap information. The descriptor begins with two words:
+ word 0: number of entries
+ word 1: bitmask of enabled entries
+ Then follow variable-length entries, one byte followed by a
+ '\0'-terminated hwcap name string. The byte gives the bit
+ number to test if enabled, (1U << bit) & bitmask. */
+#define NT_GNU_HWCAP 2
+
+/* Build ID bits as generated by ld --build-id.
+ The descriptor consists of any nonzero number of bytes. */
+#define NT_GNU_BUILD_ID 3
+
+/* Version note generated by GNU gold containing a version string. */
+#define NT_GNU_GOLD_VERSION 4
+
+
+/* Move records. */
+typedef struct
+{
+ Elf32_Xword m_value; /* Symbol value. */
+ Elf32_Word m_info; /* Size and index. */
+ Elf32_Word m_poffset; /* Symbol offset. */
+ Elf32_Half m_repeat; /* Repeat count. */
+ Elf32_Half m_stride; /* Stride info. */
+} Elf32_Move;
+
+typedef struct
+{
+ Elf64_Xword m_value; /* Symbol value. */
+ Elf64_Xword m_info; /* Size and index. */
+ Elf64_Xword m_poffset; /* Symbol offset. */
+ Elf64_Half m_repeat; /* Repeat count. */
+ Elf64_Half m_stride; /* Stride info. */
+} Elf64_Move;
+
+/* Macro to construct move records. */
+#define ELF32_M_SYM(info) ((info) >> 8)
+#define ELF32_M_SIZE(info) ((unsigned char) (info))
+#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
+
+#define ELF64_M_SYM(info) ELF32_M_SYM (info)
+#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
+#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
+
+
+/* Motorola 68k specific definitions. */
+
+/* Values for Elf32_Ehdr.e_flags. */
+#define EF_CPU32 0x00810000
+
+/* m68k relocs. */
+
+#define R_68K_NONE 0 /* No reloc */
+#define R_68K_32 1 /* Direct 32 bit */
+#define R_68K_16 2 /* Direct 16 bit */
+#define R_68K_8 3 /* Direct 8 bit */
+#define R_68K_PC32 4 /* PC relative 32 bit */
+#define R_68K_PC16 5 /* PC relative 16 bit */
+#define R_68K_PC8 6 /* PC relative 8 bit */
+#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */
+#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */
+#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */
+#define R_68K_GOT32O 10 /* 32 bit GOT offset */
+#define R_68K_GOT16O 11 /* 16 bit GOT offset */
+#define R_68K_GOT8O 12 /* 8 bit GOT offset */
+#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */
+#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */
+#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */
+#define R_68K_PLT32O 16 /* 32 bit PLT offset */
+#define R_68K_PLT16O 17 /* 16 bit PLT offset */
+#define R_68K_PLT8O 18 /* 8 bit PLT offset */
+#define R_68K_COPY 19 /* Copy symbol at runtime */
+#define R_68K_GLOB_DAT 20 /* Create GOT entry */
+#define R_68K_JMP_SLOT 21 /* Create PLT entry */
+#define R_68K_RELATIVE 22 /* Adjust by program base */
+#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */
+#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */
+#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */
+#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */
+#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */
+#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */
+#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */
+#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */
+#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */
+#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */
+#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */
+#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */
+#define R_68K_TLS_LE32 37 /* 32 bit offset relative to
+ static TLS block */
+#define R_68K_TLS_LE16 38 /* 16 bit offset relative to
+ static TLS block */
+#define R_68K_TLS_LE8 39 /* 8 bit offset relative to
+ static TLS block */
+#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */
+#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */
+#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */
+/* Keep this the last entry. */
+#define R_68K_NUM 43
+
+/* Intel 80386 specific definitions. */
+
+/* i386 relocs. */
+
+#define R_386_NONE 0 /* No reloc */
+#define R_386_32 1 /* Direct 32 bit */
+#define R_386_PC32 2 /* PC relative 32 bit */
+#define R_386_GOT32 3 /* 32 bit GOT entry */
+#define R_386_PLT32 4 /* 32 bit PLT address */
+#define R_386_COPY 5 /* Copy symbol at runtime */
+#define R_386_GLOB_DAT 6 /* Create GOT entry */
+#define R_386_JMP_SLOT 7 /* Create PLT entry */
+#define R_386_RELATIVE 8 /* Adjust by program base */
+#define R_386_GOTOFF 9 /* 32 bit offset to GOT */
+#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */
+#define R_386_32PLT 11
+#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */
+#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS
+ block offset */
+#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block
+ offset */
+#define R_386_TLS_LE 17 /* Offset relative to static TLS
+ block */
+#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of
+ general dynamic thread local data */
+#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of
+ local dynamic thread local data
+ in LE code */
+#define R_386_16 20
+#define R_386_PC16 21
+#define R_386_8 22
+#define R_386_PC8 23
+#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic
+ thread local data */
+#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */
+#define R_386_TLS_GD_CALL 26 /* Relocation for call to
+ __tls_get_addr() */
+#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */
+#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic
+ thread local data in LE code */
+#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */
+#define R_386_TLS_LDM_CALL 30 /* Relocation for call to
+ __tls_get_addr() in LDM code */
+#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */
+#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */
+#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS
+ block offset */
+#define R_386_TLS_LE_32 34 /* Negated offset relative to static
+ TLS block */
+#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */
+#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */
+#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */
+/* 38? */
+#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */
+#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS
+ descriptor for
+ relaxation. */
+#define R_386_TLS_DESC 41 /* TLS descriptor containing
+ pointer to code and to
+ argument, returning the TLS
+ offset for the symbol. */
+#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */
+/* Keep this the last entry. */
+#define R_386_NUM 43
+
+/* SUN SPARC specific definitions. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */
+
+/* Values for Elf64_Ehdr.e_flags. */
+
+#define EF_SPARCV9_MM 3
+#define EF_SPARCV9_TSO 0
+#define EF_SPARCV9_PSO 1
+#define EF_SPARCV9_RMO 2
+#define EF_SPARC_LEDATA 0x800000 /* little endian data */
+#define EF_SPARC_EXT_MASK 0xFFFF00
+#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */
+#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */
+#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */
+#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */
+
+/* SPARC relocs. */
+
+#define R_SPARC_NONE 0 /* No reloc */
+#define R_SPARC_8 1 /* Direct 8 bit */
+#define R_SPARC_16 2 /* Direct 16 bit */
+#define R_SPARC_32 3 /* Direct 32 bit */
+#define R_SPARC_DISP8 4 /* PC relative 8 bit */
+#define R_SPARC_DISP16 5 /* PC relative 16 bit */
+#define R_SPARC_DISP32 6 /* PC relative 32 bit */
+#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */
+#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */
+#define R_SPARC_HI22 9 /* High 22 bit */
+#define R_SPARC_22 10 /* Direct 22 bit */
+#define R_SPARC_13 11 /* Direct 13 bit */
+#define R_SPARC_LO10 12 /* Truncated 10 bit */
+#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */
+#define R_SPARC_GOT13 14 /* 13 bit GOT entry */
+#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */
+#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */
+#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */
+#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */
+#define R_SPARC_COPY 19 /* Copy symbol at runtime */
+#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */
+#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */
+#define R_SPARC_RELATIVE 22 /* Adjust by program base */
+#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */
+
+/* Additional Sparc64 relocs. */
+
+#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */
+#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */
+#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */
+#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */
+#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */
+#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */
+#define R_SPARC_10 30 /* Direct 10 bit */
+#define R_SPARC_11 31 /* Direct 11 bit */
+#define R_SPARC_64 32 /* Direct 64 bit */
+#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */
+#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */
+#define R_SPARC_HM10 35 /* High middle 10 bits of ... */
+#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */
+#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */
+#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */
+#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */
+#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */
+#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */
+#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */
+#define R_SPARC_7 43 /* Direct 7 bit */
+#define R_SPARC_5 44 /* Direct 5 bit */
+#define R_SPARC_6 45 /* Direct 6 bit */
+#define R_SPARC_DISP64 46 /* PC relative 64 bit */
+#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */
+#define R_SPARC_HIX22 48 /* High 22 bit complemented */
+#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */
+#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */
+#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */
+#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */
+#define R_SPARC_REGISTER 53 /* Global register usage */
+#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */
+#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */
+#define R_SPARC_TLS_GD_HI22 56
+#define R_SPARC_TLS_GD_LO10 57
+#define R_SPARC_TLS_GD_ADD 58
+#define R_SPARC_TLS_GD_CALL 59
+#define R_SPARC_TLS_LDM_HI22 60
+#define R_SPARC_TLS_LDM_LO10 61
+#define R_SPARC_TLS_LDM_ADD 62
+#define R_SPARC_TLS_LDM_CALL 63
+#define R_SPARC_TLS_LDO_HIX22 64
+#define R_SPARC_TLS_LDO_LOX10 65
+#define R_SPARC_TLS_LDO_ADD 66
+#define R_SPARC_TLS_IE_HI22 67
+#define R_SPARC_TLS_IE_LO10 68
+#define R_SPARC_TLS_IE_LD 69
+#define R_SPARC_TLS_IE_LDX 70
+#define R_SPARC_TLS_IE_ADD 71
+#define R_SPARC_TLS_LE_HIX22 72
+#define R_SPARC_TLS_LE_LOX10 73
+#define R_SPARC_TLS_DTPMOD32 74
+#define R_SPARC_TLS_DTPMOD64 75
+#define R_SPARC_TLS_DTPOFF32 76
+#define R_SPARC_TLS_DTPOFF64 77
+#define R_SPARC_TLS_TPOFF32 78
+#define R_SPARC_TLS_TPOFF64 79
+#define R_SPARC_GOTDATA_HIX22 80
+#define R_SPARC_GOTDATA_LOX10 81
+#define R_SPARC_GOTDATA_OP_HIX22 82
+#define R_SPARC_GOTDATA_OP_LOX10 83
+#define R_SPARC_GOTDATA_OP 84
+#define R_SPARC_H34 85
+#define R_SPARC_SIZE32 86
+#define R_SPARC_SIZE64 87
+#define R_SPARC_WDISP10 88
+#define R_SPARC_JMP_IREL 248
+#define R_SPARC_IRELATIVE 249
+#define R_SPARC_GNU_VTINHERIT 250
+#define R_SPARC_GNU_VTENTRY 251
+#define R_SPARC_REV32 252
+/* Keep this the last entry. */
+#define R_SPARC_NUM 253
+
+/* For Sparc64, legal values for d_tag of Elf64_Dyn. */
+
+#define DT_SPARC_REGISTER 0x70000001
+#define DT_SPARC_NUM 2
+
+/* MIPS R3000 specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */
+#define EF_MIPS_PIC 2 /* Contains PIC code */
+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */
+#define EF_MIPS_XGOT 8
+#define EF_MIPS_64BIT_WHIRL 16
+#define EF_MIPS_ABI2 32
+#define EF_MIPS_ABI_ON32 64
+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */
+
+/* Legal values for MIPS architecture level. */
+
+#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
+#define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
+
+/* The following are non-official names and should not be used. */
+
+#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
+#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
+#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
+#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
+#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
+#define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */
+#define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */
+
+/* Special section indices. */
+
+#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */
+#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */
+#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */
+#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */
+#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */
+#define SHT_MIPS_MSYM 0x70000001
+#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */
+#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */
+#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */
+#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/
+#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */
+#define SHT_MIPS_PACKAGE 0x70000007
+#define SHT_MIPS_PACKSYM 0x70000008
+#define SHT_MIPS_RELD 0x70000009
+#define SHT_MIPS_IFACE 0x7000000b
+#define SHT_MIPS_CONTENT 0x7000000c
+#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */
+#define SHT_MIPS_SHDR 0x70000010
+#define SHT_MIPS_FDESC 0x70000011
+#define SHT_MIPS_EXTSYM 0x70000012
+#define SHT_MIPS_DENSE 0x70000013
+#define SHT_MIPS_PDESC 0x70000014
+#define SHT_MIPS_LOCSYM 0x70000015
+#define SHT_MIPS_AUXSYM 0x70000016
+#define SHT_MIPS_OPTSYM 0x70000017
+#define SHT_MIPS_LOCSTR 0x70000018
+#define SHT_MIPS_LINE 0x70000019
+#define SHT_MIPS_RFDESC 0x7000001a
+#define SHT_MIPS_DELTASYM 0x7000001b
+#define SHT_MIPS_DELTAINST 0x7000001c
+#define SHT_MIPS_DELTACLASS 0x7000001d
+#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */
+#define SHT_MIPS_DELTADECL 0x7000001f
+#define SHT_MIPS_SYMBOL_LIB 0x70000020
+#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */
+#define SHT_MIPS_TRANSLATE 0x70000022
+#define SHT_MIPS_PIXIE 0x70000023
+#define SHT_MIPS_XLATE 0x70000024
+#define SHT_MIPS_XLATE_DEBUG 0x70000025
+#define SHT_MIPS_WHIRL 0x70000026
+#define SHT_MIPS_EH_REGION 0x70000027
+#define SHT_MIPS_XLATE_OLD 0x70000028
+#define SHT_MIPS_PDR_EXCEPTION 0x70000029
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */
+#define SHF_MIPS_MERGE 0x20000000
+#define SHF_MIPS_ADDR 0x40000000
+#define SHF_MIPS_STRINGS 0x80000000
+#define SHF_MIPS_NOSTRIP 0x08000000
+#define SHF_MIPS_LOCAL 0x04000000
+#define SHF_MIPS_NAMES 0x02000000
+#define SHF_MIPS_NODUPE 0x01000000
+
+
+/* Symbol tables. */
+
+/* MIPS specific values for `st_other'. */
+#define STO_MIPS_DEFAULT 0x0
+#define STO_MIPS_INTERNAL 0x1
+#define STO_MIPS_HIDDEN 0x2
+#define STO_MIPS_PROTECTED 0x3
+#define STO_MIPS_PLT 0x8
+#define STO_MIPS_SC_ALIGN_UNUSED 0xff
+
+/* MIPS specific values for `st_info'. */
+#define STB_MIPS_SPLIT_COMMON 13
+
+/* Entries found in sections of type SHT_MIPS_GPTAB. */
+
+typedef union
+{
+ struct
+ {
+ Elf32_Word gt_current_g_value; /* -G value used for compilation */
+ Elf32_Word gt_unused; /* Not used */
+ } gt_header; /* First entry in section */
+ struct
+ {
+ Elf32_Word gt_g_value; /* If this value were used for -G */
+ Elf32_Word gt_bytes; /* This many bytes would be used */
+ } gt_entry; /* Subsequent entries in section */
+} Elf32_gptab;
+
+/* Entry found in sections of type SHT_MIPS_REGINFO. */
+
+typedef struct
+{
+ Elf32_Word ri_gprmask; /* General registers used */
+ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */
+ Elf32_Sword ri_gp_value; /* $gp register value */
+} Elf32_RegInfo;
+
+/* Entries found in sections of type SHT_MIPS_OPTIONS. */
+
+typedef struct
+{
+ unsigned char kind; /* Determines interpretation of the
+ variable part of descriptor. */
+ unsigned char size; /* Size of descriptor, including header. */
+ Elf32_Section section; /* Section header index of section affected,
+ 0 for global options. */
+ Elf32_Word info; /* Kind-specific information. */
+} Elf_Options;
+
+/* Values for `kind' field in Elf_Options. */
+
+#define ODK_NULL 0 /* Undefined. */
+#define ODK_REGINFO 1 /* Register usage information. */
+#define ODK_EXCEPTIONS 2 /* Exception processing options. */
+#define ODK_PAD 3 /* Section padding options. */
+#define ODK_HWPATCH 4 /* Hardware workarounds performed */
+#define ODK_FILL 5 /* record the fill value used by the linker. */
+#define ODK_TAGS 6 /* reserve space for desktop tools to write. */
+#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */
+#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */
+
+/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */
+
+#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */
+#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */
+#define OEX_PAGE0 0x10000 /* page zero must be mapped. */
+#define OEX_SMM 0x20000 /* Force sequential memory mode? */
+#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */
+#define OEX_PRECISEFP OEX_FPDBUG
+#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */
+
+#define OEX_FPU_INVAL 0x10
+#define OEX_FPU_DIV0 0x08
+#define OEX_FPU_OFLO 0x04
+#define OEX_FPU_UFLO 0x02
+#define OEX_FPU_INEX 0x01
+
+/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */
+
+#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */
+#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */
+#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */
+#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */
+
+#define OPAD_PREFIX 0x1
+#define OPAD_POSTFIX 0x2
+#define OPAD_SYMBOL 0x4
+
+/* Entry found in `.options' section. */
+
+typedef struct
+{
+ Elf32_Word hwp_flags1; /* Extra flags. */
+ Elf32_Word hwp_flags2; /* Extra flags. */
+} Elf_Options_Hw;
+
+/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */
+
+#define OHWA0_R4KEOP_CHECKED 0x00000001
+#define OHWA1_R4KEOP_CLEAN 0x00000002
+
+/* MIPS relocs. */
+
+#define R_MIPS_NONE 0 /* No reloc */
+#define R_MIPS_16 1 /* Direct 16 bit */
+#define R_MIPS_32 2 /* Direct 32 bit */
+#define R_MIPS_REL32 3 /* PC relative 32 bit */
+#define R_MIPS_26 4 /* Direct 26 bit shifted */
+#define R_MIPS_HI16 5 /* High 16 bit */
+#define R_MIPS_LO16 6 /* Low 16 bit */
+#define R_MIPS_GPREL16 7 /* GP relative 16 bit */
+#define R_MIPS_LITERAL 8 /* 16 bit literal entry */
+#define R_MIPS_GOT16 9 /* 16 bit GOT entry */
+#define R_MIPS_PC16 10 /* PC relative 16 bit */
+#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */
+#define R_MIPS_GPREL32 12 /* GP relative 32 bit */
+
+#define R_MIPS_SHIFT5 16
+#define R_MIPS_SHIFT6 17
+#define R_MIPS_64 18
+#define R_MIPS_GOT_DISP 19
+#define R_MIPS_GOT_PAGE 20
+#define R_MIPS_GOT_OFST 21
+#define R_MIPS_GOT_HI16 22
+#define R_MIPS_GOT_LO16 23
+#define R_MIPS_SUB 24
+#define R_MIPS_INSERT_A 25
+#define R_MIPS_INSERT_B 26
+#define R_MIPS_DELETE 27
+#define R_MIPS_HIGHER 28
+#define R_MIPS_HIGHEST 29
+#define R_MIPS_CALL_HI16 30
+#define R_MIPS_CALL_LO16 31
+#define R_MIPS_SCN_DISP 32
+#define R_MIPS_REL16 33
+#define R_MIPS_ADD_IMMEDIATE 34
+#define R_MIPS_PJUMP 35
+#define R_MIPS_RELGOT 36
+#define R_MIPS_JALR 37
+#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */
+#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */
+#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */
+#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */
+#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */
+#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */
+#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */
+#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */
+#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */
+#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */
+#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */
+#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */
+#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */
+#define R_MIPS_GLOB_DAT 51
+#define R_MIPS_COPY 126
+#define R_MIPS_JUMP_SLOT 127
+/* Keep this the last entry. */
+#define R_MIPS_NUM 128
+
+/* Legal values for p_type field of Elf32_Phdr. */
+
+#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */
+#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */
+#define PT_MIPS_OPTIONS 0x70000002
+
+/* Special program header types. */
+
+#define PF_MIPS_LOCAL 0x10000000
+
+/* Legal values for d_tag field of Elf32_Dyn. */
+
+#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */
+#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */
+#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */
+#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */
+#define DT_MIPS_FLAGS 0x70000005 /* Flags */
+#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */
+#define DT_MIPS_MSYM 0x70000007
+#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */
+#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */
+#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */
+#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */
+#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */
+#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */
+#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */
+#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */
+#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */
+#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */
+#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */
+#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in
+ DT_MIPS_DELTA_CLASS. */
+#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */
+#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in
+ DT_MIPS_DELTA_INSTANCE. */
+#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */
+#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in
+ DT_MIPS_DELTA_RELOC. */
+#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta
+ relocations refer to. */
+#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in
+ DT_MIPS_DELTA_SYM. */
+#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the
+ class declaration. */
+#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in
+ DT_MIPS_DELTA_CLASSSYM. */
+#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */
+#define DT_MIPS_PIXIE_INIT 0x70000023
+#define DT_MIPS_SYMBOL_LIB 0x70000024
+#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025
+#define DT_MIPS_LOCAL_GOTIDX 0x70000026
+#define DT_MIPS_HIDDEN_GOTIDX 0x70000027
+#define DT_MIPS_PROTECTED_GOTIDX 0x70000028
+#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */
+#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */
+#define DT_MIPS_DYNSTR_ALIGN 0x7000002b
+#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */
+#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve
+ function stored in GOT. */
+#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added
+ by rld on dlopen() calls. */
+#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */
+#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */
+#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */
+/* The address of .got.plt in an executable using the new non-PIC ABI. */
+#define DT_MIPS_PLTGOT 0x70000032
+/* The base of the PLT in an executable using the new non-PIC ABI if that
+ PLT is writable. For a non-writable PLT, this is omitted or has a zero
+ value. */
+#define DT_MIPS_RWPLT 0x70000034
+#define DT_MIPS_NUM 0x35
+
+/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */
+
+#define RHF_NONE 0 /* No flags */
+#define RHF_QUICKSTART (1 << 0) /* Use quickstart */
+#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */
+#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */
+#define RHF_NO_MOVE (1 << 3)
+#define RHF_SGI_ONLY (1 << 4)
+#define RHF_GUARANTEE_INIT (1 << 5)
+#define RHF_DELTA_C_PLUS_PLUS (1 << 6)
+#define RHF_GUARANTEE_START_INIT (1 << 7)
+#define RHF_PIXIE (1 << 8)
+#define RHF_DEFAULT_DELAY_LOAD (1 << 9)
+#define RHF_REQUICKSTART (1 << 10)
+#define RHF_REQUICKSTARTED (1 << 11)
+#define RHF_CORD (1 << 12)
+#define RHF_NO_UNRES_UNDEF (1 << 13)
+#define RHF_RLD_ORDER_SAFE (1 << 14)
+
+/* Entries found in sections of type SHT_MIPS_LIBLIST. */
+
+typedef struct
+{
+ Elf32_Word l_name; /* Name (string table index) */
+ Elf32_Word l_time_stamp; /* Timestamp */
+ Elf32_Word l_checksum; /* Checksum */
+ Elf32_Word l_version; /* Interface version */
+ Elf32_Word l_flags; /* Flags */
+} Elf32_Lib;
+
+typedef struct
+{
+ Elf64_Word l_name; /* Name (string table index) */
+ Elf64_Word l_time_stamp; /* Timestamp */
+ Elf64_Word l_checksum; /* Checksum */
+ Elf64_Word l_version; /* Interface version */
+ Elf64_Word l_flags; /* Flags */
+} Elf64_Lib;
+
+
+/* Legal values for l_flags. */
+
+#define LL_NONE 0
+#define LL_EXACT_MATCH (1 << 0) /* Require exact match */
+#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */
+#define LL_REQUIRE_MINOR (1 << 2)
+#define LL_EXPORTS (1 << 3)
+#define LL_DELAY_LOAD (1 << 4)
+#define LL_DELTA (1 << 5)
+
+/* Entries found in sections of type SHT_MIPS_CONFLICT. */
+
+typedef Elf32_Addr Elf32_Conflict;
+
+
+/* HPPA specific definitions. */
+
+/* Legal values for e_flags field of Elf32_Ehdr. */
+
+#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */
+#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */
+#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */
+#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */
+#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch
+ prediction. */
+#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */
+#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */
+
+/* Defined values for `e_flags & EF_PARISC_ARCH' are: */
+
+#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */
+#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */
+#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */
+
+/* Additional section indeces. */
+
+#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared
+ symbols in ANSI C. */
+#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */
+
+/* Legal values for sh_type field of Elf32_Shdr. */
+
+#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */
+#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */
+#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */
+
+/* Legal values for sh_flags field of Elf32_Shdr. */
+
+#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */
+#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */
+#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */
+
+/* Legal values for ST_TYPE subfield of st_info (symbol type). */
+
+#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */
+
+#define STT_HP_OPAQUE (STT_LOOS + 0x1)
+#define STT_HP_STUB (STT_LOOS + 0x2)
+
+/* HPPA relocs. */
+
+#define R_PARISC_NONE 0 /* No reloc. */
+#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */
+#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */
+#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */
+#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */
+#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */
+#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */
+#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */
+#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */
+#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */
+#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */
+#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */
+#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */
+#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */
+#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */
+#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */
+#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */
+#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */
+#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */
+#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */
+#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */
+#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */
+#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */
+#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */
+#define R_PARISC_FPTR64 64 /* 64 bits function address. */
+#define R_PARISC_PLABEL32 65 /* 32 bits function address. */
+#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */
+#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */
+#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */
+#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */
+#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */
+#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */
+#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */
+#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */
+#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */
+#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */
+#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */
+#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */
+#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */
+#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */
+#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */
+#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */
+#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */
+#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */
+#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */
+#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */
+#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */
+#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */
+#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */
+#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */
+#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */
+#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */
+#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */
+#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */
+#define R_PARISC_LORESERVE 128
+#define R_PARISC_COPY 128 /* Copy relocation. */
+#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */
+#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */
+#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */
+#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */
+#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */
+#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */
+#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */
+#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */
+#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */
+#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */
+#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/
+#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */
+#define R_PARISC_GNU_VTENTRY 232
+#define R_PARISC_GNU_VTINHERIT 233
+#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */
+#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */
+#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */
+#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */
+#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */
+#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */
+#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */
+#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */
+#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */
+#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */
+#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */
+#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */
+#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L
+#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R
+#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L
+#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R
+#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32
+#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64
+#define R_PARISC_HIRESERVE 255
+
+/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PT_HP_TLS (PT_LOOS + 0x0)
+#define PT_HP_CORE_NONE (PT_LOOS + 0x1)
+#define PT_HP_CORE_VERSION (PT_LOOS + 0x2)
+#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3)
+#define PT_HP_CORE_COMM (PT_LOOS + 0x4)
+#define PT_HP_CORE_PROC (PT_LOOS + 0x5)
+#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6)
+#define PT_HP_CORE_STACK (PT_LOOS + 0x7)
+#define PT_HP_CORE_SHM (PT_LOOS + 0x8)
+#define PT_HP_CORE_MMF (PT_LOOS + 0x9)
+#define PT_HP_PARALLEL (PT_LOOS + 0x10)
+#define PT_HP_FASTBIND (PT_LOOS + 0x11)
+#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12)
+#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13)
+#define PT_HP_STACK (PT_LOOS + 0x14)
+
+#define PT_PARISC_ARCHEXT 0x70000000
+#define PT_PARISC_UNWIND 0x70000001
+
+/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */
+
+#define PF_PARISC_SBP 0x08000000
+
+#define PF_HP_PAGE_SIZE 0x00100000
+#define PF_HP_FAR_SHARED 0x00200000
+#define PF_HP_NEAR_SHARED 0x00400000
+#define PF_HP_CODE 0x01000000
+#define PF_HP_MODIFY 0x02000000
+#define PF_HP_LAZYSWAP 0x04000000
+#define PF_HP_SBP 0x08000000
+
+
+/* Alpha specific definitions. */
+
+/* Legal values for e_flags field of Elf64_Ehdr. */
+
+#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */
+#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */
+
+/* Legal values for sh_type field of Elf64_Shdr. */
+
+/* These two are primerily concerned with ECOFF debugging info. */
+#define SHT_ALPHA_DEBUG 0x70000001
+#define SHT_ALPHA_REGINFO 0x70000002
+
+/* Legal values for sh_flags field of Elf64_Shdr. */
+
+#define SHF_ALPHA_GPREL 0x10000000
+
+/* Legal values for st_other field of Elf64_Sym. */
+#define STO_ALPHA_NOPV 0x80 /* No PV required. */
+#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */
+
+/* Alpha relocs. */
+
+#define R_ALPHA_NONE 0 /* No reloc */
+#define R_ALPHA_REFLONG 1 /* Direct 32 bit */
+#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */
+#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */
+#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */
+#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */
+#define R_ALPHA_GPDISP 6 /* Add displacement to GP */
+#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */
+#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */
+#define R_ALPHA_SREL16 9 /* PC relative 16 bit */
+#define R_ALPHA_SREL32 10 /* PC relative 32 bit */
+#define R_ALPHA_SREL64 11 /* PC relative 64 bit */
+#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */
+#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */
+#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */
+#define R_ALPHA_COPY 24 /* Copy symbol at runtime */
+#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */
+#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */
+#define R_ALPHA_RELATIVE 27 /* Adjust by program base */
+#define R_ALPHA_TLS_GD_HI 28
+#define R_ALPHA_TLSGD 29
+#define R_ALPHA_TLS_LDM 30
+#define R_ALPHA_DTPMOD64 31
+#define R_ALPHA_GOTDTPREL 32
+#define R_ALPHA_DTPREL64 33
+#define R_ALPHA_DTPRELHI 34
+#define R_ALPHA_DTPRELLO 35
+#define R_ALPHA_DTPREL16 36
+#define R_ALPHA_GOTTPREL 37
+#define R_ALPHA_TPREL64 38
+#define R_ALPHA_TPRELHI 39
+#define R_ALPHA_TPRELLO 40
+#define R_ALPHA_TPREL16 41
+/* Keep this the last entry. */
+#define R_ALPHA_NUM 46
+
+/* Magic values of the LITUSE relocation addend. */
+#define LITUSE_ALPHA_ADDR 0
+#define LITUSE_ALPHA_BASE 1
+#define LITUSE_ALPHA_BYTOFF 2
+#define LITUSE_ALPHA_JSR 3
+#define LITUSE_ALPHA_TLS_GD 4
+#define LITUSE_ALPHA_TLS_LDM 5
+
+/* Legal values for d_tag of Elf64_Dyn. */
+#define DT_ALPHA_PLTRO (DT_LOPROC + 0)
+#define DT_ALPHA_NUM 1
+
+/* PowerPC specific declarations */
+
+/* Values for Elf32/64_Ehdr.e_flags. */
+#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */
+
+/* Cygnus local bits below */
+#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/
+#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib
+ flag */
+
+/* PowerPC relocations defined by the ABIs */
+#define R_PPC_NONE 0
+#define R_PPC_ADDR32 1 /* 32bit absolute address */
+#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */
+#define R_PPC_ADDR16 3 /* 16bit absolute address */
+#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */
+#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */
+#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */
+#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */
+#define R_PPC_ADDR14_BRTAKEN 8
+#define R_PPC_ADDR14_BRNTAKEN 9
+#define R_PPC_REL24 10 /* PC relative 26 bit */
+#define R_PPC_REL14 11 /* PC relative 16 bit */
+#define R_PPC_REL14_BRTAKEN 12
+#define R_PPC_REL14_BRNTAKEN 13
+#define R_PPC_GOT16 14
+#define R_PPC_GOT16_LO 15
+#define R_PPC_GOT16_HI 16
+#define R_PPC_GOT16_HA 17
+#define R_PPC_PLTREL24 18
+#define R_PPC_COPY 19
+#define R_PPC_GLOB_DAT 20
+#define R_PPC_JMP_SLOT 21
+#define R_PPC_RELATIVE 22
+#define R_PPC_LOCAL24PC 23
+#define R_PPC_UADDR32 24
+#define R_PPC_UADDR16 25
+#define R_PPC_REL32 26
+#define R_PPC_PLT32 27
+#define R_PPC_PLTREL32 28
+#define R_PPC_PLT16_LO 29
+#define R_PPC_PLT16_HI 30
+#define R_PPC_PLT16_HA 31
+#define R_PPC_SDAREL16 32
+#define R_PPC_SECTOFF 33
+#define R_PPC_SECTOFF_LO 34
+#define R_PPC_SECTOFF_HI 35
+#define R_PPC_SECTOFF_HA 36
+
+/* PowerPC relocations defined for the TLS access ABI. */
+#define R_PPC_TLS 67 /* none (sym+add)@tls */
+#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */
+#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */
+#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
+#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
+#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
+#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */
+#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */
+#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
+#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
+#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
+#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */
+#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
+#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
+#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
+#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
+#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
+#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
+#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
+#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
+#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */
+#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */
+#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
+#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
+#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */
+#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */
+#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */
+#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */
+
+/* The remaining relocs are from the Embedded ELF ABI, and are not
+ in the SVR4 ELF ABI. */
+#define R_PPC_EMB_NADDR32 101
+#define R_PPC_EMB_NADDR16 102
+#define R_PPC_EMB_NADDR16_LO 103
+#define R_PPC_EMB_NADDR16_HI 104
+#define R_PPC_EMB_NADDR16_HA 105
+#define R_PPC_EMB_SDAI16 106
+#define R_PPC_EMB_SDA2I16 107
+#define R_PPC_EMB_SDA2REL 108
+#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */
+#define R_PPC_EMB_MRKREF 110
+#define R_PPC_EMB_RELSEC16 111
+#define R_PPC_EMB_RELST_LO 112
+#define R_PPC_EMB_RELST_HI 113
+#define R_PPC_EMB_RELST_HA 114
+#define R_PPC_EMB_BIT_FLD 115
+#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */
+
+/* Diab tool relocations. */
+#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */
+#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */
+#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */
+#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */
+#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */
+#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */
+
+/* GNU extension to support local ifunc. */
+#define R_PPC_IRELATIVE 248
+
+/* GNU relocs used in PIC code sequences. */
+#define R_PPC_REL16 249 /* half16 (sym+add-.) */
+#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */
+#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */
+#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */
+
+/* This is a phony reloc to handle any old fashioned TOC16 references
+ that may still be in object files. */
+#define R_PPC_TOC16 255
+
+/* PowerPC specific values for the Dyn d_tag field. */
+#define DT_PPC_GOT (DT_LOPROC + 0)
+#define DT_PPC_NUM 1
+
+/* PowerPC64 relocations defined by the ABIs */
+#define R_PPC64_NONE R_PPC_NONE
+#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */
+#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */
+#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */
+#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */
+#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */
+#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */
+#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */
+#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN
+#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN
+#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */
+#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */
+#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN
+#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN
+#define R_PPC64_GOT16 R_PPC_GOT16
+#define R_PPC64_GOT16_LO R_PPC_GOT16_LO
+#define R_PPC64_GOT16_HI R_PPC_GOT16_HI
+#define R_PPC64_GOT16_HA R_PPC_GOT16_HA
+
+#define R_PPC64_COPY R_PPC_COPY
+#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT
+#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT
+#define R_PPC64_RELATIVE R_PPC_RELATIVE
+
+#define R_PPC64_UADDR32 R_PPC_UADDR32
+#define R_PPC64_UADDR16 R_PPC_UADDR16
+#define R_PPC64_REL32 R_PPC_REL32
+#define R_PPC64_PLT32 R_PPC_PLT32
+#define R_PPC64_PLTREL32 R_PPC_PLTREL32
+#define R_PPC64_PLT16_LO R_PPC_PLT16_LO
+#define R_PPC64_PLT16_HI R_PPC_PLT16_HI
+#define R_PPC64_PLT16_HA R_PPC_PLT16_HA
+
+#define R_PPC64_SECTOFF R_PPC_SECTOFF
+#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO
+#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI
+#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA
+#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */
+#define R_PPC64_ADDR64 38 /* doubleword64 S + A */
+#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */
+#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */
+#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */
+#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */
+#define R_PPC64_UADDR64 43 /* doubleword64 S + A */
+#define R_PPC64_REL64 44 /* doubleword64 S + A - P */
+#define R_PPC64_PLT64 45 /* doubleword64 L + A */
+#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */
+#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */
+#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */
+#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */
+#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */
+#define R_PPC64_TOC 51 /* doubleword64 .TOC */
+#define R_PPC64_PLTGOT16 52 /* half16* M + A */
+#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */
+#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */
+#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */
+
+#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */
+#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */
+#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */
+#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */
+#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */
+#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */
+#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */
+#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */
+#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */
+#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */
+#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */
+
+/* PowerPC64 relocations defined for the TLS access ABI. */
+#define R_PPC64_TLS 67 /* none (sym+add)@tls */
+#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */
+#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */
+#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */
+#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */
+#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */
+#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */
+#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */
+#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */
+#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */
+#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */
+#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */
+#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */
+#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */
+#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */
+#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */
+#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */
+#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */
+#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */
+#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */
+#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */
+#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */
+#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */
+#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */
+#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */
+#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */
+#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */
+#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */
+#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */
+#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */
+#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */
+#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */
+#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */
+#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */
+#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */
+#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */
+#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */
+#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */
+#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */
+#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */
+
+/* GNU extension to support local ifunc. */
+#define R_PPC64_JMP_IREL 247
+#define R_PPC64_IRELATIVE 248
+#define R_PPC64_REL16 249 /* half16 (sym+add-.) */
+#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */
+#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */
+#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */
+
+/* PowerPC64 specific values for the Dyn d_tag field. */
+#define DT_PPC64_GLINK (DT_LOPROC + 0)
+#define DT_PPC64_OPD (DT_LOPROC + 1)
+#define DT_PPC64_OPDSZ (DT_LOPROC + 2)
+#define DT_PPC64_NUM 3
+
+
+/* ARM specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_ARM_RELEXEC 0x01
+#define EF_ARM_HASENTRY 0x02
+#define EF_ARM_INTERWORK 0x04
+#define EF_ARM_APCS_26 0x08
+#define EF_ARM_APCS_FLOAT 0x10
+#define EF_ARM_PIC 0x20
+#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */
+#define EF_ARM_NEW_ABI 0x80
+#define EF_ARM_OLD_ABI 0x100
+#define EF_ARM_SOFT_FLOAT 0x200
+#define EF_ARM_VFP_FLOAT 0x400
+#define EF_ARM_MAVERICK_FLOAT 0x800
+
+#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */
+#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */
+
+
+/* Other constants defined in the ARM ELF spec. version B-01. */
+/* NB. These conflict with values defined above. */
+#define EF_ARM_SYMSARESORTED 0x04
+#define EF_ARM_DYNSYMSUSESEGIDX 0x08
+#define EF_ARM_MAPSYMSFIRST 0x10
+#define EF_ARM_EABIMASK 0XFF000000
+
+/* Constants defined in AAELF. */
+#define EF_ARM_BE8 0x00800000
+#define EF_ARM_LE8 0x00400000
+
+#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK)
+#define EF_ARM_EABI_UNKNOWN 0x00000000
+#define EF_ARM_EABI_VER1 0x01000000
+#define EF_ARM_EABI_VER2 0x02000000
+#define EF_ARM_EABI_VER3 0x03000000
+#define EF_ARM_EABI_VER4 0x04000000
+#define EF_ARM_EABI_VER5 0x05000000
+
+/* Additional symbol types for Thumb. */
+#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */
+#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */
+
+/* ARM-specific values for sh_flags */
+#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */
+#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined
+ in the input to a link step. */
+
+/* ARM-specific program header flags */
+#define PF_ARM_SB 0x10000000 /* Segment contains the location
+ addressed by the static base. */
+#define PF_ARM_PI 0x20000000 /* Position-independent segment. */
+#define PF_ARM_ABS 0x40000000 /* Absolute segment. */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */
+#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */
+#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */
+
+
+/* AArch64 relocs. */
+
+#define R_AARCH64_NONE 0 /* No relocation. */
+#define R_AARCH64_ABS64 257 /* Direct 64 bit. */
+#define R_AARCH64_ABS32 258 /* Direct 32 bit. */
+#define R_AARCH64_ABS16 259 /* Direct 16-bit. */
+#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */
+#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */
+#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */
+#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */
+#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */
+#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */
+#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */
+#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */
+#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */
+#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */
+#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */
+#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */
+#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */
+#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */
+#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */
+#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */
+#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */
+#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */
+#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */
+#define R_AARCH64_CALL26 283 /* Likewise for CALL. */
+#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */
+#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */
+#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */
+#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */
+#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */
+#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */
+#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */
+#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */
+#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */
+#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */
+#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */
+#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */
+#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */
+#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */
+#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */
+#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */
+#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */
+#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */
+#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */
+#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */
+#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */
+#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */
+#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */
+#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */
+#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */
+#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */
+#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */
+#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */
+#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */
+#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */
+#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */
+#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */
+#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */
+#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */
+#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */
+#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */
+#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */
+#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */
+#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */
+#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */
+#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */
+#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */
+#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */
+#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */
+#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */
+#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */
+#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */
+#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */
+#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */
+#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */
+#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */
+#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */
+#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */
+#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */
+#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */
+#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */
+#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */
+#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */
+#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */
+#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */
+#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */
+#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */
+#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */
+#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */
+#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */
+#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */
+#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */
+#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */
+#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */
+#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */
+#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */
+#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */
+#define R_AARCH64_TLS_DTPMOD64 1028 /* Module number, 64 bit. */
+#define R_AARCH64_TLS_DTPREL64 1029 /* Module-relative offset, 64 bit. */
+#define R_AARCH64_TLS_TPREL64 1030 /* TP-relative offset, 64 bit. */
+#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */
+#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */
+
+/* ARM relocs. */
+
+#define R_ARM_NONE 0 /* No reloc */
+#define R_ARM_PC24 1 /* PC relative 26 bit branch */
+#define R_ARM_ABS32 2 /* Direct 32 bit */
+#define R_ARM_REL32 3 /* PC relative 32 bit */
+#define R_ARM_PC13 4
+#define R_ARM_ABS16 5 /* Direct 16 bit */
+#define R_ARM_ABS12 6 /* Direct 12 bit */
+#define R_ARM_THM_ABS5 7
+#define R_ARM_ABS8 8 /* Direct 8 bit */
+#define R_ARM_SBREL32 9
+#define R_ARM_THM_PC22 10
+#define R_ARM_THM_PC8 11
+#define R_ARM_AMP_VCALL9 12
+#define R_ARM_SWI24 13 /* Obsolete static relocation. */
+#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */
+#define R_ARM_THM_SWI8 14
+#define R_ARM_XPC25 15
+#define R_ARM_THM_XPC22 16
+#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */
+#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */
+#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */
+#define R_ARM_COPY 20 /* Copy symbol at runtime */
+#define R_ARM_GLOB_DAT 21 /* Create GOT entry */
+#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */
+#define R_ARM_RELATIVE 23 /* Adjust by program base */
+#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */
+#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */
+#define R_ARM_GOT32 26 /* 32 bit GOT entry */
+#define R_ARM_PLT32 27 /* 32 bit PLT address */
+#define R_ARM_CALL 28
+#define R_ARM_JUMP24 29
+#define R_ARM_THM_JUMP24 30
+#define R_ARM_ALU_PCREL_7_0 32
+#define R_ARM_ALU_PCREL_15_8 33
+#define R_ARM_ALU_PCREL_23_15 34
+#define R_ARM_LDR_SBREL_11_0 35
+#define R_ARM_ALU_SBREL_19_12 36
+#define R_ARM_ALU_SBREL_27_20 37
+#define R_ARM_V4BX 40
+#define R_ARM_PREL31 42
+#define R_ARM_MOVW_ABS_NC 43
+#define R_ARM_MOVT_ABS 44
+#define R_ARM_THM_MOVW_ABS_NC 47
+#define R_ARM_THM_MOVT_ABS 48
+#define R_ARM_TLS_GOTDESC 90
+#define R_ARM_TLS_CALL 91
+#define R_ARM_TLS_DESCSEQ 92
+#define R_ARM_THM_TLS_CALL 93
+#define R_ARM_GNU_VTENTRY 100
+#define R_ARM_GNU_VTINHERIT 101
+#define R_ARM_THM_PC11 102 /* thumb unconditional branch */
+#define R_ARM_THM_PC9 103 /* thumb conditional branch */
+#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic
+ thread local data */
+#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic
+ thread local data */
+#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS
+ block */
+#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of
+ static TLS block offset */
+#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static
+ TLS block */
+#define R_ARM_THM_TLS_DESCSEQ 129
+#define R_ARM_IRELATIVE 160
+#define R_ARM_RXPC25 249
+#define R_ARM_RSBREL32 250
+#define R_ARM_THM_RPC22 251
+#define R_ARM_RREL32 252
+#define R_ARM_RABS22 253
+#define R_ARM_RPC24 254
+#define R_ARM_RBASE 255
+/* Keep this the last entry. */
+#define R_ARM_NUM 256
+
+/* TMS320C67xx specific declarations */
+
+/* XXX: no ELF standard yet*/
+
+/* TMS320C67xx relocs. */
+#define R_C60_32 1
+#define R_C60_GOT32 3 /* 32 bit GOT entry */
+#define R_C60_PLT32 4 /* 32 bit PLT address */
+#define R_C60_COPY 5 /* Copy symbol at runtime */
+#define R_C60_GLOB_DAT 6 /* Create GOT entry */
+#define R_C60_JMP_SLOT 7 /* Create PLT entry */
+#define R_C60_RELATIVE 8 /* Adjust by program base */
+#define R_C60_GOTOFF 9 /* 32 bit offset to GOT */
+#define R_C60_GOTPC 10 /* 32 bit PC relative offset to GOT */
+
+#define R_C60HI16 0x55 /* high 16 bit MVKH embedded */
+#define R_C60LO16 0x54 /* low 16 bit MVKL embedded */
+
+/* IA-64 specific declarations. */
+
+/* Processor specific flags for the Ehdr e_flags field. */
+#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */
+#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */
+#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */
+
+/* Processor specific values for the Phdr p_type field. */
+#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */
+#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */
+#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12)
+#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13)
+#define PT_IA_64_HP_STACK (PT_LOOS + 0x14)
+
+/* Processor specific flags for the Phdr p_flags field. */
+#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Shdr sh_type field. */
+#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */
+#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */
+
+/* Processor specific flags for the Shdr sh_flags field. */
+#define SHF_IA_64_SHORT 0x10000000 /* section near gp */
+#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */
+
+/* Processor specific values for the Dyn d_tag field. */
+#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0)
+#define DT_IA_64_NUM 1
+
+/* IA-64 relocations. */
+#define R_IA64_NONE 0x00 /* none */
+#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */
+#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */
+#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */
+#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */
+#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */
+#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */
+#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */
+#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */
+#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */
+#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */
+#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */
+#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */
+#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */
+#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */
+#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */
+#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */
+#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */
+#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */
+#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */
+#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */
+#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */
+#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */
+#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */
+#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */
+#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */
+#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */
+#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */
+#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */
+#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */
+#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */
+#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */
+#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */
+#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */
+#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */
+#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */
+#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */
+#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */
+#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */
+#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */
+#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */
+#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */
+#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */
+#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */
+#define R_IA64_REL32MSB 0x6c /* data 4 + REL */
+#define R_IA64_REL32LSB 0x6d /* data 4 + REL */
+#define R_IA64_REL64MSB 0x6e /* data 8 + REL */
+#define R_IA64_REL64LSB 0x6f /* data 8 + REL */
+#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */
+#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */
+#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */
+#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */
+#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */
+#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */
+#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */
+#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */
+#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */
+#define R_IA64_COPY 0x84 /* copy relocation */
+#define R_IA64_SUB 0x85 /* Addend and symbol difference */
+#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */
+#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */
+#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */
+#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */
+#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */
+#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */
+#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */
+#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */
+#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */
+#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */
+#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */
+#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */
+#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */
+#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */
+#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */
+#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */
+#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */
+
+/* SH specific declarations */
+
+/* Processor specific flags for the ELF header e_flags field. */
+#define EF_SH_MACH_MASK 0x1f
+#define EF_SH_UNKNOWN 0x0
+#define EF_SH1 0x1
+#define EF_SH2 0x2
+#define EF_SH3 0x3
+#define EF_SH_DSP 0x4
+#define EF_SH3_DSP 0x5
+#define EF_SH4AL_DSP 0x6
+#define EF_SH3E 0x8
+#define EF_SH4 0x9
+#define EF_SH2E 0xb
+#define EF_SH4A 0xc
+#define EF_SH2A 0xd
+#define EF_SH4_NOFPU 0x10
+#define EF_SH4A_NOFPU 0x11
+#define EF_SH4_NOMMU_NOFPU 0x12
+#define EF_SH2A_NOFPU 0x13
+#define EF_SH3_NOMMU 0x14
+#define EF_SH2A_SH4_NOFPU 0x15
+#define EF_SH2A_SH3_NOFPU 0x16
+#define EF_SH2A_SH4 0x17
+#define EF_SH2A_SH3E 0x18
+
+/* SH relocs. */
+#define R_SH_NONE 0
+#define R_SH_DIR32 1
+#define R_SH_REL32 2
+#define R_SH_DIR8WPN 3
+#define R_SH_IND12W 4
+#define R_SH_DIR8WPL 5
+#define R_SH_DIR8WPZ 6
+#define R_SH_DIR8BP 7
+#define R_SH_DIR8W 8
+#define R_SH_DIR8L 9
+#define R_SH_SWITCH16 25
+#define R_SH_SWITCH32 26
+#define R_SH_USES 27
+#define R_SH_COUNT 28
+#define R_SH_ALIGN 29
+#define R_SH_CODE 30
+#define R_SH_DATA 31
+#define R_SH_LABEL 32
+#define R_SH_SWITCH8 33
+#define R_SH_GNU_VTINHERIT 34
+#define R_SH_GNU_VTENTRY 35
+#define R_SH_TLS_GD_32 144
+#define R_SH_TLS_LD_32 145
+#define R_SH_TLS_LDO_32 146
+#define R_SH_TLS_IE_32 147
+#define R_SH_TLS_LE_32 148
+#define R_SH_TLS_DTPMOD32 149
+#define R_SH_TLS_DTPOFF32 150
+#define R_SH_TLS_TPOFF32 151
+#define R_SH_GOT32 160
+#define R_SH_PLT32 161
+#define R_SH_COPY 162
+#define R_SH_GLOB_DAT 163
+#define R_SH_JMP_SLOT 164
+#define R_SH_RELATIVE 165
+#define R_SH_GOTOFF 166
+#define R_SH_GOTPC 167
+/* Keep this the last entry. */
+#define R_SH_NUM 256
+
+/* S/390 specific definitions. */
+
+/* Valid values for the e_flags field. */
+
+#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */
+
+/* Additional s390 relocs */
+
+#define R_390_NONE 0 /* No reloc. */
+#define R_390_8 1 /* Direct 8 bit. */
+#define R_390_12 2 /* Direct 12 bit. */
+#define R_390_16 3 /* Direct 16 bit. */
+#define R_390_32 4 /* Direct 32 bit. */
+#define R_390_PC32 5 /* PC relative 32 bit. */
+#define R_390_GOT12 6 /* 12 bit GOT offset. */
+#define R_390_GOT32 7 /* 32 bit GOT offset. */
+#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */
+#define R_390_COPY 9 /* Copy symbol at runtime. */
+#define R_390_GLOB_DAT 10 /* Create GOT entry. */
+#define R_390_JMP_SLOT 11 /* Create PLT entry. */
+#define R_390_RELATIVE 12 /* Adjust by program base. */
+#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */
+#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */
+#define R_390_GOT16 15 /* 16 bit GOT offset. */
+#define R_390_PC16 16 /* PC relative 16 bit. */
+#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */
+#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */
+#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */
+#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */
+#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */
+#define R_390_64 22 /* Direct 64 bit. */
+#define R_390_PC64 23 /* PC relative 64 bit. */
+#define R_390_GOT64 24 /* 64 bit GOT offset. */
+#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */
+#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */
+#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */
+#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */
+#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */
+#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */
+#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */
+#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */
+#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */
+#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */
+#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */
+#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */
+#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */
+#define R_390_TLS_GDCALL 38 /* Tag for function call in general
+ dynamic TLS code. */
+#define R_390_TLS_LDCALL 39 /* Tag for function call in local
+ dynamic TLS code. */
+#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic
+ thread local data. */
+#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS
+ block offset. */
+#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic
+ thread local data in LE code. */
+#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic
+ thread local data in LE code. */
+#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for
+ negated static TLS block offset. */
+#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to
+ static TLS block. */
+#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS
+ block. */
+#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS
+ block. */
+#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */
+#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */
+#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS
+ block. */
+#define R_390_20 57 /* Direct 20 bit. */
+#define R_390_GOT20 58 /* 20 bit GOT offset. */
+#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */
+#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS
+ block offset. */
+#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */
+/* Keep this the last entry. */
+#define R_390_NUM 62
+
+
+/* CRIS relocations. */
+#define R_CRIS_NONE 0
+#define R_CRIS_8 1
+#define R_CRIS_16 2
+#define R_CRIS_32 3
+#define R_CRIS_8_PCREL 4
+#define R_CRIS_16_PCREL 5
+#define R_CRIS_32_PCREL 6
+#define R_CRIS_GNU_VTINHERIT 7
+#define R_CRIS_GNU_VTENTRY 8
+#define R_CRIS_COPY 9
+#define R_CRIS_GLOB_DAT 10
+#define R_CRIS_JUMP_SLOT 11
+#define R_CRIS_RELATIVE 12
+#define R_CRIS_16_GOT 13
+#define R_CRIS_32_GOT 14
+#define R_CRIS_16_GOTPLT 15
+#define R_CRIS_32_GOTPLT 16
+#define R_CRIS_32_GOTREL 17
+#define R_CRIS_32_PLT_GOTREL 18
+#define R_CRIS_32_PLT_PCREL 19
+
+#define R_CRIS_NUM 20
+
+
+/* AMD x86-64 relocations. */
+#define R_X86_64_NONE 0 /* No reloc */
+#define R_X86_64_64 1 /* Direct 64 bit */
+#define R_X86_64_PC32 2 /* PC relative 32 bit signed */
+#define R_X86_64_GOT32 3 /* 32 bit GOT entry */
+#define R_X86_64_PLT32 4 /* 32 bit PLT address */
+#define R_X86_64_COPY 5 /* Copy symbol at runtime */
+#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */
+#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */
+#define R_X86_64_RELATIVE 8 /* Adjust by program base */
+#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative
+ offset to GOT */
+#define R_X86_64_32 10 /* Direct 32 bit zero extended */
+#define R_X86_64_32S 11 /* Direct 32 bit sign extended */
+#define R_X86_64_16 12 /* Direct 16 bit zero extended */
+#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */
+#define R_X86_64_8 14 /* Direct 8 bit sign extended */
+#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */
+#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */
+#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */
+#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */
+#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset
+ to two GOT entries for GD symbol */
+#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset
+ to two GOT entries for LD symbol */
+#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */
+#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset
+ to GOT entry for IE symbol */
+#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */
+#define R_X86_64_PC64 24 /* PC relative 64 bit */
+#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */
+#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative
+ offset to GOT */
+#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */
+#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset
+ to GOT entry */
+#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */
+#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */
+#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset
+ to PLT entry */
+#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */
+#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */
+#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */
+#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS
+ descriptor. */
+#define R_X86_64_TLSDESC 36 /* TLS descriptor. */
+#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */
+#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */
+
+#define R_X86_64_NUM 39
+
+
+/* AM33 relocations. */
+#define R_MN10300_NONE 0 /* No reloc. */
+#define R_MN10300_32 1 /* Direct 32 bit. */
+#define R_MN10300_16 2 /* Direct 16 bit. */
+#define R_MN10300_8 3 /* Direct 8 bit. */
+#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */
+#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */
+#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */
+#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */
+#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */
+#define R_MN10300_24 9 /* Direct 24 bit. */
+#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */
+#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */
+#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */
+#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */
+#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */
+#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */
+#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */
+#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */
+#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */
+#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */
+#define R_MN10300_COPY 20 /* Copy symbol at runtime. */
+#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */
+#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */
+#define R_MN10300_RELATIVE 23 /* Adjust by program base. */
+#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */
+#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */
+#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */
+#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block
+ offset. */
+#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block
+ offset. */
+#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS
+ block. */
+#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */
+#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */
+#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */
+#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed
+ by linker relaxation. */
+#define R_MN10300_ALIGN 34 /* Alignment requirement for linker
+ relaxation. */
+#define R_MN10300_NUM 35
+
+
+/* M32R relocs. */
+#define R_M32R_NONE 0 /* No reloc. */
+#define R_M32R_16 1 /* Direct 16 bit. */
+#define R_M32R_32 2 /* Direct 32 bit. */
+#define R_M32R_24 3 /* Direct 24 bit. */
+#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */
+#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */
+#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */
+#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */
+#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */
+#define R_M32R_LO16 9 /* Low 16 bit. */
+#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */
+#define R_M32R_GNU_VTINHERIT 11
+#define R_M32R_GNU_VTENTRY 12
+/* M32R relocs use SHT_RELA. */
+#define R_M32R_16_RELA 33 /* Direct 16 bit. */
+#define R_M32R_32_RELA 34 /* Direct 32 bit. */
+#define R_M32R_24_RELA 35 /* Direct 24 bit. */
+#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */
+#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */
+#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */
+#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */
+#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */
+#define R_M32R_LO16_RELA 41 /* Low 16 bit */
+#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */
+#define R_M32R_RELA_GNU_VTINHERIT 43
+#define R_M32R_RELA_GNU_VTENTRY 44
+#define R_M32R_REL32 45 /* PC relative 32 bit. */
+
+#define R_M32R_GOT24 48 /* 24 bit GOT entry */
+#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */
+#define R_M32R_COPY 50 /* Copy symbol at runtime */
+#define R_M32R_GLOB_DAT 51 /* Create GOT entry */
+#define R_M32R_JMP_SLOT 52 /* Create PLT entry */
+#define R_M32R_RELATIVE 53 /* Adjust by program base */
+#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */
+#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */
+#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned
+ low */
+#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed
+ low */
+#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */
+#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to
+ GOT with unsigned low */
+#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to
+ GOT with signed low */
+#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to
+ GOT */
+#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT
+ with unsigned low */
+#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT
+ with signed low */
+#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */
+#define R_M32R_NUM 256 /* Keep this the last entry. */
+
+
+/* TILEPro relocations. */
+#define R_TILEPRO_NONE 0 /* No reloc */
+#define R_TILEPRO_32 1 /* Direct 32 bit */
+#define R_TILEPRO_16 2 /* Direct 16 bit */
+#define R_TILEPRO_8 3 /* Direct 8 bit */
+#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */
+#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */
+#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */
+#define R_TILEPRO_LO16 7 /* Low 16 bit */
+#define R_TILEPRO_HI16 8 /* High 16 bit */
+#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */
+#define R_TILEPRO_COPY 10 /* Copy relocation */
+#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */
+#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */
+#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */
+#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */
+#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */
+#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */
+#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */
+#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */
+#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */
+#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */
+#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */
+#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */
+#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */
+#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */
+#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */
+#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */
+#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */
+#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */
+#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */
+#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */
+#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */
+#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */
+#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */
+#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */
+#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */
+#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */
+#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */
+#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */
+#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */
+#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */
+#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */
+#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */
+#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */
+#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */
+#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */
+#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */
+#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */
+#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */
+#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */
+/* Relocs 56-59 are currently not defined. */
+#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */
+#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */
+#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */
+#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */
+#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */
+#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */
+#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */
+#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */
+#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */
+#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */
+#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */
+#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */
+#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */
+
+#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
+#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
+
+#define R_TILEPRO_NUM 130
+
+
+/* TILE-Gx relocations. */
+#define R_TILEGX_NONE 0 /* No reloc */
+#define R_TILEGX_64 1 /* Direct 64 bit */
+#define R_TILEGX_32 2 /* Direct 32 bit */
+#define R_TILEGX_16 3 /* Direct 16 bit */
+#define R_TILEGX_8 4 /* Direct 8 bit */
+#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */
+#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */
+#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */
+#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */
+#define R_TILEGX_HW0 9 /* hword 0 16-bit */
+#define R_TILEGX_HW1 10 /* hword 1 16-bit */
+#define R_TILEGX_HW2 11 /* hword 2 16-bit */
+#define R_TILEGX_HW3 12 /* hword 3 16-bit */
+#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */
+#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */
+#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */
+#define R_TILEGX_COPY 16 /* Copy relocation */
+#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */
+#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */
+#define R_TILEGX_RELATIVE 19 /* Adjust by program base */
+#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */
+#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */
+#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */
+#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */
+#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */
+#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */
+#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */
+#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */
+#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */
+#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */
+#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */
+#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */
+#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */
+#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */
+#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */
+#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */
+#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */
+#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */
+#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */
+#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */
+#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */
+#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */
+#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */
+#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */
+#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */
+#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */
+#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */
+#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */
+#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */
+#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */
+#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */
+#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */
+#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */
+#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */
+#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */
+#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */
+#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */
+#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */
+#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */
+#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */
+#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */
+#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */
+#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */
+#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */
+#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */
+#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */
+#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */
+/* Relocs 90-91 are currently not defined. */
+#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */
+#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */
+#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */
+#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */
+#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */
+#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */
+#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */
+#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */
+#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */
+#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */
+#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */
+#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */
+/* Relocs 104-105 are currently not defined. */
+#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */
+#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */
+#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */
+#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */
+#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */
+#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */
+#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */
+#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */
+#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */
+#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */
+#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */
+#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */
+#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */
+#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */
+#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */
+#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */
+
+#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */
+#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */
+
+#define R_TILEGX_NUM 130
+
+
+#endif /* elf.h */
diff --git a/src/libtcc.c b/src/libtcc.c
new file mode 100644
index 0000000..f600280
--- /dev/null
+++ b/src/libtcc.c
@@ -0,0 +1,2322 @@
+/*
+ * TCC - Tiny C Compiler
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+/********************************************************/
+/* global variables */
+
+/* use GNU C extensions */
+ST_DATA int gnu_ext = 1;
+
+/* use TinyCC extensions */
+ST_DATA int tcc_ext = 1;
+
+/* XXX: get rid of this ASAP */
+ST_DATA struct TCCState *tcc_state;
+
+/********************************************************/
+
+#ifdef ONE_SOURCE
+#include "tccpp.c"
+#include "tccgen.c"
+#include "tccelf.c"
+#include "tccrun.c"
+#ifdef TCC_TARGET_I386
+#include "x86/i386-gen.c"
+#endif
+#ifdef TCC_TARGET_ARM
+#include "arm/arm-gen.c"
+#endif
+#ifdef TCC_TARGET_ARM64
+#include "arm/arm64-gen.c"
+#endif
+#ifdef TCC_TARGET_C67
+#include "tms320c67/c67-gen.c"
+#endif
+#ifdef TCC_TARGET_X86_64
+#include "x86/x86_64-gen.c"
+#endif
+#ifdef CONFIG_TCC_ASM
+#include "tccasm.c"
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+#include "x86/i386-asm.c"
+#endif
+#endif
+#ifdef TCC_TARGET_COFF
+#include "tcccoff.c"
+#endif
+#ifdef TCC_TARGET_PE
+#include "tccpe.c"
+#endif
+#endif /* ONE_SOURCE */
+
+/********************************************************/
+#ifndef CONFIG_TCC_ASM
+ST_FUNC void asm_instr(void)
+{
+ tcc_error("inline asm() not supported");
+}
+ST_FUNC void asm_global_instr(void)
+{
+ tcc_error("inline asm() not supported");
+}
+#endif
+
+/********************************************************/
+#ifdef _WIN32
+static char *normalize_slashes(char *path)
+{
+ char *p;
+ for (p = path; *p; ++p)
+ if (*p == '\\')
+ *p = '/';
+ return path;
+}
+
+static HMODULE tcc_module;
+
+/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */
+static void tcc_set_lib_path_w32(TCCState *s)
+{
+ char path[1024], *p;
+ GetModuleFileNameA(tcc_module, path, sizeof path);
+ p = tcc_basename(normalize_slashes(strlwr(path)));
+ if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5))
+ p -= 5;
+ else if (p > path)
+ p--;
+ *p = 0;
+ tcc_set_lib_path(s, path);
+}
+
+#ifdef TCC_TARGET_PE
+static void tcc_add_systemdir(TCCState *s)
+{
+ char buf[1000];
+ GetSystemDirectory(buf, sizeof buf);
+ tcc_add_library_path(s, normalize_slashes(buf));
+}
+#endif
+
+#ifndef CONFIG_TCC_STATIC
+void dlclose(void *p)
+{
+ FreeLibrary((HMODULE)p);
+}
+#endif
+
+#ifdef LIBTCC_AS_DLL
+BOOL WINAPI DllMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
+{
+ if (DLL_PROCESS_ATTACH == dwReason)
+ tcc_module = hDll;
+ return TRUE;
+}
+#endif
+#endif
+
+/********************************************************/
+/* copy a string and truncate it. */
+PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s)
+{
+ char *q, *q_end;
+ int c;
+
+ if (buf_size > 0) {
+ q = buf;
+ q_end = buf + buf_size - 1;
+ while (q < q_end) {
+ c = *s++;
+ if (c == '\0')
+ break;
+ *q++ = c;
+ }
+ *q = '\0';
+ }
+ return buf;
+}
+
+/* strcat and truncate. */
+PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s)
+{
+ int len;
+ len = strlen(buf);
+ if (len < buf_size)
+ pstrcpy(buf + len, buf_size - len, s);
+ return buf;
+}
+
+PUB_FUNC char *pstrncpy(char *out, const char *in, size_t num)
+{
+ memcpy(out, in, num);
+ out[num] = '\0';
+ return out;
+}
+
+/* extract the basename of a file */
+PUB_FUNC char *tcc_basename(const char *name)
+{
+ char *p = strchr(name, 0);
+ while (p > name && !IS_DIRSEP(p[-1]))
+ --p;
+ return p;
+}
+
+/* extract extension part of a file
+ *
+ * (if no extension, return pointer to end-of-string)
+ */
+PUB_FUNC char *tcc_fileextension (const char *name)
+{
+ char *b = tcc_basename(name);
+ char *e = strrchr(b, '.');
+ return e ? e : strchr(b, 0);
+}
+
+/********************************************************/
+/* memory management */
+
+#undef free
+#undef malloc
+#undef realloc
+
+#ifndef MEM_DEBUG
+
+PUB_FUNC void tcc_free(void *ptr)
+{
+ free(ptr);
+}
+
+PUB_FUNC void *tcc_malloc(unsigned long size)
+{
+ void *ptr;
+ ptr = malloc(size);
+ if (!ptr && size)
+ tcc_error("memory full (malloc)");
+ return ptr;
+}
+
+PUB_FUNC void *tcc_mallocz(unsigned long size)
+{
+ void *ptr;
+ ptr = tcc_malloc(size);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size)
+{
+ void *ptr1;
+ ptr1 = realloc(ptr, size);
+ if (!ptr1 && size)
+ tcc_error("memory full (realloc)");
+ return ptr1;
+}
+
+PUB_FUNC char *tcc_strdup(const char *str)
+{
+ char *ptr;
+ ptr = tcc_malloc(strlen(str) + 1);
+ strcpy(ptr, str);
+ return ptr;
+}
+
+PUB_FUNC void tcc_memstats(int bench)
+{
+}
+
+#else
+
+#define MEM_DEBUG_MAGIC1 0xFEEDDEB1
+#define MEM_DEBUG_MAGIC2 0xFEEDDEB2
+#define MEM_DEBUG_FILE_LEN 15
+
+struct mem_debug_header {
+ size_t magic1;
+ size_t size;
+ struct mem_debug_header *prev;
+ struct mem_debug_header *next;
+ size_t line_num;
+ char file_name[MEM_DEBUG_FILE_LEN + 1];
+ size_t magic2;
+};
+
+typedef struct mem_debug_header mem_debug_header_t;
+
+static mem_debug_header_t *mem_debug_chain;
+static size_t mem_cur_size;
+static size_t mem_max_size;
+
+PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line)
+{
+ void *ptr;
+ mem_debug_header_t *header;
+
+ ptr = malloc(sizeof(mem_debug_header_t) + size);
+ if (!ptr)
+ tcc_error("memory full (malloc)");
+
+ mem_cur_size += size;
+ if (mem_cur_size > mem_max_size)
+ mem_max_size = mem_cur_size;
+
+ header = (mem_debug_header_t *)ptr;
+
+ header->magic1 = MEM_DEBUG_MAGIC1;
+ header->magic2 = MEM_DEBUG_MAGIC2;
+ header->size = size;
+ header->line_num = line;
+
+ strncpy(header->file_name, file, MEM_DEBUG_FILE_LEN);
+ header->file_name[MEM_DEBUG_FILE_LEN] = 0;
+
+ header->next = mem_debug_chain;
+ header->prev = NULL;
+
+ if (header->next)
+ header->next->prev = header;
+
+ mem_debug_chain = header;
+
+ ptr = (char *)ptr + sizeof(mem_debug_header_t);
+ return ptr;
+}
+
+PUB_FUNC void tcc_free_debug(void *ptr)
+{
+ mem_debug_header_t *header;
+
+ if (!ptr)
+ return;
+
+ ptr = (char *)ptr - sizeof(mem_debug_header_t);
+ header = (mem_debug_header_t *)ptr;
+ if (header->magic1 != MEM_DEBUG_MAGIC1 ||
+ header->magic2 != MEM_DEBUG_MAGIC2 ||
+ header->size == (size_t)-1 )
+ {
+ tcc_error("tcc_free check failed");
+ }
+
+ mem_cur_size -= header->size;
+ header->size = (size_t)-1;
+
+ if (header->next)
+ header->next->prev = header->prev;
+
+ if (header->prev)
+ header->prev->next = header->next;
+
+ if (header == mem_debug_chain)
+ mem_debug_chain = header->next;
+
+ free(ptr);
+}
+
+
+PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line)
+{
+ void *ptr;
+ ptr = tcc_malloc_debug(size,file,line);
+ memset(ptr, 0, size);
+ return ptr;
+}
+
+PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line)
+{
+ mem_debug_header_t *header;
+ int mem_debug_chain_update = 0;
+
+ if (!ptr) {
+ ptr = tcc_malloc_debug(size, file, line);
+ return ptr;
+ }
+
+ ptr = (char *)ptr - sizeof(mem_debug_header_t);
+ header = (mem_debug_header_t *)ptr;
+ if (header->magic1 != MEM_DEBUG_MAGIC1 ||
+ header->magic2 != MEM_DEBUG_MAGIC2 ||
+ header->size == (size_t)-1 )
+ {
+ check_error:
+ tcc_error("tcc_realloc check failed");
+ }
+
+ mem_debug_chain_update = (header == mem_debug_chain);
+
+ mem_cur_size -= header->size;
+ ptr = realloc(ptr, sizeof(mem_debug_header_t) + size);
+ if (!ptr)
+ tcc_error("memory full (realloc)");
+
+ header = (mem_debug_header_t *)ptr;
+ if (header->magic1 != MEM_DEBUG_MAGIC1 ||
+ header->magic2 != MEM_DEBUG_MAGIC2)
+ {
+ goto check_error;
+ }
+
+ mem_cur_size += size;
+ if (mem_cur_size > mem_max_size)
+ mem_max_size = mem_cur_size;
+
+ header->size = size;
+ if (header->next)
+ header->next->prev = header;
+
+ if (header->prev)
+ header->prev->next = header;
+
+ if (mem_debug_chain_update)
+ mem_debug_chain = header;
+
+ ptr = (char *)ptr + sizeof(mem_debug_header_t);
+ return ptr;
+}
+
+PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line)
+{
+ char *ptr;
+ ptr = tcc_malloc_debug(strlen(str) + 1, file, line);
+ strcpy(ptr, str);
+ return ptr;
+}
+
+PUB_FUNC void tcc_memstats(int bench)
+{
+ if (mem_cur_size) {
+ mem_debug_header_t *header = mem_debug_chain;
+
+ fprintf(stderr, "MEM_DEBUG: mem_leak= %d bytes, mem_max_size= %d bytes\n",
+ mem_cur_size, mem_max_size);
+
+ while (header) {
+ fprintf(stderr, " file %s, line %u: %u bytes\n",
+ header->file_name, header->line_num, header->size);
+ header = header->next;
+ }
+ }
+ else if (bench)
+ fprintf(stderr, "mem_max_size= %d bytes\n", mem_max_size);
+}
+
+#undef MEM_DEBUG_MAGIC1
+#undef MEM_DEBUG_MAGIC2
+#undef MEM_DEBUG_FILE_LEN
+
+#endif
+
+#define free(p) use_tcc_free(p)
+#define malloc(s) use_tcc_malloc(s)
+#define realloc(p, s) use_tcc_realloc(p, s)
+
+/********************************************************/
+/* dynarrays */
+
+ST_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data)
+{
+ int nb, nb_alloc;
+ void **pp;
+
+ nb = *nb_ptr;
+ pp = *ptab;
+ /* every power of two we double array size */
+ if ((nb & (nb - 1)) == 0) {
+ if (!nb)
+ nb_alloc = 1;
+ else
+ nb_alloc = nb * 2;
+ pp = tcc_realloc(pp, nb_alloc * sizeof(void *));
+ *ptab = pp;
+ }
+ pp[nb++] = data;
+ *nb_ptr = nb;
+}
+
+ST_FUNC void dynarray_reset(void *pp, int *n)
+{
+ void **p;
+ for (p = *(void***)pp; *n; ++p, --*n)
+ if (*p)
+ tcc_free(*p);
+ tcc_free(*(void**)pp);
+ *(void**)pp = NULL;
+}
+
+static void tcc_split_path(TCCState *s, void ***p_ary, int *p_nb_ary, const char *in)
+{
+ const char *p;
+ do {
+ int c;
+ CString str;
+
+ cstr_new(&str);
+ for (p = in; c = *p, c != '\0' && c != PATHSEP; ++p) {
+ if (c == '{' && p[1] && p[2] == '}') {
+ c = p[1], p += 2;
+ if (c == 'B')
+ cstr_cat(&str, s->tcc_lib_path);
+ } else {
+ cstr_ccat(&str, c);
+ }
+ }
+ cstr_ccat(&str, '\0');
+ dynarray_add(p_ary, p_nb_ary, str.data);
+ in = p+1;
+ } while (*p);
+}
+
+/********************************************************/
+
+ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags)
+{
+ Section *sec;
+
+ sec = tcc_mallocz(sizeof(Section) + strlen(name));
+ strcpy(sec->name, name);
+ sec->sh_type = sh_type;
+ sec->sh_flags = sh_flags;
+ switch(sh_type) {
+ case SHT_HASH:
+ case SHT_REL:
+ case SHT_RELA:
+ case SHT_DYNSYM:
+ case SHT_SYMTAB:
+ case SHT_DYNAMIC:
+ sec->sh_addralign = 4;
+ break;
+ case SHT_STRTAB:
+ sec->sh_addralign = 1;
+ break;
+ default:
+ sec->sh_addralign = 32; /* default conservative alignment */
+ break;
+ }
+
+ if (sh_flags & SHF_PRIVATE) {
+ dynarray_add((void ***)&s1->priv_sections, &s1->nb_priv_sections, sec);
+ } else {
+ sec->sh_num = s1->nb_sections;
+ dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec);
+ }
+
+ return sec;
+}
+
+static void free_section(Section *s)
+{
+ tcc_free(s->data);
+}
+
+/* realloc section and set its content to zero */
+ST_FUNC void section_realloc(Section *sec, unsigned long new_size)
+{
+ unsigned long size;
+ unsigned char *data;
+
+ size = sec->data_allocated;
+ if (size == 0)
+ size = 1;
+ while (size < new_size)
+ size = size * 2;
+ data = tcc_realloc(sec->data, size);
+ memset(data + sec->data_allocated, 0, size - sec->data_allocated);
+ sec->data = data;
+ sec->data_allocated = size;
+}
+
+/* reserve at least 'size' bytes in section 'sec' from
+ sec->data_offset. */
+ST_FUNC void *section_ptr_add(Section *sec, addr_t size)
+{
+ size_t offset, offset1;
+
+ offset = sec->data_offset;
+ offset1 = offset + size;
+ if (offset1 > sec->data_allocated)
+ section_realloc(sec, offset1);
+ sec->data_offset = offset1;
+ return sec->data + offset;
+}
+
+/* reserve at least 'size' bytes from section start */
+ST_FUNC void section_reserve(Section *sec, unsigned long size)
+{
+ if (size > sec->data_allocated)
+ section_realloc(sec, size);
+ if (size > sec->data_offset)
+ sec->data_offset = size;
+}
+
+/* return a reference to a section, and create it if it does not
+ exists */
+ST_FUNC Section *find_section(TCCState *s1, const char *name)
+{
+ Section *sec;
+ int i;
+ for(i = 1; i < s1->nb_sections; i++) {
+ sec = s1->sections[i];
+ if (!strcmp(name, sec->name))
+ return sec;
+ }
+ /* sections are created as PROGBITS */
+ return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
+}
+
+/* update sym->c so that it points to an external symbol in section
+ 'section' with value 'value' */
+ST_FUNC void put_extern_sym2(Sym *sym, Section *section,
+ addr_t value, unsigned long size,
+ int can_add_underscore)
+{
+ int sym_type, sym_bind, sh_num, info, other;
+ ElfW(Sym) *esym;
+ const char *name;
+ char buf1[256];
+
+#ifdef CONFIG_TCC_BCHECK
+ char buf[32];
+#endif
+
+ if (section == NULL)
+ sh_num = SHN_UNDEF;
+ else if (section == SECTION_ABS)
+ sh_num = SHN_ABS;
+ else
+ sh_num = section->sh_num;
+
+ if ((sym->type.t & VT_BTYPE) == VT_FUNC) {
+ sym_type = STT_FUNC;
+ } else if ((sym->type.t & VT_BTYPE) == VT_VOID) {
+ sym_type = STT_NOTYPE;
+ } else {
+ sym_type = STT_OBJECT;
+ }
+
+ if (sym->type.t & VT_STATIC)
+ sym_bind = STB_LOCAL;
+ else {
+ if (sym->type.t & VT_WEAK)
+ sym_bind = STB_WEAK;
+ else
+ sym_bind = STB_GLOBAL;
+ }
+
+ if (!sym->c) {
+ name = get_tok_str(sym->v, NULL);
+#ifdef CONFIG_TCC_BCHECK
+ if (tcc_state->do_bounds_check) {
+ /* XXX: avoid doing that for statics ? */
+ /* if bound checking is activated, we change some function
+ names by adding the "__bound" prefix */
+ switch(sym->v) {
+#ifdef TCC_TARGET_PE
+ /* XXX: we rely only on malloc hooks */
+ case TOK_malloc:
+ case TOK_free:
+ case TOK_realloc:
+ case TOK_memalign:
+ case TOK_calloc:
+#endif
+ case TOK_memcpy:
+ case TOK_memmove:
+ case TOK_memset:
+ case TOK_strlen:
+ case TOK_strcpy:
+ case TOK_alloca:
+ strcpy(buf, "__bound_");
+ strcat(buf, name);
+ name = buf;
+ break;
+ }
+ }
+#endif
+ other = 0;
+
+#ifdef TCC_TARGET_PE
+ if (sym->type.t & VT_EXPORT)
+ other |= ST_PE_EXPORT;
+ if (sym_type == STT_FUNC && sym->type.ref) {
+ Sym *ref = sym->type.ref;
+ if (ref->a.func_export)
+ other |= ST_PE_EXPORT;
+ if (ref->a.func_call == FUNC_STDCALL && can_add_underscore) {
+ sprintf(buf1, "_%s@%d", name, ref->a.func_args * PTR_SIZE);
+ name = buf1;
+ other |= ST_PE_STDCALL;
+ can_add_underscore = 0;
+ }
+ } else {
+ if (find_elf_sym(tcc_state->dynsymtab_section, name))
+ other |= ST_PE_IMPORT;
+ if (sym->type.t & VT_IMPORT)
+ other |= ST_PE_IMPORT;
+ }
+#else
+ if (! (sym->type.t & VT_STATIC))
+ other = (sym->type.t & VT_VIS_MASK) >> VT_VIS_SHIFT;
+#endif
+ if (tcc_state->leading_underscore && can_add_underscore) {
+ buf1[0] = '_';
+ pstrcpy(buf1 + 1, sizeof(buf1) - 1, name);
+ name = buf1;
+ }
+ if (sym->asm_label) {
+ name = sym->asm_label;
+ }
+ info = ELFW(ST_INFO)(sym_bind, sym_type);
+ sym->c = add_elf_sym(symtab_section, value, size, info, other, sh_num, name);
+ } else {
+ esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+ esym->st_value = value;
+ esym->st_size = size;
+ esym->st_shndx = sh_num;
+ }
+}
+
+ST_FUNC void put_extern_sym(Sym *sym, Section *section,
+ addr_t value, unsigned long size)
+{
+ put_extern_sym2(sym, section, value, size, 1);
+}
+
+/* add a new relocation entry to symbol 'sym' in section 's' */
+ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type,
+ addr_t addend)
+{
+ int c = 0;
+ if (sym) {
+ if (0 == sym->c)
+ put_extern_sym(sym, NULL, 0, 0);
+ c = sym->c;
+ }
+ /* now we can add ELF relocation info */
+ put_elf_reloca(symtab_section, s, offset, type, c, addend);
+}
+
+ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type)
+{
+ greloca(s, sym, offset, type, 0);
+}
+
+/********************************************************/
+
+static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap)
+{
+ int len;
+ len = strlen(buf);
+ vsnprintf(buf + len, buf_size - len, fmt, ap);
+}
+
+static void strcat_printf(char *buf, int buf_size, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ strcat_vprintf(buf, buf_size, fmt, ap);
+ va_end(ap);
+}
+
+static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap)
+{
+ char buf[2048];
+ BufferedFile **pf, *f;
+
+ buf[0] = '\0';
+ /* use upper file if inline ":asm:" or token ":paste:" */
+ for (f = file; f && f->filename[0] == ':'; f = f->prev)
+ ;
+ if (f) {
+ for(pf = s1->include_stack; pf < s1->include_stack_ptr; pf++)
+ strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n",
+ (*pf)->filename, (*pf)->line_num);
+ if (f->line_num > 0) {
+ strcat_printf(buf, sizeof(buf), "%s:%d: ",
+ f->filename, f->line_num);
+ } else {
+ strcat_printf(buf, sizeof(buf), "%s: ",
+ f->filename);
+ }
+ } else {
+ strcat_printf(buf, sizeof(buf), "tcc: ");
+ }
+ if (is_warning)
+ strcat_printf(buf, sizeof(buf), "warning: ");
+ else
+ strcat_printf(buf, sizeof(buf), "error: ");
+ strcat_vprintf(buf, sizeof(buf), fmt, ap);
+
+ if (!s1->error_func) {
+ /* default case: stderr */
+ if (s1->ppfp) /* print a newline during tcc -E */
+ fprintf(s1->ppfp, "\n"), fflush(s1->ppfp);
+ fprintf(stderr, "%s\n", buf);
+ fflush(stderr); /* print error/warning now (win32) */
+ } else {
+ s1->error_func(s1->error_opaque, buf);
+ }
+ if (!is_warning || s1->warn_error)
+ s1->nb_errors++;
+}
+
+LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
+ void (*error_func)(void *opaque, const char *msg))
+{
+ s->error_opaque = error_opaque;
+ s->error_func = error_func;
+}
+
+/* error without aborting current compilation */
+PUB_FUNC void tcc_error_noabort(const char *fmt, ...)
+{
+ TCCState *s1 = tcc_state;
+ va_list ap;
+
+ va_start(ap, fmt);
+ error1(s1, 0, fmt, ap);
+ va_end(ap);
+}
+
+PUB_FUNC void tcc_error(const char *fmt, ...)
+{
+ TCCState *s1 = tcc_state;
+ va_list ap;
+
+ va_start(ap, fmt);
+ error1(s1, 0, fmt, ap);
+ va_end(ap);
+ /* better than nothing: in some cases, we accept to handle errors */
+ if (s1->error_set_jmp_enabled) {
+ longjmp(s1->error_jmp_buf, 1);
+ } else {
+ /* XXX: eliminate this someday */
+ exit(1);
+ }
+}
+
+PUB_FUNC void tcc_warning(const char *fmt, ...)
+{
+ TCCState *s1 = tcc_state;
+ va_list ap;
+
+ if (s1->warn_none)
+ return;
+
+ va_start(ap, fmt);
+ error1(s1, 1, fmt, ap);
+ va_end(ap);
+}
+
+/********************************************************/
+/* I/O layer */
+
+ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen)
+{
+ BufferedFile *bf;
+ int buflen = initlen ? initlen : IO_BUF_SIZE;
+
+ bf = tcc_mallocz(sizeof(BufferedFile) + buflen);
+ bf->buf_ptr = bf->buffer;
+ bf->buf_end = bf->buffer + initlen;
+ bf->buf_end[0] = CH_EOB; /* put eob symbol */
+ pstrcpy(bf->filename, sizeof(bf->filename), filename);
+#ifdef _WIN32
+ normalize_slashes(bf->filename);
+#endif
+ bf->line_num = 1;
+ bf->ifdef_stack_ptr = s1->ifdef_stack_ptr;
+ bf->fd = -1;
+ bf->prev = file;
+ file = bf;
+}
+
+ST_FUNC void tcc_close(void)
+{
+ BufferedFile *bf = file;
+ if (bf->fd > 0) {
+ close(bf->fd);
+ total_lines += bf->line_num;
+ }
+ file = bf->prev;
+ tcc_free(bf);
+}
+
+ST_FUNC int tcc_open(TCCState *s1, const char *filename)
+{
+ int fd;
+ if (strcmp(filename, "-") == 0)
+ fd = 0, filename = "<stdin>";
+ else
+ fd = open(filename, O_RDONLY | O_BINARY);
+ if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3)
+ printf("%s %*s%s\n", fd < 0 ? "nf":"->",
+ (int)(s1->include_stack_ptr - s1->include_stack), "", filename);
+ if (fd < 0)
+ return -1;
+
+ tcc_open_bf(s1, filename, 0);
+ file->fd = fd;
+ return fd;
+}
+
+/* compile the C file opened in 'file'. Return non zero if errors. */
+static int tcc_compile(TCCState *s1)
+{
+ Sym *define_start;
+ char buf[512];
+ volatile int section_sym;
+
+#ifdef INC_DEBUG
+ printf("%s: **** new file\n", file->filename);
+#endif
+ preprocess_init(s1);
+
+ cur_text_section = NULL;
+ funcname = "";
+ anon_sym = SYM_FIRST_ANOM;
+
+ /* file info: full path + filename */
+ section_sym = 0; /* avoid warning */
+ if (s1->do_debug) {
+ section_sym = put_elf_sym(symtab_section, 0, 0,
+ ELFW(ST_INFO)(STB_LOCAL, STT_SECTION), 0,
+ text_section->sh_num, NULL);
+ getcwd(buf, sizeof(buf));
+#ifdef _WIN32
+ normalize_slashes(buf);
+#endif
+ pstrcat(buf, sizeof(buf), "/");
+ put_stabs_r(buf, N_SO, 0, 0,
+ text_section->data_offset, text_section, section_sym);
+ put_stabs_r(file->filename, N_SO, 0, 0,
+ text_section->data_offset, text_section, section_sym);
+ }
+ /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
+ symbols can be safely used */
+ put_elf_sym(symtab_section, 0, 0,
+ ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
+ SHN_ABS, file->filename);
+
+ /* define some often used types */
+ int_type.t = VT_INT;
+
+ char_pointer_type.t = VT_BYTE;
+ mk_pointer(&char_pointer_type);
+
+#if PTR_SIZE == 4
+ size_type.t = VT_INT;
+#else
+ size_type.t = VT_LLONG;
+#endif
+
+ func_old_type.t = VT_FUNC;
+ func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD);
+#ifdef TCC_TARGET_ARM
+ arm_init(s1);
+#endif
+
+#if 0
+ /* define 'void *alloca(unsigned int)' builtin function */
+ {
+ Sym *s1;
+
+ p = anon_sym++;
+ sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW);
+ s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0);
+ s1->next = NULL;
+ sym->next = s1;
+ sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0);
+ }
+#endif
+
+ define_start = define_stack;
+ nocode_wanted = 1;
+
+ if (setjmp(s1->error_jmp_buf) == 0) {
+ s1->nb_errors = 0;
+ s1->error_set_jmp_enabled = 1;
+
+ ch = file->buf_ptr[0];
+ tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
+ parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_TOK_STR;
+ next();
+ decl(VT_CONST);
+ if (tok != TOK_EOF)
+ expect("declaration");
+ check_vstack();
+
+ /* end of translation unit info */
+ if (s1->do_debug) {
+ put_stabs_r(NULL, N_SO, 0, 0,
+ text_section->data_offset, text_section, section_sym);
+ }
+ }
+
+ s1->error_set_jmp_enabled = 0;
+
+ /* reset define stack, but leave -Dsymbols (may be incorrect if
+ they are undefined) */
+ free_defines(define_start);
+
+ gen_inline_functions();
+
+ sym_pop(&global_stack, NULL);
+ sym_pop(&local_stack, NULL);
+
+ return s1->nb_errors != 0 ? -1 : 0;
+}
+
+LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str)
+{
+ int len, ret;
+
+ len = strlen(str);
+ tcc_open_bf(s, "<string>", len);
+ memcpy(file->buffer, str, len);
+ ret = tcc_compile(s);
+ tcc_close();
+ return ret;
+}
+
+/* define a preprocessor symbol. A value can also be provided with the '=' operator */
+LIBTCCAPI void tcc_define_symbol(TCCState *s1, const char *sym, const char *value)
+{
+ int len1, len2;
+ /* default value */
+ if (!value)
+ value = "1";
+ len1 = strlen(sym);
+ len2 = strlen(value);
+
+ /* init file structure */
+ tcc_open_bf(s1, "<define>", len1 + len2 + 1);
+ memcpy(file->buffer, sym, len1);
+ file->buffer[len1] = ' ';
+ memcpy(file->buffer + len1 + 1, value, len2);
+
+ /* parse with define parser */
+ ch = file->buf_ptr[0];
+ next_nomacro();
+ parse_define();
+
+ tcc_close();
+}
+
+/* undefine a preprocessor symbol */
+LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym)
+{
+ TokenSym *ts;
+ Sym *s;
+ ts = tok_alloc(sym, strlen(sym));
+ s = define_find(ts->tok);
+ /* undefine symbol by putting an invalid name */
+ if (s)
+ define_undef(s);
+}
+
+/* cleanup all static data used during compilation */
+static void tcc_cleanup(void)
+{
+ if (NULL == tcc_state)
+ return;
+ tcc_state = NULL;
+
+ preprocess_delete();
+
+ /* free sym_pools */
+ dynarray_reset(&sym_pools, &nb_sym_pools);
+ /* string buffer */
+ cstr_free(&tokcstr);
+ /* reset symbol stack */
+ sym_free_first = NULL;
+}
+
+LIBTCCAPI TCCState *tcc_new(void)
+{
+ TCCState *s;
+ char buffer[100];
+ int a,b,c;
+
+ tcc_cleanup();
+
+ s = tcc_mallocz(sizeof(TCCState));
+ if (!s)
+ return NULL;
+ tcc_state = s;
+#ifdef _WIN32
+ tcc_set_lib_path_w32(s);
+#else
+ tcc_set_lib_path(s, CONFIG_TCCDIR);
+#endif
+ s->output_type = 0;
+ preprocess_new();
+ s->include_stack_ptr = s->include_stack;
+
+ /* we add dummy defines for some special macros to speed up tests
+ and to have working defined() */
+ define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL);
+ define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL);
+ define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL);
+ define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL);
+
+ /* define __TINYC__ 92X */
+ sscanf(TCC_VERSION, "%d.%d.%d", &a, &b, &c);
+ sprintf(buffer, "%d", a*10000 + b*100 + c);
+ tcc_define_symbol(s, "__TINYC__", buffer);
+
+ /* standard defines */
+ tcc_define_symbol(s, "__STDC__", NULL);
+ tcc_define_symbol(s, "__STDC_VERSION__", "199901L");
+ tcc_define_symbol(s, "__STDC_HOSTED__", NULL);
+
+ /* target defines */
+#if defined(TCC_TARGET_I386)
+ tcc_define_symbol(s, "__i386__", NULL);
+ tcc_define_symbol(s, "__i386", NULL);
+ tcc_define_symbol(s, "i386", NULL);
+#elif defined(TCC_TARGET_X86_64)
+ tcc_define_symbol(s, "__x86_64__", NULL);
+#elif defined(TCC_TARGET_ARM)
+ tcc_define_symbol(s, "__ARM_ARCH_4__", NULL);
+ tcc_define_symbol(s, "__arm_elf__", NULL);
+ tcc_define_symbol(s, "__arm_elf", NULL);
+ tcc_define_symbol(s, "arm_elf", NULL);
+ tcc_define_symbol(s, "__arm__", NULL);
+ tcc_define_symbol(s, "__arm", NULL);
+ tcc_define_symbol(s, "arm", NULL);
+ tcc_define_symbol(s, "__APCS_32__", NULL);
+ tcc_define_symbol(s, "__ARMEL__", NULL);
+#if defined(TCC_ARM_EABI)
+ tcc_define_symbol(s, "__ARM_EABI__", NULL);
+#endif
+#if defined(TCC_ARM_HARDFLOAT)
+ s->float_abi = ARM_HARD_FLOAT;
+ tcc_define_symbol(s, "__ARM_PCS_VFP", NULL);
+#else
+ s->float_abi = ARM_SOFTFP_FLOAT;
+#endif
+#elif defined(TCC_TARGET_ARM64)
+ tcc_define_symbol(s, "__aarch64__", NULL);
+#endif
+
+#ifdef TCC_TARGET_PE
+ tcc_define_symbol(s, "_WIN32", NULL);
+# ifdef TCC_TARGET_X86_64
+ tcc_define_symbol(s, "_WIN64", NULL);
+# endif
+#else
+ tcc_define_symbol(s, "__unix__", NULL);
+ tcc_define_symbol(s, "__unix", NULL);
+ tcc_define_symbol(s, "unix", NULL);
+# if defined(__linux)
+ tcc_define_symbol(s, "__linux__", NULL);
+ tcc_define_symbol(s, "__linux", NULL);
+# endif
+# if defined(__FreeBSD__)
+# define str(s) #s
+ tcc_define_symbol(s, "__FreeBSD__", str( __FreeBSD__));
+# undef str
+# endif
+# if defined(__FreeBSD_kernel__)
+ tcc_define_symbol(s, "__FreeBSD_kernel__", NULL);
+# endif
+#endif
+
+ /* TinyCC & gcc defines */
+#if defined TCC_TARGET_PE && defined TCC_TARGET_X86_64
+ tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long long");
+ tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long long");
+#else
+ tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned long");
+ tcc_define_symbol(s, "__PTRDIFF_TYPE__", "long");
+#endif
+
+#ifdef TCC_TARGET_PE
+ tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short");
+ tcc_define_symbol(s, "__WINT_TYPE__", "unsigned short");
+#else
+ tcc_define_symbol(s, "__WCHAR_TYPE__", "int");
+ /* wint_t is unsigned int by default, but (signed) int on BSDs
+ and unsigned short on windows. Other OSes might have still
+ other conventions, sigh. */
+#if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
+ tcc_define_symbol(s, "__WINT_TYPE__", "int");
+#else
+ tcc_define_symbol(s, "__WINT_TYPE__", "unsigned int");
+#endif
+#endif
+
+#ifndef TCC_TARGET_PE
+ /* glibc defines */
+ tcc_define_symbol(s, "__REDIRECT(name, proto, alias)", "name proto __asm__ (#alias)");
+ tcc_define_symbol(s, "__REDIRECT_NTH(name, proto, alias)", "name proto __asm__ (#alias) __THROW");
+ /* paths for crt objects */
+ tcc_split_path(s, (void ***)&s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX);
+#endif
+
+ /* no section zero */
+ dynarray_add((void ***)&s->sections, &s->nb_sections, NULL);
+
+ /* create standard sections */
+ text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR);
+ data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
+ bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE);
+
+ /* symbols are always generated for linking stage */
+ symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0,
+ ".strtab",
+ ".hashtab", SHF_PRIVATE);
+ strtab_section = symtab_section->link;
+ s->symtab = symtab_section;
+
+ /* private symbol table for dynamic symbols */
+ s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE,
+ ".dynstrtab",
+ ".dynhashtab", SHF_PRIVATE);
+ s->alacarte_link = 1;
+ s->nocommon = 1;
+ s->warn_implicit_function_declaration = 1;
+
+#ifdef CHAR_IS_UNSIGNED
+ s->char_is_unsigned = 1;
+#endif
+ /* enable this if you want symbols with leading underscore on windows: */
+#if 0 /* def TCC_TARGET_PE */
+ s->leading_underscore = 1;
+#endif
+#ifdef TCC_TARGET_I386
+ s->seg_size = 32;
+#endif
+#ifdef TCC_IS_NATIVE
+ s->runtime_main = "main";
+#endif
+ return s;
+}
+
+LIBTCCAPI void tcc_delete(TCCState *s1)
+{
+ int i;
+ int bench = s1->do_bench;
+
+ tcc_cleanup();
+
+ /* close a preprocessor output */
+ if (s1->ppfp && s1->ppfp != stdout)
+ fclose(s1->ppfp);
+
+ /* free all sections */
+ for(i = 1; i < s1->nb_sections; i++)
+ free_section(s1->sections[i]);
+ dynarray_reset(&s1->sections, &s1->nb_sections);
+
+ for(i = 0; i < s1->nb_priv_sections; i++)
+ free_section(s1->priv_sections[i]);
+ dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections);
+
+ /* free any loaded DLLs */
+#ifdef TCC_IS_NATIVE
+ for ( i = 0; i < s1->nb_loaded_dlls; i++) {
+ DLLReference *ref = s1->loaded_dlls[i];
+ if ( ref->handle )
+ dlclose(ref->handle);
+ }
+#endif
+
+ /* free loaded dlls array */
+ dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls);
+
+ /* free library paths */
+ dynarray_reset(&s1->library_paths, &s1->nb_library_paths);
+ dynarray_reset(&s1->crt_paths, &s1->nb_crt_paths);
+
+ /* free include paths */
+ dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes);
+ dynarray_reset(&s1->include_paths, &s1->nb_include_paths);
+ dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths);
+
+ tcc_free(s1->tcc_lib_path);
+ tcc_free(s1->soname);
+ tcc_free(s1->rpath);
+ tcc_free(s1->init_symbol);
+ tcc_free(s1->fini_symbol);
+ tcc_free(s1->outfile);
+ tcc_free(s1->deps_outfile);
+ dynarray_reset(&s1->files, &s1->nb_files);
+ dynarray_reset(&s1->target_deps, &s1->nb_target_deps);
+ dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs);
+
+#ifdef TCC_IS_NATIVE
+# ifdef HAVE_SELINUX
+ munmap (s1->write_mem, s1->mem_size);
+ munmap (s1->runtime_mem, s1->mem_size);
+# else
+ tcc_free(s1->runtime_mem);
+# endif
+#endif
+
+ tcc_free(s1->sym_attrs);
+ tcc_free(s1);
+ tcc_memstats(bench);
+}
+
+LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname)
+{
+ tcc_split_path(s, (void ***)&s->include_paths, &s->nb_include_paths, pathname);
+ return 0;
+}
+
+LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname)
+{
+ tcc_split_path(s, (void ***)&s->sysinclude_paths, &s->nb_sysinclude_paths, pathname);
+ return 0;
+}
+
+ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags, int filetype)
+{
+ ElfW(Ehdr) ehdr;
+ int fd, ret, size;
+
+ parse_flags = 0;
+#ifdef CONFIG_TCC_ASM
+ /* if .S file, define __ASSEMBLER__ like gcc does */
+ if ((filetype == TCC_FILETYPE_ASM) || (filetype == TCC_FILETYPE_ASM_PP)) {
+ tcc_define_symbol(s1, "__ASSEMBLER__", NULL);
+ parse_flags = PARSE_FLAG_ASM_FILE;
+ }
+#endif
+
+ /* open the file */
+ ret = tcc_open(s1, filename);
+ if (ret < 0) {
+ if (flags & AFF_PRINT_ERROR)
+ tcc_error_noabort("file '%s' not found", filename);
+ return ret;
+ }
+
+ /* update target deps */
+ dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps,
+ tcc_strdup(filename));
+
+ if (flags & AFF_PREPROCESS) {
+ ret = tcc_preprocess(s1);
+ goto the_end;
+ }
+
+ if (filetype == TCC_FILETYPE_C) {
+ /* C file assumed */
+ ret = tcc_compile(s1);
+ goto the_end;
+ }
+
+#ifdef CONFIG_TCC_ASM
+ if (filetype == TCC_FILETYPE_ASM_PP) {
+ /* non preprocessed assembler */
+ ret = tcc_assemble(s1, 1);
+ goto the_end;
+ }
+
+ if (filetype == TCC_FILETYPE_ASM) {
+ /* preprocessed assembler */
+ ret = tcc_assemble(s1, 0);
+ goto the_end;
+ }
+#endif
+
+ fd = file->fd;
+ /* assume executable format: auto guess file type */
+ size = read(fd, &ehdr, sizeof(ehdr));
+ lseek(fd, 0, SEEK_SET);
+ if (size <= 0) {
+ tcc_error_noabort("could not read header");
+ goto the_end;
+ }
+
+ if (size == sizeof(ehdr) &&
+ ehdr.e_ident[0] == ELFMAG0 &&
+ ehdr.e_ident[1] == ELFMAG1 &&
+ ehdr.e_ident[2] == ELFMAG2 &&
+ ehdr.e_ident[3] == ELFMAG3) {
+
+ /* do not display line number if error */
+ file->line_num = 0;
+ if (ehdr.e_type == ET_REL) {
+ ret = tcc_load_object_file(s1, fd, 0);
+ goto the_end;
+
+ }
+#ifndef TCC_TARGET_PE
+ if (ehdr.e_type == ET_DYN) {
+ if (s1->output_type == TCC_OUTPUT_MEMORY) {
+#ifdef TCC_IS_NATIVE
+ void *h;
+ h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY);
+ if (h)
+#endif
+ ret = 0;
+ } else {
+ ret = tcc_load_dll(s1, fd, filename,
+ (flags & AFF_REFERENCED_DLL) != 0);
+ }
+ goto the_end;
+ }
+#endif
+ tcc_error_noabort("unrecognized ELF file");
+ goto the_end;
+ }
+
+ if (memcmp((char *)&ehdr, ARMAG, 8) == 0) {
+ file->line_num = 0; /* do not display line number if error */
+ ret = tcc_load_archive(s1, fd);
+ goto the_end;
+ }
+
+#ifdef TCC_TARGET_COFF
+ if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) {
+ ret = tcc_load_coff(s1, fd);
+ goto the_end;
+ }
+#endif
+
+#ifdef TCC_TARGET_PE
+ ret = pe_load_file(s1, filename, fd);
+#else
+ /* as GNU ld, consider it is an ld script if not recognized */
+ ret = tcc_load_ldscript(s1);
+#endif
+ if (ret < 0)
+ tcc_error_noabort("unrecognized file type");
+
+the_end:
+ tcc_close();
+ return ret;
+}
+
+LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename, int filetype)
+{
+ if (s->output_type == TCC_OUTPUT_PREPROCESS)
+ return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS, filetype);
+ else
+ return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR, filetype);
+}
+
+LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname)
+{
+ tcc_split_path(s, (void ***)&s->library_paths, &s->nb_library_paths, pathname);
+ return 0;
+}
+
+static int tcc_add_library_internal(TCCState *s, const char *fmt,
+ const char *filename, int flags, char **paths, int nb_paths)
+{
+ char buf[1024];
+ int i;
+
+ for(i = 0; i < nb_paths; i++) {
+ snprintf(buf, sizeof(buf), fmt, paths[i], filename);
+ if (tcc_add_file_internal(s, buf, flags, TCC_FILETYPE_BINARY) == 0)
+ return 0;
+ }
+ return -1;
+}
+
+#ifndef TCC_TARGET_PE
+/* find and load a dll. Return non zero if not found */
+/* XXX: add '-rpath' option support ? */
+ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags)
+{
+ return tcc_add_library_internal(s, "%s/%s", filename, flags,
+ s->library_paths, s->nb_library_paths);
+}
+#endif
+
+ST_FUNC int tcc_add_crt(TCCState *s, const char *filename)
+{
+ if (-1 == tcc_add_library_internal(s, "%s/%s",
+ filename, 0, s->crt_paths, s->nb_crt_paths))
+ tcc_error_noabort("file '%s' not found", filename);
+ return 0;
+}
+
+/* the library name is the same as the argument of the '-l' option */
+LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname)
+{
+#ifdef TCC_TARGET_PE
+ const char *libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL };
+ const char **pp = s->static_link ? libs + 4 : libs;
+#else
+ const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL };
+ const char **pp = s->static_link ? libs + 1 : libs;
+#endif
+ while (*pp) {
+ if (0 == tcc_add_library_internal(s, *pp,
+ libraryname, 0, s->library_paths, s->nb_library_paths))
+ return 0;
+ ++pp;
+ }
+ return -1;
+}
+
+PUB_FUNC int tcc_add_library_err(TCCState *s, const char *libname)
+{
+ int ret = tcc_add_library(s, libname);
+ if (ret < 0)
+ tcc_error_noabort("cannot find library 'lib%s'", libname);
+ return ret;
+}
+
+/* habdle #pragma comment(lib,) */
+ST_FUNC void tcc_add_pragma_libs(TCCState *s1)
+{
+ int i;
+ for (i = 0; i < s1->nb_pragma_libs; i++)
+ tcc_add_library_err(s1, s1->pragma_libs[i]);
+}
+
+LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val)
+{
+#ifdef TCC_TARGET_PE
+ /* On x86_64 'val' might not be reachable with a 32bit offset.
+ So it is handled here as if it were in a DLL. */
+ pe_putimport(s, 0, name, (uintptr_t)val);
+#else
+ add_elf_sym(symtab_section, (uintptr_t)val, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ SHN_ABS, name);
+#endif
+ return 0;
+}
+
+LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type)
+{
+ s->output_type = output_type;
+
+ if (!s->nostdinc) {
+ /* default include paths */
+ /* -isystem paths have already been handled */
+ tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS);
+ }
+
+ /* if bound checking, then add corresponding sections */
+#ifdef CONFIG_TCC_BCHECK
+ if (s->do_bounds_check) {
+ /* define symbol */
+ tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL);
+ /* create bounds sections */
+ bounds_section = new_section(s, ".bounds",
+ SHT_PROGBITS, SHF_ALLOC);
+ lbounds_section = new_section(s, ".lbounds",
+ SHT_PROGBITS, SHF_ALLOC);
+ }
+#endif
+
+ if (s->char_is_unsigned) {
+ tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL);
+ }
+
+ /* add debug sections */
+ if (s->do_debug) {
+ /* stab symbols */
+ stab_section = new_section(s, ".stab", SHT_PROGBITS, 0);
+ stab_section->sh_entsize = sizeof(Stab_Sym);
+ stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0);
+ put_elf_str(stabstr_section, "");
+ stab_section->link = stabstr_section;
+ /* put first entry */
+ put_stabs("", 0, 0, 0, 0);
+ }
+
+ tcc_add_library_path(s, CONFIG_TCC_LIBPATHS);
+#ifdef TCC_TARGET_PE
+# ifdef _WIN32
+ tcc_add_systemdir(s);
+# endif
+#else
+ /* add libc crt1/crti objects */
+ if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) &&
+ !s->nostdlib) {
+ if (output_type != TCC_OUTPUT_DLL)
+ tcc_add_crt(s, "crt1.o");
+ tcc_add_crt(s, "crti.o");
+ }
+#endif
+
+#ifdef CONFIG_TCC_BCHECK
+ if (s->do_bounds_check && (output_type == TCC_OUTPUT_EXE))
+ {
+ /* force a bcheck.o linking */
+ addr_t func = TOK___bound_init;
+ Sym *sym = external_global_sym(func, &func_old_type, 0);
+ if (!sym->c)
+ put_extern_sym(sym, NULL, 0, 0);
+ }
+#endif
+
+ if (s->output_type == TCC_OUTPUT_PREPROCESS)
+ print_defines();
+
+ return 0;
+}
+
+LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path)
+{
+ tcc_free(s->tcc_lib_path);
+ s->tcc_lib_path = tcc_strdup(path);
+}
+
+#define WD_ALL 0x0001 /* warning is activated when using -Wall */
+#define FD_INVERT 0x0002 /* invert value before storing */
+
+typedef struct FlagDef {
+ uint16_t offset;
+ uint16_t flags;
+ const char *name;
+} FlagDef;
+
+static const FlagDef warning_defs[] = {
+ { offsetof(TCCState, warn_unsupported), 0, "unsupported" },
+ { offsetof(TCCState, warn_write_strings), 0, "write-strings" },
+ { offsetof(TCCState, warn_error), 0, "error" },
+ { offsetof(TCCState, warn_implicit_function_declaration), WD_ALL,
+ "implicit-function-declaration" },
+};
+
+ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags,
+ const char *name, int value)
+{
+ int i;
+ const FlagDef *p;
+ const char *r;
+
+ r = name;
+ if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') {
+ r += 3;
+ value = !value;
+ }
+ for(i = 0, p = flags; i < nb_flags; i++, p++) {
+ if (!strcmp(r, p->name))
+ goto found;
+ }
+ return -1;
+ found:
+ if (p->flags & FD_INVERT)
+ value = !value;
+ *(int *)((uint8_t *)s + p->offset) = value;
+ return 0;
+}
+
+/* set/reset a warning */
+static int tcc_set_warning(TCCState *s, const char *warning_name, int value)
+{
+ int i;
+ const FlagDef *p;
+
+ if (!strcmp(warning_name, "all")) {
+ for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) {
+ if (p->flags & WD_ALL)
+ *(int *)((uint8_t *)s + p->offset) = 1;
+ }
+ return 0;
+ } else {
+ return set_flag(s, warning_defs, countof(warning_defs),
+ warning_name, value);
+ }
+}
+
+static const FlagDef flag_defs[] = {
+ { offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
+ { offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
+ { offsetof(TCCState, nocommon), FD_INVERT, "common" },
+ { offsetof(TCCState, leading_underscore), 0, "leading-underscore" },
+ { offsetof(TCCState, ms_extensions), 0, "ms-extensions" },
+ { offsetof(TCCState, old_struct_init_code), 0, "old-struct-init-code" },
+ { offsetof(TCCState, dollars_in_identifiers), 0, "dollars-in-identifiers" },
+};
+
+/* set/reset a flag */
+static int tcc_set_flag(TCCState *s, const char *flag_name, int value)
+{
+ return set_flag(s, flag_defs, countof(flag_defs),
+ flag_name, value);
+}
+
+
+static int strstart(const char *val, const char **str)
+{
+ const char *p, *q;
+ p = *str;
+ q = val;
+ while (*q) {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+ *str = p;
+ return 1;
+}
+
+/* Like strstart, but automatically takes into account that ld options can
+ *
+ * - start with double or single dash (e.g. '--soname' or '-soname')
+ * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so'
+ * or '-Wl,-soname=x.so')
+ *
+ * you provide `val` always in 'option[=]' form (no leading -)
+ */
+static int link_option(const char *str, const char *val, const char **ptr)
+{
+ const char *p, *q;
+
+ /* there should be 1 or 2 dashes */
+ if (*str++ != '-')
+ return 0;
+ if (*str == '-')
+ str++;
+
+ /* then str & val should match (potentialy up to '=') */
+ p = str;
+ q = val;
+
+ while (*q != '\0' && *q != '=') {
+ if (*p != *q)
+ return 0;
+ p++;
+ q++;
+ }
+
+ /* '=' near eos means ',' or '=' is ok */
+ if (*q == '=') {
+ if (*p != ',' && *p != '=')
+ return 0;
+ p++;
+ q++;
+ }
+
+ if (ptr)
+ *ptr = p;
+ return 1;
+}
+
+static const char *skip_linker_arg(const char **str)
+{
+ const char *s1 = *str;
+ const char *s2 = strchr(s1, ',');
+ *str = s2 ? s2++ : (s2 = s1 + strlen(s1));
+ return s2;
+}
+
+static char *copy_linker_arg(const char *p)
+{
+ const char *q = p;
+ skip_linker_arg(&q);
+ return pstrncpy(tcc_malloc(q - p + 1), p, q - p);
+}
+
+/* set linker options */
+static int tcc_set_linker(TCCState *s, const char *option)
+{
+ while (option && *option) {
+
+ const char *p = option;
+ char *end = NULL;
+ int ignoring = 0;
+
+ if (link_option(option, "Bsymbolic", &p)) {
+ s->symbolic = 1;
+ } else if (link_option(option, "nostdlib", &p)) {
+ s->nostdlib = 1;
+ } else if (link_option(option, "fini=", &p)) {
+ s->fini_symbol = copy_linker_arg(p);
+ ignoring = 1;
+ } else if (link_option(option, "image-base=", &p)
+ || link_option(option, "Ttext=", &p)) {
+ s->text_addr = strtoull(p, &end, 16);
+ s->has_text_addr = 1;
+ } else if (link_option(option, "init=", &p)) {
+ s->init_symbol = copy_linker_arg(p);
+ ignoring = 1;
+ } else if (link_option(option, "oformat=", &p)) {
+#if defined(TCC_TARGET_PE)
+ if (strstart("pe-", &p)) {
+#elif defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ if (strstart("elf64-", &p)) {
+#else
+ if (strstart("elf32-", &p)) {
+#endif
+ s->output_format = TCC_OUTPUT_FORMAT_ELF;
+ } else if (!strcmp(p, "binary")) {
+ s->output_format = TCC_OUTPUT_FORMAT_BINARY;
+#ifdef TCC_TARGET_COFF
+ } else if (!strcmp(p, "coff")) {
+ s->output_format = TCC_OUTPUT_FORMAT_COFF;
+#endif
+ } else
+ goto err;
+
+ } else if (link_option(option, "as-needed", &p)) {
+ ignoring = 1;
+ } else if (link_option(option, "O", &p)) {
+ ignoring = 1;
+ } else if (link_option(option, "rpath=", &p)) {
+ s->rpath = copy_linker_arg(p);
+ } else if (link_option(option, "section-alignment=", &p)) {
+ s->section_align = strtoul(p, &end, 16);
+ } else if (link_option(option, "soname=", &p)) {
+ s->soname = copy_linker_arg(p);
+#ifdef TCC_TARGET_PE
+ } else if (link_option(option, "file-alignment=", &p)) {
+ s->pe_file_align = strtoul(p, &end, 16);
+ } else if (link_option(option, "stack=", &p)) {
+ s->pe_stack_size = strtoul(p, &end, 10);
+ } else if (link_option(option, "subsystem=", &p)) {
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
+ if (!strcmp(p, "native")) {
+ s->pe_subsystem = 1;
+ } else if (!strcmp(p, "console")) {
+ s->pe_subsystem = 3;
+ } else if (!strcmp(p, "gui")) {
+ s->pe_subsystem = 2;
+ } else if (!strcmp(p, "posix")) {
+ s->pe_subsystem = 7;
+ } else if (!strcmp(p, "efiapp")) {
+ s->pe_subsystem = 10;
+ } else if (!strcmp(p, "efiboot")) {
+ s->pe_subsystem = 11;
+ } else if (!strcmp(p, "efiruntime")) {
+ s->pe_subsystem = 12;
+ } else if (!strcmp(p, "efirom")) {
+ s->pe_subsystem = 13;
+#elif defined(TCC_TARGET_ARM)
+ if (!strcmp(p, "wince")) {
+ s->pe_subsystem = 9;
+#endif
+ } else
+ goto err;
+#endif
+ } else
+ goto err;
+
+ if (ignoring && s->warn_unsupported) err: {
+ char buf[100], *e;
+ pstrcpy(buf, sizeof buf, e = copy_linker_arg(option)), tcc_free(e);
+ if (ignoring)
+ tcc_warning("unsupported linker option '%s'", buf);
+ else
+ tcc_error("unsupported linker option '%s'", buf);
+ }
+ option = skip_linker_arg(&p);
+ }
+ return 0;
+}
+
+typedef struct TCCOption {
+ const char *name;
+ uint16_t index;
+ uint16_t flags;
+} TCCOption;
+
+enum {
+ TCC_OPTION_HELP,
+ TCC_OPTION_I,
+ TCC_OPTION_D,
+ TCC_OPTION_U,
+ TCC_OPTION_P,
+ TCC_OPTION_L,
+ TCC_OPTION_B,
+ TCC_OPTION_l,
+ TCC_OPTION_bench,
+ TCC_OPTION_bt,
+ TCC_OPTION_b,
+ TCC_OPTION_g,
+ TCC_OPTION_c,
+ TCC_OPTION_dumpversion,
+ TCC_OPTION_d,
+ TCC_OPTION_float_abi,
+ TCC_OPTION_static,
+ TCC_OPTION_std,
+ TCC_OPTION_shared,
+ TCC_OPTION_soname,
+ TCC_OPTION_o,
+ TCC_OPTION_r,
+ TCC_OPTION_s,
+ TCC_OPTION_traditional,
+ TCC_OPTION_Wl,
+ TCC_OPTION_W,
+ TCC_OPTION_O,
+ TCC_OPTION_m,
+ TCC_OPTION_f,
+ TCC_OPTION_isystem,
+ TCC_OPTION_iwithprefix,
+ TCC_OPTION_nostdinc,
+ TCC_OPTION_nostdlib,
+ TCC_OPTION_print_search_dirs,
+ TCC_OPTION_rdynamic,
+ TCC_OPTION_pedantic,
+ TCC_OPTION_pthread,
+ TCC_OPTION_run,
+ TCC_OPTION_v,
+ TCC_OPTION_w,
+ TCC_OPTION_pipe,
+ TCC_OPTION_E,
+ TCC_OPTION_MD,
+ TCC_OPTION_MF,
+ TCC_OPTION_x,
+};
+
+#define TCC_OPTION_HAS_ARG 0x0001
+#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */
+
+static const TCCOption tcc_options[] = {
+ { "h", TCC_OPTION_HELP, 0 },
+ { "-help", TCC_OPTION_HELP, 0 },
+ { "?", TCC_OPTION_HELP, 0 },
+ { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG },
+ { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG },
+ { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG },
+ { "P", TCC_OPTION_P, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG },
+ { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG },
+ { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "bench", TCC_OPTION_bench, 0 },
+#ifdef CONFIG_TCC_BACKTRACE
+ { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG },
+#endif
+#ifdef CONFIG_TCC_BCHECK
+ { "b", TCC_OPTION_b, 0 },
+#endif
+ { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "c", TCC_OPTION_c, 0 },
+ { "dumpversion", TCC_OPTION_dumpversion, 0},
+ { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+#ifdef TCC_TARGET_ARM
+ { "mfloat-abi", TCC_OPTION_float_abi, TCC_OPTION_HAS_ARG },
+#endif
+ { "static", TCC_OPTION_static, 0 },
+ { "std", TCC_OPTION_std, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "shared", TCC_OPTION_shared, 0 },
+ { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG },
+ { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG },
+ { "pedantic", TCC_OPTION_pedantic, 0},
+ { "pthread", TCC_OPTION_pthread, 0},
+ { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "rdynamic", TCC_OPTION_rdynamic, 0 },
+ { "r", TCC_OPTION_r, 0 },
+ { "s", TCC_OPTION_s, 0 },
+ { "traditional", TCC_OPTION_traditional, 0 },
+ { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG },
+ { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG },
+ { "iwithprefix", TCC_OPTION_iwithprefix, TCC_OPTION_HAS_ARG },
+ { "nostdinc", TCC_OPTION_nostdinc, 0 },
+ { "nostdlib", TCC_OPTION_nostdlib, 0 },
+ { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 },
+ { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
+ { "w", TCC_OPTION_w, 0 },
+ { "pipe", TCC_OPTION_pipe, 0},
+ { "E", TCC_OPTION_E, 0},
+ { "MD", TCC_OPTION_MD, 0},
+ { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG },
+ { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG },
+ { NULL, 0, 0 },
+};
+
+static void parse_option_D(TCCState *s1, const char *optarg)
+{
+ char *sym = tcc_strdup(optarg);
+ char *value = strchr(sym, '=');
+ if (value)
+ *value++ = '\0';
+ tcc_define_symbol(s1, sym, value);
+ tcc_free(sym);
+}
+
+static void args_parser_add_file(TCCState *s, const char* filename, int filetype)
+{
+ int len = strlen(filename);
+ char *p = tcc_malloc(len + 2);
+ if (filetype) {
+ *p = filetype;
+ }
+ else {
+ /* use a file extension to detect a filetype */
+ const char *ext = tcc_fileextension(filename);
+ if (ext[0]) {
+ ext++;
+ if (!strcmp(ext, "S"))
+ *p = TCC_FILETYPE_ASM_PP;
+ else
+ if (!strcmp(ext, "s"))
+ *p = TCC_FILETYPE_ASM;
+ else
+ if (!PATHCMP(ext, "c") || !PATHCMP(ext, "i"))
+ *p = TCC_FILETYPE_C;
+ else
+ *p = TCC_FILETYPE_BINARY;
+ }
+ else {
+ *p = TCC_FILETYPE_C;
+ }
+ }
+ strcpy(p+1, filename);
+ dynarray_add((void ***)&s->files, &s->nb_files, p);
+}
+
+PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv)
+{
+ const TCCOption *popt;
+ const char *optarg, *r;
+ int run = 0;
+ int pthread = 0;
+ int optind = 0;
+ int filetype = 0;
+
+ /* collect -Wl options for input such as "-Wl,-rpath -Wl,<path>" */
+ CString linker_arg;
+ cstr_new(&linker_arg);
+
+ while (optind < argc) {
+
+ r = argv[optind++];
+ if (r[0] != '-' || r[1] == '\0') {
+ args_parser_add_file(s, r, filetype);
+ if (run) {
+ optind--;
+ /* argv[0] will be this file */
+ break;
+ }
+ continue;
+ }
+
+ /* find option in table */
+ for(popt = tcc_options; ; ++popt) {
+ const char *p1 = popt->name;
+ const char *r1 = r + 1;
+ if (p1 == NULL)
+ tcc_error("invalid option -- '%s'", r);
+ if (!strstart(p1, &r1))
+ continue;
+ optarg = r1;
+ if (popt->flags & TCC_OPTION_HAS_ARG) {
+ if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) {
+ if (optind >= argc)
+ tcc_error("argument to '%s' is missing", r);
+ optarg = argv[optind++];
+ }
+ } else if (*r1 != '\0')
+ continue;
+ break;
+ }
+
+ switch(popt->index) {
+ case TCC_OPTION_HELP:
+ return 0;
+ case TCC_OPTION_I:
+ tcc_add_include_path(s, optarg);
+ break;
+ case TCC_OPTION_D:
+ parse_option_D(s, optarg);
+ break;
+ case TCC_OPTION_U:
+ tcc_undefine_symbol(s, optarg);
+ break;
+ case TCC_OPTION_L:
+ tcc_add_library_path(s, optarg);
+ break;
+ case TCC_OPTION_B:
+ /* set tcc utilities path (mainly for tcc development) */
+ tcc_set_lib_path(s, optarg);
+ break;
+ case TCC_OPTION_l:
+ args_parser_add_file(s, r, TCC_FILETYPE_BINARY);
+ s->nb_libraries++;
+ break;
+ case TCC_OPTION_pthread:
+ parse_option_D(s, "_REENTRANT");
+ pthread = 1;
+ break;
+ case TCC_OPTION_bench:
+ s->do_bench = 1;
+ break;
+#ifdef CONFIG_TCC_BACKTRACE
+ case TCC_OPTION_bt:
+ tcc_set_num_callers(atoi(optarg));
+ break;
+#endif
+#ifdef CONFIG_TCC_BCHECK
+ case TCC_OPTION_b:
+ s->do_bounds_check = 1;
+ s->do_debug = 1;
+ break;
+#endif
+ case TCC_OPTION_g:
+ s->do_debug = 1;
+ break;
+ case TCC_OPTION_c:
+ if (s->output_type)
+ tcc_warning("-c: some compiler action already specified (%d)", s->output_type);
+ s->output_type = TCC_OUTPUT_OBJ;
+ break;
+ case TCC_OPTION_d:
+ if (*optarg == 'D')
+ s->dflag = 1;
+ else {
+ if (s->warn_unsupported)
+ goto unsupported_option;
+ tcc_error("invalid option -- '%s'", r);
+ }
+ break;
+#ifdef TCC_TARGET_ARM
+ case TCC_OPTION_float_abi:
+ /* tcc doesn't support soft float yet */
+ if (!strcmp(optarg, "softfp")) {
+ s->float_abi = ARM_SOFTFP_FLOAT;
+ tcc_undefine_symbol(s, "__ARM_PCS_VFP");
+ } else if (!strcmp(optarg, "hard"))
+ s->float_abi = ARM_HARD_FLOAT;
+ else
+ tcc_error("unsupported float abi '%s'", optarg);
+ break;
+#endif
+ case TCC_OPTION_static:
+ s->static_link = 1;
+ break;
+ case TCC_OPTION_std:
+ /* silently ignore, a current purpose:
+ allow to use a tcc as a reference compiler for "make test" */
+ break;
+ case TCC_OPTION_shared:
+ if (s->output_type)
+ tcc_warning("-shared: some compiler action already specified (%d)", s->output_type);
+ s->output_type = TCC_OUTPUT_DLL;
+ break;
+ case TCC_OPTION_soname:
+ s->soname = tcc_strdup(optarg);
+ break;
+ case TCC_OPTION_m:
+ s->option_m = tcc_strdup(optarg);
+ break;
+ case TCC_OPTION_o:
+ if (s->outfile) {
+ tcc_warning("multiple -o option");
+ tcc_free(s->outfile);
+ }
+ s->outfile = tcc_strdup(optarg);
+ break;
+ case TCC_OPTION_r:
+ /* generate a .o merging several output files */
+ if (s->output_type)
+ tcc_warning("-r: some compiler action already specified (%d)", s->output_type);
+ s->option_r = 1;
+ s->output_type = TCC_OUTPUT_OBJ;
+ break;
+ case TCC_OPTION_isystem:
+ tcc_add_sysinclude_path(s, optarg);
+ break;
+ case TCC_OPTION_iwithprefix:
+ if (1) {
+ char buf[1024];
+ int buf_size = sizeof(buf)-1;
+ char *p = &buf[0];
+
+ char *sysroot = "{B}/";
+ int len = strlen(sysroot);
+ if (len > buf_size)
+ len = buf_size;
+ strncpy(p, sysroot, len);
+ p += len;
+ buf_size -= len;
+
+ len = strlen(optarg);
+ if (len > buf_size)
+ len = buf_size;
+ strncpy(p, optarg, len+1);
+ tcc_add_sysinclude_path(s, buf);
+ }
+ break;
+ case TCC_OPTION_nostdinc:
+ s->nostdinc = 1;
+ break;
+ case TCC_OPTION_nostdlib:
+ s->nostdlib = 1;
+ break;
+ case TCC_OPTION_print_search_dirs:
+ s->print_search_dirs = 1;
+ break;
+ case TCC_OPTION_run:
+ if (s->output_type)
+ tcc_warning("-run: some compiler action already specified (%d)", s->output_type);
+ s->output_type = TCC_OUTPUT_MEMORY;
+ tcc_set_options(s, optarg);
+ run = 1;
+ break;
+ case TCC_OPTION_v:
+ do ++s->verbose; while (*optarg++ == 'v');
+ break;
+ case TCC_OPTION_f:
+ if (tcc_set_flag(s, optarg, 1) < 0 && s->warn_unsupported)
+ goto unsupported_option;
+ break;
+ case TCC_OPTION_W:
+ if (tcc_set_warning(s, optarg, 1) < 0 &&
+ s->warn_unsupported)
+ goto unsupported_option;
+ break;
+ case TCC_OPTION_w:
+ s->warn_none = 1;
+ break;
+ case TCC_OPTION_rdynamic:
+ s->rdynamic = 1;
+ break;
+ case TCC_OPTION_Wl:
+ if (linker_arg.size)
+ --linker_arg.size, cstr_ccat(&linker_arg, ',');
+ cstr_cat(&linker_arg, optarg);
+ cstr_ccat(&linker_arg, '\0');
+ break;
+ case TCC_OPTION_E:
+ if (s->output_type)
+ tcc_warning("-E: some compiler action already specified (%d)", s->output_type);
+ s->output_type = TCC_OUTPUT_PREPROCESS;
+ break;
+ case TCC_OPTION_P:
+ s->Pflag = atoi(optarg) + 1;
+ break;
+ case TCC_OPTION_MD:
+ s->gen_deps = 1;
+ break;
+ case TCC_OPTION_MF:
+ s->deps_outfile = tcc_strdup(optarg);
+ break;
+ case TCC_OPTION_dumpversion:
+ printf ("%s\n", TCC_VERSION);
+ exit(0);
+ case TCC_OPTION_s:
+ s->do_strip = 1;
+ break;
+ case TCC_OPTION_traditional:
+ break;
+ case TCC_OPTION_x:
+ if (*optarg == 'c')
+ filetype = TCC_FILETYPE_C;
+ else
+ if (*optarg == 'a')
+ filetype = TCC_FILETYPE_ASM_PP;
+ else
+ if (*optarg == 'n')
+ filetype = 0;
+ else
+ tcc_warning("unsupported language '%s'", optarg);
+ break;
+ case TCC_OPTION_O:
+ if (1) {
+ int opt = atoi(optarg);
+ char *sym = "__OPTIMIZE__";
+ if (opt)
+ tcc_define_symbol(s, sym, 0);
+ else
+ tcc_undefine_symbol(s, sym);
+ }
+ break;
+ case TCC_OPTION_pedantic:
+ case TCC_OPTION_pipe:
+ /* ignored */
+ break;
+ default:
+ if (s->warn_unsupported) {
+ unsupported_option:
+ tcc_warning("unsupported option '%s'", r);
+ }
+ break;
+ }
+ }
+
+ if (s->output_type == 0)
+ s->output_type = TCC_OUTPUT_EXE;
+
+ if (pthread && s->output_type != TCC_OUTPUT_OBJ)
+ tcc_set_options(s, "-lpthread");
+
+ if (s->output_type == TCC_OUTPUT_EXE)
+ tcc_set_linker(s, (const char *)linker_arg.data);
+ cstr_free(&linker_arg);
+
+ return optind;
+}
+
+LIBTCCAPI int tcc_set_options(TCCState *s, const char *str)
+{
+ const char *s1;
+ char **argv, *arg;
+ int argc, len;
+ int ret;
+
+ argc = 0, argv = NULL;
+ for(;;) {
+ while (is_space(*str))
+ str++;
+ if (*str == '\0')
+ break;
+ s1 = str;
+ while (*str != '\0' && !is_space(*str))
+ str++;
+ len = str - s1;
+ arg = tcc_malloc(len + 1);
+ pstrncpy(arg, s1, len);
+ dynarray_add((void ***)&argv, &argc, arg);
+ }
+ ret = tcc_parse_args(s, argc, argv);
+ dynarray_reset(&argv, &argc);
+ return ret;
+}
+
+PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time)
+{
+ double tt;
+ tt = (double)total_time / 1000000.0;
+ if (tt < 0.001)
+ tt = 0.001;
+ if (total_bytes < 1)
+ total_bytes = 1;
+ fprintf(stderr, "%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n",
+ tok_ident - TOK_IDENT, total_lines, total_bytes,
+ tt, (int)(total_lines / tt),
+ total_bytes / tt / 1000000.0);
+}
+
+PUB_FUNC void tcc_set_environment(TCCState *s)
+{
+ char * path;
+
+ path = getenv("C_INCLUDE_PATH");
+ if(path != NULL) {
+ tcc_add_include_path(s, path);
+ }
+ path = getenv("CPATH");
+ if(path != NULL) {
+ tcc_add_include_path(s, path);
+ }
+ path = getenv("LIBRARY_PATH");
+ if(path != NULL) {
+ tcc_add_library_path(s, path);
+ }
+}
diff --git a/src/libtcc.h b/src/libtcc.h
new file mode 100644
index 0000000..97b8587
--- /dev/null
+++ b/src/libtcc.h
@@ -0,0 +1,104 @@
+#ifndef LIBTCC_H
+#define LIBTCC_H
+
+#ifndef LIBTCCAPI
+# define LIBTCCAPI
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct TCCState;
+
+typedef struct TCCState TCCState;
+
+/* create a new TCC compilation context */
+LIBTCCAPI TCCState *tcc_new(void);
+
+/* free a TCC compilation context */
+LIBTCCAPI void tcc_delete(TCCState *s);
+
+/* set CONFIG_TCCDIR at runtime */
+LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path);
+
+/* set error/warning display callback */
+LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque,
+ void (*error_func)(void *opaque, const char *msg));
+
+/* set options as from command line (multiple supported) */
+LIBTCCAPI int tcc_set_options(TCCState *s, const char *str);
+
+/*****************************/
+/* preprocessor */
+
+/* add include path */
+LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname);
+
+/* add in system include path */
+LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname);
+
+/* define preprocessor symbol 'sym'. Can put optional value */
+LIBTCCAPI void tcc_define_symbol(TCCState *s, const char *sym, const char *value);
+
+/* undefine preprocess symbol 'sym' */
+LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym);
+
+/*****************************/
+/* compiling */
+
+/* add a file (C file, dll, object, library, ld script). Return -1 if error. */
+LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename, int filetype);
+#define TCC_FILETYPE_BINARY 1
+#define TCC_FILETYPE_C 2
+#define TCC_FILETYPE_ASM 3
+#define TCC_FILETYPE_ASM_PP 4
+
+/* compile a string containing a C source. Return -1 if error. */
+LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf);
+
+/*****************************/
+/* linking commands */
+
+/* set output type. MUST BE CALLED before any compilation */
+LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type);
+#define TCC_OUTPUT_MEMORY 1 /* output will be run in memory (default) */
+#define TCC_OUTPUT_EXE 2 /* executable file */
+#define TCC_OUTPUT_DLL 3 /* dynamic library */
+#define TCC_OUTPUT_OBJ 4 /* object file */
+#define TCC_OUTPUT_PREPROCESS 5 /* only preprocess (used internally) */
+
+/* equivalent to -Lpath option */
+LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname);
+
+/* the library name is the same as the argument of the '-l' option */
+LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname);
+
+/* add a symbol to the compiled program */
+LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val);
+
+/* output an executable, library or object file. DO NOT call
+ tcc_relocate() before. */
+LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename);
+
+/* link and run main() function and return its value. DO NOT call
+ tcc_relocate() before. */
+LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv);
+
+/* do all relocations (needed before using tcc_get_symbol()) */
+LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr);
+/* possible values for 'ptr':
+ - TCC_RELOCATE_AUTO : Allocate and manage memory internally
+ - NULL : return required memory size for the step below
+ - memory address : copy code to memory passed by the caller
+ returns -1 if error. */
+#define TCC_RELOCATE_AUTO (void*)1
+
+/* return symbol value or NULL if not found */
+LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/stab.def b/src/stab.def
new file mode 100644
index 0000000..48ea231
--- /dev/null
+++ b/src/stab.def
@@ -0,0 +1,234 @@
+/* Table of DBX symbol codes for the GNU system.
+ Copyright (C) 1988, 1997 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with the GNU C Library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+/* This contains contribution from Cygnus Support. */
+
+/* Global variable. Only the name is significant.
+ To find the address, look in the corresponding external symbol. */
+__define_stab (N_GSYM, 0x20, "GSYM")
+
+/* Function name for BSD Fortran. Only the name is significant.
+ To find the address, look in the corresponding external symbol. */
+__define_stab (N_FNAME, 0x22, "FNAME")
+
+/* Function name or text-segment variable for C. Value is its address.
+ Desc is supposedly starting line number, but GCC doesn't set it
+ and DBX seems not to miss it. */
+__define_stab (N_FUN, 0x24, "FUN")
+
+/* Data-segment variable with internal linkage. Value is its address.
+ "Static Sym". */
+__define_stab (N_STSYM, 0x26, "STSYM")
+
+/* BSS-segment variable with internal linkage. Value is its address. */
+__define_stab (N_LCSYM, 0x28, "LCSYM")
+
+/* Name of main routine. Only the name is significant.
+ This is not used in C. */
+__define_stab (N_MAIN, 0x2a, "MAIN")
+
+/* Global symbol in Pascal.
+ Supposedly the value is its line number; I'm skeptical. */
+__define_stab (N_PC, 0x30, "PC")
+
+/* Number of symbols: 0, files,,funcs,lines according to Ultrix V4.0. */
+__define_stab (N_NSYMS, 0x32, "NSYMS")
+
+/* "No DST map for sym: name, ,0,type,ignored" according to Ultrix V4.0. */
+__define_stab (N_NOMAP, 0x34, "NOMAP")
+
+/* New stab from Solaris. I don't know what it means, but it
+ don't seem to contain useful information. */
+__define_stab (N_OBJ, 0x38, "OBJ")
+
+/* New stab from Solaris. I don't know what it means, but it
+ don't seem to contain useful information. Possibly related to the
+ optimization flags used in this module. */
+__define_stab (N_OPT, 0x3c, "OPT")
+
+/* Register variable. Value is number of register. */
+__define_stab (N_RSYM, 0x40, "RSYM")
+
+/* Modula-2 compilation unit. Can someone say what info it contains? */
+__define_stab (N_M2C, 0x42, "M2C")
+
+/* Line number in text segment. Desc is the line number;
+ value is corresponding address. */
+__define_stab (N_SLINE, 0x44, "SLINE")
+
+/* Similar, for data segment. */
+__define_stab (N_DSLINE, 0x46, "DSLINE")
+
+/* Similar, for bss segment. */
+__define_stab (N_BSLINE, 0x48, "BSLINE")
+
+/* Sun's source-code browser stabs. ?? Don't know what the fields are.
+ Supposedly the field is "path to associated .cb file". THIS VALUE
+ OVERLAPS WITH N_BSLINE! */
+__define_stab (N_BROWS, 0x48, "BROWS")
+
+/* GNU Modula-2 definition module dependency. Value is the modification time
+ of the definition file. Other is non-zero if it is imported with the
+ GNU M2 keyword %INITIALIZE. Perhaps N_M2C can be used if there
+ are enough empty fields? */
+__define_stab(N_DEFD, 0x4a, "DEFD")
+
+/* THE FOLLOWING TWO STAB VALUES CONFLICT. Happily, one is for Modula-2
+ and one is for C++. Still,... */
+/* GNU C++ exception variable. Name is variable name. */
+__define_stab (N_EHDECL, 0x50, "EHDECL")
+/* Modula2 info "for imc": name,,0,0,0 according to Ultrix V4.0. */
+__define_stab (N_MOD2, 0x50, "MOD2")
+
+/* GNU C++ `catch' clause. Value is its address. Desc is nonzero if
+ this entry is immediately followed by a CAUGHT stab saying what exception
+ was caught. Multiple CAUGHT stabs means that multiple exceptions
+ can be caught here. If Desc is 0, it means all exceptions are caught
+ here. */
+__define_stab (N_CATCH, 0x54, "CATCH")
+
+/* Structure or union element. Value is offset in the structure. */
+__define_stab (N_SSYM, 0x60, "SSYM")
+
+/* Name of main source file.
+ Value is starting text address of the compilation. */
+__define_stab (N_SO, 0x64, "SO")
+
+/* Automatic variable in the stack. Value is offset from frame pointer.
+ Also used for type descriptions. */
+__define_stab (N_LSYM, 0x80, "LSYM")
+
+/* Beginning of an include file. Only Sun uses this.
+ In an object file, only the name is significant.
+ The Sun linker puts data into some of the other fields. */
+__define_stab (N_BINCL, 0x82, "BINCL")
+
+/* Name of sub-source file (#include file).
+ Value is starting text address of the compilation. */
+__define_stab (N_SOL, 0x84, "SOL")
+
+/* Parameter variable. Value is offset from argument pointer.
+ (On most machines the argument pointer is the same as the frame pointer. */
+__define_stab (N_PSYM, 0xa0, "PSYM")
+
+/* End of an include file. No name.
+ This and N_BINCL act as brackets around the file's output.
+ In an object file, there is no significant data in this entry.
+ The Sun linker puts data into some of the fields. */
+__define_stab (N_EINCL, 0xa2, "EINCL")
+
+/* Alternate entry point. Value is its address. */
+__define_stab (N_ENTRY, 0xa4, "ENTRY")
+
+/* Beginning of lexical block.
+ The desc is the nesting level in lexical blocks.
+ The value is the address of the start of the text for the block.
+ The variables declared inside the block *precede* the N_LBRAC symbol. */
+__define_stab (N_LBRAC, 0xc0, "LBRAC")
+
+/* Place holder for deleted include file. Replaces a N_BINCL and everything
+ up to the corresponding N_EINCL. The Sun linker generates these when
+ it finds multiple identical copies of the symbols from an include file.
+ This appears only in output from the Sun linker. */
+__define_stab (N_EXCL, 0xc2, "EXCL")
+
+/* Modula-2 scope information. Can someone say what info it contains? */
+__define_stab (N_SCOPE, 0xc4, "SCOPE")
+
+/* End of a lexical block. Desc matches the N_LBRAC's desc.
+ The value is the address of the end of the text for the block. */
+__define_stab (N_RBRAC, 0xe0, "RBRAC")
+
+/* Begin named common block. Only the name is significant. */
+__define_stab (N_BCOMM, 0xe2, "BCOMM")
+
+/* End named common block. Only the name is significant
+ (and it should match the N_BCOMM). */
+__define_stab (N_ECOMM, 0xe4, "ECOMM")
+
+/* End common (local name): value is address.
+ I'm not sure how this is used. */
+__define_stab (N_ECOML, 0xe8, "ECOML")
+
+/* These STAB's are used on Gould systems for Non-Base register symbols
+ or something like that. FIXME. I have assigned the values at random
+ since I don't have a Gould here. Fixups from Gould folk welcome... */
+__define_stab (N_NBTEXT, 0xF0, "NBTEXT")
+__define_stab (N_NBDATA, 0xF2, "NBDATA")
+__define_stab (N_NBBSS, 0xF4, "NBBSS")
+__define_stab (N_NBSTS, 0xF6, "NBSTS")
+__define_stab (N_NBLCS, 0xF8, "NBLCS")
+
+/* Second symbol entry containing a length-value for the preceding entry.
+ The value is the length. */
+__define_stab (N_LENG, 0xfe, "LENG")
+
+/* The above information, in matrix format.
+
+ STAB MATRIX
+ _________________________________________________
+ | 00 - 1F are not dbx stab symbols |
+ | In most cases, the low bit is the EXTernal bit|
+
+ | 00 UNDEF | 02 ABS | 04 TEXT | 06 DATA |
+ | 01 |EXT | 03 |EXT | 05 |EXT | 07 |EXT |
+
+ | 08 BSS | 0A INDR | 0C FN_SEQ | 0E |
+ | 09 |EXT | 0B | 0D | 0F |
+
+ | 10 | 12 COMM | 14 SETA | 16 SETT |
+ | 11 | 13 | 15 | 17 |
+
+ | 18 SETD | 1A SETB | 1C SETV | 1E WARNING|
+ | 19 | 1B | 1D | 1F FN |
+
+ |_______________________________________________|
+ | Debug entries with bit 01 set are unused. |
+ | 20 GSYM | 22 FNAME | 24 FUN | 26 STSYM |
+ | 28 LCSYM | 2A MAIN | 2C | 2E |
+ | 30 PC | 32 NSYMS | 34 NOMAP | 36 |
+ | 38 OBJ | 3A | 3C OPT | 3E |
+ | 40 RSYM | 42 M2C | 44 SLINE | 46 DSLINE |
+ | 48 BSLINE*| 4A DEFD | 4C | 4E |
+ | 50 EHDECL*| 52 | 54 CATCH | 56 |
+ | 58 | 5A | 5C | 5E |
+ | 60 SSYM | 62 | 64 SO | 66 |
+ | 68 | 6A | 6C | 6E |
+ | 70 | 72 | 74 | 76 |
+ | 78 | 7A | 7C | 7E |
+ | 80 LSYM | 82 BINCL | 84 SOL | 86 |
+ | 88 | 8A | 8C | 8E |
+ | 90 | 92 | 94 | 96 |
+ | 98 | 9A | 9C | 9E |
+ | A0 PSYM | A2 EINCL | A4 ENTRY | A6 |
+ | A8 | AA | AC | AE |
+ | B0 | B2 | B4 | B6 |
+ | B8 | BA | BC | BE |
+ | C0 LBRAC | C2 EXCL | C4 SCOPE | C6 |
+ | C8 | CA | CC | CE |
+ | D0 | D2 | D4 | D6 |
+ | D8 | DA | DC | DE |
+ | E0 RBRAC | E2 BCOMM | E4 ECOMM | E6 |
+ | E8 ECOML | EA | EC | EE |
+ | F0 | F2 | F4 | F6 |
+ | F8 | FA | FC | FE LENG |
+ +-----------------------------------------------+
+ * 50 EHDECL is also MOD2.
+ * 48 BSLINE is also BROWS.
+ */
diff --git a/src/stab.h b/src/stab.h
new file mode 100644
index 0000000..80bd594
--- /dev/null
+++ b/src/stab.h
@@ -0,0 +1,17 @@
+#ifndef __GNU_STAB__
+
+/* Indicate the GNU stab.h is in use. */
+
+#define __GNU_STAB__
+
+#define __define_stab(NAME, CODE, STRING) NAME=CODE,
+
+enum __stab_debug_code
+{
+#include "stab.def"
+LAST_UNUSED_STAB_CODE
+};
+
+#undef __define_stab
+
+#endif /* __GNU_STAB_ */
diff --git a/src/tcc.c b/src/tcc.c
new file mode 100644
index 0000000..cce9699
--- /dev/null
+++ b/src/tcc.c
@@ -0,0 +1,365 @@
+/*
+ * TCC - Tiny C Compiler
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef ONE_SOURCE
+#include "libtcc.c"
+#else
+#include "tcc.h"
+#endif
+
+static void print_paths(const char *msg, char **paths, int nb_paths)
+{
+ int i;
+ printf("%s:\n%s", msg, nb_paths ? "" : " -\n");
+ for(i = 0; i < nb_paths; i++)
+ printf(" %s\n", paths[i]);
+}
+
+static void display_info(TCCState *s, int what)
+{
+ switch (what) {
+ case 0:
+ printf("tcc version %s ("
+#ifdef TCC_TARGET_I386
+ "i386"
+#elif defined TCC_TARGET_X86_64
+ "x86-64"
+#elif defined TCC_TARGET_C67
+ "C67"
+#elif defined TCC_TARGET_ARM
+ "ARM"
+# ifdef TCC_ARM_HARDFLOAT
+ " Hard Float"
+# endif
+#elif defined TCC_TARGET_ARM64
+ "AArch64"
+# ifdef TCC_ARM_HARDFLOAT
+ " Hard Float"
+# endif
+#endif
+#ifdef TCC_TARGET_PE
+ " Windows"
+#else
+ " Linux"
+#endif
+ ")\n", TCC_VERSION);
+ break;
+ case 1:
+ printf("install: %s\n", s->tcc_lib_path);
+ /* print_paths("programs", NULL, 0); */
+ print_paths("include", s->sysinclude_paths, s->nb_sysinclude_paths);
+ print_paths("libraries", s->library_paths, s->nb_library_paths);
+#ifndef TCC_TARGET_PE
+ print_paths("crt", s->crt_paths, s->nb_crt_paths);
+ printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s));
+#endif
+ break;
+ }
+}
+
+static void help(void)
+{
+ printf("Tiny C Compiler "TCC_VERSION" - Copyright (C) 2001-2006 Fabrice Bellard\n"
+ "Usage: tcc [options...] [-o outfile] [-c] infile(s)...\n"
+ " tcc [options...] -run infile [arguments...]\n"
+ "General options:\n"
+ " -c compile only - generate an object file\n"
+ " -o outfile set output filename\n"
+ " -run run compiled source\n"
+ " -fflag set or reset (with 'no-' prefix) 'flag' (see man page)\n"
+ " -Wwarning set or reset (with 'no-' prefix) 'warning' (see man page)\n"
+ " -w disable all warnings\n"
+ " -v show version\n"
+ " -vv show included files (as sole argument: show search paths)\n"
+ " -dumpversion\n"
+ " -bench show compilation statistics\n"
+ "Preprocessor options:\n"
+ " -Idir add include path 'dir'\n"
+ " -Dsym[=val] define 'sym' with value 'val'\n"
+ " -Usym undefine 'sym'\n"
+ " -E preprocess only\n"
+ " -P[1] no/alternative output of #line directives with -E\n"
+ " -dD dump defines\n"
+ "Linker options:\n"
+ " -Ldir add library path 'dir'\n"
+ " -llib link with dynamic or static library 'lib'\n"
+ " -pthread link with -lpthread and -D_REENTRANT (POSIX Linux)\n"
+ " -r generate (relocatable) object file\n"
+ " -rdynamic export all global symbols to dynamic linker\n"
+ " -shared generate a shared library\n"
+ " -soname set name for shared library to be used at runtime\n"
+ " -static static linking\n"
+ " -Wl,-opt[=val] set linker option (see manual)\n"
+ "Debugger options:\n"
+ " -g generate runtime debug info\n"
+#ifdef CONFIG_TCC_BCHECK
+ " -b compile with built-in memory and bounds checker (implies -g)\n"
+#endif
+#ifdef CONFIG_TCC_BACKTRACE
+ " -bt N show N callers in stack traces\n"
+#endif
+ "Misc options:\n"
+ " -nostdinc do not use standard system include paths\n"
+ " -nostdlib do not link with standard crt and libraries\n"
+ " -Bdir use 'dir' as tcc internal library and include path\n"
+ " -MD generate target dependencies for make\n"
+ " -MF depfile put generated dependencies here\n"
+ );
+}
+
+/* re-execute the i386/x86_64 cross-compilers with tcc -m32/-m64: */
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+#ifdef _WIN32
+#include <process.h>
+static int execvp_win32(const char *prog, char **argv)
+{
+ int ret = spawnvp(P_NOWAIT, prog, (const char *const*)argv);
+ if (-1 == ret)
+ return ret;
+ cwait(&ret, ret, WAIT_CHILD);
+ exit(ret);
+}
+#define execvp execvp_win32
+#endif
+static void exec_other_tcc(TCCState *s, char **argv, const char *optarg)
+{
+ char child_path[4096], *child_name; const char *target;
+ switch (atoi(optarg)) {
+#ifdef TCC_TARGET_I386
+ case 32: break;
+ case 64: target = "x86_64";
+#else
+ case 64: break;
+ case 32: target = "i386";
+#endif
+ pstrcpy(child_path, sizeof child_path - 40, argv[0]);
+ child_name = tcc_basename(child_path);
+ strcpy(child_name, target);
+#ifdef TCC_TARGET_PE
+ strcat(child_name, "-win32");
+#endif
+ strcat(child_name, "-tcc");
+ if (strcmp(argv[0], child_path)) {
+ if (s->verbose > 0)
+ printf("tcc: using '%s'\n", child_name), fflush(stdout);
+ execvp(argv[0] = child_path, argv);
+ }
+ tcc_error("'%s' not found", child_name);
+ case 0: /* ignore -march etc. */
+ break;
+ default:
+ tcc_warning("unsupported option \"-m%s\"", optarg);
+ }
+}
+#else
+#define exec_other_tcc(s, argv, optarg)
+#endif
+
+static void gen_makedeps(TCCState *s, const char *target, const char *filename)
+{
+ FILE *depout;
+ char buf[1024], *ext;
+ int i;
+
+ if (!filename) {
+ /* compute filename automatically
+ * dir/file.o -> dir/file.d */
+ pstrcpy(buf, sizeof(buf), target);
+ ext = tcc_fileextension(buf);
+ pstrcpy(ext, sizeof(buf) - (ext-buf), ".d");
+ filename = buf;
+ }
+
+ if (s->verbose)
+ printf("<- %s\n", filename);
+
+ /* XXX return err codes instead of error() ? */
+ depout = fopen(filename, "w");
+ if (!depout)
+ tcc_error("could not open '%s'", filename);
+
+ fprintf(depout, "%s : \\\n", target);
+ for (i=0; i<s->nb_target_deps; ++i)
+ fprintf(depout, " %s \\\n", s->target_deps[i]);
+ fprintf(depout, "\n");
+ fclose(depout);
+}
+
+static char *default_outputfile(TCCState *s, const char *first_file)
+{
+ char buf[1024];
+ char *ext;
+ const char *name = "a";
+
+ if (first_file && strcmp(first_file, "-"))
+ name = tcc_basename(first_file);
+ pstrcpy(buf, sizeof(buf), name);
+ ext = tcc_fileextension(buf);
+#ifdef TCC_TARGET_PE
+ if (s->output_type == TCC_OUTPUT_DLL)
+ strcpy(ext, ".dll");
+ else
+ if (s->output_type == TCC_OUTPUT_EXE)
+ strcpy(ext, ".exe");
+ else
+#endif
+ if (( (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) ||
+ (s->output_type == TCC_OUTPUT_PREPROCESS) )
+ && *ext)
+ strcpy(ext, ".o");
+ else
+ strcpy(buf, "a.out");
+
+ return tcc_strdup(buf);
+}
+
+static int64_t getclock_us(void)
+{
+#ifdef _WIN32
+ struct _timeb tb;
+ _ftime(&tb);
+ return (tb.time * 1000LL + tb.millitm) * 1000LL;
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec * 1000000LL + tv.tv_usec;
+#endif
+}
+
+int main(int argc, char **argv)
+{
+ TCCState *s;
+ int ret, optind, i;
+ int64_t start_time = 0;
+
+ s = tcc_new();
+
+ optind = tcc_parse_args(s, argc - 1, argv + 1);
+
+ if (s->do_bench)
+ start_time = getclock_us();
+
+ tcc_set_environment(s);
+
+ if (optind == 0) {
+ help();
+ return 1;
+ }
+
+ if (s->option_m)
+ exec_other_tcc(s, argv, s->option_m);
+
+ if (s->verbose)
+ display_info(s, 0);
+
+ if (s->print_search_dirs || (s->verbose == 2 && optind == 1)) {
+ tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
+ display_info(s, 1);
+ return 0;
+ }
+
+ if (s->verbose && optind == 1)
+ return 0;
+
+ if (s->nb_files == 0)
+ tcc_error("no input files\n");
+
+ /* check -c consistency : only single file handled. XXX: checks file type */
+ if (s->output_type == TCC_OUTPUT_OBJ && !s->option_r) {
+ if (s->nb_libraries != 0)
+ tcc_error("cannot specify libraries with -c");
+ /* accepts only a single input file */
+ if ((s->nb_files != 1) && s->outfile) {
+ tcc_error("cannot specify multiple files with -c and -o");
+ }
+ }
+
+ if (s->output_type == TCC_OUTPUT_PREPROCESS) {
+ if (!s->outfile) {
+ s->ppfp = stdout;
+ } else {
+ s->ppfp = fopen(s->outfile, "w");
+ if (!s->ppfp)
+ tcc_error("could not write '%s'", s->outfile);
+ }
+ }
+
+ tcc_set_output_type(s, s->output_type);
+
+ /* compile or add each files or library */
+ for(i = ret = 0; i < s->nb_files && ret == 0; i++) {
+ int filetype = *(unsigned char *)s->files[i];
+ const char *filename = s->files[i] + 1;
+ if (filename[0] == '-' && filename[1] == 'l') {
+ if (tcc_add_library(s, filename + 2) < 0) {
+ tcc_error_noabort("cannot find library 'lib%s'", filename+2);
+ ret = 1;
+ }
+ } else {
+ if (1 == s->verbose)
+ printf("-> %s\n", filename);
+ if (!s->outfile)
+ s->outfile = default_outputfile(s, filename);
+ if (tcc_add_file(s, filename, filetype) < 0)
+ ret = 1;
+ else
+ if (s->output_type == TCC_OUTPUT_OBJ) {
+ ret = !!tcc_output_file(s, s->outfile);
+ if (s->gen_deps && !ret)
+ gen_makedeps(s, s->outfile, s->deps_outfile);
+ if (!ret) {
+ if ((i+1) < s->nb_files) {
+ tcc_delete(s);
+ s = tcc_new();
+ tcc_parse_args(s, argc - 1, argv + 1);
+ tcc_set_environment(s);
+ if (s->output_type != TCC_OUTPUT_OBJ)
+ tcc_error("interlnal error");
+ tcc_set_output_type(s, s->output_type);
+ }
+ }
+ }
+ }
+ }
+
+ if (0 == ret) {
+ if (s->output_type == TCC_OUTPUT_MEMORY) {
+#ifdef TCC_IS_NATIVE
+ ret = tcc_run(s, argc - 1 - optind, argv + 1 + optind);
+#else
+ tcc_error_noabort("-run is not available in a cross compiler");
+ ret = 1;
+#endif
+ } else
+ if (s->output_type == TCC_OUTPUT_EXE ||
+ s->output_type == TCC_OUTPUT_DLL)
+ {
+ ret = !!tcc_output_file(s, s->outfile);
+ if (s->gen_deps && !ret)
+ gen_makedeps(s, s->outfile, s->deps_outfile);
+ }
+ }
+
+ if (s->do_bench)
+ tcc_print_stats(s, getclock_us() - start_time);
+
+ tcc_delete(s);
+ return ret;
+}
diff --git a/src/tcc.h b/src/tcc.h
new file mode 100644
index 0000000..eb0365d
--- /dev/null
+++ b/src/tcc.h
@@ -0,0 +1,1498 @@
+/*
+ * TCC - Tiny C Compiler
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TCC_H
+#define _TCC_H
+
+#define _GNU_SOURCE
+#include "config.h"
+
+#ifdef CONFIG_TCCBOOT
+#include "tccboot.h"
+#define CONFIG_TCC_STATIC
+#else
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <time.h>
+
+#ifdef CONFIG_TCCASSERT
+#include <assert.h>
+#define TCC_ASSERT(ex) assert(ex)
+#else
+#define TCC_ASSERT(ex)
+#endif
+
+#ifndef _WIN32
+# include <unistd.h>
+# include <sys/time.h>
+# include <sys/ucontext.h>
+# include <sys/mman.h>
+# ifndef CONFIG_TCC_STATIC
+# include <dlfcn.h>
+# endif
+/* XXX: need to define this to use them in non ISOC99 context */
+ extern float strtof (const char *__nptr, char **__endptr);
+ extern long double strtold (const char *__nptr, char **__endptr);
+#else /* on _WIN32: */
+# include <windows.h>
+# include <sys/timeb.h>
+# include <io.h> /* open, close etc. */
+# include <direct.h> /* getcwd */
+# ifdef __GNUC__
+# include <stdint.h>
+# endif
+# define inline __inline
+# define inp next_inp
+# define snprintf _snprintf
+# define vsnprintf _vsnprintf
+# ifndef __GNUC__
+# define strtold (long double)strtod
+# define strtof (float)strtod
+# define strtoll _strtoi64
+# define strtoull _strtoui64
+# endif
+# ifdef LIBTCC_AS_DLL
+# define LIBTCCAPI __declspec(dllexport)
+# define PUB_FUNC LIBTCCAPI
+# endif
+#endif
+
+#endif /* !CONFIG_TCCBOOT */
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+#ifdef __GNUC__
+# define NORETURN __attribute__ ((noreturn))
+#elif defined _MSC_VER
+# define NORETURN __declspec(noreturn)
+#else
+# define NORETURN
+#endif
+
+#ifdef _WIN32
+# define IS_DIRSEP(c) (c == '/' || c == '\\')
+# define IS_ABSPATH(p) (IS_DIRSEP(p[0]) || (p[0] && p[1] == ':' && IS_DIRSEP(p[2])))
+# define PATHCMP stricmp
+#else
+# define IS_DIRSEP(c) (c == '/')
+# define IS_ABSPATH(p) IS_DIRSEP(p[0])
+# define PATHCMP strcmp
+#endif
+
+#ifdef TCC_TARGET_PE
+#define PATHSEP ';'
+#else
+#define PATHSEP ':'
+#endif
+
+#include "elf.h"
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+# define ELFCLASSW ELFCLASS64
+# define ElfW(type) Elf##64##_##type
+# define ELFW(type) ELF##64##_##type
+# define ElfW_Rel ElfW(Rela)
+# define SHT_RELX SHT_RELA
+# define REL_SECTION_FMT ".rela%s"
+/* XXX: DLL with PLT would only work with x86-64 for now */
+# define TCC_OUTPUT_DLL_WITH_PLT
+#else
+# define ELFCLASSW ELFCLASS32
+# define ElfW(type) Elf##32##_##type
+# define ELFW(type) ELF##32##_##type
+# define ElfW_Rel ElfW(Rel)
+# define SHT_RELX SHT_REL
+# define REL_SECTION_FMT ".rel%s"
+#endif
+
+/* target address type */
+#define addr_t ElfW(Addr)
+
+#include "stab.h"
+#include "libtcc.h"
+
+/* parser debug */
+/* #define PARSE_DEBUG */
+/* preprocessor debug */
+/* #define PP_DEBUG */
+/* include file debug */
+/* #define INC_DEBUG */
+/* memory leak debug */
+/* #define MEM_DEBUG */
+/* assembler debug */
+/* #define ASM_DEBUG */
+
+/* target selection */
+/* #define TCC_TARGET_I386 *//* i386 code generator */
+/* #define TCC_TARGET_ARM *//* ARMv4 code generator */
+/* #define TCC_TARGET_ARM64 *//* ARMv8 code generator */
+/* #define TCC_TARGET_C67 *//* TMS320C67xx code generator */
+/* #define TCC_TARGET_X86_64 *//* x86-64 code generator */
+
+/* default target is I386 */
+#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \
+ !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
+ !defined(TCC_TARGET_X86_64)
+#define TCC_TARGET_I386
+#endif
+
+#if !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \
+ !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_C67) && \
+ !defined(CONFIG_USE_LIBGCC)
+#define CONFIG_TCC_BCHECK /* enable bound checking code */
+#endif
+
+/* define it to include assembler support */
+#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_ARM64) && \
+ !defined(TCC_TARGET_C67)
+#define CONFIG_TCC_ASM
+#endif
+
+/* object format selection */
+#if defined(TCC_TARGET_C67)
+#define TCC_TARGET_COFF
+#endif
+
+/* only native compiler supports -run */
+#if defined _WIN32 == defined TCC_TARGET_PE
+# if (defined __i386__ || defined _X86_) && defined TCC_TARGET_I386
+# define TCC_IS_NATIVE
+# elif (defined __x86_64__ || defined _AMD64_) && defined TCC_TARGET_X86_64
+# define TCC_IS_NATIVE
+# elif defined __arm__ && defined TCC_TARGET_ARM
+# define TCC_IS_NATIVE
+# elif defined __aarch64__ && defined TCC_TARGET_ARM64
+# define TCC_IS_NATIVE
+# endif
+#endif
+
+#if defined TCC_IS_NATIVE && !defined CONFIG_TCCBOOT
+# define CONFIG_TCC_BACKTRACE
+#endif
+
+/* ------------ path configuration ------------ */
+
+#ifndef CONFIG_SYSROOT
+# define CONFIG_SYSROOT ""
+#endif
+#ifndef CONFIG_TCCDIR
+# define CONFIG_TCCDIR "."
+#endif
+#ifndef CONFIG_LDDIR
+# ifdef TCC_TARGET_X86_64
+# define CONFIG_LDDIR "lib64"
+# else
+# define CONFIG_LDDIR "lib"
+# endif
+#endif
+
+#ifdef CONFIG_MULTIARCHDIR
+# define USE_MUADIR(s) s "/" CONFIG_MULTIARCHDIR
+# define ALSO_MUADIR(s) s "/" CONFIG_MULTIARCHDIR ":" s
+#else
+# define USE_MUADIR(s) s
+# define ALSO_MUADIR(s) s
+#endif
+
+/* path to find crt1.o, crti.o and crtn.o */
+#ifndef CONFIG_TCC_CRTPREFIX
+# define CONFIG_TCC_CRTPREFIX USE_MUADIR(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR)
+#endif
+
+/* Below: {B} is substituted by CONFIG_TCCDIR (rsp. -B option) */
+
+/* system include paths */
+#ifndef CONFIG_TCC_SYSINCLUDEPATHS
+# ifdef TCC_TARGET_PE
+# define CONFIG_TCC_SYSINCLUDEPATHS "{B}/include;{B}/include/winapi"
+# else
+# define CONFIG_TCC_SYSINCLUDEPATHS \
+ ALSO_MUADIR(CONFIG_SYSROOT "/usr/local/include") \
+ ":" ALSO_MUADIR(CONFIG_SYSROOT "/usr/include") \
+ ":" "{B}/include"
+# endif
+#endif
+
+/* library search paths */
+#ifndef CONFIG_TCC_LIBPATHS
+# ifdef TCC_TARGET_PE
+# define CONFIG_TCC_LIBPATHS "{B}/lib"
+# else
+# define CONFIG_TCC_LIBPATHS \
+ ALSO_MUADIR(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \
+ ":" ALSO_MUADIR(CONFIG_SYSROOT "/" CONFIG_LDDIR) \
+ ":" ALSO_MUADIR(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)
+# endif
+#endif
+
+/* name of ELF interpreter */
+#ifndef CONFIG_TCC_ELFINTERP
+# if defined __FreeBSD__
+# define CONFIG_TCC_ELFINTERP "/libexec/ld-elf.so.1"
+# elif defined __FreeBSD_kernel__
+# if defined(TCC_TARGET_X86_64)
+# define CONFIG_TCC_ELFINTERP "/lib/ld-kfreebsd-x86-64.so.1"
+# else
+# define CONFIG_TCC_ELFINTERP "/lib/ld.so.1"
+# endif
+# elif defined __DragonFly__
+# define CONFIG_TCC_ELFINTERP "/usr/libexec/ld-elf.so.2"
+# elif defined __GNU__
+# define CONFIG_TCC_ELFINTERP "/lib/ld.so"
+# elif defined(TCC_TARGET_PE)
+# define CONFIG_TCC_ELFINTERP "-"
+# elif defined(TCC_UCLIBC)
+# define CONFIG_TCC_ELFINTERP "/lib/ld-uClibc.so.0" /* is there a uClibc for x86_64 ? */
+# elif defined TCC_TARGET_ARM64
+# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-aarch64.so.1"
+# elif defined(TCC_TARGET_X86_64)
+# define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2"
+# elif !defined(TCC_ARM_EABI)
+# define CONFIG_TCC_ELFINTERP "/lib/ld-linux.so.2"
+# endif
+#endif
+
+/* var elf_interp dans *-gen.c */
+#ifdef CONFIG_TCC_ELFINTERP
+# define DEFAULT_ELFINTERP(s) CONFIG_TCC_ELFINTERP
+#else
+# define DEFAULT_ELFINTERP(s) default_elfinterp(s)
+#endif
+
+/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */
+#define TCC_LIBGCC USE_MUADIR(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1"
+
+/* -------------------------------------------- */
+/* include the target specific definitions */
+
+#define TARGET_DEFS_ONLY
+#ifdef TCC_TARGET_I386
+# include "x86/i386-gen.c"
+#endif
+#ifdef TCC_TARGET_X86_64
+# include "x86/x86_64-gen.c"
+#endif
+#ifdef TCC_TARGET_ARM
+# include "arm/arm-gen.c"
+#endif
+#ifdef TCC_TARGET_ARM64
+# include "arm/arm64-gen.c"
+#endif
+#ifdef TCC_TARGET_C67
+# include "coff.h"
+# include "tms320c67c67-gen.c"
+#endif
+#undef TARGET_DEFS_ONLY
+
+/* -------------------------------------------- */
+
+#define INCLUDE_STACK_SIZE 32
+#define IFDEF_STACK_SIZE 64
+#define VSTACK_SIZE 256
+#define STRING_MAX_SIZE 1024
+#define PACK_STACK_SIZE 8
+
+#define TOK_HASH_SIZE 8192 /* must be a power of two */
+#define TOK_ALLOC_INCR 512 /* must be a power of two */
+#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */
+
+/* token symbol management */
+typedef struct TokenSym {
+ struct TokenSym *hash_next;
+ struct Sym *sym_define; /* direct pointer to define */
+ struct Sym *sym_label; /* direct pointer to label */
+ struct Sym *sym_struct; /* direct pointer to structure */
+ struct Sym *sym_identifier; /* direct pointer to identifier */
+ int tok; /* token number */
+ int len;
+ char str[1];
+} TokenSym;
+
+#ifdef TCC_TARGET_PE
+typedef unsigned short nwchar_t;
+#else
+typedef int nwchar_t;
+#endif
+
+typedef struct CString {
+ int size; /* size in bytes */
+ void *data; /* either 'char *' or 'nwchar_t *' */
+ int size_allocated;
+ void *data_allocated; /* if non NULL, data has been malloced */
+} CString;
+
+/* type definition */
+typedef struct CType {
+ int t;
+ struct Sym *ref;
+} CType;
+
+/* constant value */
+typedef union CValue {
+ long double ld;
+ double d;
+ float f;
+ int i;
+ unsigned int ui;
+ unsigned int ul; /* address (should be unsigned long on 64 bit cpu) */
+ long long ll;
+ unsigned long long ull;
+ struct CString *cstr;
+ addr_t ptr_offset;
+ int tab[LDOUBLE_SIZE/4];
+} CValue;
+
+/* value on stack */
+typedef struct SValue {
+ CType type; /* type */
+ unsigned short r; /* register + flags */
+ unsigned short r2; /* second register, used for 'long long'
+ type. If not used, set to VT_CONST */
+ CValue c; /* constant, if VT_CONST */
+ struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */
+} SValue;
+
+struct Attribute {
+ unsigned
+ func_call : 3, /* calling convention (0..5), see below */
+ aligned : 5, /* alignement (0..16) */
+ packed : 1,
+ func_export : 1,
+ func_import : 1,
+ func_args : 5,
+ func_proto : 1,
+ mode : 4,
+ weak : 1,
+ visibility : 2,
+ fill : 8; // 8 bits left to fit well in union below
+};
+
+/* GNUC attribute definition */
+typedef struct AttributeDef {
+ struct Attribute a;
+ struct Section *section;
+ int alias_target; /* token */
+} AttributeDef;
+
+/* symbol management */
+typedef struct Sym {
+ int v; /* symbol token */
+ char *asm_label; /* associated asm label */
+ union {
+ long r; /* associated register */
+ struct Attribute a;
+ };
+ union {
+ long c; /* associated number */
+ int *d; /* define token stream */
+ };
+ CType type; /* associated type */
+ union {
+ struct Sym *next; /* next related symbol */
+ long jnext; /* next jump label */
+ };
+ struct Sym *prev; /* prev symbol in stack */
+ struct Sym *prev_tok; /* previous symbol for this token */
+} Sym;
+
+/* section definition */
+/* XXX: use directly ELF structure for parameters ? */
+/* special flag to indicate that the section should not be linked to
+ the other ones */
+#define SHF_PRIVATE 0x80000000
+
+/* special flag, too */
+#define SECTION_ABS ((void *)1)
+
+typedef struct Section {
+ unsigned long data_offset; /* current data offset */
+ unsigned char *data; /* section data */
+ unsigned long data_allocated; /* used for realloc() handling */
+ int sh_name; /* elf section name (only used during output) */
+ int sh_num; /* elf section number */
+ int sh_type; /* elf section type */
+ int sh_flags; /* elf section flags */
+ int sh_info; /* elf section info */
+ int sh_addralign; /* elf section alignment */
+ int sh_entsize; /* elf entry size */
+ unsigned long sh_size; /* section size (only used during output) */
+ addr_t sh_addr; /* address at which the section is relocated */
+ unsigned long sh_offset; /* file offset */
+ int nb_hashed_syms; /* used to resize the hash table */
+ struct Section *link; /* link to another section */
+ struct Section *reloc; /* corresponding section for relocation, if any */
+ struct Section *hash; /* hash table for symbols */
+ struct Section *next;
+ char name[1]; /* section name */
+} Section;
+
+typedef struct DLLReference {
+ int level;
+ void *handle;
+ char name[1];
+} DLLReference;
+
+/* -------------------------------------------------- */
+
+#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
+#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
+#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
+
+/* stored in 'Sym.c' field */
+#define FUNC_NEW 1 /* ansi function prototype */
+#define FUNC_OLD 2 /* old function prototype */
+#define FUNC_ELLIPSIS 3 /* ansi function prototype with ... */
+
+/* stored in 'Sym.r' field */
+#define FUNC_CDECL 0 /* standard c call */
+#define FUNC_STDCALL 1 /* pascal c call */
+#define FUNC_FASTCALL1 2 /* first param in %eax */
+#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */
+#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */
+#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */
+
+/* field 'Sym.t' for macros */
+#define MACRO_OBJ 0 /* object like macro */
+#define MACRO_FUNC 1 /* function like macro */
+
+/* field 'Sym.r' for C labels */
+#define LABEL_DEFINED 0 /* label is defined */
+#define LABEL_FORWARD 1 /* label is forward defined */
+#define LABEL_DECLARED 2 /* label is declared but never used */
+
+/* type_decl() types */
+#define TYPE_ABSTRACT 1 /* type without variable */
+#define TYPE_DIRECT 2 /* type with variable */
+
+#define IO_BUF_SIZE 8192
+
+typedef struct BufferedFile {
+ uint8_t *buf_ptr;
+ uint8_t *buf_end;
+ int fd;
+ struct BufferedFile *prev;
+ int line_num; /* current line number - here to simplify code */
+ int line_ref; /* tcc -E: last printed line */
+ int ifndef_macro; /* #ifndef macro / #endif search */
+ int ifndef_macro_saved; /* saved ifndef_macro */
+ int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */
+ char filename[1024]; /* filename */
+ unsigned char unget[4];
+ unsigned char buffer[1]; /* extra size for CH_EOB char */
+} BufferedFile;
+
+#define CH_EOB '\\' /* end of buffer or '\0' char in file */
+#define CH_EOF (-1) /* end of file */
+
+/* parsing state (used to save parser state to reparse part of the
+ source several times) */
+typedef struct ParseState {
+ const int *macro_ptr;
+ int line_num;
+ int tok;
+ CValue tokc;
+} ParseState;
+
+/* used to record tokens */
+typedef struct TokenString {
+ int *str;
+ int len;
+ int allocated_len;
+ int last_line_num;
+ /* used to chain token-strings with begin/end_macro() */
+ struct TokenString *prev;
+ const int *prev_ptr;
+ char alloc;
+} TokenString;
+
+/* inline functions */
+typedef struct InlineFunc {
+ TokenString func_str;
+ Sym *sym;
+ char filename[1];
+} InlineFunc;
+
+/* include file cache, used to find files faster and also to eliminate
+ inclusion if the include file is protected by #ifndef ... #endif */
+typedef struct CachedInclude {
+ int ifndef_macro;
+ int hash_next; /* -1 if none */
+ char filename[1]; /* path specified in #include */
+} CachedInclude;
+
+#define CACHED_INCLUDES_HASH_SIZE 512
+
+#ifdef CONFIG_TCC_ASM
+typedef struct ExprValue {
+ uint32_t v;
+ Sym *sym;
+} ExprValue;
+
+#define MAX_ASM_OPERANDS 30
+typedef struct ASMOperand {
+ int id; /* GCC 3 optionnal identifier (0 if number only supported */
+ char *constraint;
+ char asm_str[16]; /* computed asm string for operand */
+ SValue *vt; /* C value of the expression */
+ int ref_index; /* if >= 0, gives reference to a output constraint */
+ int input_index; /* if >= 0, gives reference to an input constraint */
+ int priority; /* priority, used to assign registers */
+ int reg; /* if >= 0, register number used for this operand */
+ int is_llong; /* true if double register value */
+ int is_memory; /* true if memory operand */
+ int is_rw; /* for '+' modifier */
+} ASMOperand;
+#endif
+
+struct sym_attr {
+ unsigned long got_offset;
+ unsigned long plt_offset;
+#ifdef TCC_TARGET_ARM
+ unsigned char plt_thumb_stub:1;
+#endif
+};
+
+struct TCCState {
+
+ int verbose; /* if true, display some information during compilation */
+ int nostdinc; /* if true, no standard headers are added */
+ int nostdlib; /* if true, no standard libraries are added */
+ int nocommon; /* if true, do not use common symbols for .bss data */
+ int static_link; /* if true, static linking is performed */
+ int rdynamic; /* if true, all symbols are exported */
+ int symbolic; /* if true, resolve symbols in the current module first */
+ int alacarte_link; /* if true, only link in referenced objects from archive */
+
+ char *tcc_lib_path; /* CONFIG_TCCDIR or -B option */
+ char *soname; /* as specified on the command line (-soname) */
+ char *rpath; /* as specified on the command line (-Wl,-rpath=) */
+
+ /* output type, see TCC_OUTPUT_XXX */
+ int output_type;
+ /* output format, see TCC_OUTPUT_FORMAT_xxx */
+ int output_format;
+
+ /* C language options */
+ int char_is_unsigned;
+ int leading_underscore;
+ int ms_extensions; /* allow nested named struct w/o identifier behave like unnamed */
+ int old_struct_init_code; /* use old algorithm to init array in struct when there is no '{' used.
+ Liuux 2.4.26 can't find initrd when compiled with a new algorithm */
+ int dollars_in_identifiers; /* allows '$' char in indentifiers */
+
+ /* warning switches */
+ int warn_write_strings;
+ int warn_unsupported;
+ int warn_error;
+ int warn_none;
+ int warn_implicit_function_declaration;
+
+ /* compile with debug symbol (and use them if error during execution) */
+ int do_debug;
+ int do_strip;
+#ifdef CONFIG_TCC_BCHECK
+ /* compile with built-in memory and bounds checker */
+ int do_bounds_check;
+#endif
+#ifdef TCC_TARGET_ARM
+ enum float_abi float_abi; /* float ABI of the generated code*/
+#endif
+
+ addr_t text_addr; /* address of text section */
+ int has_text_addr;
+
+ unsigned long section_align; /* section alignment */
+
+ char *init_symbol; /* symbols to call at load-time (not used currently) */
+ char *fini_symbol; /* symbols to call at unload-time (not used currently) */
+
+#ifdef TCC_TARGET_I386
+ int seg_size; /* 32. Can be 16 with i386 assembler (.code16) */
+#endif
+
+ /* array of all loaded dlls (including those referenced by loaded dlls) */
+ DLLReference **loaded_dlls;
+ int nb_loaded_dlls;
+
+ /* include paths */
+ char **include_paths;
+ int nb_include_paths;
+
+ char **sysinclude_paths;
+ int nb_sysinclude_paths;
+
+ /* library paths */
+ char **library_paths;
+ int nb_library_paths;
+
+ /* crt?.o object path */
+ char **crt_paths;
+ int nb_crt_paths;
+
+ /* error handling */
+ void *error_opaque;
+ void (*error_func)(void *opaque, const char *msg);
+ int error_set_jmp_enabled;
+ jmp_buf error_jmp_buf;
+ int nb_errors;
+
+ /* output file for preprocessing (-E) */
+ FILE *ppfp;
+ enum {
+ LINE_MACRO_OUTPUT_FORMAT_GCC,
+ LINE_MACRO_OUTPUT_FORMAT_NONE,
+ LINE_MACRO_OUTPUT_FORMAT_STD,
+ } Pflag; /* -P switch */
+ int dflag; /* -dX value */
+
+ /* for -MD/-MF: collected dependencies for this compilation */
+ char **target_deps;
+ int nb_target_deps;
+
+ /* compilation */
+ BufferedFile *include_stack[INCLUDE_STACK_SIZE];
+ BufferedFile **include_stack_ptr;
+
+ int ifdef_stack[IFDEF_STACK_SIZE];
+ int *ifdef_stack_ptr;
+
+ /* included files enclosed with #ifndef MACRO */
+ int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE];
+ CachedInclude **cached_includes;
+ int nb_cached_includes;
+
+ /* #pragma pack stack */
+ int pack_stack[PACK_STACK_SIZE];
+ int *pack_stack_ptr;
+ char **pragma_libs;
+ int nb_pragma_libs;
+
+ /* inline functions are stored as token lists and compiled last
+ only if referenced */
+ struct InlineFunc **inline_fns;
+ int nb_inline_fns;
+
+ /* sections */
+ Section **sections;
+ int nb_sections; /* number of sections, including first dummy section */
+
+ Section **priv_sections;
+ int nb_priv_sections; /* number of private sections */
+
+ /* got & plt handling */
+ Section *got;
+ Section *plt;
+ struct sym_attr *sym_attrs;
+ int nb_sym_attrs;
+ /* give the correspondance from symtab indexes to dynsym indexes */
+ int *symtab_to_dynsym;
+
+ /* temporary dynamic symbol sections (for dll loading) */
+ Section *dynsymtab_section;
+ /* exported dynamic symbol section */
+ Section *dynsym;
+ /* copy of the gobal symtab_section variable */
+ Section *symtab;
+ /* tiny assembler state */
+ Sym *asm_labels;
+
+#ifdef TCC_TARGET_PE
+ /* PE info */
+ int pe_subsystem;
+ unsigned pe_file_align;
+ unsigned pe_stack_size;
+# ifdef TCC_TARGET_X86_64
+ Section *uw_pdata;
+ int uw_sym;
+ unsigned uw_offs;
+# endif
+#endif
+
+#ifdef TCC_IS_NATIVE
+ const char *runtime_main;
+ /* for tcc_relocate */
+ void *runtime_mem;
+# ifdef HAVE_SELINUX
+ void *write_mem;
+ unsigned long mem_size;
+# endif
+#endif
+
+ /* used by main and tcc_parse_args only */
+ char **files; /* files seen on command line */
+ int nb_files; /* number thereof */
+ int nb_libraries; /* number of libs thereof */
+ char *outfile; /* output filename */
+ char *option_m; /* only -m32/-m64 handled */
+ int print_search_dirs; /* option */
+ int option_r; /* option -r */
+ int do_bench; /* option -bench */
+ int gen_deps; /* option -MD */
+ char *deps_outfile; /* option -MF */
+};
+
+/* The current value can be: */
+#define VT_VALMASK 0x003f /* mask for value location, register or: */
+#define VT_CONST 0x0030 /* constant in vc (must be first non register value) */
+#define VT_LLOCAL 0x0031 /* lvalue, offset on stack */
+#define VT_LOCAL 0x0032 /* offset on stack */
+#define VT_CMP 0x0033 /* the value is stored in processor flags (in vc) */
+#define VT_JMP 0x0034 /* value is the consequence of jmp true (even) */
+#define VT_JMPI 0x0035 /* value is the consequence of jmp false (odd) */
+#define VT_REF 0x0040 /* value is pointer to structure rather than address */
+#define VT_LVAL 0x0100 /* var is an lvalue */
+#define VT_SYM 0x0200 /* a symbol value is added */
+#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for
+ char/short stored in integer registers) */
+#define VT_MUSTBOUND 0x0800 /* bound checking must be done before
+ dereferencing value */
+#define VT_BOUNDED 0x8000 /* value is bounded. The address of the
+ bounding function call point is in vc */
+#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */
+#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */
+#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */
+#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
+
+/* types */
+#define VT_BTYPE 0x000f /* mask for basic type */
+#define VT_INT 0 /* integer type */
+#define VT_BYTE 1 /* signed byte type */
+#define VT_SHORT 2 /* short type */
+#define VT_VOID 3 /* void type */
+#define VT_PTR 4 /* pointer */
+#define VT_ENUM 5 /* enum definition */
+#define VT_FUNC 6 /* function type */
+#define VT_STRUCT 7 /* struct/union definition */
+#define VT_FLOAT 8 /* IEEE float */
+#define VT_DOUBLE 9 /* IEEE double */
+#define VT_LDOUBLE 10 /* IEEE long double */
+#define VT_BOOL 11 /* ISOC99 boolean type */
+#define VT_LLONG 12 /* 64 bit integer */
+#define VT_LONG 13 /* long integer (NEVER USED as type, only
+ during parsing) */
+#define VT_QLONG 14 /* 128-bit integer. No longer used. */
+#define VT_QFLOAT 15 /* 128-bit float. No longer used. */
+#define VT_UNSIGNED 0x0010 /* unsigned type */
+#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */
+#define VT_BITFIELD 0x0040 /* bitfield modifier */
+#define VT_CONSTANT 0x0800 /* const modifier */
+#define VT_VOLATILE 0x1000 /* volatile modifier */
+#define VT_DEFSIGN 0x2000 /* signed type */
+#define VT_VLA 0x00020000 /* VLA type (also has VT_PTR and VT_ARRAY) */
+
+/* storage */
+#define VT_EXTERN 0x00000080 /* extern definition */
+#define VT_STATIC 0x00000100 /* static variable */
+#define VT_TYPEDEF 0x00000200 /* typedef definition */
+#define VT_INLINE 0x00000400 /* inline definition */
+#define VT_IMPORT 0x00004000 /* win32: extern data imported from dll */
+#define VT_EXPORT 0x00008000 /* win32: data exported from dll */
+#define VT_WEAK 0x00010000 /* weak symbol */
+#define VT_TLS 0x00040000 /* thread-local storage */
+#define VT_VIS_SHIFT 19 /* shift for symbol visibility, overlapping
+ bitfield values, because bitfields never
+ have linkage and hence never have
+ visibility. */
+#define VT_VIS_SIZE 2 /* We have four visibilities. */
+#define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT)
+
+#define VT_STRUCT_SHIFT 19 /* shift for bitfield shift values (max: 32 - 2*6) */
+
+
+/* type mask (except storage) */
+#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE | VT_IMPORT | VT_EXPORT | VT_WEAK | VT_VIS_MASK)
+#define VT_TYPE (~(VT_STORAGE))
+
+/* token values */
+
+/* warning: the following compare tokens depend on i386 asm code */
+#define TOK_ULT 0x92
+#define TOK_UGE 0x93
+#define TOK_EQ 0x94
+#define TOK_NE 0x95
+#define TOK_ULE 0x96
+#define TOK_UGT 0x97
+#define TOK_Nset 0x98
+#define TOK_Nclear 0x99
+#define TOK_LT 0x9c
+#define TOK_GE 0x9d
+#define TOK_LE 0x9e
+#define TOK_GT 0x9f
+
+#define TOK_LAND 0xa0
+#define TOK_LOR 0xa1
+#define TOK_DEC 0xa2
+#define TOK_MID 0xa3 /* inc/dec, to void constant */
+#define TOK_INC 0xa4
+#define TOK_UDIV 0xb0 /* unsigned division */
+#define TOK_UMOD 0xb1 /* unsigned modulo */
+#define TOK_PDIV 0xb2 /* fast division with undefined rounding for pointers */
+
+/* tokens that carry values (in additional token string space / tokc) --> */
+#define TOK_CCHAR 0xb3 /* char constant in tokc */
+#define TOK_LCHAR 0xb4
+#define TOK_CINT 0xb5 /* number in tokc */
+#define TOK_CUINT 0xb6 /* unsigned int constant */
+#define TOK_CLLONG 0xb7 /* long long constant */
+#define TOK_CULLONG 0xb8 /* unsigned long long constant */
+#define TOK_STR 0xb9 /* pointer to string in tokc */
+#define TOK_LSTR 0xba
+#define TOK_CFLOAT 0xbb /* float constant */
+#define TOK_CDOUBLE 0xbc /* double constant */
+#define TOK_CLDOUBLE 0xbd /* long double constant */
+#define TOK_PPNUM 0xbe /* preprocessor number */
+#define TOK_PPSTR 0xbf /* preprocessor string */
+#define TOK_LINENUM 0xc0 /* line number info */
+/* <-- */
+
+#define TOK_UMULL 0xc2 /* unsigned 32x32 -> 64 mul */
+#define TOK_ADDC1 0xc3 /* add with carry generation */
+#define TOK_ADDC2 0xc4 /* add with carry use */
+#define TOK_SUBC1 0xc5 /* add with carry generation */
+#define TOK_SUBC2 0xc6 /* add with carry use */
+#define TOK_ARROW 0xc7
+#define TOK_DOTS 0xc8 /* three dots */
+#define TOK_SHR 0xc9 /* unsigned shift right */
+#define TOK_TWOSHARPS 0xca /* ## preprocessing token */
+#define TOK_PLCHLDR 0xcb /* placeholder token as defined in C99 */
+#define TOK_NOSUBST 0xcc /* means following token has already been pp'd */
+
+#define TOK_SHL 0x01 /* shift left */
+#define TOK_SAR 0x02 /* signed shift right */
+
+/* assignement operators : normal operator or 0x80 */
+#define TOK_A_MOD 0xa5
+#define TOK_A_AND 0xa6
+#define TOK_A_MUL 0xaa
+#define TOK_A_ADD 0xab
+#define TOK_A_SUB 0xad
+#define TOK_A_DIV 0xaf
+#define TOK_A_XOR 0xde
+#define TOK_A_OR 0xfc
+#define TOK_A_SHL 0x81
+#define TOK_A_SAR 0x82
+
+#ifndef offsetof
+#define offsetof(type, field) ((size_t) &((type *)0)->field)
+#endif
+
+#ifndef countof
+#define countof(tab) (sizeof(tab) / sizeof((tab)[0]))
+#endif
+
+#define TOK_EOF (-1) /* end of file */
+#define TOK_LINEFEED 10 /* line feed */
+
+/* all identificators and strings have token above that */
+#define TOK_IDENT 256
+
+#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x)
+#define TOK_ASM_int TOK_INT
+#define TOK_ASM_weak TOK_WEAK1
+
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+/* only used for i386 asm opcodes definitions */
+#define DEF_BWL(x) \
+ DEF(TOK_ASM_ ## x ## b, #x "b") \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x, #x)
+#define DEF_WL(x) \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x, #x)
+#ifdef TCC_TARGET_X86_64
+# define DEF_BWLQ(x) \
+ DEF(TOK_ASM_ ## x ## b, #x "b") \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x ## q, #x "q") \
+ DEF(TOK_ASM_ ## x, #x)
+# define DEF_WLQ(x) \
+ DEF(TOK_ASM_ ## x ## w, #x "w") \
+ DEF(TOK_ASM_ ## x ## l, #x "l") \
+ DEF(TOK_ASM_ ## x ## q, #x "q") \
+ DEF(TOK_ASM_ ## x, #x)
+# define DEF_BWLX DEF_BWLQ
+# define DEF_WLX DEF_WLQ
+/* number of sizes + 1 */
+# define NBWLX 5
+#else
+# define DEF_BWLX DEF_BWL
+# define DEF_WLX DEF_WL
+/* number of sizes + 1 */
+# define NBWLX 4
+#endif
+
+#define DEF_FP1(x) \
+ DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \
+ DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \
+ DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \
+ DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s")
+
+#define DEF_FP(x) \
+ DEF(TOK_ASM_ ## f ## x, "f" #x ) \
+ DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \
+ DEF_FP1(x)
+
+#define DEF_ASMTEST(x,suffix) \
+ DEF_ASM(x ## o ## suffix) \
+ DEF_ASM(x ## no ## suffix) \
+ DEF_ASM(x ## b ## suffix) \
+ DEF_ASM(x ## c ## suffix) \
+ DEF_ASM(x ## nae ## suffix) \
+ DEF_ASM(x ## nb ## suffix) \
+ DEF_ASM(x ## nc ## suffix) \
+ DEF_ASM(x ## ae ## suffix) \
+ DEF_ASM(x ## e ## suffix) \
+ DEF_ASM(x ## z ## suffix) \
+ DEF_ASM(x ## ne ## suffix) \
+ DEF_ASM(x ## nz ## suffix) \
+ DEF_ASM(x ## be ## suffix) \
+ DEF_ASM(x ## na ## suffix) \
+ DEF_ASM(x ## nbe ## suffix) \
+ DEF_ASM(x ## a ## suffix) \
+ DEF_ASM(x ## s ## suffix) \
+ DEF_ASM(x ## ns ## suffix) \
+ DEF_ASM(x ## p ## suffix) \
+ DEF_ASM(x ## pe ## suffix) \
+ DEF_ASM(x ## np ## suffix) \
+ DEF_ASM(x ## po ## suffix) \
+ DEF_ASM(x ## l ## suffix) \
+ DEF_ASM(x ## nge ## suffix) \
+ DEF_ASM(x ## nl ## suffix) \
+ DEF_ASM(x ## ge ## suffix) \
+ DEF_ASM(x ## le ## suffix) \
+ DEF_ASM(x ## ng ## suffix) \
+ DEF_ASM(x ## nle ## suffix) \
+ DEF_ASM(x ## g ## suffix)
+
+#endif /* defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 */
+
+enum tcc_token {
+ TOK_LAST = TOK_IDENT - 1,
+#define DEF(id, str) id,
+#include "tcctok.h"
+#undef DEF
+};
+
+#define TOK_UIDENT TOK_DEFINE
+
+/* space exlcuding newline */
+static inline int is_space(int ch)
+{
+ return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r';
+}
+
+static inline int isid(int c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_';
+}
+
+static inline int isnum(int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+static inline int isoct(int c)
+{
+ return c >= '0' && c <= '7';
+}
+
+static inline int toup(int c)
+{
+ return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c;
+}
+
+#ifndef PUB_FUNC
+# define PUB_FUNC
+#endif
+
+#ifdef ONE_SOURCE
+#define ST_INLN static inline
+#define ST_FUNC static
+#define ST_DATA static
+#else
+#define ST_INLN
+#define ST_FUNC
+#define ST_DATA extern
+#endif
+
+/* ------------ libtcc.c ------------ */
+
+/* use GNU C extensions */
+ST_DATA int gnu_ext;
+/* use Tiny C extensions */
+ST_DATA int tcc_ext;
+/* XXX: get rid of this ASAP */
+ST_DATA struct TCCState *tcc_state;
+
+#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */
+#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */
+#define AFF_PREPROCESS 0x0004 /* preprocess file */
+
+/* public functions currently used by the tcc main function */
+PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s);
+PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s);
+PUB_FUNC char *pstrncpy(char *out, const char *in, size_t num);
+PUB_FUNC char *tcc_basename(const char *name);
+PUB_FUNC char *tcc_fileextension (const char *name);
+
+#ifndef MEM_DEBUG
+PUB_FUNC void tcc_free(void *ptr);
+PUB_FUNC void *tcc_malloc(unsigned long size);
+PUB_FUNC void *tcc_mallocz(unsigned long size);
+PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size);
+PUB_FUNC char *tcc_strdup(const char *str);
+#else
+#define tcc_free(ptr) tcc_free_debug(ptr)
+#define tcc_malloc(size) tcc_malloc_debug(size, __FILE__, __LINE__)
+#define tcc_mallocz(size) tcc_mallocz_debug(size, __FILE__, __LINE__)
+#define tcc_realloc(ptr,size) tcc_realloc_debug(ptr, size, __FILE__, __LINE__)
+#define tcc_strdup(str) tcc_strdup_debug(str, __FILE__, __LINE__)
+PUB_FUNC void tcc_free_debug(void *ptr);
+PUB_FUNC void *tcc_malloc_debug(unsigned long size, const char *file, int line);
+PUB_FUNC void *tcc_mallocz_debug(unsigned long size, const char *file, int line);
+PUB_FUNC void *tcc_realloc_debug(void *ptr, unsigned long size, const char *file, int line);
+PUB_FUNC char *tcc_strdup_debug(const char *str, const char *file, int line);
+#endif
+
+#define free(p) use_tcc_free(p)
+#define malloc(s) use_tcc_malloc(s)
+#define realloc(p, s) use_tcc_realloc(p, s)
+#undef strdup
+#define strdup(s) use_tcc_strdup(s)
+PUB_FUNC void tcc_memstats(int bench);
+PUB_FUNC void tcc_error_noabort(const char *fmt, ...);
+PUB_FUNC NORETURN void tcc_error(const char *fmt, ...);
+PUB_FUNC void tcc_warning(const char *fmt, ...);
+
+/* other utilities */
+ST_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data);
+ST_FUNC void dynarray_reset(void *pp, int *n);
+ST_FUNC void cstr_ccat(CString *cstr, int ch);
+ST_FUNC void cstr_cat(CString *cstr, const char *str);
+ST_FUNC void cstr_wccat(CString *cstr, int ch);
+ST_FUNC void cstr_new(CString *cstr);
+ST_FUNC void cstr_free(CString *cstr);
+ST_FUNC void cstr_reset(CString *cstr);
+
+ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags);
+ST_FUNC void section_realloc(Section *sec, unsigned long new_size);
+ST_FUNC void *section_ptr_add(Section *sec, addr_t size);
+ST_FUNC void section_reserve(Section *sec, unsigned long size);
+ST_FUNC Section *find_section(TCCState *s1, const char *name);
+
+ST_FUNC void put_extern_sym2(Sym *sym, Section *section, addr_t value, unsigned long size, int can_add_underscore);
+ST_FUNC void put_extern_sym(Sym *sym, Section *section, addr_t value, unsigned long size);
+ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type);
+ST_FUNC void greloca(Section *s, Sym *sym, unsigned long offset, int type, addr_t addend);
+
+ST_INLN void sym_free(Sym *sym);
+ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c);
+ST_FUNC Sym *sym_find2(Sym *s, int v);
+ST_FUNC Sym *sym_push(int v, CType *type, int r, int c);
+ST_FUNC void sym_pop(Sym **ptop, Sym *b);
+ST_INLN Sym *struct_find(int v);
+ST_INLN Sym *sym_find(int v);
+ST_FUNC Sym *global_identifier_push(int v, int t, int c);
+
+ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen);
+ST_FUNC int tcc_open(TCCState *s1, const char *filename);
+ST_FUNC void tcc_close(void);
+
+ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags, int filetype);
+ST_FUNC int tcc_add_crt(TCCState *s, const char *filename);
+
+#ifndef TCC_TARGET_PE
+ST_FUNC int tcc_add_dll(TCCState *s, const char *filename, int flags);
+#endif
+
+ST_FUNC void tcc_add_pragma_libs(TCCState *s1);
+PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f);
+
+PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time);
+PUB_FUNC int tcc_parse_args(TCCState *s, int argc, char **argv);
+PUB_FUNC void tcc_set_environment(TCCState *s);
+
+/* ------------ tccpp.c ------------ */
+
+ST_DATA struct BufferedFile *file;
+ST_DATA int ch, tok;
+ST_DATA CValue tokc;
+ST_DATA const int *macro_ptr;
+ST_DATA int parse_flags;
+ST_DATA int tok_flags;
+ST_DATA CString tokcstr; /* current parsed string, if any */
+
+/* display benchmark infos */
+ST_DATA int total_lines;
+ST_DATA int total_bytes;
+ST_DATA int tok_ident;
+ST_DATA TokenSym **table_ident;
+
+#define TOK_FLAG_BOL 0x0001 /* beginning of line before */
+#define TOK_FLAG_BOF 0x0002 /* beginning of file before */
+#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */
+#define TOK_FLAG_EOF 0x0008 /* end of file */
+
+#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */
+#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */
+#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
+ token. line feed is also
+ returned at eof */
+#define PARSE_FLAG_ASM_FILE 0x0008 /* we processing an asm file: '#' can be used for line comment, etc. */
+#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */
+#define PARSE_FLAG_ACCEPT_STRAYS 0x0020 /* next() returns '\\' token */
+#define PARSE_FLAG_TOK_STR 0x0040 /* return parsed strings instead of TOK_PPSTR */
+
+ST_FUNC TokenSym *tok_alloc(const char *str, int len);
+ST_FUNC const char *get_tok_str(int v, CValue *cv);
+ST_FUNC void begin_macro(TokenString *str, int alloc);
+ST_FUNC void end_macro(void);
+ST_FUNC void save_parse_state(ParseState *s);
+ST_FUNC void restore_parse_state(ParseState *s);
+ST_INLN void tok_str_new(TokenString *s);
+ST_FUNC void tok_str_free(int *str);
+ST_FUNC void tok_str_add(TokenString *s, int t);
+ST_FUNC void tok_str_add_tok(TokenString *s);
+ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg);
+ST_FUNC void define_undef(Sym *s);
+ST_INLN Sym *define_find(int v);
+ST_FUNC void free_defines(Sym *b);
+ST_FUNC void print_defines(void);
+ST_FUNC Sym *label_find(int v);
+ST_FUNC Sym *label_push(Sym **ptop, int v, int flags);
+ST_FUNC void label_pop(Sym **ptop, Sym *slast);
+ST_FUNC void parse_define(void);
+ST_FUNC void preprocess(int is_bof);
+ST_FUNC void next_nomacro(void);
+ST_FUNC void next(void);
+ST_INLN void unget_tok(int last_tok);
+ST_FUNC void preprocess_init(TCCState *s1);
+ST_FUNC void preprocess_new(void);
+ST_FUNC void preprocess_delete(void);
+ST_FUNC int tcc_preprocess(TCCState *s1);
+ST_FUNC void skip(int c);
+ST_FUNC NORETURN void expect(const char *msg);
+
+/* ------------ tccgen.c ------------ */
+
+ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
+ST_DATA Section *cur_text_section; /* current section where function code is generated */
+#ifdef CONFIG_TCC_ASM
+ST_DATA Section *last_text_section; /* to handle .previous asm directive */
+#endif
+#ifdef CONFIG_TCC_BCHECK
+/* bound check related sections */
+ST_DATA Section *bounds_section; /* contains global data bound description */
+ST_DATA Section *lbounds_section; /* contains local data bound description */
+#endif
+/* symbol sections */
+ST_DATA Section *symtab_section, *strtab_section;
+/* debug sections */
+ST_DATA Section *stab_section, *stabstr_section;
+
+#define SYM_POOL_NB (8192 / sizeof(Sym))
+ST_DATA Sym *sym_free_first;
+ST_DATA void **sym_pools;
+ST_DATA int nb_sym_pools;
+
+ST_DATA Sym *global_stack;
+ST_DATA Sym *local_stack;
+ST_DATA Sym *local_label_stack;
+ST_DATA Sym *global_label_stack;
+ST_DATA Sym *define_stack;
+ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
+ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop, *pvtop;
+#define vstack (__vstack + 1)
+ST_DATA int rsym, anon_sym, ind, loc;
+
+ST_DATA int const_wanted; /* true if constant wanted */
+ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
+ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
+ST_DATA CType func_vt; /* current function return type (used by return instruction) */
+ST_DATA int func_var; /* true if current function is variadic */
+ST_DATA int func_vc;
+ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
+ST_DATA const char *funcname;
+
+ST_FUNC void check_vstack(void);
+ST_INLN int is_float(int t);
+ST_FUNC int ieee_finite(double d);
+ST_FUNC void test_lvalue(void);
+ST_FUNC void swap(int *p, int *q);
+ST_FUNC void vpushi(int v);
+ST_FUNC Sym *external_global_sym(int v, CType *type, int r);
+ST_FUNC void vset(CType *type, int r, int v);
+ST_FUNC void vswap(void);
+ST_FUNC void vpush_global_sym(CType *type, int v);
+ST_FUNC void vrote(SValue *e, int n);
+ST_FUNC void vrott(int n);
+ST_FUNC void vrotb(int n);
+#ifdef TCC_TARGET_ARM
+ST_FUNC int get_reg_ex(int rc, int rc2);
+ST_FUNC void lexpand_nr(void);
+#endif
+ST_FUNC void vpushv(SValue *v);
+ST_FUNC void save_reg(int r);
+ST_FUNC int get_reg(int rc);
+ST_FUNC void save_regs(int n);
+ST_FUNC void gaddrof(void);
+ST_FUNC int gv(int rc);
+ST_FUNC void gv2(int rc1, int rc2);
+ST_FUNC void vdup(void);
+ST_FUNC void vpop(void);
+ST_FUNC void gen_op(int op);
+ST_FUNC int type_size(CType *type, int *a);
+ST_FUNC void mk_pointer(CType *type);
+ST_FUNC void vstore(void);
+ST_FUNC void inc(int post, int c);
+ST_FUNC void parse_asm_str(CString *astr);
+ST_FUNC int lvalue_type(int t);
+ST_FUNC void indir(void);
+ST_FUNC void unary(void);
+ST_FUNC void expr_prod(void);
+ST_FUNC void expr_sum(void);
+ST_FUNC void gexpr(void);
+ST_FUNC int expr_const(void);
+ST_FUNC void gen_inline_functions(void);
+ST_FUNC void decl(int l);
+#if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67
+ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size);
+#endif
+#if defined TCC_TARGET_X86_64 && !defined TCC_TARGET_PE
+ST_FUNC int classify_x86_64_va_arg(CType *ty);
+#endif
+
+/* ------------ tccelf.c ------------ */
+
+#define TCC_OUTPUT_FORMAT_ELF 0 /* default output format: ELF */
+#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */
+#define TCC_OUTPUT_FORMAT_COFF 2 /* COFF */
+
+#define ARMAG "!<arch>\012" /* For COFF and a.out archives */
+
+typedef struct {
+ unsigned int n_strx; /* index into string table of name */
+ unsigned char n_type; /* type of symbol */
+ unsigned char n_other; /* misc info (usually empty) */
+ unsigned short n_desc; /* description field */
+ unsigned int n_value; /* value of symbol */
+} Stab_Sym;
+
+ST_FUNC Section *new_symtab(TCCState *s1, const char *symtab_name, int sh_type, int sh_flags, const char *strtab_name, const char *hash_name, int hash_sh_flags);
+
+ST_FUNC int put_elf_str(Section *s, const char *sym);
+ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int shndx, const char *name);
+ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size, int info, int other, int sh_num, const char *name);
+ST_FUNC int find_elf_sym(Section *s, const char *name);
+ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, int type, int symbol);
+ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, int type, int symbol, addr_t addend);
+
+ST_FUNC void put_stabs(const char *str, int type, int other, int desc, unsigned long value);
+ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc, unsigned long value, Section *sec, int sym_index);
+ST_FUNC void put_stabn(int type, int other, int desc, int value);
+ST_FUNC void put_stabd(int type, int other, int desc);
+
+ST_FUNC void relocate_common_syms(void);
+ST_FUNC void relocate_syms(TCCState *s1, int do_resolve);
+ST_FUNC void relocate_section(TCCState *s1, Section *s);
+ST_FUNC void relocate_plt(TCCState *s1);
+
+ST_FUNC void tcc_add_linker_symbols(TCCState *s1);
+ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
+ST_FUNC int tcc_load_archive(TCCState *s1, int fd);
+ST_FUNC void tcc_add_bcheck(TCCState *s1);
+
+ST_FUNC void build_got_entries(TCCState *s1);
+ST_FUNC void tcc_add_runtime(TCCState *s1);
+
+ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err);
+#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
+ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name);
+#endif
+
+#ifndef TCC_TARGET_PE
+ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level);
+ST_FUNC int tcc_load_ldscript(TCCState *s1);
+ST_FUNC uint8_t *parse_comment(uint8_t *p);
+ST_FUNC void minp(void);
+ST_INLN void inp(void);
+ST_FUNC int handle_eob(void);
+#endif
+
+/* ------------ xxx-gen.c ------------ */
+
+ST_DATA const int reg_classes[NB_REGS];
+
+ST_FUNC void gsym_addr(int t, int a);
+ST_FUNC void gsym(int t);
+ST_FUNC void load(int r, SValue *sv);
+ST_FUNC void store(int r, SValue *v);
+ST_FUNC int regargs_nregs(RegArgs *args);
+ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *align, int *regsize, RegArgs *args);
+ST_FUNC void gfunc_call(int nb_args);
+ST_FUNC void gfunc_prolog(CType *func_type);
+ST_FUNC void gfunc_epilog(void);
+ST_FUNC int gjmp(int t);
+ST_FUNC void gjmp_addr(int a);
+ST_FUNC int gtst(int inv, int t);
+ST_FUNC void gen_opi(int op);
+ST_FUNC void gen_opf(int op);
+ST_FUNC void gen_cvt_ftoi(int t);
+ST_FUNC void gen_cvt_ftof(int t);
+ST_FUNC void ggoto(void);
+#ifndef TCC_TARGET_C67
+ST_FUNC void o(unsigned int c);
+#endif
+#ifndef TCC_TARGET_ARM
+ST_FUNC void gen_cvt_itof(int t);
+#endif
+ST_FUNC void gen_vla_sp_save(int addr);
+ST_FUNC void gen_vla_sp_restore(int addr);
+ST_FUNC void gen_vla_alloc(CType *type, int align);
+
+/* ------------ i386-gen.c ------------ */
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+ST_FUNC void g(int c);
+ST_FUNC int oad(int c, int s);
+ST_FUNC void gen_le16(int c);
+ST_FUNC void gen_le32(int c);
+ST_FUNC void gen_addr32(int r, Sym *sym, int c);
+ST_FUNC void gen_addrpc32(int r, Sym *sym, int c);
+#endif
+
+#ifdef CONFIG_TCC_BCHECK
+ST_FUNC void gen_bounded_ptr_add(void);
+ST_FUNC void gen_bounded_ptr_deref(void);
+#endif
+
+/* ------------ x86_64-gen.c ------------ */
+#ifdef TCC_TARGET_X86_64
+ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c);
+ST_FUNC void gen_opl(int op);
+#endif
+
+/* ------------ arm-gen.c ------------ */
+#ifdef TCC_TARGET_ARM
+#if defined(TCC_ARM_EABI) && !defined(CONFIG_TCC_ELFINTERP)
+ST_FUNC char *default_elfinterp(struct TCCState *s);
+#endif
+ST_FUNC void arm_init(struct TCCState *s);
+ST_FUNC uint32_t encbranch(int pos, int addr, int fail);
+ST_FUNC void gen_cvt_itof1(int t);
+#endif
+
+/* ------------ arm64-gen.c ------------ */
+#ifdef TCC_TARGET_ARM64
+ST_FUNC void gen_cvt_sxtw(void);
+ST_FUNC void gen_opl(int op);
+ST_FUNC void greturn(void);
+ST_FUNC void gen_va_start(void);
+ST_FUNC void gen_va_arg(CType *t);
+ST_FUNC void gen_clear_cache(void);
+#endif
+
+/* ------------ c67-gen.c ------------ */
+#ifdef TCC_TARGET_C67
+#endif
+
+/* ------------ tcccoff.c ------------ */
+
+#ifdef TCC_TARGET_COFF
+ST_FUNC int tcc_output_coff(TCCState *s1, FILE *f);
+ST_FUNC int tcc_load_coff(TCCState * s1, int fd);
+#endif
+
+/* ------------ tccasm.c ------------ */
+ST_FUNC void asm_instr(void);
+ST_FUNC void asm_global_instr(void);
+#ifdef CONFIG_TCC_ASM
+ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp);
+ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
+ST_FUNC int asm_int_expr(TCCState *s1);
+ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess);
+/* ------------ i386-asm.c ------------ */
+ST_FUNC void gen_expr32(ExprValue *pe);
+ST_FUNC void asm_opcode(TCCState *s1, int opcode);
+ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg);
+ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier);
+ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg);
+ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str);
+#endif
+
+/* ------------ tccpe.c -------------- */
+#ifdef TCC_TARGET_PE
+ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd);
+ST_FUNC int pe_output_file(TCCState * s1, const char *filename);
+ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value);
+ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2);
+#ifdef TCC_TARGET_X86_64
+ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack);
+#endif
+/* symbol properties stored in Elf32_Sym->st_other */
+# define ST_PE_EXPORT 0x10
+# define ST_PE_IMPORT 0x20
+# define ST_PE_STDCALL 0x40
+#endif
+
+/* ------------ tccrun.c ----------------- */
+#ifdef TCC_IS_NATIVE
+#ifdef CONFIG_TCC_STATIC
+#define RTLD_LAZY 0x001
+#define RTLD_NOW 0x002
+#define RTLD_GLOBAL 0x100
+#define RTLD_DEFAULT NULL
+/* dummy function for profiling */
+ST_FUNC void *dlopen(const char *filename, int flag);
+ST_FUNC void dlclose(void *p);
+ST_FUNC const char *dlerror(void);
+ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol);
+#elif !defined _WIN32
+ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol);
+#endif
+
+#ifdef CONFIG_TCC_BACKTRACE
+ST_DATA int rt_num_callers;
+ST_DATA const char **rt_bound_error_msg;
+ST_DATA void *rt_prog_main;
+ST_FUNC void tcc_set_num_callers(int n);
+#endif
+#endif
+
+/********************************************************/
+#undef ST_DATA
+#ifdef ONE_SOURCE
+#define ST_DATA static
+#else
+#define ST_DATA
+#endif
+/********************************************************/
+#endif /* _TCC_H */
diff --git a/src/tccasm.c b/src/tccasm.c
new file mode 100644
index 0000000..b184c88
--- /dev/null
+++ b/src/tccasm.c
@@ -0,0 +1,1130 @@
+/*
+ * GAS like assembler for TCC
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+#ifdef CONFIG_TCC_ASM
+
+ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
+{
+ char buf[64];
+ TokenSym *ts;
+
+ snprintf(buf, sizeof(buf), "L..%u", n);
+ ts = tok_alloc(buf, strlen(buf));
+ return ts->tok;
+}
+
+ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
+
+/* We do not use the C expression parser to handle symbols. Maybe the
+ C expression parser could be tweaked to do so. */
+
+static void asm_expr_unary(TCCState *s1, ExprValue *pe)
+{
+ Sym *sym;
+ int op, n, label;
+ const char *p;
+
+ switch(tok) {
+ case TOK_PPNUM:
+ p = tokc.cstr->data;
+ n = strtoul(p, (char **)&p, 0);
+ if (*p == 'b' || *p == 'f') {
+ /* backward or forward label */
+ label = asm_get_local_label_name(s1, n);
+ sym = label_find(label);
+ if (*p == 'b') {
+ /* backward : find the last corresponding defined label */
+ if (sym && sym->r == 0)
+ sym = sym->prev_tok;
+ if (!sym)
+ tcc_error("local label '%d' not found backward", n);
+ } else {
+ /* forward */
+ if (!sym || sym->r) {
+ /* if the last label is defined, then define a new one */
+ sym = label_push(&s1->asm_labels, label, 0);
+ sym->type.t = VT_STATIC | VT_VOID;
+ }
+ }
+ pe->v = 0;
+ pe->sym = sym;
+ } else if (*p == '\0') {
+ pe->v = n;
+ pe->sym = NULL;
+ } else {
+ tcc_error("invalid number syntax");
+ }
+ next();
+ break;
+ case '+':
+ next();
+ asm_expr_unary(s1, pe);
+ break;
+ case '-':
+ case '~':
+ op = tok;
+ next();
+ asm_expr_unary(s1, pe);
+ if (pe->sym)
+ tcc_error("invalid operation with label");
+ if (op == '-')
+ pe->v = -pe->v;
+ else
+ pe->v = ~pe->v;
+ break;
+ case TOK_CCHAR:
+ case TOK_LCHAR:
+ pe->v = tokc.i;
+ pe->sym = NULL;
+ next();
+ break;
+ case '(':
+ next();
+ asm_expr(s1, pe);
+ skip(')');
+ break;
+ default:
+ if (tok >= TOK_IDENT) {
+ /* label case : if the label was not found, add one */
+ sym = label_find(tok);
+ if (!sym) {
+ sym = label_push(&s1->asm_labels, tok, 0);
+ /* NOTE: by default, the symbol is global */
+ sym->type.t = VT_VOID;
+ }
+ if (sym->r == SHN_ABS) {
+ /* if absolute symbol, no need to put a symbol value */
+ pe->v = sym->jnext;
+ pe->sym = NULL;
+ } else {
+ pe->v = 0;
+ pe->sym = sym;
+ }
+ next();
+ } else {
+ tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
+ }
+ break;
+ }
+}
+
+static void asm_expr_prod(TCCState *s1, ExprValue *pe)
+{
+ int op;
+ ExprValue e2;
+
+ asm_expr_unary(s1, pe);
+ for(;;) {
+ op = tok;
+ if (op != '*' && op != '/' && op != '%' &&
+ op != TOK_SHL && op != TOK_SAR)
+ break;
+ next();
+ asm_expr_unary(s1, &e2);
+ if (pe->sym || e2.sym)
+ tcc_error("invalid operation with label");
+ switch(op) {
+ case '*':
+ pe->v *= e2.v;
+ break;
+ case '/':
+ if (e2.v == 0) {
+ div_error:
+ tcc_error("division by zero");
+ }
+ pe->v /= e2.v;
+ break;
+ case '%':
+ if (e2.v == 0)
+ goto div_error;
+ pe->v %= e2.v;
+ break;
+ case TOK_SHL:
+ pe->v <<= e2.v;
+ break;
+ default:
+ case TOK_SAR:
+ pe->v >>= e2.v;
+ break;
+ }
+ }
+}
+
+static void asm_expr_logic(TCCState *s1, ExprValue *pe)
+{
+ int op;
+ ExprValue e2;
+
+ asm_expr_prod(s1, pe);
+ for(;;) {
+ op = tok;
+ if (op != '&' && op != '|' && op != '^')
+ break;
+ next();
+ asm_expr_prod(s1, &e2);
+ if (pe->sym || e2.sym)
+ tcc_error("invalid operation with label");
+ switch(op) {
+ case '&':
+ pe->v &= e2.v;
+ break;
+ case '|':
+ pe->v |= e2.v;
+ break;
+ default:
+ case '^':
+ pe->v ^= e2.v;
+ break;
+ }
+ }
+}
+
+static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
+{
+ int op;
+ ExprValue e2;
+
+ asm_expr_logic(s1, pe);
+ for(;;) {
+ op = tok;
+ if (op != '+' && op != '-')
+ break;
+ next();
+ asm_expr_logic(s1, &e2);
+ if (op == '+') {
+ if (pe->sym != NULL && e2.sym != NULL)
+ goto cannot_relocate;
+ pe->v += e2.v;
+ if (pe->sym == NULL && e2.sym != NULL)
+ pe->sym = e2.sym;
+ } else {
+ pe->v -= e2.v;
+ /* NOTE: we are less powerful than gas in that case
+ because we store only one symbol in the expression */
+ if (!pe->sym && !e2.sym) {
+ /* OK */
+ } else if (pe->sym && !e2.sym) {
+ /* OK */
+ } else if (pe->sym && e2.sym) {
+ if (pe->sym == e2.sym) {
+ /* OK */
+ } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
+ /* we also accept defined symbols in the same section */
+ pe->v += pe->sym->jnext - e2.sym->jnext;
+ } else {
+ goto cannot_relocate;
+ }
+ pe->sym = NULL; /* same symbols can be subtracted to NULL */
+ } else {
+ cannot_relocate:
+ tcc_error("invalid operation with label");
+ }
+ }
+ }
+}
+
+ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe)
+{
+ asm_expr_sum(s1, pe);
+}
+
+ST_FUNC int asm_int_expr(TCCState *s1)
+{
+ ExprValue e;
+ asm_expr(s1, &e);
+ if (e.sym)
+ expect("constant");
+ return e.v;
+}
+
+/* NOTE: the same name space as C labels is used to avoid using too
+ much memory when storing labels in TokenStrings */
+static void asm_new_label1(TCCState *s1, int label, int is_local,
+ int sh_num, int value)
+{
+ Sym *sym;
+
+ sym = label_find(label);
+ if (sym) {
+ if (sym->r) {
+ /* the label is already defined */
+ if (!is_local) {
+ tcc_error("assembler label '%s' already defined",
+ get_tok_str(label, NULL));
+ } else {
+ /* redefinition of local labels is possible */
+ goto new_label;
+ }
+ }
+ } else {
+ new_label:
+ sym = label_push(&s1->asm_labels, label, 0);
+ sym->type.t = VT_STATIC | VT_VOID;
+ }
+ sym->r = sh_num;
+ sym->jnext = value;
+}
+
+static void asm_new_label(TCCState *s1, int label, int is_local)
+{
+ asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
+}
+
+static void asm_free_labels(TCCState *st)
+{
+ Sym *s, *s1;
+ Section *sec;
+
+ for(s = st->asm_labels; s != NULL; s = s1) {
+ s1 = s->prev;
+ /* define symbol value in object file */
+ if (s->r) {
+ if (s->r == SHN_ABS)
+ sec = SECTION_ABS;
+ else
+ sec = st->sections[s->r];
+ put_extern_sym2(s, sec, s->jnext, 0, 0);
+ }
+ /* remove label */
+ table_ident[s->v - TOK_IDENT]->sym_label = NULL;
+ sym_free(s);
+ }
+ st->asm_labels = NULL;
+}
+
+static void use_section1(TCCState *s1, Section *sec)
+{
+ cur_text_section->data_offset = ind;
+ cur_text_section = sec;
+ ind = cur_text_section->data_offset;
+}
+
+static void use_section(TCCState *s1, const char *name)
+{
+ Section *sec;
+ sec = find_section(s1, name);
+ use_section1(s1, sec);
+}
+
+static void asm_parse_directive(TCCState *s1)
+{
+ int n, offset, v, size, tok1;
+ Section *sec;
+ uint8_t *ptr;
+
+ /* assembler directive */
+ next();
+ sec = cur_text_section;
+ switch(tok) {
+ case TOK_ASM_align:
+ case TOK_ASM_p2align:
+ case TOK_ASM_skip:
+ case TOK_ASM_space:
+ tok1 = tok;
+ next();
+ n = asm_int_expr(s1);
+ if (tok1 == TOK_ASM_p2align)
+ {
+ if (n < 0 || n > 30)
+ tcc_error("invalid p2align, must be between 0 and 30");
+ n = 1 << n;
+ tok1 = TOK_ASM_align;
+ }
+ if (tok1 == TOK_ASM_align) {
+ if (n < 0 || (n & (n-1)) != 0)
+ tcc_error("alignment must be a positive power of two");
+ offset = (ind + n - 1) & -n;
+ size = offset - ind;
+ /* the section must have a compatible alignment */
+ if (sec->sh_addralign < n)
+ sec->sh_addralign = n;
+ } else {
+ size = n;
+ }
+ v = 0;
+ if (tok == ',') {
+ next();
+ v = asm_int_expr(s1);
+ }
+ zero_pad:
+ if (sec->sh_type != SHT_NOBITS) {
+ sec->data_offset = ind;
+ ptr = section_ptr_add(sec, size);
+ memset(ptr, v, size);
+ }
+ ind += size;
+ break;
+ case TOK_ASM_quad:
+ next();
+ for(;;) {
+ uint64_t vl;
+ const char *p;
+
+ p = tokc.cstr->data;
+ if (tok != TOK_PPNUM) {
+ error_constant:
+ tcc_error("64 bit constant");
+ }
+ vl = strtoll(p, (char **)&p, 0);
+ if (*p != '\0')
+ goto error_constant;
+ next();
+ if (sec->sh_type != SHT_NOBITS) {
+ /* XXX: endianness */
+ gen_le32(vl);
+ gen_le32(vl >> 32);
+ } else {
+ ind += 8;
+ }
+ if (tok != ',')
+ break;
+ next();
+ }
+ break;
+ case TOK_ASM_byte:
+ size = 1;
+ goto asm_data;
+ case TOK_ASM_word:
+ case TOK_SHORT:
+ size = 2;
+ goto asm_data;
+ case TOK_LONG:
+ case TOK_INT:
+ size = 4;
+ asm_data:
+ next();
+ for(;;) {
+ ExprValue e;
+ asm_expr(s1, &e);
+ if (sec->sh_type != SHT_NOBITS) {
+ if (size == 4) {
+ gen_expr32(&e);
+ } else {
+ if (e.sym)
+ expect("constant");
+ if (size == 1)
+ g(e.v);
+ else
+ gen_le16(e.v);
+ }
+ } else {
+ ind += size;
+ }
+ if (tok != ',')
+ break;
+ next();
+ }
+ break;
+ case TOK_ASM_fill:
+ {
+ int repeat, size, val, i, j;
+ uint8_t repeat_buf[8];
+ next();
+ repeat = asm_int_expr(s1);
+ if (repeat < 0) {
+ tcc_error("repeat < 0; .fill ignored");
+ break;
+ }
+ size = 1;
+ val = 0;
+ if (tok == ',') {
+ next();
+ size = asm_int_expr(s1);
+ if (size < 0) {
+ tcc_error("size < 0; .fill ignored");
+ break;
+ }
+ if (size > 8)
+ size = 8;
+ if (tok == ',') {
+ next();
+ val = asm_int_expr(s1);
+ }
+ }
+ /* XXX: endianness */
+ repeat_buf[0] = val;
+ repeat_buf[1] = val >> 8;
+ repeat_buf[2] = val >> 16;
+ repeat_buf[3] = val >> 24;
+ repeat_buf[4] = 0;
+ repeat_buf[5] = 0;
+ repeat_buf[6] = 0;
+ repeat_buf[7] = 0;
+ for(i = 0; i < repeat; i++) {
+ for(j = 0; j < size; j++) {
+ g(repeat_buf[j]);
+ }
+ }
+ }
+ break;
+ case TOK_ASM_org:
+ {
+ unsigned long n;
+ next();
+ /* XXX: handle section symbols too */
+ n = asm_int_expr(s1);
+ if (n < ind)
+ tcc_error("attempt to .org backwards");
+ v = 0;
+ size = n - ind;
+ goto zero_pad;
+ }
+ break;
+ case TOK_ASM_globl:
+ case TOK_ASM_global:
+ case TOK_ASM_weak:
+ case TOK_ASM_hidden:
+ tok1 = tok;
+ do {
+ Sym *sym;
+
+ next();
+ sym = label_find(tok);
+ if (!sym) {
+ sym = label_push(&s1->asm_labels, tok, 0);
+ sym->type.t = VT_VOID;
+ }
+ if (tok1 != TOK_ASM_hidden)
+ sym->type.t &= ~VT_STATIC;
+ if (tok1 == TOK_ASM_weak)
+ sym->type.t |= VT_WEAK;
+ else if (tok1 == TOK_ASM_hidden)
+ sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT;
+ next();
+ } while (tok == ',');
+ break;
+ case TOK_ASM_string:
+ case TOK_ASM_ascii:
+ case TOK_ASM_asciz:
+ {
+ const uint8_t *p;
+ int i, size, t;
+
+ t = tok;
+ next();
+ for(;;) {
+ if (tok != TOK_STR)
+ expect("string constant");
+ p = tokc.cstr->data;
+ size = tokc.cstr->size;
+ if (t == TOK_ASM_ascii && size > 0)
+ size--;
+ for(i = 0; i < size; i++)
+ g(p[i]);
+ next();
+ if (tok == ',') {
+ next();
+ } else if (tok != TOK_STR) {
+ break;
+ }
+ }
+ }
+ break;
+ case TOK_ASM_text:
+ case TOK_ASM_data:
+ case TOK_ASM_bss:
+ {
+ char sname[64];
+ tok1 = tok;
+ n = 0;
+ next();
+ if (tok != ';' && tok != TOK_LINEFEED) {
+ n = asm_int_expr(s1);
+ next();
+ }
+ sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
+ use_section(s1, sname);
+ }
+ break;
+ case TOK_ASM_file:
+ {
+ char filename[512];
+
+ filename[0] = '\0';
+ next();
+
+ if (tok == TOK_STR)
+ pstrcat(filename, sizeof(filename), tokc.cstr->data);
+ else
+ pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL));
+
+ if (s1->warn_unsupported)
+ tcc_warning("ignoring .file %s", filename);
+
+ next();
+ }
+ break;
+ case TOK_ASM_ident:
+ {
+ char ident[256];
+
+ ident[0] = '\0';
+ next();
+
+ if (tok == TOK_STR)
+ pstrcat(ident, sizeof(ident), tokc.cstr->data);
+ else
+ pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL));
+
+ if (s1->warn_unsupported)
+ tcc_warning("ignoring .ident %s", ident);
+
+ next();
+ }
+ break;
+ case TOK_ASM_size:
+ {
+ Sym *sym;
+
+ next();
+ sym = label_find(tok);
+ if (!sym) {
+ tcc_error("label not found: %s", get_tok_str(tok, NULL));
+ }
+
+ /* XXX .size name,label2-label1 */
+ if (s1->warn_unsupported)
+ tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL));
+
+ next();
+ skip(',');
+ while (tok != '\n' && tok != CH_EOF) {
+ next();
+ }
+ }
+ break;
+ case TOK_ASM_type:
+ {
+ Sym *sym;
+ const char *newtype;
+
+ next();
+ sym = label_find(tok);
+ if (!sym) {
+ sym = label_push(&s1->asm_labels, tok, 0);
+ sym->type.t = VT_VOID;
+ }
+
+ next();
+ skip(',');
+ if (tok == TOK_STR) {
+ newtype = tokc.cstr->data;
+ } else {
+ if (tok == '@' || tok == '%')
+ skip(tok);
+ newtype = get_tok_str(tok, NULL);
+ }
+
+ if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) {
+ sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC;
+ }
+ else if (s1->warn_unsupported)
+ tcc_warning("change type of '%s' from 0x%x to '%s' ignored",
+ get_tok_str(sym->v, NULL), sym->type.t, newtype);
+
+ next();
+ }
+ break;
+ case TOK_SECTION1:
+ {
+ char sname[256];
+
+ /* XXX: support more options */
+ next();
+ sname[0] = '\0';
+ while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
+ if (tok == TOK_STR)
+ pstrcat(sname, sizeof(sname), tokc.cstr->data);
+ else
+ pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
+ next();
+ }
+ if (tok == ',') {
+ /* skip section options */
+ next();
+ if (tok != TOK_STR)
+ expect("string constant");
+ next();
+ }
+ last_text_section = cur_text_section;
+ use_section(s1, sname);
+ }
+ break;
+ case TOK_ASM_previous:
+ {
+ Section *sec;
+ next();
+ if (!last_text_section)
+ tcc_error("no previous section referenced");
+ sec = cur_text_section;
+ use_section1(s1, last_text_section);
+ last_text_section = sec;
+ }
+ break;
+#ifdef TCC_TARGET_I386
+ case TOK_ASM_code16:
+ {
+ next();
+ s1->seg_size = 16;
+ }
+ break;
+ case TOK_ASM_code32:
+ {
+ next();
+ s1->seg_size = 32;
+ }
+ break;
+#endif
+#ifdef TCC_TARGET_X86_64
+ /* added for compatibility with GAS */
+ case TOK_ASM_code64:
+ next();
+ break;
+#endif
+ default:
+ tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
+ break;
+ }
+}
+
+
+/* assemble a file */
+static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
+{
+ int opcode;
+
+#if 0
+ /* print stats about opcodes */
+ {
+ const ASMInstr *pa;
+ int freq[4];
+ int op_vals[500];
+ int nb_op_vals, i, j;
+
+ nb_op_vals = 0;
+ memset(freq, 0, sizeof(freq));
+ for(pa = asm_instrs; pa->sym != 0; pa++) {
+ freq[pa->nb_ops]++;
+ for(i=0;i<pa->nb_ops;i++) {
+ for(j=0;j<nb_op_vals;j++) {
+ if (pa->op_type[i] == op_vals[j])
+ goto found;
+ }
+ op_vals[nb_op_vals++] = pa->op_type[i];
+ found: ;
+ }
+ }
+ for(i=0;i<nb_op_vals;i++) {
+ int v = op_vals[i];
+ if ((v & (v - 1)) != 0)
+ printf("%3d: %08x\n", i, v);
+ }
+ printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
+ sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
+ freq[0], freq[1], freq[2], freq[3]);
+ }
+#endif
+
+ /* XXX: undefine C labels */
+
+ ch = file->buf_ptr[0];
+ tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
+ parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
+ if (do_preprocess)
+ parse_flags |= PARSE_FLAG_PREPROCESS;
+ next();
+ for(;;) {
+ if (tok == TOK_EOF)
+ break;
+ parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
+ redo:
+ if (tok == '#') {
+ /* horrible gas comment */
+ while (tok != TOK_LINEFEED)
+ next();
+ } else if (tok == '.') {
+ asm_parse_directive(s1);
+ } else if (tok == TOK_PPNUM) {
+ const char *p;
+ int n;
+ p = tokc.cstr->data;
+ n = strtoul(p, (char **)&p, 10);
+ if (*p != '\0')
+ expect("':'");
+ /* new local label */
+ asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
+ next();
+ skip(':');
+ goto redo;
+ } else if (tok >= TOK_IDENT) {
+ /* instruction or label */
+ opcode = tok;
+ next();
+ if (tok == ':') {
+ /* new label */
+ asm_new_label(s1, opcode, 0);
+ next();
+ goto redo;
+ } else if (tok == '=') {
+ int n;
+ next();
+ n = asm_int_expr(s1);
+ asm_new_label1(s1, opcode, 0, SHN_ABS, n);
+ goto redo;
+ } else {
+ asm_opcode(s1, opcode);
+ }
+ }
+ /* end of line */
+ if (tok != ';' && tok != TOK_LINEFEED){
+ expect("end of line");
+ }
+ parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
+ next();
+ }
+
+ asm_free_labels(s1);
+
+ return 0;
+}
+
+/* Assemble the current file */
+ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
+{
+ Sym *define_start;
+ int ret;
+
+ preprocess_init(s1);
+
+ /* default section is text */
+ cur_text_section = text_section;
+ ind = cur_text_section->data_offset;
+
+ define_start = define_stack;
+
+ /* an elf symbol of type STT_FILE must be put so that STB_LOCAL
+ symbols can be safely used */
+ put_elf_sym(symtab_section, 0, 0,
+ ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0,
+ SHN_ABS, file->filename);
+
+ ret = tcc_assemble_internal(s1, do_preprocess);
+
+ cur_text_section->data_offset = ind;
+
+ free_defines(define_start);
+
+ return ret;
+}
+
+/********************************************************************/
+/* GCC inline asm support */
+
+/* assemble the string 'str' in the current C compilation unit without
+ C preprocessing. NOTE: str is modified by modifying the '\0' at the
+ end */
+static void tcc_assemble_inline(TCCState *s1, char *str, int len)
+{
+ int saved_parse_flags;
+ const int *saved_macro_ptr;
+
+ saved_parse_flags = parse_flags;
+ saved_macro_ptr = macro_ptr;
+
+ tcc_open_bf(s1, ":asm:", len);
+ memcpy(file->buffer, str, len);
+
+ macro_ptr = NULL;
+ tcc_assemble_internal(s1, 0);
+ tcc_close();
+
+ parse_flags = saved_parse_flags;
+ macro_ptr = saved_macro_ptr;
+}
+
+/* find a constraint by its number or id (gcc 3 extended
+ syntax). return -1 if not found. Return in *pp in char after the
+ constraint */
+ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands,
+ const char *name, const char **pp)
+{
+ int index;
+ TokenSym *ts;
+ const char *p;
+
+ if (isnum(*name)) {
+ index = 0;
+ while (isnum(*name)) {
+ index = (index * 10) + (*name) - '0';
+ name++;
+ }
+ if ((unsigned)index >= nb_operands)
+ index = -1;
+ } else if (*name == '[') {
+ name++;
+ p = strchr(name, ']');
+ if (p) {
+ ts = tok_alloc(name, p - name);
+ for(index = 0; index < nb_operands; index++) {
+ if (operands[index].id == ts->tok)
+ goto found;
+ }
+ index = -1;
+ found:
+ name = p + 1;
+ } else {
+ index = -1;
+ }
+ } else {
+ index = -1;
+ }
+ if (pp)
+ *pp = name;
+ return index;
+}
+
+static void subst_asm_operands(ASMOperand *operands, int nb_operands,
+ int nb_outputs,
+ CString *out_str, CString *in_str)
+{
+ int c, index, modifier;
+ const char *str;
+ ASMOperand *op;
+ SValue sv;
+
+ cstr_new(out_str);
+ str = in_str->data;
+ for(;;) {
+ c = *str++;
+ if (c == '%') {
+ if (*str == '%') {
+ str++;
+ goto add_char;
+ }
+ modifier = 0;
+ if (*str == 'c' || *str == 'n' ||
+ *str == 'b' || *str == 'w' || *str == 'h')
+ modifier = *str++;
+ index = find_constraint(operands, nb_operands, str, &str);
+ if (index < 0)
+ tcc_error("invalid operand reference after %%");
+ op = &operands[index];
+ sv = *op->vt;
+ if (op->reg >= 0) {
+ sv.r = op->reg;
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
+ sv.r |= VT_LVAL;
+ }
+ subst_asm_operand(out_str, &sv, modifier);
+ } else {
+ add_char:
+ cstr_ccat(out_str, c);
+ if (c == '\0')
+ break;
+ }
+ }
+}
+
+
+static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
+ int is_output)
+{
+ ASMOperand *op;
+ int nb_operands;
+
+ if (tok != ':') {
+ nb_operands = *nb_operands_ptr;
+ for(;;) {
+ if (nb_operands >= MAX_ASM_OPERANDS)
+ tcc_error("too many asm operands");
+ op = &operands[nb_operands++];
+ op->id = 0;
+ if (tok == '[') {
+ next();
+ if (tok < TOK_IDENT)
+ expect("identifier");
+ op->id = tok;
+ next();
+ skip(']');
+ }
+ if (tok != TOK_STR)
+ expect("string constant");
+ op->constraint = tcc_malloc(tokc.cstr->size);
+ strcpy(op->constraint, tokc.cstr->data);
+ next();
+ skip('(');
+ gexpr();
+ if (is_output) {
+ test_lvalue();
+ } else {
+ /* we want to avoid LLOCAL case, except when the 'm'
+ constraint is used. Note that it may come from
+ register storage, so we need to convert (reg)
+ case */
+ if ((vtop->r & VT_LVAL) &&
+ ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
+ (vtop->r & VT_VALMASK) < VT_CONST) &&
+ !strchr(op->constraint, 'm')) {
+ gv(RC_INT);
+ }
+ }
+ op->vt = vtop;
+ skip(')');
+ if (tok == ',') {
+ next();
+ } else {
+ break;
+ }
+ }
+ *nb_operands_ptr = nb_operands;
+ }
+}
+
+/* parse the GCC asm() instruction */
+ST_FUNC void asm_instr(void)
+{
+ CString astr, astr1;
+ ASMOperand operands[MAX_ASM_OPERANDS];
+ int nb_outputs, nb_operands, i, must_subst, out_reg;
+ uint8_t clobber_regs[NB_ASM_REGS];
+
+ next();
+ /* since we always generate the asm() instruction, we can ignore
+ volatile */
+ if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
+ next();
+ }
+ parse_asm_str(&astr);
+ nb_operands = 0;
+ nb_outputs = 0;
+ must_subst = 0;
+ memset(clobber_regs, 0, sizeof(clobber_regs));
+ if (tok == ':') {
+ next();
+ must_subst = 1;
+ /* output args */
+ parse_asm_operands(operands, &nb_operands, 1);
+ nb_outputs = nb_operands;
+ if (tok == ':') {
+ next();
+ if (tok != ')') {
+ /* input args */
+ parse_asm_operands(operands, &nb_operands, 0);
+ if (tok == ':') {
+ /* clobber list */
+ /* XXX: handle registers */
+ next();
+ for(;;) {
+ if (tok != TOK_STR)
+ expect("string constant");
+ asm_clobber(clobber_regs, tokc.cstr->data);
+ next();
+ if (tok == ',') {
+ next();
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ skip(')');
+ /* NOTE: we do not eat the ';' so that we can restore the current
+ token after the assembler parsing */
+ if (tok != ';')
+ expect("';'");
+
+ /* save all values in the memory */
+ save_regs(0);
+
+ /* compute constraints */
+ asm_compute_constraints(operands, nb_operands, nb_outputs,
+ clobber_regs, &out_reg);
+
+ /* substitute the operands in the asm string. No substitution is
+ done if no operands (GCC behaviour) */
+#ifdef ASM_DEBUG
+ printf("asm: \"%s\"\n", (char *)astr.data);
+#endif
+ if (must_subst) {
+ subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
+ cstr_free(&astr);
+ } else {
+ astr1 = astr;
+ }
+#ifdef ASM_DEBUG
+ printf("subst_asm: \"%s\"\n", (char *)astr1.data);
+#endif
+
+ /* generate loads */
+ asm_gen_code(operands, nb_operands, nb_outputs, 0,
+ clobber_regs, out_reg);
+
+ /* assemble the string with tcc internal assembler */
+ tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
+
+ /* restore the current C token */
+ next();
+
+ /* store the output values if needed */
+ asm_gen_code(operands, nb_operands, nb_outputs, 1,
+ clobber_regs, out_reg);
+
+ /* free everything */
+ for(i=0;i<nb_operands;i++) {
+ ASMOperand *op;
+ op = &operands[i];
+ tcc_free(op->constraint);
+ vpop();
+ }
+ cstr_free(&astr1);
+}
+
+ST_FUNC void asm_global_instr(void)
+{
+ CString astr;
+
+ next();
+ parse_asm_str(&astr);
+ skip(')');
+ /* NOTE: we do not eat the ';' so that we can restore the current
+ token after the assembler parsing */
+ if (tok != ';')
+ expect("';'");
+
+#ifdef ASM_DEBUG
+ printf("asm_global: \"%s\"\n", (char *)astr.data);
+#endif
+ cur_text_section = text_section;
+ ind = cur_text_section->data_offset;
+
+ /* assemble the string with tcc internal assembler */
+ tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
+
+ cur_text_section->data_offset = ind;
+
+ /* restore the current C token */
+ next();
+
+ cstr_free(&astr);
+}
+#endif /* CONFIG_TCC_ASM */
diff --git a/src/tcccoff.c b/src/tcccoff.c
new file mode 100644
index 0000000..6ef9759
--- /dev/null
+++ b/src/tcccoff.c
@@ -0,0 +1,924 @@
+/*
+ * COFF file handling for TCC
+ *
+ * Copyright (c) 2003, 2004 TK
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+#define MAXNSCNS 255 /* MAXIMUM NUMBER OF SECTIONS */
+#define MAX_STR_TABLE 1000000
+AOUTHDR o_filehdr; /* OPTIONAL (A.OUT) FILE HEADER */
+
+SCNHDR section_header[MAXNSCNS];
+
+#define MAX_FUNCS 1000
+#define MAX_FUNC_NAME_LENGTH 128
+
+int nFuncs;
+char Func[MAX_FUNCS][MAX_FUNC_NAME_LENGTH];
+char AssociatedFile[MAX_FUNCS][MAX_FUNC_NAME_LENGTH];
+int LineNoFilePtr[MAX_FUNCS];
+int EndAddress[MAX_FUNCS];
+int LastLineNo[MAX_FUNCS];
+int FuncEntries[MAX_FUNCS];
+
+int OutputTheSection(Section* sect);
+short int GetCoffFlags(const char* s);
+void SortSymbolTable(void);
+Section* FindSection(TCCState* s1, const char* sname);
+
+int C67_main_entry_point;
+
+int FindCoffSymbolIndex(const char* func_name);
+int nb_syms;
+
+typedef struct {
+ long tag;
+ long size;
+ long fileptr;
+ long nextsym;
+ short int dummy;
+} AUXFUNC;
+
+typedef struct {
+ long regmask;
+ unsigned short lineno;
+ unsigned short nentries;
+ int localframe;
+ int nextentry;
+ short int dummy;
+} AUXBF;
+
+typedef struct {
+ long dummy;
+ unsigned short lineno;
+ unsigned short dummy1;
+ int dummy2;
+ int dummy3;
+ unsigned short dummy4;
+} AUXEF;
+
+ST_FUNC int tcc_output_coff(TCCState* s1, FILE* f)
+{
+ Section* tcc_sect;
+ SCNHDR* coff_sec;
+ int file_pointer;
+ char* Coff_str_table, *pCoff_str_table;
+ int CoffTextSectionNo, coff_nb_syms;
+ FILHDR file_hdr; /* FILE HEADER STRUCTURE */
+ Section* stext, *sdata, *sbss;
+ int i, NSectionsToOutput = 0;
+
+ Coff_str_table = pCoff_str_table = NULL;
+
+ stext = FindSection(s1, ".text");
+ sdata = FindSection(s1, ".data");
+ sbss = FindSection(s1, ".bss");
+
+ nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym);
+ coff_nb_syms = FindCoffSymbolIndex("XXXXXXXXXX1");
+
+ file_hdr.f_magic = COFF_C67_MAGIC; /* magic number */
+ file_hdr.f_timdat = 0; /* time & date stamp */
+ file_hdr.f_opthdr = sizeof(AOUTHDR); /* sizeof(optional hdr) */
+ file_hdr.f_flags = 0x1143; /* flags (copied from what code composer does) */
+ file_hdr.f_TargetID = 0x99; /* for C6x = 0x0099 */
+
+ o_filehdr.magic = 0x0108; /* see magic.h */
+ o_filehdr.vstamp = 0x0190; /* version stamp */
+ o_filehdr.tsize =
+ stext->data_offset; /* text size in bytes, padded to FW bdry */
+ o_filehdr.dsize = sdata->data_offset; /* initialized data " " */
+ o_filehdr.bsize = sbss->data_offset; /* uninitialized data " " */
+ o_filehdr.entrypt = C67_main_entry_point; /* entry pt. */
+ o_filehdr.text_start = stext->sh_addr; /* base of text used for this file */
+ o_filehdr.data_start = sdata->sh_addr; /* base of data used for this file */
+
+ // create all the section headers
+
+ file_pointer = FILHSZ + sizeof(AOUTHDR);
+
+ CoffTextSectionNo = -1;
+
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (OutputTheSection(tcc_sect)) {
+ NSectionsToOutput++;
+
+ if (CoffTextSectionNo == -1 && tcc_sect == stext)
+ CoffTextSectionNo = NSectionsToOutput; // rem which coff sect
+ // number the .text sect
+ // is
+
+ strcpy(coff_sec->s_name, tcc_sect->name); /* section name */
+
+ coff_sec->s_paddr = tcc_sect->sh_addr; /* physical address */
+ coff_sec->s_vaddr = tcc_sect->sh_addr; /* virtual address */
+ coff_sec->s_size = tcc_sect->data_offset; /* section size */
+ coff_sec->s_scnptr = 0; /* file ptr to raw data for section */
+ coff_sec->s_relptr = 0; /* file ptr to relocation */
+ coff_sec->s_lnnoptr = 0; /* file ptr to line numbers */
+ coff_sec->s_nreloc = 0; /* number of relocation entries */
+ coff_sec->s_flags = GetCoffFlags(coff_sec->s_name); /* flags */
+ coff_sec->s_reserved = 0; /* reserved byte */
+ coff_sec->s_page = 0; /* memory page id */
+
+ file_pointer += sizeof(SCNHDR);
+ }
+ }
+
+ file_hdr.f_nscns = NSectionsToOutput; /* number of sections */
+
+ // now loop through and determine file pointer locations
+ // for the raw data
+
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (OutputTheSection(tcc_sect)) {
+ // put raw data
+ coff_sec->s_scnptr =
+ file_pointer; /* file ptr to raw data for section */
+ file_pointer += coff_sec->s_size;
+ }
+ }
+
+ // now loop through and determine file pointer locations
+ // for the relocation data
+
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (OutputTheSection(tcc_sect)) {
+ // put relocations data
+ if (coff_sec->s_nreloc > 0) {
+ coff_sec->s_relptr = file_pointer; /* file ptr to relocation */
+ file_pointer += coff_sec->s_nreloc * sizeof(struct reloc);
+ }
+ }
+ }
+
+ // now loop through and determine file pointer locations
+ // for the line number data
+
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ coff_sec->s_nlnno = 0;
+ coff_sec->s_lnnoptr = 0;
+
+ if (s1->do_debug && tcc_sect == stext) {
+ // count how many line nos data
+
+ // also find association between source file name and function
+ // so we can sort the symbol table
+
+ Stab_Sym* sym, *sym_end;
+ char func_name[MAX_FUNC_NAME_LENGTH],
+ last_func_name[MAX_FUNC_NAME_LENGTH];
+ unsigned long func_addr, last_pc, pc;
+ const char* incl_files[INCLUDE_STACK_SIZE];
+ int incl_index, len, last_line_num;
+ const char* str, *p;
+
+ coff_sec->s_lnnoptr = file_pointer; /* file ptr to linno */
+
+ func_name[0] = '\0';
+ func_addr = 0;
+ incl_index = 0;
+ last_func_name[0] = '\0';
+ last_pc = 0xffffffff;
+ last_line_num = 1;
+ sym = (Stab_Sym*)stab_section->data + 1;
+ sym_end =
+ (Stab_Sym*)(stab_section->data + stab_section->data_offset);
+
+ nFuncs = 0;
+ while (sym < sym_end) {
+ switch (sym->n_type) {
+ /* function start or end */
+ case N_FUN:
+ if (sym->n_strx == 0) {
+ // end of function
+
+ coff_sec->s_nlnno++;
+ file_pointer += LINESZ;
+
+ pc = sym->n_value + func_addr;
+ func_name[0] = '\0';
+ func_addr = 0;
+ EndAddress[nFuncs] = pc;
+ FuncEntries[nFuncs] =
+ (file_pointer - LineNoFilePtr[nFuncs]) / LINESZ - 1;
+ LastLineNo[nFuncs++] = last_line_num + 1;
+ } else {
+ // beginning of function
+
+ LineNoFilePtr[nFuncs] = file_pointer;
+ coff_sec->s_nlnno++;
+ file_pointer += LINESZ;
+
+ str = (const char*)stabstr_section->data + sym->n_strx;
+
+ p = strchr(str, ':');
+ if (!p) {
+ pstrcpy(func_name, sizeof(func_name), str);
+ pstrcpy(Func[nFuncs], sizeof(func_name), str);
+ } else {
+ len = p - str;
+ if (len > sizeof(func_name) - 1)
+ len = sizeof(func_name) - 1;
+ memcpy(func_name, str, len);
+ memcpy(Func[nFuncs], str, len);
+ func_name[len] = '\0';
+ }
+
+ // save the file that it came in so we can sort later
+ pstrcpy(AssociatedFile[nFuncs], sizeof(func_name),
+ incl_files[incl_index - 1]);
+
+ func_addr = sym->n_value;
+ }
+ break;
+
+ /* line number info */
+ case N_SLINE:
+ pc = sym->n_value + func_addr;
+
+ last_pc = pc;
+ last_line_num = sym->n_desc;
+
+ /* XXX: slow! */
+ strcpy(last_func_name, func_name);
+
+ coff_sec->s_nlnno++;
+ file_pointer += LINESZ;
+ break;
+ /* include files */
+ case N_BINCL:
+ str = (const char*)stabstr_section->data + sym->n_strx;
+ add_incl:
+ if (incl_index < INCLUDE_STACK_SIZE) {
+ incl_files[incl_index++] = str;
+ }
+ break;
+ case N_EINCL:
+ if (incl_index > 1)
+ incl_index--;
+ break;
+ case N_SO:
+ if (sym->n_strx == 0) {
+ incl_index = 0; /* end of translation unit */
+ } else {
+ str = (const char*)stabstr_section->data + sym->n_strx;
+ /* do not add path */
+ len = strlen(str);
+ if (len > 0 && str[len - 1] != '/')
+ goto add_incl;
+ }
+ break;
+ }
+ sym++;
+ }
+ }
+ }
+
+ file_hdr.f_symptr = file_pointer; /* file pointer to symtab */
+
+ if (s1->do_debug)
+ file_hdr.f_nsyms = coff_nb_syms; /* number of symtab entries */
+ else
+ file_hdr.f_nsyms = 0;
+
+ file_pointer += file_hdr.f_nsyms * SYMNMLEN;
+
+ // OK now we are all set to write the file
+
+ fwrite(&file_hdr, FILHSZ, 1, f);
+ fwrite(&o_filehdr, sizeof(o_filehdr), 1, f);
+
+ // write section headers
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (OutputTheSection(tcc_sect)) {
+ fwrite(coff_sec, sizeof(SCNHDR), 1, f);
+ }
+ }
+
+ // write raw data
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (OutputTheSection(tcc_sect)) {
+ fwrite(tcc_sect->data, tcc_sect->data_offset, 1, f);
+ }
+ }
+
+ // write relocation data
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (OutputTheSection(tcc_sect)) {
+ // put relocations data
+ if (coff_sec->s_nreloc > 0) {
+ fwrite(tcc_sect->reloc,
+ coff_sec->s_nreloc * sizeof(struct reloc), 1, f);
+ }
+ }
+ }
+
+ // group the symbols in order of filename, func1, func2, etc
+ // finally global symbols
+
+ if (s1->do_debug)
+ SortSymbolTable();
+
+ // write line no data
+
+ for (i = 1; i < s1->nb_sections; i++) {
+ coff_sec = &section_header[i];
+ tcc_sect = s1->sections[i];
+
+ if (s1->do_debug && tcc_sect == stext) {
+ // count how many line nos data
+
+ Stab_Sym* sym, *sym_end;
+ char func_name[128], last_func_name[128];
+ unsigned long func_addr, last_pc, pc;
+ const char* incl_files[INCLUDE_STACK_SIZE];
+ int incl_index, len, last_line_num;
+ const char* str, *p;
+
+ LINENO CoffLineNo;
+
+ func_name[0] = '\0';
+ func_addr = 0;
+ incl_index = 0;
+ last_func_name[0] = '\0';
+ last_pc = 0;
+ last_line_num = 1;
+ sym = (Stab_Sym*)stab_section->data + 1;
+ sym_end =
+ (Stab_Sym*)(stab_section->data + stab_section->data_offset);
+
+ while (sym < sym_end) {
+ switch (sym->n_type) {
+ /* function start or end */
+ case N_FUN:
+ if (sym->n_strx == 0) {
+ // end of function
+
+ CoffLineNo.l_addr.l_paddr = last_pc;
+ CoffLineNo.l_lnno = last_line_num + 1;
+ fwrite(&CoffLineNo, 6, 1, f);
+
+ pc = sym->n_value + func_addr;
+ func_name[0] = '\0';
+ func_addr = 0;
+ } else {
+ // beginning of function
+
+ str = (const char*)stabstr_section->data + sym->n_strx;
+
+ p = strchr(str, ':');
+ if (!p) {
+ pstrcpy(func_name, sizeof(func_name), str);
+ } else {
+ len = p - str;
+ if (len > sizeof(func_name) - 1)
+ len = sizeof(func_name) - 1;
+ memcpy(func_name, str, len);
+ func_name[len] = '\0';
+ }
+ func_addr = sym->n_value;
+ last_pc = func_addr;
+ last_line_num = -1;
+
+ // output a function begin
+
+ CoffLineNo.l_addr.l_symndx =
+ FindCoffSymbolIndex(func_name);
+ CoffLineNo.l_lnno = 0;
+
+ fwrite(&CoffLineNo, 6, 1, f);
+ }
+ break;
+
+ /* line number info */
+ case N_SLINE:
+ pc = sym->n_value + func_addr;
+
+ /* XXX: slow! */
+ strcpy(last_func_name, func_name);
+
+ // output a line reference
+
+ CoffLineNo.l_addr.l_paddr = last_pc;
+
+ if (last_line_num == -1) {
+ CoffLineNo.l_lnno = sym->n_desc;
+ } else {
+ CoffLineNo.l_lnno = last_line_num + 1;
+ }
+
+ fwrite(&CoffLineNo, 6, 1, f);
+
+ last_pc = pc;
+ last_line_num = sym->n_desc;
+
+ break;
+
+ /* include files */
+ case N_BINCL:
+ str = (const char*)stabstr_section->data + sym->n_strx;
+ add_incl2:
+ if (incl_index < INCLUDE_STACK_SIZE) {
+ incl_files[incl_index++] = str;
+ }
+ break;
+ case N_EINCL:
+ if (incl_index > 1)
+ incl_index--;
+ break;
+ case N_SO:
+ if (sym->n_strx == 0) {
+ incl_index = 0; /* end of translation unit */
+ } else {
+ str = (const char*)stabstr_section->data + sym->n_strx;
+ /* do not add path */
+ len = strlen(str);
+ if (len > 0 && str[len - 1] != '/')
+ goto add_incl2;
+ }
+ break;
+ }
+ sym++;
+ }
+ }
+ }
+
+ // write symbol table
+ if (s1->do_debug) {
+ int k;
+ struct syment csym;
+ AUXFUNC auxfunc;
+ AUXBF auxbf;
+ AUXEF auxef;
+ int i;
+ Elf32_Sym* p;
+ const char* name;
+ int nstr;
+ int n = 0;
+
+ Coff_str_table = (char*)tcc_malloc(MAX_STR_TABLE);
+ pCoff_str_table = Coff_str_table;
+ nstr = 0;
+
+ p = (Elf32_Sym*)symtab_section->data;
+
+ for (i = 0; i < nb_syms; i++) {
+
+ name = symtab_section->link->data + p->st_name;
+
+ for (k = 0; k < 8; k++)
+ csym._n._n_name[k] = 0;
+
+ if (strlen(name) <= 8) {
+ strcpy(csym._n._n_name, name);
+ } else {
+ if (pCoff_str_table - Coff_str_table + strlen(name) >
+ MAX_STR_TABLE - 1)
+ tcc_error("String table too large");
+
+ csym._n._n_n._n_zeroes = 0;
+ csym._n._n_n._n_offset = pCoff_str_table - Coff_str_table + 4;
+
+ strcpy(pCoff_str_table, name);
+ pCoff_str_table += strlen(name) + 1; // skip over null
+ nstr++;
+ }
+
+ if (p->st_info == 4) {
+ // put a filename symbol
+ csym.n_value = 33; // ?????
+ csym.n_scnum = N_DEBUG;
+ csym.n_type = 0;
+ csym.n_sclass = C_FILE;
+ csym.n_numaux = 0;
+ fwrite(&csym, 18, 1, f);
+ n++;
+
+ } else if (p->st_info == 0x12) {
+ // find the function data
+
+ for (k = 0; k < nFuncs; k++) {
+ if (strcmp(name, Func[k]) == 0)
+ break;
+ }
+
+ if (k >= nFuncs) {
+ tcc_error("debug info can't find function: %s", name);
+ }
+ // put a Function Name
+
+ csym.n_value = p->st_value; // physical address
+ csym.n_scnum = CoffTextSectionNo;
+ csym.n_type = MKTYPE(T_INT, DT_FCN, 0, 0, 0, 0, 0);
+ csym.n_sclass = C_EXT;
+ csym.n_numaux = 1;
+ fwrite(&csym, 18, 1, f);
+
+ // now put aux info
+
+ auxfunc.tag = 0;
+ auxfunc.size = EndAddress[k] - p->st_value;
+ auxfunc.fileptr = LineNoFilePtr[k];
+ auxfunc.nextsym = n + 6; // tktk
+ auxfunc.dummy = 0;
+ fwrite(&auxfunc, 18, 1, f);
+
+ // put a .bf
+
+ strcpy(csym._n._n_name, ".bf");
+ csym.n_value = p->st_value; // physical address
+ csym.n_scnum = CoffTextSectionNo;
+ csym.n_type = 0;
+ csym.n_sclass = C_FCN;
+ csym.n_numaux = 1;
+ fwrite(&csym, 18, 1, f);
+
+ // now put aux info
+
+ auxbf.regmask = 0;
+ auxbf.lineno = 0;
+ auxbf.nentries = FuncEntries[k];
+ auxbf.localframe = 0;
+ auxbf.nextentry = n + 6;
+ auxbf.dummy = 0;
+ fwrite(&auxbf, 18, 1, f);
+
+ // put a .ef
+
+ strcpy(csym._n._n_name, ".ef");
+ csym.n_value = EndAddress[k]; // physical address
+ csym.n_scnum = CoffTextSectionNo;
+ csym.n_type = 0;
+ csym.n_sclass = C_FCN;
+ csym.n_numaux = 1;
+ fwrite(&csym, 18, 1, f);
+
+ // now put aux info
+
+ auxef.dummy = 0;
+ auxef.lineno = LastLineNo[k];
+ auxef.dummy1 = 0;
+ auxef.dummy2 = 0;
+ auxef.dummy3 = 0;
+ auxef.dummy4 = 0;
+ fwrite(&auxef, 18, 1, f);
+
+ n += 6;
+
+ } else {
+ // try an put some type info
+
+ if ((p->st_other & VT_BTYPE) == VT_DOUBLE) {
+ csym.n_type = T_DOUBLE; // int
+ csym.n_sclass = C_EXT;
+ } else if ((p->st_other & VT_BTYPE) == VT_FLOAT) {
+ csym.n_type = T_FLOAT;
+ csym.n_sclass = C_EXT;
+ } else if ((p->st_other & VT_BTYPE) == VT_INT) {
+ csym.n_type = T_INT; // int
+ csym.n_sclass = C_EXT;
+ } else if ((p->st_other & VT_BTYPE) == VT_SHORT) {
+ csym.n_type = T_SHORT;
+ csym.n_sclass = C_EXT;
+ } else if ((p->st_other & VT_BTYPE) == VT_BYTE) {
+ csym.n_type = T_CHAR;
+ csym.n_sclass = C_EXT;
+ } else {
+ csym.n_type = T_INT; // just mark as a label
+ csym.n_sclass = C_LABEL;
+ }
+
+ csym.n_value = p->st_value;
+ csym.n_scnum = 2;
+ csym.n_numaux = 1;
+ fwrite(&csym, 18, 1, f);
+
+ auxfunc.tag = 0;
+ auxfunc.size = 0x20;
+ auxfunc.fileptr = 0;
+ auxfunc.nextsym = 0;
+ auxfunc.dummy = 0;
+ fwrite(&auxfunc, 18, 1, f);
+ n++;
+ n++;
+ }
+
+ p++;
+ }
+ }
+
+ if (s1->do_debug) {
+ // write string table
+
+ // first write the size
+ i = pCoff_str_table - Coff_str_table;
+ fwrite(&i, 4, 1, f);
+
+ // then write the strings
+ fwrite(Coff_str_table, i, 1, f);
+
+ tcc_free(Coff_str_table);
+ }
+
+ return 0;
+}
+
+// group the symbols in order of filename, func1, func2, etc
+// finally global symbols
+
+void SortSymbolTable(void)
+{
+ int i, j, k, n = 0;
+ Elf32_Sym* p, *p2, *NewTable;
+ char* name, *name2;
+
+ NewTable = (Elf32_Sym*)tcc_malloc(nb_syms * sizeof(Elf32_Sym));
+
+ p = (Elf32_Sym*)symtab_section->data;
+
+ // find a file symbol, copy it over
+ // then scan the whole symbol list and copy any function
+ // symbols that match the file association
+
+ for (i = 0; i < nb_syms; i++) {
+ if (p->st_info == 4) {
+ name = (char*)symtab_section->link->data + p->st_name;
+
+ // this is a file symbol, copy it over
+
+ NewTable[n++] = *p;
+
+ p2 = (Elf32_Sym*)symtab_section->data;
+
+ for (j = 0; j < nb_syms; j++) {
+ if (p2->st_info == 0x12) {
+ // this is a func symbol
+
+ name2 = (char*)symtab_section->link->data + p2->st_name;
+
+ // find the function data index
+
+ for (k = 0; k < nFuncs; k++) {
+ if (strcmp(name2, Func[k]) == 0)
+ break;
+ }
+
+ if (k >= nFuncs) {
+ tcc_error("debug (sort) info can't find function: %s",
+ name2);
+ }
+
+ if (strcmp(AssociatedFile[k], name) == 0) {
+ // yes they match copy it over
+
+ NewTable[n++] = *p2;
+ }
+ }
+ p2++;
+ }
+ }
+ p++;
+ }
+
+ // now all the filename and func symbols should have been copied over
+ // copy all the rest over (all except file and funcs)
+
+ p = (Elf32_Sym*)symtab_section->data;
+ for (i = 0; i < nb_syms; i++) {
+ if (p->st_info != 4 && p->st_info != 0x12) {
+ NewTable[n++] = *p;
+ }
+ p++;
+ }
+
+ if (n != nb_syms)
+ tcc_error("Internal Compiler error, debug info");
+
+ // copy it all back
+
+ p = (Elf32_Sym*)symtab_section->data;
+ for (i = 0; i < nb_syms; i++) {
+ *p++ = NewTable[i];
+ }
+
+ tcc_free(NewTable);
+}
+
+int FindCoffSymbolIndex(const char* func_name)
+{
+ int i, n = 0;
+ Elf32_Sym* p;
+ char* name;
+
+ p = (Elf32_Sym*)symtab_section->data;
+
+ for (i = 0; i < nb_syms; i++) {
+
+ name = (char*)symtab_section->link->data + p->st_name;
+
+ if (p->st_info == 4) {
+ // put a filename symbol
+ n++;
+ } else if (p->st_info == 0x12) {
+
+ if (strcmp(func_name, name) == 0)
+ return n;
+
+ n += 6;
+
+ // put a Function Name
+
+ // now put aux info
+
+ // put a .bf
+
+ // now put aux info
+
+ // put a .ef
+
+ // now put aux info
+
+ } else {
+ n += 2;
+ }
+
+ p++;
+ }
+
+ return n; // total number of symbols
+}
+
+int OutputTheSection(Section* sect)
+{
+ const char* s = sect->name;
+
+ if (!strcmp(s, ".text"))
+ return 1;
+ else if (!strcmp(s, ".data"))
+ return 1;
+ else
+ return 0;
+}
+
+short int GetCoffFlags(const char* s)
+{
+ if (!strcmp(s, ".text"))
+ return STYP_TEXT | STYP_DATA | STYP_ALIGN | 0x400;
+ else if (!strcmp(s, ".data"))
+ return STYP_DATA;
+ else if (!strcmp(s, ".bss"))
+ return STYP_BSS;
+ else if (!strcmp(s, ".stack"))
+ return STYP_BSS | STYP_ALIGN | 0x200;
+ else if (!strcmp(s, ".cinit"))
+ return STYP_COPY | STYP_DATA | STYP_ALIGN | 0x200;
+ else
+ return 0;
+}
+
+Section* FindSection(TCCState* s1, const char* sname)
+{
+ Section* s;
+ int i;
+
+ for (i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+
+ if (!strcmp(sname, s->name))
+ return s;
+ }
+
+ tcc_error("could not find section %s", sname);
+ return 0;
+}
+
+ST_FUNC int tcc_load_coff(TCCState* s1, int fd)
+{
+ // tktk TokenSym *ts;
+
+ FILE* f;
+ unsigned int str_size;
+ char* Coff_str_table, *name;
+ int i, k;
+ struct syment csym;
+ char name2[9];
+ FILHDR file_hdr; /* FILE HEADER STRUCTURE */
+
+ f = fdopen(fd, "rb");
+ if (!f) {
+ tcc_error("Unable to open .out file for input");
+ }
+
+ if (fread(&file_hdr, FILHSZ, 1, f) != 1)
+ tcc_error("error reading .out file for input");
+
+ if (fread(&o_filehdr, sizeof(o_filehdr), 1, f) != 1)
+ tcc_error("error reading .out file for input");
+
+ // first read the string table
+
+ if (fseek(f, file_hdr.f_symptr + file_hdr.f_nsyms * SYMESZ, SEEK_SET))
+ tcc_error("error reading .out file for input");
+
+ if (fread(&str_size, sizeof(int), 1, f) != 1)
+ tcc_error("error reading .out file for input");
+
+ Coff_str_table = (char*)tcc_malloc(str_size);
+
+ if (fread(Coff_str_table, str_size - 4, 1, f) != 1)
+ tcc_error("error reading .out file for input");
+
+ // read/process all the symbols
+
+ // seek back to symbols
+
+ if (fseek(f, file_hdr.f_symptr, SEEK_SET))
+ tcc_error("error reading .out file for input");
+
+ for (i = 0; i < file_hdr.f_nsyms; i++) {
+ if (fread(&csym, SYMESZ, 1, f) != 1)
+ tcc_error("error reading .out file for input");
+
+ if (csym._n._n_n._n_zeroes == 0) {
+ name = Coff_str_table + csym._n._n_n._n_offset - 4;
+ } else {
+ name = csym._n._n_name;
+
+ if (name[7] != 0) {
+ for (k = 0; k < 8; k++)
+ name2[k] = name[k];
+
+ name2[8] = 0;
+
+ name = name2;
+ }
+ }
+ // if (strcmp("_DAC_Buffer",name)==0) // tktk
+ // name[0]=0;
+
+ if (((csym.n_type & 0x30) == 0x20 && csym.n_sclass == 0x2) ||
+ ((csym.n_type & 0x30) == 0x30 && csym.n_sclass == 0x2) ||
+ (csym.n_type == 0x4 && csym.n_sclass == 0x2) ||
+ (csym.n_type == 0x8 && csym.n_sclass == 0x2) || // structures
+ (csym.n_type == 0x18 &&
+ csym.n_sclass == 0x2) || // pointer to structure
+ (csym.n_type == 0x7 && csym.n_sclass == 0x2) || // doubles
+ (csym.n_type == 0x6 && csym.n_sclass == 0x2)) // floats
+ {
+ // strip off any leading underscore (except for other main routine)
+
+ if (name[0] == '_' && strcmp(name, "_main") != 0)
+ name++;
+
+ tcc_add_symbol(s1, name, (void*)(uintptr_t)csym.n_value);
+ }
+ // skip any aux records
+
+ if (csym.n_numaux == 1) {
+ if (fread(&csym, SYMESZ, 1, f) != 1)
+ tcc_error("error reading .out file for input");
+ i++;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/tccelf.c b/src/tccelf.c
new file mode 100644
index 0000000..d00fadd
--- /dev/null
+++ b/src/tccelf.c
@@ -0,0 +1,3504 @@
+/*
+ * ELF file handling for TCC
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+/* Define this to get some debug output during relocation processing. */
+#undef DEBUG_RELOC
+
+/* XXX: avoid static variable */
+static int new_undef_sym = 0; /* Is there a new undefined sym since last new_undef_sym() */
+
+ST_FUNC int put_elf_str(Section *s, const char *sym)
+{
+ int offset, len;
+ char *ptr;
+
+ len = strlen(sym) + 1;
+ offset = s->data_offset;
+ ptr = section_ptr_add(s, len);
+ memcpy(ptr, sym, len);
+ return offset;
+}
+
+/* elf symbol hashing function */
+static unsigned long elf_hash(const unsigned char *name)
+{
+ unsigned long h = 0, g;
+
+ while (*name) {
+ h = (h << 4) + *name++;
+ g = h & 0xf0000000;
+ if (g)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+/* rebuild hash table of section s */
+/* NOTE: we do factorize the hash table code to go faster */
+static void rebuild_hash(Section *s, unsigned int nb_buckets)
+{
+ ElfW(Sym) *sym;
+ int *ptr, *hash, nb_syms, sym_index, h;
+ unsigned char *strtab;
+
+ strtab = s->link->data;
+ nb_syms = s->data_offset / sizeof(ElfW(Sym));
+
+ s->hash->data_offset = 0;
+ ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int));
+ ptr[0] = nb_buckets;
+ ptr[1] = nb_syms;
+ ptr += 2;
+ hash = ptr;
+ memset(hash, 0, (nb_buckets + 1) * sizeof(int));
+ ptr += nb_buckets + 1;
+
+ sym = (ElfW(Sym) *)s->data + 1;
+ for(sym_index = 1; sym_index < nb_syms; sym_index++) {
+ if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
+ h = elf_hash(strtab + sym->st_name) % nb_buckets;
+ *ptr = hash[h];
+ hash[h] = sym_index;
+ } else {
+ *ptr = 0;
+ }
+ ptr++;
+ sym++;
+ }
+}
+
+/* return the symbol number */
+ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
+ int info, int other, int shndx, const char *name)
+{
+ int name_offset, sym_index;
+ int nbuckets, h;
+ ElfW(Sym) *sym;
+ Section *hs;
+
+ sym = section_ptr_add(s, sizeof(ElfW(Sym)));
+ if (name)
+ name_offset = put_elf_str(s->link, name);
+ else
+ name_offset = 0;
+ /* XXX: endianness */
+ sym->st_name = name_offset;
+ sym->st_value = value;
+ sym->st_size = size;
+ sym->st_info = info;
+ sym->st_other = other;
+ sym->st_shndx = shndx;
+ sym_index = sym - (ElfW(Sym) *)s->data;
+ hs = s->hash;
+ if (hs) {
+ int *ptr, *base;
+ ptr = section_ptr_add(hs, sizeof(int));
+ base = (int *)hs->data;
+ /* only add global or weak symbols */
+ if (ELFW(ST_BIND)(info) != STB_LOCAL) {
+ /* add another hashing entry */
+ nbuckets = base[0];
+ h = elf_hash((unsigned char *) name) % nbuckets;
+ *ptr = base[2 + h];
+ base[2 + h] = sym_index;
+ base[1]++;
+ /* we resize the hash table */
+ hs->nb_hashed_syms++;
+ if (hs->nb_hashed_syms > 2 * nbuckets) {
+ rebuild_hash(s, 2 * nbuckets);
+ }
+ } else {
+ *ptr = 0;
+ base[1]++;
+ }
+ }
+ return sym_index;
+}
+
+/* find global ELF symbol 'name' and return its index. Return 0 if not
+ found. */
+ST_FUNC int find_elf_sym(Section *s, const char *name)
+{
+ ElfW(Sym) *sym;
+ Section *hs;
+ int nbuckets, sym_index, h;
+ const char *name1;
+
+ hs = s->hash;
+ if (!hs)
+ return 0;
+ nbuckets = ((int *)hs->data)[0];
+ h = elf_hash((unsigned char *) name) % nbuckets;
+ sym_index = ((int *)hs->data)[2 + h];
+ while (sym_index != 0) {
+ sym = &((ElfW(Sym) *)s->data)[sym_index];
+ name1 = (char *) s->link->data + sym->st_name;
+ if (!strcmp(name, name1))
+ return sym_index;
+ sym_index = ((int *)hs->data)[2 + nbuckets + sym_index];
+ }
+ return 0;
+}
+
+/* return elf symbol value, signal error if 'err' is nonzero */
+ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err)
+{
+ int sym_index;
+ ElfW(Sym) *sym;
+
+ sym_index = find_elf_sym(s->symtab, name);
+ sym = &((ElfW(Sym) *)s->symtab->data)[sym_index];
+ if (!sym_index || sym->st_shndx == SHN_UNDEF) {
+ if (err)
+ tcc_error("%s not defined", name);
+ return 0;
+ }
+ return sym->st_value;
+}
+
+/* return elf symbol value */
+LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name)
+{
+ return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0);
+}
+
+#if defined TCC_IS_NATIVE || defined TCC_TARGET_PE
+/* return elf symbol value or error */
+ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name)
+{
+ return (void*)(uintptr_t)get_elf_sym_addr(s, name, 1);
+}
+#endif
+
+/* add an elf symbol : check if it is already defined and patch
+ it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
+ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size,
+ int info, int other, int sh_num, const char *name)
+{
+ ElfW(Sym) *esym;
+ int sym_bind, sym_index, sym_type, esym_bind;
+ unsigned char sym_vis, esym_vis, new_vis;
+
+ sym_bind = ELFW(ST_BIND)(info);
+ sym_type = ELFW(ST_TYPE)(info);
+ sym_vis = ELFW(ST_VISIBILITY)(other);
+
+ if (sym_bind != STB_LOCAL) {
+ /* we search global or weak symbols */
+ sym_index = find_elf_sym(s, name);
+ if (!sym_index)
+ goto do_def;
+ esym = &((ElfW(Sym) *)s->data)[sym_index];
+ if (esym->st_shndx != SHN_UNDEF) {
+ esym_bind = ELFW(ST_BIND)(esym->st_info);
+ /* propagate the most constraining visibility */
+ /* STV_DEFAULT(0)<STV_PROTECTED(3)<STV_HIDDEN(2)<STV_INTERNAL(1) */
+ esym_vis = ELFW(ST_VISIBILITY)(esym->st_other);
+ if (esym_vis == STV_DEFAULT) {
+ new_vis = sym_vis;
+ } else if (sym_vis == STV_DEFAULT) {
+ new_vis = esym_vis;
+ } else {
+ new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis;
+ }
+ esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1))
+ | new_vis;
+ other = esym->st_other; /* in case we have to patch esym */
+ if (sh_num == SHN_UNDEF) {
+ /* ignore adding of undefined symbol if the
+ corresponding symbol is already defined */
+ } else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) {
+ /* global overrides weak, so patch */
+ goto do_patch;
+ } else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) {
+ /* weak is ignored if already global */
+ } else if (sym_bind == STB_WEAK && esym_bind == STB_WEAK) {
+ /* keep first-found weak definition, ignore subsequents */
+ } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
+ /* ignore hidden symbols after */
+ } else if (esym->st_shndx == SHN_COMMON
+ && (sh_num < SHN_LORESERVE || sh_num == SHN_COMMON)) {
+ /* gr: Happens with 'tcc ... -static tcctest.c' on e.g. Ubuntu 6.01
+ No idea if this is the correct solution ... */
+ goto do_patch;
+ } else if (s == tcc_state->dynsymtab_section) {
+ /* we accept that two DLL define the same symbol */
+ } else {
+#if 0
+ printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
+ sym_bind, sh_num, new_vis, esym_bind, esym->st_shndx, esym_vis);
+#endif
+ tcc_error_noabort("'%s' defined twice", name);
+ }
+ } else {
+ do_patch:
+ esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type);
+ esym->st_shndx = sh_num;
+ new_undef_sym = 1;
+ esym->st_value = value;
+ esym->st_size = size;
+ esym->st_other = other;
+ }
+ } else {
+ do_def:
+ sym_index = put_elf_sym(s, value, size,
+ ELFW(ST_INFO)(sym_bind, sym_type), other,
+ sh_num, name);
+ }
+ return sym_index;
+}
+
+/* put relocation */
+ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset,
+ int type, int symbol, addr_t addend)
+{
+ char buf[256];
+ Section *sr;
+ ElfW_Rel *rel;
+
+ sr = s->reloc;
+ if (!sr) {
+ /* if no relocation section, create it */
+ snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name);
+ /* if the symtab is allocated, then we consider the relocation
+ are also */
+ sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags);
+ sr->sh_entsize = sizeof(ElfW_Rel);
+ sr->link = symtab;
+ sr->sh_info = s->sh_num;
+ s->reloc = sr;
+ }
+ rel = section_ptr_add(sr, sizeof(ElfW_Rel));
+ rel->r_offset = offset;
+ rel->r_info = ELFW(R_INFO)(symbol, type);
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ rel->r_addend = addend;
+#else
+ if (addend)
+ tcc_error("non-zero addend on REL architecture");
+#endif
+}
+
+ST_FUNC void put_elf_reloc(Section *symtab, Section *s, unsigned long offset,
+ int type, int symbol)
+{
+ put_elf_reloca(symtab, s, offset, type, symbol, 0);
+}
+
+/* put stab debug information */
+
+ST_FUNC void put_stabs(const char *str, int type, int other, int desc,
+ unsigned long value)
+{
+ Stab_Sym *sym;
+
+ sym = section_ptr_add(stab_section, sizeof(Stab_Sym));
+ if (str) {
+ sym->n_strx = put_elf_str(stabstr_section, str);
+ } else {
+ sym->n_strx = 0;
+ }
+ sym->n_type = type;
+ sym->n_other = other;
+ sym->n_desc = desc;
+ sym->n_value = value;
+}
+
+ST_FUNC void put_stabs_r(const char *str, int type, int other, int desc,
+ unsigned long value, Section *sec, int sym_index)
+{
+ put_stabs(str, type, other, desc, value);
+ put_elf_reloc(symtab_section, stab_section,
+ stab_section->data_offset - sizeof(unsigned int),
+ R_DATA_32, sym_index);
+}
+
+ST_FUNC void put_stabn(int type, int other, int desc, int value)
+{
+ put_stabs(NULL, type, other, desc, value);
+}
+
+ST_FUNC void put_stabd(int type, int other, int desc)
+{
+ put_stabs(NULL, type, other, desc, 0);
+}
+
+/* Browse each elem of type <type> in section <sec> starting at elem <startoff>
+ using variable <elem> */
+#define for_each_elem(sec, startoff, elem, type) \
+ for (elem = (type *) sec->data + startoff; \
+ elem < (type *) (sec->data + sec->data_offset); elem++)
+
+/* In an ELF file symbol table, the local symbols must appear below
+ the global and weak ones. Since TCC cannot sort it while generating
+ the code, we must do it after. All the relocation tables are also
+ modified to take into account the symbol table sorting */
+static void sort_syms(TCCState *s1, Section *s)
+{
+ int *old_to_new_syms;
+ ElfW(Sym) *new_syms;
+ int nb_syms, i;
+ ElfW(Sym) *p, *q;
+ ElfW_Rel *rel;
+ Section *sr;
+ int type, sym_index;
+
+ nb_syms = s->data_offset / sizeof(ElfW(Sym));
+ new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym)));
+ old_to_new_syms = tcc_malloc(nb_syms * sizeof(int));
+
+ /* first pass for local symbols */
+ p = (ElfW(Sym) *)s->data;
+ q = new_syms;
+ for(i = 0; i < nb_syms; i++) {
+ if (ELFW(ST_BIND)(p->st_info) == STB_LOCAL) {
+ old_to_new_syms[i] = q - new_syms;
+ *q++ = *p;
+ }
+ p++;
+ }
+ /* save the number of local symbols in section header */
+ s->sh_info = q - new_syms;
+
+ /* then second pass for non local symbols */
+ p = (ElfW(Sym) *)s->data;
+ for(i = 0; i < nb_syms; i++) {
+ if (ELFW(ST_BIND)(p->st_info) != STB_LOCAL) {
+ old_to_new_syms[i] = q - new_syms;
+ *q++ = *p;
+ }
+ p++;
+ }
+
+ /* we copy the new symbols to the old */
+ memcpy(s->data, new_syms, nb_syms * sizeof(ElfW(Sym)));
+ tcc_free(new_syms);
+
+ /* now we modify all the relocations */
+ for(i = 1; i < s1->nb_sections; i++) {
+ sr = s1->sections[i];
+ if (sr->sh_type == SHT_RELX && sr->link == s) {
+ for_each_elem(sr, 0, rel, ElfW_Rel) {
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ type = ELFW(R_TYPE)(rel->r_info);
+ sym_index = old_to_new_syms[sym_index];
+ rel->r_info = ELFW(R_INFO)(sym_index, type);
+ }
+ }
+ }
+
+ tcc_free(old_to_new_syms);
+}
+
+/* relocate common symbols in the .bss section */
+ST_FUNC void relocate_common_syms(void)
+{
+ ElfW(Sym) *sym;
+ unsigned long offset, align;
+
+ for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
+ if (sym->st_shndx == SHN_COMMON) {
+ /* align symbol */
+ align = sym->st_value;
+ offset = bss_section->data_offset;
+ offset = (offset + align - 1) & -align;
+ sym->st_value = offset;
+ sym->st_shndx = bss_section->sh_num;
+ offset += sym->st_size;
+ bss_section->data_offset = offset;
+ }
+ }
+}
+
+/* relocate symbol table, resolve undefined symbols if do_resolve is
+ true and output error if undefined symbol. */
+ST_FUNC void relocate_syms(TCCState *s1, int do_resolve)
+{
+ ElfW(Sym) *sym, *esym;
+ int sym_bind, sh_num, sym_index;
+ const char *name;
+
+ for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
+ sh_num = sym->st_shndx;
+ if (sh_num == SHN_UNDEF) {
+ name = (char *) strtab_section->data + sym->st_name;
+ /* Use ld.so to resolve symbol for us (for tcc -run) */
+ if (do_resolve) {
+#if defined TCC_IS_NATIVE && !defined _WIN32
+ void *addr;
+ name = (char *) symtab_section->link->data + sym->st_name;
+ addr = resolve_sym(s1, name);
+ if (addr) {
+ sym->st_value = (addr_t)addr;
+#ifdef DEBUG_RELOC
+ printf ("relocate_sym: %s -> 0x%lx\n", name, sym->st_value);
+#endif
+ goto found;
+ }
+#endif
+ } else if (s1->dynsym) {
+ /* if dynamic symbol exist, then use it */
+ sym_index = find_elf_sym(s1->dynsym, name);
+ if (sym_index) {
+ esym = &((ElfW(Sym) *)s1->dynsym->data)[sym_index];
+ sym->st_value = esym->st_value;
+ goto found;
+ }
+ }
+ /* XXX: _fp_hw seems to be part of the ABI, so we ignore
+ it */
+ if (!strcmp(name, "_fp_hw"))
+ goto found;
+ /* only weak symbols are accepted to be undefined. Their
+ value is zero */
+ sym_bind = ELFW(ST_BIND)(sym->st_info);
+ if (sym_bind == STB_WEAK) {
+ sym->st_value = 0;
+ } else {
+ tcc_error_noabort("undefined symbol '%s'", name);
+ }
+ } else if (sh_num < SHN_LORESERVE) {
+ /* add section base */
+ sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
+ }
+ found: ;
+ }
+}
+
+/* relocate a given section (CPU dependent) by applying the relocations
+ in the associated relocation section */
+ST_FUNC void relocate_section(TCCState *s1, Section *s)
+{
+ Section *sr = s->reloc;
+ ElfW_Rel *rel;
+ ElfW(Sym) *sym;
+ int type, sym_index;
+ unsigned char *ptr;
+ addr_t val, addr;
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+ ElfW_Rel *qrel = (ElfW_Rel *) sr->data; /* ptr to next reloc entry reused */
+ int esym_index;
+#endif
+
+ for_each_elem(sr, 0, rel, ElfW_Rel) {
+ ptr = s->data + rel->r_offset;
+
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ val = sym->st_value;
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ val += rel->r_addend;
+#endif
+ type = ELFW(R_TYPE)(rel->r_info);
+ addr = s->sh_addr + rel->r_offset;
+
+ /* CPU specific */
+ switch(type) {
+#if defined(TCC_TARGET_I386)
+ case R_386_32:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ qrel->r_offset = rel->r_offset;
+ if (esym_index) {
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
+ qrel++;
+ break;
+ } else {
+ qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
+ qrel++;
+ }
+ }
+ *(int *)ptr += val;
+ break;
+ case R_386_PC32:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ /* DLL relocation */
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ if (esym_index) {
+ qrel->r_offset = rel->r_offset;
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
+ qrel++;
+ break;
+ }
+ }
+ *(int *)ptr += val - addr;
+ break;
+ case R_386_PLT32:
+ *(int *)ptr += val - addr;
+ break;
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ *(int *)ptr = val;
+ break;
+ case R_386_GOTPC:
+ *(int *)ptr += s1->got->sh_addr - addr;
+ break;
+ case R_386_GOTOFF:
+ *(int *)ptr += val - s1->got->sh_addr;
+ break;
+ case R_386_GOT32:
+ /* we load the got offset */
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
+ break;
+ case R_386_16:
+ if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
+ output_file:
+ tcc_error("can only produce 16-bit binary files");
+ }
+ *(short *)ptr += val;
+ break;
+ case R_386_PC16:
+ if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
+ goto output_file;
+ *(short *)ptr += val - addr;
+ break;
+#elif defined(TCC_TARGET_ARM)
+ case R_ARM_PC24:
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_PLT32:
+ {
+ int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko;
+ x = (*(int *) ptr) & 0xffffff;
+ if (sym->st_shndx == SHN_UNDEF)
+ val = s1->plt->sh_addr;
+#ifdef DEBUG_RELOC
+ printf ("reloc %d: x=0x%x val=0x%x ", type, x, val);
+#endif
+ (*(int *)ptr) &= 0xff000000;
+ if (x & 0x800000)
+ x -= 0x1000000;
+ x <<= 2;
+ blx_avail = (TCC_ARM_VERSION >= 5);
+ is_thumb = val & 1;
+ is_bl = (*(unsigned *) ptr) >> 24 == 0xeb;
+ is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl));
+ x += val - addr;
+#ifdef DEBUG_RELOC
+ printf (" newx=0x%x name=%s\n", x,
+ (char *) symtab_section->link->data + sym->st_name);
+#endif
+ h = x & 2;
+ th_ko = (x & 3) && (!blx_avail || !is_call);
+ if (th_ko || x >= 0x2000000 || x < -0x2000000)
+ tcc_error("can't relocate value at %x,%d",addr, type);
+ x >>= 2;
+ x &= 0xffffff;
+ /* Only reached if blx is avail and it is a call */
+ if (is_thumb) {
+ x |= h << 24;
+ (*(int *)ptr) = 0xfa << 24; /* bl -> blx */
+ }
+ (*(int *) ptr) |= x;
+ }
+ break;
+ /* Since these relocations only concern Thumb-2 and blx instruction was
+ introduced before Thumb-2, we can assume blx is available and not
+ guard its use */
+ case R_ARM_THM_PC22:
+ case R_ARM_THM_JUMP24:
+ {
+ int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
+ int to_thumb, is_call, to_plt, blx_bit = 1 << 12;
+ Section *plt;
+
+ /* weak reference */
+ if (sym->st_shndx == SHN_UNDEF &&
+ ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
+ break;
+
+ /* Get initial offset */
+ hi = (*(uint16_t *)ptr);
+ lo = (*(uint16_t *)(ptr+2));
+ s = (hi >> 10) & 1;
+ j1 = (lo >> 13) & 1;
+ j2 = (lo >> 11) & 1;
+ i1 = (j1 ^ s) ^ 1;
+ i2 = (j2 ^ s) ^ 1;
+ imm10 = hi & 0x3ff;
+ imm11 = lo & 0x7ff;
+ x = (s << 24) | (i1 << 23) | (i2 << 22) |
+ (imm10 << 12) | (imm11 << 1);
+ if (x & 0x01000000)
+ x -= 0x02000000;
+
+ /* Relocation infos */
+ to_thumb = val & 1;
+ plt = s1->plt;
+ to_plt = (val >= plt->sh_addr) &&
+ (val < plt->sh_addr + plt->data_offset);
+ is_call = (type == R_ARM_THM_PC22);
+
+ /* Compute final offset */
+ if (to_plt && !is_call) /* Point to 1st instr of Thumb stub */
+ x -= 4;
+ x += val - addr;
+ if (!to_thumb && is_call) {
+ blx_bit = 0; /* bl -> blx */
+ x = (x + 3) & -4; /* Compute offset from aligned PC */
+ }
+
+ /* Check that relocation is possible
+ * offset must not be out of range
+ * if target is to be entered in arm mode:
+ - bit 1 must not set
+ - instruction must be a call (bl) or a jump to PLT */
+ if (!to_thumb || x >= 0x1000000 || x < -0x1000000)
+ if (to_thumb || (val & 2) || (!is_call && !to_plt))
+ tcc_error("can't relocate value at %x,%d",addr, type);
+
+ /* Compute and store final offset */
+ s = (x >> 24) & 1;
+ i1 = (x >> 23) & 1;
+ i2 = (x >> 22) & 1;
+ j1 = s ^ (i1 ^ 1);
+ j2 = s ^ (i2 ^ 1);
+ imm10 = (x >> 12) & 0x3ff;
+ imm11 = (x >> 1) & 0x7ff;
+ (*(uint16_t *)ptr) = (uint16_t) ((hi & 0xf800) |
+ (s << 10) | imm10);
+ (*(uint16_t *)(ptr+2)) = (uint16_t) ((lo & 0xc000) |
+ (j1 << 13) | blx_bit | (j2 << 11) |
+ imm11);
+ }
+ break;
+ case R_ARM_MOVT_ABS:
+ case R_ARM_MOVW_ABS_NC:
+ {
+ int x, imm4, imm12;
+ if (type == R_ARM_MOVT_ABS)
+ val >>= 16;
+ imm12 = val & 0xfff;
+ imm4 = (val >> 12) & 0xf;
+ x = (imm4 << 16) | imm12;
+ if (type == R_ARM_THM_MOVT_ABS)
+ *(int *)ptr |= x;
+ else
+ *(int *)ptr += x;
+ }
+ break;
+ case R_ARM_THM_MOVT_ABS:
+ case R_ARM_THM_MOVW_ABS_NC:
+ {
+ int x, i, imm4, imm3, imm8;
+ if (type == R_ARM_THM_MOVT_ABS)
+ val >>= 16;
+ imm8 = val & 0xff;
+ imm3 = (val >> 8) & 0x7;
+ i = (val >> 11) & 1;
+ imm4 = (val >> 12) & 0xf;
+ x = (imm3 << 28) | (imm8 << 16) | (i << 10) | imm4;
+ if (type == R_ARM_THM_MOVT_ABS)
+ *(int *)ptr |= x;
+ else
+ *(int *)ptr += x;
+ }
+ break;
+ case R_ARM_PREL31:
+ {
+ int x;
+ x = (*(int *)ptr) & 0x7fffffff;
+ (*(int *)ptr) &= 0x80000000;
+ x = (x * 2) / 2;
+ x += val - addr;
+ if((x^(x>>1))&0x40000000)
+ tcc_error("can't relocate value at %x,%d",addr, type);
+ (*(int *)ptr) |= x & 0x7fffffff;
+ }
+ case R_ARM_ABS32:
+ *(int *)ptr += val;
+ break;
+ case R_ARM_REL32:
+ *(int *)ptr += val - addr;
+ break;
+ case R_ARM_GOTPC:
+ *(int *)ptr += s1->got->sh_addr - addr;
+ break;
+ case R_ARM_GOTOFF:
+ *(int *)ptr += val - s1->got->sh_addr;
+ break;
+ case R_ARM_GOT32:
+ /* we load the got offset */
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
+ break;
+ case R_ARM_COPY:
+ break;
+ case R_ARM_V4BX:
+ /* trade Thumb support for ARMv4 support */
+ if ((0x0ffffff0 & *(int*)ptr) == 0x012FFF10)
+ *(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */
+ break;
+ case R_ARM_GLOB_DAT:
+ case R_ARM_JUMP_SLOT:
+ *(addr_t *)ptr = val;
+ break;
+ case R_ARM_NONE:
+ /* Nothing to do. Normally used to indicate a dependency
+ on a certain symbol (like for exception handling under EABI). */
+ break;
+ default:
+ fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
+ break;
+#elif defined(TCC_TARGET_ARM64)
+ case R_AARCH64_ABS64:
+ *(uint64_t *)ptr = val;
+ break;
+ case R_AARCH64_ABS32:
+ *(uint32_t *)ptr = val;
+ break;
+ case R_AARCH64_MOVW_UABS_G0_NC:
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) |
+ (val & 0xffff) << 5;
+ break;
+ case R_AARCH64_MOVW_UABS_G1_NC:
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) |
+ (val >> 16 & 0xffff) << 5;
+ break;
+ case R_AARCH64_MOVW_UABS_G2_NC:
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) |
+ (val >> 32 & 0xffff) << 5;
+ break;
+ case R_AARCH64_MOVW_UABS_G3:
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffe0001f) |
+ (val >> 48 & 0xffff) << 5;
+ break;
+ case R_AARCH64_ADR_PREL_PG_HI21: {
+ uint64_t off = (val >> 12) - (addr >> 12);
+ if ((off + ((uint64_t)1 << 20)) >> 21)
+ tcc_error("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0x9f00001f) |
+ (off & 0x1ffffc) << 3 | (off & 3) << 29;
+ break;
+ }
+ case R_AARCH64_ADD_ABS_LO12_NC:
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xffc003ff) |
+ (val & 0xfff) << 10;
+ break;
+ case R_AARCH64_JUMP26:
+ case R_AARCH64_CALL26:
+ /* This check must match the one in build_got_entries, testing
+ if we really need a PLT slot. */
+ if (sym->st_shndx == SHN_UNDEF)
+ /* We've put the PLT slot offset into r_addend when generating
+ it, and that's what we must use as relocation value (adjusted
+ by section offset of course). */
+ val = s1->plt->sh_addr + rel->r_addend;
+#ifdef DEBUG_RELOC
+ printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
+ (char *) symtab_section->link->data + sym->st_name);
+#endif
+ if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc)
+ {
+ tcc_error("R_AARCH64_(JUMP|CALL)26 relocation failed (val=%lx, addr=%lx)", addr, val);
+ }
+ *(uint32_t *)ptr = 0x14000000 | (type == R_AARCH64_CALL26) << 31 |
+ ((val - addr) >> 2 & 0x3ffffff);
+ break;
+ case R_AARCH64_ADR_GOT_PAGE: {
+ uint64_t off =
+ (((s1->got->sh_addr +
+ s1->sym_attrs[sym_index].got_offset) >> 12) - (addr >> 12));
+ if ((off + ((uint64_t)1 << 20)) >> 21)
+ tcc_error("R_AARCH64_ADR_GOT_PAGE relocation failed");
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0x9f00001f) |
+ (off & 0x1ffffc) << 3 | (off & 3) << 29;
+ break;
+ }
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ *(uint32_t *)ptr = (*(uint32_t *)ptr & 0xfff803ff) |
+ ((s1->got->sh_addr + s1->sym_attrs[sym_index].got_offset)
+ & 0xff8) << 7;
+ break;
+ case R_AARCH64_COPY:
+ break;
+ case R_AARCH64_GLOB_DAT:
+ case R_AARCH64_JUMP_SLOT:
+ /* They don't need addend */
+#ifdef DEBUG_RELOC
+ printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr,
+ val - rel->r_addend,
+ (char *) symtab_section->link->data + sym->st_name);
+#endif
+ *(addr_t *)ptr = val - rel->r_addend;
+ break;
+ default:
+ fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
+ break;
+#elif defined(TCC_TARGET_C67)
+ case R_C60_32:
+ *(int *)ptr += val;
+ break;
+ case R_C60LO16:
+ {
+ uint32_t orig;
+
+ /* put the low 16 bits of the absolute address
+ add to what is already there */
+
+ orig = ((*(int *)(ptr )) >> 7) & 0xffff;
+ orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16;
+
+ /* patch both at once - assumes always in pairs Low - High */
+
+ *(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) | (((val+orig) & 0xffff) << 7);
+ *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | ((((val+orig)>>16) & 0xffff) << 7);
+ }
+ break;
+ case R_C60HI16:
+ break;
+ default:
+ fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
+ break;
+#elif defined(TCC_TARGET_X86_64)
+ case R_X86_64_64:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ qrel->r_offset = rel->r_offset;
+ if (esym_index) {
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64);
+ qrel->r_addend = rel->r_addend;
+ qrel++;
+ break;
+ } else {
+ qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
+ qrel->r_addend = *(long long *)ptr + val;
+ qrel++;
+ }
+ }
+ *(long long *)ptr += val;
+ break;
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ /* XXX: this logic may depend on TCC's codegen
+ now TCC uses R_X86_64_32 even for a 64bit pointer */
+ qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
+ qrel->r_addend = *(int *)ptr + val;
+ qrel++;
+ }
+ *(int *)ptr += val;
+ break;
+
+ case R_X86_64_PC32:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ /* DLL relocation */
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ if (esym_index) {
+ qrel->r_offset = rel->r_offset;
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
+ qrel->r_addend = *(int *)ptr;
+ qrel++;
+ break;
+ }
+ }
+ goto plt32pc32;
+
+ case R_X86_64_PLT32:
+ /* We've put the PLT slot offset into r_addend when generating
+ it, and that's what we must use as relocation value (adjusted
+ by section offset of course). */
+ val = s1->plt->sh_addr + rel->r_addend;
+ /* fallthrough. */
+
+ plt32pc32:
+ {
+ long long diff;
+ diff = (long long)val - addr;
+ if (diff < -2147483648LL || diff > 2147483647LL) {
+ tcc_error("internal error: relocation failed");
+ }
+ *(int *)ptr += diff;
+ }
+ break;
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_JUMP_SLOT:
+ /* They don't need addend */
+ *(addr_t *)ptr = val - rel->r_addend;
+ break;
+ case R_X86_64_GOTPCREL:
+ *(int *)ptr += (s1->got->sh_addr - addr +
+ s1->sym_attrs[sym_index].got_offset - 4);
+ break;
+ case R_X86_64_GOTTPOFF:
+ *(int *)ptr += val - s1->got->sh_addr;
+ break;
+ case R_X86_64_GOT32:
+ /* we load the got offset */
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
+ break;
+#else
+#error unsupported processor
+#endif
+ }
+ }
+ /* if the relocation is allocated, we change its symbol table */
+ if (sr->sh_flags & SHF_ALLOC)
+ sr->link = s1->dynsym;
+}
+
+/* relocate relocation table in 'sr' */
+static void relocate_rel(TCCState *s1, Section *sr)
+{
+ Section *s;
+ ElfW_Rel *rel;
+
+ s = s1->sections[sr->sh_info];
+ for_each_elem(sr, 0, rel, ElfW_Rel)
+ rel->r_offset += s->sh_addr;
+}
+
+/* count the number of dynamic relocations so that we can reserve
+ their space */
+static int prepare_dynamic_rel(TCCState *s1, Section *sr)
+{
+ ElfW_Rel *rel;
+ int sym_index, esym_index, type, count;
+
+ count = 0;
+ for_each_elem(sr, 0, rel, ElfW_Rel) {
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ type = ELFW(R_TYPE)(rel->r_info);
+ switch(type) {
+#if defined(TCC_TARGET_I386)
+ case R_386_32:
+#elif defined(TCC_TARGET_X86_64)
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ case R_X86_64_64:
+#endif
+ count++;
+ break;
+#if defined(TCC_TARGET_I386)
+ case R_386_PC32:
+#elif defined(TCC_TARGET_X86_64)
+ case R_X86_64_PC32:
+#endif
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ if (esym_index)
+ count++;
+ break;
+ default:
+ break;
+ }
+ }
+ if (count) {
+ /* allocate the section */
+ sr->sh_flags |= SHF_ALLOC;
+ sr->sh_size = count * sizeof(ElfW_Rel);
+ }
+ return count;
+}
+
+static struct sym_attr *alloc_sym_attr(TCCState *s1, int index)
+{
+ int n;
+ struct sym_attr *tab;
+
+ if (index >= s1->nb_sym_attrs) {
+ /* find immediately bigger power of 2 and reallocate array */
+ n = 1;
+ while (index >= n)
+ n *= 2;
+ tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
+ s1->sym_attrs = tab;
+ memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
+ (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
+ s1->nb_sym_attrs = n;
+ }
+ return &s1->sym_attrs[index];
+}
+
+/* XXX: suppress that */
+static void put32(unsigned char *p, uint32_t val)
+{
+ p[0] = val;
+ p[1] = val >> 8;
+ p[2] = val >> 16;
+ p[3] = val >> 24;
+}
+
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM) || \
+ defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+static uint32_t get32(unsigned char *p)
+{
+ return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+}
+#endif
+
+static void build_got(TCCState *s1)
+{
+ unsigned char *ptr;
+
+ /* if no got, then create it */
+ s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);
+ s1->got->sh_entsize = 4;
+ add_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
+ 0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_");
+ ptr = section_ptr_add(s1->got, 3 * PTR_SIZE);
+#if PTR_SIZE == 4
+ /* keep space for _DYNAMIC pointer, if present */
+ put32(ptr, 0);
+ /* two dummy got entries */
+ put32(ptr + 4, 0);
+ put32(ptr + 8, 0);
+#else
+ /* keep space for _DYNAMIC pointer, if present */
+ put32(ptr, 0);
+ put32(ptr + 4, 0);
+ /* two dummy got entries */
+ put32(ptr + 8, 0);
+ put32(ptr + 12, 0);
+ put32(ptr + 16, 0);
+ put32(ptr + 20, 0);
+#endif
+}
+
+/* put a got or plt entry corresponding to a symbol in symtab_section. 'size'
+ and 'info' can be modifed if more precise info comes from the DLL.
+ Returns offset of GOT or PLT slot. */
+static unsigned long put_got_entry(TCCState *s1,
+ int reloc_type, unsigned long size, int info,
+ int sym_index)
+{
+ int index, need_plt_entry;
+ const char *name;
+ ElfW(Sym) *sym;
+ unsigned long offset;
+ int *ptr;
+ struct sym_attr *symattr;
+
+ if (!s1->got)
+ build_got(s1);
+
+ need_plt_entry =
+#ifdef TCC_TARGET_X86_64
+ (reloc_type == R_X86_64_JUMP_SLOT);
+#elif defined(TCC_TARGET_I386)
+ (reloc_type == R_386_JMP_SLOT);
+#elif defined(TCC_TARGET_ARM)
+ (reloc_type == R_ARM_JUMP_SLOT);
+#elif defined(TCC_TARGET_ARM64)
+ (reloc_type == R_AARCH64_JUMP_SLOT);
+#else
+ 0;
+#endif
+
+ if (need_plt_entry && !s1->plt) {
+ /* add PLT */
+ s1->plt = new_section(s1, ".plt", SHT_PROGBITS,
+ SHF_ALLOC | SHF_EXECINSTR);
+ s1->plt->sh_entsize = 4;
+ }
+
+ /* If a got/plt entry already exists for that symbol, no need to add one */
+ if (sym_index < s1->nb_sym_attrs) {
+ if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset)
+ return s1->sym_attrs[sym_index].plt_offset;
+ else if (!need_plt_entry && s1->sym_attrs[sym_index].got_offset)
+ return s1->sym_attrs[sym_index].got_offset;
+ }
+
+ symattr = alloc_sym_attr(s1, sym_index);
+
+ /* Only store the GOT offset if it's not generated for the PLT entry. */
+ if (!need_plt_entry)
+ symattr->got_offset = s1->got->data_offset;
+
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ name = (char *) symtab_section->link->data + sym->st_name;
+ offset = sym->st_value;
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
+ if (need_plt_entry) {
+ Section *plt;
+ uint8_t *p;
+ int modrm;
+ unsigned long relofs;
+
+#if defined(TCC_OUTPUT_DLL_WITH_PLT)
+ modrm = 0x25;
+#else
+ /* if we build a DLL, we add a %ebx offset */
+ if (s1->output_type == TCC_OUTPUT_DLL)
+ modrm = 0xa3;
+ else
+ modrm = 0x25;
+#endif
+
+ /* add a PLT entry */
+ plt = s1->plt;
+ if (plt->data_offset == 0) {
+ /* first plt entry */
+ p = section_ptr_add(plt, 16);
+ p[0] = 0xff; /* pushl got + PTR_SIZE */
+ p[1] = modrm + 0x10;
+ put32(p + 2, PTR_SIZE);
+ p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */
+ p[7] = modrm;
+ put32(p + 8, PTR_SIZE * 2);
+ }
+
+ /* The PLT slot refers to the relocation entry it needs
+ via offset. The reloc entry is created below, so its
+ offset is the current data_offset. */
+ relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0;
+ symattr->plt_offset = plt->data_offset;
+ p = section_ptr_add(plt, 16);
+ p[0] = 0xff; /* jmp *(got + x) */
+ p[1] = modrm;
+ put32(p + 2, s1->got->data_offset);
+ p[6] = 0x68; /* push $xxx */
+#ifdef TCC_TARGET_X86_64
+ /* On x86-64, the relocation is referred to by _index_. */
+ put32(p + 7, relofs / sizeof (ElfW_Rel));
+#else
+ put32(p + 7, relofs);
+#endif
+ p[11] = 0xe9; /* jmp plt_start */
+ put32(p + 12, -(plt->data_offset));
+
+ /* If this was an UNDEF symbol set the offset in the
+ dynsymtab to the PLT slot, so that PC32 relocs to it
+ can be resolved. */
+ if (sym->st_shndx == SHN_UNDEF)
+ offset = plt->data_offset - 16;
+ }
+#elif defined(TCC_TARGET_ARM)
+ if (need_plt_entry) {
+ Section *plt;
+ uint8_t *p;
+
+ /* if we build a DLL, we add a %ebx offset */
+ if (s1->output_type == TCC_OUTPUT_DLL)
+ tcc_error("DLLs unimplemented!");
+
+ /* add a PLT entry */
+ plt = s1->plt;
+ if (plt->data_offset == 0) {
+ /* first plt entry */
+ p = section_ptr_add(plt, 16);
+ put32(p, 0xe52de004); /* push {lr} */
+ put32(p+4, 0xe59fe010); /* ldr lr, [pc, #16] */
+ put32(p+8, 0xe08fe00e); /* add lr, pc, lr */
+ put32(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */
+ }
+
+ symattr->plt_offset = plt->data_offset;
+ if (symattr->plt_thumb_stub) {
+ p = section_ptr_add(plt, 20);
+ put32(p, 0x4778); /* bx pc */
+ put32(p+2, 0x46c0); /* nop */
+ p += 4;
+ } else
+ p = section_ptr_add(plt, 16);
+ put32(p, 0xe59fc004); /* ldr ip, [pc, #4] ; GOT entry offset */
+ put32(p+4, 0xe08fc00c); /* add ip, pc, ip ; addr of GOT entry */
+ put32(p+8, 0xe59cf000); /* ldr pc, [ip] ; jump to GOT entry */
+ put32(p+12, s1->got->data_offset); /* GOT entry off once patched */
+
+ /* the symbol is modified so that it will be relocated to
+ the PLT */
+ if (sym->st_shndx == SHN_UNDEF)
+ offset = plt->data_offset - 16;
+ }
+#elif defined(TCC_TARGET_ARM64)
+ if (need_plt_entry) {
+ Section *plt;
+ uint8_t *p;
+
+ if (s1->output_type == TCC_OUTPUT_DLL)
+ tcc_error("DLLs unimplemented!");
+
+ plt = s1->plt;
+ if (plt->data_offset == 0)
+ section_ptr_add(plt, 32);
+ symattr->plt_offset = plt->data_offset;
+ p = section_ptr_add(plt, 16);
+ put32(p, s1->got->data_offset);
+ put32(p + 4, (uint64_t)s1->got->data_offset >> 32);
+
+ if (sym->st_shndx == SHN_UNDEF)
+ offset = plt->data_offset - 16;
+ }
+#elif defined(TCC_TARGET_C67)
+ if (s1->dynsym) {
+ tcc_error("C67 got not implemented");
+ }
+#else
+#error unsupported CPU
+#endif
+ if (s1->dynsym) {
+ /* XXX This might generate multiple syms for name. */
+ index = put_elf_sym(s1->dynsym, offset,
+ size, info, 0, sym->st_shndx, name);
+ /* Create the relocation (it's against the GOT for PLT
+ and GOT relocs). */
+ put_elf_reloc(s1->dynsym, s1->got,
+ s1->got->data_offset,
+ reloc_type, index);
+ } else {
+ /* Without .dynsym (i.e. static link or memory output) we
+ still need relocs against the generated got, so as to fill
+ the entries with the symbol values (determined later). */
+ put_elf_reloc(symtab_section, s1->got,
+ s1->got->data_offset,
+ reloc_type, sym_index);
+ }
+ /* And now create the GOT slot itself. */
+ ptr = section_ptr_add(s1->got, PTR_SIZE);
+ *ptr = 0;
+ if (need_plt_entry)
+ return symattr->plt_offset;
+ else
+ return symattr->got_offset;
+}
+
+/* build GOT and PLT entries */
+ST_FUNC void build_got_entries(TCCState *s1)
+{
+ Section *s;
+ ElfW_Rel *rel;
+ ElfW(Sym) *sym;
+ int i, type, reloc_type, sym_index;
+
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (s->sh_type != SHT_RELX)
+ continue;
+ /* no need to handle got relocations */
+ if (s->link != symtab_section)
+ continue;
+ for_each_elem(s, 0, rel, ElfW_Rel) {
+ type = ELFW(R_TYPE)(rel->r_info);
+ switch(type) {
+#if defined(TCC_TARGET_I386)
+ case R_386_GOT32:
+ case R_386_GOTOFF:
+ case R_386_GOTPC:
+ case R_386_PLT32:
+ if (!s1->got)
+ build_got(s1);
+ if (type == R_386_GOT32 || type == R_386_PLT32) {
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ /* look at the symbol got offset. If none, then add one */
+ if (type == R_386_GOT32)
+ reloc_type = R_386_GLOB_DAT;
+ else
+ reloc_type = R_386_JMP_SLOT;
+ put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
+ sym_index);
+ }
+ break;
+#elif defined(TCC_TARGET_ARM)
+ case R_ARM_PC24:
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_GOT32:
+ case R_ARM_GOTOFF:
+ case R_ARM_GOTPC:
+ case R_ARM_PLT32:
+ if (!s1->got)
+ build_got(s1);
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ if (type != R_ARM_GOTOFF && type != R_ARM_GOTPC
+ && sym->st_shndx == SHN_UNDEF) {
+ unsigned long ofs;
+ /* look at the symbol got offset. If none, then add one */
+ if (type == R_ARM_GOT32)
+ reloc_type = R_ARM_GLOB_DAT;
+ else
+ reloc_type = R_ARM_JUMP_SLOT;
+ ofs = put_got_entry(s1, reloc_type, sym->st_size,
+ sym->st_info, sym_index);
+#ifdef DEBUG_RELOC
+ printf ("maybegot: %s, %d, %d --> ofs=0x%x\n",
+ (char *) symtab_section->link->data + sym->st_name,
+ type, sym->st_shndx, ofs);
+#endif
+ if (type != R_ARM_GOT32) {
+ addr_t *ptr = (addr_t*)(s1->sections[s->sh_info]->data
+ + rel->r_offset);
+ /* x must be signed! */
+ int x = *ptr & 0xffffff;
+ x = (x << 8) >> 8;
+ x <<= 2;
+ x += ofs;
+ x >>= 2;
+#ifdef DEBUG_RELOC
+ printf ("insn=0x%x --> 0x%x (x==0x%x)\n", *ptr,
+ (*ptr & 0xff000000) | x, x);
+#endif
+ *ptr = (*ptr & 0xff000000) | x;
+ }
+ }
+ break;
+ case R_ARM_THM_JUMP24:
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ /* We are relocating a jump from thumb code to arm code */
+ if (sym->st_shndx != SHN_UNDEF && !(sym->st_value & 1)) {
+ int index;
+ uint8_t *p;
+ char *name, buf[1024];
+ Section *text_section;
+
+ name = (char *) symtab_section->link->data + sym->st_name;
+ text_section = s1->sections[sym->st_shndx];
+ /* Modify reloc to target a thumb stub to switch to ARM */
+ snprintf(buf, sizeof(buf), "%s_from_thumb", name);
+ index = put_elf_sym(symtab_section,
+ text_section->data_offset + 1,
+ sym->st_size, sym->st_info, 0,
+ sym->st_shndx, buf);
+ rel->r_info = ELFW(R_INFO)(index, type);
+ /* Create a thumb stub fonction to switch to ARM mode */
+ put_elf_reloc(symtab_section, text_section,
+ text_section->data_offset + 4, R_ARM_JUMP24,
+ sym_index);
+ p = section_ptr_add(text_section, 8);
+ put32(p, 0x4778); /* bx pc */
+ put32(p+2, 0x46c0); /* nop */
+ put32(p+4, 0xeafffffe); /* b $sym */
+ }
+#elif defined(TCC_TARGET_ARM64)
+ //xx Other cases may be required here:
+ case R_AARCH64_ADR_GOT_PAGE:
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ if (!s1->got)
+ build_got(s1);
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ reloc_type = R_AARCH64_GLOB_DAT;
+ put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
+ sym_index);
+ break;
+
+ case R_AARCH64_JUMP26:
+ case R_AARCH64_CALL26:
+ if (!s1->got)
+ build_got(s1);
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ if (sym->st_shndx == SHN_UNDEF) {
+ unsigned long ofs;
+ reloc_type = R_AARCH64_JUMP_SLOT;
+ ofs = put_got_entry(s1, reloc_type, sym->st_size,
+ sym->st_info, sym_index);
+ /* We store the place of the generated PLT slot
+ in our addend. */
+ rel->r_addend += ofs;
+ }
+ break;
+#elif defined(TCC_TARGET_C67)
+ case R_C60_GOT32:
+ case R_C60_GOTOFF:
+ case R_C60_GOTPC:
+ case R_C60_PLT32:
+ if (!s1->got)
+ build_got(s1);
+ if (type == R_C60_GOT32 || type == R_C60_PLT32) {
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ /* look at the symbol got offset. If none, then add one */
+ if (type == R_C60_GOT32)
+ reloc_type = R_C60_GLOB_DAT;
+ else
+ reloc_type = R_C60_JMP_SLOT;
+ put_got_entry(s1, reloc_type, sym->st_size, sym->st_info,
+ sym_index);
+ }
+ break;
+#elif defined(TCC_TARGET_X86_64)
+ case R_X86_64_GOT32:
+ case R_X86_64_GOTTPOFF:
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_PLT32:
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ if (type == R_X86_64_PLT32 &&
+ ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT)
+ {
+ rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32);
+ break;
+ }
+
+ if (!s1->got)
+ build_got(s1);
+ if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL ||
+ type == R_X86_64_PLT32) {
+ unsigned long ofs;
+ /* look at the symbol got offset. If none, then add one */
+ if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL)
+ reloc_type = R_X86_64_GLOB_DAT;
+ else
+ reloc_type = R_X86_64_JUMP_SLOT;
+ ofs = put_got_entry(s1, reloc_type, sym->st_size,
+ sym->st_info, sym_index);
+ if (type == R_X86_64_PLT32)
+ /* We store the place of the generated PLT slot
+ in our addend. */
+ rel->r_addend += ofs;
+ }
+ break;
+#else
+#error unsupported CPU
+#endif
+ default:
+ break;
+ }
+ }
+ }
+}
+
+ST_FUNC Section *new_symtab(TCCState *s1,
+ const char *symtab_name, int sh_type, int sh_flags,
+ const char *strtab_name,
+ const char *hash_name, int hash_sh_flags)
+{
+ Section *symtab, *strtab, *hash;
+ int *ptr, nb_buckets;
+
+ symtab = new_section(s1, symtab_name, sh_type, sh_flags);
+ symtab->sh_entsize = sizeof(ElfW(Sym));
+ strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags);
+ put_elf_str(strtab, "");
+ symtab->link = strtab;
+ put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL);
+
+ nb_buckets = 1;
+
+ hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags);
+ hash->sh_entsize = sizeof(int);
+ symtab->hash = hash;
+ hash->link = symtab;
+
+ ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int));
+ ptr[0] = nb_buckets;
+ ptr[1] = 1;
+ memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int));
+ return symtab;
+}
+
+/* put dynamic tag */
+static void put_dt(Section *dynamic, int dt, addr_t val)
+{
+ ElfW(Dyn) *dyn;
+ dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
+ dyn->d_tag = dt;
+ dyn->d_un.d_val = val;
+}
+
+static void add_init_array_defines(TCCState *s1, const char *section_name)
+{
+ Section *s;
+ long end_offset;
+ char sym_start[1024];
+ char sym_end[1024];
+
+ snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1);
+ snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1);
+
+ s = find_section(s1, section_name);
+ if (!s) {
+ end_offset = 0;
+ s = data_section;
+ } else {
+ end_offset = s->data_offset;
+ }
+
+ add_elf_sym(symtab_section,
+ 0, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ s->sh_num, sym_start);
+ add_elf_sym(symtab_section,
+ end_offset, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ s->sh_num, sym_end);
+}
+
+static int tcc_add_support(TCCState *s1, const char *filename)
+{
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s/%s/%s", s1->tcc_lib_path,
+ /* an cpu specific path inside tcc_lib_path, mainly for keeping libtcc1.a */
+ #ifdef TCC_TARGET_I386
+ "i386"
+ #endif
+ #ifdef TCC_TARGET_X86_64
+ "x86_64"
+ #endif
+ #ifdef TCC_TARGET_ARM
+ "arm"
+ #endif
+ #ifdef TCC_TARGET_ARM64
+ "arm64"
+ #endif
+ #ifdef TCC_TARGET_C67
+ "C67"
+ #endif
+ ,filename);
+
+ return tcc_add_file(s1, buf, TCC_FILETYPE_BINARY);
+}
+
+ST_FUNC void tcc_add_bcheck(TCCState *s1)
+{
+#ifdef CONFIG_TCC_BCHECK
+ addr_t *ptr;
+
+ if (0 == s1->do_bounds_check)
+ return;
+
+ /* XXX: add an object file to do that */
+ ptr = section_ptr_add(bounds_section, sizeof(*ptr));
+ *ptr = 0;
+ add_elf_sym(symtab_section, 0, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ bounds_section->sh_num, "__bounds_start");
+ if (s1->output_type != TCC_OUTPUT_MEMORY) {
+ /* add 'call __bound_init()' in .init section */
+
+ /* XXX not called on MSYS, reason is unknown. For this
+ case a call to __bound_init is performed in bcheck.c
+ when __bound_ptr_add, __bound_new_region,
+ __bound_delete_region called */
+
+ int sym_index = find_elf_sym(symtab_section, "__bound_init");
+ if (sym_index) {
+ Section *init_section = find_section(s1, ".init");
+ unsigned char *pinit = section_ptr_add(init_section, 5);
+ pinit[0] = 0xe8;
+ put32(pinit + 1, -4);
+ put_elf_reloc(symtab_section, init_section,
+ init_section->data_offset - 4, R_386_PC32, sym_index);
+ }
+ else
+ tcc_warning("__bound_init not defined");
+ }
+#endif
+}
+
+/* add tcc runtime libraries */
+ST_FUNC void tcc_add_runtime(TCCState *s1)
+{
+ tcc_add_pragma_libs(s1);
+
+ /* add libc */
+ if (!s1->nostdlib) {
+ tcc_add_library(s1, "c");
+#ifdef CONFIG_USE_LIBGCC
+ if (!s1->static_link) {
+ tcc_add_file(s1, TCC_LIBGCC, TCC_FILETYPE_BINARY);
+ }
+#endif
+ tcc_add_support(s1, "libtcc1.a");
+ }
+
+ /* tcc_add_bcheck tries to relocate a call to __bound_init in _init so
+ libtcc1.a must be loaded before for __bound_init to be defined and
+ crtn.o must be loaded after to not finalize _init too early. */
+ tcc_add_bcheck(s1);
+
+ if (!s1->nostdlib) {
+ /* add crt end if not memory output */
+ if (s1->output_type != TCC_OUTPUT_MEMORY)
+ tcc_add_crt(s1, "crtn.o");
+ }
+}
+
+/* add various standard linker symbols (must be done after the
+ sections are filled (for example after allocating common
+ symbols)) */
+ST_FUNC void tcc_add_linker_symbols(TCCState *s1)
+{
+ char buf[1024];
+ int i;
+ Section *s;
+
+ add_elf_sym(symtab_section,
+ text_section->data_offset, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ text_section->sh_num, "_etext");
+ add_elf_sym(symtab_section,
+ data_section->data_offset, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ data_section->sh_num, "_edata");
+ add_elf_sym(symtab_section,
+ bss_section->data_offset, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ bss_section->sh_num, "_end");
+ /* horrible new standard ldscript defines */
+ add_init_array_defines(s1, ".preinit_array");
+ add_init_array_defines(s1, ".init_array");
+ add_init_array_defines(s1, ".fini_array");
+
+ /* add start and stop symbols for sections whose name can be
+ expressed in C */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (s->sh_type == SHT_PROGBITS &&
+ (s->sh_flags & SHF_ALLOC)) {
+ const char *p;
+ int ch;
+
+ /* check if section name can be expressed in C */
+ p = s->name;
+ for(;;) {
+ ch = *p;
+ if (!ch)
+ break;
+ if (!isid(ch) && !isnum(ch))
+ goto next_sec;
+ p++;
+ }
+ snprintf(buf, sizeof(buf), "__start_%s", s->name);
+ add_elf_sym(symtab_section,
+ 0, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ s->sh_num, buf);
+ snprintf(buf, sizeof(buf), "__stop_%s", s->name);
+ add_elf_sym(symtab_section,
+ s->data_offset, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ s->sh_num, buf);
+ }
+ next_sec: ;
+ }
+}
+
+static void tcc_output_binary(TCCState *s1, FILE *f,
+ const int *sec_order)
+{
+ Section *s;
+ int i, offset, size;
+
+ offset = 0;
+ for(i=1;i<s1->nb_sections;i++) {
+ s = s1->sections[sec_order[i]];
+ if (s->sh_type != SHT_NOBITS &&
+ (s->sh_flags & SHF_ALLOC)) {
+ while (offset < s->sh_offset) {
+ fputc(0, f);
+ offset++;
+ }
+ size = s->sh_size;
+ fwrite(s->data, 1, size, f);
+ offset += size;
+ }
+ }
+}
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#define HAVE_PHDR 1
+#define EXTRA_RELITEMS 14
+
+/* move the relocation value from .dynsym to .got */
+void patch_dynsym_undef(TCCState *s1, Section *s)
+{
+ uint32_t *gotd = (void *)s1->got->data;
+ ElfW(Sym) *sym;
+
+ gotd += 3; /* dummy entries in .got */
+ /* relocate symbols in .dynsym */
+ for_each_elem(s, 1, sym, ElfW(Sym)) {
+ if (sym->st_shndx == SHN_UNDEF) {
+ *gotd++ = sym->st_value + 6; /* XXX 6 is magic ? */
+ sym->st_value = 0;
+ }
+ }
+}
+#else
+#define HAVE_PHDR 1
+#define EXTRA_RELITEMS 9
+
+/* zero plt offsets of weak symbols in .dynsym */
+void patch_dynsym_undef(TCCState *s1, Section *s)
+{
+ ElfW(Sym) *sym;
+
+ for_each_elem(s, 1, sym, ElfW(Sym))
+ if (sym->st_shndx == SHN_UNDEF && ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
+ sym->st_value = 0;
+}
+#endif
+
+ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
+{
+ int sym_index = ELFW(R_SYM) (rel->r_info);
+ ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
+ unsigned long offset;
+
+ if (sym_index >= s1->nb_sym_attrs)
+ return;
+ offset = s1->sym_attrs[sym_index].got_offset;
+ section_reserve(s1->got, offset + PTR_SIZE);
+#ifdef TCC_TARGET_X86_64
+ /* only works for x86-64 */
+ put32(s1->got->data + offset + 4, sym->st_value >> 32);
+#endif
+ put32(s1->got->data + offset, sym->st_value & 0xffffffff);
+}
+
+/* Perform relocation to GOT or PLT entries */
+ST_FUNC void fill_got(TCCState *s1)
+{
+ Section *s;
+ ElfW_Rel *rel;
+ int i;
+
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (s->sh_type != SHT_RELX)
+ continue;
+ /* no need to handle got relocations */
+ if (s->link != symtab_section)
+ continue;
+ for_each_elem(s, 0, rel, ElfW_Rel) {
+ switch (ELFW(R_TYPE) (rel->r_info)) {
+ case R_X86_64_GOT32:
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_PLT32:
+ fill_got_entry(s1, rel);
+ break;
+ }
+ }
+ }
+}
+
+/* Bind symbols of executable: resolve undefined symbols from exported symbols
+ in shared libraries and export non local defined symbols to shared libraries
+ if -rdynamic switch was given on command line */
+static void bind_exe_dynsyms(TCCState *s1)
+{
+ const char *name;
+ int sym_index, index;
+ ElfW(Sym) *sym, *esym;
+ int type;
+
+ /* Resolve undefined symbols from dynamic symbols. When there is a match:
+ - if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT
+ - if STT_OBJECT symbol -> add it in .bss section with suitable reloc */
+ for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
+ if (sym->st_shndx == SHN_UNDEF) {
+ name = (char *) symtab_section->link->data + sym->st_name;
+ sym_index = find_elf_sym(s1->dynsymtab_section, name);
+ if (sym_index) {
+ esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
+ type = ELFW(ST_TYPE)(esym->st_info);
+ if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
+ /* Indirect functions shall have STT_FUNC type in executable
+ * dynsym section. Indeed, a dlsym call following a lazy
+ * resolution would pick the symbol value from the
+ * executable dynsym entry which would contain the address
+ * of the function wanted by the caller of dlsym instead of
+ * the address of the function that would return that
+ * address */
+ put_got_entry(s1, R_JMP_SLOT, esym->st_size,
+ ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC),
+ sym - (ElfW(Sym) *)symtab_section->data);
+ } else if (type == STT_OBJECT) {
+ unsigned long offset;
+ ElfW(Sym) *dynsym;
+ offset = bss_section->data_offset;
+ /* XXX: which alignment ? */
+ offset = (offset + 16 - 1) & -16;
+ index = put_elf_sym(s1->dynsym, offset, esym->st_size,
+ esym->st_info, 0, bss_section->sh_num,
+ name);
+ /* Ensure R_COPY works for weak symbol aliases */
+ if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
+ for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
+ if ((dynsym->st_value == esym->st_value)
+ && (ELFW(ST_BIND)(dynsym->st_info) == STB_GLOBAL)) {
+ char *dynname = (char *) s1->dynsymtab_section->link->data
+ + dynsym->st_name;
+ put_elf_sym(s1->dynsym, offset, dynsym->st_size,
+ dynsym->st_info, 0,
+ bss_section->sh_num, dynname);
+ break;
+ }
+ }
+ }
+ put_elf_reloc(s1->dynsym, bss_section,
+ offset, R_COPY, index);
+ offset += esym->st_size;
+ bss_section->data_offset = offset;
+ }
+ } else {
+ /* STB_WEAK undefined symbols are accepted */
+ /* XXX: _fp_hw seems to be part of the ABI, so we ignore it */
+ if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
+ !strcmp(name, "_fp_hw")) {
+ } else {
+ tcc_error_noabort("undefined symbol '%s'", name);
+ }
+ }
+ } else if (s1->rdynamic && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
+ /* if -rdynamic option, then export all non local symbols */
+ name = (char *) symtab_section->link->data + sym->st_name;
+ put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info,
+ 0, sym->st_shndx, name);
+ }
+ }
+}
+
+/* Bind symbols of libraries: export non local symbols of executable that
+ resolve undefined symbols of shared libraries */
+static void bind_libs_dynsyms(TCCState *s1)
+{
+ const char *name;
+ int sym_index;
+ ElfW(Sym) *sym, *esym;
+
+ /* now look at unresolved dynamic symbols and export
+ corresponding symbol */
+ for_each_elem(s1->dynsymtab_section, 1, esym, ElfW(Sym)) {
+ name = (char *) s1->dynsymtab_section->link->data + esym->st_name;
+ sym_index = find_elf_sym(symtab_section, name);
+ if (sym_index) {
+ /* XXX: avoid adding a symbol if already present because of
+ -rdynamic ? */
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ if (sym->st_shndx != SHN_UNDEF)
+ put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
+ sym->st_info, 0, sym->st_shndx, name);
+ } else if (esym->st_shndx == SHN_UNDEF) {
+ /* weak symbols can stay undefined */
+ if (ELFW(ST_BIND)(esym->st_info) != STB_WEAK)
+ tcc_warning("undefined dynamic symbol '%s'", name);
+ }
+ }
+}
+
+/* Export all non local symbols (for shared libraries) */
+static void export_global_syms(TCCState *s1)
+{
+ int nb_syms, dynindex, index;
+ const char *name;
+ ElfW(Sym) *sym;
+
+ nb_syms = symtab_section->data_offset / sizeof(ElfW(Sym));
+ s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms);
+ for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
+ if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
+ name = (char *) symtab_section->link->data + sym->st_name;
+ dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size,
+ sym->st_info, 0, sym->st_shndx, name);
+ index = sym - (ElfW(Sym) *) symtab_section->data;
+ s1->symtab_to_dynsym[index] = dynindex;
+ }
+ }
+}
+
+/* relocate the PLT: compute addresses and offsets in the PLT now that final
+ address for PLT and GOT are known (see fill_program_header) */
+ST_FUNC void relocate_plt(TCCState *s1)
+{
+ uint8_t *p, *p_end;
+
+ if (!s1->plt)
+ return;
+
+ p = s1->plt->data;
+ p_end = p + s1->plt->data_offset;
+ if (p < p_end) {
+#if defined(TCC_TARGET_I386)
+ put32(p + 2, get32(p + 2) + s1->got->sh_addr);
+ put32(p + 8, get32(p + 8) + s1->got->sh_addr);
+ p += 16;
+ while (p < p_end) {
+ put32(p + 2, get32(p + 2) + s1->got->sh_addr);
+ p += 16;
+ }
+#elif defined(TCC_TARGET_X86_64)
+ int x = s1->got->sh_addr - s1->plt->sh_addr - 6;
+ put32(p + 2, get32(p + 2) + x);
+ put32(p + 8, get32(p + 8) + x - 6);
+ p += 16;
+ while (p < p_end) {
+ put32(p + 2, get32(p + 2) + x + s1->plt->data - p);
+ p += 16;
+ }
+#elif defined(TCC_TARGET_ARM)
+ int x;
+ x=s1->got->sh_addr - s1->plt->sh_addr - 12;
+ p += 16;
+ while (p < p_end) {
+ if (get32(p) == 0x46c04778) /* PLT Thumb stub present */
+ p += 4;
+ put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
+ p += 16;
+ }
+#elif defined(TCC_TARGET_ARM64)
+ uint64_t plt = s1->plt->sh_addr;
+ uint64_t got = s1->got->sh_addr;
+ uint64_t off = (got >> 12) - (plt >> 12);
+ if ((off + ((uint32_t)1 << 20)) >> 21)
+ tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", off, got, plt);
+ put32(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]!
+ put32(p + 4, (0x90000010 | // adrp x16,...
+ (off & 0x1ffffc) << 3 | (off & 3) << 29));
+ put32(p + 8, (0xf9400211 | // ldr x17,[x16,#...]
+ (got & 0xff8) << 7));
+ put32(p + 12, (0x91000210 | // add x16,x16,#...
+ (got & 0xfff) << 10));
+ put32(p + 16, 0xd61f0220); // br x17
+ put32(p + 20, 0xd503201f); // nop
+ put32(p + 24, 0xd503201f); // nop
+ put32(p + 28, 0xd503201f); // nop
+ p += 32;
+ while (p < p_end) {
+ uint64_t pc = plt + (p - s1->plt->data);
+ uint64_t addr = got +
+ (get32(p) | (uint64_t)get32(p + 4) << 32);
+ uint32_t off = (addr >> 12) - (pc >> 12);
+ if ((off + ((uint32_t)1 << 20)) >> 21)
+ tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc);
+ put32(p, (0x90000010 | // adrp x16,...
+ (off & 0x1ffffc) << 3 | (off & 3) << 29));
+ put32(p + 4, (0xf9400211 | // ldr x17,[x16,#...]
+ (addr & 0xff8) << 7));
+ put32(p + 8, (0x91000210 | // add x16,x16,#...
+ (addr & 0xfff) << 10));
+ put32(p + 12, 0xd61f0220); // br x17
+ p += 16;
+ }
+#elif defined(TCC_TARGET_C67)
+ /* XXX: TODO */
+#else
+#error unsupported CPU
+#endif
+ }
+}
+
+/* Allocate strings for section names and decide if an unallocated section
+ should be output.
+
+ NOTE: the strsec section comes last, so its size is also correct ! */
+static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec)
+{
+ int i;
+ Section *s;
+
+ /* Allocate strings for section names */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ s->sh_name = put_elf_str(strsec, s->name);
+ /* when generating a DLL, we include relocations but we may
+ patch them */
+ if (file_type == TCC_OUTPUT_DLL &&
+ s->sh_type == SHT_RELX &&
+ !(s->sh_flags & SHF_ALLOC)) {
+ /* gr: avoid bogus relocs for empty (debug) sections */
+ if (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC)
+ prepare_dynamic_rel(s1, s);
+ else if (s1->do_debug)
+ s->sh_size = s->data_offset;
+ } else if (s1->do_debug ||
+ file_type == TCC_OUTPUT_OBJ ||
+ file_type == TCC_OUTPUT_EXE ||
+ (s->sh_flags & SHF_ALLOC) ||
+ i == (s1->nb_sections - 1)) {
+ /* we output all sections if debug or object file */
+ s->sh_size = s->data_offset;
+ }
+ }
+}
+
+/* Info to be copied in dynamic section */
+struct dyn_inf {
+ Section *dynamic;
+ Section *dynstr;
+ unsigned long dyn_rel_off;
+ addr_t rel_addr;
+ addr_t rel_size;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ addr_t bss_addr;
+ addr_t bss_size;
+#endif
+};
+
+/* Assign sections to segments and decide how are sections laid out when loaded
+ in memory. This function also fills corresponding program headers. */
+static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum,
+ Section *interp, Section* strsec,
+ struct dyn_inf *dyninf, int *sec_order)
+{
+ int i, j, k, file_type, sh_order_index, file_offset;
+ unsigned long s_align;
+ long long tmp;
+ addr_t addr;
+ ElfW(Phdr) *ph;
+ Section *s;
+
+ file_type = s1->output_type;
+ sh_order_index = 1;
+ file_offset = 0;
+ if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
+ file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
+ s_align = ELF_PAGE_SIZE;
+ if (s1->section_align)
+ s_align = s1->section_align;
+
+ if (phnum > 0) {
+ if (s1->has_text_addr) {
+ int a_offset, p_offset;
+ addr = s1->text_addr;
+ /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset %
+ ELF_PAGE_SIZE */
+ a_offset = (int) (addr & (s_align - 1));
+ p_offset = file_offset & (s_align - 1);
+ if (a_offset < p_offset)
+ a_offset += s_align;
+ file_offset += (a_offset - p_offset);
+ } else {
+ if (file_type == TCC_OUTPUT_DLL)
+ addr = 0;
+ else
+ addr = ELF_START_ADDR;
+ /* compute address after headers */
+ addr += (file_offset & (s_align - 1));
+ }
+
+ ph = &phdr[0];
+ /* Leave one program headers for the program interpreter and one for
+ the program header table itself if needed. These are done later as
+ they require section layout to be done first. */
+ if (interp)
+ ph += 1 + HAVE_PHDR;
+
+ /* dynamic relocation table information, for .dynamic section */
+ dyninf->rel_addr = dyninf->rel_size = 0;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ dyninf->bss_addr = dyninf->bss_size = 0;
+#endif
+
+ for(j = 0; j < 2; j++) {
+ ph->p_type = PT_LOAD;
+ if (j == 0)
+ ph->p_flags = PF_R | PF_X;
+ else
+ ph->p_flags = PF_R | PF_W;
+ ph->p_align = s_align;
+
+ /* Decide the layout of sections loaded in memory. This must
+ be done before program headers are filled since they contain
+ info about the layout. We do the following ordering: interp,
+ symbol tables, relocations, progbits, nobits */
+ /* XXX: do faster and simpler sorting */
+ for(k = 0; k < 5; k++) {
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ /* compute if section should be included */
+ if (j == 0) {
+ if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
+ SHF_ALLOC)
+ continue;
+ } else {
+ if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) !=
+ (SHF_ALLOC | SHF_WRITE))
+ continue;
+ }
+ if (s == interp) {
+ if (k != 0)
+ continue;
+ } else if (s->sh_type == SHT_DYNSYM ||
+ s->sh_type == SHT_STRTAB ||
+ s->sh_type == SHT_HASH) {
+ if (k != 1)
+ continue;
+ } else if (s->sh_type == SHT_RELX) {
+ if (k != 2)
+ continue;
+ } else if (s->sh_type == SHT_NOBITS) {
+ if (k != 4)
+ continue;
+ } else {
+ if (k != 3)
+ continue;
+ }
+ sec_order[sh_order_index++] = i;
+
+ /* section matches: we align it and add its size */
+ tmp = addr;
+ addr = (addr + s->sh_addralign - 1) &
+ ~(s->sh_addralign - 1);
+ file_offset += (int) ( addr - tmp );
+ s->sh_offset = file_offset;
+ s->sh_addr = addr;
+
+ /* update program header infos */
+ if (ph->p_offset == 0) {
+ ph->p_offset = file_offset;
+ ph->p_vaddr = addr;
+ ph->p_paddr = ph->p_vaddr;
+ }
+ /* update dynamic relocation infos */
+ if (s->sh_type == SHT_RELX) {
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ if (!strcmp(strsec->data + s->sh_name, ".rel.got")) {
+ dyninf->rel_addr = addr;
+ dyninf->rel_size += s->sh_size; /* XXX only first rel. */
+ }
+ if (!strcmp(strsec->data + s->sh_name, ".rel.bss")) {
+ dyninf->bss_addr = addr;
+ dyninf->bss_size = s->sh_size; /* XXX only first rel. */
+ }
+#else
+ if (dyninf->rel_size == 0)
+ dyninf->rel_addr = addr;
+ dyninf->rel_size += s->sh_size;
+#endif
+ }
+ addr += s->sh_size;
+ if (s->sh_type != SHT_NOBITS)
+ file_offset += s->sh_size;
+ }
+ }
+ if (j == 0) {
+ /* Make the first PT_LOAD segment include the program
+ headers itself (and the ELF header as well), it'll
+ come out with same memory use but will make various
+ tools like binutils strip work better. */
+ ph->p_offset &= ~(ph->p_align - 1);
+ ph->p_vaddr &= ~(ph->p_align - 1);
+ ph->p_paddr &= ~(ph->p_align - 1);
+ }
+ ph->p_filesz = file_offset - ph->p_offset;
+ ph->p_memsz = addr - ph->p_vaddr;
+ ph++;
+ if (j == 0) {
+ if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) {
+ /* if in the middle of a page, we duplicate the page in
+ memory so that one copy is RX and the other is RW */
+ if ((addr & (s_align - 1)) != 0)
+ addr += s_align;
+ } else {
+ addr = (addr + s_align - 1) & ~(s_align - 1);
+ file_offset = (file_offset + s_align - 1) & ~(s_align - 1);
+ }
+ }
+ }
+ }
+
+ /* all other sections come after */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (phnum > 0 && (s->sh_flags & SHF_ALLOC))
+ continue;
+ sec_order[sh_order_index++] = i;
+
+ file_offset = (file_offset + s->sh_addralign - 1) &
+ ~(s->sh_addralign - 1);
+ s->sh_offset = file_offset;
+ if (s->sh_type != SHT_NOBITS)
+ file_offset += s->sh_size;
+ }
+
+ return file_offset;
+}
+
+static void fill_unloadable_phdr(ElfW(Phdr) *phdr, int phnum, Section *interp,
+ Section *dynamic)
+{
+ ElfW(Phdr) *ph;
+
+ /* if interpreter, then add corresponding program header */
+ if (interp) {
+ ph = &phdr[0];
+
+ if (HAVE_PHDR)
+ {
+ int len = phnum * sizeof(ElfW(Phdr));
+
+ ph->p_type = PT_PHDR;
+ ph->p_offset = sizeof(ElfW(Ehdr));
+ ph->p_vaddr = interp->sh_addr - len;
+ ph->p_paddr = ph->p_vaddr;
+ ph->p_filesz = ph->p_memsz = len;
+ ph->p_flags = PF_R | PF_X;
+ ph->p_align = 4; /* interp->sh_addralign; */
+ ph++;
+ }
+
+ ph->p_type = PT_INTERP;
+ ph->p_offset = interp->sh_offset;
+ ph->p_vaddr = interp->sh_addr;
+ ph->p_paddr = ph->p_vaddr;
+ ph->p_filesz = interp->sh_size;
+ ph->p_memsz = interp->sh_size;
+ ph->p_flags = PF_R;
+ ph->p_align = interp->sh_addralign;
+ }
+
+ /* if dynamic section, then add corresponding program header */
+ if (dynamic) {
+ ph = &phdr[phnum - 1];
+
+ ph->p_type = PT_DYNAMIC;
+ ph->p_offset = dynamic->sh_offset;
+ ph->p_vaddr = dynamic->sh_addr;
+ ph->p_paddr = ph->p_vaddr;
+ ph->p_filesz = dynamic->sh_size;
+ ph->p_memsz = dynamic->sh_size;
+ ph->p_flags = PF_R | PF_W;
+ ph->p_align = dynamic->sh_addralign;
+ }
+}
+
+/* Fill the dynamic section with tags describing the address and size of
+ sections */
+static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf)
+{
+ Section *dynamic;
+
+ dynamic = dyninf->dynamic;
+
+ /* put dynamic section entries */
+ dynamic->data_offset = dyninf->dyn_rel_off;
+ put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr);
+ put_dt(dynamic, DT_STRTAB, dyninf->dynstr->sh_addr);
+ put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr);
+ put_dt(dynamic, DT_STRSZ, dyninf->dynstr->data_offset);
+ put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym)));
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ put_dt(dynamic, DT_RELA, dyninf->rel_addr);
+ put_dt(dynamic, DT_RELASZ, dyninf->rel_size);
+ put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel));
+#else
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr);
+ put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size);
+ put_dt(dynamic, DT_JMPREL, dyninf->rel_addr);
+ put_dt(dynamic, DT_PLTREL, DT_REL);
+ put_dt(dynamic, DT_REL, dyninf->bss_addr);
+ put_dt(dynamic, DT_RELSZ, dyninf->bss_size);
+#else
+ put_dt(dynamic, DT_REL, dyninf->rel_addr);
+ put_dt(dynamic, DT_RELSZ, dyninf->rel_size);
+ put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel));
+#endif
+#endif
+ if (s1->do_debug)
+ put_dt(dynamic, DT_DEBUG, 0);
+ put_dt(dynamic, DT_NULL, 0);
+}
+
+/* Relocate remaining sections and symbols (that is those not related to
+ dynamic linking) */
+static int final_sections_reloc(TCCState *s1)
+{
+ int i;
+ Section *s;
+
+ relocate_syms(s1, 0);
+
+ if (s1->nb_errors != 0)
+ return -1;
+
+ /* relocate sections */
+ /* XXX: ignore sections with allocated relocations ? */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+#ifdef TCC_TARGET_I386
+ if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
+ /* On X86 gdb 7.3 works in any case but gdb 6.6 will crash if SHF_ALLOC
+ checking is removed */
+#else
+ if (s->reloc && s != s1->got)
+ /* On X86_64 gdb 7.3 will crash if SHF_ALLOC checking is present */
+#endif
+ relocate_section(s1, s);
+ }
+
+ /* relocate relocation entries if the relocation tables are
+ allocated in the executable */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if ((s->sh_flags & SHF_ALLOC) &&
+ s->sh_type == SHT_RELX) {
+ relocate_rel(s1, s);
+ }
+ }
+ return 0;
+}
+
+/* Create an ELF file on disk.
+ This function handle ELF specific layout requirements */
+static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr,
+ int file_offset, int *sec_order)
+{
+ int i, shnum, offset, size, file_type;
+ Section *s;
+ ElfW(Ehdr) ehdr;
+ ElfW(Shdr) shdr, *sh;
+
+ file_type = s1->output_type;
+ shnum = s1->nb_sections;
+
+ memset(&ehdr, 0, sizeof(ehdr));
+
+ if (phnum > 0) {
+ ehdr.e_phentsize = sizeof(ElfW(Phdr));
+ ehdr.e_phnum = phnum;
+ ehdr.e_phoff = sizeof(ElfW(Ehdr));
+ }
+
+ /* align to 4 */
+ file_offset = (file_offset + 3) & -4;
+
+ /* fill header */
+ ehdr.e_ident[0] = ELFMAG0;
+ ehdr.e_ident[1] = ELFMAG1;
+ ehdr.e_ident[2] = ELFMAG2;
+ ehdr.e_ident[3] = ELFMAG3;
+ ehdr.e_ident[4] = ELFCLASSW;
+ ehdr.e_ident[5] = ELFDATA2LSB;
+ ehdr.e_ident[6] = EV_CURRENT;
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+ ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
+#endif
+#ifdef TCC_TARGET_ARM
+#ifdef TCC_ARM_EABI
+ ehdr.e_ident[EI_OSABI] = 0;
+ ehdr.e_flags = EF_ARM_EABI_VER4;
+ if (file_type == TCC_OUTPUT_EXE || file_type == TCC_OUTPUT_DLL)
+ ehdr.e_flags |= EF_ARM_HASENTRY;
+ if (s1->float_abi == ARM_HARD_FLOAT)
+ ehdr.e_flags |= EF_ARM_VFP_FLOAT;
+ else
+ ehdr.e_flags |= EF_ARM_SOFT_FLOAT;
+#else
+ ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM;
+#endif
+#endif
+ switch(file_type) {
+ default:
+ case TCC_OUTPUT_EXE:
+ ehdr.e_type = ET_EXEC;
+ ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
+ break;
+ case TCC_OUTPUT_DLL:
+ ehdr.e_type = ET_DYN;
+ ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
+ break;
+ case TCC_OUTPUT_OBJ:
+ ehdr.e_type = ET_REL;
+ break;
+ }
+ ehdr.e_machine = EM_TCC_TARGET;
+ ehdr.e_version = EV_CURRENT;
+ ehdr.e_shoff = file_offset;
+ ehdr.e_ehsize = sizeof(ElfW(Ehdr));
+ ehdr.e_shentsize = sizeof(ElfW(Shdr));
+ ehdr.e_shnum = shnum;
+ ehdr.e_shstrndx = shnum - 1;
+
+ fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f);
+ fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f);
+ offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr));
+
+ sort_syms(s1, symtab_section);
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[sec_order[i]];
+ if (s->sh_type != SHT_NOBITS) {
+ if (s->sh_type == SHT_DYNSYM)
+ patch_dynsym_undef(s1, s);
+ while (offset < s->sh_offset) {
+ fputc(0, f);
+ offset++;
+ }
+ size = s->sh_size;
+ fwrite(s->data, 1, size, f);
+ offset += size;
+ }
+ }
+
+ /* output section headers */
+ while (offset < ehdr.e_shoff) {
+ fputc(0, f);
+ offset++;
+ }
+
+ for(i = 0; i < s1->nb_sections; i++) {
+ sh = &shdr;
+ memset(sh, 0, sizeof(ElfW(Shdr)));
+ s = s1->sections[i];
+ if (s) {
+ sh->sh_name = s->sh_name;
+ sh->sh_type = s->sh_type;
+ sh->sh_flags = s->sh_flags;
+ sh->sh_entsize = s->sh_entsize;
+ sh->sh_info = s->sh_info;
+ if (s->link)
+ sh->sh_link = s->link->sh_num;
+ sh->sh_addralign = s->sh_addralign;
+ sh->sh_addr = s->sh_addr;
+ sh->sh_offset = s->sh_offset;
+ sh->sh_size = s->sh_size;
+ }
+ fwrite(sh, 1, sizeof(ElfW(Shdr)), f);
+ }
+}
+
+/* Write an elf, coff or "binary" file */
+static int tcc_write_elf_file(TCCState *s1, const char *filename, int phnum,
+ ElfW(Phdr) *phdr, int file_offset, int *sec_order)
+{
+ int fd, mode, file_type;
+ FILE *f;
+
+ file_type = s1->output_type;
+ if (file_type == TCC_OUTPUT_OBJ)
+ mode = 0666;
+ else
+ mode = 0777;
+ unlink(filename);
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode);
+ if (fd < 0) {
+ tcc_error_noabort("could not write '%s'", filename);
+ return -1;
+ }
+ f = fdopen(fd, "wb");
+ if (s1->verbose)
+ printf("<- %s\n", filename);
+
+#ifdef TCC_TARGET_COFF
+ if (s1->output_format == TCC_OUTPUT_FORMAT_COFF)
+ tcc_output_coff(s1, f);
+ else
+#endif
+ if (s1->output_format == TCC_OUTPUT_FORMAT_ELF)
+ tcc_output_elf(s1, f, phnum, phdr, file_offset, sec_order);
+ else
+ tcc_output_binary(s1, f, sec_order);
+ fclose(f);
+
+ return 0;
+}
+
+/* Output an elf, coff or binary file */
+/* XXX: suppress unneeded sections */
+static int elf_output_file(TCCState *s1, const char *filename)
+{
+ int i, ret, phnum, shnum, file_type, file_offset, *sec_order;
+ struct dyn_inf dyninf;
+ ElfW(Phdr) *phdr;
+ ElfW(Sym) *sym;
+ Section *strsec, *interp, *dynamic, *dynstr;
+
+ file_type = s1->output_type;
+ s1->nb_errors = 0;
+
+ /* if linking, also link in runtime libraries (libc, libgcc, etc.) */
+ if (file_type != TCC_OUTPUT_OBJ) {
+ tcc_add_runtime(s1);
+ }
+
+ phdr = NULL;
+ sec_order = NULL;
+ interp = dynamic = dynstr = NULL; /* avoid warning */
+ dyninf.dyn_rel_off = 0; /* avoid warning */
+
+ if (file_type != TCC_OUTPUT_OBJ) {
+ relocate_common_syms();
+
+ tcc_add_linker_symbols(s1);
+
+ if (!s1->static_link) {
+ if (file_type == TCC_OUTPUT_EXE) {
+ char *ptr;
+ /* allow override the dynamic loader */
+ const char *elfint = getenv("LD_SO");
+ if (elfint == NULL)
+ elfint = DEFAULT_ELFINTERP(s1);
+ /* add interpreter section only if executable */
+ interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC);
+ interp->sh_addralign = 1;
+ ptr = section_ptr_add(interp, 1 + strlen(elfint));
+ strcpy(ptr, elfint);
+ }
+
+ /* add dynamic symbol table */
+ s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC,
+ ".dynstr",
+ ".hash", SHF_ALLOC);
+ dynstr = s1->dynsym->link;
+
+ /* add dynamic section */
+ dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC,
+ SHF_ALLOC | SHF_WRITE);
+ dynamic->link = dynstr;
+ dynamic->sh_entsize = sizeof(ElfW(Dyn));
+
+ build_got(s1);
+
+ if (file_type == TCC_OUTPUT_EXE) {
+ bind_exe_dynsyms(s1);
+
+ if (s1->nb_errors) {
+ ret = -1;
+ goto the_end;
+ }
+
+ bind_libs_dynsyms(s1);
+ } else /* shared library case: simply export all global symbols */
+ export_global_syms(s1);
+
+ build_got_entries(s1);
+
+ /* add a list of needed dlls */
+ for(i = 0; i < s1->nb_loaded_dlls; i++) {
+ DLLReference *dllref = s1->loaded_dlls[i];
+ if (dllref->level == 0)
+ put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name));
+ }
+
+ if (s1->rpath)
+ put_dt(dynamic, DT_RPATH, put_elf_str(dynstr, s1->rpath));
+
+ /* XXX: currently, since we do not handle PIC code, we
+ must relocate the readonly segments */
+ if (file_type == TCC_OUTPUT_DLL) {
+ if (s1->soname)
+ put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname));
+ put_dt(dynamic, DT_TEXTREL, 0);
+ }
+
+ if (s1->symbolic)
+ put_dt(dynamic, DT_SYMBOLIC, 0);
+
+ /* add necessary space for other entries */
+ dyninf.dyn_rel_off = dynamic->data_offset;
+ dynamic->data_offset += sizeof(ElfW(Dyn)) * EXTRA_RELITEMS;
+ } else {
+ /* still need to build got entries in case of static link */
+ build_got_entries(s1);
+ }
+ }
+
+ /* we add a section for symbols */
+ strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0);
+ put_elf_str(strsec, "");
+
+ /* compute number of sections */
+ shnum = s1->nb_sections;
+
+ /* this array is used to reorder sections in the output file */
+ sec_order = tcc_malloc(sizeof(int) * shnum);
+ sec_order[0] = 0;
+
+ /* compute number of program headers */
+ switch(file_type) {
+ default:
+ case TCC_OUTPUT_OBJ:
+ phnum = 0;
+ break;
+ case TCC_OUTPUT_EXE:
+ if (!s1->static_link)
+ phnum = 4 + HAVE_PHDR;
+ else
+ phnum = 2;
+ break;
+ case TCC_OUTPUT_DLL:
+ phnum = 3;
+ break;
+ }
+
+ /* Allocate strings for section names */
+ alloc_sec_names(s1, file_type, strsec);
+
+ /* allocate program segment headers */
+ phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));
+
+ /* compute section to program header mapping */
+ file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf,
+ sec_order);
+
+ /* Fill remaining program header and finalize relocation related to dynamic
+ linking. */
+ if (phnum > 0) {
+ fill_unloadable_phdr(phdr, phnum, interp, dynamic);
+ if (dynamic) {
+ dyninf.dynamic = dynamic;
+ dyninf.dynstr = dynstr;
+
+ fill_dynamic(s1, &dyninf);
+
+ /* put in GOT the dynamic section address and relocate PLT */
+ put32(s1->got->data, dynamic->sh_addr);
+ if (file_type == TCC_OUTPUT_EXE
+#if defined(TCC_OUTPUT_DLL_WITH_PLT)
+ || file_type == TCC_OUTPUT_DLL
+#endif
+ )
+ relocate_plt(s1);
+
+ /* relocate symbols in .dynsym now that final addresses are known */
+ for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) {
+ if (sym->st_shndx == SHN_UNDEF) {
+ /* relocate to PLT if symbol corresponds to a PLT entry,
+ but not if it's a weak symbol */
+ if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
+ sym->st_value = 0;
+ else if (sym->st_value)
+ sym->st_value += s1->plt->sh_addr;
+ } else if (sym->st_shndx < SHN_LORESERVE) {
+ /* do symbol relocation */
+ sym->st_value += s1->sections[sym->st_shndx]->sh_addr;
+ }
+ }
+ }
+ }
+
+ /* if building executable or DLL, then relocate each section
+ except the GOT which is already relocated */
+ if (file_type != TCC_OUTPUT_OBJ) {
+ ret = final_sections_reloc(s1);
+ if (ret)
+ goto the_end;
+ }
+
+ /* Perform relocation to GOT or PLT entries */
+ if (file_type == TCC_OUTPUT_EXE && s1->static_link)
+ fill_got(s1);
+
+ /* Create the ELF file with name 'filename' */
+ ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order);
+ if (s1->do_strip) {
+ int rc;
+ const char *strip_cmd = "sstrip "; // super strip utility from ELFkickers
+ const char *null_dev = " 2> /dev/null";
+ char buf[1050];
+ snprintf(buf, sizeof(buf), "%s%s%s", strip_cmd, filename, null_dev);
+ rc = system(buf);
+ if (rc)
+ system(buf+1); // call a strip utility from binutils
+ }
+ the_end:
+ tcc_free(s1->symtab_to_dynsym);
+ tcc_free(sec_order);
+ tcc_free(phdr);
+ tcc_free(s1->sym_attrs);
+ s1->sym_attrs = NULL;
+ return ret;
+}
+
+LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename)
+{
+ int ret;
+#ifdef TCC_TARGET_PE
+ if (s->output_type != TCC_OUTPUT_OBJ) {
+ ret = pe_output_file(s, filename);
+ } else
+#endif
+ ret = elf_output_file(s, filename);
+ return ret;
+}
+
+static void *load_data(int fd, unsigned long file_offset, unsigned long size)
+{
+ void *data;
+
+ data = tcc_malloc(size);
+ lseek(fd, file_offset, SEEK_SET);
+ read(fd, data, size);
+ return data;
+}
+
+typedef struct SectionMergeInfo {
+ Section *s; /* corresponding existing section */
+ unsigned long offset; /* offset of the new section in the existing section */
+ uint8_t new_section; /* true if section 's' was added */
+ uint8_t link_once; /* true if link once section */
+} SectionMergeInfo;
+
+/* load an object file and merge it with current files */
+/* XXX: handle correctly stab (debug) info */
+ST_FUNC int tcc_load_object_file(TCCState *s1,
+ int fd, unsigned long file_offset)
+{
+ ElfW(Ehdr) ehdr;
+ ElfW(Shdr) *shdr, *sh;
+ int size, i, j, offset, offseti, nb_syms, sym_index, ret;
+ unsigned char *strsec, *strtab;
+ int *old_to_new_syms;
+ char *sh_name, *name;
+ SectionMergeInfo *sm_table, *sm;
+ ElfW(Sym) *sym, *symtab;
+ ElfW_Rel *rel;
+ Section *s;
+
+ int stab_index;
+ int stabstr_index;
+
+ stab_index = stabstr_index = 0;
+
+ if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
+ goto fail1;
+ if (ehdr.e_ident[0] != ELFMAG0 ||
+ ehdr.e_ident[1] != ELFMAG1 ||
+ ehdr.e_ident[2] != ELFMAG2 ||
+ ehdr.e_ident[3] != ELFMAG3)
+ goto fail1;
+ /* test if object file */
+ if (ehdr.e_type != ET_REL)
+ goto fail1;
+ /* test CPU specific stuff */
+ if (ehdr.e_ident[5] != ELFDATA2LSB ||
+ ehdr.e_machine != EM_TCC_TARGET) {
+ fail1:
+ tcc_error_noabort("invalid object file");
+ return -1;
+ }
+ /* read sections */
+ shdr = load_data(fd, file_offset + ehdr.e_shoff,
+ sizeof(ElfW(Shdr)) * ehdr.e_shnum);
+ sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum);
+
+ /* load section names */
+ sh = &shdr[ehdr.e_shstrndx];
+ strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
+
+ /* load symtab and strtab */
+ old_to_new_syms = NULL;
+ symtab = NULL;
+ strtab = NULL;
+ nb_syms = 0;
+ for(i = 1; i < ehdr.e_shnum; i++) {
+ sh = &shdr[i];
+ if (sh->sh_type == SHT_SYMTAB) {
+ if (symtab) {
+ tcc_error_noabort("object must contain only one symtab");
+ fail:
+ ret = -1;
+ goto the_end;
+ }
+ nb_syms = sh->sh_size / sizeof(ElfW(Sym));
+ symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
+ sm_table[i].s = symtab_section;
+
+ /* now load strtab */
+ sh = &shdr[sh->sh_link];
+ strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size);
+ }
+ }
+
+ /* now examine each section and try to merge its content with the
+ ones in memory */
+ for(i = 1; i < ehdr.e_shnum; i++) {
+ /* no need to examine section name strtab */
+ if (i == ehdr.e_shstrndx)
+ continue;
+ sh = &shdr[i];
+ sh_name = (char *) strsec + sh->sh_name;
+ /* ignore sections types we do not handle */
+ if (sh->sh_type != SHT_PROGBITS &&
+ sh->sh_type != SHT_RELX &&
+#ifdef TCC_ARM_EABI
+ sh->sh_type != SHT_ARM_EXIDX &&
+#endif
+ sh->sh_type != SHT_NOBITS &&
+ sh->sh_type != SHT_PREINIT_ARRAY &&
+ sh->sh_type != SHT_INIT_ARRAY &&
+ sh->sh_type != SHT_FINI_ARRAY &&
+ strcmp(sh_name, ".stabstr")
+ )
+ continue;
+ if (sh->sh_addralign < 1)
+ sh->sh_addralign = 1;
+ /* find corresponding section, if any */
+ for(j = 1; j < s1->nb_sections;j++) {
+ s = s1->sections[j];
+ if (!strcmp(s->name, sh_name)) {
+ if (!strncmp(sh_name, ".gnu.linkonce",
+ sizeof(".gnu.linkonce") - 1)) {
+ /* if a 'linkonce' section is already present, we
+ do not add it again. It is a little tricky as
+ symbols can still be defined in
+ it. */
+ sm_table[i].link_once = 1;
+ goto next;
+ } else {
+ goto found;
+ }
+ }
+ }
+ /* not found: create new section */
+ s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags);
+ /* take as much info as possible from the section. sh_link and
+ sh_info will be updated later */
+ s->sh_addralign = sh->sh_addralign;
+ s->sh_entsize = sh->sh_entsize;
+ sm_table[i].new_section = 1;
+ found:
+ if (sh->sh_type != s->sh_type) {
+ tcc_error_noabort("invalid section type");
+ goto fail;
+ }
+
+ /* align start of section */
+ offset = s->data_offset;
+
+ if (0 == strcmp(sh_name, ".stab")) {
+ stab_index = i;
+ goto no_align;
+ }
+ if (0 == strcmp(sh_name, ".stabstr")) {
+ stabstr_index = i;
+ goto no_align;
+ }
+
+ size = sh->sh_addralign - 1;
+ offset = (offset + size) & ~size;
+ if (sh->sh_addralign > s->sh_addralign)
+ s->sh_addralign = sh->sh_addralign;
+ s->data_offset = offset;
+ no_align:
+ sm_table[i].offset = offset;
+ sm_table[i].s = s;
+ /* concatenate sections */
+ size = sh->sh_size;
+ if (sh->sh_type != SHT_NOBITS) {
+ unsigned char *ptr;
+ lseek(fd, file_offset + sh->sh_offset, SEEK_SET);
+ ptr = section_ptr_add(s, size);
+ read(fd, ptr, size);
+ } else {
+ s->data_offset += size;
+ }
+ next: ;
+ }
+
+ /* gr relocate stab strings */
+ if (stab_index && stabstr_index) {
+ Stab_Sym *a, *b;
+ unsigned o;
+ s = sm_table[stab_index].s;
+ a = (Stab_Sym *)(s->data + sm_table[stab_index].offset);
+ b = (Stab_Sym *)(s->data + s->data_offset);
+ o = sm_table[stabstr_index].offset;
+ while (a < b)
+ a->n_strx += o, a++;
+ }
+
+ /* second short pass to update sh_link and sh_info fields of new
+ sections */
+ for(i = 1; i < ehdr.e_shnum; i++) {
+ s = sm_table[i].s;
+ if (!s || !sm_table[i].new_section)
+ continue;
+ sh = &shdr[i];
+ if (sh->sh_link > 0)
+ s->link = sm_table[sh->sh_link].s;
+ if (sh->sh_type == SHT_RELX) {
+ s->sh_info = sm_table[sh->sh_info].s->sh_num;
+ /* update backward link */
+ s1->sections[s->sh_info]->reloc = s;
+ }
+ }
+ sm = sm_table;
+
+ /* resolve symbols */
+ old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int));
+
+ sym = symtab + 1;
+ for(i = 1; i < nb_syms; i++, sym++) {
+ if (sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx < SHN_LORESERVE) {
+ sm = &sm_table[sym->st_shndx];
+ if (sm->link_once) {
+ /* if a symbol is in a link once section, we use the
+ already defined symbol. It is very important to get
+ correct relocations */
+ if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
+ name = (char *) strtab + sym->st_name;
+ sym_index = find_elf_sym(symtab_section, name);
+ if (sym_index)
+ old_to_new_syms[i] = sym_index;
+ }
+ continue;
+ }
+ /* if no corresponding section added, no need to add symbol */
+ if (!sm->s)
+ continue;
+ /* convert section number */
+ sym->st_shndx = sm->s->sh_num;
+ /* offset value */
+ sym->st_value += sm->offset;
+ }
+ /* add symbol */
+ name = (char *) strtab + sym->st_name;
+ sym_index = add_elf_sym(symtab_section, sym->st_value, sym->st_size,
+ sym->st_info, sym->st_other,
+ sym->st_shndx, name);
+ old_to_new_syms[i] = sym_index;
+ }
+
+ /* third pass to patch relocation entries */
+ for(i = 1; i < ehdr.e_shnum; i++) {
+ s = sm_table[i].s;
+ if (!s)
+ continue;
+ sh = &shdr[i];
+ offset = sm_table[i].offset;
+ switch(s->sh_type) {
+ case SHT_RELX:
+ /* take relocation offset information */
+ offseti = sm_table[sh->sh_info].offset;
+ for_each_elem(s, (offset / sizeof(*rel)), rel, ElfW_Rel) {
+ int type;
+ unsigned sym_index;
+ /* convert symbol index */
+ type = ELFW(R_TYPE)(rel->r_info);
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ /* NOTE: only one symtab assumed */
+ if (sym_index >= nb_syms)
+ goto invalid_reloc;
+ sym_index = old_to_new_syms[sym_index];
+ /* ignore link_once in rel section. */
+ if (!sym_index && !sm->link_once
+#ifdef TCC_TARGET_ARM
+ && type != R_ARM_V4BX
+#endif
+ ) {
+ invalid_reloc:
+ tcc_error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x",
+ i, strsec + sh->sh_name, rel->r_offset);
+ goto fail;
+ }
+ rel->r_info = ELFW(R_INFO)(sym_index, type);
+ /* offset the relocation offset */
+ rel->r_offset += offseti;
+#ifdef TCC_TARGET_ARM
+ /* Jumps and branches from a Thumb code to a PLT entry need
+ special handling since PLT entries are ARM code.
+ Unconditional bl instructions referencing PLT entries are
+ handled by converting these instructions into blx
+ instructions. Other case of instructions referencing a PLT
+ entry require to add a Thumb stub before the PLT entry to
+ switch to ARM mode. We set bit plt_thumb_stub of the
+ attribute of a symbol to indicate such a case. */
+ if (type == R_ARM_THM_JUMP24)
+ alloc_sym_attr(s1, sym_index)->plt_thumb_stub = 1;
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ ret = 0;
+ the_end:
+ tcc_free(symtab);
+ tcc_free(strtab);
+ tcc_free(old_to_new_syms);
+ tcc_free(sm_table);
+ tcc_free(strsec);
+ tcc_free(shdr);
+ return ret;
+}
+
+typedef struct ArchiveHeader {
+ char ar_name[16]; /* name of this member */
+ char ar_date[12]; /* file mtime */
+ char ar_uid[6]; /* owner uid; printed as decimal */
+ char ar_gid[6]; /* owner gid; printed as decimal */
+ char ar_mode[8]; /* file mode, printed as octal */
+ char ar_size[10]; /* file size, printed as decimal */
+ char ar_fmag[2]; /* should contain ARFMAG */
+} ArchiveHeader;
+
+static int get_be32(const uint8_t *b)
+{
+ return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24);
+}
+
+/* load only the objects which resolve undefined symbols */
+static int tcc_load_alacarte(TCCState *s1, int fd, int size)
+{
+ int i, bound, nsyms, sym_index, off, ret;
+ uint8_t *data;
+ const char *ar_names, *p;
+ const uint8_t *ar_index;
+ ElfW(Sym) *sym;
+
+ data = tcc_malloc(size);
+ if (read(fd, data, size) != size)
+ goto fail;
+ nsyms = get_be32(data);
+ ar_index = data + 4;
+ ar_names = (char *) ar_index + nsyms * 4;
+
+ do {
+ bound = 0;
+ for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) {
+ sym_index = find_elf_sym(symtab_section, p);
+ if(sym_index) {
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+ if(sym->st_shndx == SHN_UNDEF) {
+ off = get_be32(ar_index + i * 4) + sizeof(ArchiveHeader);
+ ++bound;
+ lseek(fd, off, SEEK_SET);
+ if(tcc_load_object_file(s1, fd, off) < 0) {
+ fail:
+ ret = -1;
+ goto the_end;
+ }
+ }
+ }
+ }
+ } while(bound);
+ ret = 0;
+ the_end:
+ tcc_free(data);
+ return ret;
+}
+
+/* load a '.a' file */
+ST_FUNC int tcc_load_archive(TCCState *s1, int fd)
+{
+ ArchiveHeader hdr;
+ char ar_size[11];
+ char ar_name[17];
+ char magic[8];
+ int size, len, i;
+ unsigned long file_offset;
+
+ /* skip magic which was already checked */
+ read(fd, magic, sizeof(magic));
+
+ for(;;) {
+ len = read(fd, &hdr, sizeof(hdr));
+ if (len == 0)
+ break;
+ if (len != sizeof(hdr)) {
+ tcc_error_noabort("invalid archive");
+ return -1;
+ }
+ memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size));
+ ar_size[sizeof(hdr.ar_size)] = '\0';
+ size = strtol(ar_size, NULL, 0);
+ memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name));
+ for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) {
+ if (ar_name[i] != ' ')
+ break;
+ }
+ ar_name[i + 1] = '\0';
+ file_offset = lseek(fd, 0, SEEK_CUR);
+ /* align to even */
+ size = (size + 1) & ~1;
+ if (!strcmp(ar_name, "/")) {
+ /* coff symbol table : we handle it */
+ if(s1->alacarte_link)
+ return tcc_load_alacarte(s1, fd, size);
+ } else if (!strcmp(ar_name, "//") ||
+ !strcmp(ar_name, "__.SYMDEF") ||
+ !strcmp(ar_name, "__.SYMDEF/") ||
+ !strcmp(ar_name, "ARFILENAMES/")) {
+ /* skip symbol table or archive names */
+ } else {
+ if (tcc_load_object_file(s1, fd, file_offset) < 0)
+ return -1;
+ }
+ lseek(fd, file_offset + size, SEEK_SET);
+ }
+ return 0;
+}
+
+#ifndef TCC_TARGET_PE
+/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL
+ is referenced by the user (so it should be added as DT_NEEDED in
+ the generated ELF file) */
+ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level)
+{
+ ElfW(Ehdr) ehdr;
+ ElfW(Shdr) *shdr, *sh, *sh1;
+ int i, j, nb_syms, nb_dts, sym_bind, ret;
+ ElfW(Sym) *sym, *dynsym;
+ ElfW(Dyn) *dt, *dynamic;
+ unsigned char *dynstr;
+ const char *name, *soname;
+ DLLReference *dllref;
+
+ read(fd, &ehdr, sizeof(ehdr));
+
+ /* test CPU specific stuff */
+ if (ehdr.e_ident[5] != ELFDATA2LSB ||
+ ehdr.e_machine != EM_TCC_TARGET) {
+ tcc_error_noabort("bad architecture");
+ return -1;
+ }
+
+ /* read sections */
+ shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(Shdr)) * ehdr.e_shnum);
+
+ /* load dynamic section and dynamic symbols */
+ nb_syms = 0;
+ nb_dts = 0;
+ dynamic = NULL;
+ dynsym = NULL; /* avoid warning */
+ dynstr = NULL; /* avoid warning */
+ for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) {
+ switch(sh->sh_type) {
+ case SHT_DYNAMIC:
+ nb_dts = sh->sh_size / sizeof(ElfW(Dyn));
+ dynamic = load_data(fd, sh->sh_offset, sh->sh_size);
+ break;
+ case SHT_DYNSYM:
+ nb_syms = sh->sh_size / sizeof(ElfW(Sym));
+ dynsym = load_data(fd, sh->sh_offset, sh->sh_size);
+ sh1 = &shdr[sh->sh_link];
+ dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* compute the real library name */
+ soname = tcc_basename(filename);
+
+ for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
+ if (dt->d_tag == DT_SONAME) {
+ soname = (char *) dynstr + dt->d_un.d_val;
+ }
+ }
+
+ /* if the dll is already loaded, do not load it */
+ for(i = 0; i < s1->nb_loaded_dlls; i++) {
+ dllref = s1->loaded_dlls[i];
+ if (!strcmp(soname, dllref->name)) {
+ /* but update level if needed */
+ if (level < dllref->level)
+ dllref->level = level;
+ ret = 0;
+ goto the_end;
+ }
+ }
+
+ /* add the dll and its level */
+ dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname));
+ dllref->level = level;
+ strcpy(dllref->name, soname);
+ dynarray_add((void ***)&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
+
+ /* add dynamic symbols in dynsym_section */
+ for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) {
+ sym_bind = ELFW(ST_BIND)(sym->st_info);
+ if (sym_bind == STB_LOCAL)
+ continue;
+ name = (char *) dynstr + sym->st_name;
+ add_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size,
+ sym->st_info, sym->st_other, sym->st_shndx, name);
+ }
+
+ /* load all referenced DLLs */
+ for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) {
+ switch(dt->d_tag) {
+ case DT_NEEDED:
+ name = (char *) dynstr + dt->d_un.d_val;
+ for(j = 0; j < s1->nb_loaded_dlls; j++) {
+ dllref = s1->loaded_dlls[j];
+ if (!strcmp(name, dllref->name))
+ goto already_loaded;
+ }
+ if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) {
+ tcc_error_noabort("referenced dll '%s' not found", name);
+ ret = -1;
+ goto the_end;
+ }
+ already_loaded:
+ break;
+ }
+ }
+ ret = 0;
+ the_end:
+ tcc_free(dynstr);
+ tcc_free(dynsym);
+ tcc_free(dynamic);
+ tcc_free(shdr);
+ return ret;
+}
+
+#define LD_TOK_NAME 256
+#define LD_TOK_EOF (-1)
+
+/* return next ld script token */
+static int ld_next(TCCState *s1, char *name, int name_size)
+{
+ int c;
+ char *q;
+
+ redo:
+ switch(ch) {
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ case '\r':
+ case '\n':
+ inp();
+ goto redo;
+ case '/':
+ minp();
+ if (ch == '*') {
+ file->buf_ptr = parse_comment(file->buf_ptr);
+ ch = file->buf_ptr[0];
+ goto redo;
+ } else {
+ q = name;
+ *q++ = '/';
+ goto parse_name;
+ }
+ break;
+ case '\\':
+ ch = handle_eob();
+ if (ch != '\\')
+ goto redo;
+ /* fall through */
+ /* case 'a' ... 'z': */
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ case 'g':
+ case 'h':
+ case 'i':
+ case 'j':
+ case 'k':
+ case 'l':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'r':
+ case 's':
+ case 't':
+ case 'u':
+ case 'v':
+ case 'w':
+ case 'x':
+ case 'y':
+ case 'z':
+ /* case 'A' ... 'z': */
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ case 'G':
+ case 'H':
+ case 'I':
+ case 'J':
+ case 'K':
+ case 'L':
+ case 'M':
+ case 'N':
+ case 'O':
+ case 'P':
+ case 'Q':
+ case 'R':
+ case 'S':
+ case 'T':
+ case 'U':
+ case 'V':
+ case 'W':
+ case 'X':
+ case 'Y':
+ case 'Z':
+ case '_':
+ case '.':
+ case '$':
+ case '~':
+ q = name;
+ parse_name:
+ for(;;) {
+ if (!((ch >= 'a' && ch <= 'z') ||
+ (ch >= 'A' && ch <= 'Z') ||
+ (ch >= '0' && ch <= '9') ||
+ strchr("/.-_+=$:\\,~", ch)))
+ break;
+ if ((q - name) < name_size - 1) {
+ *q++ = ch;
+ }
+ minp();
+ }
+ *q = '\0';
+ c = LD_TOK_NAME;
+ break;
+ case CH_EOF:
+ c = LD_TOK_EOF;
+ break;
+ default:
+ c = ch;
+ inp();
+ break;
+ }
+ return c;
+}
+
+static int ld_add_file(TCCState *s1, const char filename[])
+{
+ int ret;
+
+ ret = tcc_add_file_internal(s1, filename, 0, TCC_FILETYPE_BINARY);
+ if (ret)
+ ret = tcc_add_dll(s1, filename, 0);
+ return ret;
+}
+
+static inline int new_undef_syms(void)
+{
+ int ret = 0;
+ ret = new_undef_sym;
+ new_undef_sym = 0;
+ return ret;
+}
+
+static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed)
+{
+ char filename[1024], libname[1024];
+ int t, group, nblibs = 0, ret = 0;
+ char **libs = NULL;
+
+ group = !strcmp(cmd, "GROUP");
+ if (!as_needed)
+ new_undef_syms();
+ t = ld_next(s1, filename, sizeof(filename));
+ if (t != '(')
+ expect("(");
+ t = ld_next(s1, filename, sizeof(filename));
+ for(;;) {
+ libname[0] = '\0';
+ if (t == LD_TOK_EOF) {
+ tcc_error_noabort("unexpected end of file");
+ ret = -1;
+ goto lib_parse_error;
+ } else if (t == ')') {
+ break;
+ } else if (t == '-') {
+ t = ld_next(s1, filename, sizeof(filename));
+ if ((t != LD_TOK_NAME) || (filename[0] != 'l')) {
+ tcc_error_noabort("library name expected");
+ ret = -1;
+ goto lib_parse_error;
+ }
+ pstrcpy(libname, sizeof libname, &filename[1]);
+ if (s1->static_link) {
+ snprintf(filename, sizeof filename, "lib%s.a", libname);
+ } else {
+ snprintf(filename, sizeof filename, "lib%s.so", libname);
+ }
+ } else if (t != LD_TOK_NAME) {
+ tcc_error_noabort("filename expected");
+ ret = -1;
+ goto lib_parse_error;
+ }
+ if (!strcmp(filename, "AS_NEEDED")) {
+ ret = ld_add_file_list(s1, cmd, 1);
+ if (ret)
+ goto lib_parse_error;
+ } else {
+ /* TODO: Implement AS_NEEDED support. Ignore it for now */
+ if (!as_needed) {
+ ret = ld_add_file(s1, filename);
+ if (ret)
+ goto lib_parse_error;
+ if (group) {
+ /* Add the filename *and* the libname to avoid future conversions */
+ dynarray_add((void ***) &libs, &nblibs, tcc_strdup(filename));
+ if (libname[0] != '\0')
+ dynarray_add((void ***) &libs, &nblibs, tcc_strdup(libname));
+ }
+ }
+ }
+ t = ld_next(s1, filename, sizeof(filename));
+ if (t == ',') {
+ t = ld_next(s1, filename, sizeof(filename));
+ }
+ }
+ if (group && !as_needed) {
+ while (new_undef_syms()) {
+ int i;
+
+ for (i = 0; i < nblibs; i ++)
+ ld_add_file(s1, libs[i]);
+ }
+ }
+lib_parse_error:
+ dynarray_reset(&libs, &nblibs);
+ return ret;
+}
+
+/* interpret a subset of GNU ldscripts to handle the dummy libc.so
+ files */
+ST_FUNC int tcc_load_ldscript(TCCState *s1)
+{
+ char cmd[64];
+ char filename[1024];
+ int t, ret;
+
+ ch = handle_eob();
+ for(;;) {
+ t = ld_next(s1, cmd, sizeof(cmd));
+ if (t == LD_TOK_EOF)
+ return 0;
+ else if (t != LD_TOK_NAME)
+ return -1;
+ if (!strcmp(cmd, "INPUT") ||
+ !strcmp(cmd, "GROUP")) {
+ ret = ld_add_file_list(s1, cmd, 0);
+ if (ret)
+ return ret;
+ } else if (!strcmp(cmd, "OUTPUT_FORMAT") ||
+ !strcmp(cmd, "TARGET")) {
+ /* ignore some commands */
+ t = ld_next(s1, cmd, sizeof(cmd));
+ if (t != '(')
+ expect("(");
+ for(;;) {
+ t = ld_next(s1, filename, sizeof(filename));
+ if (t == LD_TOK_EOF) {
+ tcc_error_noabort("unexpected end of file");
+ return -1;
+ } else if (t == ')') {
+ break;
+ }
+ }
+ } else {
+ return -1;
+ }
+ }
+ return 0;
+}
+#endif /* !TCC_TARGET_PE */
diff --git a/src/tccgen.c b/src/tccgen.c
new file mode 100644
index 0000000..cf97d1f
--- /dev/null
+++ b/src/tccgen.c
@@ -0,0 +1,6463 @@
+/*
+ * TCC - Tiny C Compiler
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+/********************************************************/
+/* global variables */
+
+/* loc : local variable index
+ ind : output code index
+ rsym: return symbol
+ anon_sym: anonymous symbol index
+*/
+ST_DATA int rsym, anon_sym, ind, loc;
+
+ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */
+ST_DATA Section *cur_text_section; /* current section where function code is generated */
+#ifdef CONFIG_TCC_ASM
+ST_DATA Section *last_text_section; /* to handle .previous asm directive */
+#endif
+#ifdef CONFIG_TCC_BCHECK
+/* bound check related sections */
+ST_DATA Section *bounds_section; /* contains global data bound description */
+ST_DATA Section *lbounds_section; /* contains local data bound description */
+#endif
+/* symbol sections */
+ST_DATA Section *symtab_section, *strtab_section;
+/* debug sections */
+ST_DATA Section *stab_section, *stabstr_section;
+ST_DATA Sym *sym_free_first;
+ST_DATA void **sym_pools;
+ST_DATA int nb_sym_pools;
+
+ST_DATA Sym *global_stack;
+ST_DATA Sym *local_stack;
+ST_DATA Sym *scope_stack_bottom;
+ST_DATA Sym *define_stack;
+ST_DATA Sym *global_label_stack;
+ST_DATA Sym *local_label_stack;
+
+ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */
+ST_DATA int vla_sp_root_loc; /* vla_sp_loc for SP before any VLAs were pushed */
+ST_DATA int vla_sp_loc; /* Pointer to variable holding location to store stack pointer on the stack when modifying stack pointer */
+
+ST_DATA SValue __vstack[1+VSTACK_SIZE], *vtop, *pvtop;
+
+ST_DATA int const_wanted; /* true if constant wanted */
+ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */
+ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
+ST_DATA CType func_vt; /* current function return type (used by return instruction) */
+ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
+ST_DATA int func_vc;
+ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */
+ST_DATA const char *funcname;
+
+ST_DATA CType char_pointer_type, func_old_type, int_type, size_type;
+
+/* ------------------------------------------------------------------------- */
+static void gen_cast(CType *type);
+static inline CType *pointed_type(CType *type);
+static int is_compatible_types(CType *type1, CType *type2);
+static int parse_btype(CType *type, AttributeDef *ad);
+static void type_decl(CType *type, AttributeDef *ad, int *v, int td);
+static void parse_expr_type(CType *type);
+static void decl_initializer(CType *type, Section *sec, unsigned long c, int first, int size_only);
+static void block(int *bsym, int *csym, int *case_sym, int *def_sym, int case_reg, int is_expr);
+static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, int has_init, int v, char *asm_label, int scope);
+static int decl0(int l, int is_for_loop_init);
+static void expr_eq(void);
+static void unary_type(CType *type);
+static void vla_runtime_type_size(CType *type, int *a);
+static void vla_sp_restore(void);
+static void vla_sp_restore_root(void);
+static int is_compatible_parameter_types(CType *type1, CType *type2);
+static void expr_type(CType *type);
+ST_FUNC void vpush64(int ty, unsigned long long v);
+ST_FUNC void vpush(CType *type);
+ST_FUNC int gvtst(int inv, int t);
+ST_FUNC int is_btype_size(int bt);
+
+ST_INLN int is_float(int t)
+{
+ int bt;
+ bt = t & VT_BTYPE;
+ return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT || bt == VT_QFLOAT;
+}
+
+/* we use our own 'finite' function to avoid potential problems with
+ non standard math libs */
+/* XXX: endianness dependent */
+ST_FUNC int ieee_finite(double d)
+{
+ int p[4];
+ memcpy(p, &d, sizeof(double));
+ return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31;
+}
+
+ST_FUNC void test_lvalue(void)
+{
+ if (!(vtop->r & VT_LVAL))
+ expect("lvalue");
+}
+
+ST_FUNC void check_vstack(void)
+{
+ if (pvtop != vtop)
+ tcc_error("internal compiler error: vstack leak (%d)", vtop - pvtop);
+}
+
+/* ------------------------------------------------------------------------- */
+/* symbol allocator */
+static Sym *__sym_malloc(void)
+{
+ Sym *sym_pool, *sym, *last_sym;
+ int i;
+
+ sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym));
+ dynarray_add(&sym_pools, &nb_sym_pools, sym_pool);
+
+ last_sym = sym_free_first;
+ sym = sym_pool;
+ for(i = 0; i < SYM_POOL_NB; i++) {
+ sym->next = last_sym;
+ last_sym = sym;
+ sym++;
+ }
+ sym_free_first = last_sym;
+ return last_sym;
+}
+
+static inline Sym *sym_malloc(void)
+{
+ Sym *sym;
+ sym = sym_free_first;
+ if (!sym)
+ sym = __sym_malloc();
+ sym_free_first = sym->next;
+ return sym;
+}
+
+ST_INLN void sym_free(Sym *sym)
+{
+ sym->next = sym_free_first;
+ tcc_free(sym->asm_label);
+ sym_free_first = sym;
+}
+
+/* push, without hashing */
+ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c)
+{
+ Sym *s;
+ if (ps == &local_stack) {
+ for (s = *ps; s && s != scope_stack_bottom; s = s->prev)
+ if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM && s->v == v)
+ tcc_error("incompatible types for redefinition of '%s'",
+ get_tok_str(v, NULL));
+ }
+ s = sym_malloc();
+ s->asm_label = NULL;
+ s->v = v;
+ s->type.t = t;
+ s->type.ref = NULL;
+#ifdef _WIN64
+ s->d = NULL;
+#endif
+ s->c = c;
+ s->next = NULL;
+ /* add in stack */
+ s->prev = *ps;
+ *ps = s;
+ return s;
+}
+
+/* find a symbol and return its associated structure. 's' is the top
+ of the symbol stack */
+ST_FUNC Sym *sym_find2(Sym *s, int v)
+{
+ while (s) {
+ if (s->v == v)
+ return s;
+ else if (s->v == -1)
+ return NULL;
+ s = s->prev;
+ }
+ return NULL;
+}
+
+/* structure lookup */
+ST_INLN Sym *struct_find(int v)
+{
+ v -= TOK_IDENT;
+ if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+ return NULL;
+ return table_ident[v]->sym_struct;
+}
+
+/* find an identifier */
+ST_INLN Sym *sym_find(int v)
+{
+ v -= TOK_IDENT;
+ if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+ return NULL;
+ return table_ident[v]->sym_identifier;
+}
+
+/* push a given symbol on the symbol stack */
+ST_FUNC Sym *sym_push(int v, CType *type, int r, int c)
+{
+ Sym *s, **ps;
+ TokenSym *ts;
+
+ if (local_stack)
+ ps = &local_stack;
+ else
+ ps = &global_stack;
+ s = sym_push2(ps, v, type->t, c);
+ s->type.ref = type->ref;
+ s->r = r;
+ /* don't record fields or anonymous symbols */
+ /* XXX: simplify */
+ if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
+ /* record symbol in token array */
+ ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
+ if (v & SYM_STRUCT)
+ ps = &ts->sym_struct;
+ else
+ ps = &ts->sym_identifier;
+ s->prev_tok = *ps;
+ *ps = s;
+ }
+ return s;
+}
+
+/* push a global identifier */
+ST_FUNC Sym *global_identifier_push(int v, int t, int c)
+{
+ Sym *s, **ps;
+ s = sym_push2(&global_stack, v, t, c);
+ /* don't record anonymous symbol */
+ if (v < SYM_FIRST_ANOM) {
+ ps = &table_ident[v - TOK_IDENT]->sym_identifier;
+ /* modify the top most local identifier, so that
+ sym_identifier will point to 's' when popped */
+ while (*ps != NULL)
+ ps = &(*ps)->prev_tok;
+ s->prev_tok = NULL;
+ *ps = s;
+ }
+ return s;
+}
+
+/* pop symbols until top reaches 'b' */
+ST_FUNC void sym_pop(Sym **ptop, Sym *b)
+{
+ Sym *s, *ss, **ps;
+ TokenSym *ts;
+ int v;
+
+ s = *ptop;
+ while(s != b) {
+ ss = s->prev;
+ v = s->v;
+ /* remove symbol in token array */
+ /* XXX: simplify */
+ if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
+ ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT];
+ if (v & SYM_STRUCT)
+ ps = &ts->sym_struct;
+ else
+ ps = &ts->sym_identifier;
+ *ps = s->prev_tok;
+ }
+ sym_free(s);
+ s = ss;
+ }
+ *ptop = b;
+}
+
+static void weaken_symbol(Sym *sym)
+{
+ sym->type.t |= VT_WEAK;
+ if (sym->c > 0) {
+ int esym_type;
+ ElfW(Sym) *esym;
+
+ esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+ esym_type = ELFW(ST_TYPE)(esym->st_info);
+ esym->st_info = ELFW(ST_INFO)(STB_WEAK, esym_type);
+ }
+}
+
+static void apply_visibility(Sym *sym, CType *type)
+{
+ int vis = sym->type.t & VT_VIS_MASK;
+ int vis2 = type->t & VT_VIS_MASK;
+ if (vis == (STV_DEFAULT << VT_VIS_SHIFT))
+ vis = vis2;
+ else if (vis2 == (STV_DEFAULT << VT_VIS_SHIFT))
+ ;
+ else
+ vis = (vis < vis2) ? vis : vis2;
+ sym->type.t &= ~VT_VIS_MASK;
+ sym->type.t |= vis;
+
+ if (sym->c > 0) {
+ ElfW(Sym) *esym;
+
+ esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+ vis >>= VT_VIS_SHIFT;
+ esym->st_other = (esym->st_other & ~ELFW(ST_VISIBILITY)(-1)) | vis;
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+ST_FUNC void swap(int *p, int *q)
+{
+ int t;
+ t = *p;
+ *p = *q;
+ *q = t;
+}
+
+static void vsetc(CType *type, int r, CValue *vc)
+{
+ int v;
+
+ if (vtop >= vstack + (VSTACK_SIZE - 1))
+ tcc_error("memory full (vstack)");
+ /* cannot let cpu flags if other instruction are generated. Also
+ avoid leaving VT_JMP anywhere except on the top of the stack
+ because it would complicate the code generator. */
+ if (vtop >= vstack) {
+ v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP || (v & ~1) == VT_JMP)
+ gv(RC_INT);
+ }
+ vtop++;
+ vtop->type = *type;
+ vtop->r = r;
+ vtop->r2 = VT_CONST;
+ vtop->c = *vc;
+}
+
+/* push constant of type "type" with useless value */
+ST_FUNC void vpush(CType *type)
+{
+ CValue cval;
+ vsetc(type, VT_CONST, &cval);
+}
+
+/* push integer constant */
+ST_FUNC void vpushi(int v)
+{
+ CValue cval;
+ cval.i = v;
+ vsetc(&int_type, VT_CONST, &cval);
+}
+
+/* push a pointer sized constant */
+static void vpushs(addr_t v)
+{
+ CValue cval;
+ cval.ptr_offset = v;
+ vsetc(&size_type, VT_CONST, &cval);
+}
+
+/* push arbitrary 64bit constant */
+ST_FUNC void vpush64(int ty, unsigned long long v)
+{
+ CValue cval;
+ CType ctype;
+ ctype.t = ty;
+ ctype.ref = NULL;
+ cval.ull = v;
+ vsetc(&ctype, VT_CONST, &cval);
+}
+
+/* push long long constant */
+static inline void vpushll(long long v)
+{
+ vpush64(VT_LLONG, v);
+}
+
+/* push a symbol value of TYPE */
+static inline void vpushsym(CType *type, Sym *sym)
+{
+ CValue cval;
+ cval.ptr_offset = 0;
+ vsetc(type, VT_CONST | VT_SYM, &cval);
+ vtop->sym = sym;
+}
+
+/* Return a static symbol pointing to a section */
+ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
+{
+ int v;
+ Sym *sym;
+
+ v = anon_sym++;
+ sym = global_identifier_push(v, type->t | VT_STATIC, 0);
+ sym->type.ref = type->ref;
+ sym->r = VT_CONST | VT_SYM;
+ put_extern_sym(sym, sec, offset, size);
+ return sym;
+}
+
+/* push a reference to a section offset by adding a dummy symbol */
+static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size)
+{
+ vpushsym(type, get_sym_ref(type, sec, offset, size));
+}
+
+/* define a new external reference to a symbol 'v' of type 'u' */
+ST_FUNC Sym *external_global_sym(int v, CType *type, int r)
+{
+ Sym *s;
+
+ s = sym_find(v);
+ if (!s) {
+ /* push forward reference */
+ s = global_identifier_push(v, type->t | VT_EXTERN, 0);
+ s->type.ref = type->ref;
+ s->r = r | VT_CONST | VT_SYM;
+ }
+ return s;
+}
+
+/* define a new external reference to a symbol 'v' with alternate asm
+ name 'asm_label' of type 'u'. 'asm_label' is equal to NULL if there
+ is no alternate name (most cases) */
+static Sym *external_sym(int v, CType *type, int r, char *asm_label)
+{
+ Sym *s;
+
+ s = sym_find(v);
+ if (!s) {
+ /* push forward reference */
+ s = sym_push(v, type, r | VT_CONST | VT_SYM, 0);
+ s->asm_label = asm_label;
+ s->type.t |= VT_EXTERN;
+ } else if (s->type.ref == func_old_type.ref) {
+ s->type.ref = type->ref;
+ s->r = r | VT_CONST | VT_SYM;
+ s->type.t |= VT_EXTERN;
+ } else if (!is_compatible_types(&s->type, type)) {
+ tcc_error("incompatible types for redefinition of '%s'",
+ get_tok_str(v, NULL));
+ }
+ /* Merge some storage attributes. */
+ if (type->t & VT_WEAK)
+ weaken_symbol(s);
+
+ if (type->t & VT_VIS_MASK)
+ apply_visibility(s, type);
+
+ return s;
+}
+
+/* push a reference to global symbol v */
+ST_FUNC void vpush_global_sym(CType *type, int v)
+{
+ vpushsym(type, external_global_sym(v, type, 0));
+}
+
+ST_FUNC void vset(CType *type, int r, int v)
+{
+ CValue cval;
+
+ cval.i = v;
+ vsetc(type, r, &cval);
+}
+
+static void vseti(int r, int v)
+{
+ CType type;
+ type.t = VT_INT;
+ type.ref = 0;
+ vset(&type, r, v);
+}
+
+ST_FUNC void vswap(void)
+{
+ SValue tmp;
+ /* cannot let cpu flags if other instruction are generated. Also
+ avoid leaving VT_JMP anywhere except on the top of the stack
+ because it would complicate the code generator. */
+ if (vtop >= vstack) {
+ int v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP || (v & ~1) == VT_JMP)
+ gv(RC_INT);
+ }
+ tmp = vtop[0];
+ vtop[0] = vtop[-1];
+ vtop[-1] = tmp;
+
+/* XXX: +2% overall speed possible with optimized memswap
+ *
+ * memswap(&vtop[0], &vtop[1], sizeof *vtop);
+ */
+}
+
+ST_FUNC void vpushv(SValue *v)
+{
+ if (vtop >= vstack + (VSTACK_SIZE - 1))
+ tcc_error("memory full (vstack)");
+ vtop++;
+ *vtop = *v;
+}
+
+ST_FUNC void vdup(void)
+{
+ vpushv(vtop);
+}
+
+/* save r to the memory stack, and mark it as being free */
+ST_FUNC void save_reg(int r)
+{
+ int l, saved, size, align;
+ SValue *p, sv;
+ CType *type;
+
+ /* modify all stack values */
+ saved = 0;
+ l = 0;
+ for(p=vstack;p<=vtop;p++) {
+ if ((p->r & VT_VALMASK) == r ||
+ ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) {
+ /* must save value on stack if not already done */
+ if (!saved) {
+ /* NOTE: must reload 'r' because r might be equal to r2 */
+ r = p->r & VT_VALMASK;
+ /* store register in the stack */
+ type = &p->type;
+ if ((p->r & VT_LVAL) ||
+ (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG))
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ type = &char_pointer_type;
+#else
+ type = &int_type;
+#endif
+ size = type_size(type, &align);
+ loc = (loc - size) & -align;
+ sv.type.t = type->t;
+ sv.r = VT_LOCAL | VT_LVAL;
+ sv.c.ul = loc;
+ store(r, &sv);
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
+ /* x86 specific: need to pop fp register ST0 if saved */
+ if (r == TREG_ST0) {
+ o(0xd8dd); /* fstp %st(0) */
+ }
+#endif
+#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64)
+ /* special long long case */
+ if ((type->t & VT_BTYPE) == VT_LLONG) {
+ sv.c.ul += 4;
+ store(p->r2, &sv);
+ }
+#endif
+ l = loc;
+ saved = 1;
+ }
+ /* mark that stack entry as being saved on the stack */
+ if (p->r & VT_LVAL) {
+ /* also clear the bounded flag because the
+ relocation address of the function was stored in
+ p->c.ul */
+ p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL;
+ } else {
+ p->r = lvalue_type(p->type.t) | VT_LOCAL;
+ }
+ p->r2 = VT_CONST;
+ p->c.ul = l;
+ }
+ }
+}
+
+#ifdef TCC_TARGET_ARM
+/* find a register of class 'rc2' with at most one reference on stack.
+ * If none, call get_reg(rc) */
+ST_FUNC int get_reg_ex(int rc, int rc2)
+{
+ int r;
+ SValue *p;
+
+ for(r=0;r<NB_REGS;r++) {
+ if (reg_classes[r] & rc2) {
+ int n;
+ n=0;
+ for(p = vstack; p <= vtop; p++) {
+ if ((p->r & VT_VALMASK) == r ||
+ (p->r2 & VT_VALMASK) == r)
+ n++;
+ }
+ if (n <= 1)
+ return r;
+ }
+ }
+ return get_reg(rc);
+}
+#endif
+
+/* find a free register of class 'rc'. If none, save one register */
+ST_FUNC int get_reg(int rc)
+{
+ int r;
+ SValue *p;
+
+ /* find a free register */
+ for(r=0;r<NB_REGS;r++) {
+ if (reg_classes[r] & rc) {
+ for(p=vstack;p<=vtop;p++) {
+ if ((p->r & VT_VALMASK) == r ||
+ (p->r2 & VT_VALMASK) == r)
+ goto notfound;
+ }
+ return r;
+ }
+ notfound: ;
+ }
+
+ /* no register left : free the first one on the stack (VERY
+ IMPORTANT to start from the bottom to ensure that we don't
+ spill registers used in gen_opi()) */
+ for(p=vstack;p<=vtop;p++) {
+ /* look at second register (if long long) */
+ r = p->r2 & VT_VALMASK;
+ if (r < VT_CONST && (reg_classes[r] & rc))
+ goto save_found;
+ r = p->r & VT_VALMASK;
+ if (r < VT_CONST && (reg_classes[r] & rc)) {
+ save_found:
+ save_reg(r);
+ return r;
+ }
+ }
+ /* Should never comes here */
+ return -1;
+}
+
+/* save registers up to (vtop - n) stack entry */
+ST_FUNC void save_regs(int n)
+{
+ int r;
+ SValue *p, *p1;
+ p1 = vtop - n;
+ for(p = vstack;p <= p1; p++) {
+ r = p->r & VT_VALMASK;
+ if (r < VT_CONST) {
+ save_reg(r);
+ }
+ }
+}
+
+/* move register 's' (of type 't') to 'r', and flush previous value of r to memory
+ if needed */
+static void move_reg(int r, int s, int t)
+{
+ SValue sv;
+
+ if (r != s) {
+ save_reg(r);
+ sv.type.t = t;
+ sv.type.ref = NULL;
+ sv.r = s;
+ sv.c.ul = 0;
+ load(r, &sv);
+ }
+}
+
+/* get address of vtop (vtop MUST BE an lvalue) */
+ST_FUNC void gaddrof(void)
+{
+ if (vtop->r & VT_REF && !nocode_wanted)
+ gv(RC_INT);
+ vtop->r &= ~VT_LVAL;
+ /* tricky: if saved lvalue, then we can go back to lvalue */
+ if ((vtop->r & VT_VALMASK) == VT_LLOCAL)
+ vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL;
+
+
+}
+
+#ifdef CONFIG_TCC_BCHECK
+/* generate lvalue bound code */
+static void gbound(void)
+{
+ int lval_type;
+ CType type1;
+
+ vtop->r &= ~VT_MUSTBOUND;
+ /* if lvalue, then use checking code before dereferencing */
+ if (vtop->r & VT_LVAL) {
+ /* if not VT_BOUNDED value, then make one */
+ if (!(vtop->r & VT_BOUNDED)) {
+ lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL);
+ /* must save type because we must set it to int to get pointer */
+ type1 = vtop->type;
+ vtop->type.t = VT_PTR;
+ gaddrof();
+ vpushi(0);
+ gen_bounded_ptr_add();
+ vtop->r |= lval_type;
+ vtop->type = type1;
+ }
+ /* then check for dereferencing */
+ gen_bounded_ptr_deref();
+ }
+}
+#endif
+
+/* store vtop a register belonging to class 'rc'. lvalues are
+ converted to values. Cannot be used if cannot be converted to
+ register value (such as structures). */
+ST_FUNC int gv(int rc)
+{
+ int r, bit_pos, bit_size, size, align, i;
+ int rc2;
+
+ /* NOTE: get_reg can modify vstack[] */
+ if (vtop->type.t & VT_BITFIELD) {
+ CType type;
+ int bits = 32;
+ bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
+ bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
+ /* remove bit field info to avoid loops */
+ vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT));
+ /* cast to int to propagate signedness in following ops */
+ if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ type.t = VT_LLONG;
+ bits = 64;
+ } else
+ type.t = VT_INT;
+ if((vtop->type.t & VT_UNSIGNED) ||
+ (vtop->type.t & VT_BTYPE) == VT_BOOL)
+ type.t |= VT_UNSIGNED;
+ gen_cast(&type);
+ /* generate shifts */
+ vpushi(bits - (bit_pos + bit_size));
+ gen_op(TOK_SHL);
+ vpushi(bits - bit_size);
+ /* NOTE: transformed to SHR if unsigned */
+ gen_op(TOK_SAR);
+ r = gv(rc);
+ } else {
+ if (is_float(vtop->type.t) &&
+ (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ Sym *sym;
+ int *ptr;
+ unsigned long offset;
+#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP)
+ CValue check;
+#endif
+
+ /* XXX: unify with initializers handling ? */
+ /* CPUs usually cannot use float constants, so we store them
+ generically in data segment */
+ size = type_size(&vtop->type, &align);
+ offset = (data_section->data_offset + align - 1) & -align;
+ data_section->data_offset = offset;
+ /* XXX: not portable yet */
+#if defined(__i386__) || defined(__x86_64__)
+ /* Zero pad x87 tenbyte long doubles */
+ if (size == LDOUBLE_SIZE) {
+ vtop->c.tab[2] &= 0xffff;
+#if LDOUBLE_SIZE == 16
+ vtop->c.tab[3] = 0;
+#endif
+ }
+#endif
+ ptr = section_ptr_add(data_section, size);
+ size = size >> 2;
+#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP)
+ check.d = 1;
+ if(check.tab[0])
+ for(i=0;i<size;i++)
+ ptr[i] = vtop->c.tab[size-1-i];
+ else
+#endif
+ for(i=0;i<size;i++)
+ ptr[i] = vtop->c.tab[i];
+ sym = get_sym_ref(&vtop->type, data_section, offset, size << 2);
+ vtop->r |= VT_LVAL | VT_SYM;
+ vtop->sym = sym;
+ vtop->c.ptr_offset = 0;
+ }
+#ifdef CONFIG_TCC_BCHECK
+ if (vtop->r & VT_MUSTBOUND)
+ gbound();
+#endif
+
+ r = vtop->r & VT_VALMASK;
+ rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
+#ifndef TCC_TARGET_ARM64
+ if (rc == RC_IRET)
+ rc2 = RC_LRET;
+#ifdef TCC_TARGET_X86_64
+ else if (rc == RC_FRET)
+ rc2 = RC_QRET;
+#endif
+#endif
+
+ /* need to reload if:
+ - constant
+ - lvalue (need to dereference pointer)
+ - already a register, but not in the right class */
+ if (r >= VT_CONST
+ || (vtop->r & VT_LVAL)
+ || !(reg_classes[r] & rc)
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
+ || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
+#else
+ || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
+#endif
+ )
+ {
+ r = get_reg(rc);
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
+ int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
+#else
+ if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
+ unsigned long long ll;
+#endif
+ int r2, original_type;
+ original_type = vtop->type.t;
+ /* two register type load : expand to two words
+ temporarily */
+#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64)
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ /* load constant */
+ ll = vtop->c.ull;
+ vtop->c.ui = ll; /* first word */
+ load(r, vtop);
+ vtop->r = r; /* save register value */
+ vpushi(ll >> 32); /* second word */
+ } else
+#endif
+ if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */
+ (vtop->r & VT_LVAL)) {
+ /* We do not want to modifier the long long
+ pointer here, so the safest (and less
+ efficient) is to save all the other registers
+ in the stack. XXX: totally inefficient. */
+ save_regs(1);
+ /* load from memory */
+ vtop->type.t = load_type;
+ load(r, vtop);
+ vdup();
+ vtop[-1].r = r; /* save register value */
+ /* increment pointer to get second word */
+ vtop->type.t = addr_type;
+ gaddrof();
+ vpushi(load_size);
+ gen_op('+');
+ vtop->r |= VT_LVAL;
+ vtop->type.t = load_type;
+ } else {
+ /* move registers */
+ load(r, vtop);
+ vdup();
+ vtop[-1].r = r; /* save register value */
+ vtop->r = vtop[-1].r2;
+ }
+ /* Allocate second register. Here we rely on the fact that
+ get_reg() tries first to free r2 of an SValue. */
+ r2 = get_reg(rc2);
+ load(r2, vtop);
+ vpop();
+ /* write second register */
+ vtop->r2 = r2;
+ vtop->type.t = original_type;
+ } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
+ int t1, t;
+ /* lvalue of scalar type : need to use lvalue type
+ because of possible cast */
+ t = vtop->type.t;
+ t1 = t;
+ /* compute memory access type */
+ if (vtop->r & VT_REF)
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ t = VT_PTR;
+#else
+ t = VT_INT;
+#endif
+ else if (vtop->r & VT_LVAL_BYTE)
+ t = VT_BYTE;
+ else if (vtop->r & VT_LVAL_SHORT)
+ t = VT_SHORT;
+ if (vtop->r & VT_LVAL_UNSIGNED)
+ t |= VT_UNSIGNED;
+ vtop->type.t = t;
+ load(r, vtop);
+ /* restore wanted type */
+ vtop->type.t = t1;
+ } else {
+ /* one register type load */
+ load(r, vtop);
+ }
+ }
+ vtop->r = r;
+#ifdef TCC_TARGET_C67
+ /* uses register pairs for doubles */
+ if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
+ vtop->r2 = r+1;
+#endif
+ }
+ return r;
+}
+
+/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
+ST_FUNC void gv2(int rc1, int rc2)
+{
+ int v;
+
+ /* generate more generic register first. But VT_JMP or VT_CMP
+ values must be generated first in all cases to avoid possible
+ reload errors */
+ v = vtop[0].r & VT_VALMASK;
+ if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
+ vswap();
+ gv(rc1);
+ vswap();
+ gv(rc2);
+ /* test if reload is needed for first register */
+ if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
+ vswap();
+ gv(rc1);
+ vswap();
+ }
+ } else {
+ gv(rc2);
+ vswap();
+ gv(rc1);
+ vswap();
+ /* test if reload is needed for first register */
+ if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
+ gv(rc2);
+ }
+ }
+}
+
+#ifndef TCC_TARGET_ARM64
+/* wrapper around RC_FRET to return a register by type */
+static int rc_fret(int t)
+{
+#ifdef TCC_TARGET_X86_64
+ if (t == VT_LDOUBLE) {
+ return RC_ST0;
+ }
+#endif
+ return RC_FRET;
+}
+#endif
+
+/* wrapper around REG_FRET to return a register by type */
+static int reg_fret(int t)
+{
+#ifdef TCC_TARGET_X86_64
+ if (t == VT_LDOUBLE) {
+ return TREG_ST0;
+ }
+#endif
+ return REG_FRET;
+}
+
+/* expand long long on stack in two int registers */
+static void lexpand(void)
+{
+ int u;
+
+ u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
+ gv(RC_INT);
+ vdup();
+ vtop[0].r = vtop[-1].r2;
+ vtop[0].r2 = VT_CONST;
+ vtop[-1].r2 = VT_CONST;
+ vtop[0].type.t = VT_INT | u;
+ vtop[-1].type.t = VT_INT | u;
+}
+
+#ifdef TCC_TARGET_ARM
+/* expand long long on stack */
+ST_FUNC void lexpand_nr(void)
+{
+ int u,v;
+
+ u = vtop->type.t & (VT_DEFSIGN | VT_UNSIGNED);
+ vdup();
+ vtop->r2 = VT_CONST;
+ vtop->type.t = VT_INT | u;
+ v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
+ if (v == VT_CONST) {
+ vtop[-1].c.ui = vtop->c.ull;
+ vtop->c.ui = vtop->c.ull >> 32;
+ vtop->r = VT_CONST;
+ } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
+ vtop->c.ui += 4;
+ vtop->r = vtop[-1].r;
+ } else if (v > VT_CONST) {
+ vtop--;
+ lexpand();
+ } else
+ vtop->r = vtop[-1].r2;
+ vtop[-1].r2 = VT_CONST;
+ vtop[-1].type.t = VT_INT | u;
+}
+#endif
+
+/* build a long long from two ints */
+static void lbuild(int t)
+{
+ gv2(RC_INT, RC_INT);
+ vtop[-1].r2 = vtop[0].r;
+ vtop[-1].type.t = t;
+ vpop();
+}
+
+/* rotate n first stack elements to the bottom
+ I1 ... In -> I2 ... In I1 [top is right]
+*/
+ST_FUNC void vrotb(int n)
+{
+ int i;
+ SValue tmp;
+
+ tmp = vtop[-n + 1];
+ for(i=-n+1;i!=0;i++)
+ vtop[i] = vtop[i+1];
+ vtop[0] = tmp;
+}
+
+/* rotate the n elements before entry e towards the top
+ I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
+ */
+ST_FUNC void vrote(SValue *e, int n)
+{
+ int i;
+ SValue tmp;
+
+ tmp = *e;
+ for(i = 0;i < n - 1; i++)
+ e[-i] = e[-i - 1];
+ e[-n + 1] = tmp;
+}
+
+/* rotate n first stack elements to the top
+ I1 ... In -> In I1 ... I(n-1) [top is right]
+ */
+ST_FUNC void vrott(int n)
+{
+ vrote(vtop, n);
+}
+
+/* pop stack value */
+ST_FUNC void vpop(void)
+{
+ int v;
+ v = vtop->r & VT_VALMASK;
+#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
+ /* for x86, we need to pop the FP stack */
+ if (v == TREG_ST0 && !nocode_wanted) {
+ o(0xd8dd); /* fstp %st(0) */
+ } else
+#endif
+ if (v == VT_JMP || v == VT_JMPI) {
+ /* need to put correct jump if && or || without test */
+ gsym(vtop->c.ul);
+ }
+ vtop--;
+}
+
+/* convert stack entry to register and duplicate its value in another
+ register */
+static void gv_dup(void)
+{
+ int rc, t, r, r1;
+ SValue sv;
+
+ t = vtop->type.t;
+ if ((t & VT_BTYPE) == VT_LLONG) {
+ lexpand();
+ gv_dup();
+ vswap();
+ vrotb(3);
+ gv_dup();
+ vrotb(4);
+ /* stack: H L L1 H1 */
+ lbuild(t);
+ vrotb(3);
+ vrotb(3);
+ vswap();
+ lbuild(t);
+ vswap();
+ } else {
+ /* duplicate value */
+ rc = RC_INT;
+ sv.type.t = VT_INT;
+ if (is_float(t)) {
+ rc = RC_FLOAT;
+#ifdef TCC_TARGET_X86_64
+ if ((t & VT_BTYPE) == VT_LDOUBLE) {
+ rc = RC_ST0;
+ }
+#endif
+ sv.type.t = t;
+ }
+ r = gv(rc);
+ r1 = get_reg(rc);
+ sv.r = r;
+ sv.c.ul = 0;
+ load(r1, &sv); /* move r to r1 */
+ vdup();
+ /* duplicates value */
+ if (r != r1)
+ vtop->r = r1;
+ }
+}
+
+/* Generate value test
+ *
+ * Generate a test for any value (jump, comparison and integers) */
+ST_FUNC int gvtst(int inv, int t)
+{
+ int v = vtop->r & VT_VALMASK;
+ if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) {
+ vpushi(0);
+ gen_op(TOK_NE);
+ }
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ /* constant jmp optimization */
+ if ((vtop->c.i != 0) != inv)
+ t = gjmp(t);
+ vtop--;
+ return t;
+ }
+ return gtst(inv, t);
+}
+
+#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64)
+/* generate CPU independent (unsigned) long long operations */
+static void gen_opl(int op)
+{
+ int t, a, b, op1, c, i;
+ int func;
+ unsigned short reg_iret = REG_IRET;
+ unsigned short reg_lret = REG_LRET;
+ SValue tmp;
+
+ switch(op) {
+ case '/':
+ case TOK_PDIV:
+ func = TOK___divdi3;
+ goto gen_func;
+ case TOK_UDIV:
+ func = TOK___udivdi3;
+ goto gen_func;
+ case '%':
+ func = TOK___moddi3;
+ goto gen_mod_func;
+ case TOK_UMOD:
+ func = TOK___umoddi3;
+ gen_mod_func:
+#ifdef TCC_ARM_EABI
+ reg_iret = TREG_R2;
+ reg_lret = TREG_R3;
+#endif
+ gen_func:
+ /* call generic long long function */
+ vpush_global_sym(&func_old_type, func);
+ vrott(3);
+ gfunc_call(2);
+ vpushi(0);
+ vtop->r = reg_iret;
+ vtop->r2 = reg_lret;
+ break;
+ case '^':
+ case '&':
+ case '|':
+ case '*':
+ case '+':
+ case '-':
+ t = vtop->type.t;
+ vswap();
+ lexpand();
+ vrotb(3);
+ lexpand();
+ /* stack: L1 H1 L2 H2 */
+ tmp = vtop[0];
+ vtop[0] = vtop[-3];
+ vtop[-3] = tmp;
+ tmp = vtop[-2];
+ vtop[-2] = vtop[-3];
+ vtop[-3] = tmp;
+ vswap();
+ /* stack: H1 H2 L1 L2 */
+ if (op == '*') {
+ vpushv(vtop - 1);
+ vpushv(vtop - 1);
+ gen_op(TOK_UMULL);
+ lexpand();
+ /* stack: H1 H2 L1 L2 ML MH */
+ for(i=0;i<4;i++)
+ vrotb(6);
+ /* stack: ML MH H1 H2 L1 L2 */
+ tmp = vtop[0];
+ vtop[0] = vtop[-2];
+ vtop[-2] = tmp;
+ /* stack: ML MH H1 L2 H2 L1 */
+ gen_op('*');
+ vrotb(3);
+ vrotb(3);
+ gen_op('*');
+ /* stack: ML MH M1 M2 */
+ gen_op('+');
+ gen_op('+');
+ } else if (op == '+' || op == '-') {
+ /* XXX: add non carry method too (for MIPS or alpha) */
+ if (op == '+')
+ op1 = TOK_ADDC1;
+ else
+ op1 = TOK_SUBC1;
+ gen_op(op1);
+ /* stack: H1 H2 (L1 op L2) */
+ vrotb(3);
+ vrotb(3);
+ gen_op(op1 + 1); /* TOK_xxxC2 */
+ } else {
+ gen_op(op);
+ /* stack: H1 H2 (L1 op L2) */
+ vrotb(3);
+ vrotb(3);
+ /* stack: (L1 op L2) H1 H2 */
+ gen_op(op);
+ /* stack: (L1 op L2) (H1 op H2) */
+ }
+ /* stack: L H */
+ lbuild(t);
+ break;
+ case TOK_SAR:
+ case TOK_SHR:
+ case TOK_SHL:
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ t = vtop[-1].type.t;
+ vswap();
+ lexpand();
+ vrotb(3);
+ /* stack: L H shift */
+ c = (int)vtop->c.i;
+ /* constant: simpler */
+ /* NOTE: all comments are for SHL. the other cases are
+ done by swaping words */
+ vpop();
+ if (op != TOK_SHL)
+ vswap();
+ if (c >= 32) {
+ /* stack: L H */
+ vpop();
+ if (c > 32) {
+ vpushi(c - 32);
+ gen_op(op);
+ }
+ if (op != TOK_SAR) {
+ vpushi(0);
+ } else {
+ gv_dup();
+ vpushi(31);
+ gen_op(TOK_SAR);
+ }
+ vswap();
+ } else {
+ vswap();
+ gv_dup();
+ /* stack: H L L */
+ vpushi(c);
+ gen_op(op);
+ vswap();
+ vpushi(32 - c);
+ if (op == TOK_SHL)
+ gen_op(TOK_SHR);
+ else
+ gen_op(TOK_SHL);
+ vrotb(3);
+ /* stack: L L H */
+ vpushi(c);
+ if (op == TOK_SHL)
+ gen_op(TOK_SHL);
+ else
+ gen_op(TOK_SHR);
+ gen_op('|');
+ }
+ if (op != TOK_SHL)
+ vswap();
+ lbuild(t);
+ } else {
+ /* XXX: should provide a faster fallback on x86 ? */
+ switch(op) {
+ case TOK_SAR:
+ func = TOK___ashrdi3;
+ goto gen_func;
+ case TOK_SHR:
+ func = TOK___lshrdi3;
+ goto gen_func;
+ case TOK_SHL:
+ func = TOK___ashldi3;
+ goto gen_func;
+ }
+ }
+ break;
+ default:
+ /* compare operations */
+ t = vtop->type.t;
+ vswap();
+ lexpand();
+ vrotb(3);
+ lexpand();
+ /* stack: L1 H1 L2 H2 */
+ tmp = vtop[-1];
+ vtop[-1] = vtop[-2];
+ vtop[-2] = tmp;
+ /* stack: L1 L2 H1 H2 */
+ /* compare high */
+ op1 = op;
+ /* when values are equal, we need to compare low words. since
+ the jump is inverted, we invert the test too. */
+ if (op1 == TOK_LT)
+ op1 = TOK_LE;
+ else if (op1 == TOK_GT)
+ op1 = TOK_GE;
+ else if (op1 == TOK_ULT)
+ op1 = TOK_ULE;
+ else if (op1 == TOK_UGT)
+ op1 = TOK_UGE;
+ a = 0;
+ b = 0;
+ gen_op(op1);
+ if (op1 != TOK_NE) {
+ a = gvtst(1, 0);
+ }
+ if (op != TOK_EQ) {
+ /* generate non equal test */
+ /* XXX: NOT PORTABLE yet */
+ if (a == 0) {
+ b = gvtst(0, 0);
+ } else {
+#if defined(TCC_TARGET_I386)
+ b = psym(0x850f, 0);
+#elif defined(TCC_TARGET_ARM)
+ b = ind;
+ o(0x1A000000 | encbranch(ind, 0, 1));
+#elif defined(TCC_TARGET_C67) || defined(TCC_TARGET_ARM64)
+ tcc_error("not implemented");
+#else
+#error not supported
+#endif
+ }
+ }
+ /* compare low. Always unsigned */
+ op1 = op;
+ if (op1 == TOK_LT)
+ op1 = TOK_ULT;
+ else if (op1 == TOK_LE)
+ op1 = TOK_ULE;
+ else if (op1 == TOK_GT)
+ op1 = TOK_UGT;
+ else if (op1 == TOK_GE)
+ op1 = TOK_UGE;
+ gen_op(op1);
+ a = gvtst(1, a);
+ gsym(b);
+ vseti(VT_JMPI, a);
+ break;
+ }
+}
+#endif
+
+/* handle integer constant optimizations and various machine
+ independent opt */
+static void gen_opic(int op)
+{
+ int c1, c2, t1, t2, n;
+ SValue *v1, *v2;
+ long long l1, l2;
+ typedef unsigned long long U;
+
+ v1 = vtop - 1;
+ v2 = vtop;
+ t1 = v1->type.t & VT_BTYPE;
+ t2 = v2->type.t & VT_BTYPE;
+
+ if (t1 == VT_LLONG)
+ l1 = v1->c.ll;
+ else if (v1->type.t & VT_UNSIGNED)
+ l1 = v1->c.ui;
+ else
+ l1 = v1->c.i;
+
+ if (t2 == VT_LLONG)
+ l2 = v2->c.ll;
+ else if (v2->type.t & VT_UNSIGNED)
+ l2 = v2->c.ui;
+ else
+ l2 = v2->c.i;
+
+ /* currently, we cannot do computations with forward symbols */
+ c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+ c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+ if (c1 && c2) {
+ switch(op) {
+ case '+': l1 += l2; break;
+ case '-': l1 -= l2; break;
+ case '&': l1 &= l2; break;
+ case '^': l1 ^= l2; break;
+ case '|': l1 |= l2; break;
+ case '*': l1 *= l2; break;
+
+ case TOK_PDIV:
+ case '/':
+ case '%':
+ case TOK_UDIV:
+ case TOK_UMOD:
+ /* if division by zero, generate explicit division */
+ if (l2 == 0) {
+ if (const_wanted)
+ tcc_error("division by zero in constant");
+ goto general_case;
+ }
+ switch(op) {
+ default: l1 /= l2; break;
+ case '%': l1 %= l2; break;
+ case TOK_UDIV: l1 = (U)l1 / l2; break;
+ case TOK_UMOD: l1 = (U)l1 % l2; break;
+ }
+ break;
+ case TOK_SHL: l1 <<= l2; break;
+ case TOK_SHR: l1 = (U)l1 >> l2; break;
+ case TOK_SAR: l1 >>= l2; break;
+ /* tests */
+ case TOK_ULT: l1 = (U)l1 < (U)l2; break;
+ case TOK_UGE: l1 = (U)l1 >= (U)l2; break;
+ case TOK_EQ: l1 = l1 == l2; break;
+ case TOK_NE: l1 = l1 != l2; break;
+ case TOK_ULE: l1 = (U)l1 <= (U)l2; break;
+ case TOK_UGT: l1 = (U)l1 > (U)l2; break;
+ case TOK_LT: l1 = l1 < l2; break;
+ case TOK_GE: l1 = l1 >= l2; break;
+ case TOK_LE: l1 = l1 <= l2; break;
+ case TOK_GT: l1 = l1 > l2; break;
+ /* logical */
+ case TOK_LAND: l1 = l1 && l2; break;
+ case TOK_LOR: l1 = l1 || l2; break;
+ default:
+ goto general_case;
+ }
+ v1->c.ll = l1;
+ vtop--;
+ } else {
+ /* if commutative ops, put c2 as constant */
+ if (c1 && (op == '+' || op == '&' || op == '^' ||
+ op == '|' || op == '*')) {
+ vswap();
+ c2 = c1; //c = c1, c1 = c2, c2 = c;
+ l2 = l1; //l = l1, l1 = l2, l2 = l;
+ }
+ if (!const_wanted &&
+ c1 && ((l1 == 0 &&
+ (op == TOK_SHL || op == TOK_SHR || op == TOK_SAR)) ||
+ (l1 == -1 && op == TOK_SAR))) {
+ /* treat (0 << x), (0 >> x) and (-1 >> x) as constant */
+ vtop--;
+ } else if (!const_wanted &&
+ c2 && ((l2 == 0 && (op == '&' || op == '*')) ||
+ (l2 == -1 && op == '|') ||
+ (l2 == 0xffffffff && t2 != VT_LLONG && op == '|') ||
+ (l2 == 1 && (op == '%' || op == TOK_UMOD)))) {
+ /* treat (x & 0), (x * 0), (x | -1) and (x % 1) as constant */
+ if (l2 == 1)
+ vtop->c.ll = 0;
+ vswap();
+ vtop--;
+ } else if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV ||
+ op == TOK_PDIV) &&
+ l2 == 1) ||
+ ((op == '+' || op == '-' || op == '|' || op == '^' ||
+ op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) &&
+ l2 == 0) ||
+ (op == '&' &&
+ l2 == -1))) {
+ /* filter out NOP operations like x*1, x-0, x&-1... */
+ vtop--;
+ } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) {
+ /* try to use shifts instead of muls or divs */
+ if (l2 > 0 && (l2 & (l2 - 1)) == 0) {
+ n = -1;
+ while (l2) {
+ l2 >>= 1;
+ n++;
+ }
+ vtop->c.ll = n;
+ if (op == '*')
+ op = TOK_SHL;
+ else if (op == TOK_PDIV)
+ op = TOK_SAR;
+ else
+ op = TOK_SHR;
+ }
+ goto general_case;
+ } else if (c2 && (op == '+' || op == '-') &&
+ (((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM))
+ || (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) {
+ /* symbol + constant case */
+ if (op == '-')
+ l2 = -l2;
+ vtop--;
+ vtop->c.ll += l2;
+ } else {
+ general_case:
+ if (!nocode_wanted) {
+ /* call low level op generator */
+ if (t1 == VT_LLONG || t2 == VT_LLONG ||
+ (PTR_SIZE == 8 && (t1 == VT_PTR || t2 == VT_PTR)))
+ gen_opl(op);
+ else
+ gen_opi(op);
+ } else {
+ vtop--;
+ }
+ }
+ }
+}
+
+/* generate a floating point operation with constant propagation */
+static void gen_opif(int op)
+{
+ int c1, c2;
+ SValue *v1, *v2;
+ long double f1, f2;
+
+ v1 = vtop - 1;
+ v2 = vtop;
+ /* currently, we cannot do computations with forward symbols */
+ c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+ c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+ if (c1 && c2) {
+ if (v1->type.t == VT_FLOAT) {
+ f1 = v1->c.f;
+ f2 = v2->c.f;
+ } else if (v1->type.t == VT_DOUBLE) {
+ f1 = v1->c.d;
+ f2 = v2->c.d;
+ } else {
+ f1 = v1->c.ld;
+ f2 = v2->c.ld;
+ }
+
+ /* NOTE: we only do constant propagation if finite number (not
+ NaN or infinity) (ANSI spec) */
+ if (!ieee_finite(f1) || !ieee_finite(f2))
+ goto general_case;
+
+ switch(op) {
+ case '+': f1 += f2; break;
+ case '-': f1 -= f2; break;
+ case '*': f1 *= f2; break;
+ case '/':
+ if (f2 == 0.0) {
+ if (const_wanted)
+ tcc_error("division by zero in constant");
+ goto general_case;
+ }
+ f1 /= f2;
+ break;
+ /* XXX: also handles tests ? */
+ default:
+ goto general_case;
+ }
+ /* XXX: overflow test ? */
+ if (v1->type.t == VT_FLOAT) {
+ v1->c.f = f1;
+ } else if (v1->type.t == VT_DOUBLE) {
+ v1->c.d = f1;
+ } else {
+ v1->c.ld = f1;
+ }
+ vtop--;
+ } else {
+ general_case:
+ if (!nocode_wanted) {
+ gen_opf(op);
+ } else {
+ vtop--;
+ }
+ }
+}
+
+static int pointed_size(CType *type)
+{
+ int align;
+ return type_size(pointed_type(type), &align);
+}
+
+static void vla_runtime_pointed_size(CType *type)
+{
+ int align;
+ vla_runtime_type_size(pointed_type(type), &align);
+}
+
+static inline int is_null_pointer(SValue *p)
+{
+ if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+ return 0;
+ return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) ||
+ ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0) ||
+ ((p->type.t & VT_BTYPE) == VT_PTR && p->c.ptr_offset == 0);
+}
+
+static inline int is_integer_btype(int bt)
+{
+ return (bt == VT_BYTE || bt == VT_SHORT ||
+ bt == VT_INT || bt == VT_LLONG);
+}
+
+/* check types for comparison or subtraction of pointers */
+static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op)
+{
+ CType *type1, *type2, tmp_type1, tmp_type2;
+ int bt1, bt2;
+
+ /* null pointers are accepted for all comparisons as gcc */
+ if (is_null_pointer(p1) || is_null_pointer(p2))
+ return;
+ type1 = &p1->type;
+ type2 = &p2->type;
+ bt1 = type1->t & VT_BTYPE;
+ bt2 = type2->t & VT_BTYPE;
+ /* accept comparison between pointer and integer with a warning */
+ if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') {
+ if (op != TOK_LOR && op != TOK_LAND )
+ tcc_warning("comparison between pointer and integer");
+ return;
+ }
+
+ /* both must be pointers or implicit function pointers */
+ if (bt1 == VT_PTR) {
+ type1 = pointed_type(type1);
+ } else if (bt1 != VT_FUNC)
+ goto invalid_operands;
+
+ if (bt2 == VT_PTR) {
+ type2 = pointed_type(type2);
+ } else if (bt2 != VT_FUNC) {
+ invalid_operands:
+ tcc_error("invalid operands to binary %s", get_tok_str(op, NULL));
+ }
+ if ((type1->t & VT_BTYPE) == VT_VOID ||
+ (type2->t & VT_BTYPE) == VT_VOID)
+ return;
+ tmp_type1 = *type1;
+ tmp_type2 = *type2;
+ tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
+ tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE);
+ if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
+ /* gcc-like error if '-' is used */
+ if (op == '-')
+ goto invalid_operands;
+ else
+ tcc_warning("comparison of distinct pointer types lacks a cast");
+ }
+}
+
+/* generic gen_op: handles types problems */
+ST_FUNC void gen_op(int op)
+{
+ int u, t1, t2, bt1, bt2, t;
+ CType type1;
+
+ t1 = vtop[-1].type.t;
+ t2 = vtop[0].type.t;
+ bt1 = t1 & VT_BTYPE;
+ bt2 = t2 & VT_BTYPE;
+
+ if (bt1 == VT_PTR || bt2 == VT_PTR) {
+ /* at least one operand is a pointer */
+ /* relationnal op: must be both pointers */
+ if (op >= TOK_ULT && op <= TOK_LOR) {
+ check_comparison_pointer_types(vtop - 1, vtop, op);
+ /* pointers are handled are unsigned */
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ t = VT_LLONG | VT_UNSIGNED;
+#else
+ t = VT_INT | VT_UNSIGNED;
+#endif
+ goto std_op;
+ }
+ /* if both pointers, then it must be the '-' op */
+ if (bt1 == VT_PTR && bt2 == VT_PTR) {
+ if (op != '-')
+ tcc_error("cannot use pointers here");
+ check_comparison_pointer_types(vtop - 1, vtop, op);
+ /* XXX: check that types are compatible */
+ if (vtop[-1].type.t & VT_VLA) {
+ vla_runtime_pointed_size(&vtop[-1].type);
+ } else {
+ vpushi(pointed_size(&vtop[-1].type));
+ }
+ vrott(3);
+ gen_opic(op);
+ /* set to integer type */
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ vtop->type.t = VT_LLONG;
+#else
+ vtop->type.t = VT_INT;
+#endif
+ vswap();
+ gen_op(TOK_PDIV);
+ } else {
+ /* exactly one pointer : must be '+' or '-'. */
+ if (op != '-' && op != '+')
+ tcc_error("cannot use pointers here");
+ /* Put pointer as first operand */
+ if (bt2 == VT_PTR) {
+ vswap();
+ swap(&t1, &t2);
+ }
+ type1 = vtop[-1].type;
+ type1.t &= ~VT_ARRAY;
+ if (vtop[-1].type.t & VT_VLA)
+ vla_runtime_pointed_size(&vtop[-1].type);
+ else {
+ u = pointed_size(&vtop[-1].type);
+ if (u < 0)
+ tcc_error("unknown array element size");
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ vpushll(u);
+#else
+ /* XXX: cast to int ? (long long case) */
+ vpushi(u);
+#endif
+ }
+ gen_op('*');
+#if 0
+/* #ifdef CONFIG_TCC_BCHECK
+ The main reason to removing this code:
+ #include <stdio.h>
+ int main ()
+ {
+ int v[10];
+ int i = 10;
+ int j = 9;
+ fprintf(stderr, "v+i-j = %p\n", v+i-j);
+ fprintf(stderr, "v+(i-j) = %p\n", v+(i-j));
+ }
+ When this code is on. then the output looks like
+ v+i-j = 0xfffffffe
+ v+(i-j) = 0xbff84000
+ */
+ /* if evaluating constant expression, no code should be
+ generated, so no bound check */
+ if (tcc_state->do_bounds_check && !const_wanted) {
+ /* if bounded pointers, we generate a special code to
+ test bounds */
+ if (op == '-') {
+ vpushi(0);
+ vswap();
+ gen_op('-');
+ }
+ gen_bounded_ptr_add();
+ } else
+#endif
+ {
+ gen_opic(op);
+ }
+ /* put again type if gen_opic() swaped operands */
+ vtop->type = type1;
+ }
+ } else if (is_float(bt1) || is_float(bt2)) {
+ /* compute bigger type and do implicit casts */
+ if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
+ t = VT_LDOUBLE;
+ } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
+ t = VT_DOUBLE;
+ } else {
+ t = VT_FLOAT;
+ }
+ /* floats can only be used for a few operations */
+ if (op != '+' && op != '-' && op != '*' && op != '/' &&
+ (op < TOK_ULT || op > TOK_GT))
+ tcc_error("invalid operands for binary operation");
+ goto std_op;
+ } else if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) {
+ t = bt1 == VT_LLONG ? VT_LLONG : VT_INT;
+ if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (t | VT_UNSIGNED))
+ t |= VT_UNSIGNED;
+ goto std_op;
+ } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
+ /* cast to biggest op */
+ t = VT_LLONG;
+ /* convert to unsigned if it does not fit in a long long */
+ if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) ||
+ (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
+ t |= VT_UNSIGNED;
+ goto std_op;
+ } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
+ tcc_error("comparison of struct");
+ } else {
+ /* integer operations */
+ t = VT_INT;
+ /* convert to unsigned if it does not fit in an integer */
+ if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) ||
+ (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED))
+ t |= VT_UNSIGNED;
+ std_op:
+ /* XXX: currently, some unsigned operations are explicit, so
+ we modify them here */
+ if (t & VT_UNSIGNED) {
+ if (op == TOK_SAR)
+ op = TOK_SHR;
+ else if (op == '/')
+ op = TOK_UDIV;
+ else if (op == '%')
+ op = TOK_UMOD;
+ else if (op == TOK_LT)
+ op = TOK_ULT;
+ else if (op == TOK_GT)
+ op = TOK_UGT;
+ else if (op == TOK_LE)
+ op = TOK_ULE;
+ else if (op == TOK_GE)
+ op = TOK_UGE;
+ }
+ vswap();
+ type1.t = t;
+ gen_cast(&type1);
+ vswap();
+ /* special case for shifts and long long: we keep the shift as
+ an integer */
+ if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL)
+ type1.t = VT_INT;
+ gen_cast(&type1);
+ if (is_float(t))
+ gen_opif(op);
+ else
+ gen_opic(op);
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ /* relationnal op: the result is an int */
+ vtop->type.t = VT_INT;
+ } else {
+ vtop->type.t = t;
+ }
+ }
+ // Make sure that we have converted to an rvalue:
+ if (vtop->r & VT_LVAL && !nocode_wanted)
+ gv(is_float(vtop->type.t & VT_BTYPE) ? RC_FLOAT : RC_INT);
+}
+
+#ifndef TCC_TARGET_ARM
+/* generic itof for unsigned long long case */
+static void gen_cvt_itof1(int t)
+{
+#ifdef TCC_TARGET_ARM64
+ gen_cvt_itof(t);
+#else
+ if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
+ (VT_LLONG | VT_UNSIGNED)) {
+
+ if (t == VT_FLOAT)
+ vpush_global_sym(&func_old_type, TOK___floatundisf);
+#if LDOUBLE_SIZE != 8
+ else if (t == VT_LDOUBLE)
+ vpush_global_sym(&func_old_type, TOK___floatundixf);
+#endif
+ else
+ vpush_global_sym(&func_old_type, TOK___floatundidf);
+ vrott(2);
+ gfunc_call(1);
+ vpushi(0);
+ vtop->r = reg_fret(t);
+ } else {
+ gen_cvt_itof(t);
+ }
+#endif
+}
+#endif
+
+/* generic ftoi for unsigned long long case */
+static void gen_cvt_ftoi1(int t)
+{
+#ifdef TCC_TARGET_ARM64
+ gen_cvt_ftoi(t);
+#else
+ int st;
+
+ if (t == (VT_LLONG | VT_UNSIGNED)) {
+ /* not handled natively */
+ st = vtop->type.t & VT_BTYPE;
+ if (st == VT_FLOAT)
+ vpush_global_sym(&func_old_type, TOK___fixunssfdi);
+#if LDOUBLE_SIZE != 8
+ else if (st == VT_LDOUBLE)
+ vpush_global_sym(&func_old_type, TOK___fixunsxfdi);
+#endif
+ else
+ vpush_global_sym(&func_old_type, TOK___fixunsdfdi);
+ vrott(2);
+ gfunc_call(1);
+ vpushi(0);
+ vtop->r = REG_IRET;
+ vtop->r2 = REG_LRET;
+ } else {
+ gen_cvt_ftoi(t);
+ }
+#endif
+}
+
+/* force char or short cast */
+static void force_charshort_cast(int t)
+{
+ int bits, dbt;
+ dbt = t & VT_BTYPE;
+ /* XXX: add optimization if lvalue : just change type and offset */
+ if (dbt == VT_BYTE)
+ bits = 8;
+ else
+ bits = 16;
+ if (t & VT_UNSIGNED) {
+ vpushi((1 << bits) - 1);
+ gen_op('&');
+ } else {
+ bits = 32 - bits;
+ vpushi(bits);
+ gen_op(TOK_SHL);
+ /* result must be signed or the SAR is converted to an SHL
+ This was not the case when "t" was a signed short
+ and the last value on the stack was an unsigned int */
+ vtop->type.t &= ~VT_UNSIGNED;
+ vpushi(bits);
+ gen_op(TOK_SAR);
+ }
+}
+
+/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */
+static void gen_cast(CType *type)
+{
+ int sbt, dbt, sf, df, c, p;
+
+ /* special delayed cast for char/short */
+ /* XXX: in some cases (multiple cascaded casts), it may still
+ be incorrect */
+ if (vtop->r & VT_MUSTCAST) {
+ vtop->r &= ~VT_MUSTCAST;
+ force_charshort_cast(vtop->type.t);
+ }
+
+ /* bitfields first get cast to ints */
+ if (vtop->type.t & VT_BITFIELD && !nocode_wanted) {
+ gv(RC_INT);
+ }
+
+ dbt = type->t & (VT_BTYPE | VT_UNSIGNED);
+ sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
+
+ if (sbt != dbt) {
+ sf = is_float(sbt);
+ df = is_float(dbt);
+ c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+ p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM);
+ if (c) {
+ /* constant case: we can do it now */
+ /* XXX: in ISOC, cannot do it if error in convert */
+ if (sbt == VT_FLOAT)
+ vtop->c.ld = vtop->c.f;
+ else if (sbt == VT_DOUBLE)
+ vtop->c.ld = vtop->c.d;
+
+ if (df) {
+ if ((sbt & VT_BTYPE) == VT_LLONG) {
+ if (sbt & VT_UNSIGNED)
+ vtop->c.ld = vtop->c.ull;
+ else
+ vtop->c.ld = vtop->c.ll;
+ } else if(!sf) {
+ if (sbt & VT_UNSIGNED)
+ vtop->c.ld = vtop->c.ui;
+ else
+ vtop->c.ld = vtop->c.i;
+ }
+
+ if (dbt == VT_FLOAT)
+ vtop->c.f = (float)vtop->c.ld;
+ else if (dbt == VT_DOUBLE)
+ vtop->c.d = (double)vtop->c.ld;
+ } else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) {
+ vtop->c.ull = (unsigned long long)vtop->c.ld;
+ } else if (sf && dbt == VT_BOOL) {
+ vtop->c.i = (vtop->c.ld != 0);
+ } else {
+ if(sf)
+ vtop->c.ll = (long long)vtop->c.ld;
+ else if (sbt == (VT_LLONG|VT_UNSIGNED))
+ vtop->c.ll = vtop->c.ull;
+ else if (sbt & VT_UNSIGNED)
+ vtop->c.ll = vtop->c.ui;
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ else if (sbt == VT_PTR)
+ ;
+#endif
+ else if (sbt != VT_LLONG)
+ vtop->c.ll = vtop->c.i;
+
+ if (dbt == (VT_LLONG|VT_UNSIGNED))
+ vtop->c.ull = vtop->c.ll;
+ else if (dbt == VT_BOOL)
+ vtop->c.i = (vtop->c.ll != 0);
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ else if (dbt == VT_PTR)
+ ;
+#endif
+ else if (dbt != VT_LLONG) {
+ int s = 0;
+ if ((dbt & VT_BTYPE) == VT_BYTE)
+ s = 24;
+ else if ((dbt & VT_BTYPE) == VT_SHORT)
+ s = 16;
+ if(dbt & VT_UNSIGNED)
+ vtop->c.ui = ((unsigned int)vtop->c.ll << s) >> s;
+ else
+ vtop->c.i = ((int)vtop->c.ll << s) >> s;
+ }
+ }
+ } else if (p && dbt == VT_BOOL) {
+ vtop->r = VT_CONST;
+ vtop->c.i = 1;
+ } else if (!nocode_wanted) {
+ /* non constant case: generate code */
+ if (sf && df) {
+ /* convert from fp to fp */
+ gen_cvt_ftof(dbt);
+ } else if (df) {
+ /* convert int to fp */
+ gen_cvt_itof1(dbt);
+ } else if (sf) {
+ /* convert fp to int */
+ if (dbt == VT_BOOL) {
+ vpushi(0);
+ gen_op(TOK_NE);
+ } else {
+ /* we handle char/short/etc... with generic code */
+ if (dbt != (VT_INT | VT_UNSIGNED) &&
+ dbt != (VT_LLONG | VT_UNSIGNED) &&
+ dbt != VT_LLONG)
+ dbt = VT_INT;
+ gen_cvt_ftoi1(dbt);
+ if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) {
+ /* additional cast for char/short... */
+ vtop->type.t = dbt;
+ gen_cast(type);
+ }
+ }
+#if !defined(TCC_TARGET_ARM64) && !defined(TCC_TARGET_X86_64)
+ } else if ((dbt & VT_BTYPE) == VT_LLONG) {
+ if ((sbt & VT_BTYPE) != VT_LLONG && !nocode_wanted) {
+ /* scalar to long long */
+ /* machine independent conversion */
+ gv(RC_INT);
+ /* generate high word */
+ if (sbt == (VT_INT | VT_UNSIGNED)) {
+ vpushi(0);
+ gv(RC_INT);
+ } else {
+ if (sbt == VT_PTR) {
+ /* cast from pointer to int before we apply
+ shift operation, which pointers don't support*/
+ gen_cast(&int_type);
+ }
+ gv_dup();
+ vpushi(31);
+ gen_op(TOK_SAR);
+ }
+ /* patch second register */
+ vtop[-1].r2 = vtop->r;
+ vpop();
+ }
+#else
+ } else if ((dbt & VT_BTYPE) == VT_LLONG ||
+ (dbt & VT_BTYPE) == VT_PTR ||
+ (dbt & VT_BTYPE) == VT_FUNC) {
+ if ((sbt & VT_BTYPE) != VT_LLONG &&
+ (sbt & VT_BTYPE) != VT_PTR &&
+ (sbt & VT_BTYPE) != VT_FUNC && !nocode_wanted) {
+ /* need to convert from 32bit to 64bit */
+ gv(RC_INT);
+ if (sbt != (VT_INT | VT_UNSIGNED)) {
+#if defined(TCC_TARGET_ARM64)
+ gen_cvt_sxtw();
+#elif defined(TCC_TARGET_X86_64)
+ int r = gv(RC_INT);
+ /* x86_64 specific: movslq */
+ o(0x6348);
+ o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r));
+#else
+#error
+#endif
+ }
+ }
+#endif
+ } else if (dbt == VT_BOOL) {
+ /* scalar to bool */
+ vpushi(0);
+ gen_op(TOK_NE);
+ } else if ((dbt & VT_BTYPE) == VT_BYTE ||
+ (dbt & VT_BTYPE) == VT_SHORT) {
+ if (sbt == VT_PTR) {
+ vtop->type.t = VT_INT;
+ tcc_warning("nonportable conversion from pointer to char/short");
+ }
+ force_charshort_cast(dbt);
+ } else if ((dbt & VT_BTYPE) == VT_INT) {
+ /* scalar to int */
+ if (sbt == VT_LLONG && !nocode_wanted) {
+ /* from long long: just take low order word */
+ lexpand();
+ vpop();
+ }
+ /* if lvalue and single word type, nothing to do because
+ the lvalue already contains the real type size (see
+ VT_LVAL_xxx constants) */
+ }
+ }
+ } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
+ /* if we are casting between pointer types,
+ we must update the VT_LVAL_xxx size */
+ vtop->r = (vtop->r & ~VT_LVAL_TYPE)
+ | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
+ }
+ vtop->type = *type;
+}
+
+/* return type size as known at compile time. Put alignment at 'a' */
+ST_FUNC int type_size(CType *type, int *a)
+{
+ Sym *s;
+ int bt;
+
+ bt = type->t & VT_BTYPE;
+ if (bt == VT_STRUCT) {
+ /* struct/union */
+ s = type->ref;
+ *a = s->r;
+ return s->c;
+ } else if (bt == VT_PTR) {
+ if (type->t & VT_ARRAY) {
+ int ts;
+
+ s = type->ref;
+ ts = type_size(&s->type, a);
+
+ if (ts < 0 && s->c < 0)
+ ts = -ts;
+
+ return ts * s->c;
+ } else {
+ *a = PTR_SIZE;
+ return PTR_SIZE;
+ }
+ } else if (bt == VT_LDOUBLE) {
+ *a = LDOUBLE_ALIGN;
+ return LDOUBLE_SIZE;
+ } else if (bt == VT_DOUBLE || bt == VT_LLONG) {
+#ifdef TCC_TARGET_I386
+#ifdef TCC_TARGET_PE
+ *a = 8;
+#else
+ *a = 4;
+#endif
+#elif defined(TCC_TARGET_ARM)
+#ifdef TCC_ARM_EABI
+ *a = 8;
+#else
+ *a = 4;
+#endif
+#else
+ *a = 8;
+#endif
+ return 8;
+ } else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) {
+ *a = 4;
+ return 4;
+ } else if (bt == VT_SHORT) {
+ *a = 2;
+ return 2;
+ } else if (bt == VT_QLONG || bt == VT_QFLOAT) {
+ *a = 8;
+ return 16;
+ } else {
+ /* char, void, function, _Bool */
+ *a = 1;
+ return 1;
+ }
+}
+
+/* push type size as known at runtime time on top of value stack. Put
+ alignment at 'a' */
+ST_FUNC void vla_runtime_type_size(CType *type, int *a)
+{
+ if (type->t & VT_VLA) {
+ vset(&int_type, VT_LOCAL|VT_LVAL, type->ref->c);
+ } else {
+ vpushi(type_size(type, a));
+ }
+}
+
+static void vla_sp_restore(void) {
+ if (vlas_in_scope) {
+ gen_vla_sp_restore(vla_sp_loc);
+ }
+}
+
+static void vla_sp_restore_root(void) {
+ if (vlas_in_scope) {
+ gen_vla_sp_restore(vla_sp_root_loc);
+ }
+}
+
+/* return the pointed type of t */
+static inline CType *pointed_type(CType *type)
+{
+ return &type->ref->type;
+}
+
+/* modify type so that its it is a pointer to type. */
+ST_FUNC void mk_pointer(CType *type)
+{
+ Sym *s;
+ s = sym_push(SYM_FIELD, type, 0, -1);
+ type->t = VT_PTR | (type->t & ~VT_TYPE);
+ type->ref = s;
+}
+
+/* compare function types. OLD functions match any new functions */
+static int is_compatible_func(CType *type1, CType *type2)
+{
+ Sym *s1, *s2;
+
+ s1 = type1->ref;
+ s2 = type2->ref;
+ if (!is_compatible_types(&s1->type, &s2->type))
+ return 0;
+ /* check func_call */
+ if (s1->a.func_call != s2->a.func_call)
+ return 0;
+ /* XXX: not complete */
+ if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
+ return 1;
+ if (s1->c != s2->c)
+ return 0;
+ while (s1 != NULL) {
+ if (s2 == NULL)
+ return 0;
+ if (!is_compatible_parameter_types(&s1->type, &s2->type))
+ return 0;
+ s1 = s1->next;
+ s2 = s2->next;
+ }
+ if (s2)
+ return 0;
+ return 1;
+}
+
+/* return true if type1 and type2 are the same. If unqualified is
+ true, qualifiers on the types are ignored.
+
+ - enums are not checked as gcc __builtin_types_compatible_p ()
+ */
+static int compare_types(CType *type1, CType *type2, int unqualified)
+{
+ int bt1, t1, t2;
+
+ t1 = type1->t & VT_TYPE;
+ t2 = type2->t & VT_TYPE;
+ if (unqualified) {
+ /* strip qualifiers before comparing */
+ t1 &= ~(VT_CONSTANT | VT_VOLATILE);
+ t2 &= ~(VT_CONSTANT | VT_VOLATILE);
+ }
+ /* Default Vs explicit signedness only matters for char */
+ if ((t1 & VT_BTYPE) != VT_BYTE) {
+ t1 &= ~VT_DEFSIGN;
+ t2 &= ~VT_DEFSIGN;
+ }
+ /* XXX: bitfields ? */
+ if (t1 != t2)
+ return 0;
+ /* test more complicated cases */
+ bt1 = t1 & VT_BTYPE;
+ if (bt1 == VT_PTR) {
+ type1 = pointed_type(type1);
+ type2 = pointed_type(type2);
+ return is_compatible_types(type1, type2);
+ } else if (bt1 == VT_STRUCT) {
+ return (type1->ref == type2->ref);
+ } else if (bt1 == VT_FUNC) {
+ return is_compatible_func(type1, type2);
+ } else {
+ return 1;
+ }
+}
+
+/* return true if type1 and type2 are exactly the same (including
+ qualifiers).
+*/
+static int is_compatible_types(CType *type1, CType *type2)
+{
+ return compare_types(type1,type2,0);
+}
+
+/* return true if type1 and type2 are the same (ignoring qualifiers).
+*/
+static int is_compatible_parameter_types(CType *type1, CType *type2)
+{
+ return compare_types(type1,type2,1);
+}
+
+/* print a type. If 'varstr' is not NULL, then the variable is also
+ printed in the type */
+/* XXX: union */
+/* XXX: add array and function pointers */
+static void type_to_str(char *buf, int buf_size,
+ CType *type, const char *varstr)
+{
+ int bt, v, t;
+ Sym *s, *sa;
+ char buf1[256];
+ const char *tstr;
+
+ t = type->t & VT_TYPE;
+ bt = t & VT_BTYPE;
+ buf[0] = '\0';
+ if (t & VT_CONSTANT)
+ pstrcat(buf, buf_size, "const ");
+ if (t & VT_VOLATILE)
+ pstrcat(buf, buf_size, "volatile ");
+ if ((t & (VT_DEFSIGN | VT_UNSIGNED)) == (VT_DEFSIGN | VT_UNSIGNED))
+ pstrcat(buf, buf_size, "unsigned ");
+ else if (t & VT_DEFSIGN)
+ pstrcat(buf, buf_size, "signed ");
+ switch(bt) {
+ case VT_VOID:
+ tstr = "void";
+ goto add_tstr;
+ case VT_BOOL:
+ tstr = "_Bool";
+ goto add_tstr;
+ case VT_BYTE:
+ tstr = "char";
+ goto add_tstr;
+ case VT_SHORT:
+ tstr = "short";
+ goto add_tstr;
+ case VT_INT:
+ tstr = "int";
+ goto add_tstr;
+ case VT_LONG:
+ tstr = "long";
+ goto add_tstr;
+ case VT_LLONG:
+ tstr = "long long";
+ goto add_tstr;
+ case VT_FLOAT:
+ tstr = "float";
+ goto add_tstr;
+ case VT_DOUBLE:
+ tstr = "double";
+ goto add_tstr;
+ case VT_LDOUBLE:
+ tstr = "long double";
+ add_tstr:
+ pstrcat(buf, buf_size, tstr);
+ break;
+ case VT_ENUM:
+ case VT_STRUCT:
+ if (bt == VT_STRUCT)
+ tstr = "struct ";
+ else
+ tstr = "enum ";
+ pstrcat(buf, buf_size, tstr);
+ v = type->ref->v & ~SYM_STRUCT;
+ if (v >= SYM_FIRST_ANOM)
+ pstrcat(buf, buf_size, "<anonymous>");
+ else
+ pstrcat(buf, buf_size, get_tok_str(v, NULL));
+ break;
+ case VT_FUNC:
+ s = type->ref;
+ type_to_str(buf, buf_size, &s->type, varstr);
+ pstrcat(buf, buf_size, "(");
+ sa = s->next;
+ while (sa != NULL) {
+ type_to_str(buf1, sizeof(buf1), &sa->type, NULL);
+ pstrcat(buf, buf_size, buf1);
+ sa = sa->next;
+ if (sa)
+ pstrcat(buf, buf_size, ", ");
+ }
+ pstrcat(buf, buf_size, ")");
+ goto no_var;
+ case VT_PTR:
+ s = type->ref;
+ pstrcpy(buf1, sizeof(buf1), "*");
+ if (varstr)
+ pstrcat(buf1, sizeof(buf1), varstr);
+ type_to_str(buf, buf_size, &s->type, buf1);
+ goto no_var;
+ }
+ if (varstr) {
+ pstrcat(buf, buf_size, " ");
+ pstrcat(buf, buf_size, varstr);
+ }
+ no_var: ;
+}
+
+/* verify type compatibility to store vtop in 'dt' type, and generate
+ casts if needed. */
+static void gen_assign_cast(CType *dt)
+{
+ CType *st, *type1, *type2, tmp_type1, tmp_type2;
+ char buf1[256], buf2[256];
+ int dbt, sbt;
+
+ st = &vtop->type; /* source type */
+ dbt = dt->t & VT_BTYPE;
+ sbt = st->t & VT_BTYPE;
+ if (sbt == VT_VOID || dbt == VT_VOID) {
+ if (sbt == VT_VOID && dbt == VT_VOID)
+ ; /*
+ It is Ok if both are void
+ A test program:
+ void func1() {}
+ void func2() {
+ return func1();
+ }
+ gcc accepts this program
+ */
+ else
+ tcc_error("cannot cast from/to void");
+ }
+ if (dt->t & VT_CONSTANT)
+ tcc_warning("assignment of read-only location");
+ switch(dbt) {
+ case VT_PTR:
+ /* special cases for pointers */
+ /* '0' can also be a pointer */
+ if (is_null_pointer(vtop))
+ goto type_ok;
+ /* accept implicit pointer to integer cast with warning */
+ if (is_integer_btype(sbt)) {
+ tcc_warning("assignment makes pointer from integer without a cast");
+ goto type_ok;
+ }
+ type1 = pointed_type(dt);
+ /* a function is implicitely a function pointer */
+ if (sbt == VT_FUNC) {
+ if ((type1->t & VT_BTYPE) != VT_VOID &&
+ !is_compatible_types(pointed_type(dt), st))
+ tcc_warning("assignment from incompatible pointer type");
+ goto type_ok;
+ }
+ if (sbt != VT_PTR)
+ goto error;
+ type2 = pointed_type(st);
+ if ((type1->t & VT_BTYPE) == VT_VOID ||
+ (type2->t & VT_BTYPE) == VT_VOID) {
+ /* void * can match anything */
+ } else {
+ /* exact type match, except for unsigned */
+ tmp_type1 = *type1;
+ tmp_type2 = *type2;
+ tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT |
+ VT_VOLATILE);
+ tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT |
+ VT_VOLATILE);
+ if (!is_compatible_types(&tmp_type1, &tmp_type2))
+ tcc_warning("assignment from incompatible pointer type");
+ }
+ /* check const and volatile */
+ if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
+ (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE)))
+ tcc_warning("assignment discards qualifiers from pointer target type");
+ break;
+ case VT_BYTE:
+ case VT_SHORT:
+ case VT_INT:
+ case VT_LLONG:
+ if (sbt == VT_PTR || sbt == VT_FUNC) {
+ tcc_warning("assignment makes integer from pointer without a cast");
+ }
+ /* XXX: more tests */
+ break;
+ case VT_STRUCT:
+ tmp_type1 = *dt;
+ tmp_type2 = *st;
+ tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
+ tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
+ if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
+ error:
+ type_to_str(buf1, sizeof(buf1), st, NULL);
+ type_to_str(buf2, sizeof(buf2), dt, NULL);
+ tcc_error("cannot cast '%s' to '%s'", buf1, buf2);
+ }
+ break;
+ }
+ type_ok:
+ gen_cast(dt);
+}
+
+/* store vtop in lvalue pushed on stack */
+ST_FUNC void vstore(void)
+{
+ int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast;
+
+ ft = vtop[-1].type.t;
+ sbt = vtop->type.t & VT_BTYPE;
+ dbt = ft & VT_BTYPE;
+ if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) ||
+ (sbt == VT_INT && dbt == VT_SHORT))
+ && !(vtop->type.t & VT_BITFIELD)) {
+ /* optimize char/short casts */
+ delayed_cast = VT_MUSTCAST;
+ vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)));
+ /* XXX: factorize */
+ if (ft & VT_CONSTANT)
+ tcc_warning("assignment of read-only location");
+ } else {
+ delayed_cast = 0;
+ if (!(ft & VT_BITFIELD))
+ gen_assign_cast(&vtop[-1].type);
+ }
+
+ if (sbt == VT_STRUCT) {
+ /* if structure, only generate pointer */
+ /* structure assignment : generate memcpy */
+ /* XXX: optimize if small size */
+ if (!nocode_wanted) {
+ size = type_size(&vtop->type, &align);
+
+ /* destination */
+ vswap();
+ vtop->type.t = VT_PTR;
+ gaddrof();
+
+ /* address of memcpy() */
+#ifdef TCC_ARM_EABI
+ if(!(align & 7))
+ vpush_global_sym(&func_old_type, TOK_memcpy8);
+ else if(!(align & 3))
+ vpush_global_sym(&func_old_type, TOK_memcpy4);
+ else
+#endif
+ vpush_global_sym(&func_old_type, TOK_memcpy);
+
+ vswap();
+ /* source */
+ vpushv(vtop - 2);
+ vtop->type.t = VT_PTR;
+ gaddrof();
+ /* type size */
+ vpushi(size);
+ gfunc_call(3);
+ } else {
+ vswap();
+ vpop();
+ }
+ /* leave source on stack */
+ } else if (ft & VT_BITFIELD) {
+ /* bitfield store handling */
+
+ /* save lvalue as expression result (example: s.b = s.a = n;) */
+ vdup(), vtop[-1] = vtop[-2];
+
+ bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f;
+ bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
+ /* remove bit field info to avoid loops */
+ vtop[-1].type.t = ft & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT));
+
+ if((ft & VT_BTYPE) == VT_BOOL) {
+ gen_cast(&vtop[-1].type);
+ vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED);
+ }
+
+ /* duplicate destination */
+ vdup();
+ vtop[-1] = vtop[-2];
+
+ /* mask and shift source */
+ if((ft & VT_BTYPE) != VT_BOOL) {
+ if((ft & VT_BTYPE) == VT_LLONG) {
+ vpushll((1ULL << bit_size) - 1ULL);
+ } else {
+ vpushi((1 << bit_size) - 1);
+ }
+ gen_op('&');
+ }
+ vpushi(bit_pos);
+ gen_op(TOK_SHL);
+ /* load destination, mask and or with source */
+ vswap();
+ if((ft & VT_BTYPE) == VT_LLONG) {
+ vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos));
+ } else {
+ vpushi(~(((1 << bit_size) - 1) << bit_pos));
+ }
+ gen_op('&');
+ gen_op('|');
+ /* store result */
+ vstore();
+ /* ... and discard */
+ vpop();
+
+ } else {
+ if (!nocode_wanted) {
+#ifdef CONFIG_TCC_BCHECK
+ /* bound check case */
+ if (vtop[-1].r & VT_MUSTBOUND) {
+ vswap();
+ gbound();
+ vswap();
+ }
+#endif
+ rc = RC_INT;
+ if (is_float(ft)) {
+ rc = RC_FLOAT;
+#ifdef TCC_TARGET_X86_64
+ if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ rc = RC_ST0;
+ } else if ((ft & VT_BTYPE) == VT_QFLOAT) {
+ rc = RC_FRET;
+ }
+#endif
+ }
+ r = gv(rc); /* generate value */
+ /* if lvalue was saved on stack, must read it */
+ if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) {
+ SValue sv;
+ t = get_reg(RC_INT);
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ sv.type.t = VT_PTR;
+#else
+ sv.type.t = VT_INT;
+#endif
+ sv.r = VT_LOCAL | VT_LVAL;
+ sv.c.ul = vtop[-1].c.ul;
+ load(t, &sv);
+ vtop[-1].r = t | VT_LVAL;
+ }
+ /* two word case handling : store second register at word + 4 (or +8 for x86-64) */
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) {
+ int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
+#else
+ if ((ft & VT_BTYPE) == VT_LLONG) {
+ int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
+#endif
+ vtop[-1].type.t = load_type;
+ store(r, vtop - 1);
+ vswap();
+ /* convert to int to increment easily */
+ vtop->type.t = addr_type;
+ gaddrof();
+ vpushi(load_size);
+ gen_op('+');
+ vtop->r |= VT_LVAL;
+ vswap();
+ vtop[-1].type.t = load_type;
+ /* XXX: it works because r2 is spilled last ! */
+ store(vtop->r2, vtop - 1);
+ } else {
+ store(r, vtop - 1);
+ }
+ }
+ vswap();
+ vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
+ vtop->r |= delayed_cast;
+ }
+}
+
+/* post defines POST/PRE add. c is the token ++ or -- */
+ST_FUNC void inc(int post, int c)
+{
+ test_lvalue();
+ vdup(); /* save lvalue */
+ if (post) {
+ if (!nocode_wanted)
+ gv_dup(); /* duplicate value */
+ else
+ vdup(); /* duplicate value */
+ vrotb(3);
+ vrotb(3);
+ }
+ /* add constant */
+ vpushi(c - TOK_MID);
+ gen_op('+');
+ vstore(); /* store value */
+ if (post)
+ vpop(); /* if post op, return saved value */
+}
+
+/* Parse GNUC __attribute__ extension. Currently, the following
+ extensions are recognized:
+ - aligned(n) : set data/function alignment.
+ - packed : force data alignment to 1
+ - section(x) : generate data/code in this section.
+ - unused : currently ignored, but may be used someday.
+ - regparm(n) : pass function parameters in registers (i386 only)
+ */
+static void parse_attribute(AttributeDef *ad)
+{
+ int t, n;
+
+ while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) {
+ next();
+ skip('(');
+ skip('(');
+ while (tok != ')') {
+ if (tok < TOK_IDENT)
+ expect("attribute name");
+ t = tok;
+ next();
+ switch(t) {
+ case TOK_SECTION1:
+ case TOK_SECTION2:
+ skip('(');
+ if (tok != TOK_STR)
+ expect("section name");
+ ad->section = find_section(tcc_state, (char *)tokc.cstr->data);
+ next();
+ skip(')');
+ break;
+ case TOK_ALIAS1:
+ case TOK_ALIAS2:
+ skip('(');
+ if (tok != TOK_STR)
+ expect("alias(\"target\")");
+ ad->alias_target = /* save string as token, for later */
+ tok_alloc((char*)tokc.cstr->data, tokc.cstr->size-1)->tok;
+ next();
+ skip(')');
+ break;
+ case TOK_VISIBILITY1:
+ case TOK_VISIBILITY2:
+ skip('(');
+ if (tok != TOK_STR)
+ expect("visibility(\"default|hidden|internal|protected\")");
+ if (!strcmp (tokc.cstr->data, "default"))
+ ad->a.visibility = STV_DEFAULT;
+ else if (!strcmp (tokc.cstr->data, "hidden"))
+ ad->a.visibility = STV_HIDDEN;
+ else if (!strcmp (tokc.cstr->data, "internal"))
+ ad->a.visibility = STV_INTERNAL;
+ else if (!strcmp (tokc.cstr->data, "protected"))
+ ad->a.visibility = STV_PROTECTED;
+ else
+ expect("visibility(\"default|hidden|internal|protected\")");
+ next();
+ skip(')');
+ break;
+ case TOK_ALIGNED1:
+ case TOK_ALIGNED2:
+ if (tok == '(') {
+ next();
+ n = expr_const();
+ if (n <= 0 || (n & (n - 1)) != 0)
+ tcc_error("alignment must be a positive power of two");
+ skip(')');
+ } else {
+ n = MAX_ALIGN;
+ }
+ ad->a.aligned = n;
+ break;
+ case TOK_PACKED1:
+ case TOK_PACKED2:
+ ad->a.packed = 1;
+ break;
+ case TOK_WEAK1:
+ case TOK_WEAK2:
+ ad->a.weak = 1;
+ break;
+ case TOK_UNUSED1:
+ case TOK_UNUSED2:
+ /* currently, no need to handle it because tcc does not
+ track unused objects */
+ break;
+ case TOK_NORETURN1:
+ case TOK_NORETURN2:
+ /* currently, no need to handle it because tcc does not
+ track unused objects */
+ break;
+ case TOK_CDECL1:
+ case TOK_CDECL2:
+ case TOK_CDECL3:
+ ad->a.func_call = FUNC_CDECL;
+ break;
+ case TOK_STDCALL1:
+ case TOK_STDCALL2:
+ case TOK_STDCALL3:
+ ad->a.func_call = FUNC_STDCALL;
+ break;
+#ifdef TCC_TARGET_I386
+ case TOK_REGPARM1:
+ case TOK_REGPARM2:
+ skip('(');
+ n = expr_const();
+ if (n > 3)
+ n = 3;
+ else if (n < 0)
+ n = 0;
+ if (n > 0)
+ ad->a.func_call = FUNC_FASTCALL1 + n - 1;
+ skip(')');
+ break;
+ case TOK_FASTCALL1:
+ case TOK_FASTCALL2:
+ case TOK_FASTCALL3:
+ ad->a.func_call = FUNC_FASTCALLW;
+ break;
+#endif
+ case TOK_MODE:
+ skip('(');
+ switch(tok) {
+ case TOK_MODE_DI:
+ ad->a.mode = VT_LLONG + 1;
+ break;
+ case TOK_MODE_HI:
+ ad->a.mode = VT_SHORT + 1;
+ break;
+ case TOK_MODE_SI:
+ ad->a.mode = VT_INT + 1;
+ break;
+ default:
+ tcc_warning("__mode__(%s) not supported\n", get_tok_str(tok, NULL));
+ break;
+ }
+ next();
+ skip(')');
+ break;
+ case TOK_DLLEXPORT:
+ ad->a.func_export = 1;
+ break;
+ case TOK_DLLIMPORT:
+ ad->a.func_import = 1;
+ break;
+ default:
+ if (tcc_state->warn_unsupported)
+ tcc_warning("'%s' attribute ignored", get_tok_str(t, NULL));
+ /* skip parameters */
+ if (tok == '(') {
+ int parenthesis = 0;
+ do {
+ if (tok == '(')
+ parenthesis++;
+ else if (tok == ')')
+ parenthesis--;
+ next();
+ } while (parenthesis && tok != -1);
+ }
+ break;
+ }
+ if (tok != ',')
+ break;
+ next();
+ }
+ skip(')');
+ skip(')');
+ }
+}
+
+/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */
+static void struct_decl(CType *type, int u, int tdef)
+{
+ int a, v, size, align, maxalign, c, offset, flexible;
+ int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt;
+ Sym *s, *ss, *ass, **ps;
+ AttributeDef ad;
+ CType type1, btype;
+
+ a = tok; /* save decl type */
+ next();
+ if (tok != '{') {
+ v = tok;
+ next();
+ /* struct already defined ? return it */
+ if (v < TOK_IDENT)
+ expect("struct/union/enum name");
+ s = struct_find(v);
+ if (s) {
+ if (s->type.t != a)
+ tcc_error("invalid type");
+ goto do_decl;
+ } else if (tok >= TOK_IDENT && !tdef)
+ tcc_error("unknown struct/union/enum");
+ } else {
+ v = anon_sym++;
+ }
+ type1.t = a;
+ type1.ref = NULL;
+ /* we put an undefined size for struct/union */
+ s = sym_push(v | SYM_STRUCT, &type1, 0, -1);
+ s->r = 0; /* default alignment is zero as gcc */
+ /* put struct/union/enum name in type */
+ do_decl:
+ type->t = u;
+ type->ref = s;
+
+ if (tok == '{') {
+ next();
+ if (s->c != -1)
+ tcc_error("struct/union/enum already defined");
+ /* cannot be empty */
+ c = 0;
+ /* non empty enums are not allowed */
+ if (a == TOK_ENUM) {
+ for(;;) {
+ v = tok;
+ if (v < TOK_UIDENT)
+ expect("identifier");
+ ss = sym_find(v);
+ if (ss && !local_stack)
+ tcc_error("redefinition of enumerator '%s'",
+ get_tok_str(v, NULL));
+ next();
+ if (tok == '=') {
+ next();
+ c = expr_const();
+ }
+ /* enum symbols have static storage */
+ ss = sym_push(v, &int_type, VT_CONST, c);
+ ss->type.t |= VT_STATIC;
+ if (tok != ',')
+ break;
+ next();
+ c++;
+ /* NOTE: we accept a trailing comma */
+ if (tok == '}')
+ break;
+ }
+ s->c = type_size(&int_type, &align);
+ skip('}');
+ } else {
+ maxalign = 1;
+ ps = &s->next;
+ prevbt = VT_INT;
+ bit_pos = 0;
+ offset = 0;
+ flexible = 0;
+ while (tok != '}') {
+ parse_btype(&btype, &ad);
+ while (1) {
+ if (flexible)
+ tcc_error("flexible array member '%s' not at the end of struct",
+ get_tok_str(v, NULL));
+ bit_size = -1;
+ v = 0;
+ type1 = btype;
+ if (tok != ':') {
+ type_decl(&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT);
+ if (v == 0) {
+ if ((type1.t & VT_BTYPE) != VT_STRUCT)
+ expect("identifier");
+ else {
+ int v = btype.ref->v;
+ if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) {
+ if (tcc_state->ms_extensions == 0)
+ expect("identifier");
+ }
+ }
+ }
+ if (type_size(&type1, &align) < 0) {
+ if ((a == TOK_STRUCT) && (type1.t & VT_ARRAY) && c)
+ flexible = 1;
+ else
+ tcc_error("field '%s' has incomplete type",
+ get_tok_str(v, NULL));
+ }
+ if ((type1.t & VT_BTYPE) == VT_FUNC ||
+ (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE)))
+ tcc_error("invalid type for '%s'",
+ get_tok_str(v, NULL));
+ }
+ if (tok == ':') {
+ next();
+ bit_size = expr_const();
+ /* XXX: handle v = 0 case for messages */
+ if (bit_size < 0)
+ tcc_error("negative width in bit-field '%s'",
+ get_tok_str(v, NULL));
+ if (v && bit_size == 0)
+ tcc_error("zero width for bit-field '%s'",
+ get_tok_str(v, NULL));
+ }
+ size = type_size(&type1, &align);
+ if (ad.a.aligned) {
+ if (align < ad.a.aligned)
+ align = ad.a.aligned;
+ } else if (ad.a.packed) {
+ align = 1;
+ } else if (*tcc_state->pack_stack_ptr) {
+ if (align > *tcc_state->pack_stack_ptr)
+ align = *tcc_state->pack_stack_ptr;
+ }
+ lbit_pos = 0;
+ if (bit_size >= 0) {
+ bt = type1.t & VT_BTYPE;
+ if (bt != VT_INT &&
+ bt != VT_BYTE &&
+ bt != VT_SHORT &&
+ bt != VT_BOOL &&
+ bt != VT_ENUM &&
+ bt != VT_LLONG)
+ tcc_error("bitfields must have scalar type");
+ bsize = size * 8;
+ if (bit_size > bsize) {
+ tcc_error("width of '%s' exceeds its type",
+ get_tok_str(v, NULL));
+ } else if (bit_size == bsize) {
+ /* no need for bit fields */
+ bit_pos = 0;
+ } else if (bit_size == 0) {
+ /* XXX: what to do if only padding in a
+ structure ? */
+ /* zero size: means to pad */
+ bit_pos = 0;
+ } else {
+ /* we do not have enough room ?
+ did the type change?
+ is it a union? */
+ if ((bit_pos + bit_size) > bsize ||
+ bt != prevbt || a == TOK_UNION)
+ bit_pos = 0;
+ lbit_pos = bit_pos;
+ /* XXX: handle LSB first */
+ type1.t |= VT_BITFIELD |
+ (bit_pos << VT_STRUCT_SHIFT) |
+ (bit_size << (VT_STRUCT_SHIFT + 6));
+ bit_pos += bit_size;
+ }
+ prevbt = bt;
+ } else {
+ bit_pos = 0;
+ }
+ if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) {
+ /* add new memory data only if starting
+ bit field */
+ if (lbit_pos == 0) {
+ if (a == TOK_STRUCT) {
+ c = (c + align - 1) & -align;
+ offset = c;
+ if (size > 0)
+ c += size;
+ } else {
+ offset = 0;
+ if (size > c)
+ c = size;
+ }
+ if (align > maxalign)
+ maxalign = align;
+ }
+#if 0
+ printf("add field %s offset=%d",
+ get_tok_str(v, NULL), offset);
+ if (type1.t & VT_BITFIELD) {
+ printf(" pos=%d size=%d",
+ (type1.t >> VT_STRUCT_SHIFT) & 0x3f,
+ (type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f);
+ }
+ printf("\n");
+#endif
+ }
+ if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) {
+ ass = type1.ref;
+ while ((ass = ass->next) != NULL) {
+ ss = sym_push(ass->v, &ass->type, 0, offset + ass->c);
+ *ps = ss;
+ ps = &ss->next;
+ }
+ } else if (v) {
+ ss = sym_push(v | SYM_FIELD, &type1, 0, offset);
+ *ps = ss;
+ ps = &ss->next;
+ }
+ if (tok == ';' || tok == TOK_EOF)
+ break;
+ skip(',');
+ }
+ skip(';');
+ }
+ skip('}');
+ /* store size and alignment */
+ s->c = (c + maxalign - 1) & -maxalign;
+ s->r = maxalign;
+ }
+ }
+}
+
+/* return 1 if basic type is a type size (short, long, long long) */
+ST_FUNC int is_btype_size(int bt)
+{
+ return bt == VT_SHORT || bt == VT_LONG || bt == VT_LLONG;
+}
+
+/* return 0 if no type declaration. otherwise, return the basic type
+ and skip it.
+ */
+static int parse_btype(CType *type, AttributeDef *ad)
+{
+ int t, u, bt_size, complete, type_found, typespec_found;
+ Sym *s;
+ CType type1;
+
+ memset(ad, 0, sizeof(AttributeDef));
+ complete = 0;
+ type_found = 0;
+ typespec_found = 0;
+ t = 0;
+ while(1) {
+ switch(tok) {
+ case TOK_EXTENSION:
+ /* currently, we really ignore extension */
+ next();
+ continue;
+
+ /* basic types */
+ case TOK_CHAR:
+ u = VT_BYTE;
+ basic_type:
+ next();
+ basic_type1:
+ if (complete)
+ tcc_error("too many basic types");
+ t |= u;
+ bt_size = is_btype_size (u & VT_BTYPE);
+ if (u == VT_INT || (!bt_size && !(t & VT_TYPEDEF)))
+ complete = 1;
+ typespec_found = 1;
+ break;
+ case TOK_VOID:
+ u = VT_VOID;
+ goto basic_type;
+ case TOK_SHORT:
+ u = VT_SHORT;
+ goto basic_type;
+ case TOK_INT:
+ u = VT_INT;
+ goto basic_type;
+ case TOK_LONG:
+ next();
+ if ((t & VT_BTYPE) == VT_DOUBLE) {
+#ifndef TCC_TARGET_PE
+ t = (t & ~VT_BTYPE) | VT_LDOUBLE;
+#endif
+ } else if ((t & VT_BTYPE) == VT_LONG) {
+ t = (t & ~VT_BTYPE) | VT_LLONG;
+ } else {
+ u = VT_LONG;
+ goto basic_type1;
+ }
+ break;
+#ifdef TCC_TARGET_ARM64
+ case TOK_UINT128:
+ /* GCC's __uint128_t appears in some Linux header files. Make it a
+ synonym for long double to get the size and alignment right. */
+ u = VT_LDOUBLE;
+ goto basic_type;
+#endif
+ case TOK_BOOL:
+ u = VT_BOOL;
+ goto basic_type;
+ case TOK_FLOAT:
+ u = VT_FLOAT;
+ goto basic_type;
+ case TOK_DOUBLE:
+ next();
+ if ((t & VT_BTYPE) == VT_LONG) {
+#ifdef TCC_TARGET_PE
+ t = (t & ~VT_BTYPE) | VT_DOUBLE;
+#else
+ t = (t & ~VT_BTYPE) | VT_LDOUBLE;
+#endif
+ } else {
+ u = VT_DOUBLE;
+ goto basic_type1;
+ }
+ break;
+ case TOK_ENUM:
+ struct_decl(&type1, VT_ENUM, t & (VT_TYPEDEF | VT_EXTERN));
+ basic_type2:
+ u = type1.t;
+ type->ref = type1.ref;
+ goto basic_type1;
+ case TOK_STRUCT:
+ case TOK_UNION:
+ struct_decl(&type1, VT_STRUCT, t & (VT_TYPEDEF | VT_EXTERN));
+ goto basic_type2;
+
+ /* type modifiers */
+ case TOK_CONST1:
+ case TOK_CONST2:
+ case TOK_CONST3:
+ t |= VT_CONSTANT;
+ next();
+ break;
+ case TOK_VOLATILE1:
+ case TOK_VOLATILE2:
+ case TOK_VOLATILE3:
+ t |= VT_VOLATILE;
+ next();
+ break;
+ case TOK_SIGNED1:
+ case TOK_SIGNED2:
+ case TOK_SIGNED3:
+ if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == (VT_DEFSIGN|VT_UNSIGNED))
+ tcc_error("signed and unsigned modifier");
+ typespec_found = 1;
+ t |= VT_DEFSIGN;
+ next();
+ break;
+ case TOK_REGISTER:
+ case TOK_AUTO:
+ case TOK_RESTRICT1:
+ case TOK_RESTRICT2:
+ case TOK_RESTRICT3:
+ next();
+ break;
+ case TOK_UNSIGNED:
+ if ((t & (VT_DEFSIGN|VT_UNSIGNED)) == VT_DEFSIGN)
+ tcc_error("signed and unsigned modifier");
+ t |= VT_DEFSIGN | VT_UNSIGNED;
+ next();
+ typespec_found = 1;
+ break;
+
+ /* storage */
+ case TOK_EXTERN:
+ t |= VT_EXTERN;
+ next();
+ break;
+ case TOK_STATIC:
+ t |= VT_STATIC;
+ next();
+ break;
+ case TOK_TYPEDEF:
+ t |= VT_TYPEDEF;
+ next();
+ break;
+ case TOK_INLINE1:
+ case TOK_INLINE2:
+ case TOK_INLINE3:
+ t |= VT_INLINE;
+ next();
+ break;
+
+ /* GNUC attribute */
+ case TOK_ATTRIBUTE1:
+ case TOK_ATTRIBUTE2:
+ parse_attribute(ad);
+ if (ad->a.mode) {
+ u = ad->a.mode -1;
+ t = (t & ~VT_BTYPE) | u;
+ }
+ break;
+ /* GNUC typeof */
+ case TOK_TYPEOF1:
+ case TOK_TYPEOF2:
+ case TOK_TYPEOF3:
+ next();
+ parse_expr_type(&type1);
+ /* remove all storage modifiers except typedef */
+ type1.t &= ~(VT_STORAGE&~VT_TYPEDEF);
+ goto basic_type2;
+ default:
+ if (typespec_found)
+ goto the_end;
+ s = sym_find(tok);
+ if (!s || !(s->type.t & VT_TYPEDEF))
+ goto the_end;
+ t |= (s->type.t & ~VT_TYPEDEF);
+ type->ref = s->type.ref;
+ if (s->r) {
+ /* get attributes from typedef */
+ if (0 == ad->a.aligned)
+ ad->a.aligned = s->a.aligned;
+ if (0 == ad->a.func_call)
+ ad->a.func_call = s->a.func_call;
+ ad->a.packed |= s->a.packed;
+ }
+ next();
+ typespec_found = 1;
+ break;
+ }
+ type_found = 1;
+ }
+the_end:
+ if (tcc_state->char_is_unsigned) {
+ if ((t & (VT_DEFSIGN|VT_BTYPE)) == VT_BYTE)
+ t |= VT_UNSIGNED;
+ }
+
+ /* long is never used as type */
+ if ((t & VT_BTYPE) == VT_LONG)
+#if (!defined TCC_TARGET_X86_64 && !defined TCC_TARGET_ARM64) || \
+ defined TCC_TARGET_PE
+ t = (t & ~VT_BTYPE) | VT_INT;
+#else
+ t = (t & ~VT_BTYPE) | VT_LLONG;
+#endif
+ type->t = t;
+ return type_found;
+}
+
+/* convert a function parameter type (array to pointer and function to
+ function pointer) */
+static inline void convert_parameter_type(CType *pt)
+{
+ /* remove const and volatile qualifiers (XXX: const could be used
+ to indicate a const function parameter */
+ pt->t &= ~(VT_CONSTANT | VT_VOLATILE);
+ /* array must be transformed to pointer according to ANSI C */
+ pt->t &= ~VT_ARRAY;
+ if ((pt->t & VT_BTYPE) == VT_FUNC) {
+ mk_pointer(pt);
+ }
+}
+
+ST_FUNC void parse_asm_str(CString *astr)
+{
+ skip('(');
+ /* read the string */
+ if (tok != TOK_STR)
+ expect("string constant");
+ cstr_new(astr);
+ while (tok == TOK_STR) {
+ /* XXX: add \0 handling too ? */
+ cstr_cat(astr, tokc.cstr->data);
+ next();
+ }
+ cstr_ccat(astr, '\0');
+}
+
+/* Parse an asm label and return the label
+ * Don't forget to free the CString in the caller! */
+static void asm_label_instr(CString *astr)
+{
+ next();
+ parse_asm_str(astr);
+ skip(')');
+#ifdef ASM_DEBUG
+ printf("asm_alias: \"%s\"\n", (char *)astr->data);
+#endif
+}
+
+static void post_type(CType *type, AttributeDef *ad)
+{
+ int n, l, t1, arg_size, align;
+ Sym **plast, *s, *first;
+ AttributeDef ad1;
+ CType pt;
+
+ if (tok == '(') {
+ /* function declaration */
+ next();
+ l = 0;
+ first = NULL;
+ plast = &first;
+ arg_size = 0;
+ if (tok != ')') {
+ for(;;) {
+ /* read param name and compute offset */
+ if (l != FUNC_OLD) {
+ if (!parse_btype(&pt, &ad1)) {
+ if (l) {
+ tcc_error("invalid type");
+ } else {
+ l = FUNC_OLD;
+ goto old_proto;
+ }
+ }
+ l = FUNC_NEW;
+ if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')')
+ break;
+ type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT);
+ if ((pt.t & VT_BTYPE) == VT_VOID)
+ tcc_error("parameter declared as void");
+ arg_size += (type_size(&pt, &align) + PTR_SIZE - 1) / PTR_SIZE;
+ } else {
+ old_proto:
+ n = tok;
+ if (n < TOK_UIDENT)
+ expect("identifier");
+ pt.t = VT_INT;
+ next();
+ }
+ convert_parameter_type(&pt);
+ s = sym_push(n | SYM_FIELD, &pt, 0, 0);
+ *plast = s;
+ plast = &s->next;
+ if (tok == ')')
+ break;
+ skip(',');
+ if (l == FUNC_NEW && tok == TOK_DOTS) {
+ l = FUNC_ELLIPSIS;
+ next();
+ break;
+ }
+ }
+ }
+ /* if no parameters, then old type prototype */
+ if (l == 0)
+ l = FUNC_OLD;
+ skip(')');
+ /* NOTE: const is ignored in returned type as it has a special
+ meaning in gcc / C++ */
+ type->t &= ~VT_CONSTANT;
+ /* some ancient pre-K&R C allows a function to return an array
+ and the array brackets to be put after the arguments, such
+ that "int c()[]" means something like "int[] c()" */
+ if (tok == '[') {
+ next();
+ skip(']'); /* only handle simple "[]" */
+ type->t |= VT_PTR;
+ }
+ /* we push a anonymous symbol which will contain the function prototype */
+ ad->a.func_args = arg_size;
+ s = sym_push(SYM_FIELD, type, 0, l);
+ s->a = ad->a;
+ s->next = first;
+ type->t = VT_FUNC;
+ type->ref = s;
+ } else if (tok == '[') {
+ /* array definition */
+ next();
+ if (tok == TOK_RESTRICT1)
+ next();
+ n = -1;
+ t1 = 0;
+ if (tok != ']') {
+ if (!local_stack || nocode_wanted)
+ vpushi(expr_const());
+ else gexpr();
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ n = vtop->c.i;
+ if (n < 0)
+ tcc_error("invalid array size");
+ } else {
+ if (!is_integer_btype(vtop->type.t & VT_BTYPE))
+ tcc_error("size of variable length array should be an integer");
+ t1 = VT_VLA;
+ }
+ }
+ skip(']');
+ /* parse next post type */
+ post_type(type, ad);
+ if (type->t == VT_FUNC)
+ tcc_error("declaration of an array of functions");
+ t1 |= type->t & VT_VLA;
+
+ if (t1 & VT_VLA) {
+ loc -= type_size(&int_type, &align);
+ loc &= -align;
+ n = loc;
+
+ vla_runtime_type_size(type, &align);
+ gen_op('*');
+ vset(&int_type, VT_LOCAL|VT_LVAL, n);
+ vswap();
+ vstore();
+ }
+ if (n != -1)
+ vpop();
+
+ /* we push an anonymous symbol which will contain the array
+ element type */
+ s = sym_push(SYM_FIELD, type, 0, n);
+ type->t = (t1 ? VT_VLA : VT_ARRAY) | VT_PTR;
+ type->ref = s;
+ }
+}
+
+/* Parse a type declaration (except basic type), and return the type
+ in 'type'. 'td' is a bitmask indicating which kind of type decl is
+ expected. 'type' should contain the basic type. 'ad' is the
+ attribute definition of the basic type. It can be modified by
+ type_decl().
+ */
+static void type_decl(CType *type, AttributeDef *ad, int *v, int td)
+{
+ Sym *s;
+ CType type1, *type2;
+ int qualifiers, storage;
+
+ while (tok == '*') {
+ qualifiers = 0;
+ redo:
+ next();
+ switch(tok) {
+ case TOK_CONST1:
+ case TOK_CONST2:
+ case TOK_CONST3:
+ qualifiers |= VT_CONSTANT;
+ goto redo;
+ case TOK_VOLATILE1:
+ case TOK_VOLATILE2:
+ case TOK_VOLATILE3:
+ qualifiers |= VT_VOLATILE;
+ goto redo;
+ case TOK_RESTRICT1:
+ case TOK_RESTRICT2:
+ case TOK_RESTRICT3:
+ goto redo;
+ }
+ mk_pointer(type);
+ type->t |= qualifiers;
+ }
+
+ /* XXX: clarify attribute handling */
+ if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
+ parse_attribute(ad);
+
+ /* recursive type */
+ /* XXX: incorrect if abstract type for functions (e.g. 'int ()') */
+ type1.t = 0; /* XXX: same as int */
+ if (tok == '(') {
+ next();
+ /* XXX: this is not correct to modify 'ad' at this point, but
+ the syntax is not clear */
+ if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
+ parse_attribute(ad);
+ type_decl(&type1, ad, v, td);
+ skip(')');
+ } else {
+ /* type identifier */
+ if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) {
+ *v = tok;
+ next();
+ } else {
+ if (!(td & TYPE_ABSTRACT))
+ expect("identifier");
+ *v = 0;
+ }
+ }
+ storage = type->t & VT_STORAGE;
+ type->t &= ~VT_STORAGE;
+ if (storage & VT_STATIC) {
+ int saved_nocode_wanted = nocode_wanted;
+ nocode_wanted = 1;
+ post_type(type, ad);
+ nocode_wanted = saved_nocode_wanted;
+ } else
+ post_type(type, ad);
+ type->t |= storage;
+ if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2)
+ parse_attribute(ad);
+
+ if (!type1.t)
+ return;
+ /* append type at the end of type1 */
+ type2 = &type1;
+ for(;;) {
+ s = type2->ref;
+ type2 = &s->type;
+ if (!type2->t) {
+ *type2 = *type;
+ break;
+ }
+ }
+ *type = type1;
+}
+
+/* compute the lvalue VT_LVAL_xxx needed to match type t. */
+ST_FUNC int lvalue_type(int t)
+{
+ int bt, r;
+ r = VT_LVAL;
+ bt = t & VT_BTYPE;
+ if (bt == VT_BYTE || bt == VT_BOOL)
+ r |= VT_LVAL_BYTE;
+ else if (bt == VT_SHORT)
+ r |= VT_LVAL_SHORT;
+ else
+ return r;
+ if (t & VT_UNSIGNED)
+ r |= VT_LVAL_UNSIGNED;
+ return r;
+}
+
+/* indirection with full error checking and bound check */
+ST_FUNC void indir(void)
+{
+ if ((vtop->type.t & VT_BTYPE) != VT_PTR) {
+ if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
+ return;
+ expect("pointer");
+ }
+ if ((vtop->r & VT_LVAL) && !nocode_wanted)
+ gv(RC_INT);
+ vtop->type = *pointed_type(&vtop->type);
+ /* Arrays and functions are never lvalues */
+ if (!(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_VLA)
+ && (vtop->type.t & VT_BTYPE) != VT_FUNC) {
+ vtop->r |= lvalue_type(vtop->type.t);
+ /* if bound checking, the referenced pointer must be checked */
+#ifdef CONFIG_TCC_BCHECK
+ if (tcc_state->do_bounds_check)
+ vtop->r |= VT_MUSTBOUND;
+#endif
+ }
+}
+
+/* pass a parameter to a function and do type checking and casting */
+static void gfunc_param_typed(Sym *func, Sym *arg)
+{
+ int func_type;
+ CType type;
+
+ func_type = func->c;
+ if (func_type == FUNC_OLD ||
+ (func_type == FUNC_ELLIPSIS && arg == NULL)) {
+ /* default casting : only need to convert float to double */
+ if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) {
+ type.t = VT_DOUBLE;
+ gen_cast(&type);
+ } else if (vtop->type.t & VT_BITFIELD) {
+ type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED);
+ gen_cast(&type);
+ }
+ } else if (arg == NULL) {
+ tcc_error("too many arguments to function");
+ } else {
+ type = arg->type;
+ type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
+ gen_assign_cast(&type);
+ }
+}
+
+/* parse an expression of the form '(type)' or '(expr)' and return its
+ type */
+static void parse_expr_type(CType *type)
+{
+ int n;
+ AttributeDef ad;
+
+ skip('(');
+ if (parse_btype(type, &ad)) {
+ type_decl(type, &ad, &n, TYPE_ABSTRACT);
+ } else {
+ expr_type(type);
+ }
+ skip(')');
+}
+
+static void parse_type(CType *type)
+{
+ AttributeDef ad;
+ int n;
+
+ if (!parse_btype(type, &ad)) {
+ expect("type");
+ }
+ type_decl(type, &ad, &n, TYPE_ABSTRACT);
+}
+
+static void vpush_tokc(int t)
+{
+ CType type;
+ type.t = t;
+ type.ref = 0;
+ vsetc(&type, VT_CONST, &tokc);
+}
+
+ST_FUNC void unary(void)
+{
+ int n, t, align, size, r, sizeof_caller;
+ CType type;
+ Sym *s;
+ AttributeDef ad;
+ static int in_sizeof = 0;
+
+ sizeof_caller = in_sizeof;
+ in_sizeof = 0;
+ /* XXX: GCC 2.95.3 does not generate a table although it should be
+ better here */
+ tok_next:
+ switch(tok) {
+ case TOK_EXTENSION:
+ next();
+ goto tok_next;
+ case TOK_CINT:
+ case TOK_CCHAR:
+ case TOK_LCHAR:
+ vpushi(tokc.i);
+ next();
+ break;
+ case TOK_CUINT:
+ vpush_tokc(VT_INT | VT_UNSIGNED);
+ next();
+ break;
+ case TOK_CLLONG:
+ vpush_tokc(VT_LLONG);
+ next();
+ break;
+ case TOK_CULLONG:
+ vpush_tokc(VT_LLONG | VT_UNSIGNED);
+ next();
+ break;
+ case TOK_CFLOAT:
+ vpush_tokc(VT_FLOAT);
+ next();
+ break;
+ case TOK_CDOUBLE:
+ vpush_tokc(VT_DOUBLE);
+ next();
+ break;
+ case TOK_CLDOUBLE:
+ vpush_tokc(VT_LDOUBLE);
+ next();
+ break;
+ case TOK___FUNCTION__:
+ if (!gnu_ext)
+ goto tok_identifier;
+ /* fall thru */
+ case TOK___FUNC__:
+ {
+ void *ptr;
+ int len;
+ /* special function name identifier */
+ len = strlen(funcname) + 1;
+ /* generate char[len] type */
+ type.t = VT_BYTE;
+ mk_pointer(&type);
+ type.t |= VT_ARRAY;
+ type.ref->c = len;
+ vpush_ref(&type, data_section, data_section->data_offset, len);
+ ptr = section_ptr_add(data_section, len);
+ memcpy(ptr, funcname, len);
+ next();
+ }
+ break;
+ case TOK_LSTR:
+#ifdef TCC_TARGET_PE
+ t = VT_SHORT | VT_UNSIGNED;
+#else
+ t = VT_INT;
+#endif
+ goto str_init;
+ case TOK_STR:
+ /* string parsing */
+ t = VT_BYTE;
+ str_init:
+ if (tcc_state->warn_write_strings)
+ t |= VT_CONSTANT;
+ type.t = t;
+ mk_pointer(&type);
+ type.t |= VT_ARRAY;
+ memset(&ad, 0, sizeof(AttributeDef));
+ decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, NULL, 0);
+ break;
+ case '(':
+ next();
+ /* cast ? */
+ if (parse_btype(&type, &ad)) {
+ type_decl(&type, &ad, &n, TYPE_ABSTRACT);
+ skip(')');
+ /* check ISOC99 compound literal */
+ if (tok == '{') {
+ /* data is allocated locally by default */
+ if (global_expr)
+ r = VT_CONST;
+ else
+ r = VT_LOCAL;
+ /* all except arrays are lvalues */
+ if (!(type.t & VT_ARRAY))
+ r |= lvalue_type(type.t);
+ memset(&ad, 0, sizeof(AttributeDef));
+ decl_initializer_alloc(&type, &ad, r, 1, 0, NULL, 0);
+ } else {
+ if (sizeof_caller) {
+ vpush(&type);
+ return;
+ }
+ unary();
+ gen_cast(&type);
+ }
+ } else if (tok == '{') {
+ /*
+ if (nocode_wanted)
+ tcc_error("statement expression in global scope"); */
+ /* this check breaks compilation of the linux 2.4.26 with the meesage:
+ linux/include/net/tcp.h:945: error: statement expression in global scope */
+
+ /* save all registers */
+ save_regs(0);
+ /* statement expression : we do not accept break/continue
+ inside as GCC does */
+ block(NULL, NULL, NULL, NULL, 0, 1);
+ skip(')');
+ } else {
+ gexpr();
+ skip(')');
+ }
+ break;
+ case '*':
+ next();
+ unary();
+ indir();
+ break;
+ case '&':
+ next();
+ unary();
+ /* functions names must be treated as function pointers,
+ except for unary '&' and sizeof. Since we consider that
+ functions are not lvalues, we only have to handle it
+ there and in function calls. */
+ /* arrays can also be used although they are not lvalues */
+ if ((vtop->type.t & VT_BTYPE) != VT_FUNC &&
+ !(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_LLOCAL))
+ test_lvalue();
+ mk_pointer(&vtop->type);
+ gaddrof();
+ break;
+ case '!':
+ next();
+ unary();
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ CType boolean;
+ boolean.t = VT_BOOL;
+ gen_cast(&boolean);
+ vtop->c.i = !vtop->c.i;
+ } else if ((vtop->r & VT_VALMASK) == VT_CMP)
+ vtop->c.i = vtop->c.i ^ 1;
+ else if (!nocode_wanted) {
+ save_regs(1);
+ vseti(VT_JMP, gvtst(1, 0));
+ }
+ else
+ vtop--;
+ break;
+ case '~':
+ next();
+ unary();
+ vpushi(-1);
+ gen_op('^');
+ break;
+ case '+':
+ next();
+ unary();
+ if ((vtop->type.t & VT_BTYPE) == VT_PTR)
+ tcc_error("pointer not accepted for unary plus");
+ /* In order to force cast, we add zero, except for floating point
+ where we really need an noop (otherwise -0.0 will be transformed
+ into +0.0). */
+ if (!is_float(vtop->type.t)) {
+ vpushi(0);
+ gen_op('+');
+ }
+ break;
+ case TOK_SIZEOF:
+ case TOK_ALIGNOF1:
+ case TOK_ALIGNOF2:
+ t = tok;
+ next();
+ in_sizeof++;
+ unary_type(&type); // Perform a in_sizeof = 0;
+ size = type_size(&type, &align);
+ if (t == TOK_SIZEOF) {
+ if (!(type.t & VT_VLA)) {
+ if (size < 0)
+ tcc_error("sizeof applied to an incomplete type");
+ vpushs(size);
+ } else {
+ vla_runtime_type_size(&type, &align);
+ }
+ } else {
+ vpushs(align);
+ }
+ vtop->type.t |= VT_UNSIGNED;
+ break;
+
+ case TOK_builtin_types_compatible_p:
+ {
+ CType type1, type2;
+ next();
+ skip('(');
+ parse_type(&type1);
+ skip(',');
+ parse_type(&type2);
+ skip(')');
+ type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
+ type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
+ vpushi(is_compatible_types(&type1, &type2));
+ }
+ break;
+ case TOK_builtin_constant_p:
+ {
+ int saved_nocode_wanted, res;
+ next();
+ skip('(');
+ saved_nocode_wanted = nocode_wanted;
+ nocode_wanted = 1;
+ gexpr();
+ res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+ vpop();
+ nocode_wanted = saved_nocode_wanted;
+ skip(')');
+ vpushi(res);
+ }
+ break;
+ case TOK_builtin_frame_address:
+ case TOK_builtin_return_address:
+ {
+ int tok1 = tok;
+ int level;
+ CType type;
+ next();
+ skip('(');
+ if (tok != TOK_CINT || tokc.i < 0) {
+ tcc_error("%s only takes positive integers",
+ tok1 == TOK_builtin_return_address ?
+ "__builtin_return_address" :
+ "__builtin_frame_address");
+ }
+ level = tokc.i;
+ next();
+ skip(')');
+ type.t = VT_VOID;
+ mk_pointer(&type);
+ vset(&type, VT_LOCAL, 0); /* local frame */
+ while (level--) {
+ mk_pointer(&vtop->type);
+ indir(); /* -> parent frame */
+ }
+ if (tok1 == TOK_builtin_return_address) {
+ // assume return address is just above frame pointer on stack
+ vpushi(PTR_SIZE);
+ gen_op('+');
+ mk_pointer(&vtop->type);
+ indir();
+ }
+ }
+ break;
+#ifdef TCC_TARGET_X86_64
+#ifdef TCC_TARGET_PE
+ case TOK_builtin_va_start:
+ {
+ next();
+ skip('(');
+ expr_eq();
+ skip(',');
+ expr_eq();
+ skip(')');
+ if ((vtop->r & VT_VALMASK) != VT_LOCAL)
+ tcc_error("__builtin_va_start expects a local variable");
+ vtop->r &= ~(VT_LVAL | VT_REF);
+ vtop->type = char_pointer_type;
+ vstore();
+ }
+ break;
+#else
+ case TOK_builtin_va_arg_types:
+ {
+ CType type;
+ next();
+ skip('(');
+ parse_type(&type);
+ skip(')');
+ vpushi(classify_x86_64_va_arg(&type));
+ }
+ break;
+#endif
+#endif
+
+#ifdef TCC_TARGET_ARM64
+ case TOK___va_start: {
+ if (nocode_wanted)
+ tcc_error("statement in global scope");
+ next();
+ skip('(');
+ expr_eq();
+ skip(',');
+ expr_eq();
+ skip(')');
+ //xx check types
+ gen_va_start();
+ vpushi(0);
+ vtop->type.t = VT_VOID;
+ break;
+ }
+ case TOK___va_arg: {
+ CType type;
+ if (nocode_wanted)
+ tcc_error("statement in global scope");
+ next();
+ skip('(');
+ expr_eq();
+ skip(',');
+ parse_type(&type);
+ skip(')');
+ //xx check types
+ gen_va_arg(&type);
+ vtop->type = type;
+ break;
+ }
+ case TOK___arm64_clear_cache: {
+ next();
+ skip('(');
+ expr_eq();
+ skip(',');
+ expr_eq();
+ skip(')');
+ gen_clear_cache();
+ vpushi(0);
+ vtop->type.t = VT_VOID;
+ break;
+ }
+#endif
+ /* pre operations */
+ case TOK_INC:
+ case TOK_DEC:
+ t = tok;
+ next();
+ unary();
+ inc(0, t);
+ break;
+ case '-':
+ next();
+ unary();
+ t = vtop->type.t & VT_BTYPE;
+ if (is_float(t)) {
+ /* In IEEE negate(x) isn't subtract(0,x), but rather
+ subtract(-0, x). */
+ vpush(&vtop->type);
+ if (t == VT_FLOAT)
+ vtop->c.f = -0.0f;
+ else if (t == VT_DOUBLE)
+ vtop->c.d = -0.0;
+ else
+ vtop->c.ld = -0.0;
+ } else
+ vpushi(0);
+ vswap();
+ gen_op('-');
+ break;
+ case TOK_LAND:
+ if (!gnu_ext)
+ goto tok_identifier;
+ next();
+ /* allow to take the address of a label */
+ if (tok < TOK_UIDENT)
+ expect("label identifier");
+ s = label_find(tok);
+ if (!s) {
+ s = label_push(&global_label_stack, tok, LABEL_FORWARD);
+ } else {
+ if (s->r == LABEL_DECLARED)
+ s->r = LABEL_FORWARD;
+ }
+ if (!s->type.t) {
+ s->type.t = VT_VOID;
+ mk_pointer(&s->type);
+ s->type.t |= VT_STATIC;
+ }
+ vpushsym(&s->type, s);
+ next();
+ break;
+
+ // special qnan , snan and infinity values
+ case TOK___NAN__:
+ vpush64(VT_DOUBLE, 0x7ff8000000000000ULL);
+ next();
+ break;
+ case TOK___SNAN__:
+ vpush64(VT_DOUBLE, 0x7ff0000000000001ULL);
+ next();
+ break;
+ case TOK___INF__:
+ vpush64(VT_DOUBLE, 0x7ff0000000000000ULL);
+ next();
+ break;
+
+ default:
+ tok_identifier:
+ t = tok;
+ next();
+ if (t < TOK_UIDENT)
+ expect("identifier");
+ s = sym_find(t);
+ if (!s) {
+ const char *name = get_tok_str(t, NULL);
+ if (tok != '(')
+ tcc_error("'%s' undeclared", name);
+ /* for simple function calls, we tolerate undeclared
+ external reference to int() function */
+ if (tcc_state->warn_implicit_function_declaration
+#ifdef TCC_TARGET_PE
+ /* people must be warned about using undeclared WINAPI functions
+ (which usually start with uppercase letter) */
+ || (name[0] >= 'A' && name[0] <= 'Z')
+#endif
+ )
+ tcc_warning("implicit declaration of function '%s'", name);
+ s = external_global_sym(t, &func_old_type, 0);
+ }
+ if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) ==
+ (VT_STATIC | VT_INLINE | VT_FUNC)) {
+ /* if referencing an inline function, then we generate a
+ symbol to it if not already done. It will have the
+ effect to generate code for it at the end of the
+ compilation unit. Inline function as always
+ generated in the text section. */
+ if (!s->c)
+ put_extern_sym(s, text_section, 0, 0);
+ r = VT_SYM | VT_CONST;
+ } else {
+ r = s->r;
+ }
+ vset(&s->type, r, s->c);
+ /* if forward reference, we must point to s */
+ if (vtop->r & VT_SYM) {
+ vtop->sym = s;
+ vtop->c.ptr_offset = 0;
+ }
+ break;
+ }
+
+ /* post operations */
+ while (1) {
+ if (tok == TOK_INC || tok == TOK_DEC) {
+ inc(1, tok);
+ next();
+ } else if (tok == '.' || tok == TOK_ARROW) {
+ int qualifiers;
+ /* field */
+ if (tok == TOK_ARROW)
+ indir();
+ qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE);
+ test_lvalue();
+ gaddrof();
+ next();
+ /* expect pointer on structure */
+ if ((vtop->type.t & VT_BTYPE) != VT_STRUCT)
+ expect("struct or union");
+ s = vtop->type.ref;
+ /* find field */
+ tok |= SYM_FIELD;
+ while ((s = s->next) != NULL) {
+ if (s->v == tok)
+ break;
+ }
+ if (!s)
+ tcc_error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, NULL));
+ /* add field offset to pointer */
+ vtop->type = char_pointer_type; /* change type to 'char *' */
+ vpushi(s->c);
+ gen_op('+');
+ /* change type to field type, and set to lvalue */
+ vtop->type = s->type;
+ vtop->type.t |= qualifiers;
+ /* an array is never an lvalue */
+ if (!(vtop->type.t & VT_ARRAY)) {
+ vtop->r |= lvalue_type(vtop->type.t);
+#ifdef CONFIG_TCC_BCHECK
+ /* if bound checking, the referenced pointer must be checked */
+ if (tcc_state->do_bounds_check)
+ vtop->r |= VT_MUSTBOUND;
+#endif
+ }
+ next();
+ } else if (tok == '[') {
+ next();
+ gexpr();
+ gen_op('+');
+ indir();
+ skip(']');
+ } else if (tok == '(') {
+ SValue ret;
+ Sym *sa;
+ int nb_args, ret_nregs, ret_align, regsize, variadic;
+ RegArgs args;
+
+ /* function call */
+ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) {
+ /* pointer test (no array accepted) */
+ if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) {
+ vtop->type = *pointed_type(&vtop->type);
+ if ((vtop->type.t & VT_BTYPE) != VT_FUNC)
+ goto error_func;
+ } else {
+ error_func:
+ expect("function pointer");
+ }
+ } else {
+ vtop->r &= ~VT_LVAL; /* no lvalue */
+ }
+ /* get return type */
+ s = vtop->type.ref;
+ next();
+ sa = s->next; /* first parameter */
+ nb_args = 0;
+ ret.r2 = VT_CONST;
+ /* compute first implicit argument if a structure is returned */
+ if ((s->type.t & VT_BTYPE) == VT_STRUCT) {
+ variadic = (s->c == FUNC_ELLIPSIS);
+ gfunc_sret(&s->type, variadic, &ret.type,
+ &ret_align, &regsize, &args);
+ ret_nregs = regargs_nregs(&args);
+
+ if (!ret_nregs) {
+ /* get some space for the returned structure */
+ size = type_size(&s->type, &align);
+#ifdef TCC_TARGET_ARM64
+ /* On arm64, a small struct is return in registers.
+ It is much easier to write it to memory if we know
+ that we are allowed to write some extra bytes, so
+ round the allocated space up to a power of 2: */
+ if (size < 16)
+ while (size & (size - 1))
+ size = (size | (size - 1)) + 1;
+#endif
+ loc = (loc - size) & -align;
+ ret.type = s->type;
+ ret.r = VT_LOCAL | VT_LVAL;
+ /* pass it as 'int' to avoid structure arg passing
+ problems */
+ vseti(VT_LOCAL, loc);
+ ret.c = vtop->c;
+ nb_args++;
+ }
+ } else {
+ ret_nregs = 1;
+ ret.type = s->type;
+ }
+
+ if (ret_nregs) {
+ /* return in register */
+ if (is_float(ret.type.t)) {
+ ret.r = reg_fret(ret.type.t);
+#ifdef TCC_TARGET_X86_64
+ if ((ret.type.t & VT_BTYPE) == VT_QFLOAT)
+ ret.r2 = REG_QRET;
+#endif
+ } else {
+#ifndef TCC_TARGET_ARM64
+#ifdef TCC_TARGET_X86_64
+ if ((ret.type.t & VT_BTYPE) == VT_QLONG)
+#else
+ if ((ret.type.t & VT_BTYPE) == VT_LLONG)
+#endif
+ ret.r2 = REG_LRET;
+#endif
+ ret.r = REG_IRET;
+ }
+ ret.c.i = 0;
+ }
+ if (tok != ')') {
+ for(;;) {
+ expr_eq();
+ gfunc_param_typed(s, sa);
+ nb_args++;
+ if (sa)
+ sa = sa->next;
+ if (tok == ')')
+ break;
+ skip(',');
+ }
+ }
+ if (sa)
+ tcc_error("too few arguments to function");
+ skip(')');
+ if (!nocode_wanted) {
+ gfunc_call(nb_args);
+ } else {
+ vtop -= (nb_args + 1);
+ }
+
+ /* return value */
+ for (r = ret.r + ret_nregs + !ret_nregs; r-- > ret.r;) {
+ vsetc(&ret.type, r, &ret.c);
+ vtop->r2 = ret.r2; /* Loop only happens when r2 is VT_CONST */
+ }
+
+ /* handle packed struct return */
+ if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) {
+ int addr, offset;
+#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE)
+ int i;
+#endif
+
+
+ size = type_size(&s->type, &align);
+ /* We're writing whole regs often, make sure there's enough
+ space. Assume register size is power of 2. */
+ if (regsize > align)
+ align = regsize;
+ loc = (loc - size) & -align;
+ addr = loc;
+#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE)
+ for (i=0; i<REG_ARGS_MAX; i++) {
+ offset = args.ireg[i];
+
+ if (offset == -1)
+ break;
+
+ ret.type.t = VT_LLONG;
+ vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
+ vsetc(&ret.type, i ? REG_LRET : REG_IRET, &ret.c);
+ vstore();
+ vtop--;
+ vtop--;
+ }
+ for (i=0; i<REG_ARGS_MAX; i++) {
+ offset = args.freg[i];
+
+ if (offset == -1)
+ break;
+
+ ret.type.t = VT_DOUBLE;
+ vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
+ vsetc(&ret.type, i ? REG_QRET : REG_FRET, &ret.c);
+ vstore();
+ vtop--;
+ vtop--;
+ }
+#else
+ offset = 0;
+ for (;;) {
+ vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset);
+ vswap();
+ vstore();
+ vtop--;
+ if (--ret_nregs == 0)
+ break;
+ offset += regsize;
+ }
+#endif
+ vset(&s->type, VT_LOCAL | VT_LVAL, addr);
+ }
+ } else {
+ break;
+ }
+ }
+}
+
+ST_FUNC void expr_prod(void)
+{
+ int t;
+
+ unary();
+ while (tok == '*' || tok == '/' || tok == '%') {
+ t = tok;
+ next();
+ unary();
+ gen_op(t);
+ }
+}
+
+ST_FUNC void expr_sum(void)
+{
+ int t;
+
+ expr_prod();
+ while (tok == '+' || tok == '-') {
+ t = tok;
+ next();
+ expr_prod();
+ gen_op(t);
+ }
+}
+
+static void expr_shift(void)
+{
+ int t;
+
+ expr_sum();
+ while (tok == TOK_SHL || tok == TOK_SAR) {
+ t = tok;
+ next();
+ expr_sum();
+ gen_op(t);
+ }
+}
+
+static void expr_cmp(void)
+{
+ int t;
+
+ expr_shift();
+ while ((tok >= TOK_ULE && tok <= TOK_GT) ||
+ tok == TOK_ULT || tok == TOK_UGE) {
+ t = tok;
+ next();
+ expr_shift();
+ gen_op(t);
+ }
+}
+
+static void expr_cmpeq(void)
+{
+ int t;
+
+ expr_cmp();
+ while (tok == TOK_EQ || tok == TOK_NE) {
+ t = tok;
+ next();
+ expr_cmp();
+ gen_op(t);
+ }
+}
+
+static void expr_and(void)
+{
+ expr_cmpeq();
+ while (tok == '&') {
+ next();
+ expr_cmpeq();
+ gen_op('&');
+ }
+}
+
+static void expr_xor(void)
+{
+ expr_and();
+ while (tok == '^') {
+ next();
+ expr_and();
+ gen_op('^');
+ }
+}
+
+static void expr_or(void)
+{
+ expr_xor();
+ while (tok == '|') {
+ next();
+ expr_xor();
+ gen_op('|');
+ }
+}
+
+/* XXX: fix this mess */
+static void expr_land_const(void)
+{
+ expr_or();
+ while (tok == TOK_LAND) {
+ next();
+ expr_or();
+ gen_op(TOK_LAND);
+ }
+}
+
+/* XXX: fix this mess */
+static void expr_lor_const(void)
+{
+ expr_land_const();
+ while (tok == TOK_LOR) {
+ next();
+ expr_land_const();
+ gen_op(TOK_LOR);
+ }
+}
+
+/* only used if non constant */
+static void expr_land(void)
+{
+ int t;
+
+ expr_or();
+ if (tok == TOK_LAND) {
+ t = 0;
+ save_regs(1);
+ for(;;) {
+ t = gvtst(1, t);
+ if (tok != TOK_LAND) {
+ vseti(VT_JMPI, t);
+ break;
+ }
+ next();
+ expr_or();
+ }
+ }
+}
+
+static void expr_lor(void)
+{
+ int t;
+
+ expr_land();
+ if (tok == TOK_LOR) {
+ t = 0;
+ save_regs(1);
+ for(;;) {
+ t = gvtst(0, t);
+ if (tok != TOK_LOR) {
+ vseti(VT_JMP, t);
+ break;
+ }
+ next();
+ expr_land();
+ }
+ }
+}
+
+/* XXX: better constant handling */
+static void expr_cond(void)
+{
+ int tt, u, r1, r2, rc, t1, t2, bt1, bt2;
+ SValue sv;
+ CType type, type1, type2;
+
+ if (const_wanted) {
+ expr_lor_const();
+ if (tok == '?') {
+ CType boolean;
+ int c;
+ boolean.t = VT_BOOL;
+ vdup();
+ gen_cast(&boolean);
+ c = vtop->c.i;
+ vpop();
+ next();
+ if (tok != ':' || !gnu_ext) {
+ vpop();
+ gexpr();
+ }
+ if (!c)
+ vpop();
+ skip(':');
+ expr_cond();
+ if (c)
+ vpop();
+ }
+ } else {
+ expr_lor();
+ if (tok == '?') {
+ next();
+ if (vtop != vstack) {
+ /* needed to avoid having different registers saved in
+ each branch */
+ if (is_float(vtop->type.t)) {
+ rc = RC_FLOAT;
+#ifdef TCC_TARGET_X86_64
+ if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+ rc = RC_ST0;
+ }
+#endif
+ }
+ else
+ rc = RC_INT;
+ gv(rc);
+ save_regs(1);
+ }
+ if (tok == ':' && gnu_ext) {
+ gv_dup();
+ tt = gvtst(1, 0);
+ } else {
+ tt = gvtst(1, 0);
+ gexpr();
+ }
+ type1 = vtop->type;
+ sv = *vtop; /* save value to handle it later */
+ vtop--; /* no vpop so that FP stack is not flushed */
+ skip(':');
+ u = gjmp(0);
+ gsym(tt);
+ expr_cond();
+ type2 = vtop->type;
+
+ t1 = type1.t;
+ bt1 = t1 & VT_BTYPE;
+ t2 = type2.t;
+ bt2 = t2 & VT_BTYPE;
+ /* cast operands to correct type according to ISOC rules */
+ if (is_float(bt1) || is_float(bt2)) {
+ if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) {
+ type.t = VT_LDOUBLE;
+ } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) {
+ type.t = VT_DOUBLE;
+ } else {
+ type.t = VT_FLOAT;
+ }
+ } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) {
+ /* cast to biggest op */
+ type.t = VT_LLONG;
+ /* convert to unsigned if it does not fit in a long long */
+ if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) ||
+ (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED))
+ type.t |= VT_UNSIGNED;
+ } else if (bt1 == VT_PTR || bt2 == VT_PTR) {
+ /* If one is a null ptr constant the result type
+ is the other. */
+ if (is_null_pointer (vtop))
+ type = type1;
+ else if (is_null_pointer (&sv))
+ type = type2;
+ /* XXX: test pointer compatibility, C99 has more elaborate
+ rules here. */
+ else
+ type = type1;
+ } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) {
+ /* XXX: test function pointer compatibility */
+ type = bt1 == VT_FUNC ? type1 : type2;
+ } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) {
+ /* XXX: test structure compatibility */
+ type = bt1 == VT_STRUCT ? type1 : type2;
+ } else if (bt1 == VT_VOID || bt2 == VT_VOID) {
+ /* NOTE: as an extension, we accept void on only one side */
+ type.t = VT_VOID;
+ } else {
+ /* integer operations */
+ type.t = VT_INT;
+ /* convert to unsigned if it does not fit in an integer */
+ if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) ||
+ (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED))
+ type.t |= VT_UNSIGNED;
+ }
+
+ /* now we convert second operand */
+ gen_cast(&type);
+ if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
+ gaddrof();
+ rc = RC_INT;
+ if (is_float(type.t)) {
+ rc = RC_FLOAT;
+#ifdef TCC_TARGET_X86_64
+ if ((type.t & VT_BTYPE) == VT_LDOUBLE) {
+ rc = RC_ST0;
+ }
+#endif
+ } else if ((type.t & VT_BTYPE) == VT_LLONG) {
+ /* for long longs, we use fixed registers to avoid having
+ to handle a complicated move */
+ rc = RC_IRET;
+ }
+
+ r2 = gv(rc);
+ /* this is horrible, but we must also convert first
+ operand */
+ tt = gjmp(0);
+ gsym(u);
+ /* put again first value and cast it */
+ *vtop = sv;
+ gen_cast(&type);
+ if (VT_STRUCT == (vtop->type.t & VT_BTYPE))
+ gaddrof();
+ r1 = gv(rc);
+ move_reg(r2, r1, type.t);
+ vtop->r = r2;
+ gsym(tt);
+ }
+ }
+}
+
+static void expr_eq(void)
+{
+ int t;
+
+ expr_cond();
+ if (tok == '=' ||
+ (tok >= TOK_A_MOD && tok <= TOK_A_DIV) ||
+ tok == TOK_A_XOR || tok == TOK_A_OR ||
+ tok == TOK_A_SHL || tok == TOK_A_SAR) {
+ test_lvalue();
+ t = tok;
+ next();
+ if (t == '=') {
+ expr_eq();
+ } else {
+ vdup();
+ expr_eq();
+ gen_op(t & 0x7f);
+ }
+ vstore();
+ }
+}
+
+ST_FUNC void gexpr(void)
+{
+ while (1) {
+ expr_eq();
+ if (tok != ',')
+ break;
+ vpop();
+ next();
+ }
+}
+
+/* parse an expression and return its type without any side effect. */
+static void expr_type(CType *type)
+{
+ int saved_nocode_wanted;
+
+ saved_nocode_wanted = nocode_wanted;
+ nocode_wanted = 1;
+ gexpr();
+ *type = vtop->type;
+ vpop();
+ nocode_wanted = saved_nocode_wanted;
+}
+
+/* parse a unary expression and return its type without any side
+ effect. */
+static void unary_type(CType *type)
+{
+ int a;
+
+ a = nocode_wanted;
+ nocode_wanted = 1;
+ unary();
+ *type = vtop->type;
+ vpop();
+ nocode_wanted = a;
+}
+
+/* parse a constant expression and return value in vtop. */
+static void expr_const1(void)
+{
+ int a;
+ a = const_wanted;
+ const_wanted = 1;
+ expr_cond();
+ const_wanted = a;
+}
+
+/* parse an integer constant and return its value. */
+ST_FUNC int expr_const(void)
+{
+ int c;
+ expr_const1();
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST)
+ expect("constant expression");
+ c = vtop->c.i;
+ vpop();
+ return c;
+}
+
+/* return the label token if current token is a label, otherwise
+ return zero */
+static int is_label(void)
+{
+ int last_tok;
+
+ /* fast test first */
+ if (tok < TOK_UIDENT)
+ return 0;
+ /* no need to save tokc because tok is an identifier */
+ last_tok = tok;
+ next();
+ if (tok == ':') {
+ next();
+ return last_tok;
+ } else {
+ unget_tok(last_tok);
+ return 0;
+ }
+}
+
+static void label_or_decl(int l)
+{
+ int last_tok;
+
+ /* fast test first */
+ if (tok >= TOK_UIDENT)
+ {
+ /* no need to save tokc because tok is an identifier */
+ last_tok = tok;
+ next();
+ if (tok == ':') {
+ unget_tok(last_tok);
+ return;
+ }
+ unget_tok(last_tok);
+ }
+ decl(l);
+}
+
+static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
+ int case_reg, int is_expr)
+{
+ int a, b, c, d;
+ Sym *s, *frame_bottom;
+
+ /* generate line number info */
+ if (tcc_state->do_debug &&
+ (last_line_num != file->line_num || last_ind != ind)) {
+ put_stabn(N_SLINE, 0, file->line_num, ind - func_ind);
+ last_ind = ind;
+ last_line_num = file->line_num;
+ }
+
+ if (is_expr) {
+ /* default return value is (void) */
+ vpushi(0);
+ vtop->type.t = VT_VOID;
+ }
+
+ if (tok == TOK_IF) {
+ /* if test */
+ next();
+ skip('(');
+ gexpr();
+ skip(')');
+ a = gvtst(1, 0);
+ block(bsym, csym, case_sym, def_sym, case_reg, 0);
+ c = tok;
+ if (c == TOK_ELSE) {
+ next();
+ d = gjmp(0);
+ gsym(a);
+ block(bsym, csym, case_sym, def_sym, case_reg, 0);
+ gsym(d); /* patch else jmp */
+ } else
+ gsym(a);
+ } else if (tok == TOK_WHILE) {
+ next();
+ d = ind;
+ vla_sp_restore();
+ skip('(');
+ gexpr();
+ skip(')');
+ a = gvtst(1, 0);
+ b = 0;
+ block(&a, &b, case_sym, def_sym, case_reg, 0);
+ gjmp_addr(d);
+ gsym(a);
+ gsym_addr(b, d);
+ } else if (tok == '{') {
+ Sym *llabel;
+ int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope;
+
+ next();
+ /* record local declaration stack position */
+ s = local_stack;
+ frame_bottom = sym_push2(&local_stack, SYM_FIELD, 0, 0);
+ frame_bottom->next = scope_stack_bottom;
+ scope_stack_bottom = frame_bottom;
+ llabel = local_label_stack;
+
+ /* handle local labels declarations */
+ if (tok == TOK_LABEL) {
+ next();
+ for(;;) {
+ if (tok < TOK_UIDENT)
+ expect("label identifier");
+ label_push(&local_label_stack, tok, LABEL_DECLARED);
+ next();
+ if (tok == ',') {
+ next();
+ } else {
+ skip(';');
+ break;
+ }
+ }
+ }
+ while (tok != '}') {
+ label_or_decl(VT_LOCAL);
+ if (tok != '}') {
+ if (is_expr)
+ vpop();
+ block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
+ }
+ }
+ /* pop locally defined labels */
+ label_pop(&local_label_stack, llabel);
+ if(is_expr) {
+ /* XXX: this solution makes only valgrind happy...
+ triggered by gcc.c-torture/execute/20000917-1.c */
+ Sym *p;
+ switch(vtop->type.t & VT_BTYPE) {
+ /* case VT_PTR: */
+ /* this breaks a compilation of the linux kernel v2.4.26 */
+ /* pmd_t *new = ({ __asm__ __volatile__("ud2\n") ; ((pmd_t *)1); }); */
+ /* Look a commit a80acab: Display error on statement expressions with complex return type */
+ /* A pointer is not a complex return type */
+ case VT_STRUCT:
+ case VT_ENUM:
+ case VT_FUNC:
+ for(p=vtop->type.ref;p;p=p->prev)
+ if(p->prev==s)
+ tcc_error("unsupported expression type");
+ }
+ }
+ /* pop locally defined symbols */
+ scope_stack_bottom = scope_stack_bottom->next;
+ sym_pop(&local_stack, s);
+
+ /* Pop VLA frames and restore stack pointer if required */
+ if (vlas_in_scope > saved_vlas_in_scope) {
+ vla_sp_loc = saved_vlas_in_scope ? block_vla_sp_loc : vla_sp_root_loc;
+ vla_sp_restore();
+ }
+ vlas_in_scope = saved_vlas_in_scope;
+
+ next();
+ } else if (tok == TOK_RETURN) {
+ next();
+ if (tok != ';') {
+ gexpr();
+ gen_assign_cast(&func_vt);
+#ifdef TCC_TARGET_ARM64
+ // Perhaps it would be better to use this for all backends:
+ greturn();
+#else
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+ CType type, ret_type;
+ int ret_align, ret_nregs, regsize;
+ RegArgs args;
+
+ gfunc_sret(&func_vt, func_var, &ret_type,
+ &ret_align, &regsize, &args);
+ ret_nregs = regargs_nregs(&args);
+ if (0 == ret_nregs) {
+ /* if returning structure, must copy it to implicit
+ first pointer arg location */
+ type = func_vt;
+ mk_pointer(&type);
+ vset(&type, VT_LOCAL | VT_LVAL, func_vc);
+ indir();
+ vswap();
+ /* copy structure value to pointer */
+ vstore();
+ } else {
+ /* returning structure packed into registers */
+ int r, size, addr, align;
+#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE)
+ int i;
+#endif
+ size = type_size(&func_vt,&align);
+ if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1)))
+ && (align & (ret_align-1))) {
+ loc = (loc - size) & -ret_align;
+ addr = loc;
+ type = func_vt;
+ vset(&type, VT_LOCAL | VT_LVAL, addr);
+ vswap();
+ vstore();
+ vpop();
+ vset(&ret_type, VT_LOCAL | VT_LVAL, addr);
+ }
+ vtop->type = ret_type;
+#if defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_PE)
+ for (i=0; i<REG_ARGS_MAX; i++) {
+ int off = args.ireg[i];
+
+ if (off == -1)
+ break;
+
+ r = i ? RC_LRET : RC_IRET;
+
+ vdup();
+ vtop->c.i += off;
+ vtop->type.t = VT_LLONG;
+ gv(r);
+ vpop();
+ }
+ for (i=0; i<REG_ARGS_MAX; i++) {
+ int off = args.freg[i];
+
+ if (off == -1)
+ break;
+
+ /* We assume that when a structure is returned in multiple
+ registers, their classes are consecutive values of the
+ suite s(n) = 2^n */
+ r = rc_fret(ret_type.t) << i;
+
+ vdup();
+ vtop->c.i += off;
+ vtop->type.t = VT_DOUBLE;
+ gv(r);
+ vpop();
+ }
+#else
+ if (is_float(ret_type.t))
+ r = rc_fret(ret_type.t);
+ else
+ r = RC_IRET;
+
+ for (;;) {
+ gv(r);
+ if (--ret_nregs == 0)
+ break;
+ /* We assume that when a structure is returned in multiple
+ registers, their classes are consecutive values of the
+ suite s(n) = 2^n */
+ r <<= 1;
+ vtop->c.i += regsize;
+ vtop->r = VT_LOCAL | VT_LVAL;
+ }
+#endif
+ }
+ } else if (is_float(func_vt.t)) {
+ gv(rc_fret(func_vt.t));
+ } else {
+ gv(RC_IRET);
+ }
+#endif
+ vtop--; /* NOT vpop() because on x86 it would flush the fp stack */
+ }
+ skip(';');
+ rsym = gjmp(rsym); /* jmp */
+ } else if (tok == TOK_BREAK) {
+ /* compute jump */
+ if (!bsym)
+ tcc_error("cannot break");
+ *bsym = gjmp(*bsym);
+ next();
+ skip(';');
+ } else if (tok == TOK_CONTINUE) {
+ /* compute jump */
+ if (!csym)
+ tcc_error("cannot continue");
+ vla_sp_restore_root();
+ *csym = gjmp(*csym);
+ next();
+ skip(';');
+ } else if (tok == TOK_FOR) {
+ int e;
+ next();
+ skip('(');
+ s = local_stack;
+ frame_bottom = sym_push2(&local_stack, SYM_FIELD, 0, 0);
+ frame_bottom->next = scope_stack_bottom;
+ scope_stack_bottom = frame_bottom;
+ if (tok != ';') {
+ /* c99 for-loop init decl? */
+ if (!decl0(VT_LOCAL, 1)) {
+ /* no, regular for-loop init expr */
+ gexpr();
+ vpop();
+ }
+ }
+ skip(';');
+ d = ind;
+ c = ind;
+ vla_sp_restore();
+ a = 0;
+ b = 0;
+ if (tok != ';') {
+ gexpr();
+ a = gvtst(1, 0);
+ }
+ skip(';');
+ if (tok != ')') {
+ e = gjmp(0);
+ c = ind;
+ vla_sp_restore();
+ gexpr();
+ vpop();
+ gjmp_addr(d);
+ gsym(e);
+ }
+ skip(')');
+ block(&a, &b, case_sym, def_sym, case_reg, 0);
+ gjmp_addr(c);
+ gsym(a);
+ gsym_addr(b, c);
+ scope_stack_bottom = scope_stack_bottom->next;
+ sym_pop(&local_stack, s);
+ } else
+ if (tok == TOK_DO) {
+ next();
+ a = 0;
+ b = 0;
+ d = ind;
+ vla_sp_restore();
+ block(&a, &b, case_sym, def_sym, case_reg, 0);
+ skip(TOK_WHILE);
+ skip('(');
+ gsym(b);
+ gexpr();
+ c = gvtst(0, 0);
+ gsym_addr(c, d);
+ skip(')');
+ gsym(a);
+ skip(';');
+ } else
+ if (tok == TOK_SWITCH) {
+ next();
+ skip('(');
+ gexpr();
+ /* XXX: other types than integer */
+ case_reg = gv(RC_INT);
+ vpop();
+ skip(')');
+ a = 0;
+ b = gjmp(0); /* jump to first case */
+ c = 0;
+ block(&a, csym, &b, &c, case_reg, 0);
+ /* if no default, jmp after switch */
+ if (c == 0)
+ c = ind;
+ /* default label */
+ gsym_addr(b, c);
+ /* break label */
+ gsym(a);
+ } else
+ if (tok == TOK_CASE) {
+ int v1, v2;
+ if (!case_sym)
+ expect("switch");
+ next();
+ v1 = expr_const();
+ v2 = v1;
+ if (gnu_ext && tok == TOK_DOTS) {
+ next();
+ v2 = expr_const();
+ if (v2 < v1)
+ tcc_warning("empty case range");
+ }
+ /* since a case is like a label, we must skip it with a jmp */
+ b = gjmp(0);
+ gsym(*case_sym);
+ vseti(case_reg, 0);
+ vdup();
+ vpushi(v1);
+ if (v1 == v2) {
+ gen_op(TOK_EQ);
+ *case_sym = gtst(1, 0);
+ } else {
+ gen_op(TOK_GE);
+ *case_sym = gtst(1, 0);
+ vseti(case_reg, 0);
+ vpushi(v2);
+ gen_op(TOK_LE);
+ *case_sym = gtst(1, *case_sym);
+ }
+ case_reg = gv(RC_INT);
+ vpop();
+ gsym(b);
+ skip(':');
+ is_expr = 0;
+ goto block_after_label;
+ } else
+ if (tok == TOK_DEFAULT) {
+ next();
+ skip(':');
+ if (!def_sym)
+ expect("switch");
+ if (*def_sym)
+ tcc_error("too many 'default'");
+ *def_sym = ind;
+ is_expr = 0;
+ goto block_after_label;
+ } else
+ if (tok == TOK_GOTO) {
+ next();
+ if (tok == '*' && gnu_ext) {
+ /* computed goto */
+ next();
+ gexpr();
+ if ((vtop->type.t & VT_BTYPE) != VT_PTR)
+ expect("pointer");
+ ggoto();
+ } else if (tok >= TOK_UIDENT) {
+ s = label_find(tok);
+ /* put forward definition if needed */
+ if (!s) {
+ s = label_push(&global_label_stack, tok, LABEL_FORWARD);
+ } else {
+ if (s->r == LABEL_DECLARED)
+ s->r = LABEL_FORWARD;
+ }
+ vla_sp_restore_root();
+ if (s->r & LABEL_FORWARD)
+ s->jnext = gjmp(s->jnext);
+ else
+ gjmp_addr(s->jnext);
+ next();
+ } else {
+ expect("label identifier");
+ }
+ skip(';');
+ } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
+ asm_instr();
+ } else {
+ b = is_label();
+ if (b) {
+ /* label case */
+ s = label_find(b);
+ if (s) {
+ if (s->r == LABEL_DEFINED)
+ tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
+ gsym(s->jnext);
+ s->r = LABEL_DEFINED;
+ } else {
+ s = label_push(&global_label_stack, b, LABEL_DEFINED);
+ }
+ s->jnext = ind;
+ vla_sp_restore();
+ /* we accept this, but it is a mistake */
+ block_after_label:
+ if (tok == '}') {
+ tcc_warning("deprecated use of label at end of compound statement");
+ } else {
+ if (is_expr)
+ vpop();
+ block(bsym, csym, case_sym, def_sym, case_reg, is_expr);
+ }
+ } else {
+ /* expression case */
+ if (tok != ';') {
+ if (is_expr) {
+ vpop();
+ gexpr();
+ } else {
+ gexpr();
+ vpop();
+ }
+ }
+ skip(';');
+ }
+ }
+}
+
+/* t is the array or struct type. c is the array or struct
+ address. cur_index/cur_field is the pointer to the current
+ value. 'size_only' is true if only size info is needed (only used
+ in arrays) */
+static void decl_designator(CType *type, Section *sec, unsigned long c,
+ int *cur_index, Sym **cur_field,
+ int size_only)
+{
+ Sym *s, *f;
+ int notfirst, index, index_last, align, l, nb_elems, elem_size;
+ CType type1;
+
+ notfirst = 0;
+ elem_size = 0;
+ nb_elems = 1;
+ if (gnu_ext && (l = is_label()) != 0)
+ goto struct_field;
+ while (tok == '[' || tok == '.') {
+ if (tok == '[') {
+ if (!(type->t & VT_ARRAY))
+ expect("array type");
+ s = type->ref;
+ next();
+ index = expr_const();
+ if (index < 0 || (s->c >= 0 && index >= s->c))
+ expect("invalid index");
+ if (tok == TOK_DOTS && gnu_ext) {
+ next();
+ index_last = expr_const();
+ if (index_last < 0 ||
+ (s->c >= 0 && index_last >= s->c) ||
+ index_last < index)
+ expect("invalid index");
+ } else {
+ index_last = index;
+ }
+ skip(']');
+ if (!notfirst)
+ *cur_index = index_last;
+ type = pointed_type(type);
+ elem_size = type_size(type, &align);
+ c += index * elem_size;
+ /* NOTE: we only support ranges for last designator */
+ nb_elems = index_last - index + 1;
+ if (nb_elems != 1) {
+ notfirst = 1;
+ break;
+ }
+ } else {
+ next();
+ l = tok;
+ next();
+ struct_field:
+ if ((type->t & VT_BTYPE) != VT_STRUCT)
+ expect("struct/union type");
+ s = type->ref;
+ l |= SYM_FIELD;
+ f = s->next;
+ while (f) {
+ if (f->v == l)
+ break;
+ f = f->next;
+ }
+ if (!f)
+ expect("field");
+ if (!notfirst)
+ *cur_field = f;
+ /* XXX: fix this mess by using explicit storage field */
+ type1 = f->type;
+ type1.t |= (type->t & ~VT_TYPE);
+ type = &type1;
+ c += f->c;
+ }
+ notfirst = 1;
+ }
+ if (notfirst) {
+ if (tok == '=') {
+ next();
+ } else {
+ if (!gnu_ext)
+ expect("=");
+ }
+ } else {
+ if (type->t & VT_ARRAY) {
+ index = *cur_index;
+ type = pointed_type(type);
+ c += index * type_size(type, &align);
+ } else {
+ f = *cur_field;
+ if (!f)
+ tcc_error("too many field init");
+ /* XXX: fix this mess by using explicit storage field */
+ type1 = f->type;
+ type1.t |= (type->t & ~VT_TYPE);
+ type = &type1;
+ c += f->c;
+ }
+ }
+ decl_initializer(type, sec, c, 0, size_only);
+
+ /* XXX: make it more general */
+ if (!size_only && nb_elems > 1) {
+ unsigned long c_end;
+ uint8_t *src, *dst;
+ int i;
+
+ if (!sec)
+ tcc_error("range init not supported yet for dynamic storage");
+ c_end = c + nb_elems * elem_size;
+ if (c_end > sec->data_allocated)
+ section_realloc(sec, c_end);
+ src = sec->data + c;
+ dst = src;
+ for(i = 1; i < nb_elems; i++) {
+ dst += elem_size;
+ memcpy(dst, src, elem_size);
+ }
+ }
+}
+
+#define EXPR_VAL 0
+#define EXPR_CONST 1
+#define EXPR_ANY 2
+
+/* store a value or an expression directly in global data or in local array */
+static void init_putv(CType *type, Section *sec, unsigned long c,
+ int v, int expr_type)
+{
+ int saved_global_expr, bt, bit_pos, bit_size;
+ void *ptr;
+ unsigned long long bit_mask;
+ CType dtype;
+
+ switch(expr_type) {
+ case EXPR_VAL:
+ vpushi(v);
+ break;
+ case EXPR_CONST:
+ /* compound literals must be allocated globally in this case */
+ saved_global_expr = global_expr;
+ global_expr = 1;
+ expr_const1();
+ global_expr = saved_global_expr;
+ /* NOTE: symbols are accepted */
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST)
+ tcc_error("initializer element is not constant");
+ break;
+ case EXPR_ANY:
+ expr_eq();
+ break;
+ }
+
+ dtype = *type;
+ dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */
+
+ if (sec) {
+ /* XXX: not portable */
+ /* XXX: generate error if incorrect relocation */
+ gen_assign_cast(&dtype);
+ bt = type->t & VT_BTYPE;
+ /* we'll write at most 16 bytes */
+ if (c + 16 > sec->data_allocated) {
+ section_realloc(sec, c + 16);
+ }
+ ptr = sec->data + c;
+ /* XXX: make code faster ? */
+ if (!(type->t & VT_BITFIELD)) {
+ bit_pos = 0;
+ bit_size = 32;
+ bit_mask = -1LL;
+ } else {
+ bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f;
+ bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f;
+ bit_mask = (1LL << bit_size) - 1;
+ }
+ if ((vtop->r & VT_SYM) &&
+ (bt == VT_BYTE ||
+ bt == VT_SHORT ||
+ bt == VT_DOUBLE ||
+ bt == VT_LDOUBLE ||
+ bt == VT_LLONG ||
+ (bt == VT_INT && bit_size != 32)))
+ tcc_error("initializer element is not computable at load time");
+ switch(bt) {
+ /* XXX: when cross-compiling we assume that each type has the
+ same representation on host and target, which is likely to
+ be wrong in the case of long double */
+ case VT_BOOL:
+ vtop->c.i = (vtop->c.i != 0);
+ case VT_BYTE:
+ *(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+ break;
+ case VT_SHORT:
+ *(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos;
+ break;
+ case VT_DOUBLE:
+ *(double *)ptr = vtop->c.d;
+ break;
+ case VT_LDOUBLE:
+ *(long double *)ptr = vtop->c.ld;
+ break;
+ case VT_LLONG:
+ *(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos;
+ break;
+ case VT_PTR: {
+ addr_t val = (vtop->c.ptr_offset & bit_mask) << bit_pos;
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ if (vtop->r & VT_SYM)
+ greloca(sec, vtop->sym, c, R_DATA_PTR, val);
+ else
+ *(addr_t *)ptr |= val;
+#else
+ if (vtop->r & VT_SYM)
+ greloc(sec, vtop->sym, c, R_DATA_PTR);
+ *(addr_t *)ptr |= val;
+#endif
+ break;
+ }
+ default: {
+ int val = (vtop->c.i & bit_mask) << bit_pos;
+#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64)
+ if (vtop->r & VT_SYM)
+ greloca(sec, vtop->sym, c, R_DATA_PTR, val);
+ else
+ *(int *)ptr |= val;
+#else
+ if (vtop->r & VT_SYM)
+ greloc(sec, vtop->sym, c, R_DATA_PTR);
+ *(int *)ptr |= val;
+#endif
+ break;
+ }
+ }
+ vtop--;
+ } else {
+ vset(&dtype, VT_LOCAL|VT_LVAL, c);
+ vswap();
+ vstore();
+ vpop();
+ }
+}
+
+/* put zeros for variable based init */
+static void init_putz(CType *t, Section *sec, unsigned long c, int size)
+{
+ if (sec) {
+ /* nothing to do because globals are already set to zero */
+ } else {
+ vpush_global_sym(&func_old_type, TOK_memset);
+ vseti(VT_LOCAL, c);
+#ifdef TCC_TARGET_ARM
+ vpushs(size);
+ vpushi(0);
+#else
+ vpushi(0);
+ vpushs(size);
+#endif
+ gfunc_call(3);
+ }
+}
+
+/* 't' contains the type and storage info. 'c' is the offset of the
+ object in section 'sec'. If 'sec' is NULL, it means stack based
+ allocation. 'first' is true if array '{' must be read (multi
+ dimension implicit array init handling). 'size_only' is true if
+ size only evaluation is wanted (only for arrays). */
+static void decl_initializer(CType *type, Section *sec, unsigned long c,
+ int first, int size_only)
+{
+ int index, array_length, n, no_oblock, nb, parlevel, parlevel1, i;
+ int size1, align1, expr_type;
+ Sym *s, *f;
+ CType *t1;
+
+ if (type->t & VT_VLA) {
+ int a;
+
+ /* save current stack pointer */
+ if (vlas_in_scope == 0) {
+ if (vla_sp_root_loc == -1)
+ vla_sp_root_loc = (loc -= PTR_SIZE);
+ gen_vla_sp_save(vla_sp_root_loc);
+ }
+
+ vla_runtime_type_size(type, &a);
+ gen_vla_alloc(type, a);
+ gen_vla_sp_save(c);
+ vla_sp_loc = c;
+ vlas_in_scope++;
+ } else if (type->t & VT_ARRAY) {
+ s = type->ref;
+ n = s->c;
+ array_length = 0;
+ t1 = pointed_type(type);
+ size1 = type_size(t1, &align1);
+
+ no_oblock = 1;
+ if ((first && tok != TOK_LSTR && tok != TOK_STR) ||
+ tok == '{') {
+ if (tok != '{')
+ tcc_error("character array initializer must be a literal,"
+ " optionally enclosed in braces");
+ skip('{');
+ no_oblock = 0;
+ }
+
+ /* only parse strings here if correct type (otherwise: handle
+ them as ((w)char *) expressions */
+ if ((tok == TOK_LSTR &&
+#ifdef TCC_TARGET_PE
+ (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED)
+#else
+ (t1->t & VT_BTYPE) == VT_INT
+#endif
+ ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) {
+ while (tok == TOK_STR || tok == TOK_LSTR) {
+ int cstr_len, ch;
+ CString *cstr;
+
+ cstr = tokc.cstr;
+ /* compute maximum number of chars wanted */
+ if (tok == TOK_STR)
+ cstr_len = cstr->size;
+ else
+ cstr_len = cstr->size / sizeof(nwchar_t);
+ cstr_len--;
+ nb = cstr_len;
+ if (n >= 0 && nb > (n - array_length))
+ nb = n - array_length;
+ if (!size_only) {
+ if (cstr_len > nb)
+ tcc_warning("initializer-string for array is too long");
+ /* in order to go faster for common case (char
+ string in global variable, we handle it
+ specifically */
+ if (sec && tok == TOK_STR && size1 == 1) {
+ memcpy(sec->data + c + array_length, cstr->data, nb);
+ } else {
+ for(i=0;i<nb;i++) {
+ if (tok == TOK_STR)
+ ch = ((unsigned char *)cstr->data)[i];
+ else
+ ch = ((nwchar_t *)cstr->data)[i];
+ init_putv(t1, sec, c + (array_length + i) * size1,
+ ch, EXPR_VAL);
+ }
+ }
+ }
+ array_length += nb;
+ next();
+ }
+ /* only add trailing zero if enough storage (no
+ warning in this case since it is standard) */
+ if (n < 0 || array_length < n) {
+ if (!size_only) {
+ init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL);
+ }
+ array_length++;
+ }
+ } else {
+ index = 0;
+ while (tok != '}') {
+ decl_designator(type, sec, c, &index, NULL, size_only);
+ if (n >= 0 && index >= n)
+ tcc_error("index too large");
+ /* must put zero in holes (note that doing it that way
+ ensures that it even works with designators) */
+ if (!size_only && array_length < index) {
+ init_putz(t1, sec, c + array_length * size1,
+ (index - array_length) * size1);
+ }
+ index++;
+ if (index > array_length)
+ array_length = index;
+ /* special test for multi dimensional arrays (may not
+ be strictly correct if designators are used at the
+ same time) */
+ if (index >= n && no_oblock)
+ break;
+ if (tok == '}')
+ break;
+ skip(',');
+ }
+ }
+ if (!no_oblock)
+ skip('}');
+ /* put zeros at the end */
+ if (!size_only && n >= 0 && array_length < n) {
+ init_putz(t1, sec, c + array_length * size1,
+ (n - array_length) * size1);
+ }
+ /* patch type size if needed */
+ if (n < 0)
+ s->c = array_length;
+ } else if ((type->t & VT_BTYPE) == VT_STRUCT &&
+ (sec || !first || tok == '{')) {
+
+ /* NOTE: the previous test is a specific case for automatic
+ struct/union init */
+ /* XXX: union needs only one init */
+
+ int par_count = 0;
+ if (tok == '(') {
+ AttributeDef ad1;
+ CType type1;
+ next();
+ if (tcc_state->old_struct_init_code) {
+ /* an old version of struct initialization.
+ It have a problems. But with a new version
+ linux 2.4.26 can't load ramdisk.
+ */
+ while (tok == '(') {
+ par_count++;
+ next();
+ }
+ if (!parse_btype(&type1, &ad1))
+ expect("cast");
+ type_decl(&type1, &ad1, &n, TYPE_ABSTRACT);
+ #if 0
+ if (!is_assignable_types(type, &type1))
+ tcc_error("invalid type for cast");
+ #endif
+ skip(')');
+ }
+ else
+ {
+ if (tok != '(') {
+ if (!parse_btype(&type1, &ad1))
+ expect("cast");
+ type_decl(&type1, &ad1, &n, TYPE_ABSTRACT);
+ #if 0
+ if (!is_assignable_types(type, &type1))
+ tcc_error("invalid type for cast");
+ #endif
+ skip(')');
+ } else
+ unget_tok(tok);
+ }
+ }
+
+ no_oblock = 1;
+ if (first || tok == '{') {
+ skip('{');
+ no_oblock = 0;
+ }
+ s = type->ref;
+ f = s->next;
+ array_length = 0;
+ index = 0;
+ n = s->c;
+ while (tok != '}') {
+ decl_designator(type, sec, c, NULL, &f, size_only);
+ index = f->c;
+ if (!size_only && array_length < index) {
+ init_putz(type, sec, c + array_length,
+ index - array_length);
+ }
+ index = index + type_size(&f->type, &align1);
+ if (index > array_length)
+ array_length = index;
+
+ /* gr: skip fields from same union - ugly. */
+ while (f->next) {
+ int align = 0;
+ int f_size = type_size(&f->type, &align);
+ int f_type = (f->type.t & VT_BTYPE);
+
+ ///printf("index: %2d %08x -- %2d %08x\n", f->c, f->type.t, f->next->c, f->next->type.t);
+ /* test for same offset */
+ if (f->next->c != f->c)
+ break;
+ if ((f_type == VT_STRUCT) && (f_size == 0)) {
+ /*
+ Lets assume a structure of size 0 can't be a member of the union.
+ This allow to compile the following code from a linux kernel v2.4.26
+ typedef struct { } rwlock_t;
+ struct fs_struct {
+ int count;
+ rwlock_t lock;
+ int umask;
+ };
+ struct fs_struct init_fs = { { (1) }, (rwlock_t) {}, 0022, };
+ tcc-0.9.23 can succesfully compile this version of the kernel.
+ gcc don't have problems with this code too.
+ */
+ break;
+ }
+ /* if yes, test for bitfield shift */
+ if ((f->type.t & VT_BITFIELD) && (f->next->type.t & VT_BITFIELD)) {
+ int bit_pos_1 = (f->type.t >> VT_STRUCT_SHIFT) & 0x3f;
+ int bit_pos_2 = (f->next->type.t >> VT_STRUCT_SHIFT) & 0x3f;
+ //printf("bitfield %d %d\n", bit_pos_1, bit_pos_2);
+ if (bit_pos_1 != bit_pos_2)
+ break;
+ }
+ f = f->next;
+ }
+
+ f = f->next;
+ if (no_oblock && f == NULL)
+ break;
+ if (tok == '}')
+ break;
+ skip(',');
+ }
+ /* put zeros at the end */
+ if (!size_only && array_length < n) {
+ init_putz(type, sec, c + array_length,
+ n - array_length);
+ }
+ if (!no_oblock)
+ skip('}');
+ while (par_count) {
+ skip(')');
+ par_count--;
+ }
+ } else if (tok == '{') {
+ next();
+ decl_initializer(type, sec, c, first, size_only);
+ skip('}');
+ } else if (size_only) {
+ /* just skip expression */
+ parlevel = parlevel1 = 0;
+ while ((parlevel > 0 || parlevel1 > 0 ||
+ (tok != '}' && tok != ',')) && tok != -1) {
+ if (tok == '(')
+ parlevel++;
+ else if (tok == ')') {
+ if (parlevel == 0 && parlevel1 == 0)
+ break;
+ parlevel--;
+ }
+ else if (tok == '{')
+ parlevel1++;
+ else if (tok == '}') {
+ if (parlevel == 0 && parlevel1 == 0)
+ break;
+ parlevel1--;
+ }
+ next();
+ }
+ } else {
+ /* currently, we always use constant expression for globals
+ (may change for scripting case) */
+ expr_type = EXPR_CONST;
+ if (!sec)
+ expr_type = EXPR_ANY;
+ init_putv(type, sec, c, 0, expr_type);
+ }
+}
+
+/* parse an initializer for type 't' if 'has_init' is non zero, and
+ allocate space in local or global data space ('r' is either
+ VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated
+ variable 'v' with an associated name represented by 'asm_label' of
+ scope 'scope' is declared before initializers are parsed. If 'v' is
+ zero, then a reference to the new object is put in the value stack.
+ If 'has_init' is 2, a special parsing is done to handle string
+ constants. */
+static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
+ int has_init, int v, char *asm_label,
+ int scope)
+{
+ int size, align, addr, data_offset;
+ int level;
+ ParseState saved_parse_state = {0};
+ TokenString init_str;
+ Section *sec;
+ Sym *flexible_array;
+
+ flexible_array = NULL;
+ if ((type->t & VT_BTYPE) == VT_STRUCT) {
+ Sym *field = type->ref->next;
+ if (field) {
+ while (field->next)
+ field = field->next;
+ if (field->type.t & VT_ARRAY && field->type.ref->c < 0)
+ flexible_array = field;
+ }
+ }
+
+ size = type_size(type, &align);
+ /* If unknown size, we must evaluate it before
+ evaluating initializers because
+ initializers can generate global data too
+ (e.g. string pointers or ISOC99 compound
+ literals). It also simplifies local
+ initializers handling */
+ tok_str_new(&init_str);
+ if (size < 0 || (flexible_array && has_init)) {
+ if (!has_init)
+ tcc_error("unknown type size");
+ /* get all init string */
+ if (has_init == 2) {
+ /* only get strings */
+ while (tok == TOK_STR || tok == TOK_LSTR) {
+ tok_str_add_tok(&init_str);
+ next();
+ }
+ } else {
+ level = 0;
+ while (level > 0 || (tok != ',' && tok != ';')) {
+ if (tok < 0)
+ tcc_error("unexpected end of file in initializer");
+ tok_str_add_tok(&init_str);
+ if (tok == '{')
+ level++;
+ else if (tok == '}') {
+ level--;
+ if (level <= 0) {
+ next();
+ break;
+ }
+ }
+ next();
+ }
+ }
+ tok_str_add(&init_str, -1);
+ tok_str_add(&init_str, 0);
+
+ /* compute size */
+ save_parse_state(&saved_parse_state);
+
+ begin_macro(&init_str, 0);
+ next();
+ decl_initializer(type, NULL, 0, 1, 1);
+ /* prepare second initializer parsing */
+ macro_ptr = init_str.str;
+ next();
+
+ /* if still unknown size, error */
+ size = type_size(type, &align);
+ if (size < 0)
+ tcc_error("unknown type size");
+ }
+ if (flexible_array)
+ size += flexible_array->type.ref->c * pointed_size(&flexible_array->type);
+ /* take into account specified alignment if bigger */
+ if (ad->a.aligned) {
+ if (ad->a.aligned > align)
+ align = ad->a.aligned;
+ } else if (ad->a.packed) {
+ align = 1;
+ }
+ if ((r & VT_VALMASK) == VT_LOCAL) {
+ sec = NULL;
+#ifdef CONFIG_TCC_BCHECK
+ if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
+ loc--;
+ }
+#endif
+ loc = (loc - size) & -align;
+ addr = loc;
+#ifdef CONFIG_TCC_BCHECK
+ /* handles bounds */
+ /* XXX: currently, since we do only one pass, we cannot track
+ '&' operators, so we add only arrays */
+ if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) {
+ addr_t *bounds_ptr;
+ /* add padding between regions */
+ loc--;
+ /* then add local bound info */
+ bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(addr_t));
+ bounds_ptr[0] = addr;
+ bounds_ptr[1] = size;
+ }
+#endif
+ if (v) {
+ /* local variable */
+ sym_push(v, type, r, addr);
+ } else {
+ /* push local reference */
+ vset(type, r, addr);
+ }
+ } else {
+ Sym *sym;
+
+ sym = NULL;
+ if (v && scope == VT_CONST) {
+ /* see if the symbol was already defined */
+ sym = sym_find(v);
+ if (sym) {
+ if (!is_compatible_types(&sym->type, type))
+ tcc_error("incompatible types for redefinition of '%s'",
+ get_tok_str(v, NULL));
+ if (sym->type.t & VT_EXTERN) {
+ /* if the variable is extern, it was not allocated */
+ sym->type.t &= ~VT_EXTERN;
+ /* set array size if it was omitted in extern
+ declaration */
+ if ((sym->type.t & VT_ARRAY) &&
+ sym->type.ref->c < 0 &&
+ type->ref->c >= 0)
+ sym->type.ref->c = type->ref->c;
+ } else {
+ /* we accept several definitions of the same
+ global variable. this is tricky, because we
+ must play with the SHN_COMMON type of the symbol */
+ /* XXX: should check if the variable was already
+ initialized. It is incorrect to initialized it
+ twice */
+ /* no init data, we won't add more to the symbol */
+ if (!has_init)
+ goto no_alloc;
+ }
+ }
+ }
+
+ /* allocate symbol in corresponding section */
+ sec = ad->section;
+ if (!sec) {
+ if (has_init)
+ sec = data_section;
+ else if (tcc_state->nocommon)
+ sec = bss_section;
+ }
+ if (sec) {
+ data_offset = sec->data_offset;
+ data_offset = (data_offset + align - 1) & -align;
+ addr = data_offset;
+ /* very important to increment global pointer at this time
+ because initializers themselves can create new initializers */
+ data_offset += size;
+#ifdef CONFIG_TCC_BCHECK
+ /* add padding if bound check */
+ if (tcc_state->do_bounds_check)
+ data_offset++;
+#endif
+ sec->data_offset = data_offset;
+ /* allocate section space to put the data */
+ if (sec->sh_type != SHT_NOBITS &&
+ data_offset > sec->data_allocated)
+ section_realloc(sec, data_offset);
+ /* align section if needed */
+ if (align > sec->sh_addralign)
+ sec->sh_addralign = align;
+ } else {
+ addr = 0; /* avoid warning */
+ }
+
+ if (v) {
+ if (scope != VT_CONST || !sym) {
+ sym = sym_push(v, type, r | VT_SYM, 0);
+ sym->asm_label = asm_label;
+ }
+ /* update symbol definition */
+ if (sec) {
+ put_extern_sym(sym, sec, addr, size);
+ } else {
+ ElfW(Sym) *esym;
+ /* put a common area */
+ put_extern_sym(sym, NULL, align, size);
+ /* XXX: find a nicer way */
+ esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+ esym->st_shndx = SHN_COMMON;
+ }
+ } else {
+ /* push global reference */
+ sym = get_sym_ref(type, sec, addr, size);
+ vpushsym(type, sym);
+ }
+ /* patch symbol weakness */
+ if (type->t & VT_WEAK)
+ weaken_symbol(sym);
+ apply_visibility(sym, type);
+#ifdef CONFIG_TCC_BCHECK
+ /* handles bounds now because the symbol must be defined
+ before for the relocation */
+ if (tcc_state->do_bounds_check) {
+ addr_t *bounds_ptr;
+
+ greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_PTR);
+ /* then add global bound info */
+ bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t));
+ bounds_ptr[0] = 0; /* relocated */
+ bounds_ptr[1] = size;
+ }
+#endif
+ }
+ if (has_init || (type->t & VT_VLA)) {
+ decl_initializer(type, sec, addr, 1, 0);
+ /* restore parse state if needed */
+ if (init_str.str) {
+ end_macro();
+ restore_parse_state(&saved_parse_state);
+ }
+ /* patch flexible array member size back to -1, */
+ /* for possible subsequent similar declarations */
+ if (flexible_array)
+ flexible_array->type.ref->c = -1;
+ }
+ no_alloc: ;
+}
+
+static void put_func_debug(Sym *sym)
+{
+ char buf[512];
+
+ /* stabs info */
+ /* XXX: we put here a dummy type */
+ snprintf(buf, sizeof(buf), "%s:%c1",
+ funcname, sym->type.t & VT_STATIC ? 'f' : 'F');
+ put_stabs_r(buf, N_FUN, 0, file->line_num, 0,
+ cur_text_section, sym->c);
+ /* //gr gdb wants a line at the function */
+ put_stabn(N_SLINE, 0, file->line_num, 0);
+ last_ind = 0;
+ last_line_num = 0;
+}
+
+/* parse an old style function declaration list */
+/* XXX: check multiple parameter */
+static void func_decl_list(Sym *func_sym)
+{
+ AttributeDef ad;
+ int v;
+ Sym *s;
+ CType btype, type;
+
+ /* parse each declaration */
+ while (tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF &&
+ tok != TOK_ASM1 && tok != TOK_ASM2 && tok != TOK_ASM3) {
+ if (!parse_btype(&btype, &ad))
+ expect("declaration list");
+ if (((btype.t & VT_BTYPE) == VT_ENUM ||
+ (btype.t & VT_BTYPE) == VT_STRUCT) &&
+ tok == ';') {
+ /* we accept no variable after */
+ } else {
+ for(;;) {
+ type = btype;
+ type_decl(&type, &ad, &v, TYPE_DIRECT);
+ /* find parameter in function parameter list */
+ s = func_sym->next;
+ while (s != NULL) {
+ if ((s->v & ~SYM_FIELD) == v)
+ goto found;
+ s = s->next;
+ }
+ tcc_error("declaration for parameter '%s' but no such parameter",
+ get_tok_str(v, NULL));
+ found:
+ /* check that no storage specifier except 'register' was given */
+ if (type.t & VT_STORAGE)
+ tcc_error("storage class specified for '%s'", get_tok_str(v, NULL));
+ convert_parameter_type(&type);
+ /* we can add the type (NOTE: it could be local to the function) */
+ s->type = type;
+ /* accept other parameters */
+ if (tok == ',')
+ next();
+ else
+ break;
+ }
+ }
+ skip(';');
+ }
+}
+
+/* parse a function defined by symbol 'sym' and generate its code in
+ 'cur_text_section' */
+static void gen_function(Sym *sym)
+{
+ int saved_nocode_wanted = nocode_wanted;
+
+ nocode_wanted = 0;
+ ind = cur_text_section->data_offset;
+ /* NOTE: we patch the symbol size later */
+ put_extern_sym(sym, cur_text_section, ind, 0);
+ funcname = get_tok_str(sym->v, NULL);
+ func_ind = ind;
+ /* Initialize VLA state */
+ vla_sp_loc = -1;
+ vla_sp_root_loc = -1;
+ /* put debug symbol */
+ if (tcc_state->do_debug)
+ put_func_debug(sym);
+ /* push a dummy symbol to enable local sym storage */
+ sym_push2(&local_stack, SYM_FIELD, 0, 0);
+ gfunc_prolog(&sym->type);
+#ifdef CONFIG_TCC_BCHECK
+ if (tcc_state->do_bounds_check && !strcmp(funcname, "main")) {
+ int i;
+ Sym *sym;
+ for (i = 0, sym = local_stack; i < 2; i++, sym = sym->prev) {
+ if (sym->v & SYM_FIELD || sym->prev->v & SYM_FIELD)
+ break;
+ vpush_global_sym(&func_old_type, TOK___bound_main_arg);
+ vset(&sym->type, sym->r, sym->c);
+ gfunc_call(1);
+ }
+ }
+#endif
+ rsym = 0;
+ block(NULL, NULL, NULL, NULL, 0, 0);
+ gsym(rsym);
+ gfunc_epilog();
+ cur_text_section->data_offset = ind;
+ label_pop(&global_label_stack, NULL);
+ /* reset local stack */
+ scope_stack_bottom = NULL;
+ sym_pop(&local_stack, NULL);
+ /* end of function */
+ /* patch symbol size */
+ ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size =
+ ind - func_ind;
+ /* patch symbol weakness (this definition overrules any prototype) */
+ if (sym->type.t & VT_WEAK)
+ weaken_symbol(sym);
+ apply_visibility(sym, &sym->type);
+ if (tcc_state->do_debug) {
+ put_stabn(N_FUN, 0, 0, ind - func_ind);
+ }
+ /* It's better to crash than to generate wrong code */
+ cur_text_section = NULL;
+ funcname = ""; /* for safety */
+ func_vt.t = VT_VOID; /* for safety */
+ func_var = 0; /* for safety */
+ ind = 0; /* for safety */
+ nocode_wanted = saved_nocode_wanted;
+ check_vstack();
+}
+
+ST_FUNC void gen_inline_functions(void)
+{
+ Sym *sym;
+ int inline_generated, i, ln;
+ struct InlineFunc *fn;
+
+ ln = file->line_num;
+ /* iterate while inline function are referenced */
+ for(;;) {
+ inline_generated = 0;
+ for (i = 0; i < tcc_state->nb_inline_fns; ++i) {
+ fn = tcc_state->inline_fns[i];
+ sym = fn->sym;
+ if (sym && sym->c) {
+ /* the function was used: generate its code and
+ convert it to a normal function */
+ fn->sym = NULL;
+ if (file)
+ pstrcpy(file->filename, sizeof file->filename, fn->filename);
+ sym->r = VT_SYM | VT_CONST;
+ sym->type.t &= ~VT_INLINE;
+
+ begin_macro(&fn->func_str, 0);
+ next();
+ cur_text_section = text_section;
+ gen_function(sym);
+ end_macro();
+
+ inline_generated = 1;
+ }
+ }
+ if (!inline_generated)
+ break;
+ }
+ file->line_num = ln;
+ /* free tokens of unused inline functions */
+ for (i = 0; i < tcc_state->nb_inline_fns; ++i) {
+ fn = tcc_state->inline_fns[i];
+ if (fn->sym)
+ tok_str_free(fn->func_str.str);
+ }
+ dynarray_reset(&tcc_state->inline_fns, &tcc_state->nb_inline_fns);
+}
+
+/* 'l' is VT_LOCAL or VT_CONST to define default storage type */
+static int decl0(int l, int is_for_loop_init)
+{
+ int v, has_init, r;
+ CType type, btype;
+ Sym *sym;
+ AttributeDef ad;
+
+ while (1) {
+ if (!parse_btype(&btype, &ad)) {
+ if (is_for_loop_init)
+ return 0;
+ /* skip redundant ';' */
+ /* XXX: find more elegant solution */
+ if (tok == ';') {
+ next();
+ continue;
+ }
+ if (l == VT_CONST &&
+ (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
+ /* global asm block */
+ asm_global_instr();
+ continue;
+ }
+ /* special test for old K&R protos without explicit int
+ type. Only accepted when defining global data */
+ if (l == VT_LOCAL || tok < TOK_DEFINE)
+ break;
+ btype.t = VT_INT;
+ }
+ if (((btype.t & VT_BTYPE) == VT_ENUM ||
+ (btype.t & VT_BTYPE) == VT_STRUCT) &&
+ tok == ';') {
+ if ((btype.t & VT_BTYPE) == VT_STRUCT) {
+ int v = btype.ref->v;
+ if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) >= SYM_FIRST_ANOM)
+ tcc_warning("unnamed struct/union that defines no instances");
+ }
+ next();
+ continue;
+ }
+ while (1) { /* iterate thru each declaration */
+ char *asm_label; // associated asm label
+ type = btype;
+ type_decl(&type, &ad, &v, TYPE_DIRECT);
+#if 0
+ {
+ char buf[500];
+ type_to_str(buf, sizeof(buf), t, get_tok_str(v, NULL));
+ printf("type = '%s'\n", buf);
+ }
+#endif
+ if ((type.t & VT_BTYPE) == VT_FUNC) {
+ if ((type.t & VT_STATIC) && (l == VT_LOCAL)) {
+ tcc_error("function without file scope cannot be static");
+ }
+ /* if old style function prototype, we accept a
+ declaration list */
+ sym = type.ref;
+ if (sym->c == FUNC_OLD)
+ func_decl_list(sym);
+ }
+
+ asm_label = NULL;
+ if (gnu_ext && (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) {
+ CString astr;
+
+ asm_label_instr(&astr);
+ asm_label = tcc_strdup(astr.data);
+ cstr_free(&astr);
+
+ /* parse one last attribute list, after asm label */
+ parse_attribute(&ad);
+ }
+
+ if (ad.a.weak)
+ type.t |= VT_WEAK;
+#ifdef TCC_TARGET_PE
+ if (ad.a.func_import)
+ type.t |= VT_IMPORT;
+ if (ad.a.func_export)
+ type.t |= VT_EXPORT;
+#endif
+ type.t |= ad.a.visibility << VT_VIS_SHIFT;
+
+ if (tok == '{') {
+ if (l == VT_LOCAL)
+ tcc_error("cannot use local functions");
+ if ((type.t & VT_BTYPE) != VT_FUNC)
+ expect("function definition");
+
+ /* reject abstract declarators in function definition */
+ sym = type.ref;
+ while ((sym = sym->next) != NULL)
+ if (!(sym->v & ~SYM_FIELD))
+ expect("identifier");
+
+ /* XXX: cannot do better now: convert extern line to static inline */
+ if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE))
+ type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
+
+ sym = sym_find(v);
+ if (sym) {
+ Sym *ref;
+ if ((sym->type.t & VT_BTYPE) != VT_FUNC)
+ goto func_error1;
+
+ ref = sym->type.ref;
+ if (0 == ref->a.func_proto)
+ tcc_error("redefinition of '%s'", get_tok_str(v, NULL));
+
+ /* use func_call from prototype if not defined */
+ if (ref->a.func_call != FUNC_CDECL
+ && type.ref->a.func_call == FUNC_CDECL)
+ type.ref->a.func_call = ref->a.func_call;
+
+ /* use export from prototype */
+ if (ref->a.func_export)
+ type.ref->a.func_export = 1;
+
+ /* use static from prototype */
+ if (sym->type.t & VT_STATIC)
+ type.t = (type.t & ~VT_EXTERN) | VT_STATIC;
+
+ /* If the definition has no visibility use the
+ one from prototype. */
+ if (! (type.t & VT_VIS_MASK))
+ type.t |= sym->type.t & VT_VIS_MASK;
+
+ if (!is_compatible_types(&sym->type, &type)) {
+ func_error1:
+ tcc_error("incompatible types for redefinition of '%s'",
+ get_tok_str(v, NULL));
+ }
+ type.ref->a.func_proto = 0;
+ /* if symbol is already defined, then put complete type */
+ sym->type = type;
+ } else {
+ /* put function symbol */
+ sym = global_identifier_push(v, type.t, 0);
+ sym->type.ref = type.ref;
+ }
+
+ /* static inline functions are just recorded as a kind
+ of macro. Their code will be emitted at the end of
+ the compilation unit only if they are used */
+ if ((type.t & (VT_INLINE | VT_STATIC)) ==
+ (VT_INLINE | VT_STATIC)) {
+ int block_level;
+ struct InlineFunc *fn;
+ const char *filename;
+
+ filename = file ? file->filename : "";
+ fn = tcc_malloc(sizeof *fn + strlen(filename));
+ strcpy(fn->filename, filename);
+ fn->sym = sym;
+ tok_str_new(&fn->func_str);
+
+ block_level = 0;
+ for(;;) {
+ int t;
+ if (tok == TOK_EOF)
+ tcc_error("unexpected end of file");
+ tok_str_add_tok(&fn->func_str);
+ t = tok;
+ next();
+ if (t == '{') {
+ block_level++;
+ } else if (t == '}') {
+ block_level--;
+ if (block_level == 0)
+ break;
+ }
+ }
+ tok_str_add(&fn->func_str, -1);
+ tok_str_add(&fn->func_str, 0);
+ dynarray_add((void ***)&tcc_state->inline_fns, &tcc_state->nb_inline_fns, fn);
+
+ } else {
+ /* compute text section */
+ cur_text_section = ad.section;
+ if (!cur_text_section)
+ cur_text_section = text_section;
+ sym->r = VT_SYM | VT_CONST;
+ gen_function(sym);
+ }
+ break;
+ } else {
+ if (btype.t & VT_TYPEDEF) {
+ /* save typedefed type */
+ /* XXX: test storage specifiers ? */
+ sym = sym_push(v, &type, 0, 0);
+ sym->a = ad.a;
+ sym->type.t |= VT_TYPEDEF;
+ } else {
+ r = 0;
+ if ((type.t & VT_BTYPE) == VT_FUNC) {
+ /* external function definition */
+ /* specific case for func_call attribute */
+ ad.a.func_proto = 1;
+ type.ref->a = ad.a;
+ } else if (!(type.t & VT_ARRAY)) {
+ /* not lvalue if array */
+ r |= lvalue_type(type.t);
+ }
+ has_init = (tok == '=');
+ if (has_init && (type.t & VT_VLA))
+ tcc_error("Variable length array cannot be initialized");
+ if ((btype.t & VT_EXTERN) || ((type.t & VT_BTYPE) == VT_FUNC) ||
+ ((type.t & VT_ARRAY) && (type.t & VT_STATIC) &&
+ !has_init && l == VT_CONST && type.ref->c < 0)) {
+ /* external variable or function */
+ /* NOTE: as GCC, uninitialized global static
+ arrays of null size are considered as
+ extern */
+ sym = external_sym(v, &type, r, asm_label);
+
+ if (ad.alias_target) {
+ Section tsec;
+ Elf32_Sym *esym;
+ Sym *alias_target;
+
+ alias_target = sym_find(ad.alias_target);
+ if (!alias_target || !alias_target->c)
+ tcc_error("unsupported forward __alias__ attribute");
+ esym = &((Elf32_Sym *)symtab_section->data)[alias_target->c];
+ tsec.sh_num = esym->st_shndx;
+ put_extern_sym2(sym, &tsec, esym->st_value, esym->st_size, 0);
+ }
+ } else {
+ type.t |= (btype.t & VT_STATIC); /* Retain "static". */
+ if (type.t & VT_STATIC)
+ r |= VT_CONST;
+ else
+ r |= l;
+ if (has_init)
+ next();
+ decl_initializer_alloc(&type, &ad, r, has_init, v, asm_label, l);
+ }
+ }
+ if (tok != ',') {
+ if (is_for_loop_init)
+ return 1;
+ skip(';');
+ break;
+ }
+ next();
+ }
+ ad.a.aligned = 0;
+ }
+ }
+ return 0;
+}
+
+ST_FUNC void decl(int l)
+{
+ decl0(l, 0);
+}
diff --git a/src/tcclib.h b/src/tcclib.h
new file mode 100644
index 0000000..13f2204
--- /dev/null
+++ b/src/tcclib.h
@@ -0,0 +1,80 @@
+/* Simple libc header for TCC
+ *
+ * Add any function you want from the libc there. This file is here
+ * only for your convenience so that you do not need to put the whole
+ * glibc include files on your floppy disk
+ */
+#ifndef _TCCLIB_H
+#define _TCCLIB_H
+
+#include <stddef.h>
+#include <stdarg.h>
+
+/* stdlib.h */
+void *calloc(size_t nmemb, size_t size);
+void *malloc(size_t size);
+void free(void *ptr);
+void *realloc(void *ptr, size_t size);
+int atoi(const char *nptr);
+long int strtol(const char *nptr, char **endptr, int base);
+unsigned long int strtoul(const char *nptr, char **endptr, int base);
+void exit(int);
+
+/* stdio.h */
+typedef struct __FILE FILE;
+#define EOF (-1)
+extern FILE *stdin;
+extern FILE *stdout;
+extern FILE *stderr;
+FILE *fopen(const char *path, const char *mode);
+FILE *fdopen(int fildes, const char *mode);
+FILE *freopen(const char *path, const char *mode, FILE *stream);
+int fclose(FILE *stream);
+size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
+size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);
+int fgetc(FILE *stream);
+char *fgets(char *s, int size, FILE *stream);
+int getc(FILE *stream);
+int getchar(void);
+char *gets(char *s);
+int ungetc(int c, FILE *stream);
+int fflush(FILE *stream);
+int putchar (int c);
+
+int printf(const char *format, ...);
+int fprintf(FILE *stream, const char *format, ...);
+int sprintf(char *str, const char *format, ...);
+int snprintf(char *str, size_t size, const char *format, ...);
+int asprintf(char **strp, const char *format, ...);
+int dprintf(int fd, const char *format, ...);
+int vprintf(const char *format, va_list ap);
+int vfprintf(FILE *stream, const char *format, va_list ap);
+int vsprintf(char *str, const char *format, va_list ap);
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+int vasprintf(char **strp, const char *format, va_list ap);
+int vdprintf(int fd, const char *format, va_list ap);
+
+void perror(const char *s);
+
+/* string.h */
+char *strcat(char *dest, const char *src);
+char *strchr(const char *s, int c);
+char *strrchr(const char *s, int c);
+char *strcpy(char *dest, const char *src);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memmove(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+char *strdup(const char *s);
+size_t strlen(const char *s);
+
+/* dlfcn.h */
+#define RTLD_LAZY 0x001
+#define RTLD_NOW 0x002
+#define RTLD_GLOBAL 0x100
+
+void *dlopen(const char *filename, int flag);
+const char *dlerror(void);
+void *dlsym(void *handle, char *symbol);
+int dlclose(void *handle);
+
+#endif /* _TCCLIB_H */
diff --git a/src/tccpe.c b/src/tccpe.c
new file mode 100644
index 0000000..479d084
--- /dev/null
+++ b/src/tccpe.c
@@ -0,0 +1,1890 @@
+/*
+ * TCCPE.C - PE file output for the Tiny C Compiler
+ *
+ * Copyright (c) 2005-2007 grischka
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+#ifndef _WIN32
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+#ifndef MAX_PATH
+#define MAX_PATH 260
+#endif
+
+#define PE_MERGE_DATA
+/* #define PE_PRINT_SECTIONS */
+
+#ifdef TCC_TARGET_X86_64
+# define ADDR3264 ULONGLONG
+# define REL_TYPE_DIRECT R_X86_64_64
+# define R_XXX_THUNKFIX R_X86_64_PC32
+# define R_XXX_RELATIVE R_X86_64_RELATIVE
+# define IMAGE_FILE_MACHINE 0x8664
+# define RSRC_RELTYPE 3
+
+#elif defined TCC_TARGET_ARM
+# define ADDR3264 DWORD
+# define REL_TYPE_DIRECT R_ARM_ABS32
+# define R_XXX_THUNKFIX R_ARM_ABS32
+# define R_XXX_RELATIVE R_ARM_RELATIVE
+# define IMAGE_FILE_MACHINE 0x01C0
+# define RSRC_RELTYPE 7 /* ??? (not tested) */
+
+#elif defined TCC_TARGET_I386
+# define ADDR3264 DWORD
+# define REL_TYPE_DIRECT R_386_32
+# define R_XXX_THUNKFIX R_386_32
+# define R_XXX_RELATIVE R_386_RELATIVE
+# define IMAGE_FILE_MACHINE 0x014C
+# define RSRC_RELTYPE 7 /* DIR32NB */
+
+#endif
+
+#ifdef _WIN32
+void dbg_printf (const char *fmt, ...)
+{
+ char buffer[4000];
+ va_list arg;
+ int x;
+ va_start(arg, fmt);
+ x = vsprintf (buffer, fmt, arg);
+ strcpy(buffer+x, "\n");
+ OutputDebugString(buffer);
+}
+#endif
+
+/* ----------------------------------------------------------- */
+#ifndef IMAGE_NT_SIGNATURE
+/* ----------------------------------------------------------- */
+/* definitions below are from winnt.h */
+
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned int DWORD;
+typedef unsigned long long ULONGLONG;
+#pragma pack(push, 1)
+
+typedef struct _IMAGE_DOS_HEADER { /* DOS .EXE header */
+ WORD e_magic; /* Magic number */
+ WORD e_cblp; /* Bytes on last page of file */
+ WORD e_cp; /* Pages in file */
+ WORD e_crlc; /* Relocations */
+ WORD e_cparhdr; /* Size of header in paragraphs */
+ WORD e_minalloc; /* Minimum extra paragraphs needed */
+ WORD e_maxalloc; /* Maximum extra paragraphs needed */
+ WORD e_ss; /* Initial (relative) SS value */
+ WORD e_sp; /* Initial SP value */
+ WORD e_csum; /* Checksum */
+ WORD e_ip; /* Initial IP value */
+ WORD e_cs; /* Initial (relative) CS value */
+ WORD e_lfarlc; /* File address of relocation table */
+ WORD e_ovno; /* Overlay number */
+ WORD e_res[4]; /* Reserved words */
+ WORD e_oemid; /* OEM identifier (for e_oeminfo) */
+ WORD e_oeminfo; /* OEM information; e_oemid specific */
+ WORD e_res2[10]; /* Reserved words */
+ DWORD e_lfanew; /* File address of new exe header */
+} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
+
+#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */
+#define SIZE_OF_NT_SIGNATURE 4
+
+typedef struct _IMAGE_FILE_HEADER {
+ WORD Machine;
+ WORD NumberOfSections;
+ DWORD TimeDateStamp;
+ DWORD PointerToSymbolTable;
+ DWORD NumberOfSymbols;
+ WORD SizeOfOptionalHeader;
+ WORD Characteristics;
+} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
+
+
+#define IMAGE_SIZEOF_FILE_HEADER 20
+
+typedef struct _IMAGE_DATA_DIRECTORY {
+ DWORD VirtualAddress;
+ DWORD Size;
+} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
+
+
+typedef struct _IMAGE_OPTIONAL_HEADER {
+ /* Standard fields. */
+ WORD Magic;
+ BYTE MajorLinkerVersion;
+ BYTE MinorLinkerVersion;
+ DWORD SizeOfCode;
+ DWORD SizeOfInitializedData;
+ DWORD SizeOfUninitializedData;
+ DWORD AddressOfEntryPoint;
+ DWORD BaseOfCode;
+#ifndef TCC_TARGET_X86_64
+ DWORD BaseOfData;
+#endif
+ /* NT additional fields. */
+ ADDR3264 ImageBase;
+ DWORD SectionAlignment;
+ DWORD FileAlignment;
+ WORD MajorOperatingSystemVersion;
+ WORD MinorOperatingSystemVersion;
+ WORD MajorImageVersion;
+ WORD MinorImageVersion;
+ WORD MajorSubsystemVersion;
+ WORD MinorSubsystemVersion;
+ DWORD Win32VersionValue;
+ DWORD SizeOfImage;
+ DWORD SizeOfHeaders;
+ DWORD CheckSum;
+ WORD Subsystem;
+ WORD DllCharacteristics;
+ ADDR3264 SizeOfStackReserve;
+ ADDR3264 SizeOfStackCommit;
+ ADDR3264 SizeOfHeapReserve;
+ ADDR3264 SizeOfHeapCommit;
+ DWORD LoaderFlags;
+ DWORD NumberOfRvaAndSizes;
+ IMAGE_DATA_DIRECTORY DataDirectory[16];
+} IMAGE_OPTIONAL_HEADER32, IMAGE_OPTIONAL_HEADER64, IMAGE_OPTIONAL_HEADER;
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */
+/* IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 (X86 usage) */
+#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 /* Architecture Specific Data */
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* RVA of GP */
+#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */
+#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 /* Delay Load Import Descriptors */
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 /* COM Runtime descriptor */
+
+/* Section header format. */
+#define IMAGE_SIZEOF_SHORT_NAME 8
+
+typedef struct _IMAGE_SECTION_HEADER {
+ BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
+ union {
+ DWORD PhysicalAddress;
+ DWORD VirtualSize;
+ } Misc;
+ DWORD VirtualAddress;
+ DWORD SizeOfRawData;
+ DWORD PointerToRawData;
+ DWORD PointerToRelocations;
+ DWORD PointerToLinenumbers;
+ WORD NumberOfRelocations;
+ WORD NumberOfLinenumbers;
+ DWORD Characteristics;
+} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
+
+#define IMAGE_SIZEOF_SECTION_HEADER 40
+
+typedef struct _IMAGE_EXPORT_DIRECTORY {
+ DWORD Characteristics;
+ DWORD TimeDateStamp;
+ WORD MajorVersion;
+ WORD MinorVersion;
+ DWORD Name;
+ DWORD Base;
+ DWORD NumberOfFunctions;
+ DWORD NumberOfNames;
+ DWORD AddressOfFunctions;
+ DWORD AddressOfNames;
+ DWORD AddressOfNameOrdinals;
+} IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY;
+
+typedef struct _IMAGE_IMPORT_DESCRIPTOR {
+ union {
+ DWORD Characteristics;
+ DWORD OriginalFirstThunk;
+ };
+ DWORD TimeDateStamp;
+ DWORD ForwarderChain;
+ DWORD Name;
+ DWORD FirstThunk;
+} IMAGE_IMPORT_DESCRIPTOR;
+
+typedef struct _IMAGE_BASE_RELOCATION {
+ DWORD VirtualAddress;
+ DWORD SizeOfBlock;
+// WORD TypeOffset[1];
+} IMAGE_BASE_RELOCATION;
+
+#define IMAGE_SIZEOF_BASE_RELOCATION 8
+
+#define IMAGE_REL_BASED_ABSOLUTE 0
+#define IMAGE_REL_BASED_HIGH 1
+#define IMAGE_REL_BASED_LOW 2
+#define IMAGE_REL_BASED_HIGHLOW 3
+#define IMAGE_REL_BASED_HIGHADJ 4
+#define IMAGE_REL_BASED_MIPS_JMPADDR 5
+#define IMAGE_REL_BASED_SECTION 6
+#define IMAGE_REL_BASED_REL32 7
+
+#pragma pack(pop)
+
+/* ----------------------------------------------------------- */
+#endif /* ndef IMAGE_NT_SIGNATURE */
+/* ----------------------------------------------------------- */
+#pragma pack(push, 1)
+
+struct pe_header
+{
+ IMAGE_DOS_HEADER doshdr;
+ BYTE dosstub[0x40];
+ DWORD nt_sig;
+ IMAGE_FILE_HEADER filehdr;
+#ifdef TCC_TARGET_X86_64
+ IMAGE_OPTIONAL_HEADER64 opthdr;
+#else
+#ifdef _WIN64
+ IMAGE_OPTIONAL_HEADER32 opthdr;
+#else
+ IMAGE_OPTIONAL_HEADER opthdr;
+#endif
+#endif
+};
+
+struct pe_reloc_header {
+ DWORD offset;
+ DWORD size;
+};
+
+struct pe_rsrc_header {
+ struct _IMAGE_FILE_HEADER filehdr;
+ struct _IMAGE_SECTION_HEADER sectionhdr;
+};
+
+struct pe_rsrc_reloc {
+ DWORD offset;
+ DWORD size;
+ WORD type;
+};
+
+#pragma pack(pop)
+
+/* ------------------------------------------------------------- */
+/* internal temporary structures */
+
+/*
+#define IMAGE_SCN_CNT_CODE 0x00000020
+#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040
+#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080
+#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
+#define IMAGE_SCN_MEM_SHARED 0x10000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+#define IMAGE_SCN_MEM_WRITE 0x80000000
+*/
+
+enum {
+ sec_text = 0,
+ sec_data ,
+ sec_bss ,
+ sec_idata ,
+ sec_pdata ,
+ sec_other ,
+ sec_rsrc ,
+ sec_stab ,
+ sec_reloc ,
+ sec_last
+};
+
+static const DWORD pe_sec_flags[] = {
+ 0x60000020, /* ".text" , */
+ 0xC0000040, /* ".data" , */
+ 0xC0000080, /* ".bss" , */
+ 0x40000040, /* ".idata" , */
+ 0x40000040, /* ".pdata" , */
+ 0xE0000060, /* < other > , */
+ 0x40000040, /* ".rsrc" , */
+ 0x42000802, /* ".stab" , */
+ 0x42000040, /* ".reloc" , */
+};
+
+struct section_info {
+ int cls, ord;
+ char name[32];
+ DWORD sh_addr;
+ DWORD sh_size;
+ DWORD sh_flags;
+ unsigned char *data;
+ DWORD data_size;
+ IMAGE_SECTION_HEADER ish;
+};
+
+struct import_symbol {
+ int sym_index;
+ int iat_index;
+ int thk_offset;
+};
+
+struct pe_import_info {
+ int dll_index;
+ int sym_count;
+ struct import_symbol **symbols;
+};
+
+struct pe_info {
+ TCCState *s1;
+ Section *reloc;
+ Section *thunk;
+ const char *filename;
+ int type;
+ DWORD sizeofheaders;
+ ADDR3264 imagebase;
+ DWORD start_addr;
+ DWORD imp_offs;
+ DWORD imp_size;
+ DWORD iat_offs;
+ DWORD iat_size;
+ DWORD exp_offs;
+ DWORD exp_size;
+ int subsystem;
+ DWORD section_align;
+ DWORD file_align;
+ struct section_info *sec_info;
+ int sec_count;
+ struct pe_import_info **imp_info;
+ int imp_count;
+};
+
+#define PE_NUL 0
+#define PE_DLL 1
+#define PE_GUI 2
+#define PE_EXE 3
+#define PE_RUN 4
+
+/* --------------------------------------------*/
+
+static const char *pe_export_name(TCCState *s1, ElfW(Sym) *sym)
+{
+ const char *name = symtab_section->link->data + sym->st_name;
+ if (s1->leading_underscore && name[0] == '_' && !(sym->st_other & ST_PE_STDCALL))
+ return name + 1;
+ return name;
+}
+
+static int pe_find_import(TCCState * s1, ElfW(Sym) *sym)
+{
+ char buffer[200];
+ const char *s, *p;
+ int sym_index = 0, n = 0;
+
+ do {
+ s = pe_export_name(s1, sym);
+ if (n) {
+ /* second try: */
+ if (sym->st_other & ST_PE_STDCALL) {
+ /* try w/0 stdcall deco (windows API convention) */
+ p = strrchr(s, '@');
+ if (!p || s[0] != '_')
+ break;
+ strcpy(buffer, s+1)[p-s-1] = 0;
+ } else if (s[0] != '_') { /* try non-ansi function */
+ buffer[0] = '_', strcpy(buffer + 1, s);
+ } else if (0 == memcmp(s, "__imp__", 7)) { /* mingw 2.0 */
+ strcpy(buffer, s + 6);
+ } else if (0 == memcmp(s, "_imp___", 7)) { /* mingw 3.7 */
+ strcpy(buffer, s + 6);
+ } else {
+ break;
+ }
+ s = buffer;
+ }
+ sym_index = find_elf_sym(s1->dynsymtab_section, s);
+ // printf("find (%d) %d %s\n", n, sym_index, s);
+ } while (0 == sym_index && ++n < 2);
+ return sym_index;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int dynarray_assoc(void **pp, int n, int key)
+{
+ int i;
+ for (i = 0; i < n; ++i, ++pp)
+ if (key == **(int **) pp)
+ return i;
+ return -1;
+}
+
+#if 0
+ST_FN DWORD umin(DWORD a, DWORD b)
+{
+ return a < b ? a : b;
+}
+#endif
+
+static DWORD umax(DWORD a, DWORD b)
+{
+ return a < b ? b : a;
+}
+
+static DWORD pe_file_align(struct pe_info *pe, DWORD n)
+{
+ return (n + (pe->file_align - 1)) & ~(pe->file_align - 1);
+}
+
+static DWORD pe_virtual_align(struct pe_info *pe, DWORD n)
+{
+ return (n + (pe->section_align - 1)) & ~(pe->section_align - 1);
+}
+
+static void pe_align_section(Section *s, int a)
+{
+ int i = s->data_offset & (a-1);
+ if (i)
+ section_ptr_add(s, a - i);
+}
+
+static void pe_set_datadir(struct pe_header *hdr, int dir, DWORD addr, DWORD size)
+{
+ hdr->opthdr.DataDirectory[dir].VirtualAddress = addr;
+ hdr->opthdr.DataDirectory[dir].Size = size;
+}
+
+static int pe_fwrite(void *data, unsigned len, FILE *fp, DWORD *psum)
+{
+ if (psum) {
+ DWORD sum = *psum;
+ WORD *p = data;
+ int i;
+ for (i = len; i > 0; i -= 2) {
+ sum += (i >= 2) ? *p++ : *(BYTE*)p;
+ sum = (sum + (sum >> 16)) & 0xFFFF;
+ }
+ *psum = sum;
+ }
+ return len == fwrite(data, 1, len, fp) ? 0 : -1;
+}
+
+static void pe_fpad(FILE *fp, DWORD new_pos)
+{
+ DWORD pos = ftell(fp);
+ while (++pos <= new_pos)
+ fputc(0, fp);
+}
+
+/*----------------------------------------------------------------------------*/
+static int pe_write(struct pe_info *pe)
+{
+ static const struct pe_header pe_template = {
+ {
+ /* IMAGE_DOS_HEADER doshdr */
+ 0x5A4D, /*WORD e_magic; Magic number */
+ 0x0090, /*WORD e_cblp; Bytes on last page of file */
+ 0x0003, /*WORD e_cp; Pages in file */
+ 0x0000, /*WORD e_crlc; Relocations */
+
+ 0x0004, /*WORD e_cparhdr; Size of header in paragraphs */
+ 0x0000, /*WORD e_minalloc; Minimum extra paragraphs needed */
+ 0xFFFF, /*WORD e_maxalloc; Maximum extra paragraphs needed */
+ 0x0000, /*WORD e_ss; Initial (relative) SS value */
+
+ 0x00B8, /*WORD e_sp; Initial SP value */
+ 0x0000, /*WORD e_csum; Checksum */
+ 0x0000, /*WORD e_ip; Initial IP value */
+ 0x0000, /*WORD e_cs; Initial (relative) CS value */
+ 0x0040, /*WORD e_lfarlc; File address of relocation table */
+ 0x0000, /*WORD e_ovno; Overlay number */
+ {0,0,0,0}, /*WORD e_res[4]; Reserved words */
+ 0x0000, /*WORD e_oemid; OEM identifier (for e_oeminfo) */
+ 0x0000, /*WORD e_oeminfo; OEM information; e_oemid specific */
+ {0,0,0,0,0,0,0,0,0,0}, /*WORD e_res2[10]; Reserved words */
+ 0x00000080 /*DWORD e_lfanew; File address of new exe header */
+ },{
+ /* BYTE dosstub[0x40] */
+ /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */
+ 0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68,
+ 0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f,
+ 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20,
+ 0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ },
+ 0x00004550, /* DWORD nt_sig = IMAGE_NT_SIGNATURE */
+ {
+ /* IMAGE_FILE_HEADER filehdr */
+ IMAGE_FILE_MACHINE, /*WORD Machine; */
+ 0x0003, /*WORD NumberOfSections; */
+ 0x00000000, /*DWORD TimeDateStamp; */
+ 0x00000000, /*DWORD PointerToSymbolTable; */
+ 0x00000000, /*DWORD NumberOfSymbols; */
+#if defined(TCC_TARGET_X86_64)
+ 0x00F0, /*WORD SizeOfOptionalHeader; */
+ 0x022F /*WORD Characteristics; */
+#define CHARACTERISTICS_DLL 0x222E
+#elif defined(TCC_TARGET_I386)
+ 0x00E0, /*WORD SizeOfOptionalHeader; */
+ 0x030F /*WORD Characteristics; */
+#define CHARACTERISTICS_DLL 0x230E
+#elif defined(TCC_TARGET_ARM)
+ 0x00E0, /*WORD SizeOfOptionalHeader; */
+ 0x010F, /*WORD Characteristics; */
+#define CHARACTERISTICS_DLL 0x230F
+#endif
+},{
+ /* IMAGE_OPTIONAL_HEADER opthdr */
+ /* Standard fields. */
+#ifdef TCC_TARGET_X86_64
+ 0x020B, /*WORD Magic; */
+#else
+ 0x010B, /*WORD Magic; */
+#endif
+ 0x06, /*BYTE MajorLinkerVersion; */
+ 0x00, /*BYTE MinorLinkerVersion; */
+ 0x00000000, /*DWORD SizeOfCode; */
+ 0x00000000, /*DWORD SizeOfInitializedData; */
+ 0x00000000, /*DWORD SizeOfUninitializedData; */
+ 0x00000000, /*DWORD AddressOfEntryPoint; */
+ 0x00000000, /*DWORD BaseOfCode; */
+#ifndef TCC_TARGET_X86_64
+ 0x00000000, /*DWORD BaseOfData; */
+#endif
+ /* NT additional fields. */
+#if defined(TCC_TARGET_ARM)
+ 0x00100000, /*DWORD ImageBase; */
+#else
+ 0x00400000, /*DWORD ImageBase; */
+#endif
+ 0x00001000, /*DWORD SectionAlignment; */
+ 0x00000200, /*DWORD FileAlignment; */
+ 0x0004, /*WORD MajorOperatingSystemVersion; */
+ 0x0000, /*WORD MinorOperatingSystemVersion; */
+ 0x0000, /*WORD MajorImageVersion; */
+ 0x0000, /*WORD MinorImageVersion; */
+ 0x0004, /*WORD MajorSubsystemVersion; */
+ 0x0000, /*WORD MinorSubsystemVersion; */
+ 0x00000000, /*DWORD Win32VersionValue; */
+ 0x00000000, /*DWORD SizeOfImage; */
+ 0x00000200, /*DWORD SizeOfHeaders; */
+ 0x00000000, /*DWORD CheckSum; */
+ 0x0002, /*WORD Subsystem; */
+ 0x0000, /*WORD DllCharacteristics; */
+ 0x00100000, /*DWORD SizeOfStackReserve; */
+ 0x00001000, /*DWORD SizeOfStackCommit; */
+ 0x00100000, /*DWORD SizeOfHeapReserve; */
+ 0x00001000, /*DWORD SizeOfHeapCommit; */
+ 0x00000000, /*DWORD LoaderFlags; */
+ 0x00000010, /*DWORD NumberOfRvaAndSizes; */
+
+ /* IMAGE_DATA_DIRECTORY DataDirectory[16]; */
+ {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0},
+ {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}}
+ }};
+
+ struct pe_header pe_header = pe_template;
+
+ int i;
+ FILE *op;
+ DWORD file_offset, sum;
+ struct section_info *si;
+ IMAGE_SECTION_HEADER *psh;
+
+ op = fopen(pe->filename, "wb");
+ if (NULL == op) {
+ tcc_error_noabort("could not write '%s': %s", pe->filename, strerror(errno));
+ return -1;
+ }
+
+ pe->sizeofheaders = pe_file_align(pe,
+ sizeof (struct pe_header)
+ + pe->sec_count * sizeof (IMAGE_SECTION_HEADER)
+ );
+
+ file_offset = pe->sizeofheaders;
+
+ if (2 == pe->s1->verbose)
+ printf("-------------------------------"
+ "\n virt file size section" "\n");
+ for (i = 0; i < pe->sec_count; ++i) {
+ DWORD addr, size;
+ const char *sh_name;
+
+ si = pe->sec_info + i;
+ sh_name = si->name;
+ addr = si->sh_addr - pe->imagebase;
+ size = si->sh_size;
+ psh = &si->ish;
+
+ if (2 == pe->s1->verbose)
+ printf("%6x %6x %6x %s\n",
+ (unsigned)addr, (unsigned)file_offset, (unsigned)size, sh_name);
+
+ switch (si->cls) {
+ case sec_text:
+ pe_header.opthdr.BaseOfCode = addr;
+ pe_header.opthdr.AddressOfEntryPoint = addr + pe->start_addr;
+ break;
+
+ case sec_data:
+#ifndef TCC_TARGET_X86_64
+ pe_header.opthdr.BaseOfData = addr;
+#endif
+ break;
+
+ case sec_bss:
+ break;
+
+ case sec_reloc:
+ pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size);
+ break;
+
+ case sec_rsrc:
+ pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size);
+ break;
+
+ case sec_pdata:
+ pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXCEPTION, addr, size);
+ break;
+
+ case sec_stab:
+ break;
+ }
+
+ if (pe->thunk == pe->s1->sections[si->ord]) {
+ if (pe->imp_size) {
+ pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IMPORT,
+ pe->imp_offs + addr, pe->imp_size);
+ pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_IAT,
+ pe->iat_offs + addr, pe->iat_size);
+ }
+ if (pe->exp_size) {
+ pe_set_datadir(&pe_header, IMAGE_DIRECTORY_ENTRY_EXPORT,
+ pe->exp_offs + addr, pe->exp_size);
+ }
+ }
+
+ strncpy((char*)psh->Name, sh_name, sizeof psh->Name);
+
+ psh->Characteristics = pe_sec_flags[si->cls];
+ psh->VirtualAddress = addr;
+ psh->Misc.VirtualSize = size;
+ pe_header.opthdr.SizeOfImage =
+ umax(pe_virtual_align(pe, size + addr), pe_header.opthdr.SizeOfImage);
+
+ if (si->data_size) {
+ psh->PointerToRawData = file_offset;
+ file_offset = pe_file_align(pe, file_offset + si->data_size);
+ psh->SizeOfRawData = file_offset - psh->PointerToRawData;
+ }
+ }
+
+ //pe_header.filehdr.TimeDateStamp = time(NULL);
+ pe_header.filehdr.NumberOfSections = pe->sec_count;
+ pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders;
+ pe_header.opthdr.ImageBase = pe->imagebase;
+ pe_header.opthdr.Subsystem = pe->subsystem;
+ if (pe->s1->pe_stack_size)
+ pe_header.opthdr.SizeOfStackReserve = pe->s1->pe_stack_size;
+ if (PE_DLL == pe->type)
+ pe_header.filehdr.Characteristics = CHARACTERISTICS_DLL;
+
+ sum = 0;
+ pe_fwrite(&pe_header, sizeof pe_header, op, &sum);
+ for (i = 0; i < pe->sec_count; ++i)
+ pe_fwrite(&pe->sec_info[i].ish, sizeof(IMAGE_SECTION_HEADER), op, &sum);
+ pe_fpad(op, pe->sizeofheaders);
+ for (i = 0; i < pe->sec_count; ++i) {
+ si = pe->sec_info + i;
+ psh = &si->ish;
+ if (si->data_size) {
+ pe_fwrite(si->data, si->data_size, op, &sum);
+ file_offset = psh->PointerToRawData + psh->SizeOfRawData;
+ pe_fpad(op, file_offset);
+ }
+ }
+
+ pe_header.opthdr.CheckSum = sum + file_offset;
+ fseek(op, offsetof(struct pe_header, opthdr.CheckSum), SEEK_SET);
+ pe_fwrite(&pe_header.opthdr.CheckSum, sizeof pe_header.opthdr.CheckSum, op, NULL);
+ fclose (op);
+
+ if (2 == pe->s1->verbose)
+ printf("-------------------------------\n");
+ if (pe->s1->verbose)
+ printf("<- %s (%u bytes)\n", pe->filename, (unsigned)file_offset);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static struct import_symbol *pe_add_import(struct pe_info *pe, int sym_index)
+{
+ int i;
+ int dll_index;
+ struct pe_import_info *p;
+ struct import_symbol *s;
+ ElfW(Sym) *isym;
+
+ isym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index;
+ dll_index = isym->st_size;
+
+ i = dynarray_assoc ((void**)pe->imp_info, pe->imp_count, dll_index);
+ if (-1 != i) {
+ p = pe->imp_info[i];
+ goto found_dll;
+ }
+ p = tcc_mallocz(sizeof *p);
+ p->dll_index = dll_index;
+ dynarray_add((void***)&pe->imp_info, &pe->imp_count, p);
+
+found_dll:
+ i = dynarray_assoc ((void**)p->symbols, p->sym_count, sym_index);
+ if (-1 != i)
+ return p->symbols[i];
+
+ s = tcc_mallocz(sizeof *s);
+ dynarray_add((void***)&p->symbols, &p->sym_count, s);
+ s->sym_index = sym_index;
+ return s;
+}
+
+/*----------------------------------------------------------------------------*/
+static void pe_build_imports(struct pe_info *pe)
+{
+ int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i;
+ DWORD rva_base = pe->thunk->sh_addr - pe->imagebase;
+ int ndlls = pe->imp_count;
+
+ for (sym_cnt = i = 0; i < ndlls; ++i)
+ sym_cnt += pe->imp_info[i]->sym_count;
+
+ if (0 == sym_cnt)
+ return;
+
+ pe_align_section(pe->thunk, 16);
+
+ pe->imp_offs = dll_ptr = pe->thunk->data_offset;
+ pe->imp_size = (ndlls + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ pe->iat_offs = dll_ptr + pe->imp_size;
+ pe->iat_size = (sym_cnt + ndlls) * sizeof(ADDR3264);
+ section_ptr_add(pe->thunk, pe->imp_size + 2*pe->iat_size);
+
+ thk_ptr = pe->iat_offs;
+ ent_ptr = pe->iat_offs + pe->iat_size;
+
+ for (i = 0; i < pe->imp_count; ++i) {
+ IMAGE_IMPORT_DESCRIPTOR *hdr;
+ int k, n, dllindex;
+ ADDR3264 v;
+ struct pe_import_info *p = pe->imp_info[i];
+ const char *name;
+ DLLReference *dllref;
+
+ dllindex = p->dll_index;
+ if (dllindex)
+ name = (dllref = pe->s1->loaded_dlls[dllindex-1])->name;
+ else
+ name = "", dllref = NULL;
+
+ /* put the dll name into the import header */
+ v = put_elf_str(pe->thunk, name);
+ hdr = (IMAGE_IMPORT_DESCRIPTOR*)(pe->thunk->data + dll_ptr);
+ hdr->FirstThunk = thk_ptr + rva_base;
+ hdr->OriginalFirstThunk = ent_ptr + rva_base;
+ hdr->Name = v + rva_base;
+
+ for (k = 0, n = p->sym_count; k <= n; ++k) {
+ if (k < n) {
+ int iat_index = p->symbols[k]->iat_index;
+ int sym_index = p->symbols[k]->sym_index;
+ ElfW(Sym) *imp_sym = (ElfW(Sym) *)pe->s1->dynsymtab_section->data + sym_index;
+ ElfW(Sym) *org_sym = (ElfW(Sym) *)symtab_section->data + iat_index;
+ const char *name = pe->s1->dynsymtab_section->link->data + imp_sym->st_name;
+ int ordinal;
+
+ org_sym->st_value = thk_ptr;
+ org_sym->st_shndx = pe->thunk->sh_num;
+
+ if (dllref)
+ v = 0, ordinal = imp_sym->st_value; /* ordinal from pe_load_def */
+ else
+ ordinal = 0, v = imp_sym->st_value; /* address from tcc_add_symbol() */
+
+#ifdef TCC_IS_NATIVE
+ if (pe->type == PE_RUN) {
+ if (dllref) {
+ if ( !dllref->handle )
+ dllref->handle = LoadLibrary(dllref->name);
+ v = (ADDR3264)GetProcAddress(dllref->handle, ordinal?(LPCSTR)NULL+ordinal:name);
+ }
+ if (!v)
+ tcc_error_noabort("can't build symbol '%s'", name);
+ } else
+#endif
+ if (ordinal) {
+ v = ordinal | (ADDR3264)1 << (sizeof(ADDR3264)*8 - 1);
+ } else {
+ v = pe->thunk->data_offset + rva_base;
+ section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */
+ put_elf_str(pe->thunk, name);
+ }
+
+ } else {
+ v = 0; /* last entry is zero */
+ }
+
+ *(ADDR3264*)(pe->thunk->data+thk_ptr) =
+ *(ADDR3264*)(pe->thunk->data+ent_ptr) = v;
+ thk_ptr += sizeof (ADDR3264);
+ ent_ptr += sizeof (ADDR3264);
+ }
+ dll_ptr += sizeof(IMAGE_IMPORT_DESCRIPTOR);
+ dynarray_reset(&p->symbols, &p->sym_count);
+ }
+ dynarray_reset(&pe->imp_info, &pe->imp_count);
+}
+
+/* ------------------------------------------------------------- */
+
+struct pe_sort_sym
+{
+ int index;
+ const char *name;
+};
+
+static int sym_cmp(const void *va, const void *vb)
+{
+ const char *ca = (*(struct pe_sort_sym**)va)->name;
+ const char *cb = (*(struct pe_sort_sym**)vb)->name;
+ return strcmp(ca, cb);
+}
+
+static void pe_build_exports(struct pe_info *pe)
+{
+ ElfW(Sym) *sym;
+ int sym_index, sym_end;
+ DWORD rva_base, func_o, name_o, ord_o, str_o;
+ IMAGE_EXPORT_DIRECTORY *hdr;
+ int sym_count, ord;
+ struct pe_sort_sym **sorted, *p;
+
+ FILE *op;
+ char buf[MAX_PATH];
+ const char *dllname;
+ const char *name;
+
+ rva_base = pe->thunk->sh_addr - pe->imagebase;
+ sym_count = 0, sorted = NULL, op = NULL;
+
+ sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));
+ for (sym_index = 1; sym_index < sym_end; ++sym_index) {
+ sym = (ElfW(Sym)*)symtab_section->data + sym_index;
+ name = pe_export_name(pe->s1, sym);
+ if ((sym->st_other & ST_PE_EXPORT)
+ /* export only symbols from actually written sections */
+ && pe->s1->sections[sym->st_shndx]->sh_addr) {
+ p = tcc_malloc(sizeof *p);
+ p->index = sym_index;
+ p->name = name;
+ dynarray_add((void***)&sorted, &sym_count, p);
+ }
+#if 0
+ if (sym->st_other & ST_PE_EXPORT)
+ printf("export: %s\n", name);
+ if (sym->st_other & ST_PE_STDCALL)
+ printf("stdcall: %s\n", name);
+#endif
+ }
+
+ if (0 == sym_count)
+ return;
+
+ qsort (sorted, sym_count, sizeof *sorted, sym_cmp);
+
+ pe_align_section(pe->thunk, 16);
+ dllname = tcc_basename(pe->filename);
+
+ pe->exp_offs = pe->thunk->data_offset;
+ func_o = pe->exp_offs + sizeof(IMAGE_EXPORT_DIRECTORY);
+ name_o = func_o + sym_count * sizeof (DWORD);
+ ord_o = name_o + sym_count * sizeof (DWORD);
+ str_o = ord_o + sym_count * sizeof(WORD);
+
+ hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs);
+ hdr->Characteristics = 0;
+ hdr->Base = 1;
+ hdr->NumberOfFunctions = sym_count;
+ hdr->NumberOfNames = sym_count;
+ hdr->AddressOfFunctions = func_o + rva_base;
+ hdr->AddressOfNames = name_o + rva_base;
+ hdr->AddressOfNameOrdinals = ord_o + rva_base;
+ hdr->Name = str_o + rva_base;
+ put_elf_str(pe->thunk, dllname);
+
+#if 1
+ /* automatically write exports to <output-filename>.def */
+ pstrcpy(buf, sizeof buf, pe->filename);
+ strcpy(tcc_fileextension(buf), ".def");
+ op = fopen(buf, "w");
+ if (NULL == op) {
+ tcc_error_noabort("could not create '%s': %s", buf, strerror(errno));
+ } else {
+ fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname);
+ if (pe->s1->verbose)
+ printf("<- %s (%d symbols)\n", buf, sym_count);
+ }
+#endif
+
+ for (ord = 0; ord < sym_count; ++ord)
+ {
+ p = sorted[ord], sym_index = p->index, name = p->name;
+ /* insert actual address later in pe_relocate_rva */
+ put_elf_reloc(symtab_section, pe->thunk,
+ func_o, R_XXX_RELATIVE, sym_index);
+ *(DWORD*)(pe->thunk->data + name_o)
+ = pe->thunk->data_offset + rva_base;
+ *(WORD*)(pe->thunk->data + ord_o)
+ = ord;
+ put_elf_str(pe->thunk, name);
+ func_o += sizeof (DWORD);
+ name_o += sizeof (DWORD);
+ ord_o += sizeof (WORD);
+ if (op)
+ fprintf(op, "%s\n", name);
+ }
+ pe->exp_size = pe->thunk->data_offset - pe->exp_offs;
+ dynarray_reset(&sorted, &sym_count);
+ if (op)
+ fclose(op);
+}
+
+/* ------------------------------------------------------------- */
+static void pe_build_reloc (struct pe_info *pe)
+{
+ DWORD offset, block_ptr, addr;
+ int count, i;
+ ElfW_Rel *rel, *rel_end;
+ Section *s = NULL, *sr;
+
+ offset = addr = block_ptr = count = i = 0;
+ rel = rel_end = NULL;
+
+ for(;;) {
+ if (rel < rel_end) {
+ int type = ELFW(R_TYPE)(rel->r_info);
+ addr = rel->r_offset + s->sh_addr;
+ ++ rel;
+ if (type != REL_TYPE_DIRECT)
+ continue;
+ if (count == 0) { /* new block */
+ block_ptr = pe->reloc->data_offset;
+ section_ptr_add(pe->reloc, sizeof(struct pe_reloc_header));
+ offset = addr & 0xFFFFFFFF<<12;
+ }
+ if ((addr -= offset) < (1<<12)) { /* one block spans 4k addresses */
+ WORD *wp = section_ptr_add(pe->reloc, sizeof (WORD));
+ *wp = addr | IMAGE_REL_BASED_HIGHLOW<<12;
+ ++count;
+ continue;
+ }
+ -- rel;
+
+ } else if (i < pe->sec_count) {
+ sr = (s = pe->s1->sections[pe->sec_info[i++].ord])->reloc;
+ if (sr) {
+ rel = (ElfW_Rel *)sr->data;
+ rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
+ }
+ continue;
+ }
+
+ if (count) {
+ /* store the last block and ready for a new one */
+ struct pe_reloc_header *hdr;
+ if (count & 1) /* align for DWORDS */
+ section_ptr_add(pe->reloc, sizeof(WORD)), ++count;
+ hdr = (struct pe_reloc_header *)(pe->reloc->data + block_ptr);
+ hdr -> offset = offset - pe->imagebase;
+ hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header);
+ count = 0;
+ }
+
+ if (rel >= rel_end)
+ break;
+ }
+}
+
+/* ------------------------------------------------------------- */
+static int pe_section_class(Section *s)
+{
+ int type, flags;
+ const char *name;
+
+ type = s->sh_type;
+ flags = s->sh_flags;
+ name = s->name;
+ if (flags & SHF_ALLOC) {
+ if (type == SHT_PROGBITS) {
+ if (flags & SHF_EXECINSTR)
+ return sec_text;
+ if (flags & SHF_WRITE)
+ return sec_data;
+ if (0 == strcmp(name, ".rsrc"))
+ return sec_rsrc;
+ if (0 == strcmp(name, ".iedat"))
+ return sec_idata;
+ if (0 == strcmp(name, ".pdata"))
+ return sec_pdata;
+ return sec_other;
+ } else if (type == SHT_NOBITS) {
+ if (flags & SHF_WRITE)
+ return sec_bss;
+ }
+ } else {
+ if (0 == strcmp(name, ".reloc"))
+ return sec_reloc;
+ if (0 == strncmp(name, ".stab", 5)) /* .stab and .stabstr */
+ return sec_stab;
+ }
+ return -1;
+}
+
+static int pe_assign_addresses (struct pe_info *pe)
+{
+ int i, k, o, c;
+ DWORD addr;
+ int *section_order;
+ struct section_info *si;
+ Section *s;
+
+ // pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC);
+
+ section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int));
+ for (o = k = 0 ; k < sec_last; ++k) {
+ for (i = 1; i < pe->s1->nb_sections; ++i) {
+ s = pe->s1->sections[i];
+ if (k == pe_section_class(s)) {
+ // printf("%s %d\n", s->name, k);
+ s->sh_addr = pe->imagebase;
+ section_order[o++] = i;
+ }
+ }
+ }
+
+ pe->sec_info = tcc_mallocz(o * sizeof (struct section_info));
+ addr = pe->imagebase + 1;
+
+ for (i = 0; i < o; ++i)
+ {
+ k = section_order[i];
+ s = pe->s1->sections[k];
+ c = pe_section_class(s);
+ si = &pe->sec_info[pe->sec_count];
+
+#ifdef PE_MERGE_DATA
+ if (c == sec_bss && pe->sec_count && si[-1].cls == sec_data) {
+ /* append .bss to .data */
+ s->sh_addr = addr = ((addr-1) | (s->sh_addralign-1)) + 1;
+ addr += s->data_offset;
+ si[-1].sh_size = addr - si[-1].sh_addr;
+ continue;
+ }
+#endif
+ if (c == sec_stab && 0 == pe->s1->do_debug)
+ continue;
+
+ strcpy(si->name, s->name);
+ si->cls = c;
+ si->ord = k;
+ si->sh_addr = s->sh_addr = addr = pe_virtual_align(pe, addr);
+ si->sh_flags = s->sh_flags;
+
+ if (c == sec_data && NULL == pe->thunk)
+ pe->thunk = s;
+
+ if (s == pe->thunk) {
+ pe_build_imports(pe);
+ pe_build_exports(pe);
+ }
+
+ if (c == sec_reloc)
+ pe_build_reloc (pe);
+
+ if (s->data_offset)
+ {
+ if (s->sh_type != SHT_NOBITS) {
+ si->data = s->data;
+ si->data_size = s->data_offset;
+ }
+
+ addr += s->data_offset;
+ si->sh_size = s->data_offset;
+ ++pe->sec_count;
+ }
+ // printf("%08x %05x %s\n", si->sh_addr, si->sh_size, si->name);
+ }
+
+#if 0
+ for (i = 1; i < pe->s1->nb_sections; ++i) {
+ Section *s = pe->s1->sections[i];
+ int type = s->sh_type;
+ int flags = s->sh_flags;
+ printf("section %-16s %-10s %5x %s,%s,%s\n",
+ s->name,
+ type == SHT_PROGBITS ? "progbits" :
+ type == SHT_NOBITS ? "nobits" :
+ type == SHT_SYMTAB ? "symtab" :
+ type == SHT_STRTAB ? "strtab" :
+ type == SHT_RELX ? "rel" : "???",
+ s->data_offset,
+ flags & SHF_ALLOC ? "alloc" : "",
+ flags & SHF_WRITE ? "write" : "",
+ flags & SHF_EXECINSTR ? "exec" : ""
+ );
+ }
+ pe->s1->verbose = 2;
+#endif
+
+ tcc_free(section_order);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+static void pe_relocate_rva (struct pe_info *pe, Section *s)
+{
+ Section *sr = s->reloc;
+ ElfW_Rel *rel, *rel_end;
+ rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
+ for(rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) {
+ if (ELFW(R_TYPE)(rel->r_info) == R_XXX_RELATIVE) {
+ int sym_index = ELFW(R_SYM)(rel->r_info);
+ DWORD addr = s->sh_addr;
+ if (sym_index) {
+ ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + sym_index;
+ addr = sym->st_value;
+ }
+ // printf("reloc rva %08x %08x %s\n", (DWORD)rel->r_offset, addr, s->name);
+ *(DWORD*)(s->data + rel->r_offset) += addr - pe->imagebase;
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int pe_isafunc(int sym_index)
+{
+ Section *sr = text_section->reloc;
+ ElfW_Rel *rel, *rel_end;
+ Elf32_Word info = ELF32_R_INFO(sym_index, R_386_PC32);
+ if (!sr)
+ return 0;
+ rel_end = (ElfW_Rel *)(sr->data + sr->data_offset);
+ for (rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++)
+ if (rel->r_info == info)
+ return 1;
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static int pe_check_symbols(struct pe_info *pe)
+{
+ ElfW(Sym) *sym;
+ int sym_index, sym_end;
+ int ret = 0;
+
+ pe_align_section(text_section, 8);
+
+ sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));
+ for (sym_index = 1; sym_index < sym_end; ++sym_index) {
+
+ sym = (ElfW(Sym) *)symtab_section->data + sym_index;
+ if (sym->st_shndx == SHN_UNDEF) {
+
+ const char *name = symtab_section->link->data + sym->st_name;
+ unsigned type = ELFW(ST_TYPE)(sym->st_info);
+ int imp_sym = pe_find_import(pe->s1, sym);
+ struct import_symbol *is;
+
+ if (0 == imp_sym)
+ goto not_found;
+
+ if (type == STT_NOTYPE) {
+ /* symbols from assembler have no type, find out which */
+ if (pe_isafunc(sym_index))
+ type = STT_FUNC;
+ else
+ type = STT_OBJECT;
+ }
+
+ is = pe_add_import(pe, imp_sym);
+
+ if (type == STT_FUNC) {
+ unsigned long offset = is->thk_offset;
+ if (offset) {
+ /* got aliased symbol, like stricmp and _stricmp */
+
+ } else {
+ char buffer[100];
+ WORD *p;
+
+ offset = text_section->data_offset;
+ /* add the 'jmp IAT[x]' instruction */
+#ifdef TCC_TARGET_ARM
+ p = section_ptr_add(text_section, 8+4); // room for code and address
+ (*(DWORD*)(p)) = 0xE59FC000; // arm code ldr ip, [pc] ; PC+8+0 = 0001xxxx
+ (*(DWORD*)(p+2)) = 0xE59CF000; // arm code ldr pc, [ip]
+#else
+ p = section_ptr_add(text_section, 8);
+ *p = 0x25FF;
+#ifdef TCC_TARGET_X86_64
+ *(DWORD*)(p+1) = (DWORD)-4;
+#endif
+#endif
+ /* add a helper symbol, will be patched later in
+ pe_build_imports */
+ sprintf(buffer, "IAT.%s", name);
+ is->iat_index = put_elf_sym(
+ symtab_section, 0, sizeof(DWORD),
+ ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT),
+ 0, SHN_UNDEF, buffer);
+#ifdef TCC_TARGET_ARM
+ put_elf_reloc(symtab_section, text_section,
+ offset + 8, R_XXX_THUNKFIX, is->iat_index); // offset to IAT position
+#else
+ put_elf_reloc(symtab_section, text_section,
+ offset + 2, R_XXX_THUNKFIX, is->iat_index);
+#endif
+ is->thk_offset = offset;
+ }
+
+ /* tcc_realloc might have altered sym's address */
+ sym = (ElfW(Sym) *)symtab_section->data + sym_index;
+
+ /* patch the original symbol */
+ sym->st_value = offset;
+ sym->st_shndx = text_section->sh_num;
+ sym->st_other &= ~ST_PE_EXPORT; /* do not export */
+ continue;
+ }
+
+ if (type == STT_OBJECT) { /* data, ptr to that should be */
+ if (0 == is->iat_index) {
+ /* original symbol will be patched later in pe_build_imports */
+ is->iat_index = sym_index;
+ continue;
+ }
+ }
+
+ not_found:
+ tcc_error_noabort("undefined symbol '%s'", name);
+ ret = -1;
+
+ } else if (pe->s1->rdynamic
+ && ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) {
+ /* if -rdynamic option, then export all non local symbols */
+ sym->st_other |= ST_PE_EXPORT;
+ }
+ }
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef PE_PRINT_SECTIONS
+static void pe_print_section(FILE * f, Section * s)
+{
+ /* just if you'r curious */
+ BYTE *p, *e, b;
+ int i, n, l, m;
+ p = s->data;
+ e = s->data + s->data_offset;
+ l = e - p;
+
+ fprintf(f, "section \"%s\"", s->name);
+ if (s->link)
+ fprintf(f, "\nlink \"%s\"", s->link->name);
+ if (s->reloc)
+ fprintf(f, "\nreloc \"%s\"", s->reloc->name);
+ fprintf(f, "\nv_addr %08X", (unsigned)s->sh_addr);
+ fprintf(f, "\ncontents %08X", (unsigned)l);
+ fprintf(f, "\n\n");
+
+ if (s->sh_type == SHT_NOBITS)
+ return;
+
+ if (0 == l)
+ return;
+
+ if (s->sh_type == SHT_SYMTAB)
+ m = sizeof(ElfW(Sym));
+ else if (s->sh_type == SHT_RELX)
+ m = sizeof(ElfW_Rel);
+ else
+ m = 16;
+
+ fprintf(f, "%-8s", "offset");
+ for (i = 0; i < m; ++i)
+ fprintf(f, " %02x", i);
+ n = 56;
+
+ if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_RELX) {
+ const char *fields1[] = {
+ "name",
+ "value",
+ "size",
+ "bind",
+ "type",
+ "other",
+ "shndx",
+ NULL
+ };
+
+ const char *fields2[] = {
+ "offs",
+ "type",
+ "symb",
+ NULL
+ };
+
+ const char **p;
+
+ if (s->sh_type == SHT_SYMTAB)
+ p = fields1, n = 106;
+ else
+ p = fields2, n = 58;
+
+ for (i = 0; p[i]; ++i)
+ fprintf(f, "%6s", p[i]);
+ fprintf(f, " symbol");
+ }
+
+ fprintf(f, "\n");
+ for (i = 0; i < n; ++i)
+ fprintf(f, "-");
+ fprintf(f, "\n");
+
+ for (i = 0; i < l;)
+ {
+ fprintf(f, "%08X", i);
+ for (n = 0; n < m; ++n) {
+ if (n + i < l)
+ fprintf(f, " %02X", p[i + n]);
+ else
+ fprintf(f, " ");
+ }
+
+ if (s->sh_type == SHT_SYMTAB) {
+ ElfW(Sym) *sym = (ElfW(Sym) *) (p + i);
+ const char *name = s->link->data + sym->st_name;
+ fprintf(f, " %04X %04X %04X %02X %02X %02X %04X \"%s\"",
+ (unsigned)sym->st_name,
+ (unsigned)sym->st_value,
+ (unsigned)sym->st_size,
+ (unsigned)ELFW(ST_BIND)(sym->st_info),
+ (unsigned)ELFW(ST_TYPE)(sym->st_info),
+ (unsigned)sym->st_other,
+ (unsigned)sym->st_shndx,
+ name);
+
+ } else if (s->sh_type == SHT_RELX) {
+ ElfW_Rel *rel = (ElfW_Rel *) (p + i);
+ ElfW(Sym) *sym =
+ (ElfW(Sym) *) s->link->data + ELFW(R_SYM)(rel->r_info);
+ const char *name = s->link->link->data + sym->st_name;
+ fprintf(f, " %04X %02X %04X \"%s\"",
+ (unsigned)rel->r_offset,
+ (unsigned)ELFW(R_TYPE)(rel->r_info),
+ (unsigned)ELFW(R_SYM)(rel->r_info),
+ name);
+ } else {
+ fprintf(f, " ");
+ for (n = 0; n < m; ++n) {
+ if (n + i < l) {
+ b = p[i + n];
+ if (b < 32 || b >= 127)
+ b = '.';
+ fprintf(f, "%c", b);
+ }
+ }
+ }
+ i += m;
+ fprintf(f, "\n");
+ }
+ fprintf(f, "\n\n");
+}
+
+static void pe_print_sections(TCCState *s1, const char *fname)
+{
+ Section *s;
+ FILE *f;
+ int i;
+ f = fopen(fname, "w");
+ for (i = 1; i < s1->nb_sections; ++i) {
+ s = s1->sections[i];
+ pe_print_section(f, s);
+ }
+ pe_print_section(f, s1->dynsymtab_section);
+ fclose(f);
+}
+#endif
+
+/* ------------------------------------------------------------- */
+/* helper function for load/store to insert one more indirection */
+
+ST_FUNC SValue *pe_getimport(SValue *sv, SValue *v2)
+{
+ Sym *sym;
+ ElfW(Sym) *esym;
+ int r2;
+
+ if ((sv->r & (VT_VALMASK|VT_SYM)) != (VT_CONST|VT_SYM) || (sv->r2 != VT_CONST))
+ return sv;
+ sym = sv->sym;
+ if ((sym->type.t & (VT_EXTERN|VT_STATIC)) != VT_EXTERN)
+ return sv;
+ if (!sym->c)
+ put_extern_sym(sym, NULL, 0, 0);
+ esym = &((ElfW(Sym) *)symtab_section->data)[sym->c];
+ if (!(esym->st_other & ST_PE_IMPORT))
+ return sv;
+
+ // printf("import %04x %04x %04x %s\n", sv->type.t, sym->type.t, sv->r, get_tok_str(sv->sym->v, NULL));
+
+ memset(v2, 0, sizeof *v2);
+ v2->type.t = VT_PTR;
+ v2->r = VT_CONST | VT_SYM | VT_LVAL;
+ v2->sym = sv->sym;
+
+ r2 = get_reg(RC_INT);
+ load(r2, v2);
+ v2->r = r2;
+
+ if (sv->c.ui) {
+ vpushv(v2);
+ vpushi(sv->c.ui);
+ gen_opi('+');
+ *v2 = *vtop--;
+ }
+
+ v2->type.t = sv->type.t;
+ v2->r |= sv->r & VT_LVAL;
+ return v2;
+}
+
+ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value)
+{
+ return add_elf_sym(
+ s1->dynsymtab_section,
+ value,
+ dllindex, /* st_size */
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE),
+ 0,
+ value ? SHN_ABS : SHN_UNDEF,
+ name
+ );
+}
+
+static int add_dllref(TCCState *s1, const char *dllname)
+{
+ DLLReference *dllref;
+ int i;
+ for (i = 0; i < s1->nb_loaded_dlls; ++i)
+ if (0 == strcmp(s1->loaded_dlls[i]->name, dllname))
+ return i + 1;
+ dllref = tcc_mallocz(sizeof(DLLReference) + strlen(dllname));
+ strcpy(dllref->name, dllname);
+ dynarray_add((void ***) &s1->loaded_dlls, &s1->nb_loaded_dlls, dllref);
+ return s1->nb_loaded_dlls;
+}
+
+/* ------------------------------------------------------------- */
+
+static int read_mem(int fd, unsigned offset, void *buffer, unsigned len)
+{
+ lseek(fd, offset, SEEK_SET);
+ return len == read(fd, buffer, len);
+}
+
+/* -------------------------------------------------------------
+ * This is for compiled windows resources in 'coff' format
+ * as generated by 'windres.exe -O coff ...'.
+ */
+
+static int pe_load_res(TCCState *s1, int fd)
+{
+ struct pe_rsrc_header hdr;
+ Section *rsrc_section;
+ int i, ret = -1;
+ BYTE *ptr;
+ unsigned offs;
+
+ if (!read_mem(fd, 0, &hdr, sizeof hdr))
+ goto quit;
+
+ if (hdr.filehdr.Machine != IMAGE_FILE_MACHINE
+ || hdr.filehdr.NumberOfSections != 1
+ || strcmp(hdr.sectionhdr.Name, ".rsrc") != 0)
+ goto quit;
+
+ rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC);
+ ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData);
+ offs = hdr.sectionhdr.PointerToRawData;
+ if (!read_mem(fd, offs, ptr, hdr.sectionhdr.SizeOfRawData))
+ goto quit;
+ offs = hdr.sectionhdr.PointerToRelocations;
+ for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i)
+ {
+ struct pe_rsrc_reloc rel;
+ if (!read_mem(fd, offs, &rel, sizeof rel))
+ goto quit;
+ // printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type);
+ if (rel.type != RSRC_RELTYPE)
+ goto quit;
+ put_elf_reloc(symtab_section, rsrc_section,
+ rel.offset, R_XXX_RELATIVE, 0);
+ offs += sizeof rel;
+ }
+ ret = 0;
+quit:
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+static char *trimfront(char *p)
+{
+ while (*p && (unsigned char)*p <= ' ')
+ ++p;
+ return p;
+}
+
+static char *trimback(char *a, char *e)
+{
+ while (e > a && (unsigned char)e[-1] <= ' ')
+ --e;
+ *e = 0;;
+ return a;
+}
+
+/* ------------------------------------------------------------- */
+static int pe_load_def(TCCState *s1, int fd)
+{
+ int state = 0, ret = -1, dllindex = 0, ord;
+ char line[400], dllname[80], *p, *x;
+ FILE *fp;
+
+ fp = fdopen(dup(fd), "rb");
+ while (fgets(line, sizeof line, fp))
+ {
+ p = trimfront(trimback(line, strchr(line, 0)));
+ if (0 == *p || ';' == *p)
+ continue;
+
+ switch (state) {
+ case 0:
+ if (0 != strnicmp(p, "LIBRARY", 7))
+ goto quit;
+ pstrcpy(dllname, sizeof dllname, trimfront(p+7));
+ ++state;
+ continue;
+
+ case 1:
+ if (0 != stricmp(p, "EXPORTS"))
+ goto quit;
+ ++state;
+ continue;
+
+ case 2:
+ dllindex = add_dllref(s1, dllname);
+ ++state;
+ /* fall through */
+ default:
+ /* get ordinal and will store in sym->st_value */
+ ord = 0;
+ x = strchr(p, ' ');
+ if (x) {
+ *x = 0, x = strrchr(x + 1, '@');
+ if (x) {
+ char *d;
+ ord = (int)strtol(x + 1, &d, 10);
+ if (*d)
+ ord = 0;
+ }
+ }
+ pe_putimport(s1, dllindex, p, ord);
+ continue;
+ }
+ }
+ ret = 0;
+quit:
+ fclose(fp);
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+#define TINY_IMPDEF_GET_EXPORT_NAMES_ONLY
+#include "../win32/tools/tiny_impdef.c"
+
+static int pe_load_dll(TCCState *s1, const char *dllname, int fd)
+{
+ char *p, *q;
+ int index;
+ p = get_export_names(fd);
+ if (!p)
+ return -1;
+ index = add_dllref(s1, dllname);
+ for (q = p; *q; q += 1 + strlen(q))
+ pe_putimport(s1, index, q, 0);
+ tcc_free(p);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+ST_FUNC int pe_load_file(struct TCCState *s1, const char *filename, int fd)
+{
+ int ret = -1;
+ char buf[10];
+ if (0 == strcmp(tcc_fileextension(filename), ".def"))
+ ret = pe_load_def(s1, fd);
+ else if (pe_load_res(s1, fd) == 0)
+ ret = 0;
+ else if (read_mem(fd, 0, buf, sizeof buf) && 0 == strncmp(buf, "MZ", 2))
+ ret = pe_load_dll(s1, tcc_basename(filename), fd);
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
+#ifdef TCC_TARGET_X86_64
+static unsigned pe_add_uwwind_info(TCCState *s1)
+{
+ if (NULL == s1->uw_pdata) {
+ s1->uw_pdata = find_section(tcc_state, ".pdata");
+ s1->uw_pdata->sh_addralign = 4;
+ s1->uw_sym = put_elf_sym(symtab_section, 0, 0, 0, 0, text_section->sh_num, NULL);
+ }
+
+ if (0 == s1->uw_offs) {
+ /* As our functions all have the same stackframe, we use one entry for all */
+ static const unsigned char uw_info[] = {
+ 0x01, // UBYTE: 3 Version , UBYTE: 5 Flags
+ 0x04, // UBYTE Size of prolog
+ 0x02, // UBYTE Count of unwind codes
+ 0x05, // UBYTE: 4 Frame Register (rbp), UBYTE: 4 Frame Register offset (scaled)
+ // USHORT * n Unwind codes array
+ // 0x0b, 0x01, 0xff, 0xff, // stack size
+ 0x04, 0x03, // set frame ptr (mov rsp -> rbp)
+ 0x01, 0x50 // push reg (rbp)
+ };
+
+ Section *s = text_section;
+ unsigned char *p;
+
+ section_ptr_add(s, -s->data_offset & 3); /* align */
+ s1->uw_offs = s->data_offset;
+ p = section_ptr_add(s, sizeof uw_info);
+ memcpy(p, uw_info, sizeof uw_info);
+ }
+
+ return s1->uw_offs;
+}
+
+ST_FUNC void pe_add_unwind_data(unsigned start, unsigned end, unsigned stack)
+{
+ TCCState *s1 = tcc_state;
+ Section *pd;
+ unsigned o, n, d;
+ struct /* _RUNTIME_FUNCTION */ {
+ DWORD BeginAddress;
+ DWORD EndAddress;
+ DWORD UnwindData;
+ } *p;
+
+ d = pe_add_uwwind_info(s1);
+ pd = s1->uw_pdata;
+ o = pd->data_offset;
+ p = section_ptr_add(pd, sizeof *p);
+
+ /* record this function */
+ p->BeginAddress = start;
+ p->EndAddress = end;
+ p->UnwindData = d;
+
+ /* put relocations on it */
+ for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
+ put_elf_reloc(symtab_section, pd, o, R_X86_64_RELATIVE, s1->uw_sym);
+}
+#endif
+/* ------------------------------------------------------------- */
+#ifdef TCC_TARGET_X86_64
+#define PE_STDSYM(n,s) n
+#else
+#define PE_STDSYM(n,s) "_" n s
+#endif
+
+static void pe_add_runtime(TCCState *s1, struct pe_info *pe)
+{
+ const char *start_symbol;
+ int pe_type = 0;
+
+ if (find_elf_sym(symtab_section, PE_STDSYM("WinMain","@16")))
+ pe_type = PE_GUI;
+ else
+ if (TCC_OUTPUT_DLL == s1->output_type) {
+ pe_type = PE_DLL;
+ /* need this for 'tccelf.c:relocate_section()' */
+ s1->output_type = TCC_OUTPUT_EXE;
+ }
+ else
+ pe_type = PE_EXE;
+
+ start_symbol =
+ TCC_OUTPUT_MEMORY == s1->output_type
+ ? PE_GUI == pe_type ? "__runwinmain" : "_main"
+ : PE_DLL == pe_type ? PE_STDSYM("__dllstart","@12")
+ : PE_GUI == pe_type ? "__winstart" : "__start"
+ ;
+
+ if (!s1->leading_underscore || strchr(start_symbol, '@'))
+ ++start_symbol;
+
+ /* grab the startup code from libtcc1 */
+ if (TCC_OUTPUT_MEMORY != s1->output_type || PE_GUI == pe_type)
+ add_elf_sym(symtab_section,
+ 0, 0,
+ ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
+ SHN_UNDEF, start_symbol);
+
+ tcc_add_pragma_libs(s1);
+
+ if (0 == s1->nostdlib) {
+ static const char *libs[] = {
+ "tcc1", "msvcrt", "kernel32", "", "user32", "gdi32", NULL
+ };
+ const char **pp, *p;
+ for (pp = libs; 0 != (p = *pp); ++pp) {
+ if (0 == *p) {
+ if (PE_DLL != pe_type && PE_GUI != pe_type)
+ break;
+ } else if (tcc_add_library_err(s1, p) < 0) {
+ break;
+ }
+ }
+ }
+
+ if (TCC_OUTPUT_MEMORY == s1->output_type) {
+ pe_type = PE_RUN;
+#ifdef TCC_IS_NATIVE
+ s1->runtime_main = start_symbol;
+#endif
+ } else {
+ pe->start_addr = (DWORD)(uintptr_t)tcc_get_symbol_err(s1, start_symbol);
+ }
+
+ pe->type = pe_type;
+}
+
+ST_FUNC int pe_output_file(TCCState * s1, const char *filename)
+{
+ int ret;
+ struct pe_info pe;
+ int i;
+
+ memset(&pe, 0, sizeof pe);
+ pe.filename = filename;
+ pe.s1 = s1;
+
+ pe_add_runtime(s1, &pe);
+ tcc_add_bcheck(s1);
+ relocate_common_syms(); /* assign bss adresses */
+ tcc_add_linker_symbols(s1);
+
+ ret = pe_check_symbols(&pe);
+ if (ret)
+ ;
+ else if (filename) {
+ if (PE_DLL == pe.type) {
+ pe.reloc = new_section(pe.s1, ".reloc", SHT_PROGBITS, 0);
+ /* XXX: check if is correct for arm-pe target */
+ pe.imagebase = 0x10000000;
+ } else {
+#if defined(TCC_TARGET_ARM)
+ pe.imagebase = 0x00010000;
+#else
+ pe.imagebase = 0x00400000;
+#endif
+ }
+
+#if defined(TCC_TARGET_ARM)
+ /* we use "console" subsystem by default */
+ pe.subsystem = 9;
+#else
+ if (PE_DLL == pe.type || PE_GUI == pe.type)
+ pe.subsystem = 2;
+ else
+ pe.subsystem = 3;
+#endif
+ /* Allow override via -Wl,-subsystem=... option */
+ if (s1->pe_subsystem != 0)
+ pe.subsystem = s1->pe_subsystem;
+
+ /* set default file/section alignment */
+ if (pe.subsystem == 1) {
+ pe.section_align = 0x20;
+ pe.file_align = 0x20;
+ } else {
+ pe.section_align = 0x1000;
+ pe.file_align = 0x200;
+ }
+
+ if (s1->section_align != 0)
+ pe.section_align = s1->section_align;
+ if (s1->pe_file_align != 0)
+ pe.file_align = s1->pe_file_align;
+
+ if ((pe.subsystem >= 10) && (pe.subsystem <= 12))
+ pe.imagebase = 0;
+
+ if (s1->has_text_addr)
+ pe.imagebase = s1->text_addr;
+
+ pe_assign_addresses(&pe);
+ relocate_syms(s1, 0);
+ for (i = 1; i < s1->nb_sections; ++i) {
+ Section *s = s1->sections[i];
+ if (s->reloc) {
+ relocate_section(s1, s);
+ pe_relocate_rva(&pe, s);
+ }
+ }
+ if (s1->nb_errors)
+ ret = -1;
+ else
+ ret = pe_write(&pe);
+ tcc_free(pe.sec_info);
+ } else {
+#ifdef TCC_IS_NATIVE
+ pe.thunk = data_section;
+ pe_build_imports(&pe);
+#endif
+ }
+
+#ifdef PE_PRINT_SECTIONS
+ pe_print_sections(s1, "tcc.log");
+#endif
+ return ret;
+}
+
+/* ------------------------------------------------------------- */
diff --git a/src/tccpp.c b/src/tccpp.c
new file mode 100644
index 0000000..eed09b5
--- /dev/null
+++ b/src/tccpp.c
@@ -0,0 +1,3430 @@
+/*
+ * TCC - Tiny C Compiler
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+/********************************************************/
+/* global variables */
+
+ST_DATA int tok_flags;
+ST_DATA int parse_flags;
+
+ST_DATA struct BufferedFile *file;
+ST_DATA int ch, tok;
+ST_DATA CValue tokc;
+ST_DATA const int *macro_ptr;
+ST_DATA CString tokcstr; /* current parsed string, if any */
+
+/* display benchmark infos */
+ST_DATA int total_lines;
+ST_DATA int total_bytes;
+ST_DATA int tok_ident;
+ST_DATA TokenSym **table_ident;
+
+/* ------------------------------------------------------------------------- */
+
+static TokenSym *hash_ident[TOK_HASH_SIZE];
+static char token_buf[STRING_MAX_SIZE + 1];
+static unsigned char isidnum_table[256 - CH_EOF];
+/* isidnum_table flags: */
+#define IS_SPC 1
+#define IS_ID 2
+#define IS_NUM 4
+
+static TokenString *macro_stack;
+
+static const char tcc_keywords[] =
+#define DEF(id, str) str "\0"
+#include "tcctok.h"
+#undef DEF
+;
+
+/* WARNING: the content of this string encodes token numbers */
+static const unsigned char tok_two_chars[] =
+/* outdated -- gr
+ "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253"
+ "-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266";
+*/{
+ '<','=', TOK_LE,
+ '>','=', TOK_GE,
+ '!','=', TOK_NE,
+ '&','&', TOK_LAND,
+ '|','|', TOK_LOR,
+ '+','+', TOK_INC,
+ '-','-', TOK_DEC,
+ '=','=', TOK_EQ,
+ '<','<', TOK_SHL,
+ '>','>', TOK_SAR,
+ '+','=', TOK_A_ADD,
+ '-','=', TOK_A_SUB,
+ '*','=', TOK_A_MUL,
+ '/','=', TOK_A_DIV,
+ '%','=', TOK_A_MOD,
+ '&','=', TOK_A_AND,
+ '^','=', TOK_A_XOR,
+ '|','=', TOK_A_OR,
+ '-','>', TOK_ARROW,
+ '.','.', 0xa8, // C++ token ?
+ '#','#', TOK_TWOSHARPS,
+ 0
+};
+
+static void next_nomacro_spc(void);
+
+ST_FUNC void skip(int c)
+{
+ if (tok != c)
+ tcc_error("'%c' expected (got \"%s\")", c, get_tok_str(tok, &tokc));
+ next();
+}
+
+ST_FUNC void expect(const char *msg)
+{
+ tcc_error("%s expected", msg);
+}
+
+ST_FUNC void begin_macro(TokenString *str, int alloc)
+{
+ str->alloc = alloc;
+ str->prev = macro_stack;
+ str->prev_ptr = macro_ptr;
+ macro_ptr = str->str;
+ macro_stack = str;
+}
+
+ST_FUNC void end_macro(void)
+{
+ TokenString *str = macro_stack;
+ macro_stack = str->prev;
+ macro_ptr = str->prev_ptr;
+ if (str->alloc == 2) {
+ str->alloc = 3; /* just mark as finished */
+ } else {
+ tok_str_free(str->str);
+ if (str->alloc == 1)
+ tcc_free(str);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* CString handling */
+static void cstr_realloc(CString *cstr, int new_size)
+{
+ int size;
+ void *data;
+
+ size = cstr->size_allocated;
+ if (size == 0)
+ size = 8; /* no need to allocate a too small first string */
+ while (size < new_size)
+ size = size * 2;
+ data = tcc_realloc(cstr->data_allocated, size);
+ cstr->data_allocated = data;
+ cstr->size_allocated = size;
+ cstr->data = data;
+}
+
+/* add a byte */
+ST_FUNC void cstr_ccat(CString *cstr, int ch)
+{
+ int size;
+ size = cstr->size + 1;
+ if (size > cstr->size_allocated)
+ cstr_realloc(cstr, size);
+ ((unsigned char *)cstr->data)[size - 1] = ch;
+ cstr->size = size;
+}
+
+ST_FUNC void cstr_cat(CString *cstr, const char *str)
+{
+ int c;
+ for(;;) {
+ c = *str;
+ if (c == '\0')
+ break;
+ cstr_ccat(cstr, c);
+ str++;
+ }
+}
+
+/* add a wide char */
+ST_FUNC void cstr_wccat(CString *cstr, int ch)
+{
+ int size;
+ size = cstr->size + sizeof(nwchar_t);
+ if (size > cstr->size_allocated)
+ cstr_realloc(cstr, size);
+ *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch;
+ cstr->size = size;
+}
+
+ST_FUNC void cstr_new(CString *cstr)
+{
+ memset(cstr, 0, sizeof(CString));
+}
+
+/* free string and reset it to NULL */
+ST_FUNC void cstr_free(CString *cstr)
+{
+ tcc_free(cstr->data_allocated);
+ cstr_new(cstr);
+}
+
+/* reset string to empty */
+ST_FUNC void cstr_reset(CString *cstr)
+{
+ cstr->size = 0;
+}
+
+/* XXX: unicode ? */
+static void add_char(CString *cstr, int c)
+{
+ if (c == '\'' || c == '\"' || c == '\\') {
+ /* XXX: could be more precise if char or string */
+ cstr_ccat(cstr, '\\');
+ }
+ if (c >= 32 && c <= 126) {
+ cstr_ccat(cstr, c);
+ } else {
+ cstr_ccat(cstr, '\\');
+ if (c == '\n') {
+ cstr_ccat(cstr, 'n');
+ } else {
+ cstr_ccat(cstr, '0' + ((c >> 6) & 7));
+ cstr_ccat(cstr, '0' + ((c >> 3) & 7));
+ cstr_ccat(cstr, '0' + (c & 7));
+ }
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/* allocate a new token */
+static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
+{
+ TokenSym *ts, **ptable;
+ int i;
+
+ if (tok_ident >= SYM_FIRST_ANOM)
+ tcc_error("memory full (symbols)");
+
+ /* expand token table if needed */
+ i = tok_ident - TOK_IDENT;
+ if ((i % TOK_ALLOC_INCR) == 0) {
+ ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *));
+ table_ident = ptable;
+ }
+
+ ts = tcc_malloc(sizeof(TokenSym) + len);
+ table_ident[i] = ts;
+ ts->tok = tok_ident++;
+ ts->sym_define = NULL;
+ ts->sym_label = NULL;
+ ts->sym_struct = NULL;
+ ts->sym_identifier = NULL;
+ ts->len = len;
+ ts->hash_next = NULL;
+ memcpy(ts->str, str, len);
+ ts->str[len] = '\0';
+ *pts = ts;
+ return ts;
+}
+
+#define TOK_HASH_INIT 1
+#define TOK_HASH_FUNC(h, c) ((h) * 263 + (c))
+
+/* find a token and add it if not found */
+ST_FUNC TokenSym *tok_alloc(const char *str, int len)
+{
+ TokenSym *ts, **pts;
+ int i;
+ unsigned int h;
+
+ h = TOK_HASH_INIT;
+ for(i=0;i<len;i++)
+ h = TOK_HASH_FUNC(h, ((unsigned char *)str)[i]);
+ h &= (TOK_HASH_SIZE - 1);
+
+ pts = &hash_ident[h];
+ for(;;) {
+ ts = *pts;
+ if (!ts)
+ break;
+ if (ts->len == len && !memcmp(ts->str, str, len))
+ return ts;
+ pts = &(ts->hash_next);
+ }
+ return tok_alloc_new(pts, str, len);
+}
+
+/* XXX: buffer overflow */
+/* XXX: float tokens */
+ST_FUNC const char *get_tok_str(int v, CValue *cv)
+{
+ static char buf[STRING_MAX_SIZE + 1];
+ static CString cstr_buf;
+ CString *cstr;
+ char *p;
+ int i, len;
+
+ /* NOTE: to go faster, we give a fixed buffer for small strings */
+ cstr_reset(&cstr_buf);
+ cstr_buf.data = buf;
+ cstr_buf.size_allocated = sizeof(buf);
+ p = buf;
+
+ switch(v) {
+ case TOK_CINT:
+ case TOK_CUINT:
+ /* XXX: not quite exact, but only useful for testing */
+ sprintf(p, "%u", cv->ui);
+ break;
+ case TOK_CLLONG:
+ case TOK_CULLONG:
+ /* XXX: not quite exact, but only useful for testing */
+#ifdef _WIN32
+ sprintf(p, "%u", (unsigned)cv->ull);
+#else
+ sprintf(p, "%llu", cv->ull);
+#endif
+ break;
+ case TOK_LCHAR:
+ cstr_ccat(&cstr_buf, 'L');
+ case TOK_CCHAR:
+ cstr_ccat(&cstr_buf, '\'');
+ add_char(&cstr_buf, cv->i);
+ cstr_ccat(&cstr_buf, '\'');
+ cstr_ccat(&cstr_buf, '\0');
+ break;
+ case TOK_PPNUM:
+ case TOK_PPSTR:
+ return (char*)cv->cstr->data;
+ case TOK_LSTR:
+ cstr_ccat(&cstr_buf, 'L');
+ case TOK_STR:
+ cstr = cv->cstr;
+ cstr_ccat(&cstr_buf, '\"');
+ if (v == TOK_STR) {
+ len = cstr->size - 1;
+ for(i=0;i<len;i++)
+ add_char(&cstr_buf, ((unsigned char *)cstr->data)[i]);
+ } else {
+ len = (cstr->size / sizeof(nwchar_t)) - 1;
+ for(i=0;i<len;i++)
+ add_char(&cstr_buf, ((nwchar_t *)cstr->data)[i]);
+ }
+ cstr_ccat(&cstr_buf, '\"');
+ cstr_ccat(&cstr_buf, '\0');
+ break;
+
+ case TOK_CFLOAT:
+ case TOK_CDOUBLE:
+ case TOK_CLDOUBLE:
+ case TOK_LINENUM:
+ return NULL; /* should not happen */
+
+ /* above tokens have value, the ones below don't */
+
+ case TOK_LT:
+ v = '<';
+ goto addv;
+ case TOK_GT:
+ v = '>';
+ goto addv;
+ case TOK_DOTS:
+ return strcpy(p, "...");
+ case TOK_A_SHL:
+ return strcpy(p, "<<=");
+ case TOK_A_SAR:
+ return strcpy(p, ">>=");
+ default:
+ if (v < TOK_IDENT) {
+ /* search in two bytes table */
+ const unsigned char *q = tok_two_chars;
+ while (*q) {
+ if (q[2] == v) {
+ *p++ = q[0];
+ *p++ = q[1];
+ *p = '\0';
+ return buf;
+ }
+ q += 3;
+ }
+ if (v >= 127) {
+ sprintf(buf, "<%02x>", v);
+ return buf;
+ }
+ addv:
+ *p++ = v;
+ *p = '\0';
+ } else if (v < tok_ident) {
+ return table_ident[v - TOK_IDENT]->str;
+ } else if (v >= SYM_FIRST_ANOM) {
+ /* special name for anonymous symbol */
+ sprintf(p, "L.%u", v - SYM_FIRST_ANOM);
+ } else {
+ /* should never happen */
+ return NULL;
+ }
+ break;
+ }
+ return cstr_buf.data;
+}
+
+/* return the current character, handling end of block if necessary
+ (but not stray) */
+ST_FUNC int handle_eob(void)
+{
+ BufferedFile *bf = file;
+ int len;
+
+ /* only tries to read if really end of buffer */
+ if (bf->buf_ptr >= bf->buf_end) {
+ if (bf->fd != -1) {
+#if defined(PARSE_DEBUG)
+ len = 1;
+#else
+ len = IO_BUF_SIZE;
+#endif
+ len = read(bf->fd, bf->buffer, len);
+ if (len < 0)
+ len = 0;
+ } else {
+ len = 0;
+ }
+ total_bytes += len;
+ bf->buf_ptr = bf->buffer;
+ bf->buf_end = bf->buffer + len;
+ *bf->buf_end = CH_EOB;
+ }
+ if (bf->buf_ptr < bf->buf_end) {
+ return bf->buf_ptr[0];
+ } else {
+ bf->buf_ptr = bf->buf_end;
+ return CH_EOF;
+ }
+}
+
+/* read next char from current input file and handle end of input buffer */
+ST_INLN void inp(void)
+{
+ ch = *(++(file->buf_ptr));
+ /* end of buffer/file handling */
+ if (ch == CH_EOB)
+ ch = handle_eob();
+}
+
+/* handle '\[\r]\n' */
+static int handle_stray_noerror(void)
+{
+ while (ch == '\\') {
+ inp();
+ if (ch == '\n') {
+ file->line_num++;
+ inp();
+ } else if (ch == '\r') {
+ inp();
+ if (ch != '\n')
+ goto fail;
+ file->line_num++;
+ inp();
+ } else {
+ fail:
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void handle_stray(void)
+{
+ if (handle_stray_noerror())
+ tcc_error("stray '\\' in program");
+}
+
+/* skip the stray and handle the \\n case. Output an error if
+ incorrect char after the stray */
+static int handle_stray1(uint8_t *p)
+{
+ int c;
+
+ file->buf_ptr = p;
+ if (p >= file->buf_end) {
+ c = handle_eob();
+ if (c != '\\')
+ return c;
+ p = file->buf_ptr;
+ }
+ ch = *p;
+ if (handle_stray_noerror()) {
+ if (!(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
+ tcc_error("stray '\\' in program");
+ *--file->buf_ptr = '\\';
+ }
+ p = file->buf_ptr;
+ c = *p;
+ return c;
+}
+
+/* handle just the EOB case, but not stray */
+#define PEEKC_EOB(c, p)\
+{\
+ p++;\
+ c = *p;\
+ if (c == '\\') {\
+ file->buf_ptr = p;\
+ c = handle_eob();\
+ p = file->buf_ptr;\
+ }\
+}
+
+/* handle the complicated stray case */
+#define PEEKC(c, p)\
+{\
+ p++;\
+ c = *p;\
+ if (c == '\\') {\
+ c = handle_stray1(p);\
+ p = file->buf_ptr;\
+ }\
+}
+
+/* input with '\[\r]\n' handling. Note that this function cannot
+ handle other characters after '\', so you cannot call it inside
+ strings or comments */
+ST_FUNC void minp(void)
+{
+ inp();
+ if (ch == '\\')
+ handle_stray();
+}
+
+
+/* single line C++ comments */
+static uint8_t *parse_line_comment(uint8_t *p)
+{
+ int c;
+
+ p++;
+ for(;;) {
+ c = *p;
+ redo:
+ if (c == '\n' || c == CH_EOF) {
+ break;
+ } else if (c == '\\') {
+ file->buf_ptr = p;
+ c = handle_eob();
+ p = file->buf_ptr;
+ if (c == '\\') {
+ PEEKC_EOB(c, p);
+ if (c == '\n') {
+ file->line_num++;
+ PEEKC_EOB(c, p);
+ } else if (c == '\r') {
+ PEEKC_EOB(c, p);
+ if (c == '\n') {
+ file->line_num++;
+ PEEKC_EOB(c, p);
+ }
+ }
+ } else {
+ goto redo;
+ }
+ } else {
+ p++;
+ }
+ }
+ return p;
+}
+
+/* C comments */
+ST_FUNC uint8_t *parse_comment(uint8_t *p)
+{
+ int c;
+
+ p++;
+ for(;;) {
+ /* fast skip loop */
+ for(;;) {
+ c = *p;
+ if (c == '\n' || c == '*' || c == '\\')
+ break;
+ p++;
+ c = *p;
+ if (c == '\n' || c == '*' || c == '\\')
+ break;
+ p++;
+ }
+ /* now we can handle all the cases */
+ if (c == '\n') {
+ file->line_num++;
+ p++;
+ } else if (c == '*') {
+ p++;
+ for(;;) {
+ c = *p;
+ if (c == '*') {
+ p++;
+ } else if (c == '/') {
+ goto end_of_comment;
+ } else if (c == '\\') {
+ file->buf_ptr = p;
+ c = handle_eob();
+ p = file->buf_ptr;
+ if (c == '\\') {
+ /* skip '\[\r]\n', otherwise just skip the stray */
+ while (c == '\\') {
+ PEEKC_EOB(c, p);
+ if (c == '\n') {
+ file->line_num++;
+ PEEKC_EOB(c, p);
+ } else if (c == '\r') {
+ PEEKC_EOB(c, p);
+ if (c == '\n') {
+ file->line_num++;
+ PEEKC_EOB(c, p);
+ }
+ } else {
+ goto after_star;
+ }
+ }
+ }
+ } else {
+ break;
+ }
+ }
+ after_star: ;
+ } else {
+ /* stray, eob or eof */
+ file->buf_ptr = p;
+ c = handle_eob();
+ p = file->buf_ptr;
+ if (c == CH_EOF) {
+ tcc_error("unexpected end of file in comment");
+ } else if (c == '\\') {
+ p++;
+ }
+ }
+ }
+ end_of_comment:
+ p++;
+ return p;
+}
+
+#define cinp minp
+
+static inline void skip_spaces(void)
+{
+ while (isidnum_table[ch - CH_EOF] & IS_SPC)
+ cinp();
+}
+
+static inline int check_space(int t, int *spc)
+{
+ if (t < 256 && (isidnum_table[t - CH_EOF] & IS_SPC)) {
+ if (*spc)
+ return 1;
+ *spc = 1;
+ } else
+ *spc = 0;
+ return 0;
+}
+
+/* parse a string without interpreting escapes */
+static uint8_t *parse_pp_string(uint8_t *p,
+ int sep, CString *str)
+{
+ int c;
+ p++;
+ for(;;) {
+ c = *p;
+ if (c == sep) {
+ break;
+ } else if (c == '\\') {
+ file->buf_ptr = p;
+ c = handle_eob();
+ p = file->buf_ptr;
+ if (c == CH_EOF) {
+ unterminated_string:
+ /* XXX: indicate line number of start of string */
+ tcc_error("missing terminating %c character", sep);
+ } else if (c == '\\') {
+ /* escape : just skip \[\r]\n */
+ PEEKC_EOB(c, p);
+ if (c == '\n') {
+ file->line_num++;
+ p++;
+ } else if (c == '\r') {
+ PEEKC_EOB(c, p);
+ if (c != '\n')
+ expect("'\n' after '\r'");
+ file->line_num++;
+ p++;
+ } else if (c == CH_EOF) {
+ goto unterminated_string;
+ } else {
+ if (str) {
+ cstr_ccat(str, '\\');
+ cstr_ccat(str, c);
+ }
+ p++;
+ }
+ }
+ } else if (c == '\n') {
+ file->line_num++;
+ goto add_char;
+ } else if (c == '\r') {
+ PEEKC_EOB(c, p);
+ if (c != '\n') {
+ if (str)
+ cstr_ccat(str, '\r');
+ } else {
+ file->line_num++;
+ goto add_char;
+ }
+ } else {
+ add_char:
+ if (str)
+ cstr_ccat(str, c);
+ p++;
+ }
+ }
+ p++;
+ return p;
+}
+
+/* skip block of text until #else, #elif or #endif. skip also pairs of
+ #if/#endif */
+static void preprocess_skip(void)
+{
+ int a, start_of_line, c, in_warn_or_error;
+ uint8_t *p;
+
+ p = file->buf_ptr;
+ a = 0;
+redo_start:
+ start_of_line = 1;
+ in_warn_or_error = 0;
+ for(;;) {
+ redo_no_start:
+ c = *p;
+ switch(c) {
+ case ' ':
+ case '\t':
+ case '\f':
+ case '\v':
+ case '\r':
+ p++;
+ goto redo_no_start;
+ case '\n':
+ file->line_num++;
+ p++;
+ goto redo_start;
+ case '\\':
+ file->buf_ptr = p;
+ c = handle_eob();
+ if (c == CH_EOF) {
+ expect("#endif");
+ } else if (c == '\\') {
+ ch = file->buf_ptr[0];
+ handle_stray_noerror();
+ }
+ p = file->buf_ptr;
+ goto redo_no_start;
+ /* skip strings */
+ case '\"':
+ case '\'':
+ if (in_warn_or_error)
+ goto _default;
+ p = parse_pp_string(p, c, NULL);
+ break;
+ /* skip comments */
+ case '/':
+ if (in_warn_or_error)
+ goto _default;
+ file->buf_ptr = p;
+ ch = *p;
+ minp();
+ p = file->buf_ptr;
+ if (ch == '*') {
+ p = parse_comment(p);
+ } else if (ch == '/') {
+ p = parse_line_comment(p);
+ }
+ break;
+ case '#':
+ p++;
+ if (start_of_line) {
+ file->buf_ptr = p;
+ next_nomacro();
+ p = file->buf_ptr;
+ if (a == 0 &&
+ (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF))
+ goto the_end;
+ if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF)
+ a++;
+ else if (tok == TOK_ENDIF)
+ a--;
+ else if( tok == TOK_ERROR || tok == TOK_WARNING)
+ in_warn_or_error = 1;
+ else if (tok == TOK_LINEFEED)
+ goto redo_start;
+ } else if (parse_flags & PARSE_FLAG_ASM_FILE)
+ p = parse_line_comment(p);
+ break;
+_default:
+ default:
+ p++;
+ break;
+ }
+ start_of_line = 0;
+ }
+ the_end: ;
+ file->buf_ptr = p;
+}
+
+/* ParseState handling */
+
+/* XXX: currently, no include file info is stored. Thus, we cannot display
+ accurate messages if the function or data definition spans multiple
+ files */
+
+/* save current parse state in 's' */
+ST_FUNC void save_parse_state(ParseState *s)
+{
+ s->line_num = file->line_num;
+ s->macro_ptr = macro_ptr;
+ s->tok = tok;
+ s->tokc = tokc;
+}
+
+/* restore parse state from 's' */
+ST_FUNC void restore_parse_state(ParseState *s)
+{
+ file->line_num = s->line_num;
+ macro_ptr = s->macro_ptr;
+ tok = s->tok;
+ tokc = s->tokc;
+}
+
+/* return the number of additional 'ints' necessary to store the
+ token */
+static inline int tok_size(const int *p)
+{
+ switch(*p) {
+ /* 4 bytes */
+ case TOK_CINT:
+ case TOK_CUINT:
+ case TOK_CCHAR:
+ case TOK_LCHAR:
+ case TOK_CFLOAT:
+ case TOK_LINENUM:
+ return 1 + 1;
+ case TOK_STR:
+ case TOK_LSTR:
+ case TOK_PPNUM:
+ case TOK_PPSTR:
+ return 1 + ((sizeof(CString) + ((CString *)(p+1))->size + 3) >> 2);
+ case TOK_CDOUBLE:
+ case TOK_CLLONG:
+ case TOK_CULLONG:
+ return 1 + 2;
+ case TOK_CLDOUBLE:
+ return 1 + LDOUBLE_SIZE / 4;
+ default:
+ return 1 + 0;
+ }
+}
+
+/* token string handling */
+
+ST_INLN void tok_str_new(TokenString *s)
+{
+ s->str = NULL;
+ s->len = 0;
+ s->allocated_len = 0;
+ s->last_line_num = -1;
+}
+
+ST_FUNC void tok_str_free(int *str)
+{
+ tcc_free(str);
+}
+
+static int *tok_str_realloc(TokenString *s)
+{
+ int *str, len;
+
+ if (s->allocated_len == 0) {
+ len = 8;
+ } else {
+ len = s->allocated_len * 2;
+ }
+ str = tcc_realloc(s->str, len * sizeof(int));
+ s->allocated_len = len;
+ s->str = str;
+ return str;
+}
+
+ST_FUNC void tok_str_add(TokenString *s, int t)
+{
+ int len, *str;
+
+ len = s->len;
+ str = s->str;
+ if (len >= s->allocated_len)
+ str = tok_str_realloc(s);
+ str[len++] = t;
+ s->len = len;
+}
+
+static void tok_str_add2(TokenString *s, int t, CValue *cv)
+{
+ int len, *str;
+
+ len = s->len;
+ str = s->str;
+
+ /* allocate space for worst case */
+ if (len + TOK_MAX_SIZE > s->allocated_len)
+ str = tok_str_realloc(s);
+ str[len++] = t;
+ switch(t) {
+ case TOK_CINT:
+ case TOK_CUINT:
+ case TOK_CCHAR:
+ case TOK_LCHAR:
+ case TOK_CFLOAT:
+ case TOK_LINENUM:
+ str[len++] = cv->tab[0];
+ break;
+ case TOK_PPNUM:
+ case TOK_PPSTR:
+ case TOK_STR:
+ case TOK_LSTR:
+ {
+ int nb_words;
+ CString *cstr;
+
+ nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2;
+ while ((len + nb_words) > s->allocated_len)
+ str = tok_str_realloc(s);
+ cstr = (CString *)(str + len);
+ cstr->data = NULL;
+ cstr->size = cv->cstr->size;
+ cstr->data_allocated = NULL;
+ cstr->size_allocated = cstr->size;
+ memcpy((char *)cstr + sizeof(CString),
+ cv->cstr->data, cstr->size);
+ len += nb_words;
+ }
+ break;
+ case TOK_CDOUBLE:
+ case TOK_CLLONG:
+ case TOK_CULLONG:
+#if LDOUBLE_SIZE == 8
+ case TOK_CLDOUBLE:
+#endif
+ str[len++] = cv->tab[0];
+ str[len++] = cv->tab[1];
+ break;
+#if LDOUBLE_SIZE == 12
+ case TOK_CLDOUBLE:
+ str[len++] = cv->tab[0];
+ str[len++] = cv->tab[1];
+ str[len++] = cv->tab[2];
+#elif LDOUBLE_SIZE == 16
+ case TOK_CLDOUBLE:
+ str[len++] = cv->tab[0];
+ str[len++] = cv->tab[1];
+ str[len++] = cv->tab[2];
+ str[len++] = cv->tab[3];
+#elif LDOUBLE_SIZE != 8
+#error add long double size support
+#endif
+ break;
+ default:
+ break;
+ }
+ s->len = len;
+}
+
+/* add the current parse token in token string 's' */
+ST_FUNC void tok_str_add_tok(TokenString *s)
+{
+ CValue cval;
+
+ /* save line number info */
+ if (file->line_num != s->last_line_num) {
+ s->last_line_num = file->line_num;
+ cval.i = s->last_line_num;
+ tok_str_add2(s, TOK_LINENUM, &cval);
+ }
+ tok_str_add2(s, tok, &tokc);
+}
+
+/* get a token from an integer array and increment pointer
+ accordingly. we code it as a macro to avoid pointer aliasing. */
+static inline void TOK_GET(int *t, const int **pp, CValue *cv)
+{
+ const int *p = *pp;
+ int n, *tab;
+
+ tab = cv->tab;
+ switch(*t = *p++) {
+ case TOK_CINT:
+ case TOK_CUINT:
+ case TOK_CCHAR:
+ case TOK_LCHAR:
+ case TOK_CFLOAT:
+ case TOK_LINENUM:
+ tab[0] = *p++;
+ break;
+ case TOK_STR:
+ case TOK_LSTR:
+ case TOK_PPNUM:
+ case TOK_PPSTR:
+ cv->cstr = (CString *)p;
+ cv->cstr->data = (char *)p + sizeof(CString);
+ p += (sizeof(CString) + cv->cstr->size + 3) >> 2;
+ break;
+ case TOK_CDOUBLE:
+ case TOK_CLLONG:
+ case TOK_CULLONG:
+ n = 2;
+ goto copy;
+ case TOK_CLDOUBLE:
+#if LDOUBLE_SIZE == 16
+ n = 4;
+#elif LDOUBLE_SIZE == 12
+ n = 3;
+#elif LDOUBLE_SIZE == 8
+ n = 2;
+#else
+# error add long double size support
+#endif
+ copy:
+ do
+ *tab++ = *p++;
+ while (--n);
+ break;
+ default:
+ break;
+ }
+ *pp = p;
+}
+
+static int macro_is_equal(const int *a, const int *b)
+{
+ char buf[STRING_MAX_SIZE + 1];
+ CValue cv;
+ int t;
+ while (*a && *b) {
+ TOK_GET(&t, &a, &cv);
+ pstrcpy(buf, sizeof buf, get_tok_str(t, &cv));
+ TOK_GET(&t, &b, &cv);
+ if (strcmp(buf, get_tok_str(t, &cv)))
+ return 0;
+ }
+ return !(*a || *b);
+}
+
+static void pp_line(TCCState *s1, BufferedFile *f, int level)
+{
+ int d = f->line_num - f->line_ref;
+ if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_NONE
+ || (level == 0 && f->line_ref && d < 8))
+ {
+ while (d > 0)
+ fputs("\n", s1->ppfp), --d;
+ }
+ else
+ if (s1->Pflag == LINE_MACRO_OUTPUT_FORMAT_STD) {
+ fprintf(s1->ppfp, "#line %d \"%s\"\n", f->line_num, f->filename);
+ }
+ else {
+ fprintf(s1->ppfp, "# %d \"%s\"%s\n", f->line_num, f->filename,
+ level > 0 ? " 1" : level < 0 ? " 2" : "");
+ }
+ f->line_ref = f->line_num;
+}
+
+static void tok_print(const char *msg, const int *str)
+{
+ FILE *pr = tcc_state->ppfp;
+ int t;
+ CValue cval;
+
+ fprintf(pr, "%s ", msg);
+ while (str) {
+ TOK_GET(&t, &str, &cval);
+ if (!t)
+ break;
+ fprintf(pr,"%s", get_tok_str(t, &cval));
+ }
+ fprintf(pr, "\n");
+}
+
+static int define_print_prepared(Sym *s)
+{
+ if (!s || !tcc_state->ppfp || tcc_state->dflag == 0)
+ return 0;
+
+ if (s->v < TOK_IDENT || s->v >= tok_ident)
+ return 0;
+
+ if (file) {
+ file->line_num--;
+ pp_line(tcc_state, file, 0);
+ file->line_ref = ++file->line_num;
+ }
+ return 1;
+}
+
+static void define_print(int v)
+{
+ FILE *pr = tcc_state->ppfp;
+ Sym *s, *a;
+
+ s = define_find(v);
+ if (define_print_prepared(s) == 0)
+ return;
+
+ fprintf(pr, "// #define %s", get_tok_str(v, NULL));
+ if (s->type.t == MACRO_FUNC) {
+ a = s->next;
+ fprintf(pr,"(");
+ if (a)
+ for (;;) {
+ fprintf(pr,"%s", get_tok_str(a->v & ~SYM_FIELD, NULL));
+ if (!(a = a->next))
+ break;
+ fprintf(pr,",");
+ }
+ fprintf(pr,")");
+ }
+ tok_print("", s->d);
+}
+
+static void undef_print(int v)
+{
+ FILE *pr = tcc_state->ppfp;
+ Sym *s;
+
+ s = define_find(v);
+ if (define_print_prepared(s) == 0)
+ return;
+
+ fprintf(pr, "// #undef %s\n", get_tok_str(s->v, NULL));
+}
+
+ST_FUNC void print_defines(void)
+{
+ Sym *top = define_stack;
+ while (top) {
+ define_print(top->v);
+ top = top->prev;
+ }
+}
+
+/* defines handling */
+ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg)
+{
+ Sym *s;
+
+ s = define_find(v);
+ if (s && !macro_is_equal(s->d, str))
+ tcc_warning("%s redefined", get_tok_str(v, NULL));
+
+ s = sym_push2(&define_stack, v, macro_type, 0);
+ s->d = str;
+ s->next = first_arg;
+ table_ident[v - TOK_IDENT]->sym_define = s;
+}
+
+/* undefined a define symbol. Its name is just set to zero */
+ST_FUNC void define_undef(Sym *s)
+{
+ int v = s->v;
+ undef_print(v);
+ if (v >= TOK_IDENT && v < tok_ident)
+ table_ident[v - TOK_IDENT]->sym_define = NULL;
+}
+
+ST_INLN Sym *define_find(int v)
+{
+ v -= TOK_IDENT;
+ if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+ return NULL;
+ return table_ident[v]->sym_define;
+}
+
+/* free define stack until top reaches 'b' */
+ST_FUNC void free_defines(Sym *b)
+{
+ Sym *top, *top1;
+ int v;
+
+ top = define_stack;
+ while (top != b) {
+ top1 = top->prev;
+ /* do not free args or predefined defines */
+ if (top->d)
+ tok_str_free(top->d);
+ v = top->v;
+ if (v >= TOK_IDENT && v < tok_ident)
+ table_ident[v - TOK_IDENT]->sym_define = NULL;
+ sym_free(top);
+ top = top1;
+ }
+ define_stack = b;
+}
+
+/* label lookup */
+ST_FUNC Sym *label_find(int v)
+{
+ v -= TOK_IDENT;
+ if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
+ return NULL;
+ return table_ident[v]->sym_label;
+}
+
+ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
+{
+ Sym *s, **ps;
+ s = sym_push2(ptop, v, 0, 0);
+ s->r = flags;
+ ps = &table_ident[v - TOK_IDENT]->sym_label;
+ if (ptop == &global_label_stack) {
+ /* modify the top most local identifier, so that
+ sym_identifier will point to 's' when popped */
+ while (*ps != NULL)
+ ps = &(*ps)->prev_tok;
+ }
+ s->prev_tok = *ps;
+ *ps = s;
+ return s;
+}
+
+/* pop labels until element last is reached. Look if any labels are
+ undefined. Define symbols if '&&label' was used. */
+ST_FUNC void label_pop(Sym **ptop, Sym *slast)
+{
+ Sym *s, *s1;
+ for(s = *ptop; s != slast; s = s1) {
+ s1 = s->prev;
+ if (s->r == LABEL_DECLARED) {
+ tcc_warning("label '%s' declared but not used", get_tok_str(s->v, NULL));
+ } else if (s->r == LABEL_FORWARD) {
+ tcc_error("label '%s' used but not defined",
+ get_tok_str(s->v, NULL));
+ } else {
+ if (s->c) {
+ /* define corresponding symbol. A size of
+ 1 is put. */
+ put_extern_sym(s, cur_text_section, s->jnext, 1);
+ }
+ }
+ /* remove label */
+ table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok;
+ sym_free(s);
+ }
+ *ptop = slast;
+}
+
+/* eval an expression for #if/#elif */
+static int expr_preprocess(void)
+{
+ int c, t;
+ TokenString str;
+
+ tok_str_new(&str);
+ while (tok != TOK_LINEFEED && tok != TOK_EOF) {
+ next(); /* do macro subst */
+ if (tok == TOK_DEFINED) {
+ next_nomacro();
+ t = tok;
+ if (t == '(')
+ next_nomacro();
+ c = define_find(tok) != 0;
+ if (t == '(')
+ next_nomacro();
+ tok = TOK_CINT;
+ tokc.i = c;
+ } else if (tok >= TOK_IDENT) {
+ /* if undefined macro */
+ tok = TOK_CINT;
+ tokc.i = 0;
+ }
+ tok_str_add_tok(&str);
+ }
+ tok_str_add(&str, -1); /* simulate end of file */
+ tok_str_add(&str, 0);
+ /* now evaluate C constant expression */
+ begin_macro(&str, 0);
+ next();
+ c = expr_const();
+ end_macro();
+ return c != 0;
+}
+
+
+/* parse after #define */
+ST_FUNC void parse_define(void)
+{
+ Sym *s, *first, **ps;
+ int v, t, varg, is_vaargs, spc;
+ int saved_parse_flags = parse_flags;
+ TokenString str;
+
+ v = tok;
+ if (v < TOK_IDENT)
+ tcc_error("invalid macro name '%s'", get_tok_str(tok, &tokc));
+ /* XXX: should check if same macro (ANSI) */
+ first = NULL;
+ t = MACRO_OBJ;
+ /* '(' must be just after macro definition for MACRO_FUNC */
+ parse_flags |= PARSE_FLAG_SPACES;
+ next_nomacro_spc();
+ if (tok == '(') {
+ next_nomacro();
+ ps = &first;
+ if (tok != ')') for (;;) {
+ varg = tok;
+ next_nomacro();
+ is_vaargs = 0;
+ if (varg == TOK_DOTS) {
+ varg = TOK___VA_ARGS__;
+ is_vaargs = 1;
+ } else if (tok == TOK_DOTS && gnu_ext) {
+ is_vaargs = 1;
+ next_nomacro();
+ }
+ if (varg < TOK_IDENT)
+ bad_list:
+ tcc_error("bad macro parameter list");
+ s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0);
+ *ps = s;
+ ps = &s->next;
+ if (tok == ')')
+ break;
+ if (tok != ',' || is_vaargs)
+ goto bad_list;
+ next_nomacro();
+ }
+ next_nomacro_spc();
+ t = MACRO_FUNC;
+ }
+ tok_str_new(&str);
+ spc = 2;
+ parse_flags |= PARSE_FLAG_ACCEPT_STRAYS | PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED;
+ while (tok != TOK_LINEFEED && tok != TOK_EOF) {
+ /* remove spaces around ## and after '#' */
+ if (TOK_TWOSHARPS == tok) {
+ if (2 == spc)
+ goto bad_twosharp;
+ if (1 == spc)
+ --str.len;
+ spc = 3;
+ } else if ('#' == tok) {
+ spc = 4;
+ } else if (check_space(tok, &spc)) {
+ goto skip;
+ }
+ tok_str_add2(&str, tok, &tokc);
+ skip:
+ next_nomacro_spc();
+ }
+
+ parse_flags = saved_parse_flags;
+ if (spc == 1)
+ --str.len; /* remove trailing space */
+ tok_str_add(&str, 0);
+ if (3 == spc)
+bad_twosharp:
+ tcc_error("'##' cannot appear at either end of macro");
+ define_push(v, t, str.str, first);
+ define_print(v);
+}
+
+static inline int hash_cached_include(const char *filename)
+{
+ const unsigned char *s;
+ unsigned int h;
+
+ h = TOK_HASH_INIT;
+ s = (unsigned char *) filename;
+ while (*s) {
+ h = TOK_HASH_FUNC(h, *s);
+ s++;
+ }
+ h &= (CACHED_INCLUDES_HASH_SIZE - 1);
+ return h;
+}
+
+static CachedInclude *search_cached_include(TCCState *s1, const char *filename)
+{
+ CachedInclude *e;
+ int i, h;
+ h = hash_cached_include(filename);
+ i = s1->cached_includes_hash[h];
+ for(;;) {
+ if (i == 0)
+ break;
+ e = s1->cached_includes[i - 1];
+ if (0 == PATHCMP(e->filename, filename))
+ return e;
+ i = e->hash_next;
+ }
+ return NULL;
+}
+
+static inline void add_cached_include(TCCState *s1, const char *filename, int ifndef_macro)
+{
+ CachedInclude *e;
+ int h;
+
+ if (search_cached_include(s1, filename))
+ return;
+#ifdef INC_DEBUG
+ printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL));
+#endif
+ e = tcc_malloc(sizeof(CachedInclude) + strlen(filename));
+ strcpy(e->filename, filename);
+ e->ifndef_macro = ifndef_macro;
+ dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e);
+ /* add in hash table */
+ h = hash_cached_include(filename);
+ e->hash_next = s1->cached_includes_hash[h];
+ s1->cached_includes_hash[h] = s1->nb_cached_includes;
+}
+
+static void pragma_parse(TCCState *s1)
+{
+ next_nomacro();
+ if (tok == TOK_push_macro || tok == TOK_pop_macro) {
+ int t = tok, v;
+ Sym *s;
+
+ if (next(), tok != '(')
+ goto pragma_err;
+ if (next(), tok != TOK_STR)
+ goto pragma_err;
+ v = tok_alloc(tokc.cstr->data, tokc.cstr->size - 1)->tok;
+ if (next(), tok != ')')
+ goto pragma_err;
+ if (t == TOK_push_macro) {
+ while (NULL == (s = define_find(v)))
+ define_push(v, 0, NULL, NULL);
+ s->type.ref = s; /* set push boundary */
+ } else {
+ for (s = define_stack; s; s = s->prev)
+ if (s->v == v && s->type.ref == s) {
+ s->type.ref = NULL;
+ break;
+ }
+ }
+ if (s)
+ table_ident[v - TOK_IDENT]->sym_define = s->d ? s : NULL;
+ else
+ tcc_warning("unbalanced #pragma pop_macro");
+
+ } else if (tok == TOK_once) {
+ add_cached_include(s1, file->filename, TOK_once);
+
+ } else if (s1->ppfp) {
+ /* tcc -E: keep pragmas below unchanged */
+ unget_tok(' ');
+ unget_tok(TOK_PRAGMA);
+ unget_tok('#');
+ unget_tok(TOK_LINEFEED);
+
+ } else if (tok == TOK_pack) {
+ /* This may be:
+ #pragma pack(1) // set
+ #pragma pack() // reset to default
+ #pragma pack(push,1) // push & set
+ #pragma pack(pop) // restore previous */
+ next();
+ skip('(');
+ if (tok == TOK_ASM_pop) {
+ next();
+ if (s1->pack_stack_ptr <= s1->pack_stack) {
+ stk_error:
+ tcc_error("out of pack stack");
+ }
+ s1->pack_stack_ptr--;
+ } else {
+ int val = 0;
+ if (tok != ')') {
+ if (tok == TOK_ASM_push) {
+ next();
+ if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1)
+ goto stk_error;
+ s1->pack_stack_ptr++;
+ skip(',');
+ }
+ if (tok != TOK_CINT)
+ goto pragma_err;
+ val = tokc.i;
+ if (val < 1 || val > 16 || (val & (val - 1)) != 0)
+ goto pragma_err;
+ next();
+ }
+ *s1->pack_stack_ptr = val;
+ }
+ if (tok != ')')
+ goto pragma_err;
+
+ } else if (tok == TOK_comment) {
+ char *file;
+ next();
+ skip('(');
+ if (tok != TOK_lib)
+ goto pragma_warn;
+ next();
+ skip(',');
+ if (tok != TOK_STR)
+ goto pragma_err;
+ file = tcc_strdup((char *)tokc.cstr->data);
+ dynarray_add((void ***)&s1->pragma_libs, &s1->nb_pragma_libs, file);
+ next();
+ if (tok != ')')
+ goto pragma_err;
+ } else {
+pragma_warn:
+ if (s1->warn_unsupported)
+ tcc_warning("#pragma %s is ignored", get_tok_str(tok, &tokc));
+ }
+ return;
+
+pragma_err:
+ tcc_error("malformed #pragma directive");
+ return;
+}
+
+/* is_bof is true if first non space token at beginning of file */
+ST_FUNC void preprocess(int is_bof)
+{
+ TCCState *s1 = tcc_state;
+ int i, c, n, saved_parse_flags;
+ char buf[1024], *q;
+ Sym *s;
+
+ saved_parse_flags = parse_flags;
+ parse_flags = PARSE_FLAG_PREPROCESS
+ | PARSE_FLAG_TOK_NUM
+ | PARSE_FLAG_TOK_STR
+ | PARSE_FLAG_LINEFEED
+ | (parse_flags & PARSE_FLAG_ASM_FILE)
+ ;
+
+ next_nomacro();
+ redo:
+ switch(tok) {
+ case TOK_DEFINE:
+ next_nomacro();
+ parse_define();
+ break;
+ case TOK_UNDEF:
+ next_nomacro();
+ s = define_find(tok);
+ /* undefine symbol by putting an invalid name */
+ if (s)
+ define_undef(s);
+ break;
+ case TOK_INCLUDE:
+ case TOK_INCLUDE_NEXT:
+ ch = file->buf_ptr[0];
+ /* XXX: incorrect if comments : use next_nomacro with a special mode */
+ skip_spaces();
+ if (ch == '<') {
+ c = '>';
+ goto read_name;
+ } else if (ch == '\"') {
+ c = ch;
+ read_name:
+ inp();
+ q = buf;
+ while (ch != c && ch != '\n' && ch != CH_EOF) {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = ch;
+ if (ch == '\\') {
+ if (handle_stray_noerror() == 0)
+ --q;
+ } else
+ inp();
+ }
+ *q = '\0';
+ minp();
+#if 0
+ /* eat all spaces and comments after include */
+ /* XXX: slightly incorrect */
+ while (ch1 != '\n' && ch1 != CH_EOF)
+ inp();
+#endif
+ } else {
+ /* computed #include : either we have only strings or
+ we have anything enclosed in '<>' */
+ next();
+ buf[0] = '\0';
+ if (tok == TOK_STR) {
+ while (tok != TOK_LINEFEED) {
+ if (tok != TOK_STR) {
+ include_syntax:
+ tcc_error("'#include' expects \"FILENAME\" or <FILENAME>");
+ }
+ pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data);
+ next();
+ }
+ c = '\"';
+ } else {
+ int len;
+ while (tok != TOK_LINEFEED) {
+ pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc));
+ next();
+ }
+ len = strlen(buf);
+ /* check syntax and remove '<>' */
+ if (len < 2 || buf[0] != '<' || buf[len - 1] != '>')
+ goto include_syntax;
+ memmove(buf, buf + 1, len - 2);
+ buf[len - 2] = '\0';
+ c = '>';
+ }
+ }
+
+ if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE)
+ tcc_error("#include recursion too deep");
+ /* store current file in stack, but increment stack later below */
+ *s1->include_stack_ptr = file;
+
+ n = s1->nb_include_paths + s1->nb_sysinclude_paths;
+ for (i = -2; i < n; ++i) {
+ char buf1[sizeof file->filename];
+ CachedInclude *e;
+ BufferedFile **f;
+ const char *path;
+
+ if (i == -2) {
+ /* check absolute include path */
+ if (!IS_ABSPATH(buf))
+ continue;
+ buf1[0] = 0;
+ i = n; /* force end loop */
+
+ } else if (i == -1) {
+ /* search in current dir if "header.h" */
+ if (c != '\"')
+ continue;
+ path = file->filename;
+ pstrncpy(buf1, path, tcc_basename(path) - path);
+
+ } else {
+ /* search in all the include paths */
+ if (i < s1->nb_include_paths)
+ path = s1->include_paths[i];
+ else
+ path = s1->sysinclude_paths[i - s1->nb_include_paths];
+ pstrcpy(buf1, sizeof(buf1), path);
+ pstrcat(buf1, sizeof(buf1), "/");
+ }
+
+ pstrcat(buf1, sizeof(buf1), buf);
+
+ if (tok == TOK_INCLUDE_NEXT)
+ for (f = s1->include_stack_ptr; f >= s1->include_stack; --f)
+ if (0 == PATHCMP((*f)->filename, buf1)) {
+#ifdef INC_DEBUG
+ printf("%s: #include_next skipping %s\n", file->filename, buf1);
+#endif
+ goto include_trynext;
+ }
+
+ e = search_cached_include(s1, buf1);
+ if (e && (define_find(e->ifndef_macro) || e->ifndef_macro == TOK_once)) {
+ /* no need to parse the include because the 'ifndef macro'
+ is defined */
+#ifdef INC_DEBUG
+ printf("%s: skipping cached %s\n", file->filename, buf1);
+#endif
+ goto include_done;
+ }
+
+ if (tcc_open(s1, buf1) < 0)
+include_trynext:
+ continue;
+
+#ifdef INC_DEBUG
+ printf("%s: including %s\n", file->prev->filename, file->filename);
+#endif
+ /* update target deps */
+ dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps,
+ tcc_strdup(buf1));
+ /* push current file in stack */
+ ++s1->include_stack_ptr;
+ /* add include file debug info */
+ if (s1->do_debug)
+ put_stabs(file->filename, N_BINCL, 0, 0, 0);
+ tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL;
+ ch = file->buf_ptr[0];
+ goto the_end;
+ }
+ tcc_error("include file '%s' not found", buf);
+include_done:
+ break;
+ case TOK_IFNDEF:
+ c = 1;
+ goto do_ifdef;
+ case TOK_IF:
+ c = expr_preprocess();
+ goto do_if;
+ case TOK_IFDEF:
+ c = 0;
+ do_ifdef:
+ next_nomacro();
+ if (tok < TOK_IDENT)
+ tcc_error("invalid argument for '#if%sdef'", c ? "n" : "");
+ if (is_bof) {
+ if (c) {
+#ifdef INC_DEBUG
+ printf("#ifndef %s\n", get_tok_str(tok, NULL));
+#endif
+ file->ifndef_macro = tok;
+ }
+ }
+ c = (define_find(tok) != 0) ^ c;
+ do_if:
+ if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE)
+ tcc_error("memory full (ifdef)");
+ *s1->ifdef_stack_ptr++ = c;
+ goto test_skip;
+ case TOK_ELSE:
+ if (s1->ifdef_stack_ptr == s1->ifdef_stack)
+ tcc_error("#else without matching #if");
+ if (s1->ifdef_stack_ptr[-1] & 2)
+ tcc_error("#else after #else");
+ c = (s1->ifdef_stack_ptr[-1] ^= 3);
+ goto test_else;
+ case TOK_ELIF:
+ if (s1->ifdef_stack_ptr == s1->ifdef_stack)
+ tcc_error("#elif without matching #if");
+ c = s1->ifdef_stack_ptr[-1];
+ if (c > 1)
+ tcc_error("#elif after #else");
+ /* last #if/#elif expression was true: we skip */
+ if (c == 1)
+ goto skip;
+ c = expr_preprocess();
+ s1->ifdef_stack_ptr[-1] = c;
+ test_else:
+ if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1)
+ file->ifndef_macro = 0;
+ test_skip:
+ if (!(c & 1)) {
+ skip:
+ preprocess_skip();
+ is_bof = 0;
+ goto redo;
+ }
+ break;
+ case TOK_ENDIF:
+ if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr)
+ tcc_error("#endif without matching #if");
+ s1->ifdef_stack_ptr--;
+ /* '#ifndef macro' was at the start of file. Now we check if
+ an '#endif' is exactly at the end of file */
+ if (file->ifndef_macro &&
+ s1->ifdef_stack_ptr == file->ifdef_stack_ptr) {
+ file->ifndef_macro_saved = file->ifndef_macro;
+ /* need to set to zero to avoid false matches if another
+ #ifndef at middle of file */
+ file->ifndef_macro = 0;
+ while (tok != TOK_LINEFEED)
+ next_nomacro();
+ tok_flags |= TOK_FLAG_ENDIF;
+ goto the_end;
+ }
+ break;
+ case TOK_PPNUM:
+ n = strtoul((char*)tokc.cstr->data, &q, 10);
+ goto _line_num;
+ case TOK_LINE:
+ next();
+ if (tok != TOK_CINT)
+_line_err:
+ tcc_error("wrong #line format");
+ n = tokc.i;
+_line_num:
+ next();
+ if (tok != TOK_LINEFEED) {
+ if (tok == TOK_STR)
+ pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.cstr->data);
+ else if (parse_flags & PARSE_FLAG_ASM_FILE)
+ break;
+ else
+ goto _line_err;
+ --n;
+ }
+ if (file->fd > 0)
+ total_lines += file->line_num - n;
+ file->line_num = n;
+ if (s1->do_debug)
+ put_stabs(file->filename, N_BINCL, 0, 0, 0);
+ break;
+ case TOK_ERROR:
+ case TOK_WARNING:
+ c = tok;
+ ch = file->buf_ptr[0];
+ skip_spaces();
+ q = buf;
+ while (ch != '\n' && ch != CH_EOF) {
+ if ((q - buf) < sizeof(buf) - 1)
+ *q++ = ch;
+ if (ch == '\\') {
+ if (handle_stray_noerror() == 0)
+ --q;
+ } else
+ inp();
+ }
+ *q = '\0';
+ if (c == TOK_ERROR)
+ tcc_error("#error %s", buf);
+ else
+ tcc_warning("#warning %s", buf);
+ break;
+ case TOK_PRAGMA:
+ pragma_parse(s1);
+ break;
+ case TOK_LINEFEED:
+ goto the_end;
+ default:
+ /* ignore gas line comment in an 'S' file. */
+ if (saved_parse_flags & PARSE_FLAG_ASM_FILE)
+ goto ignore;
+ if (tok == '!' && is_bof)
+ /* '!' is ignored at beginning to allow C scripts. */
+ goto ignore;
+ tcc_warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc));
+ ignore:
+ file->buf_ptr = parse_line_comment(file->buf_ptr);
+ goto the_end;
+ }
+ /* ignore other preprocess commands or #! for C scripts */
+ while (tok != TOK_LINEFEED)
+ next_nomacro();
+ the_end:
+ parse_flags = saved_parse_flags;
+}
+
+/* evaluate escape codes in a string. */
+static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long)
+{
+ int c, n;
+ const uint8_t *p;
+
+ p = buf;
+ for(;;) {
+ c = *p;
+ if (c == '\0')
+ break;
+ if (c == '\\') {
+ p++;
+ /* escape */
+ c = *p;
+ switch(c) {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ /* at most three octal digits */
+ n = c - '0';
+ p++;
+ c = *p;
+ if (isoct(c)) {
+ n = n * 8 + c - '0';
+ p++;
+ c = *p;
+ if (isoct(c)) {
+ n = n * 8 + c - '0';
+ p++;
+ }
+ }
+ c = n;
+ goto add_char_nonext;
+ case 'x':
+ case 'u':
+ case 'U':
+ p++;
+ n = 0;
+ for(;;) {
+ c = *p;
+ if (c >= 'a' && c <= 'f')
+ c = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F')
+ c = c - 'A' + 10;
+ else if (isnum(c))
+ c = c - '0';
+ else
+ break;
+ n = n * 16 + c;
+ p++;
+ }
+ c = n;
+ goto add_char_nonext;
+ 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 'e':
+ if (!gnu_ext)
+ goto invalid_escape;
+ c = 27;
+ break;
+ case '\'':
+ case '\"':
+ case '\\':
+ case '?':
+ break;
+ default:
+ invalid_escape:
+ if (c >= '!' && c <= '~')
+ tcc_warning("unknown escape sequence: \'\\%c\'", c);
+ else
+ tcc_warning("unknown escape sequence: \'\\x%x\'", c);
+ break;
+ }
+ }
+ p++;
+ add_char_nonext:
+ if (!is_long)
+ cstr_ccat(outstr, c);
+ else
+ cstr_wccat(outstr, c);
+ }
+ /* add a trailing '\0' */
+ if (!is_long)
+ cstr_ccat(outstr, '\0');
+ else
+ cstr_wccat(outstr, '\0');
+}
+
+void parse_string(const char *s, int len)
+{
+ uint8_t buf[1000], *p = buf;
+ int is_long, sep;
+
+ if ((is_long = *s == 'L'))
+ ++s, --len;
+ sep = *s++;
+ len -= 2;
+ if (len >= sizeof buf)
+ p = tcc_malloc(len + 1);
+ memcpy(p, s, len);
+ p[len] = 0;
+
+ cstr_reset(&tokcstr);
+ parse_escape_string(&tokcstr, p, is_long);
+ if (p != buf)
+ tcc_free(p);
+
+ if (sep == '\'') {
+ int char_size;
+ /* XXX: make it portable */
+ if (!is_long)
+ char_size = 1;
+ else
+ char_size = sizeof(nwchar_t);
+ if (tokcstr.size <= char_size)
+ tcc_error("empty character constant");
+ if (tokcstr.size > 2 * char_size)
+ tcc_warning("multi-character character constant");
+ if (!is_long) {
+ tokc.i = *(int8_t *)tokcstr.data;
+ tok = TOK_CCHAR;
+ } else {
+ tokc.i = *(nwchar_t *)tokcstr.data;
+ tok = TOK_LCHAR;
+ }
+ } else {
+ tokc.cstr = &tokcstr;
+ if (!is_long)
+ tok = TOK_STR;
+ else
+ tok = TOK_LSTR;
+ }
+}
+
+/* we use 64 bit numbers */
+#define BN_SIZE 2
+
+/* bn = (bn << shift) | or_val */
+static void bn_lshift(unsigned int *bn, int shift, int or_val)
+{
+ int i;
+ unsigned int v;
+ for(i=0;i<BN_SIZE;i++) {
+ v = bn[i];
+ bn[i] = (v << shift) | or_val;
+ or_val = v >> (32 - shift);
+ }
+}
+
+static void bn_zero(unsigned int *bn)
+{
+ int i;
+ for(i=0;i<BN_SIZE;i++) {
+ bn[i] = 0;
+ }
+}
+
+/* parse number in null terminated string 'p' and return it in the
+ current token */
+static void parse_number(const char *p)
+{
+ int b, t, shift, frac_bits, s, exp_val, ch;
+ char *q;
+ unsigned int bn[BN_SIZE];
+ double d;
+
+ /* number */
+ q = token_buf;
+ ch = *p++;
+ t = ch;
+ ch = *p++;
+ *q++ = t;
+ b = 10;
+ if (t == '.') {
+ goto float_frac_parse;
+ } else if (t == '0') {
+ if (ch == 'x' || ch == 'X') {
+ q--;
+ ch = *p++;
+ b = 16;
+ } else if (tcc_ext && (ch == 'b' || ch == 'B')) {
+ q--;
+ ch = *p++;
+ b = 2;
+ }
+ }
+ /* parse all digits. cannot check octal numbers at this stage
+ because of floating point constants */
+ while (1) {
+ if (ch >= 'a' && ch <= 'f')
+ t = ch - 'a' + 10;
+ else if (ch >= 'A' && ch <= 'F')
+ t = ch - 'A' + 10;
+ else if (isnum(ch))
+ t = ch - '0';
+ else
+ break;
+ if (t >= b)
+ break;
+ if (q >= token_buf + STRING_MAX_SIZE) {
+ num_too_long:
+ tcc_error("number too long");
+ }
+ *q++ = ch;
+ ch = *p++;
+ }
+ if (ch == '.' ||
+ ((ch == 'e' || ch == 'E') && b == 10) ||
+ ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) {
+ if (b != 10) {
+ /* NOTE: strtox should support that for hexa numbers, but
+ non ISOC99 libcs do not support it, so we prefer to do
+ it by hand */
+ /* hexadecimal or binary floats */
+ /* XXX: handle overflows */
+ *q = '\0';
+ if (b == 16)
+ shift = 4;
+ else
+ shift = 1;
+ bn_zero(bn);
+ q = token_buf;
+ while (1) {
+ t = *q++;
+ if (t == '\0') {
+ break;
+ } else if (t >= 'a') {
+ t = t - 'a' + 10;
+ } else if (t >= 'A') {
+ t = t - 'A' + 10;
+ } else {
+ t = t - '0';
+ }
+ bn_lshift(bn, shift, t);
+ }
+ frac_bits = 0;
+ if (ch == '.') {
+ ch = *p++;
+ while (1) {
+ t = ch;
+ if (t >= 'a' && t <= 'f') {
+ t = t - 'a' + 10;
+ } else if (t >= 'A' && t <= 'F') {
+ t = t - 'A' + 10;
+ } else if (t >= '0' && t <= '9') {
+ t = t - '0';
+ } else {
+ break;
+ }
+ if (t >= b)
+ tcc_error("invalid digit");
+ bn_lshift(bn, shift, t);
+ frac_bits += shift;
+ ch = *p++;
+ }
+ }
+ if (ch != 'p' && ch != 'P')
+ expect("exponent");
+ ch = *p++;
+ s = 1;
+ exp_val = 0;
+ if (ch == '+') {
+ ch = *p++;
+ } else if (ch == '-') {
+ s = -1;
+ ch = *p++;
+ }
+ if (ch < '0' || ch > '9')
+ expect("exponent digits");
+ while (ch >= '0' && ch <= '9') {
+ exp_val = exp_val * 10 + ch - '0';
+ ch = *p++;
+ }
+ exp_val = exp_val * s;
+
+ /* now we can generate the number */
+ /* XXX: should patch directly float number */
+ d = (double)bn[1] * 4294967296.0 + (double)bn[0];
+ d = ldexp(d, exp_val - frac_bits);
+ t = toup(ch);
+ if (t == 'F') {
+ ch = *p++;
+ tok = TOK_CFLOAT;
+ /* float : should handle overflow */
+ tokc.f = (float)d;
+ } else if (t == 'L') {
+ ch = *p++;
+#ifdef TCC_TARGET_PE
+ tok = TOK_CDOUBLE;
+ tokc.d = d;
+#else
+ tok = TOK_CLDOUBLE;
+ /* XXX: not large enough */
+ tokc.ld = (long double)d;
+#endif
+ } else {
+ tok = TOK_CDOUBLE;
+ tokc.d = d;
+ }
+ } else {
+ /* decimal floats */
+ if (ch == '.') {
+ if (q >= token_buf + STRING_MAX_SIZE)
+ goto num_too_long;
+ *q++ = ch;
+ ch = *p++;
+ float_frac_parse:
+ while (ch >= '0' && ch <= '9') {
+ if (q >= token_buf + STRING_MAX_SIZE)
+ goto num_too_long;
+ *q++ = ch;
+ ch = *p++;
+ }
+ }
+ if (ch == 'e' || ch == 'E') {
+ if (q >= token_buf + STRING_MAX_SIZE)
+ goto num_too_long;
+ *q++ = ch;
+ ch = *p++;
+ if (ch == '-' || ch == '+') {
+ if (q >= token_buf + STRING_MAX_SIZE)
+ goto num_too_long;
+ *q++ = ch;
+ ch = *p++;
+ }
+ if (ch < '0' || ch > '9')
+ expect("exponent digits");
+ while (ch >= '0' && ch <= '9') {
+ if (q >= token_buf + STRING_MAX_SIZE)
+ goto num_too_long;
+ *q++ = ch;
+ ch = *p++;
+ }
+ }
+ *q = '\0';
+ t = toup(ch);
+ errno = 0;
+ if (t == 'F') {
+ ch = *p++;
+ tok = TOK_CFLOAT;
+ tokc.f = strtof(token_buf, NULL);
+ } else if (t == 'L') {
+ ch = *p++;
+#ifdef TCC_TARGET_PE
+ tok = TOK_CDOUBLE;
+ tokc.d = strtod(token_buf, NULL);
+#else
+ tok = TOK_CLDOUBLE;
+ tokc.ld = strtold(token_buf, NULL);
+#endif
+ } else {
+ tok = TOK_CDOUBLE;
+ tokc.d = strtod(token_buf, NULL);
+ }
+ }
+ } else {
+ unsigned long long n, n1;
+ int lcount, ucount, must_64bit;
+ const char *p1;
+
+ /* integer number */
+ *q = '\0';
+ q = token_buf;
+ if (b == 10 && *q == '0') {
+ b = 8;
+ q++;
+ }
+ n = 0;
+ while(1) {
+ t = *q++;
+ /* no need for checks except for base 10 / 8 errors */
+ if (t == '\0')
+ break;
+ else if (t >= 'a')
+ t = t - 'a' + 10;
+ else if (t >= 'A')
+ t = t - 'A' + 10;
+ else
+ t = t - '0';
+ if (t >= b)
+ tcc_error("invalid digit");
+ n1 = n;
+ n = n * b + t;
+ /* detect overflow */
+ /* XXX: this test is not reliable */
+ if (n < n1)
+ tcc_error("integer constant overflow");
+ }
+
+ /* Determine the characteristics (unsigned and/or 64bit) the type of
+ the constant must have according to the constant suffix(es) */
+ lcount = ucount = must_64bit = 0;
+ p1 = p;
+ for(;;) {
+ t = toup(ch);
+ if (t == 'L') {
+ if (lcount >= 2)
+ tcc_error("three 'l's in integer constant");
+ if (lcount && *(p - 1) != ch)
+ tcc_error("incorrect integer suffix: %s", p1);
+ lcount++;
+#if !defined TCC_TARGET_X86_64 || defined TCC_TARGET_PE
+ if (lcount == 2)
+#endif
+ must_64bit = 1;
+ ch = *p++;
+ } else if (t == 'U') {
+ if (ucount >= 1)
+ tcc_error("two 'u's in integer constant");
+ ucount++;
+ ch = *p++;
+ } else {
+ break;
+ }
+ }
+
+ /* Whether 64 bits are needed to hold the constant's value */
+ if (n & 0xffffffff00000000LL || must_64bit) {
+ tok = TOK_CLLONG;
+ n1 = n >> 32;
+ } else {
+ tok = TOK_CINT;
+ n1 = n;
+ }
+
+ /* Whether type must be unsigned to hold the constant's value */
+ if (ucount || ((n1 >> 31) && (b != 10))) {
+ if (tok == TOK_CLLONG)
+ tok = TOK_CULLONG;
+ else
+ tok = TOK_CUINT;
+ /* If decimal and no unsigned suffix, bump to 64 bits or throw error */
+ } else if (n1 >> 31) {
+ if (tok == TOK_CINT)
+ tok = TOK_CLLONG;
+ else
+ tcc_error("integer constant overflow");
+ }
+
+ if (tok == TOK_CINT || tok == TOK_CUINT)
+ tokc.ui = n;
+ else
+ tokc.ull = n;
+ }
+ if (ch)
+ tcc_error("invalid number\n");
+}
+
+
+#define PARSE2(c1, tok1, c2, tok2) \
+ case c1: \
+ PEEKC(c, p); \
+ if (c == c2) { \
+ p++; \
+ tok = tok2; \
+ } else { \
+ tok = tok1; \
+ } \
+ break;
+
+/* return next token without macro substitution */
+static inline void next_nomacro1(void)
+{
+ int t, c, is_long;
+ TokenSym *ts;
+ uint8_t *p, *p1;
+ unsigned int h;
+
+ p = file->buf_ptr;
+ redo_no_start:
+ c = *p;
+ switch(c) {
+ case ' ':
+ case '\t':
+ tok = c;
+ p++;
+ if (parse_flags & PARSE_FLAG_SPACES)
+ goto keep_tok_flags;
+ while (isidnum_table[*p - CH_EOF] & IS_SPC)
+ ++p;
+ goto redo_no_start;
+ case '\f':
+ case '\v':
+ case '\r':
+ p++;
+ goto redo_no_start;
+ case '\\':
+ /* first look if it is in fact an end of buffer */
+ c = handle_stray1(p);
+ p = file->buf_ptr;
+ if (c == '\\')
+ goto parse_simple;
+ if (c != CH_EOF)
+ goto redo_no_start;
+ {
+ TCCState *s1 = tcc_state;
+ if ((parse_flags & PARSE_FLAG_LINEFEED)
+ && !(tok_flags & TOK_FLAG_EOF)) {
+ tok_flags |= TOK_FLAG_EOF;
+ tok = TOK_LINEFEED;
+ goto keep_tok_flags;
+ } else if (!(parse_flags & PARSE_FLAG_PREPROCESS)) {
+ tok = TOK_EOF;
+ } else if (s1->ifdef_stack_ptr != file->ifdef_stack_ptr) {
+ tcc_error("missing #endif");
+ } else if (s1->include_stack_ptr == s1->include_stack) {
+ /* no include left : end of file. */
+ tok = TOK_EOF;
+ } else {
+ tok_flags &= ~TOK_FLAG_EOF;
+ /* pop include file */
+
+ /* test if previous '#endif' was after a #ifdef at
+ start of file */
+ if (tok_flags & TOK_FLAG_ENDIF) {
+#ifdef INC_DEBUG
+ printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL));
+#endif
+ add_cached_include(s1, file->filename, file->ifndef_macro_saved);
+ tok_flags &= ~TOK_FLAG_ENDIF;
+ }
+
+ /* add end of include file debug info */
+ if (tcc_state->do_debug) {
+ put_stabd(N_EINCL, 0, 0);
+ }
+ /* pop include stack */
+ tcc_close();
+ s1->include_stack_ptr--;
+ p = file->buf_ptr;
+ goto redo_no_start;
+ }
+ }
+ break;
+
+ case '\n':
+ file->line_num++;
+ tok_flags |= TOK_FLAG_BOL;
+ p++;
+maybe_newline:
+ if (0 == (parse_flags & PARSE_FLAG_LINEFEED))
+ goto redo_no_start;
+ tok = TOK_LINEFEED;
+ goto keep_tok_flags;
+
+ case '#':
+ /* XXX: simplify */
+ PEEKC(c, p);
+ if ((tok_flags & TOK_FLAG_BOL) &&
+ (parse_flags & PARSE_FLAG_PREPROCESS)) {
+ file->buf_ptr = p;
+ preprocess(tok_flags & TOK_FLAG_BOF);
+ p = file->buf_ptr;
+ goto maybe_newline;
+ } else {
+ if (c == '#') {
+ p++;
+ tok = TOK_TWOSHARPS;
+ } else {
+ if (parse_flags & PARSE_FLAG_ASM_FILE) {
+ p = parse_line_comment(p - 1);
+ goto redo_no_start;
+ } else {
+ tok = '#';
+ }
+ }
+ }
+ break;
+
+ /* dollar is allowed to start identifiers when not parsing asm */
+ case '$':
+ if (!(isidnum_table[c - CH_EOF] & IS_ID)
+ || (parse_flags & PARSE_FLAG_ASM_FILE))
+ goto parse_simple;
+
+ case 'a': case 'b': case 'c': case 'd':
+ case 'e': case 'f': case 'g': case 'h':
+ case 'i': case 'j': case 'k': case 'l':
+ case 'm': case 'n': case 'o': case 'p':
+ case 'q': case 'r': case 's': case 't':
+ case 'u': case 'v': case 'w': case 'x':
+ case 'y': case 'z':
+ case 'A': case 'B': case 'C': case 'D':
+ case 'E': case 'F': case 'G': case 'H':
+ case 'I': case 'J': case 'K':
+ case 'M': case 'N': case 'O': case 'P':
+ case 'Q': case 'R': case 'S': case 'T':
+ case 'U': case 'V': case 'W': case 'X':
+ case 'Y': case 'Z':
+ case '_':
+ parse_ident_fast:
+ p1 = p;
+ h = TOK_HASH_INIT;
+ h = TOK_HASH_FUNC(h, c);
+ while (c = *++p, isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
+ h = TOK_HASH_FUNC(h, c);
+ if (c != '\\') {
+ TokenSym **pts;
+ int len;
+
+ /* fast case : no stray found, so we have the full token
+ and we have already hashed it */
+ len = p - p1;
+ h &= (TOK_HASH_SIZE - 1);
+ pts = &hash_ident[h];
+ for(;;) {
+ ts = *pts;
+ if (!ts)
+ break;
+ if (ts->len == len && !memcmp(ts->str, p1, len))
+ goto token_found;
+ pts = &(ts->hash_next);
+ }
+ ts = tok_alloc_new(pts, (char *) p1, len);
+ token_found: ;
+ } else {
+ /* slower case */
+ cstr_reset(&tokcstr);
+
+ while (p1 < p) {
+ cstr_ccat(&tokcstr, *p1);
+ p1++;
+ }
+ p--;
+ PEEKC(c, p);
+ parse_ident_slow:
+ while (isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM)) {
+ cstr_ccat(&tokcstr, c);
+ PEEKC(c, p);
+ }
+ ts = tok_alloc(tokcstr.data, tokcstr.size);
+ }
+ tok = ts->tok;
+ break;
+ case 'L':
+ t = p[1];
+ if (t != '\\' && t != '\'' && t != '\"') {
+ /* fast case */
+ goto parse_ident_fast;
+ } else {
+ PEEKC(c, p);
+ if (c == '\'' || c == '\"') {
+ is_long = 1;
+ goto str_const;
+ } else {
+ cstr_reset(&tokcstr);
+ cstr_ccat(&tokcstr, 'L');
+ goto parse_ident_slow;
+ }
+ }
+ break;
+
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9':
+ cstr_reset(&tokcstr);
+ /* after the first digit, accept digits, alpha, '.' or sign if
+ prefixed by 'eEpP' */
+ parse_num:
+ for(;;) {
+ t = c;
+ cstr_ccat(&tokcstr, c);
+ PEEKC(c, p);
+ if (!((isidnum_table[c - CH_EOF] & (IS_ID|IS_NUM))
+ || c == '.'
+ || ((c == '+' || c == '-')
+ && (t == 'e' || t == 'E' || t == 'p' || t == 'P')
+ )))
+ break;
+ }
+ /* We add a trailing '\0' to ease parsing */
+ cstr_ccat(&tokcstr, '\0');
+ tokc.cstr = &tokcstr;
+ tok = TOK_PPNUM;
+ break;
+
+ case '.':
+ /* special dot handling because it can also start a number */
+ PEEKC(c, p);
+ if (isnum(c)) {
+ cstr_reset(&tokcstr);
+ cstr_ccat(&tokcstr, '.');
+ goto parse_num;
+ } else if (c == '.') {
+ PEEKC(c, p);
+ if (c != '.')
+ expect("'.'");
+ PEEKC(c, p);
+ tok = TOK_DOTS;
+ } else {
+ tok = '.';
+ }
+ break;
+ case '\'':
+ case '\"':
+ is_long = 0;
+ str_const:
+ cstr_reset(&tokcstr);
+ if (is_long)
+ cstr_ccat(&tokcstr, 'L');
+ cstr_ccat(&tokcstr, c);
+ p = parse_pp_string(p, c, &tokcstr);
+ cstr_ccat(&tokcstr, c);
+ cstr_ccat(&tokcstr, '\0');
+ tokc.cstr = &tokcstr;
+ tok = TOK_PPSTR;
+ break;
+
+ case '<':
+ PEEKC(c, p);
+ if (c == '=') {
+ p++;
+ tok = TOK_LE;
+ } else if (c == '<') {
+ PEEKC(c, p);
+ if (c == '=') {
+ p++;
+ tok = TOK_A_SHL;
+ } else {
+ tok = TOK_SHL;
+ }
+ } else {
+ tok = TOK_LT;
+ }
+ break;
+ case '>':
+ PEEKC(c, p);
+ if (c == '=') {
+ p++;
+ tok = TOK_GE;
+ } else if (c == '>') {
+ PEEKC(c, p);
+ if (c == '=') {
+ p++;
+ tok = TOK_A_SAR;
+ } else {
+ tok = TOK_SAR;
+ }
+ } else {
+ tok = TOK_GT;
+ }
+ break;
+
+ case '&':
+ PEEKC(c, p);
+ if (c == '&') {
+ p++;
+ tok = TOK_LAND;
+ } else if (c == '=') {
+ p++;
+ tok = TOK_A_AND;
+ } else {
+ tok = '&';
+ }
+ break;
+
+ case '|':
+ PEEKC(c, p);
+ if (c == '|') {
+ p++;
+ tok = TOK_LOR;
+ } else if (c == '=') {
+ p++;
+ tok = TOK_A_OR;
+ } else {
+ tok = '|';
+ }
+ break;
+
+ case '+':
+ PEEKC(c, p);
+ if (c == '+') {
+ p++;
+ tok = TOK_INC;
+ } else if (c == '=') {
+ p++;
+ tok = TOK_A_ADD;
+ } else {
+ tok = '+';
+ }
+ break;
+
+ case '-':
+ PEEKC(c, p);
+ if (c == '-') {
+ p++;
+ tok = TOK_DEC;
+ } else if (c == '=') {
+ p++;
+ tok = TOK_A_SUB;
+ } else if (c == '>') {
+ p++;
+ tok = TOK_ARROW;
+ } else {
+ tok = '-';
+ }
+ break;
+
+ PARSE2('!', '!', '=', TOK_NE)
+ PARSE2('=', '=', '=', TOK_EQ)
+ PARSE2('*', '*', '=', TOK_A_MUL)
+ PARSE2('%', '%', '=', TOK_A_MOD)
+ PARSE2('^', '^', '=', TOK_A_XOR)
+
+ /* comments or operator */
+ case '/':
+ PEEKC(c, p);
+ if (c == '*') {
+ p = parse_comment(p);
+ /* comments replaced by a blank */
+ tok = ' ';
+ goto keep_tok_flags;
+ } else if (c == '/') {
+ p = parse_line_comment(p);
+ tok = ' ';
+ goto keep_tok_flags;
+ } else if (c == '=') {
+ p++;
+ tok = TOK_A_DIV;
+ } else {
+ tok = '/';
+ }
+ break;
+
+ /* simple tokens */
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ case ',':
+ case ';':
+ case ':':
+ case '?':
+ case '~':
+ case '@': /* only used in assembler */
+ parse_simple:
+ tok = c;
+ p++;
+ break;
+ default:
+ tcc_error("unrecognized character \\x%02x", c);
+ break;
+ }
+ tok_flags = 0;
+keep_tok_flags:
+ file->buf_ptr = p;
+#if defined(PARSE_DEBUG)
+ printf("token = %s\n", get_tok_str(tok, &tokc));
+#endif
+}
+
+/* return next token without macro substitution. Can read input from
+ macro_ptr buffer */
+static void next_nomacro_spc(void)
+{
+ if (macro_ptr) {
+ redo:
+ tok = *macro_ptr;
+ if (tok) {
+ TOK_GET(&tok, &macro_ptr, &tokc);
+ if (tok == TOK_LINENUM) {
+ file->line_num = tokc.i;
+ goto redo;
+ }
+ }
+ } else {
+ next_nomacro1();
+ }
+}
+
+ST_FUNC void next_nomacro(void)
+{
+ do {
+ next_nomacro_spc();
+ } while (tok < 256 && (isidnum_table[tok - CH_EOF] & IS_SPC));
+}
+
+
+static void macro_subst(
+ TokenString *tok_str,
+ Sym **nested_list,
+ const int *macro_str,
+ int can_read_stream
+ );
+
+/* substitute arguments in replacement lists in macro_str by the values in
+ args (field d) and return allocated string */
+static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args)
+{
+ int t, t0, t1, spc;
+ const int *st;
+ Sym *s;
+ CValue cval;
+ TokenString str;
+ CString cstr;
+
+ tok_str_new(&str);
+ t0 = t1 = 0;
+ while(1) {
+ TOK_GET(&t, &macro_str, &cval);
+ if (!t)
+ break;
+ if (t == '#') {
+ /* stringize */
+ TOK_GET(&t, &macro_str, &cval);
+ if (!t)
+ goto bad_stringy;
+ s = sym_find2(args, t);
+ if (s) {
+ cstr_new(&cstr);
+ cstr_ccat(&cstr, '\"');
+ st = s->d;
+ spc = 0;
+ while (*st) {
+ TOK_GET(&t, &st, &cval);
+ if (t != TOK_PLCHLDR
+ && t != TOK_NOSUBST
+ && 0 == check_space(t, &spc)) {
+ const char *s = get_tok_str(t, &cval);
+ while (*s) {
+ if (t == TOK_PPSTR && *s != '\'')
+ add_char(&cstr, *s);
+ else
+ cstr_ccat(&cstr, *s);
+ ++s;
+ }
+ }
+ }
+ cstr.size -= spc;
+ cstr_ccat(&cstr, '\"');
+ cstr_ccat(&cstr, '\0');
+#ifdef PP_DEBUG
+ printf("\nstringize: <%s>\n", (char *)cstr.data);
+#endif
+ /* add string */
+ cval.cstr = &cstr;
+ tok_str_add2(&str, TOK_PPSTR, &cval);
+ cstr_free(cval.cstr);
+ } else {
+ bad_stringy:
+ expect("macro parameter after '#'");
+ }
+ } else if (t >= TOK_IDENT) {
+ s = sym_find2(args, t);
+ if (s) {
+ int l0 = str.len;
+ st = s->d;
+ /* if '##' is present before or after, no arg substitution */
+ if (*macro_str == TOK_TWOSHARPS || t1 == TOK_TWOSHARPS) {
+ /* special case for var arg macros : ## eats the ','
+ if empty VA_ARGS variable. */
+ if (t1 == TOK_TWOSHARPS && t0 == ',' && gnu_ext && s->type.t) {
+ if (*st == 0) {
+ /* suppress ',' '##' */
+ str.len -= 2;
+ } else {
+ /* suppress '##' and add variable */
+ str.len--;
+ goto add_var;
+ }
+ } else {
+ for(;;) {
+ int t1;
+ TOK_GET(&t1, &st, &cval);
+ if (!t1)
+ break;
+ tok_str_add2(&str, t1, &cval);
+ }
+ }
+
+ } else {
+ add_var:
+ /* NOTE: the stream cannot be read when macro
+ substituing an argument */
+ macro_subst(&str, nested_list, st, 0);
+ }
+ if (str.len == l0) /* exanded to empty string */
+ tok_str_add(&str, TOK_PLCHLDR);
+ } else {
+ tok_str_add(&str, t);
+ }
+ } else {
+ tok_str_add2(&str, t, &cval);
+ }
+ t0 = t1, t1 = t;
+ }
+ tok_str_add(&str, 0);
+ return str.str;
+}
+
+static char const ab_month_name[12][4] =
+{
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* peek or read [ws_str == NULL] next token from function macro call,
+ walking up macro levels up to the file if necessary */
+static int next_argstream(Sym **nested_list, int can_read_stream, TokenString *ws_str)
+{
+ int t;
+ const int *p;
+ Sym *sa;
+
+ for (;;) {
+ if (macro_ptr) {
+ p = macro_ptr, t = *p;
+ if (ws_str) {
+ while (is_space(t) || TOK_LINEFEED == t)
+ tok_str_add(ws_str, t), t = *++p;
+ }
+ if (t == 0 && can_read_stream) {
+ end_macro();
+ /* also, end of scope for nested defined symbol */
+ sa = *nested_list;
+ while (sa && sa->v == -1)
+ sa = sa->prev;
+ if (sa)
+ sa->v = -1;
+ continue;
+ }
+ } else {
+ ch = handle_eob();
+ if (ws_str) {
+ while (is_space(ch) || ch == '\n' || ch == '/') {
+ if (ch == '/') {
+ int c;
+ uint8_t *p = file->buf_ptr;
+ PEEKC(c, p);
+ if (c == '*') {
+ p = parse_comment(p);
+ file->buf_ptr = p - 1;
+ } else if (c == '/') {
+ p = parse_line_comment(p);
+ file->buf_ptr = p - 1;
+ } else
+ break;
+ ch = ' ';
+ }
+ tok_str_add(ws_str, ch);
+ cinp();
+ }
+ }
+ t = ch;
+ }
+
+ if (ws_str)
+ return t;
+ next_nomacro_spc();
+ return tok;
+ }
+}
+
+/* do macro substitution of current token with macro 's' and add
+ result to (tok_str,tok_len). 'nested_list' is the list of all
+ macros we got inside to avoid recursing. Return non zero if no
+ substitution needs to be done */
+static int macro_subst_tok(
+ TokenString *tok_str,
+ Sym **nested_list,
+ Sym *s,
+ int can_read_stream)
+{
+ Sym *args, *sa, *sa1;
+ int parlevel, *mstr, t, t1, spc;
+ TokenString str;
+ char *cstrval;
+ CValue cval;
+ CString cstr;
+ char buf[32];
+
+ /* if symbol is a macro, prepare substitution */
+ /* special macros */
+ if (tok == TOK___LINE__) {
+ snprintf(buf, sizeof(buf), "%d", file->line_num);
+ cstrval = buf;
+ t1 = TOK_PPNUM;
+ goto add_cstr1;
+ } else if (tok == TOK___FILE__) {
+ cstrval = file->filename;
+ goto add_cstr;
+ } else if (tok == TOK___DATE__ || tok == TOK___TIME__) {
+ time_t ti;
+ struct tm *tm;
+
+ time(&ti);
+ tm = localtime(&ti);
+ if (tok == TOK___DATE__) {
+ snprintf(buf, sizeof(buf), "%s %2d %d",
+ ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900);
+ } else {
+ snprintf(buf, sizeof(buf), "%02d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ }
+ cstrval = buf;
+ add_cstr:
+ t1 = TOK_STR;
+ add_cstr1:
+ cstr_new(&cstr);
+ cstr_cat(&cstr, cstrval);
+ cstr_ccat(&cstr, '\0');
+ cval.cstr = &cstr;
+ tok_str_add2(tok_str, t1, &cval);
+ cstr_free(&cstr);
+ } else {
+ int saved_parse_flags = parse_flags;
+
+ mstr = s->d;
+ if (s->type.t == MACRO_FUNC) {
+ /* whitespace between macro name and argument list */
+ TokenString ws_str;
+ tok_str_new(&ws_str);
+
+ spc = 0;
+ parse_flags |= PARSE_FLAG_SPACES | PARSE_FLAG_LINEFEED
+ | PARSE_FLAG_ACCEPT_STRAYS;
+
+ /* get next token from argument stream */
+ t = next_argstream(nested_list, can_read_stream, &ws_str);
+ if (t != '(') {
+ /* not a macro substitution after all, restore the
+ * macro token plus all whitespace we've read.
+ * whitespace is intentionally not merged to preserve
+ * newlines. */
+ parse_flags = saved_parse_flags;
+ tok_str_add(tok_str, tok);
+ if (parse_flags & PARSE_FLAG_SPACES) {
+ int i;
+ for (i = 0; i < ws_str.len; i++)
+ tok_str_add(tok_str, ws_str.str[i]);
+ }
+ tok_str_free(ws_str.str);
+ return 0;
+ } else {
+ tok_str_free(ws_str.str);
+ }
+ next_nomacro(); /* eat '(' */
+
+ /* argument macro */
+ args = NULL;
+ sa = s->next;
+ /* NOTE: empty args are allowed, except if no args */
+ for(;;) {
+ do {
+ next_argstream(nested_list, can_read_stream, NULL);
+ } while (is_space(tok) || TOK_LINEFEED == tok);
+ empty_arg:
+ /* handle '()' case */
+ if (!args && !sa && tok == ')')
+ break;
+ if (!sa)
+ tcc_error("macro '%s' used with too many args",
+ get_tok_str(s->v, 0));
+ tok_str_new(&str);
+ parlevel = spc = 0;
+ /* NOTE: non zero sa->t indicates VA_ARGS */
+ while ((parlevel > 0 ||
+ (tok != ')' &&
+ (tok != ',' || sa->type.t)))) {
+ if (tok == TOK_EOF || tok == 0)
+ break;
+ if (tok == '(')
+ parlevel++;
+ else if (tok == ')')
+ parlevel--;
+ if (tok == TOK_LINEFEED)
+ tok = ' ';
+ if (!check_space(tok, &spc))
+ tok_str_add2(&str, tok, &tokc);
+ next_argstream(nested_list, can_read_stream, NULL);
+ }
+ if (parlevel)
+ expect(")");
+ str.len -= spc;
+ tok_str_add(&str, 0);
+ sa1 = sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, 0);
+ sa1->d = str.str;
+ sa = sa->next;
+ if (tok == ')') {
+ /* special case for gcc var args: add an empty
+ var arg argument if it is omitted */
+ if (sa && sa->type.t && gnu_ext)
+ goto empty_arg;
+ break;
+ }
+ if (tok != ',')
+ expect(",");
+ }
+ if (sa) {
+ tcc_error("macro '%s' used with too few args",
+ get_tok_str(s->v, 0));
+ }
+
+ parse_flags = saved_parse_flags;
+
+ /* now subst each arg */
+ mstr = macro_arg_subst(nested_list, mstr, args);
+ /* free memory */
+ sa = args;
+ while (sa) {
+ sa1 = sa->prev;
+ tok_str_free(sa->d);
+ sym_free(sa);
+ sa = sa1;
+ }
+ }
+
+ sym_push2(nested_list, s->v, 0, 0);
+ parse_flags = saved_parse_flags;
+ macro_subst(tok_str, nested_list, mstr, can_read_stream);
+
+ /* pop nested defined symbol */
+ sa1 = *nested_list;
+ *nested_list = sa1->prev;
+ sym_free(sa1);
+ if (mstr != s->d)
+ tok_str_free(mstr);
+ }
+ return 0;
+}
+
+int paste_tokens(int t1, CValue *v1, int t2, CValue *v2)
+{
+ CString cstr;
+ int n;
+
+ cstr_new(&cstr);
+ if (t1 != TOK_PLCHLDR)
+ cstr_cat(&cstr, get_tok_str(t1, v1));
+ n = cstr.size;
+ if (t2 != TOK_PLCHLDR)
+ cstr_cat(&cstr, get_tok_str(t2, v2));
+ cstr_ccat(&cstr, '\0');
+
+ tcc_open_bf(tcc_state, ":paste:", cstr.size);
+ memcpy(file->buffer, cstr.data, cstr.size);
+ for (;;) {
+ next_nomacro1();
+ if (0 == *file->buf_ptr)
+ break;
+ if (is_space(tok))
+ continue;
+ tcc_warning("pasting <%.*s> and <%s> does not give a valid preprocessing token",
+ n, cstr.data, (char*)cstr.data + n);
+ break;
+ }
+ tcc_close();
+
+ //printf("paste <%s>\n", (char*)cstr.data);
+ cstr_free(&cstr);
+ return 0;
+}
+
+/* handle the '##' operator. Return NULL if no '##' seen. Otherwise
+ return the resulting string (which must be freed). */
+static inline int *macro_twosharps(const int *ptr0)
+{
+ int t;
+ CValue cval;
+ TokenString macro_str1;
+ int start_of_nosubsts = -1;
+ const int *ptr;
+
+ /* we search the first '##' */
+ for (ptr = ptr0;;) {
+ TOK_GET(&t, &ptr, &cval);
+ if (t == TOK_TWOSHARPS)
+ break;
+ if (t == 0)
+ return NULL;
+ }
+
+ tok_str_new(&macro_str1);
+
+ //tok_print(" $$$", ptr0);
+ for (ptr = ptr0;;) {
+ TOK_GET(&t, &ptr, &cval);
+ if (t == 0)
+ break;
+ if (t == TOK_TWOSHARPS)
+ continue;
+ while (*ptr == TOK_TWOSHARPS) {
+ int t1; CValue cv1;
+ /* given 'a##b', remove nosubsts preceding 'a' */
+ if (start_of_nosubsts >= 0)
+ macro_str1.len = start_of_nosubsts;
+ /* given 'a##b', remove nosubsts preceding 'b' */
+ while ((t1 = *++ptr) == TOK_NOSUBST)
+ ;
+ if (t1 && t1 != TOK_TWOSHARPS
+ && t1 != ':') /* 'a##:' don't build a new token */
+ {
+ TOK_GET(&t1, &ptr, &cv1);
+ if (t != TOK_PLCHLDR || t1 != TOK_PLCHLDR) {
+ paste_tokens(t, &cval, t1, &cv1);
+ t = tok, cval = tokc;
+ }
+ }
+ }
+ if (t == TOK_NOSUBST) {
+ if (start_of_nosubsts < 0)
+ start_of_nosubsts = macro_str1.len;
+ } else {
+ start_of_nosubsts = -1;
+ }
+ tok_str_add2(&macro_str1, t, &cval);
+ }
+ tok_str_add(&macro_str1, 0);
+ //tok_print(" ###", macro_str1.str);
+ return macro_str1.str;
+}
+
+/* do macro substitution of macro_str and add result to
+ (tok_str,tok_len). 'nested_list' is the list of all macros we got
+ inside to avoid recursing. */
+static void macro_subst(
+ TokenString *tok_str,
+ Sym **nested_list,
+ const int *macro_str,
+ int can_read_stream
+ )
+{
+ Sym *s;
+ const int *ptr;
+ int t, spc, nosubst;
+ CValue cval;
+ int *macro_str1 = NULL;
+
+ /* first scan for '##' operator handling */
+ ptr = macro_str;
+ spc = nosubst = 0;
+
+ /* first scan for '##' operator handling */
+ if (can_read_stream) {
+ macro_str1 = macro_twosharps(ptr);
+ if (macro_str1)
+ ptr = macro_str1;
+ }
+
+ while (1) {
+ TOK_GET(&t, &ptr, &cval);
+ if (t == 0)
+ break;
+
+ if (t >= TOK_IDENT && 0 == nosubst) {
+ s = define_find(t);
+ if (s == NULL)
+ goto no_subst;
+
+ /* if nested substitution, do nothing */
+ if (sym_find2(*nested_list, t)) {
+ /* and mark it as TOK_NOSUBST, so it doesn't get subst'd again */
+ tok_str_add2(tok_str, TOK_NOSUBST, NULL);
+ goto no_subst;
+ }
+
+ {
+ TokenString str;
+ str.str = (int*)ptr;
+ begin_macro(&str, 2);
+
+ tok = t;
+ macro_subst_tok(tok_str, nested_list, s, can_read_stream);
+
+ if (str.alloc == 3) {
+ /* already finished by reading function macro arguments */
+ break;
+ }
+
+ ptr = macro_ptr;
+ end_macro ();
+ }
+
+ spc = tok_str->len && is_space(tok_str->str[tok_str->len-1]);
+
+ } else {
+
+ if (t == '\\' && !(parse_flags & PARSE_FLAG_ACCEPT_STRAYS))
+ tcc_error("stray '\\' in program");
+
+no_subst:
+ if (!check_space(t, &spc))
+ tok_str_add2(tok_str, t, &cval);
+ nosubst = 0;
+ if (t == TOK_NOSUBST)
+ nosubst = 1;
+ }
+ }
+ if (macro_str1)
+ tok_str_free(macro_str1);
+
+}
+
+/* return next token with macro substitution */
+ST_FUNC void next(void)
+{
+ redo:
+ if (parse_flags & PARSE_FLAG_SPACES)
+ next_nomacro_spc();
+ else
+ next_nomacro();
+
+ if (macro_ptr) {
+ if (tok == TOK_NOSUBST || tok == TOK_PLCHLDR) {
+ /* discard preprocessor markers */
+ goto redo;
+ } else if (tok == 0) {
+ /* end of macro or unget token string */
+ end_macro();
+ goto redo;
+ }
+ } else if (tok >= TOK_IDENT && (parse_flags & PARSE_FLAG_PREPROCESS)) {
+ Sym *s;
+ /* if reading from file, try to substitute macros */
+ s = define_find(tok);
+ if (s) {
+ static TokenString str; /* using static string for speed */
+ Sym *nested_list = NULL;
+ tok_str_new(&str);
+ nested_list = NULL;
+ macro_subst_tok(&str, &nested_list, s, 1);
+ tok_str_add(&str, 0);
+ begin_macro(&str, 0);
+ goto redo;
+ }
+ }
+ /* convert preprocessor tokens into C tokens */
+ if (tok == TOK_PPNUM) {
+ if (parse_flags & PARSE_FLAG_TOK_NUM)
+ parse_number((char *)tokc.cstr->data);
+ } else if (tok == TOK_PPSTR) {
+ if (parse_flags & PARSE_FLAG_TOK_STR)
+ parse_string((char *)tokc.cstr->data, tokc.cstr->size - 1);
+ }
+}
+
+/* push back current token and set current token to 'last_tok'. Only
+ identifier case handled for labels. */
+ST_INLN void unget_tok(int last_tok)
+{
+ TokenString *str = tcc_malloc(sizeof *str);
+ tok_str_new(str);
+ tok_str_add2(str, tok, &tokc);
+ tok_str_add(str, 0);
+ begin_macro(str, 1);
+ tok = last_tok;
+}
+
+/* better than nothing, but needs extension to handle '-E' option
+ correctly too */
+ST_FUNC void preprocess_init(TCCState *s1)
+{
+ s1->include_stack_ptr = s1->include_stack;
+ /* XXX: move that before to avoid having to initialize
+ file->ifdef_stack_ptr ? */
+ s1->ifdef_stack_ptr = s1->ifdef_stack;
+ file->ifdef_stack_ptr = s1->ifdef_stack_ptr;
+
+ pvtop = vtop = vstack - 1;
+ s1->pack_stack[0] = 0;
+ s1->pack_stack_ptr = s1->pack_stack;
+
+ isidnum_table['$' - CH_EOF] =
+ tcc_state->dollars_in_identifiers ? IS_ID : 0;
+}
+
+ST_FUNC void preprocess_new(void)
+{
+ int i, c;
+ const char *p, *r;
+
+ /* init isid table */
+ for(i = CH_EOF; i<256; i++)
+ isidnum_table[i - CH_EOF]
+ = is_space(i) ? IS_SPC
+ : isid(i) ? IS_ID
+ : isnum(i) ? IS_NUM
+ : 0;
+
+ memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *));
+
+ tok_ident = TOK_IDENT;
+ p = tcc_keywords;
+ while (*p) {
+ r = p;
+ for(;;) {
+ c = *r++;
+ if (c == '\0')
+ break;
+ }
+ tok_alloc(p, r - p - 1);
+ p = r;
+ }
+}
+
+ST_FUNC void preprocess_delete(void)
+{
+ int i, n;
+
+ /* free -D and compiler defines */
+ free_defines(NULL);
+
+ /* cleanup from error/setjmp */
+ while (macro_stack)
+ end_macro();
+ macro_ptr = NULL;
+
+ /* free tokens */
+ n = tok_ident - TOK_IDENT;
+ for(i = 0; i < n; i++)
+ tcc_free(table_ident[i]);
+ tcc_free(table_ident);
+ table_ident = NULL;
+}
+
+/* Preprocess the current file */
+ST_FUNC int tcc_preprocess(TCCState *s1)
+{
+ BufferedFile **iptr;
+ int token_seen, spcs, level;
+
+ preprocess_init(s1);
+ ch = file->buf_ptr[0];
+ tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
+ parse_flags = PARSE_FLAG_PREPROCESS
+ | (parse_flags & PARSE_FLAG_ASM_FILE)
+ | PARSE_FLAG_LINEFEED
+ | PARSE_FLAG_SPACES
+ | PARSE_FLAG_ACCEPT_STRAYS
+ ;
+
+#ifdef PP_BENCH
+ do next(); while (tok != TOK_EOF); return 0;
+#endif
+
+ token_seen = spcs = 0;
+ pp_line(s1, file, 0);
+
+ for (;;) {
+ iptr = s1->include_stack_ptr;
+ next();
+ if (tok == TOK_EOF)
+ break;
+ level = s1->include_stack_ptr - iptr;
+ if (level) {
+ if (level > 0)
+ pp_line(s1, *iptr, 0);
+ pp_line(s1, file, level);
+ }
+
+ if (0 == token_seen) {
+ if (tok == ' ') {
+ ++spcs;
+ continue;
+ }
+ if (tok == TOK_LINEFEED) {
+ spcs = 0;
+ continue;
+ }
+ pp_line(s1, file, 0);
+ while (spcs)
+ fputs(" ", s1->ppfp), --spcs;
+ token_seen = 1;
+
+ } else if (tok == TOK_LINEFEED) {
+ ++file->line_ref;
+ token_seen = 0;
+ }
+
+ fputs(get_tok_str(tok, &tokc), s1->ppfp);
+ }
+
+ return 0;
+}
diff --git a/src/tccrun.c b/src/tccrun.c
new file mode 100644
index 0000000..cb98392
--- /dev/null
+++ b/src/tccrun.c
@@ -0,0 +1,772 @@
+/*
+ * TCC - Tiny C Compiler - Support for -run switch
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "tcc.h"
+
+/* only native compiler supports -run */
+#ifdef TCC_IS_NATIVE
+
+#ifdef CONFIG_TCC_BACKTRACE
+ST_DATA int rt_num_callers = 6;
+ST_DATA const char **rt_bound_error_msg;
+ST_DATA void *rt_prog_main;
+#endif
+
+#ifdef _WIN32
+#define ucontext_t CONTEXT
+#endif
+
+static void set_pages_executable(void *ptr, unsigned long length);
+static void set_exception_handler(void);
+static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level);
+static void rt_error(ucontext_t *uc, const char *fmt, ...);
+static int tcc_relocate_ex(TCCState *s1, void *ptr);
+
+#ifdef _WIN64
+static void win64_add_function_table(TCCState *s1);
+#endif
+
+/* ------------------------------------------------------------- */
+/* Do all relocations (needed before using tcc_get_symbol())
+ Returns -1 on error. */
+
+LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr)
+{
+ int ret;
+
+ if (TCC_RELOCATE_AUTO != ptr)
+ return tcc_relocate_ex(s1, ptr);
+
+ ret = tcc_relocate_ex(s1, NULL);
+ if (ret < 0)
+ return ret;
+
+#ifdef HAVE_SELINUX
+ { /* Use mmap instead of malloc for Selinux. Ref:
+ http://www.gnu.org/s/libc/manual/html_node/File-Size.html */
+
+ char tmpfname[] = "/tmp/.tccrunXXXXXX";
+ int fd = mkstemp (tmpfname);
+
+ s1->mem_size = ret;
+ unlink (tmpfname);
+ ftruncate (fd, s1->mem_size);
+
+ s1->write_mem = mmap (NULL, ret, PROT_READ|PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (s1->write_mem == MAP_FAILED)
+ tcc_error("/tmp not writeable");
+
+ s1->runtime_mem = mmap (NULL, ret, PROT_READ|PROT_EXEC,
+ MAP_SHARED, fd, 0);
+ if (s1->runtime_mem == MAP_FAILED)
+ tcc_error("/tmp not executable");
+
+ ret = tcc_relocate_ex(s1, s1->write_mem);
+ }
+#else
+ s1->runtime_mem = tcc_malloc(ret);
+ ret = tcc_relocate_ex(s1, s1->runtime_mem);
+#endif
+ return ret;
+}
+
+/* launch the compiled program with the given arguments */
+LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
+{
+ int (*prog_main)(int, char **);
+ int ret;
+
+ if (tcc_relocate(s1, TCC_RELOCATE_AUTO) < 0)
+ return -1;
+
+ prog_main = tcc_get_symbol_err(s1, s1->runtime_main);
+
+#ifdef CONFIG_TCC_BACKTRACE
+ if (s1->do_debug) {
+ set_exception_handler();
+ rt_prog_main = prog_main;
+ }
+#endif
+
+#ifdef CONFIG_TCC_BCHECK
+ if (s1->do_bounds_check) {
+ void (*bound_init)(void);
+ void (*bound_exit)(void);
+ void (*bound_new_region)(void *p, addr_t size);
+ int (*bound_delete_region)(void *p);
+ int i;
+
+ /* set error function */
+ rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg");
+ /* XXX: use .init section so that it also work in binary ? */
+ bound_init = tcc_get_symbol_err(s1, "__bound_init");
+ bound_exit = tcc_get_symbol_err(s1, "__bound_exit");
+ bound_new_region = tcc_get_symbol_err(s1, "__bound_new_region");
+ bound_delete_region = tcc_get_symbol_err(s1, "__bound_delete_region");
+ bound_init();
+ /* mark argv area as valid */
+ bound_new_region(argv, argc*sizeof(argv[0]));
+ for (i=0; i<argc; ++i)
+ bound_new_region(argv[i], strlen(argv[i]));
+
+ errno = 0; /* clean errno value */
+ ret = (*prog_main)(argc, argv);
+
+ /* unmark argv area */
+ for (i=0; i<argc; ++i)
+ bound_delete_region(argv[i]);
+ bound_delete_region(argv);
+
+ bound_exit();
+ } else
+#endif
+ {
+ errno = 0; /* clean errno value */
+ ret = (*prog_main)(argc, argv);
+ }
+ return ret;
+}
+
+/* relocate code. Return -1 on error, required size if ptr is NULL,
+ otherwise copy code into buffer passed by the caller */
+static int tcc_relocate_ex(TCCState *s1, void *ptr)
+{
+ Section *s;
+ unsigned long offset, length;
+ addr_t mem;
+ int i;
+
+ if (NULL == ptr) {
+ s1->nb_errors = 0;
+#ifdef TCC_TARGET_PE
+ pe_output_file(s1, NULL);
+#else
+ tcc_add_runtime(s1);
+ relocate_common_syms();
+ tcc_add_linker_symbols(s1);
+ build_got_entries(s1);
+#endif
+ if (s1->nb_errors)
+ return -1;
+ }
+
+ offset = 0, mem = (addr_t)ptr;
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (0 == (s->sh_flags & SHF_ALLOC))
+ continue;
+ length = s->data_offset;
+ s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0;
+ offset = (offset + length + 15) & ~15;
+ }
+ offset += 16;
+
+ /* relocate symbols */
+ relocate_syms(s1, 1);
+ if (s1->nb_errors)
+ return -1;
+
+ if (0 == mem)
+ return offset;
+
+ /* relocate each section */
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (s->reloc)
+ relocate_section(s1, s);
+ }
+ relocate_plt(s1);
+
+ for(i = 1; i < s1->nb_sections; i++) {
+ s = s1->sections[i];
+ if (0 == (s->sh_flags & SHF_ALLOC))
+ continue;
+ length = s->data_offset;
+ // printf("%-12s %08lx %04x\n", s->name, s->sh_addr, length);
+ ptr = (void*)s->sh_addr;
+ if (NULL == s->data || s->sh_type == SHT_NOBITS)
+ memset(ptr, 0, length);
+ else
+ memcpy(ptr, s->data, length);
+ /* mark executable sections as executable in memory */
+ if (s->sh_flags & SHF_EXECINSTR)
+ set_pages_executable(ptr, length);
+ }
+
+#ifdef _WIN64
+ win64_add_function_table(s1);
+#endif
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+/* allow to run code in memory */
+
+static void set_pages_executable(void *ptr, unsigned long length)
+{
+#ifdef _WIN32
+ unsigned long old_protect;
+ VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect);
+#else
+ extern void __clear_cache(char *beginning, char *end);
+#ifndef PAGESIZE
+# define PAGESIZE 4096
+#endif
+ addr_t start, end;
+ start = (addr_t)ptr & ~(PAGESIZE - 1);
+ end = (addr_t)ptr + length;
+ end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1);
+ mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC);
+ __clear_cache(ptr, ptr + length);
+#endif
+}
+
+/* ------------------------------------------------------------- */
+#ifdef CONFIG_TCC_BACKTRACE
+
+ST_FUNC void tcc_set_num_callers(int n)
+{
+ rt_num_callers = n;
+}
+
+/* print the position in the source file of PC value 'pc' by reading
+ the stabs debug information */
+static addr_t rt_printline(addr_t wanted_pc, const char *msg)
+{
+ char func_name[128], last_func_name[128];
+ addr_t func_addr, last_pc, pc;
+ const char *incl_files[INCLUDE_STACK_SIZE];
+ int incl_index, len, last_line_num, i;
+ const char *str, *p;
+
+ Stab_Sym *stab_sym = NULL, *stab_sym_end, *sym;
+ int stab_len = 0;
+ char *stab_str = NULL;
+
+ if (stab_section) {
+ stab_len = stab_section->data_offset;
+ stab_sym = (Stab_Sym *)stab_section->data;
+ stab_str = (char *) stabstr_section->data;
+ }
+
+ func_name[0] = '\0';
+ func_addr = 0;
+ incl_index = 0;
+ last_func_name[0] = '\0';
+ last_pc = (addr_t)-1;
+ last_line_num = 1;
+
+ if (!stab_sym)
+ goto no_stabs;
+
+ stab_sym_end = (Stab_Sym*)((char*)stab_sym + stab_len);
+ for (sym = stab_sym + 1; sym < stab_sym_end; ++sym) {
+ switch(sym->n_type) {
+ /* function start or end */
+ case N_FUN:
+ if (sym->n_strx == 0) {
+ /* we test if between last line and end of function */
+ pc = sym->n_value + func_addr;
+ if (wanted_pc >= last_pc && wanted_pc < pc)
+ goto found;
+ func_name[0] = '\0';
+ func_addr = 0;
+ } else {
+ str = stab_str + sym->n_strx;
+ p = strchr(str, ':');
+ if (!p) {
+ pstrcpy(func_name, sizeof(func_name), str);
+ } else {
+ len = p - str;
+ if (len > sizeof(func_name) - 1)
+ len = sizeof(func_name) - 1;
+ memcpy(func_name, str, len);
+ func_name[len] = '\0';
+ }
+ func_addr = sym->n_value;
+ }
+ break;
+ /* line number info */
+ case N_SLINE:
+ pc = sym->n_value + func_addr;
+ if (wanted_pc >= last_pc && wanted_pc < pc)
+ goto found;
+ last_pc = pc;
+ last_line_num = sym->n_desc;
+ /* XXX: slow! */
+ strcpy(last_func_name, func_name);
+ break;
+ /* include files */
+ case N_BINCL:
+ str = stab_str + sym->n_strx;
+ add_incl:
+ if (incl_index < INCLUDE_STACK_SIZE) {
+ incl_files[incl_index++] = str;
+ }
+ break;
+ case N_EINCL:
+ if (incl_index > 1)
+ incl_index--;
+ break;
+ case N_SO:
+ if (sym->n_strx == 0) {
+ incl_index = 0; /* end of translation unit */
+ } else {
+ str = stab_str + sym->n_strx;
+ /* do not add path */
+ len = strlen(str);
+ if (len > 0 && str[len - 1] != '/')
+ goto add_incl;
+ }
+ break;
+ }
+ }
+
+no_stabs:
+ /* second pass: we try symtab symbols (no line number info) */
+ incl_index = 0;
+ if (symtab_section)
+ {
+ ElfW(Sym) *sym, *sym_end;
+ int type;
+
+ sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset);
+ for(sym = (ElfW(Sym) *)symtab_section->data + 1;
+ sym < sym_end;
+ sym++) {
+ type = ELFW(ST_TYPE)(sym->st_info);
+ if (type == STT_FUNC || type == STT_GNU_IFUNC) {
+ if (wanted_pc >= sym->st_value &&
+ wanted_pc < sym->st_value + sym->st_size) {
+ pstrcpy(last_func_name, sizeof(last_func_name),
+ (char *) strtab_section->data + sym->st_name);
+ func_addr = sym->st_value;
+ goto found;
+ }
+ }
+ }
+ }
+ /* did not find any info: */
+ fprintf(stderr, "%s %p ???\n", msg, (void*)wanted_pc);
+ fflush(stderr);
+ return 0;
+ found:
+ i = incl_index;
+ if (i > 0)
+ fprintf(stderr, "%s:%d: ", incl_files[--i], last_line_num);
+ fprintf(stderr, "%s %p", msg, (void*)wanted_pc);
+ if (last_func_name[0] != '\0')
+ fprintf(stderr, " %s()", last_func_name);
+ if (--i >= 0) {
+ fprintf(stderr, " (included from ");
+ for (;;) {
+ fprintf(stderr, "%s", incl_files[i]);
+ if (--i < 0)
+ break;
+ fprintf(stderr, ", ");
+ }
+ fprintf(stderr, ")");
+ }
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ return func_addr;
+}
+
+/* emit a run time error at position 'pc' */
+static void rt_error(ucontext_t *uc, const char *fmt, ...)
+{
+ va_list ap;
+ addr_t pc;
+ int i;
+
+ fprintf(stderr, "Runtime error: ");
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+
+ for(i=0;i<rt_num_callers;i++) {
+ if (rt_get_caller_pc(&pc, uc, i) < 0)
+ break;
+ pc = rt_printline(pc, i ? "by" : "at");
+ if (pc == (addr_t)rt_prog_main && pc)
+ break;
+ }
+}
+
+/* ------------------------------------------------------------- */
+#ifndef _WIN32
+
+/* signal handler for fatal errors */
+static void sig_error(int signum, siginfo_t *siginf, void *puc)
+{
+ ucontext_t *uc = puc;
+
+ switch(signum) {
+ case SIGFPE:
+ switch(siginf->si_code) {
+ case FPE_INTDIV:
+ case FPE_FLTDIV:
+ rt_error(uc, "division by zero");
+ break;
+ default:
+ rt_error(uc, "floating point exception");
+ break;
+ }
+ break;
+ case SIGBUS:
+ case SIGSEGV:
+ if (rt_bound_error_msg && *rt_bound_error_msg)
+ rt_error(uc, *rt_bound_error_msg);
+ else
+ rt_error(uc, "dereferencing invalid pointer");
+ break;
+ case SIGILL:
+ rt_error(uc, "illegal instruction");
+ break;
+ case SIGABRT:
+ rt_error(uc, "abort() called");
+ break;
+ default:
+ rt_error(uc, "caught signal %d", signum);
+ break;
+ }
+ exit(255);
+}
+
+#ifndef SA_SIGINFO
+# define SA_SIGINFO 0x00000004u
+#endif
+
+/* Generate a stack backtrace when a CPU exception occurs. */
+static void set_exception_handler(void)
+{
+ struct sigaction sigact;
+ /* install TCC signal handlers to print debug info on fatal
+ runtime errors */
+ sigact.sa_flags = SA_SIGINFO | SA_RESETHAND;
+ sigact.sa_sigaction = sig_error;
+ sigemptyset(&sigact.sa_mask);
+ sigaction(SIGFPE, &sigact, NULL);
+ sigaction(SIGILL, &sigact, NULL);
+ sigaction(SIGSEGV, &sigact, NULL);
+ sigaction(SIGBUS, &sigact, NULL);
+ sigaction(SIGABRT, &sigact, NULL);
+}
+
+/* ------------------------------------------------------------- */
+#ifdef __i386__
+
+/* fix for glibc 2.1 */
+#ifndef REG_EIP
+#define REG_EIP EIP
+#define REG_EBP EBP
+#endif
+
+/* return the PC at frame level 'level'. Return negative if not found */
+static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
+{
+ addr_t fp;
+ int i;
+
+ if (level == 0) {
+#if defined(__APPLE__)
+ *paddr = uc->uc_mcontext->__ss.__eip;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ *paddr = uc->uc_mcontext.mc_eip;
+#elif defined(__dietlibc__)
+ *paddr = uc->uc_mcontext.eip;
+#elif defined(__NetBSD__)
+ *paddr = uc->uc_mcontext.__gregs[_REG_EIP];
+#else
+ *paddr = uc->uc_mcontext.gregs[REG_EIP];
+#endif
+ return 0;
+ } else {
+#if defined(__APPLE__)
+ fp = uc->uc_mcontext->__ss.__ebp;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ fp = uc->uc_mcontext.mc_ebp;
+#elif defined(__dietlibc__)
+ fp = uc->uc_mcontext.ebp;
+#elif defined(__NetBSD__)
+ fp = uc->uc_mcontext.__gregs[_REG_EBP];
+#else
+ fp = uc->uc_mcontext.gregs[REG_EBP];
+#endif
+ for(i=1;i<level;i++) {
+ /* XXX: check address validity with program info */
+ if (fp <= 0x1000 || fp >= 0xc0000000)
+ return -1;
+ fp = ((addr_t *)fp)[0];
+ }
+ *paddr = ((addr_t *)fp)[1];
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------- */
+#elif defined(__x86_64__)
+
+/* return the PC at frame level 'level'. Return negative if not found */
+static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
+{
+ addr_t fp;
+ int i;
+
+ if (level == 0) {
+ /* XXX: only support linux */
+#if defined(__APPLE__)
+ *paddr = uc->uc_mcontext->__ss.__rip;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ *paddr = uc->uc_mcontext.mc_rip;
+#elif defined(__NetBSD__)
+ *paddr = uc->uc_mcontext.__gregs[_REG_RIP];
+#else
+ *paddr = uc->uc_mcontext.gregs[REG_RIP];
+#endif
+ return 0;
+ } else {
+#if defined(__APPLE__)
+ fp = uc->uc_mcontext->__ss.__rbp;
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+ fp = uc->uc_mcontext.mc_rbp;
+#elif defined(__NetBSD__)
+ fp = uc->uc_mcontext.__gregs[_REG_RBP];
+#else
+ fp = uc->uc_mcontext.gregs[REG_RBP];
+#endif
+ for(i=1;i<level;i++) {
+ /* XXX: check address validity with program info */
+ if (fp <= 0x1000)
+ return -1;
+ fp = ((addr_t *)fp)[0];
+ }
+ *paddr = ((addr_t *)fp)[1];
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------- */
+#elif defined(__arm__)
+
+/* return the PC at frame level 'level'. Return negative if not found */
+static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
+{
+ addr_t fp, sp;
+ int i;
+
+ if (level == 0) {
+ /* XXX: only supports linux */
+#if defined(__linux__)
+ *paddr = uc->uc_mcontext.arm_pc;
+#else
+ return -1;
+#endif
+ return 0;
+ } else {
+#if defined(__linux__)
+ fp = uc->uc_mcontext.arm_fp;
+ sp = uc->uc_mcontext.arm_sp;
+ if (sp < 0x1000)
+ sp = 0x1000;
+#else
+ return -1;
+#endif
+ /* XXX: specific to tinycc stack frames */
+ if (fp < sp + 12 || fp & 3)
+ return -1;
+ for(i = 1; i < level; i++) {
+ sp = ((addr_t *)fp)[-2];
+ if (sp < fp || sp - fp > 16 || sp & 3)
+ return -1;
+ fp = ((addr_t *)fp)[-3];
+ if (fp <= sp || fp - sp < 12 || fp & 3)
+ return -1;
+ }
+ /* XXX: check address validity with program info */
+ *paddr = ((addr_t *)fp)[-1];
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------- */
+#elif defined(__aarch64__)
+
+static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
+{
+ if (level < 0)
+ return -1;
+ else if (level == 0) {
+ *paddr = uc->uc_mcontext.pc;
+ return 0;
+ }
+ else {
+ addr_t *fp = (addr_t *)uc->uc_mcontext.regs[29];
+ int i;
+ for (i = 1; i < level; i++)
+ fp = (addr_t *)fp[0];
+ *paddr = fp[1];
+ return 0;
+ }
+}
+
+/* ------------------------------------------------------------- */
+#else
+
+#warning add arch specific rt_get_caller_pc()
+static int rt_get_caller_pc(addr_t *paddr, ucontext_t *uc, int level)
+{
+ return -1;
+}
+
+#endif /* !__i386__ */
+
+/* ------------------------------------------------------------- */
+#else /* WIN32 */
+
+static long __stdcall cpu_exception_handler(EXCEPTION_POINTERS *ex_info)
+{
+ EXCEPTION_RECORD *er = ex_info->ExceptionRecord;
+ CONTEXT *uc = ex_info->ContextRecord;
+ switch (er->ExceptionCode) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ if (rt_bound_error_msg && *rt_bound_error_msg)
+ rt_error(uc, *rt_bound_error_msg);
+ else
+ rt_error(uc, "access violation");
+ break;
+ case EXCEPTION_STACK_OVERFLOW:
+ rt_error(uc, "stack overflow");
+ break;
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ rt_error(uc, "division by zero");
+ break;
+ default:
+ rt_error(uc, "exception caught");
+ break;
+ }
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+/* Generate a stack backtrace when a CPU exception occurs. */
+static void set_exception_handler(void)
+{
+ SetUnhandledExceptionFilter(cpu_exception_handler);
+}
+
+#ifdef _WIN64
+static void win64_add_function_table(TCCState *s1)
+{
+ RtlAddFunctionTable(
+ (RUNTIME_FUNCTION*)s1->uw_pdata->sh_addr,
+ s1->uw_pdata->data_offset / sizeof (RUNTIME_FUNCTION),
+ text_section->sh_addr
+ );
+}
+#endif
+
+/* return the PC at frame level 'level'. Return non zero if not found */
+static int rt_get_caller_pc(addr_t *paddr, CONTEXT *uc, int level)
+{
+ addr_t fp, pc;
+ int i;
+#ifdef _WIN64
+ pc = uc->Rip;
+ fp = uc->Rbp;
+#else
+ pc = uc->Eip;
+ fp = uc->Ebp;
+#endif
+ if (level > 0) {
+ for(i = 1; i < level; i++) {
+ /* XXX: check address validity with program info */
+ if (fp <= 0x1000 || fp >= 0xc0000000)
+ return -1;
+ fp = ((addr_t*)fp)[0];
+ }
+ pc = ((addr_t*)fp)[1];
+ }
+ *paddr = pc;
+ return 0;
+}
+
+#endif /* _WIN32 */
+#endif /* CONFIG_TCC_BACKTRACE */
+/* ------------------------------------------------------------- */
+#ifdef CONFIG_TCC_STATIC
+
+/* dummy function for profiling */
+ST_FUNC void *dlopen(const char *filename, int flag)
+{
+ return NULL;
+}
+
+ST_FUNC void dlclose(void *p)
+{
+}
+
+ST_FUNC const char *dlerror(void)
+{
+ return "error";
+}
+
+typedef struct TCCSyms {
+ char *str;
+ void *ptr;
+} TCCSyms;
+
+
+/* add the symbol you want here if no dynamic linking is done */
+static TCCSyms tcc_syms[] = {
+#if !defined(CONFIG_TCCBOOT)
+#define TCCSYM(a) { #a, &a, },
+ TCCSYM(printf)
+ TCCSYM(fprintf)
+ TCCSYM(fopen)
+ TCCSYM(fclose)
+#undef TCCSYM
+#endif
+ { NULL, NULL },
+};
+
+ST_FUNC void *resolve_sym(TCCState *s1, const char *symbol)
+{
+ TCCSyms *p;
+ p = tcc_syms;
+ while (p->str != NULL) {
+ if (!strcmp(p->str, symbol))
+ return p->ptr;
+ p++;
+ }
+ return NULL;
+}
+
+#elif !defined(_WIN32)
+
+ST_FUNC void *resolve_sym(TCCState *s1, const char *sym)
+{
+ return dlsym(RTLD_DEFAULT, sym);
+}
+
+#endif /* CONFIG_TCC_STATIC */
+#endif /* TCC_IS_NATIVE */
+/* ------------------------------------------------------------- */
diff --git a/src/tcctok.h b/src/tcctok.h
new file mode 100644
index 0000000..5972fd6
--- /dev/null
+++ b/src/tcctok.h
@@ -0,0 +1,335 @@
+/* keywords */
+ DEF(TOK_INT, "int")
+ DEF(TOK_VOID, "void")
+ DEF(TOK_CHAR, "char")
+ DEF(TOK_IF, "if")
+ DEF(TOK_ELSE, "else")
+ DEF(TOK_WHILE, "while")
+ DEF(TOK_BREAK, "break")
+ DEF(TOK_RETURN, "return")
+ DEF(TOK_FOR, "for")
+ DEF(TOK_EXTERN, "extern")
+ DEF(TOK_STATIC, "static")
+ DEF(TOK_UNSIGNED, "unsigned")
+ DEF(TOK_GOTO, "goto")
+ DEF(TOK_DO, "do")
+ DEF(TOK_CONTINUE, "continue")
+ DEF(TOK_SWITCH, "switch")
+ DEF(TOK_CASE, "case")
+
+ DEF(TOK_CONST1, "const")
+ DEF(TOK_CONST2, "__const") /* gcc keyword */
+ DEF(TOK_CONST3, "__const__") /* gcc keyword */
+ DEF(TOK_VOLATILE1, "volatile")
+ DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */
+ DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */
+ DEF(TOK_LONG, "long")
+ DEF(TOK_REGISTER, "register")
+ DEF(TOK_SIGNED1, "signed")
+ DEF(TOK_SIGNED2, "__signed") /* gcc keyword */
+ DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */
+ DEF(TOK_AUTO, "auto")
+ DEF(TOK_INLINE1, "inline")
+ DEF(TOK_INLINE2, "__inline") /* gcc keyword */
+ DEF(TOK_INLINE3, "__inline__") /* gcc keyword */
+ DEF(TOK_RESTRICT1, "restrict")
+ DEF(TOK_RESTRICT2, "__restrict")
+ DEF(TOK_RESTRICT3, "__restrict__")
+ DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */
+
+ DEF(TOK_FLOAT, "float")
+ DEF(TOK_DOUBLE, "double")
+ DEF(TOK_BOOL, "_Bool")
+ DEF(TOK_SHORT, "short")
+ DEF(TOK_STRUCT, "struct")
+ DEF(TOK_UNION, "union")
+ DEF(TOK_TYPEDEF, "typedef")
+ DEF(TOK_DEFAULT, "default")
+ DEF(TOK_ENUM, "enum")
+ DEF(TOK_SIZEOF, "sizeof")
+ DEF(TOK_ATTRIBUTE1, "__attribute")
+ DEF(TOK_ATTRIBUTE2, "__attribute__")
+ DEF(TOK_ALIGNOF1, "__alignof")
+ DEF(TOK_ALIGNOF2, "__alignof__")
+ DEF(TOK_TYPEOF1, "typeof")
+ DEF(TOK_TYPEOF2, "__typeof")
+ DEF(TOK_TYPEOF3, "__typeof__")
+ DEF(TOK_LABEL, "__label__")
+ DEF(TOK_ASM1, "asm")
+ DEF(TOK_ASM2, "__asm")
+ DEF(TOK_ASM3, "__asm__")
+
+#ifdef TCC_TARGET_ARM64
+ DEF(TOK_UINT128, "__uint128_t")
+#endif
+
+/*********************************************************************/
+/* the following are not keywords. They are included to ease parsing */
+/* preprocessor only */
+ DEF(TOK_DEFINE, "define")
+ DEF(TOK_INCLUDE, "include")
+ DEF(TOK_INCLUDE_NEXT, "include_next")
+ DEF(TOK_IFDEF, "ifdef")
+ DEF(TOK_IFNDEF, "ifndef")
+ DEF(TOK_ELIF, "elif")
+ DEF(TOK_ENDIF, "endif")
+ DEF(TOK_DEFINED, "defined")
+ DEF(TOK_UNDEF, "undef")
+ DEF(TOK_ERROR, "error")
+ DEF(TOK_WARNING, "warning")
+ DEF(TOK_LINE, "line")
+ DEF(TOK_PRAGMA, "pragma")
+ DEF(TOK___LINE__, "__LINE__")
+ DEF(TOK___FILE__, "__FILE__")
+ DEF(TOK___DATE__, "__DATE__")
+ DEF(TOK___TIME__, "__TIME__")
+ DEF(TOK___FUNCTION__, "__FUNCTION__")
+ DEF(TOK___VA_ARGS__, "__VA_ARGS__")
+
+/* special identifiers */
+ DEF(TOK___FUNC__, "__func__")
+
+/* special floating point values */
+ DEF(TOK___NAN__, "__nan__")
+ DEF(TOK___SNAN__, "__snan__")
+ DEF(TOK___INF__, "__inf__")
+
+/* attribute identifiers */
+/* XXX: handle all tokens generically since speed is not critical */
+ DEF(TOK_SECTION1, "section")
+ DEF(TOK_SECTION2, "__section__")
+ DEF(TOK_ALIGNED1, "aligned")
+ DEF(TOK_ALIGNED2, "__aligned__")
+ DEF(TOK_PACKED1, "packed")
+ DEF(TOK_PACKED2, "__packed__")
+ DEF(TOK_WEAK1, "weak")
+ DEF(TOK_WEAK2, "__weak__")
+ DEF(TOK_ALIAS1, "alias")
+ DEF(TOK_ALIAS2, "__alias__")
+ DEF(TOK_UNUSED1, "unused")
+ DEF(TOK_UNUSED2, "__unused__")
+ DEF(TOK_CDECL1, "cdecl")
+ DEF(TOK_CDECL2, "__cdecl")
+ DEF(TOK_CDECL3, "__cdecl__")
+ DEF(TOK_STDCALL1, "stdcall")
+ DEF(TOK_STDCALL2, "__stdcall")
+ DEF(TOK_STDCALL3, "__stdcall__")
+ DEF(TOK_FASTCALL1, "fastcall")
+ DEF(TOK_FASTCALL2, "__fastcall")
+ DEF(TOK_FASTCALL3, "__fastcall__")
+ DEF(TOK_MODE, "__mode__")
+ DEF(TOK_MODE_DI, "__DI__")
+ DEF(TOK_MODE_HI, "__HI__")
+ DEF(TOK_MODE_SI, "__SI__")
+ DEF(TOK_DLLEXPORT, "dllexport")
+ DEF(TOK_DLLIMPORT, "dllimport")
+ DEF(TOK_NORETURN1, "noreturn")
+ DEF(TOK_NORETURN2, "__noreturn__")
+ DEF(TOK_VISIBILITY1, "visibility")
+ DEF(TOK_VISIBILITY2, "__visibility__")
+ DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p")
+ DEF(TOK_builtin_constant_p, "__builtin_constant_p")
+ DEF(TOK_builtin_frame_address, "__builtin_frame_address")
+ DEF(TOK_builtin_return_address, "__builtin_return_address")
+#ifdef TCC_TARGET_X86_64
+#ifdef TCC_TARGET_PE
+ DEF(TOK_builtin_va_start, "__builtin_va_start")
+#else
+ DEF(TOK_builtin_va_arg_types, "__builtin_va_arg_types")
+#endif
+#endif
+ DEF(TOK_REGPARM1, "regparm")
+ DEF(TOK_REGPARM2, "__regparm__")
+
+#ifdef TCC_TARGET_ARM64
+ DEF(TOK___va_start, "__va_start")
+ DEF(TOK___va_arg, "__va_arg")
+#endif
+
+/* pragma */
+ DEF(TOK_pack, "pack")
+#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64)
+ /* already defined for assembler */
+ DEF(TOK_ASM_push, "push")
+ DEF(TOK_ASM_pop, "pop")
+#endif
+ DEF(TOK_comment, "comment")
+ DEF(TOK_lib, "lib")
+ DEF(TOK_push_macro, "push_macro")
+ DEF(TOK_pop_macro, "pop_macro")
+ DEF(TOK_once, "once")
+
+/* builtin functions or variables */
+#ifndef TCC_ARM_EABI
+ DEF(TOK_memcpy, "memcpy")
+ DEF(TOK_memset, "memset")
+ DEF(TOK___divdi3, "__divdi3")
+ DEF(TOK___moddi3, "__moddi3")
+ DEF(TOK___udivdi3, "__udivdi3")
+ DEF(TOK___umoddi3, "__umoddi3")
+ DEF(TOK___ashrdi3, "__ashrdi3")
+ DEF(TOK___lshrdi3, "__lshrdi3")
+ DEF(TOK___ashldi3, "__ashldi3")
+ DEF(TOK___floatundisf, "__floatundisf")
+ DEF(TOK___floatundidf, "__floatundidf")
+# ifndef TCC_ARM_VFP
+ DEF(TOK___floatundixf, "__floatundixf")
+ DEF(TOK___fixunsxfdi, "__fixunsxfdi")
+# endif
+ DEF(TOK___fixunssfdi, "__fixunssfdi")
+ DEF(TOK___fixunsdfdi, "__fixunsdfdi")
+#endif
+
+#if defined TCC_TARGET_ARM
+# ifdef TCC_ARM_EABI
+ DEF(TOK_memcpy, "__aeabi_memcpy")
+ DEF(TOK_memcpy4, "__aeabi_memcpy4")
+ DEF(TOK_memcpy8, "__aeabi_memcpy8")
+ DEF(TOK_memset, "__aeabi_memset")
+ DEF(TOK___aeabi_ldivmod, "__aeabi_ldivmod")
+ DEF(TOK___aeabi_uldivmod, "__aeabi_uldivmod")
+ DEF(TOK___aeabi_idivmod, "__aeabi_idivmod")
+ DEF(TOK___aeabi_uidivmod, "__aeabi_uidivmod")
+ DEF(TOK___divsi3, "__aeabi_idiv")
+ DEF(TOK___udivsi3, "__aeabi_uidiv")
+ DEF(TOK___floatdisf, "__aeabi_l2f")
+ DEF(TOK___floatdidf, "__aeabi_l2d")
+ DEF(TOK___fixsfdi, "__aeabi_f2lz")
+ DEF(TOK___fixdfdi, "__aeabi_d2lz")
+ DEF(TOK___ashrdi3, "__aeabi_lasr")
+ DEF(TOK___lshrdi3, "__aeabi_llsr")
+ DEF(TOK___ashldi3, "__aeabi_llsl")
+ DEF(TOK___floatundisf, "__aeabi_ul2f")
+ DEF(TOK___floatundidf, "__aeabi_ul2d")
+ DEF(TOK___fixunssfdi, "__aeabi_f2ulz")
+ DEF(TOK___fixunsdfdi, "__aeabi_d2ulz")
+# else
+ DEF(TOK___modsi3, "__modsi3")
+ DEF(TOK___umodsi3, "__umodsi3")
+ DEF(TOK___divsi3, "__divsi3")
+ DEF(TOK___udivsi3, "__udivsi3")
+ DEF(TOK___floatdisf, "__floatdisf")
+ DEF(TOK___floatdidf, "__floatdidf")
+# ifndef TCC_ARM_VFP
+ DEF(TOK___floatdixf, "__floatdixf")
+ DEF(TOK___fixunssfsi, "__fixunssfsi")
+ DEF(TOK___fixunsdfsi, "__fixunsdfsi")
+ DEF(TOK___fixunsxfsi, "__fixunsxfsi")
+ DEF(TOK___fixxfdi, "__fixxfdi")
+# endif
+ DEF(TOK___fixsfdi, "__fixsfdi")
+ DEF(TOK___fixdfdi, "__fixdfdi")
+# endif
+#endif
+
+#if defined TCC_TARGET_C67
+ DEF(TOK__divi, "_divi")
+ DEF(TOK__divu, "_divu")
+ DEF(TOK__divf, "_divf")
+ DEF(TOK__divd, "_divd")
+ DEF(TOK__remi, "_remi")
+ DEF(TOK__remu, "_remu")
+#endif
+
+#if defined TCC_TARGET_I386
+ DEF(TOK___fixsfdi, "__fixsfdi")
+ DEF(TOK___fixdfdi, "__fixdfdi")
+ DEF(TOK___fixxfdi, "__fixxfdi")
+
+ #ifndef COMMIT_4ad186c5ef61_IS_FIXED
+ DEF(TOK___tcc_cvt_ftol, "__tcc_cvt_ftol")
+ #endif
+#endif
+
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+ DEF(TOK_alloca, "alloca")
+#endif
+
+#if defined TCC_TARGET_PE
+ DEF(TOK___chkstk, "__chkstk")
+#endif
+#ifdef TCC_TARGET_ARM64
+ DEF(TOK___arm64_clear_cache, "__arm64_clear_cache")
+ DEF(TOK___addtf3, "__addtf3")
+ DEF(TOK___subtf3, "__subtf3")
+ DEF(TOK___multf3, "__multf3")
+ DEF(TOK___divtf3, "__divtf3")
+ DEF(TOK___extendsftf2, "__extendsftf2")
+ DEF(TOK___extenddftf2, "__extenddftf2")
+ DEF(TOK___trunctfsf2, "__trunctfsf2")
+ DEF(TOK___trunctfdf2, "__trunctfdf2")
+ DEF(TOK___fixtfsi, "__fixtfsi")
+ DEF(TOK___fixtfdi, "__fixtfdi")
+ DEF(TOK___fixunstfsi, "__fixunstfsi")
+ DEF(TOK___fixunstfdi, "__fixunstfdi")
+ DEF(TOK___floatsitf, "__floatsitf")
+ DEF(TOK___floatditf, "__floatditf")
+ DEF(TOK___floatunsitf, "__floatunsitf")
+ DEF(TOK___floatunditf, "__floatunditf")
+ DEF(TOK___eqtf2, "__eqtf2")
+ DEF(TOK___netf2, "__netf2")
+ DEF(TOK___lttf2, "__lttf2")
+ DEF(TOK___letf2, "__letf2")
+ DEF(TOK___gttf2, "__gttf2")
+ DEF(TOK___getf2, "__getf2")
+#endif
+
+/* bound checking symbols */
+#ifdef CONFIG_TCC_BCHECK
+ DEF(TOK___bound_ptr_add, "__bound_ptr_add")
+ DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1")
+ DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2")
+ DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4")
+ DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8")
+ DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12")
+ DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16")
+ DEF(TOK___bound_main_arg, "__bound_main_arg")
+ DEF(TOK___bound_local_new, "__bound_local_new")
+ DEF(TOK___bound_local_delete, "__bound_local_delete")
+ DEF(TOK___bound_init, "__bound_init")
+# ifdef TCC_TARGET_PE
+ DEF(TOK_malloc, "malloc")
+ DEF(TOK_free, "free")
+ DEF(TOK_realloc, "realloc")
+ DEF(TOK_memalign, "memalign")
+ DEF(TOK_calloc, "calloc")
+# endif
+ DEF(TOK_memmove, "memmove")
+ DEF(TOK_strlen, "strlen")
+ DEF(TOK_strcpy, "strcpy")
+#endif
+
+/* Tiny Assembler */
+ DEF_ASM(byte)
+ DEF_ASM(word)
+ DEF_ASM(align)
+ DEF_ASM(p2align)
+ DEF_ASM(skip)
+ DEF_ASM(space)
+ DEF_ASM(string)
+ DEF_ASM(asciz)
+ DEF_ASM(ascii)
+ DEF_ASM(file)
+ DEF_ASM(globl)
+ DEF_ASM(global)
+ DEF_ASM(hidden)
+ DEF_ASM(ident)
+ DEF_ASM(size)
+ DEF_ASM(type)
+ DEF_ASM(text)
+ DEF_ASM(data)
+ DEF_ASM(bss)
+ DEF_ASM(previous)
+ DEF_ASM(fill)
+ DEF_ASM(org)
+ DEF_ASM(quad)
+#if defined(TCC_TARGET_I386)
+ DEF_ASM(code16)
+ DEF_ASM(code32)
+#elif defined(TCC_TARGET_X86_64)
+ DEF_ASM(code64)
+#endif
+
+#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
+#include "x86/i386-tok.h"
+#endif
diff --git a/src/tms320c67/c67-gen.c b/src/tms320c67/c67-gen.c
new file mode 100644
index 0000000..84ff78c
--- /dev/null
+++ b/src/tms320c67/c67-gen.c
@@ -0,0 +1,2559 @@
+/*
+ * TMS320C67xx code generator for TCC
+ *
+ * Copyright (c) 2001, 2002 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef TARGET_DEFS_ONLY
+
+/* #define ASSEMBLY_LISTING_C67 */
+
+/* number of available registers */
+#define NB_REGS 24
+
+typedef int RegArgs;
+
+/* a register can belong to several classes. The classes must be
+ sorted from more general to more precise (see gv2() code which does
+ assumptions on it). */
+#define RC_INT 0x0001 /* generic integer register */
+#define RC_FLOAT 0x0002 /* generic float register */
+#define RC_EAX 0x0004
+#define RC_ST0 0x0008
+#define RC_ECX 0x0010
+#define RC_EDX 0x0020
+#define RC_INT_BSIDE 0x00000040 /* generic integer register on b side */
+#define RC_C67_A4 0x00000100
+#define RC_C67_A5 0x00000200
+#define RC_C67_B4 0x00000400
+#define RC_C67_B5 0x00000800
+#define RC_C67_A6 0x00001000
+#define RC_C67_A7 0x00002000
+#define RC_C67_B6 0x00004000
+#define RC_C67_B7 0x00008000
+#define RC_C67_A8 0x00010000
+#define RC_C67_A9 0x00020000
+#define RC_C67_B8 0x00040000
+#define RC_C67_B9 0x00080000
+#define RC_C67_A10 0x00100000
+#define RC_C67_A11 0x00200000
+#define RC_C67_B10 0x00400000
+#define RC_C67_B11 0x00800000
+#define RC_C67_A12 0x01000000
+#define RC_C67_A13 0x02000000
+#define RC_C67_B12 0x04000000
+#define RC_C67_B13 0x08000000
+#define RC_IRET RC_C67_A4 /* function return: integer register */
+#define RC_LRET RC_C67_A5 /* function return: second integer register */
+#define RC_FRET RC_C67_A4 /* function return: float register */
+
+/* pretty names for the registers */
+enum {
+ TREG_EAX = 0, // really A2
+ TREG_ECX, // really A3
+ TREG_EDX, // really B0
+ TREG_ST0, // really B1
+ TREG_C67_A4,
+ TREG_C67_A5,
+ TREG_C67_B4,
+ TREG_C67_B5,
+ TREG_C67_A6,
+ TREG_C67_A7,
+ TREG_C67_B6,
+ TREG_C67_B7,
+ TREG_C67_A8,
+ TREG_C67_A9,
+ TREG_C67_B8,
+ TREG_C67_B9,
+ TREG_C67_A10,
+ TREG_C67_A11,
+ TREG_C67_B10,
+ TREG_C67_B11,
+ TREG_C67_A12,
+ TREG_C67_A13,
+ TREG_C67_B12,
+ TREG_C67_B13,
+};
+
+/* return registers for function */
+#define REG_IRET TREG_C67_A4 /* single word int return register */
+#define REG_LRET TREG_C67_A5 /* second word return register (for long long) */
+#define REG_FRET TREG_C67_A4 /* float return register */
+
+/* defined if function parameters must be evaluated in reverse order */
+/* #define INVERT_FUNC_PARAMS */
+
+/* defined if structures are passed as pointers. Otherwise structures
+ are directly pushed on stack. */
+/* #define FUNC_STRUCT_PARAM_AS_PTR */
+
+/* pointer size, in bytes */
+#define PTR_SIZE 4
+
+/* long double size and alignment, in bytes */
+#define LDOUBLE_SIZE 12
+#define LDOUBLE_ALIGN 4
+/* maximum alignment (for aligned attribute support) */
+#define MAX_ALIGN 8
+
+/******************************************************/
+/* ELF defines */
+
+#define EM_TCC_TARGET EM_C60
+
+/* relocation type for 32 bit data relocation */
+#define R_DATA_32 R_C60_32
+#define R_DATA_PTR R_C60_32
+#define R_JMP_SLOT R_C60_JMP_SLOT
+#define R_COPY R_C60_COPY
+
+#define ELF_START_ADDR 0x00000400
+#define ELF_PAGE_SIZE 0x1000
+
+/******************************************************/
+#else /* ! TARGET_DEFS_ONLY */
+/******************************************************/
+#include "../tcc.h"
+
+ST_DATA const int reg_classes[NB_REGS] = {
+ /* eax */ RC_INT | RC_FLOAT | RC_EAX,
+ // only allow even regs for floats (allow for doubles)
+ /* ecx */ RC_INT | RC_ECX,
+ /* edx */ RC_INT | RC_INT_BSIDE | RC_FLOAT | RC_EDX,
+ // only allow even regs for floats (allow for doubles)
+ /* st0 */ RC_INT | RC_INT_BSIDE | RC_ST0,
+ /* A4 */ RC_C67_A4,
+ /* A5 */ RC_C67_A5,
+ /* B4 */ RC_C67_B4,
+ /* B5 */ RC_C67_B5,
+ /* A6 */ RC_C67_A6,
+ /* A7 */ RC_C67_A7,
+ /* B6 */ RC_C67_B6,
+ /* B7 */ RC_C67_B7,
+ /* A8 */ RC_C67_A8,
+ /* A9 */ RC_C67_A9,
+ /* B8 */ RC_C67_B8,
+ /* B9 */ RC_C67_B9,
+ /* A10 */ RC_C67_A10,
+ /* A11 */ RC_C67_A11,
+ /* B10 */ RC_C67_B10,
+ /* B11 */ RC_C67_B11,
+ /* A12 */ RC_C67_A10,
+ /* A13 */ RC_C67_A11,
+ /* B12 */ RC_C67_B10,
+ /* B13 */ RC_C67_B11
+};
+
+// although tcc thinks it is passing parameters on the stack,
+// the C67 really passes up to the first 10 params in special
+// regs or regs pairs (for 64 bit params). So keep track of
+// the stack offsets so we can translate to the appropriate
+// reg (pair)
+
+#define NoCallArgsPassedOnStack 10
+int NoOfCurFuncArgs;
+int TranslateStackToReg[NoCallArgsPassedOnStack];
+int ParamLocOnStack[NoCallArgsPassedOnStack];
+int TotalBytesPushedOnStack;
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE 1
+#endif
+
+#undef BOOL
+#define BOOL int
+
+#define ALWAYS_ASSERT(x) \
+do {\
+ if (!(x))\
+ tcc_error("internal compiler error file at %s:%d", __FILE__, __LINE__);\
+} while (0)
+
+/******************************************************/
+static unsigned long func_sub_sp_offset;
+static int func_ret_sub;
+
+static BOOL C67_invert_test;
+static int C67_compare_reg;
+
+#ifdef ASSEMBLY_LISTING_C67
+FILE *f = NULL;
+#endif
+
+void C67_g(int c)
+{
+ int ind1;
+
+#ifdef ASSEMBLY_LISTING_C67
+ fprintf(f, " %08X", c);
+#endif
+ ind1 = ind + 4;
+ if (ind1 > (int) cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ cur_text_section->data[ind] = c & 0xff;
+ cur_text_section->data[ind + 1] = (c >> 8) & 0xff;
+ cur_text_section->data[ind + 2] = (c >> 16) & 0xff;
+ cur_text_section->data[ind + 3] = (c >> 24) & 0xff;
+ ind = ind1;
+}
+
+
+/* output a symbol and patch all calls to it */
+void gsym_addr(int t, int a)
+{
+ int n, *ptr;
+ while (t) {
+ ptr = (int *) (cur_text_section->data + t);
+ {
+ Sym *sym;
+
+ // extract 32 bit address from MVKH/MVKL
+ n = ((*ptr >> 7) & 0xffff);
+ n |= ((*(ptr + 1) >> 7) & 0xffff) << 16;
+
+ // define a label that will be relocated
+
+ sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0);
+ greloc(cur_text_section, sym, t, R_C60LO16);
+ greloc(cur_text_section, sym, t + 4, R_C60HI16);
+
+ // clear out where the pointer was
+
+ *ptr &= ~(0xffff << 7);
+ *(ptr + 1) &= ~(0xffff << 7);
+ }
+ t = n;
+ }
+}
+
+void gsym(int t)
+{
+ gsym_addr(t, ind);
+}
+
+// these are regs that tcc doesn't really know about,
+// but assign them unique values so the mapping routines
+// can distinguish them
+
+#define C67_A0 105
+#define C67_SP 106
+#define C67_B3 107
+#define C67_FP 108
+#define C67_B2 109
+#define C67_CREG_ZERO -1 /* Special code for no condition reg test */
+
+
+int ConvertRegToRegClass(int r)
+{
+ // only works for A4-B13
+
+ return RC_C67_A4 << (r - TREG_C67_A4);
+}
+
+
+// map TCC reg to C67 reg number
+
+int C67_map_regn(int r)
+{
+ if (r == 0) // normal tcc regs
+ return 0x2; // A2
+ else if (r == 1) // normal tcc regs
+ return 3; // A3
+ else if (r == 2) // normal tcc regs
+ return 0; // B0
+ else if (r == 3) // normal tcc regs
+ return 1; // B1
+ else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs
+ return (((r & 0xfffffffc) >> 1) | (r & 1)) + 2;
+ else if (r == C67_A0)
+ return 0; // set to A0 (offset reg)
+ else if (r == C67_B2)
+ return 2; // set to B2 (offset reg)
+ else if (r == C67_B3)
+ return 3; // set to B3 (return address reg)
+ else if (r == C67_SP)
+ return 15; // set to SP (B15) (offset reg)
+ else if (r == C67_FP)
+ return 15; // set to FP (A15) (offset reg)
+ else if (r == C67_CREG_ZERO)
+ return 0; // Special code for no condition reg test
+ else
+ ALWAYS_ASSERT(FALSE);
+
+ return 0;
+}
+
+// mapping from tcc reg number to
+// C67 register to condition code field
+//
+// valid condition code regs are:
+//
+// tcc reg 2 ->B0 -> 1
+// tcc reg 3 ->B1 -> 2
+// tcc reg 0 -> A2 -> 5
+// tcc reg 1 -> A3 -> X
+// tcc reg B2 -> 3
+
+int C67_map_regc(int r)
+{
+ if (r == 0) // normal tcc regs
+ return 0x5;
+ else if (r == 2) // normal tcc regs
+ return 0x1;
+ else if (r == 3) // normal tcc regs
+ return 0x2;
+ else if (r == C67_B2) // normal tcc regs
+ return 0x3;
+ else if (r == C67_CREG_ZERO)
+ return 0; // Special code for no condition reg test
+ else
+ ALWAYS_ASSERT(FALSE);
+
+ return 0;
+}
+
+
+// map TCC reg to C67 reg side A or B
+
+int C67_map_regs(int r)
+{
+ if (r == 0) // normal tcc regs
+ return 0x0;
+ else if (r == 1) // normal tcc regs
+ return 0x0;
+ else if (r == 2) // normal tcc regs
+ return 0x1;
+ else if (r == 3) // normal tcc regs
+ return 0x1;
+ else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs
+ return (r & 2) >> 1;
+ else if (r == C67_A0)
+ return 0; // set to A side
+ else if (r == C67_B2)
+ return 1; // set to B side
+ else if (r == C67_B3)
+ return 1; // set to B side
+ else if (r == C67_SP)
+ return 0x1; // set to SP (B15) B side
+ else if (r == C67_FP)
+ return 0x0; // set to FP (A15) A side
+ else
+ ALWAYS_ASSERT(FALSE);
+
+ return 0;
+}
+
+int C67_map_S12(char *s)
+{
+ if (strstr(s, ".S1") != NULL)
+ return 0;
+ else if (strcmp(s, ".S2"))
+ return 1;
+ else
+ ALWAYS_ASSERT(FALSE);
+
+ return 0;
+}
+
+int C67_map_D12(char *s)
+{
+ if (strstr(s, ".D1") != NULL)
+ return 0;
+ else if (strcmp(s, ".D2"))
+ return 1;
+ else
+ ALWAYS_ASSERT(FALSE);
+
+ return 0;
+}
+
+
+
+void C67_asm(char *s, int a, int b, int c)
+{
+ BOOL xpath;
+
+#ifdef ASSEMBLY_LISTING_C67
+ if (!f) {
+ f = fopen("TCC67_out.txt", "wt");
+ }
+ fprintf(f, "%04X ", ind);
+#endif
+
+ if (strstr(s, "MVKL") == s) {
+ C67_g((C67_map_regn(b) << 23) |
+ ((a & 0xffff) << 7) | (0x0a << 2) | (C67_map_regs(b) << 1));
+ } else if (strstr(s, "MVKH") == s) {
+ C67_g((C67_map_regn(b) << 23) |
+ (((a >> 16) & 0xffff) << 7) |
+ (0x1a << 2) | (C67_map_regs(b) << 1));
+ } else if (strstr(s, "STW.D SP POST DEC") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (15 << 18) | //SP B15
+ (2 << 13) | //ucst5 (must keep 8 byte boundary !!)
+ (0xa << 9) | //mode a = post dec ucst
+ (0 << 8) | //r (LDDW bit 0)
+ (1 << 7) | //y D1/D2 use B side
+ (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STB.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STH.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STB.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STH.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STW.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STW.D *") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (C67_map_regn(b) << 18) | //base reg A0
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(b) << 7) | //y D1/D2 base reg side
+ (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STH.D *") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (C67_map_regn(b) << 18) | //base reg A0
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(b) << 7) | //y D1/D2 base reg side
+ (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STB.D *") == s) {
+ C67_g((C67_map_regn(a) << 23) | //src
+ (C67_map_regn(b) << 18) | //base reg A0
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(b) << 7) | //y D1/D2 base reg side
+ (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "STW.D +*") == s) {
+ ALWAYS_ASSERT(c < 32);
+ C67_g((C67_map_regn(a) << 23) | //src
+ (C67_map_regn(b) << 18) | //base reg A0
+ (c << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(b) << 7) | //y D1/D2 base reg side
+ (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of src
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDW.D SP PRE INC") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg B15
+ (2 << 13) | //ucst5 (must keep 8 byte boundary)
+ (9 << 9) | //mode 9 = pre inc ucst5
+ (0 << 8) | //r (LDDW bit 0)
+ (1 << 7) | //y D1/D2 B side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDDW.D SP PRE INC") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg B15
+ (1 << 13) | //ucst5 (must keep 8 byte boundary)
+ (9 << 9) | //mode 9 = pre inc ucst5
+ (1 << 8) | //r (LDDW bit 1)
+ (1 << 7) | //y D1/D2 B side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDW.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDDW.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (1 << 8) | //r (LDDW bit 1)
+ (0 << 7) | //y D1/D2 A side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDH.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDB.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDHU.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDBU.D *+SP[A0]") == s) {
+ C67_g((C67_map_regn(a) << 23) | //dst
+ (15 << 18) | //base reg A15
+ (0 << 13) | //offset reg A0
+ (5 << 9) | //mode 5 = pos offset, base reg + off reg
+ (0 << 8) | //r (LDDW bit 0)
+ (0 << 7) | //y D1/D2 A side
+ (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(a) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDW.D *") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDDW.D *") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (1 << 8) | //r (LDDW bit 1)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDH.D *") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDB.D *") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDHU.D *") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDBU.D *") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (0 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "LDW.D +*") == s) {
+ C67_g((C67_map_regn(b) << 23) | //dst
+ (C67_map_regn(a) << 18) | //base reg A15
+ (1 << 13) | //cst5
+ (1 << 9) | //mode 1 = pos cst offset
+ (0 << 8) | //r (LDDW bit 0)
+ (C67_map_regs(a) << 7) | //y D1/D2 src side
+ (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU
+ (1 << 2) | //opcode
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPLTSP") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x3a << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPGTSP") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x39 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPEQSP") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x38 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ }
+
+ else if (strstr(s, "CMPLTDP") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x2a << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPGTDP") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x29 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPEQDP") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x28 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPLT") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x57 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPGT") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x47 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPEQ") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x53 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPLTU") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x5f << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "CMPGTU") == s) {
+ xpath = C67_map_regs(a) ^ C67_map_regs(b);
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x use cross path for src2
+ (0x4f << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side for reg c
+ (0 << 0)); //parallel
+ } else if (strstr(s, "B DISP") == s) {
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //z
+ (a << 7) | //cnst
+ (0x4 << 2) | //opcode fixed
+ (0 << 1) | //S0/S1
+ (0 << 0)); //parallel
+ } else if (strstr(s, "B.") == s) {
+ xpath = C67_map_regs(c) ^ 1;
+
+ C67_g((C67_map_regc(b) << 29) | //creg
+ (a << 28) | //inv
+ (0 << 23) | //dst
+ (C67_map_regn(c) << 18) | //src2
+ (0 << 13) | //
+ (xpath << 12) | //x cross path if !B side
+ (0xd << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (1 << 1) | //must be S2
+ (0 << 0)); //parallel
+ } else if (strstr(s, "MV.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (0 << 13) | //src1 (cst5)
+ (xpath << 12) | //x cross path if opposite sides
+ (0x2 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SPTRUNC.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0xb << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "DPTRUNC.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0x1 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "INTSP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0x4a << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "INTSPU.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0x49 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "INTDP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0x39 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "INTDPU.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0x3b << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SPDP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (0 << 13) | //src1 NA
+ (xpath << 12) | //x cross path if opposite sides
+ (0x2 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "DPSP.L") == s) {
+ ALWAYS_ASSERT(C67_map_regs(b) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason
+ (0 << 13) | //src1 NA
+ (0 << 12) | //x cross path if opposite sides
+ (0x9 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "ADD.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x3 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SUB.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x7 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "OR.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x7f << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "AND.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x7b << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "XOR.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x6f << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "ADDSP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x10 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "ADDDP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x18 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SUBSP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x11 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SUBDP.L") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x19 << 5) | //opcode
+ (0x6 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "MPYSP.M") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x1c << 7) | //opcode
+ (0x0 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "MPYDP.M") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2 (possible x path)
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x0e << 7) | //opcode
+ (0x0 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "MPYI.M") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1 (cst5)
+ (xpath << 12) | //x cross path if opposite sides
+ (0x4 << 7) | //opcode
+ (0x0 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SHR.S") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x37 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SHRU.S") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x27 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "SHL.S") == s) {
+ xpath = C67_map_regs(b) ^ C67_map_regs(c);
+
+ ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a));
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(c) << 23) | //dst
+ (C67_map_regn(b) << 18) | //src2
+ (C67_map_regn(a) << 13) | //src1
+ (xpath << 12) | //x cross path if opposite sides
+ (0x33 << 6) | //opcode
+ (0x8 << 2) | //opcode fixed
+ (C67_map_regs(c) << 1) | //side of dest
+ (0 << 0)); //parallel
+ } else if (strstr(s, "||ADDK") == s) {
+ xpath = 0; // no xpath required just use the side of the src/dst
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(b) << 23) | //dst
+ (a << 07) | //scst16
+ (0x14 << 2) | //opcode fixed
+ (C67_map_regs(b) << 1) | //side of dst
+ (1 << 0)); //parallel
+ } else if (strstr(s, "ADDK") == s) {
+ xpath = 0; // no xpath required just use the side of the src/dst
+
+ C67_g((0 << 29) | //creg
+ (0 << 28) | //inv
+ (C67_map_regn(b) << 23) | //dst
+ (a << 07) | //scst16
+ (0x14 << 2) | //opcode fixed
+ (C67_map_regs(b) << 1) | //side of dst
+ (0 << 0)); //parallel
+ } else if (strstr(s, "NOP") == s) {
+ C67_g(((a - 1) << 13) | //no of cycles
+ (0 << 0)); //parallel
+ } else
+ ALWAYS_ASSERT(FALSE);
+
+#ifdef ASSEMBLY_LISTING_C67
+ fprintf(f, " %s %d %d %d\n", s, a, b, c);
+#endif
+
+}
+
+//r=reg to load, fr=from reg, symbol for relocation, constant
+
+void C67_MVKL(int r, int fc)
+{
+ C67_asm("MVKL.", fc, r, 0);
+}
+
+void C67_MVKH(int r, int fc)
+{
+ C67_asm("MVKH.", fc, r, 0);
+}
+
+void C67_STB_SP_A0(int r)
+{
+ C67_asm("STB.D *+SP[A0]", r, 0, 0); // STB r,*+SP[A0]
+}
+
+void C67_STH_SP_A0(int r)
+{
+ C67_asm("STH.D *+SP[A0]", r, 0, 0); // STH r,*+SP[A0]
+}
+
+void C67_STW_SP_A0(int r)
+{
+ C67_asm("STW.D *+SP[A0]", r, 0, 0); // STW r,*+SP[A0]
+}
+
+void C67_STB_PTR(int r, int r2)
+{
+ C67_asm("STB.D *", r, r2, 0); // STB r, *r2
+}
+
+void C67_STH_PTR(int r, int r2)
+{
+ C67_asm("STH.D *", r, r2, 0); // STH r, *r2
+}
+
+void C67_STW_PTR(int r, int r2)
+{
+ C67_asm("STW.D *", r, r2, 0); // STW r, *r2
+}
+
+void C67_STW_PTR_PRE_INC(int r, int r2, int n)
+{
+ C67_asm("STW.D +*", r, r2, n); // STW r, *+r2
+}
+
+void C67_PUSH(int r)
+{
+ C67_asm("STW.D SP POST DEC", r, 0, 0); // STW r,*SP--
+}
+
+void C67_LDW_SP_A0(int r)
+{
+ C67_asm("LDW.D *+SP[A0]", r, 0, 0); // LDW *+SP[A0],r
+}
+
+void C67_LDDW_SP_A0(int r)
+{
+ C67_asm("LDDW.D *+SP[A0]", r, 0, 0); // LDDW *+SP[A0],r
+}
+
+void C67_LDH_SP_A0(int r)
+{
+ C67_asm("LDH.D *+SP[A0]", r, 0, 0); // LDH *+SP[A0],r
+}
+
+void C67_LDB_SP_A0(int r)
+{
+ C67_asm("LDB.D *+SP[A0]", r, 0, 0); // LDB *+SP[A0],r
+}
+
+void C67_LDHU_SP_A0(int r)
+{
+ C67_asm("LDHU.D *+SP[A0]", r, 0, 0); // LDHU *+SP[A0],r
+}
+
+void C67_LDBU_SP_A0(int r)
+{
+ C67_asm("LDBU.D *+SP[A0]", r, 0, 0); // LDBU *+SP[A0],r
+}
+
+void C67_LDW_PTR(int r, int r2)
+{
+ C67_asm("LDW.D *", r, r2, 0); // LDW *r,r2
+}
+
+void C67_LDDW_PTR(int r, int r2)
+{
+ C67_asm("LDDW.D *", r, r2, 0); // LDDW *r,r2
+}
+
+void C67_LDH_PTR(int r, int r2)
+{
+ C67_asm("LDH.D *", r, r2, 0); // LDH *r,r2
+}
+
+void C67_LDB_PTR(int r, int r2)
+{
+ C67_asm("LDB.D *", r, r2, 0); // LDB *r,r2
+}
+
+void C67_LDHU_PTR(int r, int r2)
+{
+ C67_asm("LDHU.D *", r, r2, 0); // LDHU *r,r2
+}
+
+void C67_LDBU_PTR(int r, int r2)
+{
+ C67_asm("LDBU.D *", r, r2, 0); // LDBU *r,r2
+}
+
+void C67_LDW_PTR_PRE_INC(int r, int r2)
+{
+ C67_asm("LDW.D +*", r, r2, 0); // LDW *+r,r2
+}
+
+void C67_POP(int r)
+{
+ C67_asm("LDW.D SP PRE INC", r, 0, 0); // LDW *++SP,r
+}
+
+void C67_POP_DW(int r)
+{
+ C67_asm("LDDW.D SP PRE INC", r, 0, 0); // LDDW *++SP,r
+}
+
+void C67_CMPLT(int s1, int s2, int dst)
+{
+ C67_asm("CMPLT.L1", s1, s2, dst);
+}
+
+void C67_CMPGT(int s1, int s2, int dst)
+{
+ C67_asm("CMPGT.L1", s1, s2, dst);
+}
+
+void C67_CMPEQ(int s1, int s2, int dst)
+{
+ C67_asm("CMPEQ.L1", s1, s2, dst);
+}
+
+void C67_CMPLTU(int s1, int s2, int dst)
+{
+ C67_asm("CMPLTU.L1", s1, s2, dst);
+}
+
+void C67_CMPGTU(int s1, int s2, int dst)
+{
+ C67_asm("CMPGTU.L1", s1, s2, dst);
+}
+
+
+void C67_CMPLTSP(int s1, int s2, int dst)
+{
+ C67_asm("CMPLTSP.S1", s1, s2, dst);
+}
+
+void C67_CMPGTSP(int s1, int s2, int dst)
+{
+ C67_asm("CMPGTSP.S1", s1, s2, dst);
+}
+
+void C67_CMPEQSP(int s1, int s2, int dst)
+{
+ C67_asm("CMPEQSP.S1", s1, s2, dst);
+}
+
+void C67_CMPLTDP(int s1, int s2, int dst)
+{
+ C67_asm("CMPLTDP.S1", s1, s2, dst);
+}
+
+void C67_CMPGTDP(int s1, int s2, int dst)
+{
+ C67_asm("CMPGTDP.S1", s1, s2, dst);
+}
+
+void C67_CMPEQDP(int s1, int s2, int dst)
+{
+ C67_asm("CMPEQDP.S1", s1, s2, dst);
+}
+
+
+void C67_IREG_B_REG(int inv, int r1, int r2) // [!R] B r2
+{
+ C67_asm("B.S2", inv, r1, r2);
+}
+
+
+// call with how many 32 bit words to skip
+// (0 would branch to the branch instruction)
+
+void C67_B_DISP(int disp) // B +2 Branch with constant displacement
+{
+ // Branch point is relative to the 8 word fetch packet
+ //
+ // we will assume the text section always starts on an 8 word (32 byte boundary)
+ //
+ // so add in how many words into the fetch packet the branch is
+
+
+ C67_asm("B DISP", disp + ((ind & 31) >> 2), 0, 0);
+}
+
+void C67_NOP(int n)
+{
+ C67_asm("NOP", n, 0, 0);
+}
+
+void C67_ADDK(int n, int r)
+{
+ ALWAYS_ASSERT(abs(n) < 32767);
+
+ C67_asm("ADDK", n, r, 0);
+}
+
+void C67_ADDK_PARALLEL(int n, int r)
+{
+ ALWAYS_ASSERT(abs(n) < 32767);
+
+ C67_asm("||ADDK", n, r, 0);
+}
+
+void C67_Adjust_ADDK(int *inst, int n)
+{
+ ALWAYS_ASSERT(abs(n) < 32767);
+
+ *inst = (*inst & (~(0xffff << 7))) | ((n & 0xffff) << 7);
+}
+
+void C67_MV(int r, int v)
+{
+ C67_asm("MV.L", 0, r, v);
+}
+
+
+void C67_DPTRUNC(int r, int v)
+{
+ C67_asm("DPTRUNC.L", 0, r, v);
+}
+
+void C67_SPTRUNC(int r, int v)
+{
+ C67_asm("SPTRUNC.L", 0, r, v);
+}
+
+void C67_INTSP(int r, int v)
+{
+ C67_asm("INTSP.L", 0, r, v);
+}
+
+void C67_INTDP(int r, int v)
+{
+ C67_asm("INTDP.L", 0, r, v);
+}
+
+void C67_INTSPU(int r, int v)
+{
+ C67_asm("INTSPU.L", 0, r, v);
+}
+
+void C67_INTDPU(int r, int v)
+{
+ C67_asm("INTDPU.L", 0, r, v);
+}
+
+void C67_SPDP(int r, int v)
+{
+ C67_asm("SPDP.L", 0, r, v);
+}
+
+void C67_DPSP(int r, int v) // note regs must be on the same side
+{
+ C67_asm("DPSP.L", 0, r, v);
+}
+
+void C67_ADD(int r, int v)
+{
+ C67_asm("ADD.L", v, r, v);
+}
+
+void C67_SUB(int r, int v)
+{
+ C67_asm("SUB.L", v, r, v);
+}
+
+void C67_AND(int r, int v)
+{
+ C67_asm("AND.L", v, r, v);
+}
+
+void C67_OR(int r, int v)
+{
+ C67_asm("OR.L", v, r, v);
+}
+
+void C67_XOR(int r, int v)
+{
+ C67_asm("XOR.L", v, r, v);
+}
+
+void C67_ADDSP(int r, int v)
+{
+ C67_asm("ADDSP.L", v, r, v);
+}
+
+void C67_SUBSP(int r, int v)
+{
+ C67_asm("SUBSP.L", v, r, v);
+}
+
+void C67_MPYSP(int r, int v)
+{
+ C67_asm("MPYSP.M", v, r, v);
+}
+
+void C67_ADDDP(int r, int v)
+{
+ C67_asm("ADDDP.L", v, r, v);
+}
+
+void C67_SUBDP(int r, int v)
+{
+ C67_asm("SUBDP.L", v, r, v);
+}
+
+void C67_MPYDP(int r, int v)
+{
+ C67_asm("MPYDP.M", v, r, v);
+}
+
+void C67_MPYI(int r, int v)
+{
+ C67_asm("MPYI.M", v, r, v);
+}
+
+void C67_SHL(int r, int v)
+{
+ C67_asm("SHL.S", r, v, v);
+}
+
+void C67_SHRU(int r, int v)
+{
+ C67_asm("SHRU.S", r, v, v);
+}
+
+void C67_SHR(int r, int v)
+{
+ C67_asm("SHR.S", r, v, v);
+}
+
+
+
+/* load 'r' from value 'sv' */
+void load(int r, SValue * sv)
+{
+ int v, t, ft, fc, fr, size = 0, element;
+ BOOL Unsigned = FALSE;
+ SValue v1;
+
+ fr = sv->r;
+ ft = sv->type.t;
+ fc = sv->c.ul;
+
+ v = fr & VT_VALMASK;
+ if (fr & VT_LVAL) {
+ if (v == VT_LLOCAL) {
+ v1.type.t = VT_INT;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = fc;
+ load(r, &v1);
+ fr = r;
+ } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ tcc_error("long double not supported");
+ } else if ((ft & VT_TYPE) == VT_BYTE) {
+ size = 1;
+ } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
+ size = 1;
+ Unsigned = TRUE;
+ } else if ((ft & VT_TYPE) == VT_SHORT) {
+ size = 2;
+ } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
+ size = 2;
+ Unsigned = TRUE;
+ } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ size = 8;
+ } else {
+ size = 4;
+ }
+
+ // check if fc is a positive reference on the stack,
+ // if it is tcc is referencing what it thinks is a parameter
+ // on the stack, so check if it is really in a register.
+
+
+ if (v == VT_LOCAL && fc > 0) {
+ int stack_pos = 8;
+
+ for (t = 0; t < NoCallArgsPassedOnStack; t++) {
+ if (fc == stack_pos)
+ break;
+
+ stack_pos += TranslateStackToReg[t];
+ }
+
+ // param has been pushed on stack, get it like a local var
+
+ fc = ParamLocOnStack[t] - 8;
+ }
+
+ if ((fr & VT_VALMASK) < VT_CONST) // check for pure indirect
+ {
+ if (size == 1) {
+ if (Unsigned)
+ C67_LDBU_PTR(v, r); // LDBU *v,r
+ else
+ C67_LDB_PTR(v, r); // LDB *v,r
+ } else if (size == 2) {
+ if (Unsigned)
+ C67_LDHU_PTR(v, r); // LDHU *v,r
+ else
+ C67_LDH_PTR(v, r); // LDH *v,r
+ } else if (size == 4) {
+ C67_LDW_PTR(v, r); // LDW *v,r
+ } else if (size == 8) {
+ C67_LDDW_PTR(v, r); // LDDW *v,r
+ }
+
+ C67_NOP(4); // NOP 4
+ return;
+ } else if (fr & VT_SYM) {
+ greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched
+ greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16);
+
+
+ C67_MVKL(C67_A0, fc); //r=reg to load, constant
+ C67_MVKH(C67_A0, fc); //r=reg to load, constant
+
+
+ if (size == 1) {
+ if (Unsigned)
+ C67_LDBU_PTR(C67_A0, r); // LDBU *A0,r
+ else
+ C67_LDB_PTR(C67_A0, r); // LDB *A0,r
+ } else if (size == 2) {
+ if (Unsigned)
+ C67_LDHU_PTR(C67_A0, r); // LDHU *A0,r
+ else
+ C67_LDH_PTR(C67_A0, r); // LDH *A0,r
+ } else if (size == 4) {
+ C67_LDW_PTR(C67_A0, r); // LDW *A0,r
+ } else if (size == 8) {
+ C67_LDDW_PTR(C67_A0, r); // LDDW *A0,r
+ }
+
+ C67_NOP(4); // NOP 4
+ return;
+ } else {
+ element = size;
+
+ // divide offset in bytes to create element index
+ C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant
+ C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant
+
+ if (size == 1) {
+ if (Unsigned)
+ C67_LDBU_SP_A0(r); // LDBU r, SP[A0]
+ else
+ C67_LDB_SP_A0(r); // LDB r, SP[A0]
+ } else if (size == 2) {
+ if (Unsigned)
+ C67_LDHU_SP_A0(r); // LDHU r, SP[A0]
+ else
+ C67_LDH_SP_A0(r); // LDH r, SP[A0]
+ } else if (size == 4) {
+ C67_LDW_SP_A0(r); // LDW r, SP[A0]
+ } else if (size == 8) {
+ C67_LDDW_SP_A0(r); // LDDW r, SP[A0]
+ }
+
+
+ C67_NOP(4); // NOP 4
+ return;
+ }
+ } else {
+ if (v == VT_CONST) {
+ if (fr & VT_SYM) {
+ greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched
+ greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16);
+ }
+ C67_MVKL(r, fc); //r=reg to load, constant
+ C67_MVKH(r, fc); //r=reg to load, constant
+ } else if (v == VT_LOCAL) {
+ C67_MVKL(r, fc + 8); //r=reg to load, constant C67 stack points to next free
+ C67_MVKH(r, fc + 8); //r=reg to load, constant
+ C67_ADD(C67_FP, r); // MV v,r v -> r
+ } else if (v == VT_CMP) {
+ C67_MV(C67_compare_reg, r); // MV v,r v -> r
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ t = v & 1;
+ C67_B_DISP(4); // Branch with constant displacement, skip over this branch, load, nop, load
+ C67_MVKL(r, t); // r=reg to load, 0 or 1 (do this while branching)
+ C67_NOP(4); // NOP 4
+ gsym(fc); // modifies other branches to branch here
+ C67_MVKL(r, t ^ 1); // r=reg to load, 0 or 1
+ } else if (v != r) {
+ C67_MV(v, r); // MV v,r v -> r
+
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_MV(v + 1, r + 1); // MV v,r v -> r
+ }
+ }
+}
+
+
+/* store register 'r' in lvalue 'v' */
+void store(int r, SValue * v)
+{
+ int fr, bt, ft, fc, size, t, element;
+
+ ft = v->type.t;
+ fc = v->c.ul;
+ fr = v->r & VT_VALMASK;
+ bt = ft & VT_BTYPE;
+ /* XXX: incorrect if float reg to reg */
+
+ if (bt == VT_LDOUBLE) {
+ tcc_error("long double not supported");
+ } else {
+ if (bt == VT_SHORT)
+ size = 2;
+ else if (bt == VT_BYTE)
+ size = 1;
+ else if (bt == VT_DOUBLE)
+ size = 8;
+ else
+ size = 4;
+
+ if ((v->r & VT_VALMASK) == VT_CONST) {
+ /* constant memory reference */
+
+ if (v->r & VT_SYM) {
+ greloc(cur_text_section, v->sym, ind, R_C60LO16); // rem the inst need to be patched
+ greloc(cur_text_section, v->sym, ind + 4, R_C60HI16);
+ }
+ C67_MVKL(C67_A0, fc); //r=reg to load, constant
+ C67_MVKH(C67_A0, fc); //r=reg to load, constant
+
+ if (size == 1)
+ C67_STB_PTR(r, C67_A0); // STB r, *A0
+ else if (size == 2)
+ C67_STH_PTR(r, C67_A0); // STH r, *A0
+ else if (size == 4 || size == 8)
+ C67_STW_PTR(r, C67_A0); // STW r, *A0
+
+ if (size == 8)
+ C67_STW_PTR_PRE_INC(r + 1, C67_A0, 1); // STW r, *+A0[1]
+ } else if ((v->r & VT_VALMASK) == VT_LOCAL) {
+ // check case of storing to passed argument that
+ // tcc thinks is on the stack but for C67 is
+ // passed as a reg. However it may have been
+ // saved to the stack, if that reg was required
+ // for a call to a child function
+
+ if (fc > 0) // argument ??
+ {
+ // walk through sizes and figure which param
+
+ int stack_pos = 8;
+
+ for (t = 0; t < NoCallArgsPassedOnStack; t++) {
+ if (fc == stack_pos)
+ break;
+
+ stack_pos += TranslateStackToReg[t];
+ }
+
+ // param has been pushed on stack, get it like a local var
+ fc = ParamLocOnStack[t] - 8;
+ }
+
+ if (size == 8)
+ element = 4;
+ else
+ element = size;
+
+ // divide offset in bytes to create word index
+ C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant
+ C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant
+
+
+
+ if (size == 1)
+ C67_STB_SP_A0(r); // STB r, SP[A0]
+ else if (size == 2)
+ C67_STH_SP_A0(r); // STH r, SP[A0]
+ else if (size == 4 || size == 8)
+ C67_STW_SP_A0(r); // STW r, SP[A0]
+
+ if (size == 8) {
+ C67_ADDK(1, C67_A0); // ADDK 1,A0
+ C67_STW_SP_A0(r + 1); // STW r, SP[A0]
+ }
+ } else {
+ if (size == 1)
+ C67_STB_PTR(r, fr); // STB r, *fr
+ else if (size == 2)
+ C67_STH_PTR(r, fr); // STH r, *fr
+ else if (size == 4 || size == 8)
+ C67_STW_PTR(r, fr); // STW r, *fr
+
+ if (size == 8) {
+ C67_STW_PTR_PRE_INC(r + 1, fr, 1); // STW r, *+fr[1]
+ }
+ }
+ }
+}
+
+/* 'is_jmp' is '1' if it is a jump */
+static void gcall_or_jmp(int is_jmp)
+{
+ int r;
+ Sym *sym;
+
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ /* constant case */
+ if (vtop->r & VT_SYM) {
+ /* relocation case */
+
+ // get add into A0, then start the jump B3
+
+ greloc(cur_text_section, vtop->sym, ind, R_C60LO16); // rem the inst need to be patched
+ greloc(cur_text_section, vtop->sym, ind + 4, R_C60HI16);
+
+ C67_MVKL(C67_A0, 0); //r=reg to load, constant
+ C67_MVKH(C67_A0, 0); //r=reg to load, constant
+ C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // B.S2x A0
+
+ if (is_jmp) {
+ C67_NOP(5); // simple jump, just put NOP
+ } else {
+ // Call, must load return address into B3 during delay slots
+
+ sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address
+ greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched
+ greloc(cur_text_section, sym, ind + 4, R_C60HI16);
+ C67_MVKL(C67_B3, 0); //r=reg to load, constant
+ C67_MVKH(C67_B3, 0); //r=reg to load, constant
+ C67_NOP(3); // put remaining NOPs
+ }
+ } else {
+ /* put an empty PC32 relocation */
+ ALWAYS_ASSERT(FALSE);
+ }
+ } else {
+ /* otherwise, indirect call */
+ r = gv(RC_INT);
+ C67_IREG_B_REG(0, C67_CREG_ZERO, r); // B.S2x r
+
+ if (is_jmp) {
+ C67_NOP(5); // simple jump, just put NOP
+ } else {
+ // Call, must load return address into B3 during delay slots
+
+ sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address
+ greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched
+ greloc(cur_text_section, sym, ind + 4, R_C60HI16);
+ C67_MVKL(C67_B3, 0); //r=reg to load, constant
+ C67_MVKH(C67_B3, 0); //r=reg to load, constant
+ C67_NOP(3); // put remaining NOPs
+ }
+ }
+}
+
+ST_FUNC int regargs_nregs(RegArgs *args)
+{
+ return *args;
+}
+
+/* Return the number of registers needed to return the struct, or 0 if
+ returning via struct pointer. */
+ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args) {
+ *ret_align = 1; // Never have to re-align return values for x86-64
+ *args = 0;
+
+ return 0;
+}
+
+/* generate function call with address in (vtop->t, vtop->c) and free function
+ context. Stack entry is popped */
+void gfunc_call(int nb_args)
+{
+ int i, r, size = 0;
+ int args_sizes[NoCallArgsPassedOnStack];
+
+ if (nb_args > NoCallArgsPassedOnStack) {
+ tcc_error("more than 10 function params not currently supported");
+ // handle more than 10, put some on the stack
+ }
+
+ for (i = 0; i < nb_args; i++) {
+ if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+ ALWAYS_ASSERT(FALSE);
+ } else {
+ /* simple type (currently always same size) */
+ /* XXX: implicit cast ? */
+
+
+ if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ tcc_error("long long not supported");
+ } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+ tcc_error("long double not supported");
+ } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) {
+ size = 8;
+ } else {
+ size = 4;
+ }
+
+ // put the parameter into the corresponding reg (pair)
+
+ r = gv(RC_C67_A4 << (2 * i));
+
+ // must put on stack because with 1 pass compiler , no way to tell
+ // if an up coming nested call might overwrite these regs
+
+ C67_PUSH(r);
+
+ if (size == 8) {
+ C67_STW_PTR_PRE_INC(r + 1, C67_SP, 3); // STW r, *+SP[3] (go back and put the other)
+ }
+ args_sizes[i] = size;
+ }
+ vtop--;
+ }
+ // POP all the params on the stack into registers for the
+ // immediate call (in reverse order)
+
+ for (i = nb_args - 1; i >= 0; i--) {
+
+ if (args_sizes[i] == 8)
+ C67_POP_DW(TREG_C67_A4 + i * 2);
+ else
+ C67_POP(TREG_C67_A4 + i * 2);
+ }
+ gcall_or_jmp(0);
+ vtop--;
+}
+
+
+// to be compatible with Code Composer for the C67
+// the first 10 parameters must be passed in registers
+// (pairs for 64 bits) starting wit; A4:A5, then B4:B5 and
+// ending with B12:B13.
+//
+// When a call is made, if the caller has its parameters
+// in regs A4-B13 these must be saved before/as the call
+// parameters are loaded and restored upon return (or if/when needed).
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(CType * func_type)
+{
+ int addr, align, size, func_call, i;
+ Sym *sym;
+ CType *type;
+
+ sym = func_type->ref;
+ func_call = sym->r;
+ addr = 8;
+ /* if the function returns a structure, then add an
+ implicit pointer parameter */
+ func_vt = sym->type;
+ func_var = (sym->c == FUNC_ELLIPSIS);
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+ func_vc = addr;
+ addr += 4;
+ }
+
+ NoOfCurFuncArgs = 0;
+
+ /* define parameters */
+ while ((sym = sym->next) != NULL) {
+ type = &sym->type;
+ sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr);
+ size = type_size(type, &align);
+ size = (size + 3) & ~3;
+
+ // keep track of size of arguments so
+ // we can translate where tcc thinks they
+ // are on the stack into the appropriate reg
+
+ TranslateStackToReg[NoOfCurFuncArgs] = size;
+ NoOfCurFuncArgs++;
+
+#ifdef FUNC_STRUCT_PARAM_AS_PTR
+ /* structs are passed as pointer */
+ if ((type->t & VT_BTYPE) == VT_STRUCT) {
+ size = 4;
+ }
+#endif
+ addr += size;
+ }
+ func_ret_sub = 0;
+ /* pascal type call ? */
+ if (func_call == FUNC_STDCALL)
+ func_ret_sub = addr - 8;
+
+ C67_MV(C67_FP, C67_A0); // move FP -> A0
+ C67_MV(C67_SP, C67_FP); // move SP -> FP
+
+ // place all the args passed in regs onto the stack
+
+ loc = 0;
+ for (i = 0; i < NoOfCurFuncArgs; i++) {
+
+ ParamLocOnStack[i] = loc; // remember where the param is
+ loc += -8;
+
+ C67_PUSH(TREG_C67_A4 + i * 2);
+
+ if (TranslateStackToReg[i] == 8) {
+ C67_STW_PTR_PRE_INC(TREG_C67_A4 + i * 2 + 1, C67_SP, 3); // STW r, *+SP[1] (go back and put the other)
+ }
+ }
+
+ TotalBytesPushedOnStack = -loc;
+
+ func_sub_sp_offset = ind; // remember where we put the stack instruction
+ C67_ADDK(0, C67_SP); // ADDK.L2 loc,SP (just put zero temporarily)
+
+ C67_PUSH(C67_A0);
+ C67_PUSH(C67_B3);
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+ {
+ int local = (-loc + 7) & -8; // stack must stay aligned to 8 bytes for LDDW instr
+ C67_POP(C67_B3);
+ C67_NOP(4); // NOP wait for load
+ C67_IREG_B_REG(0, C67_CREG_ZERO, C67_B3); // B.S2 B3
+ C67_POP(C67_FP);
+ C67_ADDK(local, C67_SP); // ADDK.L2 loc,SP
+ C67_Adjust_ADDK((int *) (cur_text_section->data +
+ func_sub_sp_offset),
+ -local + TotalBytesPushedOnStack);
+ C67_NOP(3); // NOP
+ }
+}
+
+/* generate a jump to a label */
+int gjmp(int t)
+{
+ int ind1 = ind;
+
+ C67_MVKL(C67_A0, t); //r=reg to load, constant
+ C67_MVKH(C67_A0, t); //r=reg to load, constant
+ C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // [!R] B.S2x A0
+ C67_NOP(5);
+ return ind1;
+}
+
+/* generate a jump to a fixed address */
+void gjmp_addr(int a)
+{
+ Sym *sym;
+ // I guess this routine is used for relative short
+ // local jumps, for now just handle it as the general
+ // case
+
+ // define a label that will be relocated
+
+ sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0);
+ greloc(cur_text_section, sym, ind, R_C60LO16);
+ greloc(cur_text_section, sym, ind + 4, R_C60HI16);
+
+ gjmp(0); // place a zero there later the symbol will be added to it
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+int gtst(int inv, int t)
+{
+ int ind1, n;
+ int v, *p;
+
+ v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP) {
+ /* fast case : can jump directly since flags are set */
+ // C67 uses B2 sort of as flags register
+ ind1 = ind;
+ C67_MVKL(C67_A0, t); //r=reg to load, constant
+ C67_MVKH(C67_A0, t); //r=reg to load, constant
+
+ if (C67_compare_reg != TREG_EAX && // check if not already in a conditional test reg
+ C67_compare_reg != TREG_EDX &&
+ C67_compare_reg != TREG_ST0 && C67_compare_reg != C67_B2) {
+ C67_MV(C67_compare_reg, C67_B2);
+ C67_compare_reg = C67_B2;
+ }
+
+ C67_IREG_B_REG(C67_invert_test ^ inv, C67_compare_reg, C67_A0); // [!R] B.S2x A0
+ C67_NOP(5);
+ t = ind1; //return where we need to patch
+
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ /* && or || optimization */
+ if ((v & 1) == inv) {
+ /* insert vtop->c jump list in t */
+ p = &vtop->c.i;
+
+ // I guess the idea is to traverse to the
+ // null at the end of the list and store t
+ // there
+
+ n = *p;
+ while (n != 0) {
+ p = (int *) (cur_text_section->data + n);
+
+ // extract 32 bit address from MVKH/MVKL
+ n = ((*p >> 7) & 0xffff);
+ n |= ((*(p + 1) >> 7) & 0xffff) << 16;
+ }
+ *p |= (t & 0xffff) << 7;
+ *(p + 1) |= ((t >> 16) & 0xffff) << 7;
+ t = vtop->c.i;
+
+ } else {
+ t = gjmp(t);
+ gsym(vtop->c.i);
+ }
+ }
+ vtop--;
+ return t;
+}
+
+/* generate an integer binary operation */
+void gen_opi(int op)
+{
+ int r, fr, opc, t;
+
+ switch (op) {
+ case '+':
+ case TOK_ADDC1: /* add with carry generation */
+ opc = 0;
+ gen_op8:
+
+
+// C67 can't do const compares, must load into a reg
+// so just go to gv2 directly - tktk
+
+
+
+ if (op >= TOK_ULT && op <= TOK_GT)
+ gv2(RC_INT_BSIDE, RC_INT); // make sure r (src1) is on the B Side of CPU
+ else
+ gv2(RC_INT, RC_INT);
+
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+
+ C67_compare_reg = C67_B2;
+
+
+ if (op == TOK_LT) {
+ C67_CMPLT(r, fr, C67_B2);
+ C67_invert_test = FALSE;
+ } else if (op == TOK_GE) {
+ C67_CMPLT(r, fr, C67_B2);
+ C67_invert_test = TRUE;
+ } else if (op == TOK_GT) {
+ C67_CMPGT(r, fr, C67_B2);
+ C67_invert_test = FALSE;
+ } else if (op == TOK_LE) {
+ C67_CMPGT(r, fr, C67_B2);
+ C67_invert_test = TRUE;
+ } else if (op == TOK_EQ) {
+ C67_CMPEQ(r, fr, C67_B2);
+ C67_invert_test = FALSE;
+ } else if (op == TOK_NE) {
+ C67_CMPEQ(r, fr, C67_B2);
+ C67_invert_test = TRUE;
+ } else if (op == TOK_ULT) {
+ C67_CMPLTU(r, fr, C67_B2);
+ C67_invert_test = FALSE;
+ } else if (op == TOK_UGE) {
+ C67_CMPLTU(r, fr, C67_B2);
+ C67_invert_test = TRUE;
+ } else if (op == TOK_UGT) {
+ C67_CMPGTU(r, fr, C67_B2);
+ C67_invert_test = FALSE;
+ } else if (op == TOK_ULE) {
+ C67_CMPGTU(r, fr, C67_B2);
+ C67_invert_test = TRUE;
+ } else if (op == '+')
+ C67_ADD(fr, r); // ADD r,fr,r
+ else if (op == '-')
+ C67_SUB(fr, r); // SUB r,fr,r
+ else if (op == '&')
+ C67_AND(fr, r); // AND r,fr,r
+ else if (op == '|')
+ C67_OR(fr, r); // OR r,fr,r
+ else if (op == '^')
+ C67_XOR(fr, r); // XOR r,fr,r
+ else
+ ALWAYS_ASSERT(FALSE);
+
+ vtop--;
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ }
+ break;
+ case '-':
+ case TOK_SUBC1: /* sub with carry generation */
+ opc = 5;
+ goto gen_op8;
+ case TOK_ADDC2: /* add with carry use */
+ opc = 2;
+ goto gen_op8;
+ case TOK_SUBC2: /* sub with carry use */
+ opc = 3;
+ goto gen_op8;
+ case '&':
+ opc = 4;
+ goto gen_op8;
+ case '^':
+ opc = 6;
+ goto gen_op8;
+ case '|':
+ opc = 1;
+ goto gen_op8;
+ case '*':
+ case TOK_UMULL:
+ gv2(RC_INT, RC_INT);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ C67_MPYI(fr, r); // 32 bit bultiply fr,r,fr
+ C67_NOP(8); // NOP 8 for worst case
+ break;
+ case TOK_SHL:
+ gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ C67_SHL(fr, r); // arithmetic/logical shift
+ break;
+
+ case TOK_SHR:
+ gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ C67_SHRU(fr, r); // logical shift
+ break;
+
+ case TOK_SAR:
+ gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ C67_SHR(fr, r); // arithmetic shift
+ break;
+
+ case '/':
+ t = TOK__divi;
+ call_func:
+ vswap();
+ /* call generic idiv function */
+ vpush_global_sym(&func_old_type, t);
+ vrott(3);
+ gfunc_call(2);
+ vpushi(0);
+ vtop->r = REG_IRET;
+ vtop->r2 = VT_CONST;
+ break;
+ case TOK_UDIV:
+ case TOK_PDIV:
+ t = TOK__divu;
+ goto call_func;
+ case '%':
+ t = TOK__remi;
+ goto call_func;
+ case TOK_UMOD:
+ t = TOK__remu;
+ goto call_func;
+
+ default:
+ opc = 7;
+ goto gen_op8;
+ }
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ two operands are guaranted to have the same floating point type */
+/* XXX: need to use ST1 too */
+void gen_opf(int op)
+{
+ int ft, fc, fr, r;
+
+ if (op >= TOK_ULT && op <= TOK_GT)
+ gv2(RC_EDX, RC_EAX); // make sure src2 is on b side
+ else
+ gv2(RC_FLOAT, RC_FLOAT); // make sure src2 is on b side
+
+ ft = vtop->type.t;
+ fc = vtop->c.ul;
+ r = vtop->r;
+ fr = vtop[-1].r;
+
+
+ if ((ft & VT_BTYPE) == VT_LDOUBLE)
+ tcc_error("long doubles not supported");
+
+ if (op >= TOK_ULT && op <= TOK_GT) {
+
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+
+ C67_compare_reg = C67_B2;
+
+ if (op == TOK_LT) {
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_CMPLTDP(r, fr, C67_B2);
+ else
+ C67_CMPLTSP(r, fr, C67_B2);
+
+ C67_invert_test = FALSE;
+ } else if (op == TOK_GE) {
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_CMPLTDP(r, fr, C67_B2);
+ else
+ C67_CMPLTSP(r, fr, C67_B2);
+
+ C67_invert_test = TRUE;
+ } else if (op == TOK_GT) {
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_CMPGTDP(r, fr, C67_B2);
+ else
+ C67_CMPGTSP(r, fr, C67_B2);
+
+ C67_invert_test = FALSE;
+ } else if (op == TOK_LE) {
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_CMPGTDP(r, fr, C67_B2);
+ else
+ C67_CMPGTSP(r, fr, C67_B2);
+
+ C67_invert_test = TRUE;
+ } else if (op == TOK_EQ) {
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_CMPEQDP(r, fr, C67_B2);
+ else
+ C67_CMPEQSP(r, fr, C67_B2);
+
+ C67_invert_test = FALSE;
+ } else if (op == TOK_NE) {
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ C67_CMPEQDP(r, fr, C67_B2);
+ else
+ C67_CMPEQSP(r, fr, C67_B2);
+
+ C67_invert_test = TRUE;
+ } else {
+ ALWAYS_ASSERT(FALSE);
+ }
+ vtop->r = VT_CMP; // tell TCC that result is in "flags" actually B2
+ } else {
+ if (op == '+') {
+ if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ C67_ADDDP(r, fr); // ADD fr,r,fr
+ C67_NOP(6);
+ } else {
+ C67_ADDSP(r, fr); // ADD fr,r,fr
+ C67_NOP(3);
+ }
+ vtop--;
+ } else if (op == '-') {
+ if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ C67_SUBDP(r, fr); // SUB fr,r,fr
+ C67_NOP(6);
+ } else {
+ C67_SUBSP(r, fr); // SUB fr,r,fr
+ C67_NOP(3);
+ }
+ vtop--;
+ } else if (op == '*') {
+ if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ C67_MPYDP(r, fr); // MPY fr,r,fr
+ C67_NOP(9);
+ } else {
+ C67_MPYSP(r, fr); // MPY fr,r,fr
+ C67_NOP(3);
+ }
+ vtop--;
+ } else if (op == '/') {
+ if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ // must call intrinsic DP floating point divide
+ vswap();
+ /* call generic idiv function */
+ vpush_global_sym(&func_old_type, TOK__divd);
+ vrott(3);
+ gfunc_call(2);
+ vpushi(0);
+ vtop->r = REG_FRET;
+ vtop->r2 = REG_LRET;
+
+ } else {
+ // must call intrinsic SP floating point divide
+ vswap();
+ /* call generic idiv function */
+ vpush_global_sym(&func_old_type, TOK__divf);
+ vrott(3);
+ gfunc_call(2);
+ vpushi(0);
+ vtop->r = REG_FRET;
+ vtop->r2 = VT_CONST;
+ }
+ } else
+ ALWAYS_ASSERT(FALSE);
+
+
+ }
+}
+
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+ and 'long long' cases. */
+void gen_cvt_itof(int t)
+{
+ int r;
+
+ gv(RC_INT);
+ r = vtop->r;
+
+ if ((t & VT_BTYPE) == VT_DOUBLE) {
+ if (t & VT_UNSIGNED)
+ C67_INTDPU(r, r);
+ else
+ C67_INTDP(r, r);
+
+ C67_NOP(4);
+ vtop->type.t = VT_DOUBLE;
+ } else {
+ if (t & VT_UNSIGNED)
+ C67_INTSPU(r, r);
+ else
+ C67_INTSP(r, r);
+ C67_NOP(3);
+ vtop->type.t = VT_FLOAT;
+ }
+
+}
+
+/* convert fp to int 't' type */
+/* XXX: handle long long case */
+void gen_cvt_ftoi(int t)
+{
+ int r;
+
+ gv(RC_FLOAT);
+ r = vtop->r;
+
+ if (t != VT_INT)
+ tcc_error("long long not supported");
+ else {
+ if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) {
+ C67_DPTRUNC(r, r);
+ C67_NOP(3);
+ } else {
+ C67_SPTRUNC(r, r);
+ C67_NOP(3);
+ }
+
+ vtop->type.t = VT_INT;
+
+ }
+}
+
+/* convert from one floating point type to another */
+void gen_cvt_ftof(int t)
+{
+ int r, r2;
+
+ if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE &&
+ (t & VT_BTYPE) == VT_FLOAT) {
+ // convert double to float
+
+ gv(RC_FLOAT); // get it in a register pair
+
+ r = vtop->r;
+
+ C67_DPSP(r, r); // convert it to SP same register
+ C67_NOP(3);
+
+ vtop->type.t = VT_FLOAT;
+ vtop->r2 = VT_CONST; // set this as unused
+ } else if ((vtop->type.t & VT_BTYPE) == VT_FLOAT &&
+ (t & VT_BTYPE) == VT_DOUBLE) {
+ // convert float to double
+
+ gv(RC_FLOAT); // get it in a register
+
+ r = vtop->r;
+
+ if (r == TREG_EAX) { // make sure the paired reg is avail
+ r2 = get_reg(RC_ECX);
+ } else if (r == TREG_EDX) {
+ r2 = get_reg(RC_ST0);
+ } else {
+ ALWAYS_ASSERT(FALSE);
+ r2 = 0; /* avoid warning */
+ }
+
+ C67_SPDP(r, r); // convert it to DP same register
+ C67_NOP(1);
+
+ vtop->type.t = VT_DOUBLE;
+ vtop->r2 = r2; // set this as unused
+ } else {
+ ALWAYS_ASSERT(FALSE);
+ }
+}
+
+/* computed goto support */
+void ggoto(void)
+{
+ gcall_or_jmp(1);
+ vtop--;
+}
+
+/* Save the stack pointer onto the stack and return the location of its address */
+ST_FUNC void gen_vla_sp_save(int addr) {
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* Restore the SP from a location on the stack */
+ST_FUNC void gen_vla_sp_restore(int addr) {
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* Subtract from the stack pointer, and push the resulting value onto the stack */
+ST_FUNC void gen_vla_alloc(CType *type, int align) {
+ tcc_error("variable length arrays unsupported for this target");
+}
+
+/* end of C67 code generator */
+/*************************************************************/
+#endif
+/*************************************************************/
diff --git a/src/x86/i386-asm.c b/src/x86/i386-asm.c
new file mode 100644
index 0000000..adc40b2
--- /dev/null
+++ b/src/x86/i386-asm.c
@@ -0,0 +1,1502 @@
+/*
+ * i386 specific functions for TCC assembler
+ *
+ * Copyright (c) 2001, 2002 Fabrice Bellard
+ * Copyright (c) 2009 Frédéric Feret (x86_64 support)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "../tcc.h"
+
+/* #define NB_ASM_REGS 8 */
+#define MAX_OPERANDS 3
+#define NB_SAVED_REGS 3
+
+#define TOK_ASM_first TOK_ASM_clc
+#define TOK_ASM_last TOK_ASM_emms
+#define TOK_ASM_alllast TOK_ASM_pxor
+
+#define OPC_JMP 0x01 /* jmp operand */
+#define OPC_B 0x02 /* only used with OPC_WL */
+#define OPC_WL 0x04 /* accepts w, l or no suffix */
+#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */
+#define OPC_REG 0x08 /* register is added to opcode */
+#define OPC_MODRM 0x10 /* modrm encoding */
+#define OPC_FWAIT 0x20 /* add fwait opcode */
+#define OPC_TEST 0x40 /* test opcodes */
+#define OPC_SHIFT 0x80 /* shift opcodes */
+#define OPC_D16 0x0100 /* generate data16 prefix */
+#define OPC_ARITH 0x0200 /* arithmetic opcodes */
+#define OPC_SHORTJMP 0x0400 /* short jmp operand */
+#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */
+#ifdef TCC_TARGET_X86_64
+# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */
+# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */
+# define OPC_WLX OPC_WLQ
+#else
+# define OPC_WLX OPC_WL
+#endif
+
+#define OPC_GROUP_SHIFT 13
+
+/* in order to compress the operand type, we use specific operands and
+ we or only with EA */
+enum {
+ OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */
+#ifdef TCC_TARGET_X86_64
+ OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */
+#endif
+ OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */
+ OPT_SEG,
+ OPT_ST,
+ OPT_IM8,
+ OPT_IM8S,
+ OPT_IM16,
+ OPT_IM32,
+#ifdef TCC_TARGET_X86_64
+ OPT_IM64,
+#endif
+ OPT_EAX, /* %al, %ax, %eax or %rax register */
+ OPT_ST0, /* %st(0) register */
+ OPT_CL, /* %cl register */
+ OPT_DX, /* %dx register */
+ OPT_ADDR, /* OP_EA with only offset */
+ OPT_INDIR, /* *(expr) */
+ /* composite types */
+ OPT_COMPOSITE_FIRST,
+ OPT_IM, /* IM8 | IM16 | IM32 | IM64 */
+ OPT_REG, /* REG8 | REG16 | REG32 | REG64 */
+ OPT_REGW, /* REG16 | REG32 | REG64 */
+ OPT_IMW, /* IM16 | IM32 | IM64 */
+#ifdef TCC_TARGET_X86_64
+ OPT_IMNO64, /* IM16 | IM32 */
+#endif
+ /* can be ored with any OPT_xxx */
+ OPT_EA = 0x80
+};
+
+#define OP_REG8 (1 << OPT_REG8)
+#define OP_REG16 (1 << OPT_REG16)
+#define OP_REG32 (1 << OPT_REG32)
+#define OP_MMX (1 << OPT_MMX)
+#define OP_SSE (1 << OPT_SSE)
+#define OP_CR (1 << OPT_CR)
+#define OP_TR (1 << OPT_TR)
+#define OP_DB (1 << OPT_DB)
+#define OP_SEG (1 << OPT_SEG)
+#define OP_ST (1 << OPT_ST)
+#define OP_IM8 (1 << OPT_IM8)
+#define OP_IM8S (1 << OPT_IM8S)
+#define OP_IM16 (1 << OPT_IM16)
+#define OP_IM32 (1 << OPT_IM32)
+#define OP_EAX (1 << OPT_EAX)
+#define OP_ST0 (1 << OPT_ST0)
+#define OP_CL (1 << OPT_CL)
+#define OP_DX (1 << OPT_DX)
+#define OP_ADDR (1 << OPT_ADDR)
+#define OP_INDIR (1 << OPT_INDIR)
+#ifdef TCC_TARGET_X86_64
+# define OP_REG64 (1 << OPT_REG64)
+# define OP_IM64 (1 << OPT_IM64)
+#else
+# define OP_REG64 0
+# define OP_IM64 0
+#endif
+
+#define OP_EA 0x40000000
+#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64)
+
+#ifdef TCC_TARGET_X86_64
+# define OP_IM OP_IM64
+# define TREG_XAX TREG_RAX
+# define TREG_XCX TREG_RCX
+# define TREG_XDX TREG_RDX
+#else
+# define OP_IM OP_IM32
+# define TREG_XAX TREG_EAX
+# define TREG_XCX TREG_ECX
+# define TREG_XDX TREG_EDX
+#endif
+
+typedef struct ASMInstr {
+ uint16_t sym;
+ uint16_t opcode;
+ uint16_t instr_type;
+ uint8_t nb_ops;
+ uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */
+} ASMInstr;
+
+typedef struct Operand {
+ uint32_t type;
+ int8_t reg; /* register, -1 if none */
+ int8_t reg2; /* second register, -1 if none */
+ uint8_t shift;
+ ExprValue e;
+} Operand;
+
+static const uint8_t reg_to_size[9] = {
+/*
+ [OP_REG8] = 0,
+ [OP_REG16] = 1,
+ [OP_REG32] = 2,
+#ifdef TCC_TARGET_X86_64
+ [OP_REG64] = 3,
+#endif
+*/
+ 0, 0, 1, 0, 2, 0, 0, 0, 3
+};
+
+#define NB_TEST_OPCODES 30
+
+static const uint8_t test_bits[NB_TEST_OPCODES] = {
+ 0x00, /* o */
+ 0x01, /* no */
+ 0x02, /* b */
+ 0x02, /* c */
+ 0x02, /* nae */
+ 0x03, /* nb */
+ 0x03, /* nc */
+ 0x03, /* ae */
+ 0x04, /* e */
+ 0x04, /* z */
+ 0x05, /* ne */
+ 0x05, /* nz */
+ 0x06, /* be */
+ 0x06, /* na */
+ 0x07, /* nbe */
+ 0x07, /* a */
+ 0x08, /* s */
+ 0x09, /* ns */
+ 0x0a, /* p */
+ 0x0a, /* pe */
+ 0x0b, /* np */
+ 0x0b, /* po */
+ 0x0c, /* l */
+ 0x0c, /* nge */
+ 0x0d, /* nl */
+ 0x0d, /* ge */
+ 0x0e, /* le */
+ 0x0e, /* ng */
+ 0x0f, /* nle */
+ 0x0f, /* g */
+};
+
+static const uint8_t segment_prefixes[] = {
+ 0x26, /* es */
+ 0x2e, /* cs */
+ 0x36, /* ss */
+ 0x3e, /* ds */
+ 0x64, /* fs */
+ 0x65 /* gs */
+};
+
+static const ASMInstr asm_instrs[] = {
+#define ALT(x) x
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 },
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }},
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }},
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }},
+#ifdef TCC_TARGET_X86_64
+# include "x86_64-asm.h"
+#else
+# include "i386-asm.h"
+#endif
+ /* last operation */
+ { 0, },
+};
+
+static const uint16_t op0_codes[] = {
+#define ALT(x)
+#define DEF_ASM_OP0(x, opcode) opcode,
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+#ifdef TCC_TARGET_X86_64
+# include "x86_64-asm.h"
+#else
+# include "i386-asm.h"
+#endif
+};
+
+static inline int get_reg_shift(TCCState *s1)
+{
+ int shift, v;
+#ifdef I386_ASM_16
+ if (s1->seg_size == 16)
+ tcc_error("invalid effective address");
+#endif
+ v = asm_int_expr(s1);
+ switch(v) {
+ case 1:
+ shift = 0;
+ break;
+ case 2:
+ shift = 1;
+ break;
+ case 4:
+ shift = 2;
+ break;
+ case 8:
+ shift = 3;
+ break;
+ default:
+ expect("1, 2, 4 or 8 constant");
+ shift = 0;
+ break;
+ }
+ return shift;
+}
+
+static int asm_parse_reg(void)
+{
+ int reg = 0;
+ if (tok != '%')
+ goto error_32;
+ next();
+ if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) {
+ reg = tok - TOK_ASM_eax;
+#ifdef TCC_TARGET_X86_64
+ } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) {
+ reg = tok - TOK_ASM_rax;
+#endif
+#ifdef I386_ASM_16
+ } else if (tok >= TOK_ASM_ax && tok <= TOK_ASM_di) {
+ reg = tok - TOK_ASM_ax;
+#endif
+ } else {
+ error_32:
+ expect("register");
+ }
+ next();
+ return reg;
+}
+
+static void parse_operand(TCCState *s1, Operand *op)
+{
+ ExprValue e;
+ int reg, indir;
+ const char *p;
+
+ indir = 0;
+ if (tok == '*') {
+ next();
+ indir = OP_INDIR;
+ }
+
+ if (tok == '%') {
+ next();
+ if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) {
+ reg = tok - TOK_ASM_al;
+ op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */
+ op->reg = reg & 7;
+ if ((op->type & OP_REG) && op->reg == TREG_XAX)
+ op->type |= OP_EAX;
+ else if (op->type == OP_REG8 && op->reg == TREG_XCX)
+ op->type |= OP_CL;
+ else if (op->type == OP_REG16 && op->reg == TREG_XDX)
+ op->type |= OP_DX;
+ } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) {
+ op->type = OP_DB;
+ op->reg = tok - TOK_ASM_dr0;
+ } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) {
+ op->type = OP_SEG;
+ op->reg = tok - TOK_ASM_es;
+ } else if (tok == TOK_ASM_st) {
+ op->type = OP_ST;
+ op->reg = 0;
+ next();
+ if (tok == '(') {
+ next();
+ if (tok != TOK_PPNUM)
+ goto reg_error;
+ p = tokc.cstr->data;
+ reg = p[0] - '0';
+ if ((unsigned)reg >= 8 || p[1] != '\0')
+ goto reg_error;
+ op->reg = reg;
+ next();
+ skip(')');
+ }
+ if (op->reg == 0)
+ op->type |= OP_ST0;
+ goto no_skip;
+ } else {
+ reg_error:
+ tcc_error("unknown register");
+ }
+ next();
+ no_skip: ;
+ } else if (tok == '$') {
+ /* constant value */
+ next();
+ asm_expr(s1, &e);
+ op->type = OP_IM;
+ op->e.v = e.v;
+ op->e.sym = e.sym;
+ if (!op->e.sym) {
+ if (op->e.v == (uint8_t)op->e.v)
+ op->type |= OP_IM8;
+ if (op->e.v == (int8_t)op->e.v)
+ op->type |= OP_IM8S;
+ if (op->e.v == (uint16_t)op->e.v)
+ op->type |= OP_IM16;
+#ifdef TCC_TARGET_X86_64
+ if (op->e.v == (uint32_t)op->e.v)
+ op->type |= OP_IM32;
+#endif
+ }
+ } else {
+ /* address(reg,reg2,shift) with all variants */
+ op->type = OP_EA;
+ op->reg = -1;
+ op->reg2 = -1;
+ op->shift = 0;
+ if (tok != '(') {
+ asm_expr(s1, &e);
+ op->e.v = e.v;
+ op->e.sym = e.sym;
+ } else {
+ next();
+ if (tok == '%') {
+ unget_tok('(');
+ op->e.v = 0;
+ op->e.sym = NULL;
+ } else {
+ /* bracketed offset expression */
+ asm_expr(s1, &e);
+ if (tok != ')')
+ expect(")");
+ next();
+ op->e.v = e.v;
+ op->e.sym = e.sym;
+ }
+ }
+ if (tok == '(') {
+ next();
+ if (tok != ',') {
+ op->reg = asm_parse_reg();
+ }
+ if (tok == ',') {
+ next();
+ if (tok != ',') {
+ op->reg2 = asm_parse_reg();
+ }
+ if (tok == ',') {
+ next();
+ op->shift = get_reg_shift(s1);
+ }
+ }
+ skip(')');
+ }
+ if (op->reg == -1 && op->reg2 == -1)
+ op->type |= OP_ADDR;
+ }
+ op->type |= indir;
+}
+
+/* XXX: unify with C code output ? */
+ST_FUNC void gen_expr32(ExprValue *pe)
+{
+ gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
+}
+
+#ifdef TCC_TARGET_X86_64
+static void gen_expr64(ExprValue *pe)
+{
+ gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v);
+}
+#endif
+
+/* XXX: unify with C code output ? */
+static void gen_disp32(ExprValue *pe)
+{
+ Sym *sym = pe->sym;
+ if (sym && sym->r == cur_text_section->sh_num) {
+ /* same section: we can output an absolute value. Note
+ that the TCC compiler behaves differently here because
+ it always outputs a relocation to ease (future) code
+ elimination in the linker */
+ gen_le32(pe->v + sym->jnext - ind - 4);
+ } else {
+ if (sym && sym->type.t == VT_VOID) {
+ sym->type.t = VT_FUNC;
+ sym->type.ref = NULL;
+ }
+ gen_addrpc32(VT_SYM, sym, pe->v);
+ }
+}
+
+#ifdef I386_ASM_16
+static void gen_expr16(ExprValue *pe)
+{
+ if (pe->sym)
+ greloc(cur_text_section, pe->sym, ind, R_386_16);
+ gen_le16(pe->v);
+}
+static void gen_disp16(ExprValue *pe)
+{
+ Sym *sym;
+ sym = pe->sym;
+ if (sym) {
+ if (sym->r == cur_text_section->sh_num) {
+ /* same section: we can output an absolute value. Note
+ that the TCC compiler behaves differently here because
+ it always outputs a relocation to ease (future) code
+ elimination in the linker */
+ gen_le16(pe->v + sym->jnext - ind - 2);
+ } else {
+ greloc(cur_text_section, sym, ind, R_386_PC16);
+ gen_le16(pe->v - 2);
+ }
+ } else {
+ /* put an empty PC32 relocation */
+ put_elf_reloc(symtab_section, cur_text_section,
+ ind, R_386_PC16, 0);
+ gen_le16(pe->v - 2);
+ }
+}
+#endif
+
+/* generate the modrm operand */
+static inline void asm_modrm(int reg, Operand *op)
+{
+ int mod, reg1, reg2, sib_reg1;
+
+ if (op->type & (OP_REG | OP_MMX | OP_SSE)) {
+ g(0xc0 + (reg << 3) + op->reg);
+ } else if (op->reg == -1 && op->reg2 == -1) {
+ /* displacement only */
+#ifdef I386_ASM_16
+ if (tcc_state->seg_size == 16) {
+ g(0x06 + (reg << 3));
+ gen_expr16(&op->e);
+ } else if (tcc_state->seg_size == 32)
+#endif
+ {
+ g(0x05 + (reg << 3));
+ gen_expr32(&op->e);
+ }
+ } else {
+ sib_reg1 = op->reg;
+ /* fist compute displacement encoding */
+ if (sib_reg1 == -1) {
+ sib_reg1 = 5;
+ mod = 0x00;
+ } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) {
+ mod = 0x00;
+ } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) {
+ mod = 0x40;
+ } else {
+ mod = 0x80;
+ }
+ /* compute if sib byte needed */
+ reg1 = op->reg;
+ if (op->reg2 != -1)
+ reg1 = 4;
+#ifdef I386_ASM_16
+ if (tcc_state->seg_size == 32) {
+#endif
+ g(mod + (reg << 3) + reg1);
+ if (reg1 == 4) {
+ /* add sib byte */
+ reg2 = op->reg2;
+ if (reg2 == -1)
+ reg2 = 4; /* indicate no index */
+ g((op->shift << 6) + (reg2 << 3) + sib_reg1);
+ }
+#ifdef I386_ASM_16
+ } else if (tcc_state->seg_size == 16) {
+ /* edi = 7, esi = 6 --> di = 5, si = 4 */
+ if ((reg1 == 6) || (reg1 == 7)) {
+ reg1 -= 2;
+ /* ebx = 3 --> bx = 7 */
+ } else if (reg1 == 3) {
+ reg1 = 7;
+ /* o32 = 5 --> o16 = 6 */
+ } else if (reg1 == 5) {
+ reg1 = 6;
+ /* sib not valid in 16-bit mode */
+ } else if (reg1 == 4) {
+ reg2 = op->reg2;
+ /* bp + si + offset */
+ if ((sib_reg1 == 5) && (reg2 == 6)) {
+ reg1 = 2;
+ /* bp + di + offset */
+ } else if ((sib_reg1 == 5) && (reg2 == 7)) {
+ reg1 = 3;
+ /* bx + si + offset */
+ } else if ((sib_reg1 == 3) && (reg2 == 6)) {
+ reg1 = 0;
+ /* bx + di + offset */
+ } else if ((sib_reg1 == 3) && (reg2 == 7)) {
+ reg1 = 1;
+ } else {
+ tcc_error("invalid effective address");
+ }
+ if (op->e.v == 0)
+ mod = 0;
+ } else {
+ tcc_error("invalid register");
+ }
+ g(mod + (reg << 3) + reg1);
+ }
+#endif
+ /* add offset */
+ if (mod == 0x40) {
+ g(op->e.v);
+ } else if (mod == 0x80 || op->reg == -1) {
+#ifdef I386_ASM_16
+ if (tcc_state->seg_size == 16)
+ gen_expr16(&op->e);
+ else if (tcc_state->seg_size == 32)
+#endif
+ gen_expr32(&op->e);
+ }
+ }
+}
+
+ST_FUNC void asm_opcode(TCCState *s1, int opcode)
+{
+ const ASMInstr *pa;
+ int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix;
+ int nb_ops, s;
+ Operand ops[MAX_OPERANDS], *pop;
+ int op_type[3]; /* decoded op type */
+#ifdef I386_ASM_16
+ static int a32 = 0, o32 = 0, addr32 = 0, data32 = 0;
+#endif
+
+ /* force synthetic ';' after prefix instruction, so we can handle */
+ /* one-line things like "rep stosb" instead of only "rep\nstosb" */
+ if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz)
+ unget_tok(';');
+
+ /* get operands */
+ pop = ops;
+ nb_ops = 0;
+ seg_prefix = 0;
+ for(;;) {
+ if (tok == ';' || tok == TOK_LINEFEED)
+ break;
+ if (nb_ops >= MAX_OPERANDS) {
+ tcc_error("incorrect number of operands");
+ }
+ parse_operand(s1, pop);
+ if (tok == ':') {
+ if (pop->type != OP_SEG || seg_prefix)
+ tcc_error("incorrect prefix");
+ seg_prefix = segment_prefixes[pop->reg];
+ next();
+ parse_operand(s1, pop);
+#ifndef I386_ASM_16
+ if (!(pop->type & OP_EA)) {
+ tcc_error("segment prefix must be followed by memory reference");
+ }
+#endif
+ }
+ pop++;
+ nb_ops++;
+ if (tok != ',')
+ break;
+ next();
+ }
+
+ is_short_jmp = 0;
+ s = 0; /* avoid warning */
+
+ /* optimize matching by using a lookup table (no hashing is needed
+ !) */
+ for(pa = asm_instrs; pa->sym != 0; pa++) {
+ s = 0;
+ if (pa->instr_type & OPC_FARITH) {
+ v = opcode - pa->sym;
+ if (!((unsigned)v < 8 * 6 && (v % 6) == 0))
+ continue;
+ } else if (pa->instr_type & OPC_ARITH) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX))
+ continue;
+ s = (opcode - pa->sym) % NBWLX;
+ } else if (pa->instr_type & OPC_SHIFT) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX))
+ continue;
+ s = (opcode - pa->sym) % NBWLX;
+ } else if (pa->instr_type & OPC_TEST) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES))
+ continue;
+ } else if (pa->instr_type & OPC_B) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX))
+ continue;
+ s = opcode - pa->sym;
+ } else if (pa->instr_type & OPC_WLX) {
+ if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1))
+ continue;
+ s = opcode - pa->sym + 1;
+ } else {
+ if (pa->sym != opcode)
+ continue;
+ }
+ if (pa->nb_ops != nb_ops)
+ continue;
+ /* now decode and check each operand */
+ for(i = 0; i < nb_ops; i++) {
+ int op1, op2;
+ op1 = pa->op_type[i];
+ op2 = op1 & 0x1f;
+ switch(op2) {
+ case OPT_IM:
+ v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64;
+ break;
+ case OPT_REG:
+ v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64;
+ break;
+ case OPT_REGW:
+ v = OP_REG16 | OP_REG32 | OP_REG64;
+ break;
+ case OPT_IMW:
+ v = OP_IM16 | OP_IM32 | OP_IM64;
+ break;
+#ifdef TCC_TARGET_X86_64
+ case OPT_IMNO64:
+ v = OP_IM16 | OP_IM32;
+ break;
+#endif
+ default:
+ v = 1 << op2;
+ break;
+ }
+ if (op1 & OPT_EA)
+ v |= OP_EA;
+ op_type[i] = v;
+ if ((ops[i].type & v) == 0)
+ goto next;
+ }
+ /* all is matching ! */
+ break;
+ next: ;
+ }
+ if (pa->sym == 0) {
+ if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) {
+ int b;
+ b = op0_codes[opcode - TOK_ASM_first];
+#ifdef I386_ASM_16
+ if (opcode == TOK_ASM_o32) {
+ if (s1->seg_size == 32)
+ tcc_error("incorrect prefix");
+ else
+ o32 = data32 = 1;
+ } else if (opcode == TOK_ASM_a32) {
+ if (s1->seg_size == 32)
+ tcc_error("incorrect prefix");
+ else
+ a32 = addr32 = 1;
+ }
+#endif
+ if (b & 0xff00)
+ g(b >> 8);
+ g(b);
+ return;
+ } else if (opcode <= TOK_ASM_alllast) {
+ tcc_error("bad operand with opcode '%s'",
+ get_tok_str(opcode, NULL));
+ } else {
+ tcc_error("unknown opcode '%s'",
+ get_tok_str(opcode, NULL));
+ }
+ }
+ /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */
+ if (s == NBWLX-1) {
+ for(i = 0; s == NBWLX-1 && i < nb_ops; i++) {
+ if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX)))
+ s = reg_to_size[ops[i].type & OP_REG];
+ }
+ if (s == NBWLX-1) {
+ if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) &&
+ (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64)))
+ s = 2;
+ else
+ tcc_error("cannot infer opcode suffix");
+ }
+ }
+
+#ifdef I386_ASM_16
+ for(i = 0; i < nb_ops; i++) {
+ if (ops[i].type & OP_REG32) {
+ if (s1->seg_size == 16)
+ o32 = 1;
+ } else if (!(ops[i].type & OP_REG32)) {
+ if (s1->seg_size == 32)
+ o32 = 1;
+ }
+ }
+
+
+ if (s == 1 || (pa->instr_type & OPC_D16)) {
+ if (s1->seg_size == 32)
+ o32 = 1;
+ } else if (s == 2) {
+ if (s1->seg_size == 16) {
+ if (!(pa->instr_type & OPC_D16))
+ o32 = 1;
+ }
+ }
+
+ /* generate a16/a32 prefix if needed */
+ if ((a32 == 1) && (addr32 == 0))
+ g(0x67);
+ /* generate o16/o32 prefix if needed */
+ if ((o32 == 1) && (data32 == 0))
+ g(0x66);
+
+ addr32 = data32 = 0;
+#else
+ /* generate data16 prefix if needed */
+ if (s == 1 || (pa->instr_type & OPC_D16))
+ g(0x66);
+#ifdef TCC_TARGET_X86_64
+ else if (s == 3) {
+ /* generate REX prefix */
+ if ((opcode != TOK_ASM_push && opcode != TOK_ASM_pop)
+ || !(ops[0].type & OP_REG64))
+ g(0x48);
+ }
+#endif
+#endif
+
+ /* now generates the operation */
+ if (pa->instr_type & OPC_FWAIT)
+ g(0x9b);
+ if (seg_prefix)
+ g(seg_prefix);
+
+ v = pa->opcode;
+ if ((v == 0x69 || v == 0x6b) && nb_ops == 2) {
+ /* kludge for imul $im, %reg */
+ nb_ops = 3;
+ ops[2] = ops[1];
+ op_type[2] = op_type[1];
+ } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) {
+ v--; /* int $3 case */
+ nb_ops = 0;
+ } else if ((v == 0x06 || v == 0x07)) {
+ if (ops[0].reg >= 4) {
+ /* push/pop %fs or %gs */
+ v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3);
+ } else {
+ v += ops[0].reg << 3;
+ }
+ nb_ops = 0;
+ } else if (v <= 0x05) {
+ /* arith case */
+ v += ((opcode - TOK_ASM_addb) / NBWLX) << 3;
+ } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) {
+ /* fpu arith case */
+ v += ((opcode - pa->sym) / 6) << 3;
+ }
+ if (pa->instr_type & OPC_REG) {
+ for(i = 0; i < nb_ops; i++) {
+ if (op_type[i] & (OP_REG | OP_ST)) {
+ v += ops[i].reg;
+ break;
+ }
+ }
+ /* mov $im, %reg case */
+ if (pa->opcode == 0xb0 && s >= 1)
+ v += 7;
+ }
+ if (pa->instr_type & OPC_B)
+ v += s >= 1;
+ if (pa->instr_type & OPC_TEST)
+ v += test_bits[opcode - pa->sym];
+ if (pa->instr_type & OPC_SHORTJMP) {
+ Sym *sym;
+ int jmp_disp;
+
+ /* see if we can really generate the jump with a byte offset */
+ sym = ops[0].e.sym;
+ if (!sym)
+ goto no_short_jump;
+ if (sym->r != cur_text_section->sh_num)
+ goto no_short_jump;
+ jmp_disp = ops[0].e.v + sym->jnext - ind - 2;
+ if (jmp_disp == (int8_t)jmp_disp) {
+ /* OK to generate jump */
+ is_short_jmp = 1;
+ ops[0].e.v = jmp_disp;
+ } else {
+ no_short_jump:
+ if (pa->instr_type & OPC_JMP) {
+ /* long jump will be allowed. need to modify the
+ opcode slightly */
+ if (v == 0xeb)
+ v = 0xe9;
+ else
+ v += 0x0f10;
+ } else {
+ tcc_error("invalid displacement");
+ }
+ }
+ }
+ op1 = v >> 8;
+ if (op1)
+ g(op1);
+ g(v);
+
+ /* search which operand will used for modrm */
+ modrm_index = 0;
+ if (pa->instr_type & OPC_SHIFT) {
+ reg = (opcode - pa->sym) / NBWLX;
+ if (reg == 6)
+ reg = 7;
+ } else if (pa->instr_type & OPC_ARITH) {
+ reg = (opcode - pa->sym) / NBWLX;
+ } else if (pa->instr_type & OPC_FARITH) {
+ reg = (opcode - pa->sym) / 6;
+ } else {
+ reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7;
+ }
+ if (pa->instr_type & OPC_MODRM) {
+ /* first look for an ea operand */
+ for(i = 0;i < nb_ops; i++) {
+ if (op_type[i] & OP_EA)
+ goto modrm_found;
+ }
+ /* then if not found, a register or indirection (shift instructions) */
+ for(i = 0;i < nb_ops; i++) {
+ if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR))
+ goto modrm_found;
+ }
+#ifdef ASM_DEBUG
+ tcc_error("bad op table");
+#endif
+ modrm_found:
+ modrm_index = i;
+ /* if a register is used in another operand then it is
+ used instead of group */
+ for(i = 0;i < nb_ops; i++) {
+ v = op_type[i];
+ if (i != modrm_index &&
+ (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) {
+ reg = ops[i].reg;
+ break;
+ }
+ }
+
+ asm_modrm(reg, &ops[modrm_index]);
+ }
+
+ /* emit constants */
+#ifndef TCC_TARGET_X86_64
+ if (pa->opcode == 0x9a || pa->opcode == 0xea) {
+ /* ljmp or lcall kludge */
+#ifdef I386_ASM_16
+ if (s1->seg_size == 16 && o32 == 0)
+ gen_expr16(&ops[1].e);
+ else
+#endif
+ gen_expr32(&ops[1].e);
+ if (ops[0].e.sym)
+ tcc_error("cannot relocate");
+ gen_le16(ops[0].e.v);
+ return;
+ }
+#endif
+ for(i = 0;i < nb_ops; i++) {
+ v = op_type[i];
+ if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) {
+ /* if multiple sizes are given it means we must look
+ at the op size */
+ if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) {
+ if (s == 0)
+ v = OP_IM8;
+ else if (s == 1)
+ v = OP_IM16;
+ else if (s == 2 || (v & OP_IM64) == 0)
+ v = OP_IM32;
+ else
+ v = OP_IM64;
+ }
+ if (v & (OP_IM8 | OP_IM8S)) {
+ if (ops[i].e.sym)
+ goto error_relocate;
+ g(ops[i].e.v);
+ } else if (v & OP_IM16) {
+#ifdef I386_ASM_16
+ if (s1->seg_size == 16)
+ gen_expr16(&ops[i].e);
+ else
+#endif
+ if (ops[i].e.sym)
+ error_relocate:
+ tcc_error("cannot relocate");
+ else
+ gen_le16(ops[i].e.v);
+ } else {
+ if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
+ if (is_short_jmp)
+ g(ops[i].e.v);
+#ifdef I386_ASM_16
+ else if (s1->seg_size == 16)
+ gen_disp16(&ops[i].e);
+#endif
+ else
+ gen_disp32(&ops[i].e);
+ } else {
+#ifdef I386_ASM_16
+ if (s1->seg_size == 16 && !((o32 == 1) && (v & OP_IM32)))
+ gen_expr16(&ops[i].e);
+ else
+#endif
+#ifdef TCC_TARGET_X86_64
+ if (v & OP_IM64)
+ gen_expr64(&ops[i].e);
+ else
+#endif
+ gen_expr32(&ops[i].e);
+ }
+ }
+#ifdef I386_ASM_16
+ } else if (v & (OP_REG16 | OP_REG32)) {
+ if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
+ /* jmp $r */
+ g(0xE0 + ops[i].reg);
+ }
+#endif
+#ifdef TCC_TARGET_X86_64
+ } else if (v & (OP_REG32 | OP_REG64)) {
+ if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) {
+ /* jmp $r */
+ g(0xE0 + ops[i].reg);
+ }
+#endif
+ }
+ }
+#ifdef I386_ASM_16
+ a32 = o32 = 0;
+#endif
+}
+
+/* return the constraint priority (we allocate first the lowest
+ numbered constraints) */
+static inline int constraint_priority(const char *str)
+{
+ int priority, c, pr;
+
+ /* we take the lowest priority */
+ priority = 0;
+ for(;;) {
+ c = *str;
+ if (c == '\0')
+ break;
+ str++;
+ switch(c) {
+ case 'A':
+ pr = 0;
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'S':
+ case 'D':
+ pr = 1;
+ break;
+ case 'q':
+ pr = 2;
+ break;
+ case 'r':
+ pr = 3;
+ break;
+ case 'N':
+ case 'M':
+ case 'I':
+ case 'i':
+ case 'm':
+ case 'g':
+ pr = 4;
+ break;
+ default:
+ tcc_error("unknown constraint '%c'", c);
+ pr = 0;
+ }
+ if (pr > priority)
+ priority = pr;
+ }
+ return priority;
+}
+
+static const char *skip_constraint_modifiers(const char *p)
+{
+ while (*p == '=' || *p == '&' || *p == '+' || *p == '%')
+ p++;
+ return p;
+}
+
+#define REG_OUT_MASK 0x01
+#define REG_IN_MASK 0x02
+
+#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask)
+
+ST_FUNC void asm_compute_constraints(ASMOperand *operands,
+ int nb_operands, int nb_outputs,
+ const uint8_t *clobber_regs,
+ int *pout_reg)
+{
+ ASMOperand *op;
+ int sorted_op[MAX_ASM_OPERANDS];
+ int i, j, k, p1, p2, tmp, reg, c, reg_mask;
+ const char *str;
+ uint8_t regs_allocated[NB_ASM_REGS];
+
+ /* init fields */
+ for(i=0;i<nb_operands;i++) {
+ op = &operands[i];
+ op->input_index = -1;
+ op->ref_index = -1;
+ op->reg = -1;
+ op->is_memory = 0;
+ op->is_rw = 0;
+ }
+ /* compute constraint priority and evaluate references to output
+ constraints if input constraints */
+ for(i=0;i<nb_operands;i++) {
+ op = &operands[i];
+ str = op->constraint;
+ str = skip_constraint_modifiers(str);
+ if (isnum(*str) || *str == '[') {
+ /* this is a reference to another constraint */
+ k = find_constraint(operands, nb_operands, str, NULL);
+ if ((unsigned)k >= i || i < nb_outputs)
+ tcc_error("invalid reference in constraint %d ('%s')",
+ i, str);
+ op->ref_index = k;
+ if (operands[k].input_index >= 0)
+ tcc_error("cannot reference twice the same operand");
+ operands[k].input_index = i;
+ op->priority = 5;
+ } else {
+ op->priority = constraint_priority(str);
+ }
+ }
+
+ /* sort operands according to their priority */
+ for(i=0;i<nb_operands;i++)
+ sorted_op[i] = i;
+ for(i=0;i<nb_operands - 1;i++) {
+ for(j=i+1;j<nb_operands;j++) {
+ p1 = operands[sorted_op[i]].priority;
+ p2 = operands[sorted_op[j]].priority;
+ if (p2 < p1) {
+ tmp = sorted_op[i];
+ sorted_op[i] = sorted_op[j];
+ sorted_op[j] = tmp;
+ }
+ }
+ }
+
+ for(i = 0;i < NB_ASM_REGS; i++) {
+ if (clobber_regs[i])
+ regs_allocated[i] = REG_IN_MASK | REG_OUT_MASK;
+ else
+ regs_allocated[i] = 0;
+ }
+ /* esp cannot be used */
+ regs_allocated[4] = REG_IN_MASK | REG_OUT_MASK;
+ /* ebp cannot be used yet */
+ regs_allocated[5] = REG_IN_MASK | REG_OUT_MASK;
+
+ /* allocate registers and generate corresponding asm moves */
+ for(i=0;i<nb_operands;i++) {
+ j = sorted_op[i];
+ op = &operands[j];
+ str = op->constraint;
+ /* no need to allocate references */
+ if (op->ref_index >= 0)
+ continue;
+ /* select if register is used for output, input or both */
+ if (op->input_index >= 0) {
+ reg_mask = REG_IN_MASK | REG_OUT_MASK;
+ } else if (j < nb_outputs) {
+ reg_mask = REG_OUT_MASK;
+ } else {
+ reg_mask = REG_IN_MASK;
+ }
+ try_next:
+ c = *str++;
+ switch(c) {
+ case '=':
+ goto try_next;
+ case '+':
+ op->is_rw = 1;
+ /* FALL THRU */
+ case '&':
+ if (j >= nb_outputs)
+ tcc_error("'%c' modifier can only be applied to outputs", c);
+ reg_mask = REG_IN_MASK | REG_OUT_MASK;
+ goto try_next;
+ case 'A':
+ /* allocate both eax and edx */
+ if (is_reg_allocated(TREG_XAX) ||
+ is_reg_allocated(TREG_XDX))
+ goto try_next;
+ op->is_llong = 1;
+ op->reg = TREG_XAX;
+ regs_allocated[TREG_XAX] |= reg_mask;
+ regs_allocated[TREG_XDX] |= reg_mask;
+ break;
+ case 'a':
+ reg = TREG_XAX;
+ goto alloc_reg;
+ case 'b':
+ reg = 3;
+ goto alloc_reg;
+ case 'c':
+ reg = TREG_XCX;
+ goto alloc_reg;
+ case 'd':
+ reg = TREG_XDX;
+ goto alloc_reg;
+ case 'S':
+ reg = 6;
+ goto alloc_reg;
+ case 'D':
+ reg = 7;
+ alloc_reg:
+ if (is_reg_allocated(reg))
+ goto try_next;
+ goto reg_found;
+ case 'q':
+ /* eax, ebx, ecx or edx */
+ for(reg = 0; reg < 4; reg++) {
+ if (!is_reg_allocated(reg))
+ goto reg_found;
+ }
+ goto try_next;
+ case 'r':
+ /* any general register */
+ for(reg = 0; reg < 8; reg++) {
+ if (!is_reg_allocated(reg))
+ goto reg_found;
+ }
+ goto try_next;
+ reg_found:
+ /* now we can reload in the register */
+ op->is_llong = 0;
+ op->reg = reg;
+ regs_allocated[reg] |= reg_mask;
+ break;
+ case 'i':
+ if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST))
+ goto try_next;
+ break;
+ case 'I':
+ case 'N':
+ case 'M':
+ if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST))
+ goto try_next;
+ break;
+ case 'm':
+ case 'g':
+ /* nothing special to do because the operand is already in
+ memory, except if the pointer itself is stored in a
+ memory variable (VT_LLOCAL case) */
+ /* XXX: fix constant case */
+ /* if it is a reference to a memory zone, it must lie
+ in a register, so we reserve the register in the
+ input registers and a load will be generated
+ later */
+ if (j < nb_outputs || c == 'm') {
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
+ /* any general register */
+ for(reg = 0; reg < 8; reg++) {
+ if (!(regs_allocated[reg] & REG_IN_MASK))
+ goto reg_found1;
+ }
+ goto try_next;
+ reg_found1:
+ /* now we can reload in the register */
+ regs_allocated[reg] |= REG_IN_MASK;
+ op->reg = reg;
+ op->is_memory = 1;
+ }
+ }
+ break;
+ default:
+ tcc_error("asm constraint %d ('%s') could not be satisfied",
+ j, op->constraint);
+ break;
+ }
+ /* if a reference is present for that operand, we assign it too */
+ if (op->input_index >= 0) {
+ operands[op->input_index].reg = op->reg;
+ operands[op->input_index].is_llong = op->is_llong;
+ }
+ }
+
+ /* compute out_reg. It is used to store outputs registers to memory
+ locations references by pointers (VT_LLOCAL case) */
+ *pout_reg = -1;
+ for(i=0;i<nb_operands;i++) {
+ op = &operands[i];
+ if (op->reg >= 0 &&
+ (op->vt->r & VT_VALMASK) == VT_LLOCAL &&
+ !op->is_memory) {
+ for(reg = 0; reg < 8; reg++) {
+ if (!(regs_allocated[reg] & REG_OUT_MASK))
+ goto reg_found2;
+ }
+ tcc_error("could not find free output register for reloading");
+ reg_found2:
+ *pout_reg = reg;
+ break;
+ }
+ }
+
+ /* print sorted constraints */
+#ifdef ASM_DEBUG
+ for(i=0;i<nb_operands;i++) {
+ j = sorted_op[i];
+ op = &operands[j];
+ printf("%%%d [%s]: \"%s\" r=0x%04x reg=%d\n",
+ j,
+ op->id ? get_tok_str(op->id, NULL) : "",
+ op->constraint,
+ op->vt->r,
+ op->reg);
+ }
+ if (*pout_reg >= 0)
+ printf("out_reg=%d\n", *pout_reg);
+#endif
+}
+
+ST_FUNC void subst_asm_operand(CString *add_str,
+ SValue *sv, int modifier)
+{
+ int r, reg, size, val;
+ char buf[64];
+
+ r = sv->r;
+ if ((r & VT_VALMASK) == VT_CONST) {
+ if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n')
+ cstr_ccat(add_str, '$');
+ if (r & VT_SYM) {
+ cstr_cat(add_str, get_tok_str(sv->sym->v, NULL));
+ if (sv->c.i != 0) {
+ cstr_ccat(add_str, '+');
+ } else {
+ return;
+ }
+ }
+ val = sv->c.i;
+ if (modifier == 'n')
+ val = -val;
+ snprintf(buf, sizeof(buf), "%d", sv->c.i);
+ cstr_cat(add_str, buf);
+ } else if ((r & VT_VALMASK) == VT_LOCAL) {
+ snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i);
+ cstr_cat(add_str, buf);
+ } else if (r & VT_LVAL) {
+ reg = r & VT_VALMASK;
+ if (reg >= VT_CONST)
+ tcc_error("internal compiler error");
+ snprintf(buf, sizeof(buf), "(%%%s)",
+ get_tok_str(TOK_ASM_eax + reg, NULL));
+ cstr_cat(add_str, buf);
+ } else {
+ /* register case */
+ reg = r & VT_VALMASK;
+ if (reg >= VT_CONST)
+ tcc_error("internal compiler error");
+
+ /* choose register operand size */
+ if ((sv->type.t & VT_BTYPE) == VT_BYTE)
+ size = 1;
+ else if ((sv->type.t & VT_BTYPE) == VT_SHORT)
+ size = 2;
+#ifdef TCC_TARGET_X86_64
+ else if ((sv->type.t & VT_BTYPE) == VT_LLONG)
+ size = 8;
+#endif
+ else
+ size = 4;
+ if (size == 1 && reg >= 4)
+ size = 4;
+
+ if (modifier == 'b') {
+ if (reg >= 4)
+ tcc_error("cannot use byte register");
+ size = 1;
+ } else if (modifier == 'h') {
+ if (reg >= 4)
+ tcc_error("cannot use byte register");
+ size = -1;
+ } else if (modifier == 'w') {
+ size = 2;
+#ifdef TCC_TARGET_X86_64
+ } else if (modifier == 'q') {
+ size = 8;
+#endif
+ }
+
+ switch(size) {
+ case -1:
+ reg = TOK_ASM_ah + reg;
+ break;
+ case 1:
+ reg = TOK_ASM_al + reg;
+ break;
+ case 2:
+ reg = TOK_ASM_ax + reg;
+ break;
+ default:
+ reg = TOK_ASM_eax + reg;
+ break;
+#ifdef TCC_TARGET_X86_64
+ case 8:
+ reg = TOK_ASM_rax + reg;
+ break;
+#endif
+ }
+ snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL));
+ cstr_cat(add_str, buf);
+ }
+}
+
+/* generate prolog and epilog code for asm statement */
+ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
+ int nb_outputs, int is_output,
+ uint8_t *clobber_regs,
+ int out_reg)
+{
+ uint8_t regs_allocated[NB_ASM_REGS];
+ ASMOperand *op;
+ int i, reg;
+ static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 };
+
+ /* mark all used registers */
+ memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated));
+ for(i = 0; i < nb_operands;i++) {
+ op = &operands[i];
+ if (op->reg >= 0)
+ regs_allocated[op->reg] = 1;
+ }
+ if (!is_output) {
+ /* generate reg save code */
+ for(i = 0; i < NB_SAVED_REGS; i++) {
+ reg = reg_saved[i];
+ if (regs_allocated[reg]) {
+#ifdef I386_ASM_16
+ if (tcc_state->seg_size == 16)
+ g(0x66);
+#endif
+ g(0x50 + reg);
+ }
+ }
+
+ /* generate load code */
+ for(i = 0; i < nb_operands; i++) {
+ op = &operands[i];
+ if (op->reg >= 0) {
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL &&
+ op->is_memory) {
+ /* memory reference case (for both input and
+ output cases) */
+ SValue sv;
+ sv = *op->vt;
+ sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
+ load(op->reg, &sv);
+ } else if (i >= nb_outputs || op->is_rw) {
+ /* load value in register */
+ load(op->reg, op->vt);
+ if (op->is_llong) {
+ SValue sv;
+ sv = *op->vt;
+ sv.c.ul += 4;
+ load(TREG_XDX, &sv);
+ }
+ }
+ }
+ }
+ } else {
+ /* generate save code */
+ for(i = 0 ; i < nb_outputs; i++) {
+ op = &operands[i];
+ if (op->reg >= 0) {
+ if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) {
+ if (!op->is_memory) {
+ SValue sv;
+ sv = *op->vt;
+ sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL;
+ load(out_reg, &sv);
+
+ sv.r = (sv.r & ~VT_VALMASK) | out_reg;
+ store(op->reg, &sv);
+ }
+ } else {
+ store(op->reg, op->vt);
+ if (op->is_llong) {
+ SValue sv;
+ sv = *op->vt;
+ sv.c.ul += 4;
+ store(TREG_XDX, &sv);
+ }
+ }
+ }
+ }
+ /* generate reg restore code */
+ for(i = NB_SAVED_REGS - 1; i >= 0; i--) {
+ reg = reg_saved[i];
+ if (regs_allocated[reg]) {
+#ifdef I386_ASM_16
+ if (tcc_state->seg_size == 16)
+ g(0x66);
+#endif
+ g(0x58 + reg);
+ }
+ }
+ }
+}
+
+ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
+{
+ int reg;
+ TokenSym *ts;
+
+ if (!strcmp(str, "memory") ||
+ !strcmp(str, "cc"))
+ return;
+ ts = tok_alloc(str, strlen(str));
+ reg = ts->tok;
+ if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
+ reg -= TOK_ASM_eax;
+ } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {
+ reg -= TOK_ASM_ax;
+#ifdef TCC_TARGET_X86_64
+ } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) {
+ reg -= TOK_ASM_rax;
+#endif
+ } else {
+ tcc_error("invalid clobber register '%s'", str);
+ }
+ clobber_regs[reg] = 1;
+}
diff --git a/src/x86/i386-asm.h b/src/x86/i386-asm.h
new file mode 100644
index 0000000..2e82e0d
--- /dev/null
+++ b/src/x86/i386-asm.h
@@ -0,0 +1,474 @@
+ DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */
+ DEF_ASM_OP0(cld, 0xfc)
+ DEF_ASM_OP0(cli, 0xfa)
+ DEF_ASM_OP0(clts, 0x0f06)
+ DEF_ASM_OP0(cmc, 0xf5)
+ DEF_ASM_OP0(lahf, 0x9f)
+ DEF_ASM_OP0(sahf, 0x9e)
+ DEF_ASM_OP0(pusha, 0x60)
+ DEF_ASM_OP0(popa, 0x61)
+ DEF_ASM_OP0(pushfl, 0x9c)
+ DEF_ASM_OP0(popfl, 0x9d)
+ DEF_ASM_OP0(pushf, 0x9c)
+ DEF_ASM_OP0(popf, 0x9d)
+ DEF_ASM_OP0(stc, 0xf9)
+ DEF_ASM_OP0(std, 0xfd)
+ DEF_ASM_OP0(sti, 0xfb)
+ DEF_ASM_OP0(aaa, 0x37)
+ DEF_ASM_OP0(aas, 0x3f)
+ DEF_ASM_OP0(daa, 0x27)
+ DEF_ASM_OP0(das, 0x2f)
+ DEF_ASM_OP0(aad, 0xd50a)
+ DEF_ASM_OP0(aam, 0xd40a)
+ DEF_ASM_OP0(cbw, 0x6698)
+ DEF_ASM_OP0(cwd, 0x6699)
+ DEF_ASM_OP0(cwde, 0x98)
+ DEF_ASM_OP0(cdq, 0x99)
+ DEF_ASM_OP0(cbtw, 0x6698)
+ DEF_ASM_OP0(cwtl, 0x98)
+ DEF_ASM_OP0(cwtd, 0x6699)
+ DEF_ASM_OP0(cltd, 0x99)
+ DEF_ASM_OP0(int3, 0xcc)
+ DEF_ASM_OP0(into, 0xce)
+ DEF_ASM_OP0(iret, 0xcf)
+ DEF_ASM_OP0(rsm, 0x0faa)
+ DEF_ASM_OP0(hlt, 0xf4)
+ DEF_ASM_OP0(nop, 0x90)
+ DEF_ASM_OP0(pause, 0xf390)
+ DEF_ASM_OP0(xlat, 0xd7)
+
+ /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL))
+
+ /* bits */
+
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA))
+
+ /* prefixes */
+ DEF_ASM_OP0(wait, 0x9b)
+ DEF_ASM_OP0(fwait, 0x9b)
+#ifdef I386_ASM_16
+ DEF_ASM_OP0(a32, 0x67)
+ DEF_ASM_OP0(o32, 0x66)
+#else
+ DEF_ASM_OP0(aword, 0x67)
+ DEF_ASM_OP0(addr16, 0x67)
+ ALT(DEF_ASM_OP0(word, 0x66))
+ DEF_ASM_OP0(data16, 0x66)
+#endif
+ DEF_ASM_OP0(lock, 0xf0)
+ DEF_ASM_OP0(rep, 0xf3)
+ DEF_ASM_OP0(repe, 0xf3)
+ DEF_ASM_OP0(repz, 0xf3)
+ DEF_ASM_OP0(repne, 0xf2)
+ DEF_ASM_OP0(repnz, 0xf2)
+
+ DEF_ASM_OP0(invd, 0x0f08)
+ DEF_ASM_OP0(wbinvd, 0x0f09)
+ DEF_ASM_OP0(cpuid, 0x0fa2)
+ DEF_ASM_OP0(wrmsr, 0x0f30)
+ DEF_ASM_OP0(rdtsc, 0x0f31)
+ DEF_ASM_OP0(rdmsr, 0x0f32)
+ DEF_ASM_OP0(rdpmc, 0x0f33)
+ DEF_ASM_OP0(ud2, 0x0f0b)
+
+ /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+ /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX))
+
+ /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+#ifdef I386_ASM_16
+ALT(DEF_ASM_OP1(jmp, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW))
+#endif
+
+ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+ALT(DEF_ASM_OP1(setob, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+ DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+ DEF_ASM_OP0(leave, 0xc9)
+ DEF_ASM_OP0(ret, 0xc3)
+ DEF_ASM_OP0(retl,0xc3)
+ALT(DEF_ASM_OP1(retl,0xc2, 0, 0, OPT_IM16))
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+ DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+ DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+
+ /* float */
+ /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(fadd, 0xdcc0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP2(fmul, 0xdcc8, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+ DEF_ASM_OP0(fucompp, 0xdae9)
+ DEF_ASM_OP0(ftst, 0xd9e4)
+ DEF_ASM_OP0(fxam, 0xd9e5)
+ DEF_ASM_OP0(fld1, 0xd9e8)
+ DEF_ASM_OP0(fldl2t, 0xd9e9)
+ DEF_ASM_OP0(fldl2e, 0xd9ea)
+ DEF_ASM_OP0(fldpi, 0xd9eb)
+ DEF_ASM_OP0(fldlg2, 0xd9ec)
+ DEF_ASM_OP0(fldln2, 0xd9ed)
+ DEF_ASM_OP0(fldz, 0xd9ee)
+
+ DEF_ASM_OP0(f2xm1, 0xd9f0)
+ DEF_ASM_OP0(fyl2x, 0xd9f1)
+ DEF_ASM_OP0(fptan, 0xd9f2)
+ DEF_ASM_OP0(fpatan, 0xd9f3)
+ DEF_ASM_OP0(fxtract, 0xd9f4)
+ DEF_ASM_OP0(fprem1, 0xd9f5)
+ DEF_ASM_OP0(fdecstp, 0xd9f6)
+ DEF_ASM_OP0(fincstp, 0xd9f7)
+ DEF_ASM_OP0(fprem, 0xd9f8)
+ DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+ DEF_ASM_OP0(fsqrt, 0xd9fa)
+ DEF_ASM_OP0(fsincos, 0xd9fb)
+ DEF_ASM_OP0(frndint, 0xd9fc)
+ DEF_ASM_OP0(fscale, 0xd9fd)
+ DEF_ASM_OP0(fsin, 0xd9fe)
+ DEF_ASM_OP0(fcos, 0xd9ff)
+ DEF_ASM_OP0(fchs, 0xd9e0)
+ DEF_ASM_OP0(fabs, 0xd9e1)
+ DEF_ASM_OP0(fninit, 0xdbe3)
+ DEF_ASM_OP0(fnclex, 0xdbe2)
+ DEF_ASM_OP0(fnop, 0xd9d0)
+
+ /* fp load */
+ DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+ DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+ DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+
+ /* fp store */
+ DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+ DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+ DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+ /* exchange */
+ DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+ /* misc FPU */
+ DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+ DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+ DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+ DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+ DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+ DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+ /* segments */
+ DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+ DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+ DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+ DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+ DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+ DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+ DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+#ifdef I386_ASM_16
+ /* 386 */
+ DEF_ASM_OP0(loadall386, 0x0f07)
+#endif
+
+ /* 486 */
+ DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+ DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+ DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+ /* pentium */
+ DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+
+ /* pentium pro */
+ ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+#ifdef I386_ASM_16
+ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmova, 0x0f47, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+#endif
+ DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+ DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+ /* mmx */
+ DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+
+ DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+ DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+ DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) /* must be last !OP0 */
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
diff --git a/src/x86/i386-gen.c b/src/x86/i386-gen.c
new file mode 100644
index 0000000..b8b12aa
--- /dev/null
+++ b/src/x86/i386-gen.c
@@ -0,0 +1,1144 @@
+/*
+ * X86 code generator for TCC
+ *
+ * Copyright (c) 2001-2004 Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef TARGET_DEFS_ONLY
+
+/* number of available registers */
+#define NB_REGS 4
+#define NB_ASM_REGS 8
+
+typedef int RegArgs;
+
+/* a register can belong to several classes. The classes must be
+ sorted from more general to more precise (see gv2() code which does
+ assumptions on it). */
+#define RC_INT 0x0001 /* generic integer register */
+#define RC_FLOAT 0x0002 /* generic float register */
+#define RC_EAX 0x0004
+#define RC_ST0 0x0008
+#define RC_ECX 0x0010
+#define RC_EDX 0x0020
+#define RC_IRET RC_EAX /* function return: integer register */
+#define RC_LRET RC_EDX /* function return: second integer register */
+#define RC_FRET RC_ST0 /* function return: float register */
+
+/* pretty names for the registers */
+enum {
+ TREG_EAX = 0,
+ TREG_ECX,
+ TREG_EDX,
+ TREG_ST0,
+ TREG_ESP = 4
+};
+
+/* return registers for function */
+#define REG_IRET TREG_EAX /* single word int return register */
+#define REG_LRET TREG_EDX /* second word return register (for long long) */
+#define REG_FRET TREG_ST0 /* float return register */
+
+/* defined if function parameters must be evaluated in reverse order */
+#define INVERT_FUNC_PARAMS
+
+/* defined if structures are passed as pointers. Otherwise structures
+ are directly pushed on stack. */
+/* #define FUNC_STRUCT_PARAM_AS_PTR */
+
+/* pointer size, in bytes */
+#define PTR_SIZE 4
+
+/* long double size and alignment, in bytes */
+#define LDOUBLE_SIZE 12
+#define LDOUBLE_ALIGN 4
+/* maximum alignment (for aligned attribute support) */
+#define MAX_ALIGN 8
+
+
+#define psym oad
+
+/******************************************************/
+/* ELF defines */
+
+#define EM_TCC_TARGET EM_386
+
+/* relocation type for 32 bit data relocation */
+#define R_DATA_32 R_386_32
+#define R_DATA_PTR R_386_32
+#define R_JMP_SLOT R_386_JMP_SLOT
+#define R_COPY R_386_COPY
+
+#define ELF_START_ADDR 0x08048000
+#define ELF_PAGE_SIZE 0x1000
+
+/******************************************************/
+#else /* ! TARGET_DEFS_ONLY */
+/******************************************************/
+#include "../tcc.h"
+
+ST_DATA const int reg_classes[NB_REGS] = {
+ /* eax */ RC_INT | RC_EAX,
+ /* ecx */ RC_INT | RC_ECX,
+ /* edx */ RC_INT | RC_EDX,
+ /* st0 */ RC_FLOAT | RC_ST0,
+};
+
+static unsigned long func_sub_sp_offset;
+static int func_ret_sub;
+#ifdef CONFIG_TCC_BCHECK
+static addr_t func_bound_offset;
+#endif
+
+/* XXX: make it faster ? */
+ST_FUNC void g(int c)
+{
+ int ind1;
+ ind1 = ind + 1;
+ if (ind1 > cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ cur_text_section->data[ind] = c;
+ ind = ind1;
+}
+
+ST_FUNC void o(unsigned int c)
+{
+ while (c) {
+ g(c);
+ c = c >> 8;
+ }
+}
+
+ST_FUNC void gen_le16(int v)
+{
+ g(v);
+ g(v >> 8);
+}
+
+ST_FUNC void gen_le32(int c)
+{
+ g(c);
+ g(c >> 8);
+ g(c >> 16);
+ g(c >> 24);
+}
+
+/* output a symbol and patch all calls to it */
+ST_FUNC void gsym_addr(int t, int a)
+{
+ int n, *ptr;
+ while (t) {
+ ptr = (int *)(cur_text_section->data + t);
+ n = *ptr; /* next value */
+ *ptr = a - t - 4;
+ t = n;
+ }
+}
+
+ST_FUNC void gsym(int t)
+{
+ gsym_addr(t, ind);
+}
+
+/* psym is used to put an instruction with a data field which is a
+ reference to a symbol. It is in fact the same as oad ! */
+#define psym oad
+
+/* instruction + 4 bytes data. Return the address of the data */
+ST_FUNC int oad(int c, int s)
+{
+ int ind1;
+
+ o(c);
+ ind1 = ind + 4;
+ if (ind1 > cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ *(int *)(cur_text_section->data + ind) = s;
+ s = ind;
+ ind = ind1;
+ return s;
+}
+
+/* output constant with relocation if 'r & VT_SYM' is true */
+ST_FUNC void gen_addr32(int r, Sym *sym, int c)
+{
+ if (r & VT_SYM)
+ greloc(cur_text_section, sym, ind, R_386_32);
+ gen_le32(c);
+}
+
+ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
+{
+ if (r & VT_SYM)
+ greloc(cur_text_section, sym, ind, R_386_PC32);
+ gen_le32(c - 4);
+}
+
+/* generate a modrm reference. 'op_reg' contains the addtionnal 3
+ opcode bits */
+static void gen_modrm(int op_reg, int r, Sym *sym, int c)
+{
+ op_reg = op_reg << 3;
+ if ((r & VT_VALMASK) == VT_CONST) {
+ /* constant memory reference */
+ o(0x05 | op_reg);
+ gen_addr32(r, sym, c);
+ } else if ((r & VT_VALMASK) == VT_LOCAL) {
+ /* currently, we use only ebp as base */
+ if (c == (char)c) {
+ /* short reference */
+ o(0x45 | op_reg);
+ g(c);
+ } else {
+ oad(0x85 | op_reg, c);
+ }
+ } else {
+ g(0x00 | op_reg | (r & VT_VALMASK));
+ }
+}
+
+/* load 'r' from value 'sv' */
+ST_FUNC void load(int r, SValue *sv)
+{
+ int v, t, ft, fc, fr;
+ SValue v1;
+
+#ifdef TCC_TARGET_PE
+ SValue v2;
+ sv = pe_getimport(sv, &v2);
+#endif
+
+ fr = sv->r;
+ ft = sv->type.t;
+ fc = sv->c.ul;
+
+ v = fr & VT_VALMASK;
+ if (fr & VT_LVAL) {
+ if (v == VT_LLOCAL) {
+ v1.type.t = VT_INT;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = fc;
+ fr = r;
+ if (!(reg_classes[fr] & RC_INT))
+ fr = get_reg(RC_INT);
+ load(fr, &v1);
+ }
+ if ((ft & VT_BTYPE) == VT_FLOAT) {
+ o(0xd9); /* flds */
+ r = 0;
+ } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ o(0xdd); /* fldl */
+ r = 0;
+ } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ o(0xdb); /* fldt */
+ r = 5;
+ } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
+ o(0xbe0f); /* movsbl */
+ } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
+ o(0xb60f); /* movzbl */
+ } else if ((ft & VT_TYPE) == VT_SHORT) {
+ o(0xbf0f); /* movswl */
+ } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
+ o(0xb70f); /* movzwl */
+ } else {
+ o(0x8b); /* movl */
+ }
+ gen_modrm(r, fr, sv->sym, fc);
+ } else {
+ if (v == VT_CONST) {
+ o(0xb8 + r); /* mov $xx, r */
+ gen_addr32(fr, sv->sym, fc);
+ } else if (v == VT_LOCAL) {
+ if (fc) {
+ o(0x8d); /* lea xxx(%ebp), r */
+ gen_modrm(r, VT_LOCAL, sv->sym, fc);
+ } else {
+ o(0x89);
+ o(0xe8 + r); /* mov %ebp, r */
+ }
+ } else if (v == VT_CMP) {
+ oad(0xb8 + r, 0); /* mov $0, r */
+ o(0x0f); /* setxx %br */
+ o(fc);
+ o(0xc0 + r);
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ t = v & 1;
+ oad(0xb8 + r, t); /* mov $1, r */
+ o(0x05eb); /* jmp after */
+ gsym(fc);
+ oad(0xb8 + r, t ^ 1); /* mov $0, r */
+ } else if (v != r) {
+ o(0x89);
+ o(0xc0 + r + v * 8); /* mov v, r */
+ }
+ }
+}
+
+/* store register 'r' in lvalue 'v' */
+ST_FUNC void store(int r, SValue *v)
+{
+ int fr, bt, ft, fc;
+
+#ifdef TCC_TARGET_PE
+ SValue v2;
+ v = pe_getimport(v, &v2);
+#endif
+
+ ft = v->type.t;
+ fc = v->c.ul;
+ fr = v->r & VT_VALMASK;
+ bt = ft & VT_BTYPE;
+ /* XXX: incorrect if float reg to reg */
+ if (bt == VT_FLOAT) {
+ o(0xd9); /* fsts */
+ r = 2;
+ } else if (bt == VT_DOUBLE) {
+ o(0xdd); /* fstpl */
+ r = 2;
+ } else if (bt == VT_LDOUBLE) {
+ o(0xc0d9); /* fld %st(0) */
+ o(0xdb); /* fstpt */
+ r = 7;
+ } else {
+ if (bt == VT_SHORT)
+ o(0x66);
+ if (bt == VT_BYTE || bt == VT_BOOL)
+ o(0x88);
+ else
+ o(0x89);
+ }
+ if (fr == VT_CONST ||
+ fr == VT_LOCAL ||
+ (v->r & VT_LVAL)) {
+ gen_modrm(r, v->r, v->sym, fc);
+ } else if (fr != r) {
+ o(0xc0 + fr + r * 8); /* mov r, fr */
+ }
+}
+
+static void gadd_sp(int val)
+{
+ if (val == (char)val) {
+ o(0xc483);
+ g(val);
+ } else {
+ oad(0xc481, val); /* add $xxx, %esp */
+ }
+}
+
+static void gen_static_call(int v)
+{
+ Sym *sym;
+
+ sym = external_global_sym(v, &func_old_type, 0);
+ oad(0xe8, -4);
+ greloc(cur_text_section, sym, ind-4, R_386_PC32);
+}
+
+/* 'is_jmp' is '1' if it is a jump */
+static void gcall_or_jmp(int is_jmp)
+{
+ int r;
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ /* constant case */
+ if (vtop->r & VT_SYM) {
+ /* relocation case */
+ greloc(cur_text_section, vtop->sym,
+ ind + 1, R_386_PC32);
+ } else {
+ /* put an empty PC32 relocation */
+ put_elf_reloc(symtab_section, cur_text_section,
+ ind + 1, R_386_PC32, 0);
+ }
+ oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */
+ } else {
+ /* otherwise, indirect call */
+ r = gv(RC_INT);
+ o(0xff); /* call/jmp *r */
+ o(0xd0 + r + (is_jmp << 4));
+ }
+}
+
+static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX };
+static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX };
+
+ST_FUNC int regargs_nregs(RegArgs *args)
+{
+ return *args;
+}
+
+/* Return the number of registers needed to return the struct, or 0 if
+ returning via struct pointer. */
+ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args)
+{
+#ifdef TCC_TARGET_PE
+ int size, align;
+
+ *ret_align = 1; // Never have to re-align return values for x86
+ *regsize = 4;
+ size = type_size(vt, &align);
+ if (size > 8) {
+ *args = 0;
+ } else if (size > 4) {
+ ret->ref = NULL;
+ ret->t = VT_LLONG;
+ *args = 1;
+ } else {
+ ret->ref = NULL;
+ ret->t = VT_INT;
+ *args = 1;
+ }
+#else
+ *ret_align = 1; // Never have to re-align return values for x86
+ *args = 0;
+#endif
+
+ return *args != 0;
+}
+
+/* Generate function call. The function address is pushed first, then
+ all the parameters in call order. This functions pops all the
+ parameters and the function address. */
+ST_FUNC void gfunc_call(int nb_args)
+{
+ int size, align, r, args_size, i, func_call;
+ Sym *func_sym;
+
+ args_size = 0;
+ for(i = 0;i < nb_args; i++) {
+ if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+ size = type_size(&vtop->type, &align);
+ /* align to stack align size */
+ size = (size + 3) & ~3;
+ /* allocate the necessary size on stack */
+ oad(0xec81, size); /* sub $xxx, %esp */
+ /* generate structure store */
+ r = get_reg(RC_INT);
+ o(0x89); /* mov %esp, r */
+ o(0xe0 + r);
+ vset(&vtop->type, r | VT_LVAL, 0);
+ vswap();
+ vstore();
+ args_size += size;
+ } else if (is_float(vtop->type.t)) {
+ gv(RC_FLOAT); /* only one float register */
+ if ((vtop->type.t & VT_BTYPE) == VT_FLOAT)
+ size = 4;
+ else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
+ size = 8;
+ else
+ size = 12;
+ oad(0xec81, size); /* sub $xxx, %esp */
+ if (size == 12)
+ o(0x7cdb);
+ else
+ o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */
+ g(0x24);
+ g(0x00);
+ args_size += size;
+ } else {
+ /* simple type (currently always same size) */
+ /* XXX: implicit cast ? */
+ r = gv(RC_INT);
+ if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ size = 8;
+ o(0x50 + vtop->r2); /* push r */
+ } else {
+ size = 4;
+ }
+ o(0x50 + r); /* push r */
+ args_size += size;
+ }
+ vtop--;
+ }
+ save_regs(0); /* save used temporary registers */
+ func_sym = vtop->type.ref;
+ func_call = func_sym->a.func_call;
+ /* fast call case */
+ if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) ||
+ func_call == FUNC_FASTCALLW) {
+ int fastcall_nb_regs;
+ uint8_t *fastcall_regs_ptr;
+ if (func_call == FUNC_FASTCALLW) {
+ fastcall_regs_ptr = fastcallw_regs;
+ fastcall_nb_regs = 2;
+ } else {
+ fastcall_regs_ptr = fastcall_regs;
+ fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
+ }
+ for(i = 0;i < fastcall_nb_regs; i++) {
+ if (args_size <= 0)
+ break;
+ o(0x58 + fastcall_regs_ptr[i]); /* pop r */
+ /* XXX: incorrect for struct/floats */
+ args_size -= 4;
+ }
+ }
+#ifndef TCC_TARGET_PE
+ else if ((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT)
+ args_size -= 4;
+#endif
+ gcall_or_jmp(0);
+
+ if (args_size && func_call != FUNC_STDCALL)
+ gadd_sp(args_size);
+ vtop--;
+}
+
+#ifdef TCC_TARGET_PE
+#define FUNC_PROLOG_SIZE 10
+#else
+#define FUNC_PROLOG_SIZE 9
+#endif
+
+/* generate function prolog of type 't' */
+ST_FUNC void gfunc_prolog(CType *func_type)
+{
+ int addr, align, size, func_call, fastcall_nb_regs;
+ int param_index, param_addr;
+ uint8_t *fastcall_regs_ptr;
+ Sym *sym;
+ CType *type;
+
+ sym = func_type->ref;
+ func_call = sym->a.func_call;
+ addr = 8;
+ loc = 0;
+ func_vc = 0;
+
+ if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) {
+ fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1;
+ fastcall_regs_ptr = fastcall_regs;
+ } else if (func_call == FUNC_FASTCALLW) {
+ fastcall_nb_regs = 2;
+ fastcall_regs_ptr = fastcallw_regs;
+ } else {
+ fastcall_nb_regs = 0;
+ fastcall_regs_ptr = NULL;
+ }
+ param_index = 0;
+
+ ind += FUNC_PROLOG_SIZE;
+ func_sub_sp_offset = ind;
+ /* if the function returns a structure, then add an
+ implicit pointer parameter */
+ func_vt = sym->type;
+ func_var = (sym->c == FUNC_ELLIPSIS);
+#ifdef TCC_TARGET_PE
+ size = type_size(&func_vt,&align);
+ if (((func_vt.t & VT_BTYPE) == VT_STRUCT) && (size > 8)) {
+#else
+ if ((func_vt.t & VT_BTYPE) == VT_STRUCT) {
+#endif
+ /* XXX: fastcall case ? */
+ func_vc = addr;
+ addr += 4;
+ param_index++;
+ }
+ /* define parameters */
+ while ((sym = sym->next) != NULL) {
+ type = &sym->type;
+ size = type_size(type, &align);
+ size = (size + 3) & ~3;
+#ifdef FUNC_STRUCT_PARAM_AS_PTR
+ /* structs are passed as pointer */
+ if ((type->t & VT_BTYPE) == VT_STRUCT) {
+ size = 4;
+ }
+#endif
+ if (param_index < fastcall_nb_regs) {
+ /* save FASTCALL register */
+ loc -= 4;
+ o(0x89); /* movl */
+ gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc);
+ param_addr = loc;
+ } else {
+ param_addr = addr;
+ addr += size;
+ }
+ sym_push(sym->v & ~SYM_FIELD, type,
+ VT_LOCAL | lvalue_type(type->t), param_addr);
+ param_index++;
+ }
+ func_ret_sub = 0;
+ /* pascal type call ? */
+ if (func_call == FUNC_STDCALL)
+ func_ret_sub = addr - 8;
+#ifndef TCC_TARGET_PE
+ else if (func_vc)
+ func_ret_sub = 4;
+#endif
+
+#ifdef CONFIG_TCC_BCHECK
+ /* leave some room for bound checking code */
+ if (tcc_state->do_bounds_check) {
+ oad(0xb8, 0); /* lbound section pointer */
+ oad(0xb8, 0); /* call to function */
+ func_bound_offset = lbounds_section->data_offset;
+ }
+#endif
+}
+
+/* generate function epilog */
+ST_FUNC void gfunc_epilog(void)
+{
+ addr_t v, saved_ind;
+
+#ifdef CONFIG_TCC_BCHECK
+ if (tcc_state->do_bounds_check
+ && func_bound_offset != lbounds_section->data_offset) {
+ addr_t saved_ind;
+ addr_t *bounds_ptr;
+ Sym *sym_data;
+ /* add end of table info */
+ bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
+ *bounds_ptr = 0;
+ /* generate bound local allocation */
+ saved_ind = ind;
+ ind = func_sub_sp_offset;
+ sym_data = get_sym_ref(&char_pointer_type, lbounds_section,
+ func_bound_offset, lbounds_section->data_offset);
+ greloc(cur_text_section, sym_data,
+ ind + 1, R_386_32);
+ oad(0xb8, 0); /* mov %eax, xxx */
+ gen_static_call(TOK___bound_local_new);
+
+ ind = saved_ind;
+ /* generate bound check local freeing */
+ o(0x5250); /* save returned value, if any */
+ greloc(cur_text_section, sym_data,
+ ind + 1, R_386_32);
+ oad(0xb8, 0); /* mov %eax, xxx */
+ gen_static_call(TOK___bound_local_delete);
+
+ o(0x585a); /* restore returned value, if any */
+ }
+#endif
+ o(0xc9); /* leave */
+ if (func_ret_sub == 0) {
+ o(0xc3); /* ret */
+ } else {
+ o(0xc2); /* ret n */
+ g(func_ret_sub);
+ g(func_ret_sub >> 8);
+ }
+ /* align local size to word & save local variables */
+
+ v = (-loc + 3) & -4;
+ saved_ind = ind;
+ ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
+#ifdef TCC_TARGET_PE
+ if (v >= 4096) {
+ oad(0xb8, v); /* mov stacksize, %eax */
+ gen_static_call(TOK___chkstk); /* call __chkstk, (does the stackframe too) */
+ } else
+#endif
+ {
+ o(0xe58955); /* push %ebp, mov %esp, %ebp */
+ o(0xec81); /* sub esp, stacksize */
+ gen_le32(v);
+#if FUNC_PROLOG_SIZE == 10
+ o(0x90); /* adjust to FUNC_PROLOG_SIZE */
+#endif
+ }
+ ind = saved_ind;
+}
+
+/* generate a jump to a label */
+ST_FUNC int gjmp(int t)
+{
+ return psym(0xe9, t);
+}
+
+/* generate a jump to a fixed address */
+ST_FUNC void gjmp_addr(int a)
+{
+ int r;
+ r = a - ind - 2;
+ if (r == (char)r) {
+ g(0xeb);
+ g(r);
+ } else {
+ oad(0xe9, a - ind - 5);
+ }
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+ST_FUNC int gtst(int inv, int t)
+{
+ int v, *p;
+
+ v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP) {
+ /* fast case : can jump directly since flags are set */
+ g(0x0f);
+ t = psym((vtop->c.i - 16) ^ inv, t);
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ /* && or || optimization */
+ if ((v & 1) == inv) {
+ /* insert vtop->c jump list in t */
+ p = &vtop->c.i;
+ while (*p != 0)
+ p = (int *)(cur_text_section->data + *p);
+ *p = t;
+ t = vtop->c.i;
+ } else {
+ t = gjmp(t);
+ gsym(vtop->c.i);
+ }
+ }
+ vtop--;
+ return t;
+}
+
+/* generate an integer binary operation */
+ST_FUNC void gen_opi(int op)
+{
+ int r, fr, opc, c;
+
+ switch(op) {
+ case '+':
+ case TOK_ADDC1: /* add with carry generation */
+ opc = 0;
+ gen_op8:
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ /* constant case */
+ vswap();
+ r = gv(RC_INT);
+ vswap();
+ c = vtop->c.i;
+ if (c == (char)c) {
+ /* generate inc and dec for smaller code */
+ if (c==1 && opc==0) {
+ o (0x40 | r); // inc
+ } else if (c==1 && opc==5) {
+ o (0x48 | r); // dec
+ } else {
+ o(0x83);
+ o(0xc0 | (opc << 3) | r);
+ g(c);
+ }
+ } else {
+ o(0x81);
+ oad(0xc0 | (opc << 3) | r, c);
+ }
+ } else {
+ gv2(RC_INT, RC_INT);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ o((opc << 3) | 0x01);
+ o(0xc0 + r + fr * 8);
+ }
+ vtop--;
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ }
+ break;
+ case '-':
+ case TOK_SUBC1: /* sub with carry generation */
+ opc = 5;
+ goto gen_op8;
+ case TOK_ADDC2: /* add with carry use */
+ opc = 2;
+ goto gen_op8;
+ case TOK_SUBC2: /* sub with carry use */
+ opc = 3;
+ goto gen_op8;
+ case '&':
+ opc = 4;
+ goto gen_op8;
+ case '^':
+ opc = 6;
+ goto gen_op8;
+ case '|':
+ opc = 1;
+ goto gen_op8;
+ case '*':
+ gv2(RC_INT, RC_INT);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ o(0xaf0f); /* imul fr, r */
+ o(0xc0 + fr + r * 8);
+ break;
+ case TOK_SHL:
+ opc = 4;
+ goto gen_shift;
+ case TOK_SHR:
+ opc = 5;
+ goto gen_shift;
+ case TOK_SAR:
+ opc = 7;
+ gen_shift:
+ opc = 0xc0 | (opc << 3);
+ if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
+ /* constant case */
+ vswap();
+ r = gv(RC_INT);
+ vswap();
+ c = vtop->c.i & 0x1f;
+ o(0xc1); /* shl/shr/sar $xxx, r */
+ o(opc | r);
+ g(c);
+ } else {
+ /* we generate the shift in ecx */
+ gv2(RC_INT, RC_ECX);
+ r = vtop[-1].r;
+ o(0xd3); /* shl/shr/sar %cl, r */
+ o(opc | r);
+ }
+ vtop--;
+ break;
+ case '/':
+ case TOK_UDIV:
+ case TOK_PDIV:
+ case '%':
+ case TOK_UMOD:
+ case TOK_UMULL:
+ /* first operand must be in eax */
+ /* XXX: need better constraint for second operand */
+ gv2(RC_EAX, RC_ECX);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ save_reg(TREG_EDX);
+ if (op == TOK_UMULL) {
+ o(0xf7); /* mul fr */
+ o(0xe0 + fr);
+ vtop->r2 = TREG_EDX;
+ r = TREG_EAX;
+ } else {
+ if (op == TOK_UDIV || op == TOK_UMOD) {
+ o(0xf7d231); /* xor %edx, %edx, div fr, %eax */
+ o(0xf0 + fr);
+ } else {
+ o(0xf799); /* cltd, idiv fr, %eax */
+ o(0xf8 + fr);
+ }
+ if (op == '%' || op == TOK_UMOD)
+ r = TREG_EDX;
+ else
+ r = TREG_EAX;
+ }
+ vtop->r = r;
+ break;
+ default:
+ opc = 7;
+ goto gen_op8;
+ }
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ two operands are guaranted to have the same floating point type */
+/* XXX: need to use ST1 too */
+ST_FUNC void gen_opf(int op)
+{
+ int a, ft, fc, swapped, r;
+
+ /* convert constants to memory references */
+ if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ vswap();
+ gv(RC_FLOAT);
+ vswap();
+ }
+ if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
+ gv(RC_FLOAT);
+
+ /* must put at least one value in the floating point register */
+ if ((vtop[-1].r & VT_LVAL) &&
+ (vtop[0].r & VT_LVAL)) {
+ vswap();
+ gv(RC_FLOAT);
+ vswap();
+ }
+ swapped = 0;
+ /* swap the stack if needed so that t1 is the register and t2 is
+ the memory reference */
+ if (vtop[-1].r & VT_LVAL) {
+ vswap();
+ swapped = 1;
+ }
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ /* load on stack second operand */
+ load(TREG_ST0, vtop);
+ save_reg(TREG_EAX); /* eax is used by FP comparison code */
+ if (op == TOK_GE || op == TOK_GT)
+ swapped = !swapped;
+ else if (op == TOK_EQ || op == TOK_NE)
+ swapped = 0;
+ if (swapped)
+ o(0xc9d9); /* fxch %st(1) */
+ if (op == TOK_EQ || op == TOK_NE)
+ o(0xe9da); /* fucompp */
+ else
+ o(0xd9de); /* fcompp */
+ o(0xe0df); /* fnstsw %ax */
+ if (op == TOK_EQ) {
+ o(0x45e480); /* and $0x45, %ah */
+ o(0x40fC80); /* cmp $0x40, %ah */
+ } else if (op == TOK_NE) {
+ o(0x45e480); /* and $0x45, %ah */
+ o(0x40f480); /* xor $0x40, %ah */
+ op = TOK_NE;
+ } else if (op == TOK_GE || op == TOK_LE) {
+ o(0x05c4f6); /* test $0x05, %ah */
+ op = TOK_EQ;
+ } else {
+ o(0x45c4f6); /* test $0x45, %ah */
+ op = TOK_EQ;
+ }
+ vtop--;
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ } else {
+ /* no memory reference possible for long double operations */
+ if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+ load(TREG_ST0, vtop);
+ swapped = !swapped;
+ }
+
+ switch(op) {
+ default:
+ case '+':
+ a = 0;
+ break;
+ case '-':
+ a = 4;
+ if (swapped)
+ a++;
+ break;
+ case '*':
+ a = 1;
+ break;
+ case '/':
+ a = 6;
+ if (swapped)
+ a++;
+ break;
+ }
+ ft = vtop->type.t;
+ fc = vtop->c.ul;
+ if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ o(0xde); /* fxxxp %st, %st(1) */
+ o(0xc1 + (a << 3));
+ } else {
+ /* if saved lvalue, then we must reload it */
+ r = vtop->r;
+ if ((r & VT_VALMASK) == VT_LLOCAL) {
+ SValue v1;
+ r = get_reg(RC_INT);
+ v1.type.t = VT_INT;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = fc;
+ load(r, &v1);
+ fc = 0;
+ }
+
+ if ((ft & VT_BTYPE) == VT_DOUBLE)
+ o(0xdc);
+ else
+ o(0xd8);
+ gen_modrm(a, r, vtop->sym, fc);
+ }
+ vtop--;
+ }
+}
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+ and 'long long' cases. */
+ST_FUNC void gen_cvt_itof(int t)
+{
+ save_reg(TREG_ST0);
+ gv(RC_INT);
+ if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ /* signed long long to float/double/long double (unsigned case
+ is handled generically) */
+ o(0x50 + vtop->r2); /* push r2 */
+ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+ o(0x242cdf); /* fildll (%esp) */
+ o(0x08c483); /* add $8, %esp */
+ } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
+ (VT_INT | VT_UNSIGNED)) {
+ /* unsigned int to float/double/long double */
+ o(0x6a); /* push $0 */
+ g(0x00);
+ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+ o(0x242cdf); /* fildll (%esp) */
+ o(0x08c483); /* add $8, %esp */
+ } else {
+ /* int to float/double/long double */
+ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+ o(0x2404db); /* fildl (%esp) */
+ o(0x04c483); /* add $4, %esp */
+ }
+ vtop->r = TREG_ST0;
+}
+
+/* convert fp to int 't' type */
+ST_FUNC void gen_cvt_ftoi(int t)
+{
+ #ifndef COMMIT_4ad186c5ef61_IS_FIXED
+ /* a good version but it takes a more time to execute */
+ gv(RC_FLOAT);
+ save_reg(TREG_EAX);
+ save_reg(TREG_EDX);
+ gen_static_call(TOK___tcc_cvt_ftol);
+ vtop->r = TREG_EAX; /* mark reg as used */
+ if (t == VT_LLONG)
+ vtop->r2 = TREG_EDX;
+ #else
+ /* a new version with a bug: t2a = 44100312 */
+ /*
+ #include<stdio.h>
+ int main() {
+ int t1 = 176401255;
+ float f = 0.25f;
+ int t2a = (int)(t1 * f); // must be 44100313
+ int t2b = (int)(t1 * (float)0.25f);
+ printf("t2a=%d t2b=%d \n",t2a,t2b);
+ return 0;
+ }
+ */
+ int bt = vtop->type.t & VT_BTYPE;
+ if (bt == VT_FLOAT)
+ vpush_global_sym(&func_old_type, TOK___fixsfdi);
+ else if (bt == VT_LDOUBLE)
+ vpush_global_sym(&func_old_type, TOK___fixxfdi);
+ else
+ vpush_global_sym(&func_old_type, TOK___fixdfdi);
+ vswap();
+ gfunc_call(1);
+ vpushi(0);
+ vtop->r = REG_IRET;
+ vtop->r2 = REG_LRET;
+ #endif
+}
+
+/* convert from one floating point type to another */
+ST_FUNC void gen_cvt_ftof(int t)
+{
+ /* all we have to do on i386 is to put the float in a register */
+ gv(RC_FLOAT);
+}
+
+/* computed goto support */
+ST_FUNC void ggoto(void)
+{
+ gcall_or_jmp(1);
+ vtop--;
+}
+
+/* bound check support functions */
+#ifdef CONFIG_TCC_BCHECK
+
+/* generate a bounded pointer addition */
+ST_FUNC void gen_bounded_ptr_add(void)
+{
+ /* prepare fast i386 function call (args in eax and edx) */
+ gv2(RC_EAX, RC_EDX);
+ /* save all temporary registers */
+ vtop -= 2;
+ save_regs(0);
+ /* do a fast function call */
+ gen_static_call(TOK___bound_ptr_add);
+ /* returned pointer is in eax */
+ vtop++;
+ vtop->r = TREG_EAX | VT_BOUNDED;
+ /* address of bounding function call point */
+ vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel));
+}
+
+/* patch pointer addition in vtop so that pointer dereferencing is
+ also tested */
+ST_FUNC void gen_bounded_ptr_deref(void)
+{
+ addr_t func;
+ addr_t size, align;
+ Elf32_Rel *rel;
+ Sym *sym;
+
+ size = 0;
+ /* XXX: put that code in generic part of tcc */
+ if (!is_float(vtop->type.t)) {
+ if (vtop->r & VT_LVAL_BYTE)
+ size = 1;
+ else if (vtop->r & VT_LVAL_SHORT)
+ size = 2;
+ }
+ if (!size)
+ size = type_size(&vtop->type, &align);
+ switch(size) {
+ case 1: func = TOK___bound_ptr_indir1; break;
+ case 2: func = TOK___bound_ptr_indir2; break;
+ case 4: func = TOK___bound_ptr_indir4; break;
+ case 8: func = TOK___bound_ptr_indir8; break;
+ case 12: func = TOK___bound_ptr_indir12; break;
+ case 16: func = TOK___bound_ptr_indir16; break;
+ default:
+ tcc_error("unhandled size when dereferencing bounded pointer");
+ func = 0;
+ break;
+ }
+
+ /* patch relocation */
+ /* XXX: find a better solution ? */
+ rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul);
+ sym = external_global_sym(func, &func_old_type, 0);
+ if (!sym->c)
+ put_extern_sym(sym, NULL, 0, 0);
+ rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info));
+}
+#endif
+
+/* Save the stack pointer onto the stack */
+ST_FUNC void gen_vla_sp_save(int addr) {
+ /* mov %esp,addr(%ebp)*/
+ o(0x89);
+ gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
+}
+
+/* Restore the SP from a location on the stack */
+ST_FUNC void gen_vla_sp_restore(int addr) {
+ o(0x8b);
+ gen_modrm(TREG_ESP, VT_LOCAL, NULL, addr);
+}
+
+/* Subtract from the stack pointer, and push the resulting value onto the stack */
+ST_FUNC void gen_vla_alloc(CType *type, int align) {
+#ifdef TCC_TARGET_PE
+ /* alloca does more than just adjust %rsp on Windows */
+ vpush_global_sym(&func_old_type, TOK_alloca);
+ vswap(); /* Move alloca ref past allocation size */
+ gfunc_call(1);
+#else
+ int r;
+ r = gv(RC_INT); /* allocation size */
+ /* sub r,%rsp */
+ o(0x2b);
+ o(0xe0 | r);
+ /* We align to 16 bytes rather than align */
+ /* and ~15, %esp */
+ o(0xf0e483);
+ vpop();
+#endif
+}
+
+/* end of X86 code generator */
+/*************************************************************/
+#endif
+/*************************************************************/
diff --git a/src/x86/i386-tok.h b/src/x86/i386-tok.h
new file mode 100644
index 0000000..b0ca3ed
--- /dev/null
+++ b/src/x86/i386-tok.h
@@ -0,0 +1,244 @@
+/* ------------------------------------------------------------------ */
+/* WARNING: relative order of tokens is important. */
+
+/* register */
+ DEF_ASM(al)
+ DEF_ASM(cl)
+ DEF_ASM(dl)
+ DEF_ASM(bl)
+ DEF_ASM(ah)
+ DEF_ASM(ch)
+ DEF_ASM(dh)
+ DEF_ASM(bh)
+ DEF_ASM(ax)
+ DEF_ASM(cx)
+ DEF_ASM(dx)
+ DEF_ASM(bx)
+ DEF_ASM(sp)
+ DEF_ASM(bp)
+ DEF_ASM(si)
+ DEF_ASM(di)
+ DEF_ASM(eax)
+ DEF_ASM(ecx)
+ DEF_ASM(edx)
+ DEF_ASM(ebx)
+ DEF_ASM(esp)
+ DEF_ASM(ebp)
+ DEF_ASM(esi)
+ DEF_ASM(edi)
+#ifdef TCC_TARGET_X86_64
+ DEF_ASM(rax)
+ DEF_ASM(rcx)
+ DEF_ASM(rdx)
+ DEF_ASM(rbx)
+ DEF_ASM(rsp)
+ DEF_ASM(rbp)
+ DEF_ASM(rsi)
+ DEF_ASM(rdi)
+#endif
+ DEF_ASM(mm0)
+ DEF_ASM(mm1)
+ DEF_ASM(mm2)
+ DEF_ASM(mm3)
+ DEF_ASM(mm4)
+ DEF_ASM(mm5)
+ DEF_ASM(mm6)
+ DEF_ASM(mm7)
+ DEF_ASM(xmm0)
+ DEF_ASM(xmm1)
+ DEF_ASM(xmm2)
+ DEF_ASM(xmm3)
+ DEF_ASM(xmm4)
+ DEF_ASM(xmm5)
+ DEF_ASM(xmm6)
+ DEF_ASM(xmm7)
+ DEF_ASM(cr0)
+ DEF_ASM(cr1)
+ DEF_ASM(cr2)
+ DEF_ASM(cr3)
+ DEF_ASM(cr4)
+ DEF_ASM(cr5)
+ DEF_ASM(cr6)
+ DEF_ASM(cr7)
+ DEF_ASM(tr0)
+ DEF_ASM(tr1)
+ DEF_ASM(tr2)
+ DEF_ASM(tr3)
+ DEF_ASM(tr4)
+ DEF_ASM(tr5)
+ DEF_ASM(tr6)
+ DEF_ASM(tr7)
+ DEF_ASM(db0)
+ DEF_ASM(db1)
+ DEF_ASM(db2)
+ DEF_ASM(db3)
+ DEF_ASM(db4)
+ DEF_ASM(db5)
+ DEF_ASM(db6)
+ DEF_ASM(db7)
+ DEF_ASM(dr0)
+ DEF_ASM(dr1)
+ DEF_ASM(dr2)
+ DEF_ASM(dr3)
+ DEF_ASM(dr4)
+ DEF_ASM(dr5)
+ DEF_ASM(dr6)
+ DEF_ASM(dr7)
+ DEF_ASM(es)
+ DEF_ASM(cs)
+ DEF_ASM(ss)
+ DEF_ASM(ds)
+ DEF_ASM(fs)
+ DEF_ASM(gs)
+ DEF_ASM(st)
+
+ /* generic two operands */
+ DEF_BWLX(mov)
+
+ DEF_BWLX(add)
+ DEF_BWLX(or)
+ DEF_BWLX(adc)
+ DEF_BWLX(sbb)
+ DEF_BWLX(and)
+ DEF_BWLX(sub)
+ DEF_BWLX(xor)
+ DEF_BWLX(cmp)
+
+ /* unary ops */
+ DEF_BWLX(inc)
+ DEF_BWLX(dec)
+ DEF_BWLX(not)
+ DEF_BWLX(neg)
+ DEF_BWLX(mul)
+ DEF_BWLX(imul)
+ DEF_BWLX(div)
+ DEF_BWLX(idiv)
+
+ DEF_BWLX(xchg)
+ DEF_BWLX(test)
+
+ /* shifts */
+ DEF_BWLX(rol)
+ DEF_BWLX(ror)
+ DEF_BWLX(rcl)
+ DEF_BWLX(rcr)
+ DEF_BWLX(shl)
+ DEF_BWLX(shr)
+ DEF_BWLX(sar)
+
+ DEF_ASM(shldw)
+ DEF_ASM(shldl)
+ DEF_ASM(shld)
+ DEF_ASM(shrdw)
+ DEF_ASM(shrdl)
+ DEF_ASM(shrd)
+
+ DEF_ASM(pushw)
+ DEF_ASM(pushl)
+#ifdef TCC_TARGET_X86_64
+ DEF_ASM(pushq)
+#endif
+ DEF_ASM(push)
+
+ DEF_ASM(popw)
+ DEF_ASM(popl)
+#ifdef TCC_TARGET_X86_64
+ DEF_ASM(popq)
+#endif
+ DEF_ASM(pop)
+
+ DEF_BWL(in)
+ DEF_BWL(out)
+
+ DEF_WL(movzb)
+ DEF_ASM(movzwl)
+ DEF_ASM(movsbw)
+ DEF_ASM(movsbl)
+ DEF_ASM(movswl)
+#ifdef TCC_TARGET_X86_64
+ DEF_ASM(movslq)
+#endif
+
+ DEF_WLX(lea)
+
+ DEF_ASM(les)
+ DEF_ASM(lds)
+ DEF_ASM(lss)
+ DEF_ASM(lfs)
+ DEF_ASM(lgs)
+
+ DEF_ASM(call)
+ DEF_ASM(jmp)
+ DEF_ASM(lcall)
+ DEF_ASM(ljmp)
+
+ DEF_ASMTEST(j,)
+
+ DEF_ASMTEST(set,)
+ DEF_ASMTEST(set,b)
+ DEF_ASMTEST(cmov,)
+
+ DEF_WLX(bsf)
+ DEF_WLX(bsr)
+ DEF_WLX(bt)
+ DEF_WLX(bts)
+ DEF_WLX(btr)
+ DEF_WLX(btc)
+
+ DEF_WLX(lsl)
+
+ /* generic FP ops */
+ DEF_FP(add)
+ DEF_FP(mul)
+
+ DEF_ASM(fcom)
+ DEF_ASM(fcom_1) /* non existent op, just to have a regular table */
+ DEF_FP1(com)
+
+ DEF_FP(comp)
+ DEF_FP(sub)
+ DEF_FP(subr)
+ DEF_FP(div)
+ DEF_FP(divr)
+
+ DEF_BWLX(xadd)
+ DEF_BWLX(cmpxchg)
+
+ /* string ops */
+ DEF_BWLX(cmps)
+ DEF_BWLX(scmp)
+ DEF_BWL(ins)
+ DEF_BWL(outs)
+ DEF_BWLX(lods)
+ DEF_BWLX(slod)
+ DEF_BWLX(movs)
+ DEF_BWLX(smov)
+ DEF_BWLX(scas)
+ DEF_BWLX(ssca)
+ DEF_BWLX(stos)
+ DEF_BWLX(ssto)
+
+ /* generic asm ops */
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode) DEF_ASM(name)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2)
+#ifdef TCC_TARGET_X86_64
+# include "x86_64-asm.h"
+#else
+# include "i386-asm.h"
+#endif
+
+#define ALT(x)
+#define DEF_ASM_OP0(name, opcode)
+#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name)
+#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name)
+#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name)
+#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name)
+#ifdef TCC_TARGET_X86_64
+# include "x86_64-asm.h"
+#else
+# include "i386-asm.h"
+#endif
diff --git a/src/x86/x86_64-asm.h b/src/x86/x86_64-asm.h
new file mode 100644
index 0000000..2f80e4b
--- /dev/null
+++ b/src/x86/x86_64-asm.h
@@ -0,0 +1,449 @@
+ DEF_ASM_OP0(clc, 0xf8) /* must be first OP0 */
+ DEF_ASM_OP0(cld, 0xfc)
+ DEF_ASM_OP0(cli, 0xfa)
+ DEF_ASM_OP0(clts, 0x0f06)
+ DEF_ASM_OP0(cmc, 0xf5)
+ DEF_ASM_OP0(lahf, 0x9f)
+ DEF_ASM_OP0(sahf, 0x9e)
+ DEF_ASM_OP0(pushfl, 0x9c)
+ DEF_ASM_OP0(popfl, 0x9d)
+ DEF_ASM_OP0(pushf, 0x9c)
+ DEF_ASM_OP0(popf, 0x9d)
+ DEF_ASM_OP0(stc, 0xf9)
+ DEF_ASM_OP0(std, 0xfd)
+ DEF_ASM_OP0(sti, 0xfb)
+ DEF_ASM_OP0(aaa, 0x37)
+ DEF_ASM_OP0(aas, 0x3f)
+ DEF_ASM_OP0(daa, 0x27)
+ DEF_ASM_OP0(das, 0x2f)
+ DEF_ASM_OP0(aad, 0xd50a)
+ DEF_ASM_OP0(aam, 0xd40a)
+ DEF_ASM_OP0(cbw, 0x6698)
+ DEF_ASM_OP0(cwd, 0x6699)
+ DEF_ASM_OP0(cwde, 0x98)
+ DEF_ASM_OP0(cdq, 0x99)
+ DEF_ASM_OP0(cbtw, 0x6698)
+ DEF_ASM_OP0(cwtl, 0x98)
+ DEF_ASM_OP0(cwtd, 0x6699)
+ DEF_ASM_OP0(cltd, 0x99)
+ DEF_ASM_OP0(cqto, 0x4899)
+ DEF_ASM_OP0(int3, 0xcc)
+ DEF_ASM_OP0(into, 0xce)
+ DEF_ASM_OP0(iret, 0xcf)
+ DEF_ASM_OP0(rsm, 0x0faa)
+ DEF_ASM_OP0(hlt, 0xf4)
+ DEF_ASM_OP0(wait, 0x9b)
+ DEF_ASM_OP0(nop, 0x90)
+ DEF_ASM_OP0(xlat, 0xd7)
+
+ /* strings */
+ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWLQ))
+
+ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWLQ))
+ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWLQ))
+
+ /* bits */
+
+ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA, OPT_REGW))
+
+ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW | OPT_EA))
+
+ /* prefixes */
+ DEF_ASM_OP0(lock, 0xf0)
+ DEF_ASM_OP0(rep, 0xf3)
+ DEF_ASM_OP0(repe, 0xf3)
+ DEF_ASM_OP0(repz, 0xf3)
+ DEF_ASM_OP0(repne, 0xf2)
+ DEF_ASM_OP0(repnz, 0xf2)
+
+ DEF_ASM_OP0(invd, 0x0f08)
+ DEF_ASM_OP0(wbinvd, 0x0f09)
+ DEF_ASM_OP0(cpuid, 0x0fa2)
+ DEF_ASM_OP0(wrmsr, 0x0f30)
+ DEF_ASM_OP0(rdtsc, 0x0f31)
+ DEF_ASM_OP0(rdmsr, 0x0f32)
+ DEF_ASM_OP0(rdpmc, 0x0f33)
+ DEF_ASM_OP0(ud2, 0x0f0b)
+
+ /* NOTE: we took the same order as gas opcode definition order */
+ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWLQ, OPT_ADDR, OPT_EAX))
+ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWLQ, OPT_EAX, OPT_ADDR))
+ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWLQ, OPT_IM, OPT_REG))
+ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWLQ, OPT_IM, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WLQ, OPT_SEG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WLQ, OPT_EA | OPT_REG, OPT_SEG))
+
+ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WLQ, OPT_CR, OPT_REG64))
+ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WLQ, OPT_DB, OPT_REG64))
+ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WLQ, OPT_TR, OPT_REG64))
+ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_CR))
+ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_DB))
+ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WLQ, OPT_REG64, OPT_TR))
+
+ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16))
+ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(movslq, 0x4863, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32))
+
+ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WLQ, OPT_REG64))
+ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WLQ, OPT_REG64 | OPT_EA))
+ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WLQ, OPT_IM32))
+ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WLQ, OPT_SEG))
+ DEF_ASM_OP1(pushb, 0x6a, 0, OPC_B, OPT_IM8S)
+
+ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WLQ, OPT_REGW))
+ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WLQ, OPT_REGW | OPT_EA))
+ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WLQ, OPT_SEG))
+
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_REG, OPT_EAX))
+ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WLQ, OPT_EAX, OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+
+ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX))
+ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8))
+ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8))
+ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX))
+ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX))
+
+ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WLQ, OPT_EA, OPT_REG))
+
+ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32))
+
+ /* arith */
+ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */
+ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWLQ, OPT_IMNO64, OPT_EAX))
+ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_EA | OPT_REG, OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWLQ, OPT_REG, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWLQ, OPT_IMNO64, OPT_EAX))
+ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWLQ, OPT_IMNO64, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WLQ, OPT_REGW))
+ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WLQ, OPT_REGW))
+ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+
+ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WLQ, OPT_REG | OPT_EA, OPT_REG))
+ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WLQ, OPT_IM8S, OPT_REGW))
+ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW))
+ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WLQ, OPT_IMW, OPT_REGW))
+
+ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX))
+ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA))
+ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWLQ, OPT_REG | OPT_EA, OPT_EAX))
+
+ /* shifts */
+ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG))
+ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWLQ | OPC_SHIFT, OPT_EA | OPT_REG))
+
+ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WLQ, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW))
+ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WLQ, OPT_REGW, OPT_EA | OPT_REGW))
+
+ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR))
+ALT(DEF_ASM_OP1(jmp, 0xff, 0, OPC_JMP | OPC_WL, OPT_REGW))
+ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR))
+
+ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA))
+ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA))
+
+ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8))
+ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+ALT(DEF_ASM_OP1(setob, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA))
+ DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8)
+ DEF_ASM_OP0(leave, 0xc9)
+ DEF_ASM_OP0(ret, 0xc3)
+ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16))
+ DEF_ASM_OP0(lret, 0xcb)
+ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16))
+
+ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR))
+ DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR)
+ DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR)
+
+ /* float */
+ /* specific fcomp handling */
+ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0))
+
+ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0))
+ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST))
+ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH))
+ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA))
+
+ DEF_ASM_OP0(fucompp, 0xdae9)
+ DEF_ASM_OP0(ftst, 0xd9e4)
+ DEF_ASM_OP0(fxam, 0xd9e5)
+ DEF_ASM_OP0(fld1, 0xd9e8)
+ DEF_ASM_OP0(fldl2t, 0xd9e9)
+ DEF_ASM_OP0(fldl2e, 0xd9ea)
+ DEF_ASM_OP0(fldpi, 0xd9eb)
+ DEF_ASM_OP0(fldlg2, 0xd9ec)
+ DEF_ASM_OP0(fldln2, 0xd9ed)
+ DEF_ASM_OP0(fldz, 0xd9ee)
+
+ DEF_ASM_OP0(f2xm1, 0xd9f0)
+ DEF_ASM_OP0(fyl2x, 0xd9f1)
+ DEF_ASM_OP0(fptan, 0xd9f2)
+ DEF_ASM_OP0(fpatan, 0xd9f3)
+ DEF_ASM_OP0(fxtract, 0xd9f4)
+ DEF_ASM_OP0(fprem1, 0xd9f5)
+ DEF_ASM_OP0(fdecstp, 0xd9f6)
+ DEF_ASM_OP0(fincstp, 0xd9f7)
+ DEF_ASM_OP0(fprem, 0xd9f8)
+ DEF_ASM_OP0(fyl2xp1, 0xd9f9)
+ DEF_ASM_OP0(fsqrt, 0xd9fa)
+ DEF_ASM_OP0(fsincos, 0xd9fb)
+ DEF_ASM_OP0(frndint, 0xd9fc)
+ DEF_ASM_OP0(fscale, 0xd9fd)
+ DEF_ASM_OP0(fsin, 0xd9fe)
+ DEF_ASM_OP0(fcos, 0xd9ff)
+ DEF_ASM_OP0(fchs, 0xd9e0)
+ DEF_ASM_OP0(fabs, 0xd9e1)
+ DEF_ASM_OP0(fninit, 0xdbe3)
+ DEF_ASM_OP0(fnclex, 0xdbe2)
+ DEF_ASM_OP0(fnop, 0xd9d0)
+ DEF_ASM_OP0(fwait, 0x9b)
+
+ /* fp load */
+ DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA))
+ DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA)
+ DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA)
+
+ /* fp store */
+ DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA)
+ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA))
+ DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA)
+
+ DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST)
+ DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA)
+
+ /* exchange */
+ DEF_ASM_OP0(fxch, 0xd9c9)
+ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST))
+
+ /* misc FPU */
+ DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST )
+
+ DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT)
+ DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP0(fnstsw, 0xdfe0)
+ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX ))
+ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA ))
+ DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX )
+ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT))
+ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ))
+ DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT)
+ DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA )
+ DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST )
+ DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA )
+ DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA )
+
+ /* segments */
+ DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA)
+ DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32)
+ DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG)
+ DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG)
+ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG))
+ DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG)
+ DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA)
+ DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA)
+ DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA)
+ DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA)
+
+ /* 486 */
+ DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 )
+ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA ))
+ DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA )
+
+ DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA)
+ DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA)
+
+ /* pentium */
+ DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA )
+
+ /* pentium pro */
+ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovno, 0x0f41, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovc, 0x0f42, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovnc, 0x0f43, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovz, 0x0f44, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovnz, 0x0f45, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmovna, 0x0f46, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+ALT(DEF_ASM_OP2(cmova, 0x0f47, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32))
+
+ DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+ DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 )
+ DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 )
+
+ /* mmx */
+ DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */
+ DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX )
+ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 ))
+ALT(DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ))
+ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX ))
+ DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX ))
+ DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX )
+ DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) /* must be last !OP0 */
+
+#undef ALT
+#undef DEF_ASM_OP0
+#undef DEF_ASM_OP0L
+#undef DEF_ASM_OP1
+#undef DEF_ASM_OP2
+#undef DEF_ASM_OP3
diff --git a/src/x86/x86_64-gen.c b/src/x86/x86_64-gen.c
new file mode 100644
index 0000000..0ce5846
--- /dev/null
+++ b/src/x86/x86_64-gen.c
@@ -0,0 +1,2360 @@
+/*
+ * x86-64 code generator for TCC
+ *
+ * Copyright (c) 2008 Shinichiro Hamaji
+ *
+ * Based on i386-gen.c by Fabrice Bellard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef TARGET_DEFS_ONLY
+
+/* number of available registers */
+#define NB_REGS 25
+#define NB_ASM_REGS 8
+#define REG_ARGS_MAX 2 /* at most 2 registers used for each argument */
+
+#ifdef TCC_TARGET_PE
+typedef int RegArgs;
+#else
+/* This struct stores the struct offsets at which %rax, %rdx, %xmm0, and
+ * %xmm1 are to be stored.
+ *
+ * struct { long long l; double x; }: ireg = { 0, -1 } freg = { 8, -1 }
+ * struct { double x; long long l; }: ireg = { 8, -1 } freg = { 0, -1 }
+ * struct { long long l; long long l2; }: ireg = { 0, 8 } freg = { -1, -1 }
+ * struct { double x; double x2; }: ireg = { -1, -1 } freg = { 0, 8 }
+ */
+typedef struct {
+ int ireg[REG_ARGS_MAX];
+ int freg[REG_ARGS_MAX];
+} RegArgs;
+#endif
+
+/* a register can belong to several classes. The classes must be
+ sorted from more general to more precise (see gv2() code which does
+ assumptions on it). */
+#define RC_INT 0x0001 /* generic integer register */
+#define RC_FLOAT 0x0002 /* generic float register */
+#define RC_RAX 0x0004
+#define RC_RCX 0x0008
+#define RC_RDX 0x0010
+#define RC_ST0 0x0080 /* only for long double */
+#define RC_R8 0x0100
+#define RC_R9 0x0200
+#define RC_R10 0x0400
+#define RC_R11 0x0800
+#define RC_XMM0 0x1000
+#define RC_XMM1 0x2000
+#define RC_XMM2 0x4000
+#define RC_XMM3 0x8000
+#define RC_XMM4 0x10000
+#define RC_XMM5 0x20000
+#define RC_XMM6 0x40000
+#define RC_XMM7 0x80000
+#define RC_IRET RC_RAX /* function return: integer register */
+#define RC_LRET RC_RDX /* function return: second integer register */
+#define RC_FRET RC_XMM0 /* function return: float register */
+#define RC_QRET RC_XMM1 /* function return: second float register */
+
+/* pretty names for the registers */
+enum {
+ TREG_RAX = 0,
+ TREG_RCX = 1,
+ TREG_RDX = 2,
+ TREG_RSP = 4,
+ TREG_RSI = 6,
+ TREG_RDI = 7,
+
+ TREG_R8 = 8,
+ TREG_R9 = 9,
+ TREG_R10 = 10,
+ TREG_R11 = 11,
+
+ TREG_XMM0 = 16,
+ TREG_XMM1 = 17,
+ TREG_XMM2 = 18,
+ TREG_XMM3 = 19,
+ TREG_XMM4 = 20,
+ TREG_XMM5 = 21,
+ TREG_XMM6 = 22,
+ TREG_XMM7 = 23,
+
+ TREG_ST0 = 24,
+
+ TREG_MEM = 0x20,
+};
+
+#define REX_BASE(reg) (((reg) >> 3) & 1)
+#define REG_VALUE(reg) ((reg) & 7)
+
+/* return registers for function */
+#define REG_IRET TREG_RAX /* single word int return register */
+#define REG_LRET TREG_RDX /* second word return register (for long long) */
+#define REG_FRET TREG_XMM0 /* float return register */
+#define REG_QRET TREG_XMM1 /* second float return register */
+
+/* defined if function parameters must be evaluated in reverse order */
+#define INVERT_FUNC_PARAMS
+
+/* pointer size, in bytes */
+#define PTR_SIZE 8
+
+/* long double size and alignment, in bytes */
+#define LDOUBLE_SIZE 16
+#define LDOUBLE_ALIGN 16
+/* maximum alignment (for aligned attribute support) */
+#define MAX_ALIGN 16
+
+/******************************************************/
+/* ELF defines */
+
+#define EM_TCC_TARGET EM_X86_64
+
+/* relocation type for 32 bit data relocation */
+#define R_DATA_32 R_X86_64_32
+#define R_DATA_PTR R_X86_64_64
+#define R_JMP_SLOT R_X86_64_JUMP_SLOT
+#define R_COPY R_X86_64_COPY
+
+#define ELF_START_ADDR 0x400000
+#define ELF_PAGE_SIZE 0x200000
+
+/******************************************************/
+#else /* ! TARGET_DEFS_ONLY */
+/******************************************************/
+#include "../tcc.h"
+#include <assert.h>
+
+ST_DATA const int reg_classes[NB_REGS] = {
+ /* eax */ RC_INT | RC_RAX,
+ /* ecx */ RC_INT | RC_RCX,
+ /* edx */ RC_INT | RC_RDX,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ RC_R8,
+ RC_R9,
+ RC_R10,
+ RC_R11,
+ 0,
+ 0,
+ 0,
+ 0,
+ /* xmm0 */ RC_FLOAT | RC_XMM0,
+ /* xmm1 */ RC_FLOAT | RC_XMM1,
+ /* xmm2 */ RC_FLOAT | RC_XMM2,
+ /* xmm3 */ RC_FLOAT | RC_XMM3,
+ /* xmm4 */ RC_FLOAT | RC_XMM4,
+ /* xmm5 */ RC_FLOAT | RC_XMM5,
+ /* xmm6 an xmm7 are included so gv() can be used on them,
+ but they are not tagged with RC_FLOAT because they are
+ callee saved on Windows */
+ RC_XMM6,
+ RC_XMM7,
+ /* st0 */ RC_ST0
+};
+
+static unsigned long func_sub_sp_offset;
+static int func_ret_sub;
+
+/* XXX: make it faster ? */
+void g(int c)
+{
+ int ind1;
+ ind1 = ind + 1;
+ if (ind1 > cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ cur_text_section->data[ind] = c;
+ ind = ind1;
+}
+
+void o(unsigned int c)
+{
+ while (c) {
+ g(c);
+ c = c >> 8;
+ }
+}
+
+void gen_le16(int v)
+{
+ g(v);
+ g(v >> 8);
+}
+
+void gen_le32(int c)
+{
+ g(c);
+ g(c >> 8);
+ g(c >> 16);
+ g(c >> 24);
+}
+
+void gen_le64(int64_t c)
+{
+ g(c);
+ g(c >> 8);
+ g(c >> 16);
+ g(c >> 24);
+ g(c >> 32);
+ g(c >> 40);
+ g(c >> 48);
+ g(c >> 56);
+}
+
+void orex(int ll, int r, int r2, int b)
+{
+ if ((r & VT_VALMASK) >= VT_CONST)
+ r = 0;
+ if ((r2 & VT_VALMASK) >= VT_CONST)
+ r2 = 0;
+ if (ll || REX_BASE(r) || REX_BASE(r2))
+ o(0x40 | REX_BASE(r) | (REX_BASE(r2) << 2) | (ll << 3));
+ o(b);
+}
+
+/* output a symbol and patch all calls to it */
+void gsym_addr(int t, int a)
+{
+ int n, *ptr;
+ while (t) {
+ ptr = (int *)(cur_text_section->data + t);
+ n = *ptr; /* next value */
+ *ptr = a - t - 4;
+ t = n;
+ }
+}
+
+void gsym(int t)
+{
+ gsym_addr(t, ind);
+}
+
+/* psym is used to put an instruction with a data field which is a
+ reference to a symbol. It is in fact the same as oad ! */
+#define psym oad
+
+static int is64_type(int t)
+{
+ return ((t & VT_BTYPE) == VT_PTR ||
+ (t & VT_BTYPE) == VT_FUNC ||
+ (t & VT_BTYPE) == VT_LLONG);
+}
+
+/* instruction + 4 bytes data. Return the address of the data */
+ST_FUNC int oad(int c, int s)
+{
+ int ind1;
+
+ o(c);
+ ind1 = ind + 4;
+ if (ind1 > cur_text_section->data_allocated)
+ section_realloc(cur_text_section, ind1);
+ *(int *)(cur_text_section->data + ind) = s;
+ s = ind;
+ ind = ind1;
+ return s;
+}
+
+ST_FUNC void gen_addr32(int r, Sym *sym, int c)
+{
+ if (r & VT_SYM)
+ greloc(cur_text_section, sym, ind, R_X86_64_32);
+ gen_le32(c);
+}
+
+/* output constant with relocation if 'r & VT_SYM' is true */
+ST_FUNC void gen_addr64(int r, Sym *sym, int64_t c)
+{
+ if (r & VT_SYM)
+ greloc(cur_text_section, sym, ind, R_X86_64_64);
+ gen_le64(c);
+}
+
+/* output constant with relocation if 'r & VT_SYM' is true */
+ST_FUNC void gen_addrpc32(int r, Sym *sym, int c)
+{
+ if (r & VT_SYM)
+ greloc(cur_text_section, sym, ind, R_X86_64_PC32);
+ gen_le32(c-4);
+}
+
+/* output got address with relocation */
+static void gen_gotpcrel(int r, Sym *sym, int c)
+{
+#ifndef TCC_TARGET_PE
+ Section *sr;
+ ElfW(Rela) *rel;
+ greloc(cur_text_section, sym, ind, R_X86_64_GOTPCREL);
+ sr = cur_text_section->reloc;
+ rel = (ElfW(Rela) *)(sr->data + sr->data_offset - sizeof(ElfW(Rela)));
+ rel->r_addend = -4;
+#else
+ tcc_error("internal error: no GOT on PE: %s %x %x | %02x %02x %02x\n",
+ get_tok_str(sym->v, NULL), c, r,
+ cur_text_section->data[ind-3],
+ cur_text_section->data[ind-2],
+ cur_text_section->data[ind-1]
+ );
+ greloc(cur_text_section, sym, ind, R_X86_64_PC32);
+#endif
+ gen_le32(0);
+ if (c) {
+ /* we use add c, %xxx for displacement */
+ orex(1, r, 0, 0x81);
+ o(0xc0 + REG_VALUE(r));
+ gen_le32(c);
+ }
+}
+
+static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got)
+{
+ op_reg = REG_VALUE(op_reg) << 3;
+ if ((r & VT_VALMASK) == VT_CONST) {
+ /* constant memory reference */
+ o(0x05 | op_reg);
+ if (is_got) {
+ gen_gotpcrel(r, sym, c);
+ } else {
+ gen_addrpc32(r, sym, c);
+ }
+ } else if ((r & VT_VALMASK) == VT_LOCAL) {
+ /* currently, we use only ebp as base */
+ if (c == (char)c) {
+ /* short reference */
+ o(0x45 | op_reg);
+ g(c);
+ } else {
+ oad(0x85 | op_reg, c);
+ }
+ } else if ((r & VT_VALMASK) >= TREG_MEM) {
+ if (c) {
+ g(0x80 | op_reg | REG_VALUE(r));
+ gen_le32(c);
+ } else {
+ g(0x00 | op_reg | REG_VALUE(r));
+ }
+ } else {
+ g(0x00 | op_reg | REG_VALUE(r));
+ }
+}
+
+/* generate a modrm reference. 'op_reg' contains the addtionnal 3
+ opcode bits */
+static void gen_modrm(int op_reg, int r, Sym *sym, int c)
+{
+ gen_modrm_impl(op_reg, r, sym, c, 0);
+}
+
+/* generate a modrm reference. 'op_reg' contains the addtionnal 3
+ opcode bits */
+static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c)
+{
+ int is_got;
+ is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC);
+ orex(1, r, op_reg, opcode);
+ gen_modrm_impl(op_reg, r, sym, c, is_got);
+}
+
+
+/* load 'r' from value 'sv' */
+void load(int r, SValue *sv)
+{
+ int v, t, ft, fc, fr;
+ SValue v1;
+
+#ifdef TCC_TARGET_PE
+ SValue v2;
+ sv = pe_getimport(sv, &v2);
+#endif
+
+ fr = sv->r;
+ ft = sv->type.t & ~VT_DEFSIGN;
+ fc = sv->c.ul;
+
+#ifndef TCC_TARGET_PE
+ /* we use indirect access via got */
+ if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) &&
+ (fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) {
+ /* use the result register as a temporal register */
+ int tr = r | TREG_MEM;
+ if (is_float(ft)) {
+ /* we cannot use float registers as a temporal register */
+ tr = get_reg(RC_INT) | TREG_MEM;
+ }
+ gen_modrm64(0x8b, tr, fr, sv->sym, 0);
+
+ /* load from the temporal register */
+ fr = tr | VT_LVAL;
+ }
+#endif
+
+ v = fr & VT_VALMASK;
+ if (fr & VT_LVAL) {
+ int b, ll;
+ if (v == VT_LLOCAL) {
+ v1.type.t = VT_PTR;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = fc;
+ fr = r;
+ if (!(reg_classes[fr] & (RC_INT|RC_R11)))
+ fr = get_reg(RC_INT);
+ load(fr, &v1);
+ }
+ ll = 0;
+ if ((ft & VT_BTYPE) == VT_FLOAT) {
+ b = 0x6e0f66;
+ r = REG_VALUE(r); /* movd */
+ } else if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ b = 0x7e0ff3; /* movq */
+ r = REG_VALUE(r);
+ } else if ((ft & VT_BTYPE) == VT_LDOUBLE) {
+ b = 0xdb, r = 5; /* fldt */
+ } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) {
+ b = 0xbe0f; /* movsbl */
+ } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) {
+ b = 0xb60f; /* movzbl */
+ } else if ((ft & VT_TYPE) == VT_SHORT) {
+ b = 0xbf0f; /* movswl */
+ } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) {
+ b = 0xb70f; /* movzwl */
+ } else {
+ assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG)
+ || ((ft & VT_BTYPE) == VT_PTR) || ((ft & VT_BTYPE) == VT_ENUM)
+ || ((ft & VT_BTYPE) == VT_FUNC));
+ ll = is64_type(ft);
+ b = 0x8b;
+ }
+ if (ll) {
+ gen_modrm64(b, r, fr, sv->sym, fc);
+ } else {
+ orex(ll, fr, r, b);
+ gen_modrm(r, fr, sv->sym, fc);
+ }
+ } else {
+ if (v == VT_CONST) {
+ if (fr & VT_SYM) {
+#ifdef TCC_TARGET_PE
+ orex(1,0,r,0x8d);
+ o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */
+ gen_addrpc32(fr, sv->sym, fc);
+#else
+ if (sv->sym->type.t & VT_STATIC) {
+ orex(1,0,r,0x8d);
+ o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */
+ gen_addrpc32(fr, sv->sym, fc);
+ } else {
+ orex(1,0,r,0x8b);
+ o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */
+ gen_gotpcrel(r, sv->sym, fc);
+ }
+#endif
+ } else if (is64_type(ft)) {
+ orex(1,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */
+ gen_le64(sv->c.ull);
+ } else {
+ orex(0,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */
+ gen_le32(fc);
+ }
+ } else if (v == VT_LOCAL) {
+ orex(1,0,r,0x8d); /* lea xxx(%ebp), r */
+ gen_modrm(r, VT_LOCAL, sv->sym, fc);
+ } else if (v == VT_CMP) {
+ orex(0,r,0,0);
+ if ((fc & ~0x100) != TOK_NE)
+ oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */
+ else
+ oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */
+ if (fc & 0x100) {
+ /* This was a float compare. If the parity bit is
+ * set the result was unordered, meaning false for everything
+ * except TOK_NE, and true for TOK_NE. */
+ fc &= ~0x100;
+ o(0x037a + (REX_BASE(r) << 8));
+ }
+ orex(0,r,0, 0x0f); /* setxx %br */
+ o(fc);
+ o(0xc0 + REG_VALUE(r));
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ t = v & 1;
+ orex(0,r,0,0);
+ oad(0xb8 + REG_VALUE(r), t); /* mov $1, r */
+ o(0x05eb + (REX_BASE(r) << 8)); /* jmp after */
+ gsym(fc);
+ orex(0,r,0,0);
+ oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */
+ } else if (v != r) {
+ if ((r >= TREG_XMM0) && (r <= TREG_XMM7)) {
+ if (v == TREG_ST0) {
+ /* gen_cvt_ftof(VT_DOUBLE); */
+ o(0xf0245cdd); /* fstpl -0x10(%rsp) */
+ /* movsd -0x10(%rsp),%xmmN */
+ o(0x100ff2);
+ o(0x44 + REG_VALUE(r)*8); /* %xmmN */
+ o(0xf024);
+ } else {
+ assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
+ if ((ft & VT_BTYPE) == VT_FLOAT) {
+ o(0x100ff3);
+ } else {
+ assert((ft & VT_BTYPE) == VT_DOUBLE);
+ o(0x100ff2);
+ }
+ o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8);
+ }
+ } else if (r == TREG_ST0) {
+ assert((v >= TREG_XMM0) && (v <= TREG_XMM7));
+ /* gen_cvt_ftof(VT_LDOUBLE); */
+ /* movsd %xmmN,-0x10(%rsp) */
+ o(0x110ff2);
+ o(0x44 + REG_VALUE(r)*8); /* %xmmN */
+ o(0xf024);
+ o(0xf02444dd); /* fldl -0x10(%rsp) */
+ } else {
+ orex(1,r,v, 0x89);
+ o(0xc0 + REG_VALUE(r) + REG_VALUE(v) * 8); /* mov v, r */
+ }
+ }
+ }
+}
+
+/* store register 'r' in lvalue 'v' */
+void store(int r, SValue *v)
+{
+ int fr, bt, ft, fc;
+ int op64 = 0;
+ /* store the REX prefix in this variable when PIC is enabled */
+ int pic = 0;
+
+#ifdef TCC_TARGET_PE
+ SValue v2;
+ v = pe_getimport(v, &v2);
+#endif
+
+ ft = v->type.t;
+ fc = v->c.ul;
+ fr = v->r & VT_VALMASK;
+ bt = ft & VT_BTYPE;
+
+#ifndef TCC_TARGET_PE
+ /* we need to access the variable via got */
+ if (fr == VT_CONST && (v->r & VT_SYM)) {
+ /* mov xx(%rip), %r11 */
+ o(0x1d8b4c);
+ gen_gotpcrel(TREG_R11, v->sym, v->c.ul);
+ pic = is64_type(bt) ? 0x49 : 0x41;
+ }
+#endif
+
+ /* XXX: incorrect if float reg to reg */
+ if (bt == VT_FLOAT) {
+ o(0x66);
+ o(pic);
+ o(0x7e0f); /* movd */
+ r = REG_VALUE(r);
+ } else if (bt == VT_DOUBLE) {
+ o(0x66);
+ o(pic);
+ o(0xd60f); /* movq */
+ r = REG_VALUE(r);
+ } else if (bt == VT_LDOUBLE) {
+ o(0xc0d9); /* fld %st(0) */
+ o(pic);
+ o(0xdb); /* fstpt */
+ r = 7;
+ } else {
+ if (bt == VT_SHORT)
+ o(0x66);
+ o(pic);
+ if (bt == VT_BYTE || bt == VT_BOOL)
+ orex(0, 0, r, 0x88);
+ else if (is64_type(bt))
+ op64 = 0x89;
+ else
+ orex(0, 0, r, 0x89);
+ }
+ if (pic) {
+ /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */
+ if (op64)
+ o(op64);
+ o(3 + (r << 3));
+ } else if (op64) {
+ if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) {
+ gen_modrm64(op64, r, v->r, v->sym, fc);
+ } else if (fr != r) {
+ /* XXX: don't we really come here? */
+ abort();
+ o(0xc0 + fr + r * 8); /* mov r, fr */
+ }
+ } else {
+ if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) {
+ gen_modrm(r, v->r, v->sym, fc);
+ } else if (fr != r) {
+ /* XXX: don't we really come here? */
+ abort();
+ o(0xc0 + fr + r * 8); /* mov r, fr */
+ }
+ }
+}
+
+/* 'is_jmp' is '1' if it is a jump */
+static void gcall_or_jmp(int is_jmp)
+{
+ int r;
+ if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST &&
+ ((vtop->r & VT_SYM) || (vtop->c.ll-4) == (int)(vtop->c.ll-4))) {
+ /* constant case */
+ if (vtop->r & VT_SYM) {
+ /* relocation case */
+#ifdef TCC_TARGET_PE
+ greloc(cur_text_section, vtop->sym, ind + 1, R_X86_64_PC32);
+#else
+ greloc(cur_text_section, vtop->sym, ind + 1, R_X86_64_PLT32);
+#endif
+ } else {
+ /* put an empty PC32 relocation */
+ put_elf_reloc(symtab_section, cur_text_section,
+ ind + 1, R_X86_64_PC32, 0);
+ }
+ oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */
+ } else {
+ /* otherwise, indirect call */
+ r = TREG_R11;
+ load(r, vtop);
+ o(0x41); /* REX */
+ o(0xff); /* call/jmp *r */
+ o(0xd0 + REG_VALUE(r) + (is_jmp << 4));
+ }
+}
+
+#if defined(CONFIG_TCC_BCHECK)
+#ifndef TCC_TARGET_PE
+static addr_t func_bound_offset;
+static unsigned long func_bound_ind;
+#endif
+
+static void gen_static_call(int v)
+{
+ Sym *sym = external_global_sym(v, &func_old_type, 0);
+ oad(0xe8, -4);
+ greloc(cur_text_section, sym, ind-4, R_X86_64_PC32);
+}
+
+/* generate a bounded pointer addition */
+ST_FUNC void gen_bounded_ptr_add(void)
+{
+ /* save all temporary registers */
+ save_regs(0);
+
+ /* prepare fast x86_64 function call */
+ gv(RC_RAX);
+ o(0xc68948); // mov %rax,%rsi ## second arg in %rsi, this must be size
+ vtop--;
+
+ gv(RC_RAX);
+ o(0xc78948); // mov %rax,%rdi ## first arg in %rdi, this must be ptr
+ vtop--;
+
+ /* do a fast function call */
+ gen_static_call(TOK___bound_ptr_add);
+
+ /* returned pointer is in rax */
+ vtop++;
+ vtop->r = TREG_RAX | VT_BOUNDED;
+
+
+ /* relocation offset of the bounding function call point */
+ vtop->c.ull = (cur_text_section->reloc->data_offset - sizeof(ElfW(Rela)));
+}
+
+/* patch pointer addition in vtop so that pointer dereferencing is
+ also tested */
+ST_FUNC void gen_bounded_ptr_deref(void)
+{
+ addr_t func;
+ int size, align;
+ ElfW(Rela) *rel;
+ Sym *sym;
+
+ size = 0;
+ /* XXX: put that code in generic part of tcc */
+ if (!is_float(vtop->type.t)) {
+ if (vtop->r & VT_LVAL_BYTE)
+ size = 1;
+ else if (vtop->r & VT_LVAL_SHORT)
+ size = 2;
+ }
+ if (!size)
+ size = type_size(&vtop->type, &align);
+ switch(size) {
+ case 1: func = TOK___bound_ptr_indir1; break;
+ case 2: func = TOK___bound_ptr_indir2; break;
+ case 4: func = TOK___bound_ptr_indir4; break;
+ case 8: func = TOK___bound_ptr_indir8; break;
+ case 12: func = TOK___bound_ptr_indir12; break;
+ case 16: func = TOK___bound_ptr_indir16; break;
+ default:
+ tcc_error("unhandled size when dereferencing bounded pointer");
+ func = 0;
+ break;
+ }
+
+ sym = external_global_sym(func, &func_old_type, 0);
+ if (!sym->c)
+ put_extern_sym(sym, NULL, 0, 0);
+
+ /* patch relocation */
+ /* XXX: find a better solution ? */
+
+ rel = (ElfW(Rela) *)(cur_text_section->reloc->data + vtop->c.ull);
+ rel->r_info = ELF64_R_INFO(sym->c, ELF64_R_TYPE(rel->r_info));
+}
+#endif
+
+#ifdef TCC_TARGET_PE
+
+#define REGN 4
+static const uint8_t arg_regs[REGN] = {
+ TREG_RCX, TREG_RDX, TREG_R8, TREG_R9
+};
+
+/* Prepare arguments in R10 and R11 rather than RCX and RDX
+ because gv() will not ever use these */
+static int arg_prepare_reg(int idx) {
+ if (idx == 0 || idx == 1)
+ /* idx=0: r10, idx=1: r11 */
+ return idx + 10;
+ else
+ return arg_regs[idx];
+}
+
+static int func_scratch;
+
+/* Generate function call. The function address is pushed first, then
+ all the parameters in call order. This functions pops all the
+ parameters and the function address. */
+
+void gen_offs_sp(int b, int r, int d)
+{
+ orex(1,0,r & 0x100 ? 0 : r, b);
+ if (d == (char)d) {
+ o(0x2444 | (REG_VALUE(r) << 3));
+ g(d);
+ } else {
+ o(0x2484 | (REG_VALUE(r) << 3));
+ gen_le32(d);
+ }
+}
+
+ST_FUNC int regargs_nregs(RegArgs *args)
+{
+ return *args;
+}
+
+/* Return the number of registers needed to return the struct, or 0 if
+ returning via struct pointer. */
+ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args)
+{
+ int size, align;
+ *regsize = 8;
+ *ret_align = 1; // Never have to re-align return values for x86-64
+ size = type_size(vt, &align);
+ ret->ref = NULL;
+ if (size > 8) {
+ *args = 0;
+ } else if (size > 4) {
+ ret->t = VT_LLONG;
+ *args = 1;
+ } else if (size > 2) {
+ ret->t = VT_INT;
+ *args = 1;
+ } else if (size > 1) {
+ ret->t = VT_SHORT;
+ *args = 1;
+ } else {
+ ret->t = VT_BYTE;
+ *args = 1;
+ }
+
+ return *args != 0;
+}
+
+static int is_sse_float(int t) {
+ int bt;
+ bt = t & VT_BTYPE;
+ return bt == VT_DOUBLE || bt == VT_FLOAT;
+}
+
+int gfunc_arg_size(CType *type) {
+ int align;
+ if (type->t & (VT_ARRAY|VT_BITFIELD))
+ return 8;
+ return type_size(type, &align);
+}
+
+void gfunc_call(int nb_args)
+{
+ int size, r, args_size, i, d, bt, struct_size;
+ int arg;
+
+ args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE;
+ arg = nb_args;
+
+ /* for struct arguments, we need to call memcpy and the function
+ call breaks register passing arguments we are preparing.
+ So, we process arguments which will be passed by stack first. */
+ struct_size = args_size;
+ for(i = 0; i < nb_args; i++) {
+ SValue *sv;
+
+ --arg;
+ sv = &vtop[-i];
+ bt = (sv->type.t & VT_BTYPE);
+ size = gfunc_arg_size(&sv->type);
+
+ if (size <= 8)
+ continue; /* arguments smaller than 8 bytes passed in registers or on stack */
+
+ if (bt == VT_STRUCT) {
+ /* align to stack align size */
+ size = (size + 15) & ~15;
+ /* generate structure store */
+ r = get_reg(RC_INT);
+ gen_offs_sp(0x8d, r, struct_size);
+ struct_size += size;
+
+ /* generate memcpy call */
+ vset(&sv->type, r | VT_LVAL, 0);
+ vpushv(sv);
+ vstore();
+ --vtop;
+ } else if (bt == VT_LDOUBLE) {
+ gv(RC_ST0);
+ gen_offs_sp(0xdb, 0x107, struct_size);
+ struct_size += 16;
+ }
+ }
+
+ if (func_scratch < struct_size)
+ func_scratch = struct_size;
+
+ arg = nb_args;
+ struct_size = args_size;
+
+ for(i = 0; i < nb_args; i++) {
+ --arg;
+ bt = (vtop->type.t & VT_BTYPE);
+
+ size = gfunc_arg_size(&vtop->type);
+ if (size > 8) {
+ /* align to stack align size */
+ size = (size + 15) & ~15;
+ if (arg >= REGN) {
+ d = get_reg(RC_INT);
+ gen_offs_sp(0x8d, d, struct_size);
+ gen_offs_sp(0x89, d, arg*8);
+ } else {
+ d = arg_prepare_reg(arg);
+ gen_offs_sp(0x8d, d, struct_size);
+ }
+ struct_size += size;
+ } else {
+ if (is_sse_float(vtop->type.t)) {
+ gv(RC_XMM0); /* only use one float register */
+ if (arg >= REGN) {
+ /* movq %xmm0, j*8(%rsp) */
+ gen_offs_sp(0xd60f66, 0x100, arg*8);
+ } else {
+ /* movaps %xmm0, %xmmN */
+ o(0x280f);
+ o(0xc0 + (arg << 3));
+ d = arg_prepare_reg(arg);
+ /* mov %xmm0, %rxx */
+ o(0x66);
+ orex(1,d,0, 0x7e0f);
+ o(0xc0 + REG_VALUE(d));
+ }
+ } else {
+ if (bt == VT_STRUCT) {
+ vtop->type.ref = NULL;
+ vtop->type.t = size > 4 ? VT_LLONG : size > 2 ? VT_INT
+ : size > 1 ? VT_SHORT : VT_BYTE;
+ }
+
+ r = gv(RC_INT);
+ if (arg >= REGN) {
+ gen_offs_sp(0x89, r, arg*8);
+ } else {
+ d = arg_prepare_reg(arg);
+ orex(1,d,r,0x89); /* mov */
+ o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
+ }
+ }
+ }
+ vtop--;
+ }
+ save_regs(0);
+
+ /* Copy R10 and R11 into RCX and RDX, respectively */
+ if (nb_args > 0) {
+ o(0xd1894c); /* mov %r10, %rcx */
+ if (nb_args > 1) {
+ o(0xda894c); /* mov %r11, %rdx */
+ }
+ }
+
+ gcall_or_jmp(0);
+ vtop--;
+}
+
+
+#define FUNC_PROLOG_SIZE 11
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(CType *func_type)
+{
+ int addr, reg_param_index, bt, size;
+ Sym *sym;
+ CType *type;
+
+ func_ret_sub = 0;
+ func_scratch = 0;
+ loc = 0;
+
+ addr = PTR_SIZE * 2;
+ ind += FUNC_PROLOG_SIZE;
+ func_sub_sp_offset = ind;
+ reg_param_index = 0;
+
+ sym = func_type->ref;
+
+ /* if the function returns a structure, then add an
+ implicit pointer parameter */
+ func_vt = sym->type;
+ func_var = (sym->c == FUNC_ELLIPSIS);
+ size = gfunc_arg_size(&func_vt);
+ if (size > 8) {
+ gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
+ func_vc = addr;
+ reg_param_index++;
+ addr += 8;
+ }
+
+ /* define parameters */
+ while ((sym = sym->next) != NULL) {
+ type = &sym->type;
+ bt = type->t & VT_BTYPE;
+ size = gfunc_arg_size(type);
+ if (size > 8) {
+ if (reg_param_index < REGN) {
+ gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
+ }
+ sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL | VT_REF, addr);
+ } else {
+ if (reg_param_index < REGN) {
+ /* save arguments passed by register */
+ if ((bt == VT_FLOAT) || (bt == VT_DOUBLE)) {
+ o(0xd60f66); /* movq */
+ gen_modrm(reg_param_index, VT_LOCAL, NULL, addr);
+ } else {
+ gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
+ }
+ }
+ sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | VT_LVAL, addr);
+ }
+ addr += 8;
+ reg_param_index++;
+ }
+
+ while (reg_param_index < REGN) {
+ if (func_type->ref->c == FUNC_ELLIPSIS) {
+ gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, addr);
+ addr += 8;
+ }
+ reg_param_index++;
+ }
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+ int v, saved_ind;
+
+ o(0xc9); /* leave */
+ if (func_ret_sub == 0) {
+ o(0xc3); /* ret */
+ } else {
+ o(0xc2); /* ret n */
+ g(func_ret_sub);
+ g(func_ret_sub >> 8);
+ }
+
+ saved_ind = ind;
+ ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
+ /* align local size to word & save local variables */
+ v = (func_scratch + -loc + 15) & -16;
+
+ if (v >= 4096) {
+ Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0);
+ oad(0xb8, v); /* mov stacksize, %eax */
+ oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */
+ greloc(cur_text_section, sym, ind-4, R_X86_64_PC32);
+ o(0x90); /* fill for FUNC_PROLOG_SIZE = 11 bytes */
+ } else {
+ o(0xe5894855); /* push %rbp, mov %rsp, %rbp */
+ o(0xec8148); /* sub rsp, stacksize */
+ gen_le32(v);
+ }
+
+ cur_text_section->data_offset = saved_ind;
+ pe_add_unwind_data(ind, saved_ind, v);
+ ind = cur_text_section->data_offset;
+}
+
+#else
+
+static void gadd_sp(int val)
+{
+ if (val == (char)val) {
+ o(0xc48348);
+ g(val);
+ } else {
+ oad(0xc48148, val); /* add $xxx, %rsp */
+ }
+}
+
+typedef enum X86_64_Mode {
+ x86_64_mode_none,
+ x86_64_mode_memory,
+ x86_64_mode_integer,
+ x86_64_mode_sse,
+ x86_64_mode_x87
+} X86_64_Mode;
+
+static X86_64_Mode classify_x86_64_merge(X86_64_Mode a, X86_64_Mode b)
+{
+ if (a == b)
+ return a;
+ else if (a == x86_64_mode_none)
+ return b;
+ else if (b == x86_64_mode_none)
+ return a;
+ else if ((a == x86_64_mode_memory) || (b == x86_64_mode_memory))
+ return x86_64_mode_memory;
+ else if ((a == x86_64_mode_integer) || (b == x86_64_mode_integer))
+ return x86_64_mode_integer;
+ else if ((a == x86_64_mode_x87) || (b == x86_64_mode_x87))
+ return x86_64_mode_memory;
+ else
+ return x86_64_mode_sse;
+}
+
+/* classify the x86 eightbytes from byte index start to byte index
+ * end, at offset offset from the root struct */
+static X86_64_Mode classify_x86_64_inner(CType *ty, int offset, int start, int end)
+{
+ X86_64_Mode mode;
+ Sym *f;
+
+ switch (ty->t & VT_BTYPE) {
+ case VT_VOID: return x86_64_mode_none;
+
+ case VT_INT:
+ case VT_BYTE:
+ case VT_SHORT:
+ case VT_LLONG:
+ case VT_BOOL:
+ case VT_PTR:
+ case VT_FUNC:
+ case VT_ENUM: return x86_64_mode_integer;
+
+ case VT_FLOAT:
+ case VT_DOUBLE: return x86_64_mode_sse;
+
+ case VT_LDOUBLE: return x86_64_mode_x87;
+
+ case VT_STRUCT:
+ f = ty->ref;
+
+ mode = x86_64_mode_none;
+ while ((f = f->next) != NULL) {
+ if (f->c + offset >= start && f->c + offset < end)
+ mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type, f->c + offset, start, end));
+ }
+
+ return mode;
+ }
+
+ assert(0);
+}
+
+static X86_64_Mode classify_x86_64_arg_eightbyte(CType *ty, int offset)
+{
+ X86_64_Mode mode;
+
+ assert((ty->t & VT_BTYPE) == VT_STRUCT);
+
+ mode = classify_x86_64_inner(ty, 0, offset, offset + 8);
+
+ return mode;
+}
+
+static void regargs_init(RegArgs *args)
+{
+ int i;
+ for(i=0; i<REG_ARGS_MAX; i++) {
+ args->ireg[i] = -1;
+ args->freg[i] = -1;
+ }
+}
+
+static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *palign, RegArgs *args)
+{
+ X86_64_Mode mode = x86_64_mode_none;
+ int size, align, ret_t = 0;
+ int ireg = 0, freg = 0;
+
+ if (args)
+ regargs_init(args);
+
+ if (ty->t & (VT_BITFIELD|VT_ARRAY)) {
+ *psize = 8;
+ *palign = 8;
+ if (args)
+ args->ireg[ireg++] = 0;
+ ret_t = ty->t;
+ mode = x86_64_mode_integer;
+ } else {
+ size = type_size(ty, &align);
+ *psize = (size + 7) & ~7;
+ *palign = (align + 7) & ~7;
+
+ if (size > 16) {
+ mode = x86_64_mode_memory;
+ } else {
+ int start;
+
+ for(start=0; start < size; start += 8) {
+ if ((ty->t & VT_BTYPE) == VT_STRUCT) {
+ mode = classify_x86_64_arg_eightbyte(ty, start);
+ } else {
+ mode = classify_x86_64_inner(ty, 0, 0, size);
+ }
+
+ if (mode == x86_64_mode_integer) {
+ if (args)
+ args->ireg[ireg++] = start;
+ ret_t = (size > 4) ? VT_LLONG : VT_INT;
+ } else if (mode == x86_64_mode_sse) {
+ if (args)
+ args->freg[freg++] = start;
+ ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT;
+ } else {
+ ret_t = VT_LDOUBLE;
+ }
+ }
+ }
+ }
+
+ if (ret) {
+ ret->ref = NULL;
+ ret->t = ret_t;
+ }
+
+ return mode;
+}
+
+ST_FUNC int classify_x86_64_va_arg(CType *ty)
+{
+ /* This definition must be synced with stdarg.h */
+ enum __va_arg_type {
+ __va_gen_reg, __va_float_reg, __va_stack
+ };
+ int size, align;
+ X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, NULL);
+ switch (mode) {
+ default: return __va_stack;
+ case x86_64_mode_integer: return __va_gen_reg;
+ case x86_64_mode_sse: return __va_float_reg;
+ }
+}
+
+static int regargs_iregs(RegArgs *args)
+{
+ int i;
+ int ret = 0;
+ for(i=0; i<REG_ARGS_MAX; i++) {
+ if(args->ireg[i] != -1)
+ ret++;
+ }
+
+ return ret;
+}
+
+static int regargs_fregs(RegArgs *args)
+{
+ int i;
+ int ret = 0;
+ for(i=0; i<REG_ARGS_MAX; i++) {
+ if(args->freg[i] != -1)
+ ret++;
+ }
+
+ return ret;
+}
+
+/* Count the total number of registers used by args */
+ST_FUNC int regargs_nregs(RegArgs *args)
+{
+ int i;
+ int ret = 0;
+ for(i=0; i<REG_ARGS_MAX; i++) {
+ if(args->ireg[i] != -1)
+ ret++;
+
+ if(args->freg[i] != -1)
+ ret++;
+ }
+
+ return ret;
+}
+
+ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align, int *regsize, RegArgs *args)
+{
+ int size, align;
+ X86_64_Mode mode;
+ *ret_align = 1; // Never have to re-align return values for x86-64
+ *regsize = 8;
+
+ mode = classify_x86_64_arg(vt, ret, &size, &align, args);
+
+ return mode != x86_64_mode_memory &&
+ mode != x86_64_mode_none;
+}
+
+#define REGN 6
+static const uint8_t arg_regs[REGN] = {
+ TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9
+};
+
+static int arg_prepare_reg(int idx) {
+ if (idx == 2 || idx == 3)
+ /* idx=2: r10, idx=3: r11 */
+ return idx + 8;
+ else
+ return arg_regs[idx];
+}
+
+/* Generate function call. The function address is pushed first, then
+ all the parameters in call order. This functions pops all the
+ parameters and the function address. */
+void gfunc_call(int nb_args)
+{
+ X86_64_Mode mode;
+ CType type;
+ int size, align, r, args_size, stack_adjust, run_start, run_end, i;
+ int nb_reg_args = 0;
+ int nb_sse_args = 0;
+ int sse_reg = 0, gen_reg = 0;
+ RegArgs *reg_args = alloca(nb_args * sizeof *reg_args);
+
+ /* calculate the number of integer/float register arguments */
+ for(i = nb_args - 1; i >= 0; i--) {
+ int fregs, iregs;
+ mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, &reg_args[i]);
+ fregs = regargs_fregs(&reg_args[i]);
+ iregs = regargs_iregs(&reg_args[i]);
+
+ nb_sse_args += fregs;
+ nb_reg_args += iregs;
+
+ if (sse_reg + fregs > 8 || gen_reg + iregs > REGN) {
+ regargs_init(&reg_args[i]);
+ } else {
+ sse_reg += fregs;
+ gen_reg += iregs;
+ }
+ }
+
+ /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments
+ and ended by a 16-byte aligned argument. This is because, from the point of view of
+ the callee, argument alignment is computed from the bottom up. */
+ /* for struct arguments, we need to call memcpy and the function
+ call breaks register passing arguments we are preparing.
+ So, we process arguments which will be passed by stack first. */
+ gen_reg = nb_reg_args;
+ sse_reg = nb_sse_args;
+ run_start = 0;
+ args_size = 0;
+ while (run_start != nb_args) {
+ int run_gen_reg = gen_reg, run_sse_reg = sse_reg;
+
+ run_end = nb_args;
+ stack_adjust = 0;
+ for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) {
+ int stack = regargs_nregs(&reg_args[i]) == 0;
+ classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL);
+
+ if (stack) {
+ if (align == 16)
+ run_end = i;
+ else
+ stack_adjust += size;
+ }
+ }
+
+ gen_reg = run_gen_reg;
+ sse_reg = run_sse_reg;
+
+ /* adjust stack to align SSE boundary */
+ if (stack_adjust &= 15) {
+ /* fetch cpu flag before the following sub will change the value */
+ if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP)
+ gv(RC_INT);
+
+ stack_adjust = 16 - stack_adjust;
+ o(0x48);
+ oad(0xec81, stack_adjust); /* sub $xxx, %rsp */
+ args_size += stack_adjust;
+ }
+
+ for(i = run_start; i < run_end;) {
+ int arg_stored = regargs_nregs(&reg_args[i]) == 0;
+ SValue tmp;
+ RegArgs args;
+
+ if (!arg_stored) {
+ ++i;
+ continue;
+ }
+
+ /* Swap argument to top, it will possibly be changed here,
+ and might use more temps. At the end of the loop we keep
+ in on the stack and swap it back to its original position
+ if it is a register. */
+ tmp = vtop[0];
+ vtop[0] = vtop[-i];
+ vtop[-i] = tmp;
+
+ classify_x86_64_arg(&vtop->type, NULL, &size, &align, &args);
+
+ switch (vtop->type.t & VT_BTYPE) {
+ case VT_STRUCT:
+ /* allocate the necessary size on stack */
+ o(0x48);
+ oad(0xec81, size); /* sub $xxx, %rsp */
+ /* generate structure store */
+ r = get_reg(RC_INT);
+ orex(1, r, 0, 0x89); /* mov %rsp, r */
+ o(0xe0 + REG_VALUE(r));
+ vset(&vtop->type, r | VT_LVAL, 0);
+ vswap();
+ vstore();
+ args_size += size;
+ break;
+
+ case VT_LDOUBLE:
+ assert(0);
+ break;
+
+ case VT_FLOAT:
+ case VT_DOUBLE:
+ r = gv(RC_FLOAT);
+ o(0x50); /* push $rax */
+ /* movq %xmmN, (%rsp) */
+ o(0xd60f66);
+ o(0x04 + REG_VALUE(r)*8);
+ o(0x24);
+ args_size += size;
+ break;
+
+ default:
+ /* simple type */
+ /* XXX: implicit cast ? */
+ --gen_reg;
+ r = gv(RC_INT);
+ orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */
+ args_size += size;
+ break;
+ }
+
+ /* And swap the argument back to its original position. */
+ tmp = vtop[0];
+ vtop[0] = vtop[-i];
+ vtop[-i] = tmp;
+
+ vrotb(i+1);
+ assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r));
+ vpop();
+ memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args);
+ --nb_args;
+ --run_end;
+ }
+
+ /* handle 16 byte aligned arguments at end of run */
+ run_start = i = run_end;
+ while (i < nb_args) {
+ /* Rotate argument to top since it will always be popped */
+ mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, NULL);
+ if (align != 16)
+ break;
+
+ vrotb(i+1);
+
+ if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+ gv(RC_ST0);
+ oad(0xec8148, size); /* sub $xxx, %rsp */
+ o(0x7cdb); /* fstpt 0(%rsp) */
+ g(0x24);
+ g(0x00);
+ args_size += size;
+ } else {
+ assert(mode == x86_64_mode_memory);
+
+ /* allocate the necessary size on stack */
+ o(0x48);
+ oad(0xec81, size); /* sub $xxx, %rsp */
+ /* generate structure store */
+ r = get_reg(RC_INT);
+ orex(1, r, 0, 0x89); /* mov %rsp, r */
+ o(0xe0 + REG_VALUE(r));
+ vset(&vtop->type, r | VT_LVAL, 0);
+ vswap();
+ vstore();
+ args_size += size;
+ }
+
+ vpop();
+ memmove(reg_args + i, reg_args + i + 1, (nb_args - i - 1) * sizeof *reg_args);
+ --nb_args;
+ }
+ }
+
+ /* XXX This should be superfluous. */
+ save_regs(0); /* save used temporary registers */
+
+ /* recalculate the number of register arguments there actually
+ * are. This is slow but more obviously correct than using the
+ * old counts. */
+ gen_reg = 0;
+ sse_reg = 0;
+ for(i = 0; i < nb_args; i++) {
+ gen_reg += regargs_iregs(&reg_args[i]);
+ sse_reg += regargs_fregs(&reg_args[i]);
+ }
+
+ /* then, we prepare register passing arguments.
+ Note that we cannot set RDX and RCX in this loop because gv()
+ may break these temporary registers. Let's use R10 and R11
+ instead of them */
+ assert(gen_reg <= REGN);
+ assert(sse_reg <= 8);
+ for(i = 0; i < nb_args; i++) {
+ RegArgs args;
+
+ args = reg_args[i];
+
+ /* Alter stack entry type so that gv() knows how to treat it */
+ if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) {
+ int k;
+
+ for(k=REG_ARGS_MAX-1; k>=0; k--) {
+ if (args.freg[k] == -1)
+ continue;
+
+ sse_reg--;
+ assert(sse_reg >= 0);
+
+ vdup();
+ vtop->type.t = VT_DOUBLE;
+ vtop->c.ull += args.freg[k];
+ gv(RC_XMM0 << sse_reg);
+ vpop();
+ }
+ for(k=REG_ARGS_MAX-1; k>=0; k--) {
+ int d;
+ if (args.ireg[k] == -1)
+ continue;
+
+ gen_reg--;
+
+ vdup();
+ vtop->type.t = VT_LLONG;
+ vtop->c.ull += args.ireg[k];
+ r = gv(RC_INT);
+ d = arg_prepare_reg(gen_reg);
+ orex(1,d,r,0x89); /* mov */
+ o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
+ vpop();
+ }
+ } else {
+ /* XXX is it really necessary to set vtop->type? */
+ classify_x86_64_arg(&vtop->type, &type, &size, &align, NULL);
+ vtop->type = type;
+ if (args.freg[0] != -1) {
+ --sse_reg;
+ /* Load directly to register */
+ gv(RC_XMM0 << sse_reg);
+ } else if (args.ireg[0] != -1) {
+ int d;
+ /* simple type */
+ /* XXX: implicit cast ? */
+ gen_reg--;
+ r = gv(RC_INT);
+ d = arg_prepare_reg(gen_reg);
+ orex(1,d,r,0x89); /* mov */
+ o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d));
+ } else {
+ assert(0);
+ }
+ }
+ vtop--;
+ }
+ assert(gen_reg == 0);
+ assert(sse_reg == 0);
+
+ /* We shouldn't have many operands on the stack anymore, but the
+ call address itself is still there, and it might be in %eax
+ (or edx/ecx) currently, which the below writes would clobber.
+ So evict all remaining operands here. */
+ save_regs(0);
+
+ /* Copy R10 and R11 into RDX and RCX, respectively */
+ if (nb_reg_args > 2) {
+ o(0xd2894c); /* mov %r10, %rdx */
+ if (nb_reg_args > 3) {
+ o(0xd9894c); /* mov %r11, %rcx */
+ }
+ }
+
+ oad(0xb8, nb_sse_args < 8 ? nb_sse_args : 8); /* mov nb_sse_args, %eax */
+ gcall_or_jmp(0);
+ if (args_size)
+ gadd_sp(args_size);
+ vtop--;
+}
+
+
+#define FUNC_PROLOG_SIZE 11
+
+static void push_arg_reg(int i) {
+ loc -= 8;
+ gen_modrm64(0x89, arg_regs[i], VT_LOCAL, NULL, loc);
+}
+
+/* generate function prolog of type 't' */
+void gfunc_prolog(CType *func_type)
+{
+ X86_64_Mode mode;
+ int i, addr, align, size;
+ int param_addr = 0, reg_param_index, sse_param_index;
+ Sym *sym;
+ CType *type;
+
+ sym = func_type->ref;
+ addr = PTR_SIZE * 2;
+ loc = 0;
+ ind += FUNC_PROLOG_SIZE;
+ func_sub_sp_offset = ind;
+ func_ret_sub = 0;
+
+ if (func_type->ref->c == FUNC_ELLIPSIS) {
+ int seen_reg_num, seen_sse_num, seen_stack_size;
+ seen_reg_num = seen_sse_num = 0;
+ /* frame pointer and return address */
+ seen_stack_size = PTR_SIZE * 2;
+ /* count the number of seen parameters */
+ sym = func_type->ref;
+ while ((sym = sym->next) != NULL) {
+ RegArgs args;
+
+ type = &sym->type;
+ mode = classify_x86_64_arg(type, NULL, &size, &align, &args);
+
+ switch (mode) {
+ default:
+ stack_arg:
+ seen_stack_size = ((seen_stack_size + align - 1) & -align) + size;
+ break;
+
+ case x86_64_mode_integer:
+ case x86_64_mode_sse: {
+ int stack = 0;
+
+ seen_sse_num += regargs_fregs(&args);
+ seen_reg_num += regargs_iregs(&args);
+
+ if (seen_reg_num > 8) {
+ seen_reg_num = 8;
+ stack = 1;
+ }
+ if (seen_sse_num > 8) {
+ seen_sse_num = 8;
+ stack = 1;
+ }
+
+ if (stack)
+ goto stack_arg;
+ break;
+ }
+ }
+ }
+
+ loc -= 16;
+ /* movl $0x????????, -0x10(%rbp) */
+ o(0xf045c7);
+ gen_le32(seen_reg_num * 8);
+ /* movl $0x????????, -0xc(%rbp) */
+ o(0xf445c7);
+ gen_le32(seen_sse_num * 16 + 48);
+ /* movl $0x????????, -0x8(%rbp) */
+ o(0xf845c7);
+ gen_le32(seen_stack_size);
+
+ /* save all register passing arguments */
+ for (i = 0; i < 8; i++) {
+ loc -= 16;
+ o(0xd60f66); /* movq */
+ gen_modrm(7 - i, VT_LOCAL, NULL, loc);
+ /* movq $0, loc+8(%rbp) */
+ o(0x85c748);
+ gen_le32(loc + 8);
+ gen_le32(0);
+ }
+ for (i = 0; i < REGN; i++) {
+ push_arg_reg(REGN-1-i);
+ }
+ }
+
+ sym = func_type->ref;
+ reg_param_index = 0;
+ sse_param_index = 0;
+
+ /* if the function returns a structure, then add an
+ implicit pointer parameter */
+ func_vt = sym->type;
+ mode = classify_x86_64_arg(&func_vt, NULL, &size, &align, NULL);
+ if (mode == x86_64_mode_memory) {
+ push_arg_reg(reg_param_index);
+ func_vc = loc;
+ reg_param_index++;
+ }
+ /* define parameters */
+ while ((sym = sym->next) != NULL) {
+ RegArgs args;
+ int reg_count_integer = 0;
+ int reg_count_sse = 0;
+ int arg_stored = 1;
+
+ type = &sym->type;
+ mode = classify_x86_64_arg(type, NULL, &size, &align, &args);
+ reg_count_integer = regargs_iregs(&args);
+ reg_count_sse = regargs_fregs(&args);
+
+ switch (mode) {
+ case x86_64_mode_integer:
+ case x86_64_mode_sse:
+ if (reg_count_integer || reg_count_sse) {
+ if ((reg_count_sse == 0 || sse_param_index + reg_count_sse <= 8) &&
+ (reg_count_integer == 0 || reg_param_index + reg_count_integer <= REGN)) {
+ /* argument fits into registers */
+ arg_stored = 0;
+ }
+ }
+
+ if (!arg_stored) {
+ /* save arguments passed by register */
+ loc -= (reg_count_sse + reg_count_integer) * 8;
+ param_addr = loc;
+ for (i = 0; i < reg_count_sse; ++i) {
+ o(0xd60f66); /* movq */
+ gen_modrm(sse_param_index, VT_LOCAL, NULL, param_addr + args.freg[i]);
+ ++sse_param_index;
+ }
+ for (i = 0; i < reg_count_integer; ++i) {
+ gen_modrm64(0x89, arg_regs[reg_param_index], VT_LOCAL, NULL, param_addr + args.ireg[i]);
+ ++reg_param_index;
+ }
+ } else {
+ addr = (addr + align - 1) & -align;
+ param_addr = addr;
+ addr += size;
+ }
+ break;
+
+ case x86_64_mode_memory:
+ case x86_64_mode_x87:
+ addr = (addr + align - 1) & -align;
+ param_addr = addr;
+ addr += size;
+ break;
+ default: break; /* nothing to be done for x86_64_mode_none */
+ }
+ sym_push(sym->v & ~SYM_FIELD, type,
+ VT_LOCAL | VT_LVAL, param_addr);
+ }
+
+#ifdef CONFIG_TCC_BCHECK
+ /* leave some room for bound checking code */
+ if (tcc_state->do_bounds_check) {
+ func_bound_offset = lbounds_section->data_offset;
+ func_bound_ind = ind;
+ oad(0xb8, 0); /* lbound section pointer */
+ o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */
+ oad(0xb8, 0); /* call to function */
+ }
+#endif
+}
+
+/* generate function epilog */
+void gfunc_epilog(void)
+{
+ int v, saved_ind;
+
+#ifdef CONFIG_TCC_BCHECK
+ if (tcc_state->do_bounds_check
+ && func_bound_offset != lbounds_section->data_offset)
+ {
+ addr_t saved_ind;
+ addr_t *bounds_ptr;
+ Sym *sym_data;
+
+ /* add end of table info */
+ bounds_ptr = section_ptr_add(lbounds_section, sizeof(addr_t));
+ *bounds_ptr = 0;
+
+ /* generate bound local allocation */
+ sym_data = get_sym_ref(&char_pointer_type, lbounds_section,
+ func_bound_offset, lbounds_section->data_offset);
+ saved_ind = ind;
+ ind = func_bound_ind;
+ greloc(cur_text_section, sym_data, ind + 1, R_386_32);
+ ind = ind + 5 + 3;
+ gen_static_call(TOK___bound_local_new);
+ ind = saved_ind;
+
+ /* generate bound check local freeing */
+ o(0x5250); /* save returned value, if any */
+ greloc(cur_text_section, sym_data, ind + 1, R_386_32);
+ oad(0xb8, 0); /* mov xxx, %rax */
+ o(0xc78948); /* mov %rax,%rdi ## first arg in %rdi, this must be ptr */
+ gen_static_call(TOK___bound_local_delete);
+ o(0x585a); /* restore returned value, if any */
+ }
+#endif
+ o(0xc9); /* leave */
+ if (func_ret_sub == 0) {
+ o(0xc3); /* ret */
+ } else {
+ o(0xc2); /* ret n */
+ g(func_ret_sub);
+ g(func_ret_sub >> 8);
+ }
+ /* align local size to word & save local variables */
+ v = (-loc + 15) & -16;
+ saved_ind = ind;
+ ind = func_sub_sp_offset - FUNC_PROLOG_SIZE;
+ o(0xe5894855); /* push %rbp, mov %rsp, %rbp */
+ o(0xec8148); /* sub rsp, stacksize */
+ gen_le32(v);
+ ind = saved_ind;
+}
+
+#endif /* not PE */
+
+/* generate a jump to a label */
+int gjmp(int t)
+{
+ return psym(0xe9, t);
+}
+
+/* generate a jump to a fixed address */
+void gjmp_addr(int a)
+{
+ int r;
+ r = a - ind - 2;
+ if (r == (char)r) {
+ g(0xeb);
+ g(r);
+ } else {
+ oad(0xe9, a - ind - 5);
+ }
+}
+
+/* generate a test. set 'inv' to invert test. Stack entry is popped */
+int gtst(int inv, int t)
+{
+ int v, *p;
+
+ v = vtop->r & VT_VALMASK;
+ if (v == VT_CMP) {
+ /* fast case : can jump directly since flags are set */
+ if (vtop->c.i & 0x100)
+ {
+ /* This was a float compare. If the parity flag is set
+ the result was unordered. For anything except != this
+ means false and we don't jump (anding both conditions).
+ For != this means true (oring both).
+ Take care about inverting the test. We need to jump
+ to our target if the result was unordered and test wasn't NE,
+ otherwise if unordered we don't want to jump. */
+ vtop->c.i &= ~0x100;
+ if (!inv == (vtop->c.i != TOK_NE))
+ o(0x067a); /* jp +6 */
+ else
+ {
+ g(0x0f);
+ t = psym(0x8a, t); /* jp t */
+ }
+ }
+ g(0x0f);
+ t = psym((vtop->c.i - 16) ^ inv, t);
+ } else if (v == VT_JMP || v == VT_JMPI) {
+ /* && or || optimization */
+ if ((v & 1) == inv) {
+ /* insert vtop->c jump list in t */
+ p = &vtop->c.i;
+ while (*p != 0)
+ p = (int *)(cur_text_section->data + *p);
+ *p = t;
+ t = vtop->c.i;
+ } else {
+ t = gjmp(t);
+ gsym(vtop->c.i);
+ }
+ }
+ vtop--;
+ return t;
+}
+
+/* generate an integer binary operation */
+void gen_opi(int op)
+{
+ int r, fr, opc, c;
+ int ll, uu, cc;
+
+ ll = is64_type(vtop[-1].type.t);
+ uu = (vtop[-1].type.t & VT_UNSIGNED) != 0;
+ cc = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
+
+ switch(op) {
+ case '+':
+ case TOK_ADDC1: /* add with carry generation */
+ opc = 0;
+ gen_op8:
+ if (cc && (!ll || (int)vtop->c.ll == vtop->c.ll)) {
+ /* constant case */
+ vswap();
+ r = gv(RC_INT);
+ vswap();
+ c = vtop->c.i;
+ if (c == (char)c) {
+ /* XXX: generate inc and dec for smaller code ? */
+ orex(ll, r, 0, 0x83);
+ o(0xc0 | (opc << 3) | REG_VALUE(r));
+ g(c);
+ } else {
+ orex(ll, r, 0, 0x81);
+ oad(0xc0 | (opc << 3) | REG_VALUE(r), c);
+ }
+ } else {
+ gv2(RC_INT, RC_INT);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ orex(ll, r, fr, (opc << 3) | 0x01);
+ o(0xc0 + REG_VALUE(r) + REG_VALUE(fr) * 8);
+ }
+ vtop--;
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ }
+ break;
+ case '-':
+ case TOK_SUBC1: /* sub with carry generation */
+ opc = 5;
+ goto gen_op8;
+ case TOK_ADDC2: /* add with carry use */
+ opc = 2;
+ goto gen_op8;
+ case TOK_SUBC2: /* sub with carry use */
+ opc = 3;
+ goto gen_op8;
+ case '&':
+ opc = 4;
+ goto gen_op8;
+ case '^':
+ opc = 6;
+ goto gen_op8;
+ case '|':
+ opc = 1;
+ goto gen_op8;
+ case '*':
+ gv2(RC_INT, RC_INT);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ orex(ll, fr, r, 0xaf0f); /* imul fr, r */
+ o(0xc0 + REG_VALUE(fr) + REG_VALUE(r) * 8);
+ vtop--;
+ break;
+ case TOK_SHL:
+ opc = 4;
+ goto gen_shift;
+ case TOK_SHR:
+ opc = 5;
+ goto gen_shift;
+ case TOK_SAR:
+ opc = 7;
+ gen_shift:
+ opc = 0xc0 | (opc << 3);
+ if (cc) {
+ /* constant case */
+ vswap();
+ r = gv(RC_INT);
+ vswap();
+ orex(ll, r, 0, 0xc1); /* shl/shr/sar $xxx, r */
+ o(opc | REG_VALUE(r));
+ g(vtop->c.i & (ll ? 63 : 31));
+ } else {
+ /* we generate the shift in ecx */
+ gv2(RC_INT, RC_RCX);
+ r = vtop[-1].r;
+ orex(ll, r, 0, 0xd3); /* shl/shr/sar %cl, r */
+ o(opc | REG_VALUE(r));
+ }
+ vtop--;
+ break;
+ case TOK_UDIV:
+ case TOK_UMOD:
+ uu = 1;
+ goto divmod;
+ case '/':
+ case '%':
+ case TOK_PDIV:
+ uu = 0;
+ divmod:
+ /* first operand must be in eax */
+ /* XXX: need better constraint for second operand */
+ gv2(RC_RAX, RC_RCX);
+ r = vtop[-1].r;
+ fr = vtop[0].r;
+ vtop--;
+ save_reg(TREG_RDX);
+ orex(ll, 0, 0, uu ? 0xd231 : 0x99); /* xor %edx,%edx : cqto */
+ orex(ll, fr, 0, 0xf7); /* div fr, %eax */
+ o((uu ? 0xf0 : 0xf8) + REG_VALUE(fr));
+ if (op == '%' || op == TOK_UMOD)
+ r = TREG_RDX;
+ else
+ r = TREG_RAX;
+ vtop->r = r;
+ break;
+ default:
+ opc = 7;
+ goto gen_op8;
+ }
+}
+
+void gen_opl(int op)
+{
+ gen_opi(op);
+}
+
+/* generate a floating point operation 'v = t1 op t2' instruction. The
+ two operands are guaranted to have the same floating point type */
+/* XXX: need to use ST1 too */
+void gen_opf(int op)
+{
+ int a, ft, fc, swapped, r;
+ int float_type =
+ (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT;
+
+ /* convert constants to memory references */
+ if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
+ vswap();
+ gv(float_type);
+ vswap();
+ }
+ if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST)
+ gv(float_type);
+
+ /* must put at least one value in the floating point register */
+ if ((vtop[-1].r & VT_LVAL) &&
+ (vtop[0].r & VT_LVAL)) {
+ vswap();
+ gv(float_type);
+ vswap();
+ }
+ swapped = 0;
+ /* swap the stack if needed so that t1 is the register and t2 is
+ the memory reference */
+ if (vtop[-1].r & VT_LVAL) {
+ vswap();
+ swapped = 1;
+ }
+ if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) {
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ /* load on stack second operand */
+ load(TREG_ST0, vtop);
+ save_reg(TREG_RAX); /* eax is used by FP comparison code */
+ if (op == TOK_GE || op == TOK_GT)
+ swapped = !swapped;
+ else if (op == TOK_EQ || op == TOK_NE)
+ swapped = 0;
+ if (swapped)
+ o(0xc9d9); /* fxch %st(1) */
+ if (op == TOK_EQ || op == TOK_NE)
+ o(0xe9da); /* fucompp */
+ else
+ o(0xd9de); /* fcompp */
+ o(0xe0df); /* fnstsw %ax */
+ if (op == TOK_EQ) {
+ o(0x45e480); /* and $0x45, %ah */
+ o(0x40fC80); /* cmp $0x40, %ah */
+ } else if (op == TOK_NE) {
+ o(0x45e480); /* and $0x45, %ah */
+ o(0x40f480); /* xor $0x40, %ah */
+ op = TOK_NE;
+ } else if (op == TOK_GE || op == TOK_LE) {
+ o(0x05c4f6); /* test $0x05, %ah */
+ op = TOK_EQ;
+ } else {
+ o(0x45c4f6); /* test $0x45, %ah */
+ op = TOK_EQ;
+ }
+ vtop--;
+ vtop->r = VT_CMP;
+ vtop->c.i = op;
+ } else {
+ /* no memory reference possible for long double operations */
+ load(TREG_ST0, vtop);
+ swapped = !swapped;
+
+ switch(op) {
+ default:
+ case '+':
+ a = 0;
+ break;
+ case '-':
+ a = 4;
+ if (swapped)
+ a++;
+ break;
+ case '*':
+ a = 1;
+ break;
+ case '/':
+ a = 6;
+ if (swapped)
+ a++;
+ break;
+ }
+ ft = vtop->type.t;
+ fc = vtop->c.ul;
+ o(0xde); /* fxxxp %st, %st(1) */
+ o(0xc1 + (a << 3));
+ vtop--;
+ }
+ } else {
+ if (op >= TOK_ULT && op <= TOK_GT) {
+ /* if saved lvalue, then we must reload it */
+ r = vtop->r;
+ fc = vtop->c.ul;
+ if ((r & VT_VALMASK) == VT_LLOCAL) {
+ SValue v1;
+ r = get_reg(RC_INT);
+ v1.type.t = VT_PTR;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = fc;
+ load(r, &v1);
+ fc = 0;
+ }
+
+ if (op == TOK_EQ || op == TOK_NE) {
+ swapped = 0;
+ } else {
+ if (op == TOK_LE || op == TOK_LT)
+ swapped = !swapped;
+ if (op == TOK_LE || op == TOK_GE) {
+ op = 0x93; /* setae */
+ } else {
+ op = 0x97; /* seta */
+ }
+ }
+
+ if (swapped) {
+ gv(RC_FLOAT);
+ vswap();
+ }
+ assert(!(vtop[-1].r & VT_LVAL));
+
+ if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE)
+ o(0x66);
+ if (op == TOK_EQ || op == TOK_NE)
+ o(0x2e0f); /* ucomisd */
+ else
+ o(0x2f0f); /* comisd */
+
+ if (vtop->r & VT_LVAL) {
+ gen_modrm(vtop[-1].r, r, vtop->sym, fc);
+ } else {
+ o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8);
+ }
+
+ vtop--;
+ vtop->r = VT_CMP;
+ vtop->c.i = op | 0x100;
+ } else {
+ assert((vtop->type.t & VT_BTYPE) != VT_LDOUBLE);
+ switch(op) {
+ default:
+ case '+':
+ a = 0;
+ break;
+ case '-':
+ a = 4;
+ break;
+ case '*':
+ a = 1;
+ break;
+ case '/':
+ a = 6;
+ break;
+ }
+ ft = vtop->type.t;
+ fc = vtop->c.ul;
+ assert((ft & VT_BTYPE) != VT_LDOUBLE);
+
+ r = vtop->r;
+ /* if saved lvalue, then we must reload it */
+ if ((vtop->r & VT_VALMASK) == VT_LLOCAL) {
+ SValue v1;
+ r = get_reg(RC_INT);
+ v1.type.t = VT_PTR;
+ v1.r = VT_LOCAL | VT_LVAL;
+ v1.c.ul = fc;
+ load(r, &v1);
+ fc = 0;
+ }
+
+ assert(!(vtop[-1].r & VT_LVAL));
+ if (swapped) {
+ assert(vtop->r & VT_LVAL);
+ gv(RC_FLOAT);
+ vswap();
+ }
+
+ if ((ft & VT_BTYPE) == VT_DOUBLE) {
+ o(0xf2);
+ } else {
+ o(0xf3);
+ }
+ o(0x0f);
+ o(0x58 + a);
+
+ if (vtop->r & VT_LVAL) {
+ gen_modrm(vtop[-1].r, r, vtop->sym, fc);
+ } else {
+ o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8);
+ }
+
+ vtop--;
+ }
+ }
+}
+
+/* convert integers to fp 't' type. Must handle 'int', 'unsigned int'
+ and 'long long' cases. */
+void gen_cvt_itof(int t)
+{
+ if ((t & VT_BTYPE) == VT_LDOUBLE) {
+ save_reg(TREG_ST0);
+ gv(RC_INT);
+ if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ /* signed long long to float/double/long double (unsigned case
+ is handled generically) */
+ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+ o(0x242cdf); /* fildll (%rsp) */
+ o(0x08c48348); /* add $8, %rsp */
+ } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
+ (VT_INT | VT_UNSIGNED)) {
+ /* unsigned int to float/double/long double */
+ o(0x6a); /* push $0 */
+ g(0x00);
+ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+ o(0x242cdf); /* fildll (%rsp) */
+ o(0x10c48348); /* add $16, %rsp */
+ } else {
+ /* int to float/double/long double */
+ o(0x50 + (vtop->r & VT_VALMASK)); /* push r */
+ o(0x2404db); /* fildl (%rsp) */
+ o(0x08c48348); /* add $8, %rsp */
+ }
+ vtop->r = TREG_ST0;
+ } else {
+ int r = get_reg(RC_FLOAT);
+ gv(RC_INT);
+ o(0xf2 + ((t & VT_BTYPE) == VT_FLOAT?1:0));
+ if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) ==
+ (VT_INT | VT_UNSIGNED) ||
+ (vtop->type.t & VT_BTYPE) == VT_LLONG) {
+ o(0x48); /* REX */
+ }
+ o(0x2a0f);
+ o(0xc0 + (vtop->r & VT_VALMASK) + REG_VALUE(r)*8); /* cvtsi2sd */
+ vtop->r = r;
+ }
+}
+
+/* convert from one floating point type to another */
+void gen_cvt_ftof(int t)
+{
+ int ft, bt, tbt;
+
+ ft = vtop->type.t;
+ bt = ft & VT_BTYPE;
+ tbt = t & VT_BTYPE;
+
+ if (bt == VT_FLOAT) {
+ gv(RC_FLOAT);
+ if (tbt == VT_DOUBLE) {
+ o(0x140f); /* unpcklps */
+ o(0xc0 + REG_VALUE(vtop->r)*9);
+ o(0x5a0f); /* cvtps2pd */
+ o(0xc0 + REG_VALUE(vtop->r)*9);
+ } else if (tbt == VT_LDOUBLE) {
+ save_reg(RC_ST0);
+ /* movss %xmm0,-0x10(%rsp) */
+ o(0x110ff3);
+ o(0x44 + REG_VALUE(vtop->r)*8);
+ o(0xf024);
+ o(0xf02444d9); /* flds -0x10(%rsp) */
+ vtop->r = TREG_ST0;
+ }
+ } else if (bt == VT_DOUBLE) {
+ gv(RC_FLOAT);
+ if (tbt == VT_FLOAT) {
+ o(0x140f66); /* unpcklpd */
+ o(0xc0 + REG_VALUE(vtop->r)*9);
+ o(0x5a0f66); /* cvtpd2ps */
+ o(0xc0 + REG_VALUE(vtop->r)*9);
+ } else if (tbt == VT_LDOUBLE) {
+ save_reg(RC_ST0);
+ /* movsd %xmm0,-0x10(%rsp) */
+ o(0x110ff2);
+ o(0x44 + REG_VALUE(vtop->r)*8);
+ o(0xf024);
+ o(0xf02444dd); /* fldl -0x10(%rsp) */
+ vtop->r = TREG_ST0;
+ }
+ } else {
+ int r;
+ gv(RC_ST0);
+ r = get_reg(RC_FLOAT);
+ if (tbt == VT_DOUBLE) {
+ o(0xf0245cdd); /* fstpl -0x10(%rsp) */
+ /* movsd -0x10(%rsp),%xmm0 */
+ o(0x100ff2);
+ o(0x44 + REG_VALUE(r)*8);
+ o(0xf024);
+ vtop->r = r;
+ } else if (tbt == VT_FLOAT) {
+ o(0xf0245cd9); /* fstps -0x10(%rsp) */
+ /* movss -0x10(%rsp),%xmm0 */
+ o(0x100ff3);
+ o(0x44 + REG_VALUE(r)*8);
+ o(0xf024);
+ vtop->r = r;
+ }
+ }
+}
+
+/* convert fp to int 't' type */
+void gen_cvt_ftoi(int t)
+{
+ int ft, bt, size, r;
+ ft = vtop->type.t;
+ bt = ft & VT_BTYPE;
+ if (bt == VT_LDOUBLE) {
+ gen_cvt_ftof(VT_DOUBLE);
+ bt = VT_DOUBLE;
+ }
+
+ gv(RC_FLOAT);
+ if (t != VT_INT)
+ size = 8;
+ else
+ size = 4;
+
+ r = get_reg(RC_INT);
+ if (bt == VT_FLOAT) {
+ o(0xf3);
+ } else if (bt == VT_DOUBLE) {
+ o(0xf2);
+ } else {
+ assert(0);
+ }
+ orex(size == 8, r, 0, 0x2c0f); /* cvttss2si or cvttsd2si */
+ o(0xc0 + REG_VALUE(vtop->r) + REG_VALUE(r)*8);
+ vtop->r = r;
+}
+
+/* computed goto support */
+void ggoto(void)
+{
+ gcall_or_jmp(1);
+ vtop--;
+}
+
+/* Save the stack pointer onto the stack and return the location of its address */
+ST_FUNC void gen_vla_sp_save(int addr) {
+ /* mov %rsp,addr(%rbp)*/
+ gen_modrm64(0x89, TREG_RSP, VT_LOCAL, NULL, addr);
+}
+
+/* Restore the SP from a location on the stack */
+ST_FUNC void gen_vla_sp_restore(int addr) {
+ gen_modrm64(0x8b, TREG_RSP, VT_LOCAL, NULL, addr);
+}
+
+/* Subtract from the stack pointer, and push the resulting value onto the stack */
+ST_FUNC void gen_vla_alloc(CType *type, int align) {
+#ifdef TCC_TARGET_PE
+ /* alloca does more than just adjust %rsp on Windows */
+ vpush_global_sym(&func_old_type, TOK_alloca);
+ vswap(); /* Move alloca ref past allocation size */
+ gfunc_call(1);
+ vset(type, REG_IRET, 0);
+#else
+ int r = gv(RC_INT); /* allocation size */
+ /* sub r,%rsp */
+ o(0x2b48);
+ o(0xe0 | REG_VALUE(r));
+ /* We align to 16 bytes rather than align */
+ /* and ~15, %rsp */
+ o(0xf0e48348);
+ vpop();
+#endif
+}
+
+
+/* end of x86-64 code generator */
+/*************************************************************/
+#endif /* ! TARGET_DEFS_ONLY */
+/******************************************************/