diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..376ea52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,58 @@ +*.o +*.a +tcc_g +tcc +/*-tcc +tc2.c +doc +tc3s.c +p3.c +tc1.c +error.c +i386-gen1.c +test.out1 +test.out1b +test.out2 +test.out2b +test.out3 +test.out3b +web.sh +memdebug.c +bench +Makefile.uClibc +boundtest +prog.ref +test.ref +test.out +tcc-doc.html +ideas +tcctest.ref +linux.tcc +ldtest +libtcc_test +instr.S +p.c +p2.c +tcctest[1234] +test[1234].out +tests/tcclib.h +tests/tcctest.gcc +tests/weaktest.*.o.txt +tests2/fred.txt +.gdb_history +tcc.1 +tcc.pod +config.h +config.mak +config.texi +tags +.DS_Store +*.swp +lib/x86_64 +lib/i386 +lib/x86_64-win32 +lib/i386-win32 +tcc-doc.info +conftest* +tiny_libmaker +*.dSYM diff --git a/Changelog b/Changelog index b271054..bab887c 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,31 @@ +version 0.9.26: + +User interface: +- -MD/-MF (automatically generate dependencies for make) +- -pthread option (same as -D_REENTRANT -lpthread) (Henry Kroll III) +- -m32/-m64 to re-exec cross compiler (Henry Kroll III) +- -Wl, Mimic all GNU -option forms supported by ld (Kirill Smelkov) +- new LIBTCCAPI tcc_set_options() (grischka) + +Platforms: +- Many improvements for x86-64 target (Shinichiro Hamaji, Michael Matz, grischka) +- x86-64 assembler (Frederic Feret) +- Many improvements for ARM target (Daniel Glöckner, Thomas Preud'homme) +- Support WinCE PE ARM (Timo VJ Lahde) +- Support ARM hardfloat calling convention (Thomas Preud'homme) +- Support SELinux (Security-Enhanced Linux) (Henry Kroll III) +- Support Debian GNU/kFreeBSD kernels (Pierre Chifflier) +- Support GNU/Hurd kernels (Thomas Preud'homme) +- Support OSX (tcc -run only) (Milutin Jovanovic) +- Support multiarch configuration (Thomas Preud'homme) +- Support out-of-tree build (Akim Demaille) + +Features: +- C99 variable length arrays (Thomas Preud'homme & Joe Soroka) +- Asm labels for variables and functions (Thomas Preud'homme) +- STT_GNU_IFUNC (Indirect functions as externals) (Thomas Preud'homme) +- More tests (tests2) (Milutin Jovanovic) + version 0.9.25: - first support for x86-64 target (Shinichiro Hamaji) diff --git a/Makefile b/Makefile index 1a2b5f7..705b585 100644 --- a/Makefile +++ b/Makefile @@ -4,29 +4,36 @@ TOP ?= . include $(TOP)/config.mak +VPATH = $(top_srcdir) -CFLAGS+=-g -Wall -CFLAGS_P=$(CFLAGS) -pg -static -DCONFIG_TCC_STATIC -LIBS_P= +CPPFLAGS = -I$(TOP) # for config.h -ifneq ($(GCC_MAJOR),2) +ifeq (-$(findstring gcc,$(CC))-,-gcc-) +ifeq (-$(findstring $(GCC_MAJOR),01)-,--) CFLAGS+=-fno-strict-aliasing -endif - -ifeq ($(ARCH),i386) -CFLAGS+=-mpreferred-stack-boundary=2 -ifeq ($(GCC_MAJOR),2) -CFLAGS+=-m386 -malign-functions=0 +ifeq (-$(findstring $(GCC_MAJOR),23)-,--) +CFLAGS+=-Wno-pointer-sign -Wno-sign-compare +ifeq (-$(GCC_MAJOR)-$(findstring $(GCC_MINOR),56789)-,-4--) +CFLAGS+=-D_FORTIFY_SOURCE=0 else -CFLAGS+=-march=i386 -falign-functions=0 -ifneq ($(GCC_MAJOR),3) -CFLAGS+=-Wno-pointer-sign -Wno-sign-compare -D_FORTIFY_SOURCE=0 +CFLAGS+=-Wno-unused-result endif endif endif +else # not GCC +ifeq (-$(findstring clang,$(CC))-,-clang-) +# make clang accept gnuisms in libtcc1.c +CFLAGS+=-fheinous-gnu-extensions +endif +endif -ifeq ($(ARCH),x86-64) -CFLAGS+=-Wno-pointer-sign +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 @@ -36,209 +43,259 @@ LIBS+=-ldl endif endif -ifdef CONFIG_WIN32 -NATIVE_TARGET=-DTCC_TARGET_PE -LIBTCC1=libtcc1.a -else -ifeq ($(ARCH),i386) -NATIVE_TARGET=-DTCC_TARGET_I386 -LIBTCC1=libtcc1.a -BCHECK_O=bcheck.o -else -ifeq ($(ARCH),arm) -NATIVE_TARGET=-DTCC_TARGET_ARM -NATIVE_TARGET+=$(if $(wildcard /lib/ld-linux.so.3),-DTCC_ARM_EABI) -NATIVE_TARGET+=$(if $(shell grep -l "^Features.* \(vfp\|iwmmxt\) " /proc/cpuinfo),-DTCC_ARM_VFP) -else -ifeq ($(ARCH),x86-64) -NATIVE_TARGET=-DTCC_TARGET_X86_64 -LIBTCC1=libtcc1.a -endif +# make libtcc as static or dynamic library? +ifdef DISABLE_STATIC +LIBTCC=libtcc.so.1.0 +LINK_LIBTCC=-Wl,-rpath,"$(libdir)" +ifdef DISABLE_RPATH +LINK_LIBTCC= endif -endif -endif - -ifneq ($(wildcard /lib/ld-uClibc.so.0),) -NATIVE_TARGET+=-DTCC_UCLIBC -BCHECK_O= +else +LIBTCC=libtcc.a +LINK_LIBTCC= endif -ifdef CONFIG_USE_LIBGCC -LIBTCC1= -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 -DWITHOUT_LIBTCC +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 += $(NATIVE_DEFINES_yes) ifeq ($(TOP),.) PROGS=tcc$(EXESUF) - I386_CROSS = i386-tcc$(EXESUF) WIN32_CROSS = i386-win32-tcc$(EXESUF) +WIN64_CROSS = x86_64-win32-tcc$(EXESUF) +WINCE_CROSS = arm-win32-tcc$(EXESUF) X64_CROSS = x86_64-tcc$(EXESUF) -ARM_CROSS = arm-tcc-fpa$(EXESUF) arm-tcc-fpa-ld$(EXESUF) \ - arm-tcc-vfp$(EXESUF) arm-tcc-vfp-eabi$(EXESUF) +ARM_FPA_CROSS = arm-fpa-tcc$(EXESUF) +ARM_FPA_LD_CROSS = arm-fpa-ld-tcc$(EXESUF) +ARM_VFP_CROSS = arm-vfp-tcc$(EXESUF) +ARM_EABI_CROSS = arm-eabi-tcc$(EXESUF) +ARM_CROSS = $(ARM_FPA_CROSS) $(ARM_FPA_LD_CROSS) $(ARM_VFP_CROSS) $(ARM_EABI_CROSS) C67_CROSS = c67-tcc$(EXESUF) -CORE_FILES = tcc.c libtcc.c tccpp.c tccgen.c tccelf.c tccasm.c \ - tcc.h config.h libtcc.h tcctok.h -I386_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h -WIN32_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h tccpe.c -X86_64_FILES = $(CORE_FILES) x86_64-gen.c +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) i386-gen.c i386-asm.c i386-asm.h i386-tok.h +WIN32_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h i386-tok.h tccpe.c +WIN64_FILES = $(CORE_FILES) x86_64-gen.c i386-asm.c x86_64-asm.h tccpe.c +WINCE_FILES = $(CORE_FILES) arm-gen.c tccpe.c +X86_64_FILES = $(CORE_FILES) x86_64-gen.c i386-asm.c x86_64-asm.h ARM_FILES = $(CORE_FILES) arm-gen.c C67_FILES = $(CORE_FILES) c67-gen.c tcccoff.c -ifdef CONFIG_WIN32 +ifdef CONFIG_WIN64 +PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF) +NATIVE_FILES=$(WIN64_FILES) +PROGS_CROSS=$(WIN32_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(C67_CROSS) +LIBTCC1_CROSS=lib/i386-win32/libtcc1.a +LIBTCC1=libtcc1.a +else ifdef CONFIG_WIN32 PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF) NATIVE_FILES=$(WIN32_FILES) -PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(C67_CROSS) -else -ifeq ($(ARCH),i386) +PROGS_CROSS=$(WIN64_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(C67_CROSS) +LIBTCC1_CROSS=lib/x86_64-win32/libtcc1.a +LIBTCC1=libtcc1.a +else ifeq ($(ARCH),i386) NATIVE_FILES=$(I386_FILES) -PROGS_CROSS=$(X64_CROSS) $(WIN32_CROSS) $(ARM_CROSS) $(C67_CROSS) -else -ifeq ($(ARCH),x86-64) +PROGS_CROSS=$(X64_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(ARM_CROSS) $(C67_CROSS) +LIBTCC1_CROSS=lib/i386-win32/libtcc1.a lib/x86_64-win32/libtcc1.a +LIBTCC1=libtcc1.a +else ifeq ($(ARCH),x86-64) NATIVE_FILES=$(X86_64_FILES) -PROGS_CROSS=$(I386_CROSS) $(WIN32_CROSS) $(ARM_CROSS) $(C67_CROSS) -else -ifeq ($(ARCH),arm) +PROGS_CROSS=$(I386_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(ARM_CROSS) $(C67_CROSS) +LIBTCC1_CROSS=lib/i386-win32/libtcc1.a lib/x86_64-win32/libtcc1.a lib/i386/libtcc1.a +LIBTCC1=libtcc1.a +else ifeq ($(ARCH),arm) NATIVE_FILES=$(ARM_FILES) -PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(C67_CROSS) -endif +PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(WIN64_CROSS) $(C67_CROSS) endif + +ifeq ($(TARGETOS),Darwin) +PROGS+=tiny_libmaker$(EXESUF) endif + +ifdef CONFIG_USE_LIBGCC +LIBTCC1= endif +TCCLIBS = $(LIBTCC1) $(LIBTCC) +TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info + ifdef CONFIG_CROSS PROGS+=$(PROGS_CROSS) +TCCLIBS+=$(LIBTCC1_CROSS) endif -all: $(PROGS) $(LIBTCC1) $(BCHECK_O) libtcc.a tcc-doc.html tcc.1 libtcc_test$(EXESUF) +all: $(PROGS) $(TCCLIBS) $(TCCDOCS) # Host Tiny C Compiler -tcc$(EXESUF): $(NATIVE_FILES) - $(CC) -o $@ $< $(NATIVE_TARGET) $(CFLAGS) $(LIBS) +tcc$(EXESUF): tcc.o $(LIBTCC) + $(CC) -o $@ $^ $(LIBS) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LINK_LIBTCC) # Cross Tiny C Compilers -i386-tcc$(EXESUF): $(I386_FILES) - $(CC) -o $@ $< -DTCC_TARGET_I386 $(CFLAGS) $(LIBS) - -i386-win32-tcc$(EXESUF): $(WIN32_FILES) - $(CC) -o $@ $< -DTCC_TARGET_PE $(CFLAGS) $(LIBS) - -x86_64-tcc$(EXESUF): $(X86_64_FILES) - $(CC) -o $@ $< -DTCC_TARGET_X86_64 $(CFLAGS) $(LIBS) - -c67-tcc$(EXESUF): $(C67_FILES) - $(CC) -o $@ $< -DTCC_TARGET_C67 $(CFLAGS) $(LIBS) +%-tcc$(EXESUF): tcc.c + $(CC) -o $@ $< -DONE_SOURCE $(DEFINES) $(CPPFLAGS) $(CFLAGS) $(LIBS) $(LDFLAGS) -arm-tcc-fpa$(EXESUF): $(ARM_FILES) - $(CC) -o $@ $< -DTCC_TARGET_ARM $(CFLAGS) $(LIBS) - -arm-tcc-fpa-ld$(EXESUF): $(ARM_FILES) - $(CC) -o $@ $< -DTCC_TARGET_ARM -DLDOUBLE_SIZE=12 $(CFLAGS) $(LIBS) - -arm-tcc-vfp$(EXESUF): $(ARM_FILES) - $(CC) -o $@ $< -DTCC_TARGET_ARM -DTCC_ARM_VFP $(CFLAGS) $(LIBS) - -arm-tcc-vfp-eabi$(EXESUF): $(ARM_FILES) - $(CC) -o $@ $< -DTCC_TARGET_ARM -DTCC_ARM_EABI $(CFLAGS) $(LIBS) +# profiling version +tcc_p$(EXESUF): $(NATIVE_FILES) + $(CC) -o $@ $< -DONE_SOURCE $(NATIVE_DEFINES) $(CPPFLAGS_P) $(CFLAGS_P) $(LIBS_P) $(LDFLAGS_P) + +$(I386_CROSS): DEFINES = -DTCC_TARGET_I386 \ + -DCONFIG_TCCDIR="\"$(tccdir)/i386\"" +$(X64_CROSS): DEFINES = -DTCC_TARGET_X86_64 +$(WIN32_CROSS): DEFINES = -DTCC_TARGET_I386 -DTCC_TARGET_PE \ + -DCONFIG_TCCDIR="\"$(tccdir)/win32\"" \ + -DCONFIG_TCC_LIBPATHS="\"{B}/lib/32;{B}/lib\"" +$(WIN64_CROSS): 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 + +$(I386_CROSS): $(I386_FILES) +$(X64_CROSS): $(X86_64_FILES) +$(WIN32_CROSS): $(WIN32_FILES) +$(WIN64_CROSS): $(WIN64_FILES) +$(WINCE_CROSS): $(WINCE_FILES) +$(C67_CROSS): $(C67_FILES) +$(ARM_FPA_CROSS) $(ARM_FPA_LD_CROSS) $(ARM_VFP_CROSS) $(ARM_EABI_CROSS): $(ARM_FILES) # libtcc generation and test -libtcc.o: $(NATIVE_FILES) - $(CC) -o $@ -c libtcc.c $(NATIVE_TARGET) $(CFLAGS) +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.a: libtcc.o - $(AR) rcs $@ $^ +$(LIBTCC_OBJ) tcc.o : %.o : %.c $(LIBTCC_INC) + $(CC) -o $@ -c $< $(NATIVE_DEFINES) $(CPPFLAGS) $(CFLAGS) -libtcc_test$(EXESUF): tests/libtcc_test.c libtcc.a - $(CC) -o $@ $^ -I. $(CFLAGS) $(LIBS) +libtcc.a: $(LIBTCC_OBJ) + $(AR) rcs $@ $^ -libtest: libtcc_test$(EXESUF) $(LIBTCC1) - ./libtcc_test$(EXESUF) lib_path=. +libtcc.so.1.0: $(LIBTCC_OBJ) + $(CC) -shared -Wl,-soname,$@ -o $@ $^ $(LDFLAGS) -# profiling version -tcc_p$(EXESUF): $(NATIVE_FILES) - $(CC) -o $@ $< $(NATIVE_TARGET) $(CFLAGS_P) $(LIBS_P) +libtcc.so.1.0: CFLAGS+=-fPIC # windows utilities tiny_impdef$(EXESUF): win32/tools/tiny_impdef.c - $(CC) -o $@ $< $(CFLAGS) + $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) tiny_libmaker$(EXESUF): win32/tools/tiny_libmaker.c - $(CC) -o $@ $< $(CFLAGS) + $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) # TinyCC runtime libraries -LIBTCC1_OBJS=libtcc1.o -LIBTCC1_CC=$(CC) -VPATH+=lib -ifdef CONFIG_WIN32 -# for windows, we must use TCC because we generate ELF objects -LIBTCC1_OBJS+=crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o -LIBTCC1_CC=./tcc.exe -Bwin32 -DTCC_TARGET_PE -VPATH+=win32/lib -endif -ifeq ($(ARCH),i386) -LIBTCC1_OBJS+=alloca86.o alloca86-bt.o -endif - -%.o: %.c - $(LIBTCC1_CC) -o $@ -c $< -O2 -Wall - -%.o: %.S - $(LIBTCC1_CC) -o $@ -c $< - -libtcc1.a: $(LIBTCC1_OBJS) - $(AR) rcs $@ $^ +libtcc1.a : FORCE + $(MAKE) -C lib native +lib/%/libtcc1.a : FORCE $(PROGS_CROSS) + $(MAKE) -C lib cross TARGET=$* -bcheck.o: bcheck.c - $(CC) -o $@ -c $< -O2 -Wall +FORCE: # install TCC_INCLUDES = stdarg.h stddef.h stdbool.h float.h varargs.h tcclib.h INSTALL=install +ifdef STRIP_BINARIES +INSTALLBIN=$(INSTALL) -s +else +INSTALLBIN=$(INSTALL) +endif ifndef CONFIG_WIN32 -install: $(PROGS) $(LIBTCC1) $(BCHECK_O) libtcc.a tcc.1 tcc-doc.html +install: $(PROGS) $(TCCLIBS) $(TCCDOCS) mkdir -p "$(bindir)" - $(INSTALL) -s -m755 $(PROGS) "$(bindir)" +ifeq ($(CC),tcc) + $(INSTALL) -m755 $(PROGS) "$(bindir)" +else + $(INSTALLBIN) -m755 $(PROGS) "$(bindir)" +endif mkdir -p "$(mandir)/man1" - $(INSTALL) tcc.1 "$(mandir)/man1" + -$(INSTALL) tcc.1 "$(mandir)/man1" + mkdir -p "$(infodir)" + -$(INSTALL) tcc-doc.info "$(infodir)" mkdir -p "$(tccdir)" mkdir -p "$(tccdir)/include" ifneq ($(LIBTCC1),) $(INSTALL) -m644 $(LIBTCC1) "$(tccdir)" endif -ifneq ($(BCHECK_O),) - $(INSTALL) -m644 $(BCHECK_O) "$(tccdir)" -endif - $(INSTALL) -m644 $(addprefix include/,$(TCC_INCLUDES)) "$(tccdir)/include" - mkdir -p "$(docdir)" - $(INSTALL) -m644 tcc-doc.html "$(docdir)" + $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) "$(tccdir)/include" mkdir -p "$(libdir)" - $(INSTALL) -m644 libtcc.a "$(libdir)" + $(INSTALL) -m755 $(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 libtcc.h "$(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" +ifeq ($(ARCH),x86-64) + mkdir -p "$(tccdir)/i386" + $(INSTALL) -m644 lib/i386/libtcc1.a "$(tccdir)/i386" + cp -r "$(tccdir)/include" "$(tccdir)/i386" +endif + $(INSTALL) -m644 win32/lib/*.def "$(tccdir)/win32/lib" + $(INSTALL) -m644 lib/i386-win32/libtcc1.a "$(tccdir)/win32/lib/32" + $(INSTALL) -m644 lib/x86_64-win32/libtcc1.a "$(tccdir)/win32/lib/64" + cp -r win32/include/. "$(tccdir)/win32/include" + cp -r include/. "$(tccdir)/win32/include" +endif uninstall: rm -fv $(foreach P,$(PROGS),"$(bindir)/$P") - rm -fv $(foreach P,$(LIBTCC1) $(BCHECK_O),"$(tccdir)/$P") + rm -fv $(foreach P,$(LIBTCC1),"$(tccdir)/$P") rm -fv $(foreach P,$(TCC_INCLUDES),"$(tccdir)/include/$P") - rm -fv "$(docdir)/tcc-doc.html" "$(mandir)/man1/tcc.1" - rm -fv "$(libdir)/libtcc.a" "$(includedir)/libtcc.h" - + rm -fv "$(docdir)/tcc-doc.html" "$(mandir)/man1/tcc.1" "$(infodir)/tcc-doc.info" + rm -fv "$(libdir)/$(LIBTCC)" "$(includedir)/libtcc.h" + rm -fv "$(libdir)/libtcc.so*" + rm -rf "$(tccdir)/win32" + -rmdir $(tccdir)/include +ifeq ($(ARCH),x86-64) + rm -rf "$(tccdir)/i386" +endif else -install: $(PROGS) $(LIBTCC1) libtcc.a tcc-doc.html +# 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" - $(INSTALL) -s -m755 $(PROGS) "$(tccdir)" + $(INSTALLBIN) -m755 $(PROGS) "$(tccdir)" $(INSTALL) -m644 $(LIBTCC1) win32/lib/*.def "$(tccdir)/lib" cp -r win32/include/. "$(tccdir)/include" cp -r win32/examples/. "$(tccdir)/examples" -# $(INSTALL) -m644 $(addprefix include/,$(TCC_INCLUDES)) "$(tccdir)/include" + $(INSTALL) -m644 $(addprefix include/,$(TCC_INCLUDES)) "$(tccdir)/include" $(INSTALL) -m644 tcc-doc.html win32/tcc-win32.txt "$(tccdir)/doc" - $(INSTALL) -m644 libtcc.a libtcc.h "$(tccdir)/libtcc" + $(INSTALL) -m644 $(LIBTCC) libtcc.h "$(tccdir)/libtcc" +ifdef CONFIG_CROSS + mkdir -p "$(tccdir)/lib/32" + mkdir -p "$(tccdir)/lib/64" + -$(INSTALL) -m644 lib/i386-win32/libtcc1.a "$(tccdir)/lib/32" + -$(INSTALL) -m644 lib/x86_64-win32/libtcc1.a "$(tccdir)/lib/64" +endif + +uninstall: + rm -rfv "$(tccdir)/*" endif # documentation and man page @@ -246,27 +303,47 @@ tcc-doc.html: tcc-doc.texi -texi2html -monolithic -number $< tcc.1: tcc-doc.texi - -./texi2pod.pl $< tcc.pod + -$(top_srcdir)/texi2pod.pl $< tcc.pod -pod2man --section=1 --center=" " --release=" " tcc.pod > $@ -# tar release (use 'make -k tar' on a checkouted tree) -TCC-VERSION=tcc-$(shell cat VERSION) -tar: - rm -rf /tmp/$(TCC-VERSION) - cp -r . /tmp/$(TCC-VERSION) - ( cd /tmp ; tar zcvf ~/$(TCC-VERSION).tar.gz $(TCC-VERSION) --exclude CVS ) - rm -rf /tmp/$(TCC-VERSION) +tcc-doc.info: tcc-doc.texi + -makeinfo $< # in tests subdir -test clean : +export LIBTCC1 + +%est: $(MAKE) -C tests $@ -# clean -clean: local_clean -local_clean: - rm -vf $(PROGS) tcc_p$(EXESUF) tcc.pod *~ *.o *.a *.out libtcc_test$(EXESUF) +clean: + rm -vf $(PROGS) tcc_p$(EXESUF) tcc.pod *~ *.o *.a *.so* *.out *.exe libtcc_test$(EXESUF) + $(MAKE) -C tests $@ +ifneq ($(LIBTCC1),) + $(MAKE) -C lib $@ +endif distclean: clean - rm -vf config.h config.mak config.texi tcc.1 tcc-doc.html + rm -vf config.h config.mak config.texi tcc.1 tcc-doc.info tcc-doc.html + +config.mak: + @echo "Please run ./configure." + @exit 1 + +# 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 distclean install uninstall FORCE endif # ifeq ($(TOP),.) diff --git a/README b/README index bfaab39..43e6c3b 100644 --- a/README +++ b/README @@ -35,6 +35,16 @@ Documentation: make test make install +Alternatively, out-of-tree builds are supported: you may use different +directories to hold build objects, kept separate from your source tree: + + mkdir _build + cd _build + ../configure + make + make test + make install + By default, tcc is installed in /usr/local/bin. ./configure --help shows configuration options. @@ -65,7 +75,8 @@ operations given a list of numbers (benchmark). ex3.c: compute fibonacci numbers (benchmark). ex4.c: more complicated: X11 program. Very complicated test in fact -because standard headers are being used ! +because standard headers are being used ! As for ex1.c, can also be launched +directly as a script: './ex4.c'. ex5.c: 'hello world' with standard glibc headers. diff --git a/TODO b/TODO index 6f49c5d..74fe8a6 100644 --- a/TODO +++ b/TODO @@ -46,10 +46,8 @@ Missing features: - disable-asm and disable-bcheck options - __builtin_expect() - improve '-E' option. -- add '-MD' option - atexit (Nigel Horne) - packed attribute -- C99: add variable size arrays (gcc 3.2 testsuite issue) - C99: add complex types (gcc 3.2 testsuite issue) - postfix compound literals (see 20010124-1.c) diff --git a/VERSION b/VERSION index f5b38be..46e7a71 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.9.25 \ No newline at end of file +0.9.26 diff --git a/arm-gen.c b/arm-gen.c index 42feecf..6981395 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -2,6 +2,7 @@ * 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 * @@ -20,10 +21,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef TARGET_DEFS_ONLY + #ifdef TCC_ARM_EABI +#ifndef TCC_ARM_VFP // Avoid useless warning #define TCC_ARM_VFP #endif - +#endif /* number of available registers */ #ifdef TCC_ARM_VFP @@ -32,6 +36,10 @@ #define NB_REGS 9 #endif +#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). */ @@ -75,32 +83,6 @@ enum { #endif }; -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 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); -} - #ifdef TCC_ARM_VFP #define T2CPR(t) (((t) & VT_BTYPE) != VT_FLOAT ? 0x100 : 0) #endif @@ -124,15 +106,6 @@ static int regmask(int r) { are directly pushed on stack. */ //#define FUNC_STRUCT_PARAM_AS_PTR -#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) -static CType float_type, double_type, func_float_type, func_double_type; -#define func_ldouble_type func_double_type -#else -#define func_float_type func_old_type -#define func_double_type func_old_type -#define func_ldouble_type func_old_type -#endif - /* pointer size, in bytes */ #define PTR_SIZE 4 @@ -163,6 +136,7 @@ static CType float_type, double_type, func_float_type, func_double_type; /* 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 @@ -170,17 +144,67 @@ static CType float_type, double_type, func_float_type, func_double_type; #define ELF_PAGE_SIZE 0x1000 /******************************************************/ -static unsigned long func_sub_sp_offset,last_itod_magic; +#else /* ! TARGET_DEFS_ONLY */ +/******************************************************/ +#include "tcc.h" + +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; -void o(unsigned long i) +#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_types(void) +{ + 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); +} +#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_types(void) {} +#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); +} + +/******************************************************/ + +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) - error("compiler error! This happens f.ex. if the compiler\n" + 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); @@ -193,10 +217,10 @@ void o(unsigned long i) cur_text_section->data[ind++] = i; } -static unsigned long stuff_const(unsigned long op,unsigned long c) +static uint32_t stuff_const(uint32_t op, uint32_t c) { int try_neg=0; - unsigned long nc = 0,negop = 0; + uint32_t nc = 0, negop = 0; switch(op&0x1F00000) { @@ -230,7 +254,7 @@ static unsigned long stuff_const(unsigned long op,unsigned long c) break; } do { - unsigned long m; + uint32_t m; int i; if(c<256) /* catch undefined <<32 */ return op|c; @@ -247,13 +271,13 @@ static unsigned long stuff_const(unsigned long op,unsigned long c) //only add,sub -void stuff_const_harder(unsigned long op,unsigned long v) { - unsigned long x; +void stuff_const_harder(uint32_t op, uint32_t v) { + uint32_t x; x=stuff_const(op,v); if(x) o(x); else { - unsigned long a[16],nv,no,o2,n2; + uint32_t a[16], nv, no, o2, n2; int i,j,k; a[0]=0xff; o2=(op&0xfff0ffff)|((op&0xf000)<<4);; @@ -303,13 +327,13 @@ void stuff_const_harder(unsigned long op,unsigned long v) { } } -unsigned long encbranch(int pos,int addr,int fail) +ST_FUNC uint32_t encbranch(int pos, int addr, int fail) { addr-=pos+8; addr/=4; if(addr>=0x1000000 || addr<-0x1000000) { if(fail) - error("FIXME: function bigger than 32MB"); + tcc_error("FIXME: function bigger than 32MB"); return 0; } return 0x0A000000|(addr&0xffffff); @@ -318,7 +342,7 @@ unsigned long encbranch(int pos,int addr,int fail) int decbranch(int pos) { int x; - x=*(int *)(cur_text_section->data + pos); + x=*(uint32_t *)(cur_text_section->data + pos); x&=0x00ffffff; if(x&0x800000) x-=0x1000000; @@ -328,10 +352,10 @@ int decbranch(int pos) /* output a symbol and patch all calls to it */ void gsym_addr(int t, int a) { - unsigned long *x; + uint32_t *x; int lt; while(t) { - x=(unsigned long *)(cur_text_section->data + t); + x=(uint32_t *)(cur_text_section->data + t); t=decbranch(lt=t); if(a==lt+4) *x=0xE1A00000; // nop @@ -348,34 +372,34 @@ void gsym(int t) } #ifdef TCC_ARM_VFP -static unsigned long vfpr(int r) +static uint32_t vfpr(int r) { if(rTREG_F7) - error("compiler error! register %i is no vfp register",r); + tcc_error("compiler error! register %i is no vfp register",r); return r-5; } #else -static unsigned long fpr(int r) +static uint32_t fpr(int r) { if(rTREG_F3) - error("compiler error! register %i is no fpa register",r); + tcc_error("compiler error! register %i is no fpa register",r); return r-5; } #endif -static unsigned long intr(int r) +static uint32_t intr(int r) { if(r==4) return 12; if((r<0 || r>4) && r!=14) - error("compiler error! register %i is no int register",r); + tcc_error("compiler error! register %i is no int register",r); return r; } -static void calcaddr(unsigned long *base,int *off,int *sgn,int maxoff,unsigned shift) +static void calcaddr(uint32_t *base, int *off, int *sgn, int maxoff, unsigned shift) { if(*off>maxoff || *off&((1<r; @@ -485,7 +509,7 @@ void load(int r, SValue *sv) v = fr & VT_VALMASK; if (fr & VT_LVAL) { - unsigned long base=0xB; // fp + uint32_t base = 0xB; // fp if(v == VT_LLOCAL) { v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; @@ -601,7 +625,7 @@ void load(int r, SValue *sv) return; } } - error("load unimplemented!"); + tcc_error("load unimplemented!"); } /* store register 'r' in lvalue 'v' */ @@ -609,7 +633,7 @@ void store(int r, SValue *sv) { SValue v1; int v, ft, fc, fr, sign; - unsigned long op; + uint32_t op; fr = sv->r; ft = sv->type.t; @@ -624,7 +648,7 @@ void store(int r, SValue *sv) v = fr & VT_VALMASK; if (fr & VT_LVAL || fr == VT_LOCAL) { - unsigned long base=0xb; + uint32_t base = 0xb; if(v < VT_CONST) { base=intr(v); v=VT_LOCAL; @@ -682,7 +706,7 @@ void store(int r, SValue *sv) return; } } - error("store unimplemented"); + tcc_error("store unimplemented"); } static void gadd_sp(int val) @@ -695,7 +719,7 @@ static void gcall_or_jmp(int is_jmp) { int r; if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - unsigned long x; + uint32_t x; /* constant case */ x=encbranch(ind,ind+vtop->c.ul,0); if(x) { @@ -722,22 +746,91 @@ static void gcall_or_jmp(int is_jmp) } } +#ifdef TCC_ARM_HARDFLOAT +static int is_float_hgen_aggr(CType *type) +{ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + struct Sym *ref; + int btype, nb_fields = 0; + + ref = type->ref; + 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 { + /* worst case: f(float, double, 3 float struct, double, 3 float struct, double) */ + signed char avail[3]; + int first_hole; + int last_hole; + int first_free_reg; +}; + +#define AVAIL_REGS_INITIALIZER (struct avail_regs) { { 0, 0, 0}, 0, 0, 0 } + +/* Assign a register for a CPRC param with correct size and alignment + * size and align are in bytes, as returned by type_size */ +int assign_fpreg(struct avail_regs *avregs, int align, int size) +{ + int first_reg = 0; + + if (avregs->first_free_reg == -1) + return -1; + if (align >> 3) { // alignment needed (base type: double) + first_reg = avregs->first_free_reg; + if (first_reg & 1) + avregs->avail[avregs->last_hole++] = first_reg++; + } else { + 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; +} +#endif + /* 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 size, align, r, args_size, i; - Sym *func_sym; + int size, align, r, args_size, i, ncrn, ncprn, argno, vfp_argno; signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; - int todo=0xf, keep, plan2[4]={0,0,0,0}; + SValue *before_stack = NULL; /* SValue before first on stack argument */ + SValue *before_vfpreg_hfa = NULL; /* SValue before first in VFP reg hfa argument */ +#ifdef TCC_ARM_HARDFLOAT + struct avail_regs avregs = AVAIL_REGS_INITIALIZER; + signed char vfp_plan[16]; + int plan2[4+16]; + int variadic; +#else + int plan2[4]={0,0,0,0}; +#endif + int vfp_todo=0; + int todo=0, keep; +#ifdef TCC_ARM_HARDFLOAT + memset(vfp_plan, -1, sizeof(vfp_plan)); + memset(plan2, 0, sizeof(plan2)); + variadic = (vtop[-nb_args].type.ref->c == FUNC_ELLIPSIS); +#endif r = vtop->r & VT_VALMASK; if (r == VT_CMP || (r & ~1) == VT_JMP) gv(RC_INT); #ifdef TCC_ARM_EABI if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT - && type_size(&vtop[-nb_args].type, &align) <= 4) { + && type_size(&vtop[-nb_args].type.ref->type, &align) <= 4) { SValue tmp; tmp=vtop[-nb_args]; vtop[-nb_args]=vtop[-nb_args+1]; @@ -745,45 +838,137 @@ void gfunc_call(int nb_args) --nb_args; } - vpushi(0); + vpushi(0), nb_args++; vtop->type.t = VT_LLONG; - args_size = 0; - for(i = nb_args + 1 ; i-- ;) { - size = type_size(&vtop[-i].type, &align); - if(args_size & (align-1)) { - vpushi(0); - vtop->type.t = VT_VOID; /* padding */ - vrott(i+2); - args_size += 4; - ++nb_args; - } - args_size += (size + 3) & -4; - } - vtop--; #endif - args_size = 0; - for(i = nb_args ; i-- && args_size < 16 ;) { + ncrn = ncprn = argno = vfp_argno = args_size = 0; + /* Assign argument to registers and stack with alignment. + If, considering alignment constraints, enough registers of the correct type + (core or VFP) are free for the current argument, assign them to it, else + allocate on stack with correct alignment. Whenever a structure is allocated + in registers or on stack, it is always put on the stack at this stage. The + stack is divided in 3 zones. The zone are, from low addresses to high + addresses: structures to be loaded in core registers, structures to be + loaded in VFP registers, argument allocated to stack. SValue's representing + structures in the first zone are moved just after the SValue pointed by + before_vfpreg_hfa. SValue's representing structures in the second zone are + moved just after the SValue pointer by before_stack. */ + for(i = nb_args; i-- ;) { + int j, assigned_vfpreg = 0; + size = type_size(&vtop[-i].type, &align); switch(vtop[-i].type.t & VT_BTYPE) { case VT_STRUCT: case VT_FLOAT: case VT_DOUBLE: case VT_LDOUBLE: - size = type_size(&vtop[-i].type, &align); - size = (size + 3) & -4; - args_size += size; +#ifdef TCC_ARM_HARDFLOAT + if (!variadic) { + int hfa = 0; /* Homogeneous float aggregate */ + + if (is_float(vtop[-i].type.t) + || (hfa = is_float_hgen_aggr(&vtop[-i].type))) { + int end_reg; + + assigned_vfpreg = assign_fpreg(&avregs, align, size); + end_reg = assigned_vfpreg + (size - 1) / 4; + if (assigned_vfpreg >= 0) { + vfp_plan[vfp_argno++]=TREG_F0 + assigned_vfpreg/2; + if (hfa) { + /* before_stack can only have been set because all core registers + are assigned, so no need to care about before_vfpreg_hfa if + before_stack is set */ + if (before_stack) { + vrote(&vtop[-i], &vtop[-i] - before_stack); + before_stack++; + } else if (!before_vfpreg_hfa) + before_vfpreg_hfa = &vtop[-i-1]; + for (j = assigned_vfpreg; j <= end_reg; j++) + vfp_todo|=(1< 4) { + args_size = (ncrn - 4) * 4; + if (!before_stack) + before_stack = &vtop[-i-1]; + } + } + else { + ncrn = 4; + /* No need to set before_vfpreg_hfa if not set since there will no + longer be any structure assigned to core registers */ + if (!before_stack) + before_stack = &vtop[-i-1]; break; + } + continue; default: - plan[nb_args-1-i][0]=args_size/4; - args_size += 4; - if ((vtop[-i].type.t & VT_BTYPE) == VT_LLONG && args_size < 16) { - plan[nb_args-1-i][1]=args_size/4; - args_size += 4; +#ifdef TCC_ARM_EABI + if (!i) { + break; + } +#endif + if (ncrn < 4) { + int is_long = (vtop[-i].type.t & VT_BTYPE) == VT_LLONG; + + if (is_long) { + ncrn = (ncrn + 1) & -2; + if (ncrn == 4) { + argno++; + break; + } + } + plan[argno++][0]=ncrn++; + if (is_long) { + plan[argno-1][1]=ncrn++; + } + continue; } + argno++; } +#ifdef TCC_ARM_EABI + if(args_size & (align-1)) { + vpushi(0); + vtop->type.t = VT_VOID; /* padding */ + vrott(i+2); + args_size += 4; + nb_args++; + argno++; + } +#endif + args_size += (size + 3) & -4; } +#ifdef TCC_ARM_EABI + vtop--, nb_args--; +#endif args_size = keep = 0; for(i = 0;i < nb_args; i++) { - vnrott(keep+1); + vrotb(keep+1); if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { size = type_size(&vtop->type, &align); /* align to stack align size */ @@ -799,6 +984,12 @@ void gfunc_call(int nb_args) vtop--; args_size += size; } else if (is_float(vtop->type.t)) { +#ifdef TCC_ARM_HARDFLOAT + if (!variadic && --vfp_argno<16 && vfp_plan[vfp_argno]!=-1) { + plan2[keep++]=vfp_plan[vfp_argno]; + continue; + } +#endif #ifdef TCC_ARM_VFP r=vfpr(gv(RC_FLOAT))<<12; size=4; @@ -833,57 +1024,61 @@ void gfunc_call(int nb_args) size=4; if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { lexpand_nr(); - s=RC_INT; - if(nb_args-i<5 && plan[nb_args-i-1][1]!=-1) { - s=regmask(plan[nb_args-i-1][1]); - todo&=~(1<type.t == VT_VOID) { - if(s == RC_INT) + if(s == -1) o(0xE24DD004); /* sub sp,sp,#4 */ vtop--; } else -#endif - if(s == RC_INT) { - r = gv(s); +#endif + if(s == -1) { + r = gv(RC_INT); o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ vtop--; } else { + size=0; plan2[keep]=s; keep++; } args_size += size; } } - for(i=keep;i--;) { - gv(plan2[i]); - vrott(keep); + for(i = 0; i < keep; i++) { + vrotb(keep); + gv(regmask(plan2[i])); +#ifdef TCC_ARM_HARDFLOAT + /* arg is in s(2d+1): plan2[i] alignment occured (ex f,d,f) */ + if (i < keep - 1 && is_float(vtop->type.t) && (plan2[i] <= plan2[i + 1])) { + o(0xEEF00A40|(vfpr(plan2[i])<<12)|vfpr(plan2[i])); + } +#endif } save_regs(keep); /* save used temporary registers */ keep++; - if(args_size) { - int n; - n=args_size/4; - if(n>4) - n=4; - todo&=((1<4) + ncrn=4; + todo&=((1<r=i; keep++; + nb_regs++; } } - args_size-=n*4; + args_size-=nb_regs*4; } - vnrott(keep); - func_sym = vtop->type.ref; + if(vfp_todo) { + int nb_fregs=0; + + for(i=0;i<16;i++) + if(vfp_todo&(1<>1)<<12|nb_fregs); + vpushi(0); + /* There might be 2 floats in a double VFP reg but that doesn't seem + to matter */ + if (!(i%2)) + vtop->r=TREG_F0+i/2; + keep++; + nb_fregs++; + } + if (nb_fregs) { + gadd_sp(nb_fregs*4); + args_size-=nb_fregs*4; + } + } + vrotb(keep); gcall_or_jmp(0); if (args_size) gadd_sp(args_size); @@ -909,7 +1123,11 @@ save_regs(keep); /* save used temporary registers */ ++keep; } #ifdef TCC_ARM_VFP +#ifdef TCC_ARM_HARDFLOAT + else if(variadic && is_float(vtop->type.ref->type.t)) { +#else else if(is_float(vtop->type.ref->type.t)) { +#endif if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { o(0xEE000A10); /* fmsr s0,r0 */ } else { @@ -927,26 +1145,37 @@ save_regs(keep); /* save used temporary registers */ void gfunc_prolog(CType *func_type) { Sym *sym,*sym2; - int n,addr,size,align; + int n,nf,size,align, variadic, struct_ret = 0; +#ifdef TCC_ARM_HARDFLOAT + struct avail_regs avregs = AVAIL_REGS_INITIALIZER; +#endif sym = func_type->ref; func_vt = sym->type; - - n = 0; - addr = 0; + + n = nf = 0; + variadic = (func_type->ref->c == FUNC_ELLIPSIS); if((func_vt.t & VT_BTYPE) == VT_STRUCT && type_size(&func_vt,&align) > 4) { - func_vc = addr; - addr += 4; n++; + struct_ret = 1; + func_vc = 12; /* Offset from fp of the place to store the result */ } - for(sym2=sym->next;sym2 && n<4;sym2=sym2->next) { + for(sym2=sym->next;sym2 && (n<4 || nf<16);sym2=sym2->next) { size = type_size(&sym2->type, &align); - n += (size + 3) / 4; +#ifdef TCC_ARM_HARDFLOAT + if (!variadic && (is_float(sym2->type.t) + || is_float_hgen_aggr(&sym2->type))) { + int tmpnf = assign_fpreg(&avregs, align, size) + 1; + nf = (tmpnf > nf) ? tmpnf : nf; + } else +#endif + if (n < 4) + n += (size + 3) / 4; } o(0xE1A0C00D); /* mov ip,sp */ - if(func_type->ref->c == FUNC_ELLIPSIS) + if(variadic) n=4; if(n) { if(n>4) @@ -956,32 +1185,72 @@ void gfunc_prolog(CType *func_type) #endif o(0xE92D0000|((1<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(0xE28DB00C); /* add fp, sp, #12 */ + o(0xE1A0B00D); /* mov fp, sp */ func_sub_sp_offset = ind; - o(0xE1A00000); /* nop, leave space for stack adjustment */ - while ((sym = sym->next)) { - CType *type; - type = &sym->type; - size = type_size(type, &align); - size = (size + 3) & -4; + o(0xE1A00000); /* nop, leave space for stack adjustment in epilogue */ + { + int addr, pn = struct_ret, sn = 0; /* pn=core, sn=stack */ + +#ifdef TCC_ARM_HARDFLOAT + avregs = AVAIL_REGS_INITIALIZER; +#endif + 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_HARDFLOAT + if (!variadic && (is_float(sym->type.t) + || is_float_hgen_aggr(&sym->type))) { + int fpn = assign_fpreg(&avregs, align, size << 2); + if (fpn >= 0) { + addr = fpn * 4; + } else + goto from_stack; + } else +#endif + if (pn < 4) { #ifdef TCC_ARM_EABI - addr = (addr + align - 1) & -align; + pn = (pn + (align-1)/4) & -(align/4); #endif - sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr); - addr += size; + addr = (nf + pn) * 4; + pn += size; + if (!sn && pn > 4) + sn = (pn - 4); + } else { +#ifdef TCC_ARM_HARDFLOAT +from_stack: +#endif +#ifdef TCC_ARM_EABI + 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 = -12; + loc = 0; } /* generate function epilog */ void gfunc_epilog(void) { - unsigned long x; + uint32_t x; int diff; #ifdef TCC_ARM_EABI + /* Useless but harmless copy of the float result into main register(s) in case + of variadic function in the hardfloat variant */ if(is_float(func_vt.t)) { if((func_vt.t & VT_BTYPE) == VT_FLOAT) o(0xEE100A10); /* fmrs r0, s0 */ @@ -991,24 +1260,24 @@ void gfunc_epilog(void) } } #endif - o(0xE91BA800); /* restore fp, sp, pc */ + o(0xE89BA800); /* restore fp, sp, pc */ diff = (-loc + 3) & -4; #ifdef TCC_ARM_EABI if(!leaffunc) - diff = (diff + 7) & -8; + diff = ((diff + 11) & -8) - 4; #endif - if(diff > 12) { + if(diff > 0) { x=stuff_const(0xE24BD000, diff); /* sub sp,fp,# */ if(x) - *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = x; + *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = x; else { - unsigned long addr; + int addr; addr=ind; o(0xE59FC004); /* ldr ip,[pc+4] */ o(0xE04BD00C); /* sub sp,fp,ip */ o(0xE1A0F00E); /* mov pc,lr */ o(diff); - *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); + *(uint32_t *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); } } } @@ -1032,7 +1301,7 @@ void gjmp_addr(int a) int gtst(int inv, int t) { int v, r; - unsigned long op; + uint32_t op; v = vtop->r & VT_VALMASK; r=ind; if (v == VT_CMP) { @@ -1045,14 +1314,14 @@ int gtst(int inv, int t) if(!vtop->c.i) vtop->c.i=t; else { - unsigned long *x; + uint32_t *x; int p,lp; if(t) { p = vtop->c.i; do { p = decbranch(lp=p); } while(p); - x = (unsigned long *)(cur_text_section->data + lp); + x = (uint32_t *)(cur_text_section->data + lp); *x &= 0xff000000; *x |= encbranch(lp,t,1); } @@ -1094,7 +1363,7 @@ int gtst(int inv, int t) void gen_opi(int op) { int c, func = 0; - unsigned long opc = 0,r,fr; + uint32_t opc = 0, r, fr; unsigned short retreg = REG_IRET; c=0; @@ -1210,7 +1479,7 @@ void gen_opi(int op) vswap(); opc=0xE0000000|(opc<<20)|(c<<16); if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - unsigned long x; + 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))); @@ -1256,7 +1525,7 @@ void gen_opi(int op) vtop->r = retreg; break; default: - error("gen_opi %i unimplemented!",op); + tcc_error("gen_opi %i unimplemented!",op); } } @@ -1276,7 +1545,7 @@ static int is_zero(int i) * two operands are guaranted to have the same floating point type */ void gen_opf(int op) { - unsigned long x; + uint32_t x; int fneg=0,r; x=0xEE000A00|T2CPR(vtop->type.t); switch(op) { @@ -1309,8 +1578,8 @@ void gen_opf(int op) x|=0x800000; break; default: - if(op < TOK_ULT && op > TOK_GT) { - error("unknown fp op %x!",op); + if(op < TOK_ULT || op > TOK_GT) { + tcc_error("unknown fp op %x!",op); return; } if(is_zero(-1)) { @@ -1364,10 +1633,10 @@ void gen_opf(int op) } #else -static int is_fconst() +static uint32_t is_fconst() { long double f; - int r; + uint32_t r; if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) return 0; if (vtop->type.t == VT_FLOAT) @@ -1406,8 +1675,7 @@ static int is_fconst() two operands are guaranted to have the same floating point type */ void gen_opf(int op) { - unsigned long x; - int r,r2,c1,c2; + uint32_t x, r, r2, c1, c2; //fputs("gen_opf\n",stderr); vswap(); c1 = is_fconst(); @@ -1506,7 +1774,7 @@ void gen_opf(int op) case TOK_UGE: case TOK_ULE: case TOK_UGT: - error("unsigned comparision on floats?"); + tcc_error("unsigned comparision on floats?"); break; case TOK_LT: op=TOK_Nset; @@ -1550,7 +1818,7 @@ void gen_opf(int op) vtop[-1].r = VT_CMP; vtop[-1].c.i = op; } else { - error("unknown fp op %x!",op); + tcc_error("unknown fp op %x!",op); return; } } @@ -1570,19 +1838,20 @@ void gen_opf(int op) /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' and 'long long' cases. */ -void gen_cvt_itof1(int t) +ST_FUNC void gen_cvt_itof1(int t) { - int r,r2,bt; + 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 - unsigned int dsize=0; + 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<<=12; + r2|=r2<<12; if(!(vtop->type.t & VT_UNSIGNED)) r2|=0x80; /* fuitoX -> fsituX */ o(0xEEB80A40|r2|T2CPR(t)); /* fYitoX*/ @@ -1592,7 +1861,7 @@ void gen_cvt_itof1(int t) dsize=0x80; /* flts -> fltd */ o(0xEE000110|dsize|(r2<<16)|(r<<12)); /* flts */ if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { - unsigned int off=0; + uint32_t off = 0; o(0xE3500000|(r<<12)); /* cmp */ r=fpr(get_reg(RC_FLOAT)); if(last_itod_magic) { @@ -1646,13 +1915,14 @@ void gen_cvt_itof1(int t) return; } } - error("unimplemented gen_cvt_itof %x!",vtop->type.t); + tcc_error("unimplemented gen_cvt_itof %x!",vtop->type.t); } /* convert fp to int 't' type */ void gen_cvt_ftoi(int t) { - int r,r2,u,func=0; + uint32_t r, r2; + int u, func = 0; u=t&VT_UNSIGNED; t&=VT_BTYPE; r2=vtop->type.t & VT_BTYPE; @@ -1660,7 +1930,7 @@ void gen_cvt_ftoi(int t) #ifdef TCC_ARM_VFP r=vfpr(gv(RC_FLOAT)); u=u?0:0x10000; - o(0xEEBC0A40|(r<<12)|r|T2CPR(r2)); /* ftoXiY */ + o(0xEEBC0AC0|(r<<12)|r|T2CPR(r2)|u); /* ftoXizY */ r2=intr(vtop->r=get_reg(RC_INT)); o(0xEE100A10|(r<<16)|(r2<<12)); return; @@ -1705,7 +1975,7 @@ void gen_cvt_ftoi(int t) vtop->r = REG_IRET; return; } - error("unimplemented gen_cvt_ftoi!"); + tcc_error("unimplemented gen_cvt_ftoi!"); } /* convert from one floating point type to another */ @@ -1713,7 +1983,7 @@ void gen_cvt_ftof(int t) { #ifdef TCC_ARM_VFP if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) { - int r=vfpr(gv(RC_FLOAT)); + uint32_t r = vfpr(gv(RC_FLOAT)); o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t)); } #else @@ -1731,4 +2001,5 @@ void ggoto(void) /* end of ARM code generator */ /*************************************************************/ - +#endif +/*************************************************************/ diff --git a/c67-gen.c b/c67-gen.c index 04f8a12..b423ba6 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -18,6 +18,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef TARGET_DEFS_ONLY + //#define ASSEMBLY_LISTING_C67 /* number of available registers */ @@ -85,12 +87,52 @@ enum { TREG_C67_B13, }; -int reg_classes[NB_REGS] = { - /* eax */ RC_INT | RC_FLOAT | RC_EAX, - // only allow even regs for floats (allow for doubles) +/* 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) + /* 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, @@ -114,67 +156,36 @@ int reg_classes[NB_REGS] = { /* B13 */ RC_C67_B11 }; -/* 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 */ - - -#define ALWAYS_ASSERT(x) \ -do {\ - if (!(x))\ - error("internal compiler error file at %s:%d", __FILE__, __LINE__);\ -} while (0) - // 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; -/* 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 */ +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif -#define EM_TCC_TARGET EM_C60 +#undef BOOL +#define BOOL int -/* relocation type for 32 bit data relocation */ -#define R_DATA_32 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 +#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; @@ -182,7 +193,6 @@ static int C67_compare_reg; FILE *f = NULL; #endif - void C67_g(int c) { int ind1; @@ -1552,7 +1562,7 @@ void C67_SHR(int r, int v) void load(int r, SValue * sv) { int v, t, ft, fc, fr, size = 0, element; - BOOL Unsigned = false; + BOOL Unsigned = FALSE; SValue v1; fr = sv->r; @@ -1568,7 +1578,7 @@ void load(int r, SValue * sv) load(r, &v1); fr = r; } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { - error("long double not supported"); + tcc_error("long double not supported"); } else if ((ft & VT_TYPE) == VT_BYTE) { size = 1; } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { @@ -1722,7 +1732,7 @@ void store(int r, SValue * v) /* XXX: incorrect if float reg to reg */ if (bt == VT_LDOUBLE) { - error("long double not supported"); + tcc_error("long double not supported"); } else { if (bt == VT_SHORT) size = 2; @@ -1877,7 +1887,7 @@ void gfunc_call(int nb_args) int args_sizes[NoCallArgsPassedOnStack]; if (nb_args > NoCallArgsPassedOnStack) { - error("more than 10 function params not currently supported"); + tcc_error("more than 10 function params not currently supported"); // handle more than 10, put some on the stack } @@ -1892,9 +1902,9 @@ void gfunc_call(int nb_args) if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - error("long long not supported"); + tcc_error("long long not supported"); } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { - error("long double not supported"); + tcc_error("long double not supported"); } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { size = 8; } else { @@ -2178,34 +2188,34 @@ void gen_opi(int op) if (op == TOK_LT) { C67_CMPLT(r, fr, C67_B2); - C67_invert_test = false; + C67_invert_test = FALSE; } else if (op == TOK_GE) { C67_CMPLT(r, fr, C67_B2); - C67_invert_test = true; + C67_invert_test = TRUE; } else if (op == TOK_GT) { C67_CMPGT(r, fr, C67_B2); - C67_invert_test = false; + C67_invert_test = FALSE; } else if (op == TOK_LE) { C67_CMPGT(r, fr, C67_B2); - C67_invert_test = true; + C67_invert_test = TRUE; } else if (op == TOK_EQ) { C67_CMPEQ(r, fr, C67_B2); - C67_invert_test = false; + C67_invert_test = FALSE; } else if (op == TOK_NE) { C67_CMPEQ(r, fr, C67_B2); - C67_invert_test = true; + C67_invert_test = TRUE; } else if (op == TOK_ULT) { C67_CMPLTU(r, fr, C67_B2); - C67_invert_test = false; + C67_invert_test = FALSE; } else if (op == TOK_UGE) { C67_CMPLTU(r, fr, C67_B2); - C67_invert_test = true; + C67_invert_test = TRUE; } else if (op == TOK_UGT) { C67_CMPGTU(r, fr, C67_B2); - C67_invert_test = false; + C67_invert_test = FALSE; } else if (op == TOK_ULE) { C67_CMPGTU(r, fr, C67_B2); - C67_invert_test = true; + C67_invert_test = TRUE; } else if (op == '+') C67_ADD(fr, r); // ADD r,fr,r else if (op == '-') @@ -2325,7 +2335,7 @@ void gen_opf(int op) if ((ft & VT_BTYPE) == VT_LDOUBLE) - error("long doubles not supported"); + tcc_error("long doubles not supported"); if (op >= TOK_ULT && op <= TOK_GT) { @@ -2340,42 +2350,42 @@ void gen_opf(int op) else C67_CMPLTSP(r, fr, C67_B2); - C67_invert_test = false; + 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; + 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; + 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; + 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; + 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; + C67_invert_test = TRUE; } else { ALWAYS_ASSERT(FALSE); } @@ -2477,7 +2487,7 @@ void gen_cvt_ftoi(int t) r = vtop->r; if (t != VT_INT) - error("long long not supported"); + tcc_error("long long not supported"); else { if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { C67_DPTRUNC(r, r); @@ -2544,5 +2554,7 @@ void ggoto(void) vtop--; } -/* end of X86 code generator */ +/* end of C67 code generator */ +/*************************************************************/ +#endif /*************************************************************/ diff --git a/config.h b/config.h new file mode 100644 index 0000000..03f25d7 --- /dev/null +++ b/config.h @@ -0,0 +1 @@ +#define TCC_VERSION "0.9.26" diff --git a/configure b/configure index 5b38f28..6a7fae5 100755 --- a/configure +++ b/configure @@ -1,21 +1,20 @@ #!/bin/sh # # tcc configure script (c) 2003 Fabrice Bellard -# + # set temporary file name -if test ! -z "$TMPDIR" ; then - TMPDIR1="${TMPDIR}" -elif test ! -z "$TEMPDIR" ; then - TMPDIR1="${TEMPDIR}" -else - TMPDIR1="/tmp" -fi +# if test ! -z "$TMPDIR" ; then +# TMPDIR1="${TMPDIR}" +# elif test ! -z "$TEMPDIR" ; then +# TMPDIR1="${TEMPDIR}" +# else +# TMPDIR1="/tmp" +# fi +# +# bashism: TMPN="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.c" -TMPC="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.c" -TMPO="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.o" -TMPE="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}" -TMPS="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.S" -TMPH="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.h" +TMPN="./conftest-$$" +TMPH=$TMPN.h # default parameters build_cross="no" @@ -27,21 +26,70 @@ libdir="" tccdir="" includedir="" mandir="" +infodir="" sysroot="" cross_prefix="" cc="gcc" host_cc="gcc" ar="ar" strip="strip" +cygwin="no" +gprof="no" +bigendian="no" +mingw32="no" +LIBSUF=".a" +EXESUF="" +tcc_sysincludepaths="" +tcc_libpaths="" +tcc_crtprefix="" +tcc_elfinterp="" +tcc_lddir= +confvars= + cpu=`uname -m` + +# OS specific +targetos=`uname -s` +case $targetos in + MINGW32*) mingw32=yes;; + DragonFly) noldl=yes;; + OpenBSD) noldl=yes;; + *) ;; +esac + +# find source path +# XXX: we assume an absolute path is given when launching configure, +# except in './configure' case. +source_path=${0%configure} +source_path=${source_path%/} +source_path_used="yes" +if test -z "$source_path" -o "$source_path" = "." ; then + source_path=`pwd` + source_path_used="no" +fi + case "$cpu" in - i386|i486|i586|i686|i86pc|BePC) + i386|i486|i586|i686|i86pc|BePC|i686-AT386) cpu="x86" ;; x86_64) cpu="x86-64" ;; - armv4l) + arm*) + case "$cpu" in + arm|armv4l) + cpuver=4 + ;; + armv5tel|armv5tejl) + cpuver=5 + ;; + armv6j|armv6l) + cpuver=6 + ;; + armv7a|armv7l) + cpuver=7 + ;; + esac cpu="armv4l" ;; alpha) @@ -60,52 +108,30 @@ case "$cpu" in cpu="unknown" ;; esac -gprof="no" -bigendian="no" -mingw32="no" -LIBSUF=".a" -EXESUF="" - -# OS specific -targetos=`uname -s` -case $targetos in -MINGW32*) -mingw32="yes" -;; -DragonFly) -noldl="yes" -;; -OpenBSD) -noldl="yes" -;; -*) ;; -esac - -# find source path -# XXX: we assume an absolute path is given when launching configure, -# except in './configure' case. -source_path=${0%configure} -source_path=${source_path%/} -source_path_used="yes" -if test -z "$source_path" -o "$source_path" = "." ; then - source_path=`pwd` - source_path_used="no" -fi for opt do + eval opt=\"$opt\" case "$opt" in --prefix=*) prefix=`echo $opt | cut -d '=' -f 2` ;; --exec-prefix=*) execprefix=`echo $opt | cut -d '=' -f 2` ;; + --tccdir=*) tccdir=`echo $opt | cut -d '=' -f 2` + ;; --bindir=*) bindir=`echo $opt | cut -d '=' -f 2` ;; --libdir=*) libdir=`echo $opt | cut -d '=' -f 2` ;; --includedir=*) includedir=`echo $opt | cut -d '=' -f 2` ;; + --sharedir=*) sharedir=`echo $opt | cut -d '=' -f 2` + ;; --mandir=*) mandir=`echo $opt | cut -d '=' -f 2` ;; + --infodir=*) infodir=`echo $opt | cut -d '=' -f 2` + ;; + --docdir=*) docdir=`echo $opt | cut -d '=' -f 2` + ;; --sysroot=*) sysroot=`echo $opt | cut -d '=' -f 2` ;; --source-path=*) source_path=`echo $opt | cut -d '=' -f 2` @@ -120,193 +146,270 @@ for opt do ;; --extra-libs=*) extralibs=${opt#--extra-libs=} ;; + --sysincludepaths=*) tcc_sysincludepaths=`echo $opt | cut -d '=' -f 2` + ;; + --libpaths=*) tcc_libpaths=`echo $opt | cut -d '=' -f 2` + ;; + --crtprefix=*) tcc_crtprefix=`echo $opt | cut -d '=' -f 2` + ;; + --elfinterp=*) tcc_elfinterp=`echo $opt | cut -d '=' -f 2` + ;; --cpu=*) cpu=`echo $opt | cut -d '=' -f 2` ;; --enable-gprof) gprof="yes" ;; - --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" - ;; + --enable-mingw32) mingw32="yes" ; cross_prefix="i686-pc-mingw32-" ; cpu=x86 + ;; + --enable-cygwin) mingw32="yes" ; cygwin="yes" ; cross_prefix="mingw32-" ; cpu=x86 + ;; --enable-cross) build_cross="yes" ;; + --disable-static) disable_static="yes" + ;; + --disable-rpath) disable_rpath="yes" + ;; + --strip-binaries) strip_binaries="yes" + ;; --with-libgcc) use_libgcc="yes" ;; + --with-selinux) have_selinux="yes" + ;; + --help|-h) show_help="yes" + ;; + *) echo "configure: WARNING: unrecognized option $opt" + ;; esac done # Checking for CFLAGS if test -z "$CFLAGS"; then - CFLAGS="-O2" + CFLAGS="-Wall -g -O2" fi -cc="${cross_prefix}${cc}" -ar="${cross_prefix}${ar}" -strip="${cross_prefix}${strip}" - if test "$mingw32" = "yes" ; then + if test x"$tccdir" = x""; then + tccdir="tcc" + fi + if test -z "$prefix" ; then + prefix="C:/Program Files/${tccdir}" + fi + if test -z "$sharedir" ; then + sharedir="${prefix}" + fi + execprefix="$prefix" + bindir="${prefix}" + tccdir="${prefix}" + libdir="${prefix}/lib" + docdir="${sharedir}/doc" + mandir="${sharedir}/man" + infodir="${sharedir}/info" LIBSUF=".lib" EXESUF=".exe" -fi - -if test -z "$cross_prefix" ; then - -# --- -# big/little endian test -cat > $TMPC << EOF -#include -int main(int argc, char ** argv){ - volatile uint32_t i=0x01234567; - return (*((uint8_t*)(&i))) == 0x67; -} -EOF - -if $cc -o $TMPE $TMPC 2>/dev/null ; then - $TMPE && bigendian="yes" else - echo big/little test failed -fi - -else - -# if cross compiling, cannot launch a program, so make a static guess -if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" ; then - bigendian="yes" -fi - -fi - -# check gcc version -cat > $TMPC < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2) -return 0; -#else -#error gcc < 3.2 -#endif -} -EOF - -gcc_major="2" -if $cc -o $TMPO $TMPC 2> /dev/null ; then - gcc_major="3" -fi -cat > $TMPC <= 4 -return 0; -#else -#error gcc < 4 -#endif -} -EOF + if test -z "$prefix" ; then + prefix="/usr/local" + fi + if test -z "$sharedir" ; then + sharedir="${prefix}/share" + fi + if test x"$execprefix" = x""; then + execprefix="${prefix}" + fi + if test x"$libdir" = x""; then + libdir="${execprefix}/lib" + fi + if test x"$bindir" = x""; then + bindir="${execprefix}/bin" + fi + if test x"$tccdir" = x""; then + tccdir="tcc" + fi + if test x"$docdir" = x""; then + docdir="${sharedir}/doc/${tccdir}" + fi + if test x"$mandir" = x""; then + mandir="${sharedir}/man" + fi + if test x"$infodir" = x""; then + infodir="${sharedir}/info" + fi + tccdir="${libdir}/${tccdir}" +fi # mingw32 -if $cc -o $TMPO $TMPC 2> /dev/null ; then - gcc_major="4" +if test x"$includedir" = x""; then +includedir="${prefix}/include" fi -if test x"$1" = x"-h" -o x"$1" = x"--help" ; then +if test x"$show_help" = "xyes" ; then cat << EOF - Usage: configure [options] Options: [defaults in brackets after descriptions] +Standard options: + --help print this message + --prefix=PREFIX install in PREFIX [$prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --tccdir=DIR installation directory [EPREFIX/lib/tcc] + --includedir=DIR C header files in DIR [PREFIX/include] + --sharedir=DIR documentation root DIR [PREFIX/share] + --docdir=DIR documentation in DIR [SHAREDIR/doc/tcc] + --mandir=DIR man documentation in DIR [SHAREDIR/man] + --infodir=DIR info documentation in DIR [SHAREDIR/info] + +Advanced options (experts only): + --source-path=PATH path of source code [$source_path] + --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix] + --sysroot=PREFIX prepend PREFIX to library/include paths [] + --cc=CC use C compiler CC [$cc] + --extra-cflags= specify compiler flags [$CFLAGS] + --extra-ldflags= specify linker options [] + --strip-binaries strip symbol tables from resulting binaries + --disable-static make libtcc.so instead of libtcc.a + --disable-rpath disable use of -rpath with the above + --with-libgcc use /lib/libgcc_s.so.1 instead of libtcc.a + --enable-mingw32 build windows version on linux with mingw32 + --enable-cygwin build windows version on windows with cygwin + --enable-cross build cross compilers + --with-selinux use mmap for exec mem [needs writable /tmp] + --sysincludepaths=... specify system include paths, colon separated + --libpaths=... specify system library paths, colon separated + --crtprefix=... specify locations of crt?.o, colon separated + --elfinterp=... specify elf interpreter EOF -echo "Standard options:" -echo " --help print this message" -echo " --prefix=PREFIX install in PREFIX [$prefix]" -echo " --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX" -echo " [same as prefix]" -echo " --bindir=DIR user executables in DIR [EPREFIX/bin]" -echo " --libdir=DIR object code libraries in DIR [EPREFIX/lib]" -echo " --includedir=DIR C header files in DIR [PREFIX/include]" -echo " --mandir=DIR man documentation in DIR [PREFIX/man]" -echo " --enable-cross build cross compilers" -echo "" -echo "Advanced options (experts only):" -echo " --source-path=PATH path of source code [$source_path]" -echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" -echo " --sysroot=PREFIX prepend PREFIX to library/include paths []" -echo " --cc=CC use C compiler CC [$cc]" -echo " --with-libgcc use /lib/libgcc_s.so.1 instead of libtcc1.a" -echo "" #echo "NOTE: The object files are build at the place where configure is launched" exit 1 fi -if test "$mingw32" = "yes" ; then - if test -z "$prefix" ; then - prefix="C:/Program Files/tcc" +cc="${cross_prefix}${cc}" +ar="${cross_prefix}${ar}" +strip="${cross_prefix}${strip}" + +CONFTEST=./conftest$EXESUF + +if test -z "$cross_prefix" ; then + if ! $cc -o $CONFTEST $source_path/conftest.c 2>/dev/null ; then + echo "configure: error: '$cc' failed to compile conftest.c." + else + bigendian="$($CONFTEST bigendian)" + gcc_major="$($CONFTEST version)" + gcc_minor="$($CONFTEST minor)" + if test "$mingw32" = "no" ; then + triplet="$($CONFTEST triplet)" + if test -f "/usr/lib/$triplet/crti.o" ; then + tcc_lddir="lib/$triplet" + multiarch_triplet="$triplet" + elif test -f "/usr/lib64/crti.o" ; then + tcc_lddir="lib64" + fi + + if test "$cpu" = "armv4l" ; then + if test "${triplet%eabihf}" != "$triplet" ; then + confvars="$confvars arm_eabihf" + elif test "${triplet%eabi}" != "$triplet" ; then + confvars="$confvars arm_eabi" + fi + if grep -s -q "^Features.* \(vfp\|iwmmxt\) " /proc/cpuinfo ; then + confvars="$confvars arm_vfp" + fi + fi + +# multiarch_triplet=${libc_dir#*/} +# multiarch_triplet=${multiarch_triplet%/} +# tcc_lddir="${libc_dir%%/*}" +# if test -n "$multiarch_triplet" ; then +# tcc_lddir="$tcc_lddir/$multiarch_triplet" +# fi + + if test -f "/lib/ld-uClibc.so.0" ; then + confvars="$confvars uClibc" + fi +# gr: maybe for after the release: +# tcc_elfinterp="$(ldd $CONFTEST | grep 'ld.*.so' | sed 's,\s*\(\S\+\).*,\1,')" + # echo "elfinterp $tcc_elfinterp" + fi - execprefix="$prefix" - bindir="$prefix" - tccdir="$prefix" - docdir="$prefix/doc" + fi else - if test -z "$prefix" ; then - prefix="/usr/local" - fi - if test x"$execprefix" = x""; then - execprefix="${prefix}" - fi - if test x"$bindir" = x""; then - bindir="${execprefix}/bin" - fi - if test x"$docdir" = x""; then - docdir="$prefix/share/doc/tcc" - fi -fi # mingw32 - -if test x"$libdir" = x""; then -libdir="${execprefix}/lib" -fi -if test x"$tccdir" = x""; then -tccdir="${execprefix}/lib/tcc" -fi -if test x"$mandir" = x""; then -mandir="${prefix}/man" -fi -if test x"$includedir" = x""; then -includedir="${prefix}/include" + # if cross compiling, cannot launch a program, so make a static guess + case $cpu in + powerpc|mips|s390) bigendian=yes;; + esac fi -echo "Binary directory $bindir" -echo "TinyCC directory $tccdir" -echo "Library directory $libdir" -echo "Include directory $includedir" -echo "Manual directory $mandir" -echo "Doc directory $docdir" -echo "Target root prefix $sysroot" -echo "Source path $source_path" -echo "C compiler $cc" -echo "CPU $cpu" -echo "Big Endian $bigendian" -echo "gprof enabled $gprof" -echo "cross compilers $build_cross" -echo "use libgcc $use_libgcc" +cat < config.mak +cat >config.mak <> $TMPH + echo "# define $1 \"$2\"" >> $TMPH + echo "#endif" >> $TMPH + fi +} +print_mak() { + if test -n "$2"; then + echo "NATIVE_DEFINES+=-D$1=\"\\\"$2\\\"\"" >> config.mak + fi +} + echo "/* Automatically generated by configure - do not modify */" > $TMPH -echo "prefix=$prefix" >> config.mak -echo "bindir=$bindir" >> config.mak -echo "tccdir=$tccdir" >> config.mak -echo "libdir=$libdir" >> config.mak -echo "includedir=$includedir" >> config.mak -echo "mandir=$mandir" >> config.mak -echo "docdir=$docdir" >> config.mak -echo "#define CONFIG_SYSROOT \"$sysroot\"" >> $TMPH -echo "#define CONFIG_TCCDIR \"$tccdir\"" >> $TMPH -echo "CC=$cc" >> config.mak -echo "GCC_MAJOR=$gcc_major" >> config.mak +print_inc CONFIG_SYSROOT "$sysroot" +print_inc CONFIG_TCCDIR "$tccdir" +print_mak CONFIG_TCC_SYSINCLUDEPATHS "$tcc_sysincludepaths" +print_mak CONFIG_TCC_LIBPATHS "$tcc_libpaths" +print_mak CONFIG_TCC_CRTPREFIX "$tcc_crtprefix" +print_mak CONFIG_TCC_ELFINTERP "$tcc_elfinterp" +print_mak CONFIG_LDDIR "$tcc_lddir" +print_mak CONFIG_MULTIARCHDIR "$multiarch_triplet" + echo "#define GCC_MAJOR $gcc_major" >> $TMPH -echo "HOST_CC=$host_cc" >> config.mak -echo "AR=$ar" >> config.mak -echo "STRIP=$strip -s -R .comment -R .note" >> config.mak -echo "CFLAGS=$CFLAGS" >> config.mak -echo "LDFLAGS=$LDFLAGS" >> config.mak -echo "LIBSUF=$LIBSUF" >> config.mak -echo "EXESUF=$EXESUF" >> config.mak +echo "#define GCC_MINOR $gcc_minor" >> $TMPH + if test "$cpu" = "x86" ; then echo "ARCH=i386" >> config.mak echo "#define HOST_I386 1" >> $TMPH @@ -316,6 +419,7 @@ elif test "$cpu" = "x86-64" ; then elif test "$cpu" = "armv4l" ; then echo "ARCH=arm" >> config.mak echo "#define HOST_ARM 1" >> $TMPH + echo "#define TCC_ARM_VERSION $cpuver" >> $TMPH elif test "$cpu" = "powerpc" ; then echo "ARCH=ppc" >> config.mak echo "#define HOST_PPC 1" >> $TMPH @@ -332,6 +436,12 @@ else echo "Unsupported CPU" exit 1 fi + +echo "TARGETOS=$targetos" >> config.mak + +for v in $confvars ; do + echo "CONFIG_$v=yes" >> config.mak +done if test "$noldl" = "yes" ; then echo "CONFIG_NOLDL=yes" >> config.mak fi @@ -339,6 +449,12 @@ if test "$mingw32" = "yes" ; then echo "CONFIG_WIN32=yes" >> config.mak echo "#define CONFIG_WIN32 1" >> $TMPH fi +if test "$cygwin" = "yes" ; then + echo "#ifndef _WIN32" >> $TMPH + echo "# define _WIN32" >> $TMPH + echo "#endif" >> $TMPH + echo "AR=ar" >> config.mak +fi if test "$bigendian" = "yes" ; then echo "WORDS_BIGENDIAN=yes" >> config.mak echo "#define WORDS_BIGENDIAN 1" >> $TMPH @@ -350,27 +466,39 @@ fi if test "$build_cross" = "yes" ; then echo "CONFIG_CROSS=yes" >> config.mak fi +if test "$disable_static" = "yes" ; then + echo "DISABLE_STATIC=yes" >> config.mak +fi +if test "$disable_rpath" = "yes" ; then + echo "DISABLE_RPATH=yes" >> config.mak +fi +if test "$strip_binaries" = "yes" ; then + echo "STRIP_BINARIES=yes" >> config.mak +fi if test "$use_libgcc" = "yes" ; then echo "#define CONFIG_USE_LIBGCC" >> $TMPH echo "CONFIG_USE_LIBGCC=yes" >> config.mak fi +if test "$have_selinux" = "yes" ; then + echo "#define HAVE_SELINUX" >> $TMPH + echo "HAVE_SELINUX=yes" >> config.mak +fi + version=`head $source_path/VERSION` echo "VERSION=$version" >>config.mak echo "#define TCC_VERSION \"$version\"" >> $TMPH echo "@set VERSION $version" > config.texi +echo "SRC_PATH=$source_path" >>config.mak -# build tree in object directory if source path is different from current one if test "$source_path_used" = "yes" ; then - DIRS="tests" - FILES="Makefile tests/Makefile" - for dir in $DIRS ; do - mkdir -p $dir - done - for f in $FILES ; do - ln -sf $source_path/$f $f - done + case $source_path in + /*) echo "top_srcdir=$source_path";; + *) echo "top_srcdir=\$(TOP)/$source_path";; + esac >>config.mak +else + echo 'top_srcdir=$(TOP)' >>config.mak fi -echo "SRC_PATH=$source_path" >> config.mak +echo 'top_builddir=$(TOP)' >>config.mak diff $TMPH config.h >/dev/null 2>&1 if test $? -ne 0 ; then @@ -379,4 +507,34 @@ else echo "config.h is unchanged" fi -rm -f $TMPO $TMPC $TMPE $TMPS $TMPH +rm -f $TMPN* $CONFTEST + +# --------------------------------------------------------------------------- +# build tree in object directory if source path is different from current one + +fn_makelink() +{ + tgt=$1/$2 + case $2 in + */*) dn=${2%/*} + test -d $dn || mkdir -p $dn + case $1 in + /*) ;; + *) while test $dn ; do + tgt=../$tgt; dn=${dn#${dn%%/*}}; dn=${dn#/} + done + ;; + esac + ;; + esac + ln -sfn $tgt $2 +} + +if test "$source_path_used" = "yes" ; then + FILES="Makefile lib/Makefile tests/Makefile tests/tests2/Makefile" + for f in $FILES ; do + fn_makelink $source_path $f + done +fi + +# --------------------------------------------------------------------------- diff --git a/elf.h b/elf.h index 82fd7ed..a82d8f7 100644 --- a/elf.h +++ b/elf.h @@ -318,7 +318,12 @@ typedef struct #define SHT_REL 9 /* Relocation entries, no addends */ #define SHT_SHLIB 10 /* Reserved */ #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ -#define SHT_NUM 12 /* Number of defined types. */ +#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 indices */ +#define SHT_NUM 19 /* Number of defined types. */ #define SHT_LOOS 0x60000000 /* Start OS-specific */ #define SHT_LOSUNW 0x6ffffffb /* Sun-specific low bound. */ #define SHT_SUNW_COMDAT 0x6ffffffb @@ -343,6 +348,17 @@ typedef struct #define SHF_EXECINSTR (1 << 2) /* Executable */ #define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_MERGE 0x10 +#define SHF_STRINGS 0x20 +#define SHF_INFO_LINK 0x40 +#define SHF_LINK_ORDER 0x80 +#define SHF_OS_NONCONFORMING 0x100 +#define SHF_GROUP 0x200 +#define SHF_TLS 0x400 +#define SHF_MASKOS 0x0ff00000 +#define SHF_ORDERED 0x40000000 +#define SHF_EXCLUDE 0x80000000 + /* Symbol table entry. */ typedef struct @@ -431,6 +447,7 @@ typedef struct #define STT_SECTION 3 /* Symbol associated with a section */ #define STT_FILE 4 /* Symbol's name is file name */ #define STT_NUM 5 /* Number of defined types. */ +#define STT_GNU_IFUNC 10 /* Symbol is a indirect code object */ #define STT_LOOS 11 /* Start of OS-specific */ #define STT_HIOS 12 /* End of OS-specific */ #define STT_LOPROC 13 /* Start of processor-specific */ @@ -949,6 +966,10 @@ typedef struct /* Keep this the last entry. */ #define R_386_NUM 11 +/* TCC-specific 16-bit relocs. */ +#define R_386_16 12 /* Direct 16 bit */ +#define R_386_PC16 13 /* PC relative 16 bit */ + /* SUN SPARC specific definitions. */ /* Values for Elf64_Ehdr.e_flags. */ @@ -1652,7 +1673,7 @@ typedef Elf32_Addr Elf32_Conflict; #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_CALL 10 #define R_ARM_THM_PC8 11 #define R_ARM_AMP_VCALL9 12 #define R_ARM_SWI24 13 @@ -1669,7 +1690,13 @@ typedef Elf32_Addr Elf32_Conflict; #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_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_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ @@ -1701,14 +1728,4 @@ typedef Elf32_Addr Elf32_Conflict; #define R_C60HI16 0x55 // high 16 bit MVKH embedded #define R_C60LO16 0x54 // low 16 bit MVKL embedded -#ifdef TCC_TARGET_X86_64 -#define TCC_ELFCLASS ELFCLASS64 -#define ElfW(type) Elf##64##_##type -#define ELFW(type) ELF##64##_##type -#else -#define TCC_ELFCLASS ELFCLASS32 -#define ElfW(type) Elf##32##_##type -#define ELFW(type) ELF##32##_##type -#endif - #endif /* elf.h */ diff --git a/examples/ex1.c b/examples/ex1.c index 28139f9..3d2a3e1 100755 --- a/examples/ex1.c +++ b/examples/ex1.c @@ -1,4 +1,4 @@ -#! /usr/local/bin/tcc -run +#!/usr/local/bin/tcc -run #include int main() diff --git a/examples/ex4.c b/examples/ex4.c index b33b033..f92c0da 100755 --- a/examples/ex4.c +++ b/examples/ex4.c @@ -1,4 +1,4 @@ -#!./tcc -run -L/usr/X11R6/lib -lX11 +#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11 #include #include #include diff --git a/i386-asm.c b/i386-asm.c index 21b28d7..664aade 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -1,7 +1,8 @@ /* * 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 @@ -18,66 +19,80 @@ * 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 -typedef struct ASMInstr { - uint16_t sym; - uint16_t opcode; - uint16_t instr_type; -#define OPC_JMP 0x01 /* jmp operand */ -#define OPC_B 0x02 /* only used zith 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 TOK_ASM_first TOK_ASM_clc +#define TOK_ASM_last TOK_ASM_emms + +#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 */ -#define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_MMX 3 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_SSE 4 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_CR 5 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_TR 6 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_DB 7 /* warning: value is hardcoded from TOK_ASM_xxx */ -#define OPT_SEG 8 -#define OPT_ST 9 -#define OPT_IM8 10 -#define OPT_IM8S 11 -#define OPT_IM16 12 -#define OPT_IM32 13 -#define OPT_EAX 14 /* %al, %ax or %eax register */ -#define OPT_ST0 15 /* %st(0) register */ -#define OPT_CL 16 /* %cl register */ -#define OPT_DX 17 /* %dx register */ -#define OPT_ADDR 18 /* OP_EA with only offset */ -#define OPT_INDIR 19 /* *(expr) */ - -/* composite types */ -#define OPT_COMPOSITE_FIRST 20 -#define OPT_IM 20 /* IM8 | IM16 | IM32 */ -#define OPT_REG 21 /* REG8 | REG16 | REG32 */ -#define OPT_REGW 22 /* REG16 | REG32 */ -#define OPT_IMW 23 /* IM16 | IM32 */ - -/* can be ored with any OPT_xxx */ -#define OPT_EA 0x80 - - uint8_t nb_ops; - uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ -} ASMInstr; + 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 +}; -typedef struct Operand { - uint32_t type; #define OP_REG8 (1 << OPT_REG8) #define OP_REG16 (1 << OPT_REG16) #define OP_REG32 (1 << OPT_REG32) @@ -98,26 +113,56 @@ typedef struct Operand { #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) -#define OP_IM OP_IM32 +#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[5] = { +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, 1, 0, 2, 0, 0, 0, 3 }; - -#define WORD_PREFIX_OPCODE 0x66 #define NB_TEST_OPCODES 30 @@ -170,8 +215,11 @@ static const ASMInstr asm_instrs[] = { #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 }}, -#include "i386-asm.h" - +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif /* last operation */ { 0, }, }; @@ -183,13 +231,20 @@ static const uint16_t op0_codes[] = { #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) -#include "i386-asm.h" +#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: @@ -214,19 +269,26 @@ static inline int get_reg_shift(TCCState *s1) static int asm_parse_reg(void) { - int reg; + int reg = 0; if (tok != '%') goto error_32; next(); if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { reg = tok - TOK_ASM_eax; - next(); - return reg; +#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("32 bit register"); - return 0; + expect("register"); } + next(); + return reg; } static void parse_operand(TCCState *s1, Operand *op) @@ -247,11 +309,11 @@ static void parse_operand(TCCState *s1, Operand *op) 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_EAX) + if ((op->type & OP_REG) && op->reg == TREG_XAX) op->type |= OP_EAX; - else if (op->type == OP_REG8 && op->reg == TREG_ECX) + else if (op->type == OP_REG8 && op->reg == TREG_XCX) op->type |= OP_CL; - else if (op->type == OP_REG16 && op->reg == TREG_EDX) + 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; @@ -280,7 +342,7 @@ static void parse_operand(TCCState *s1, Operand *op) goto no_skip; } else { reg_error: - error("unknown register"); + tcc_error("unknown register"); } next(); no_skip: ; @@ -288,7 +350,7 @@ static void parse_operand(TCCState *s1, Operand *op) /* constant value */ next(); asm_expr(s1, &e); - op->type = OP_IM32; + op->type = OP_IM; op->e.v = e.v; op->e.sym = e.sym; if (!op->e.sym) { @@ -298,6 +360,10 @@ static void parse_operand(TCCState *s1, Operand *op) 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 */ @@ -310,8 +376,20 @@ static void parse_operand(TCCState *s1, Operand *op) op->e.v = e.v; op->e.sym = e.sym; } else { - op->e.v = 0; - op->e.sym = NULL; + 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(); @@ -322,7 +400,7 @@ static void parse_operand(TCCState *s1, Operand *op) next(); if (tok != ',') { op->reg2 = asm_parse_reg(); - } + } if (tok == ',') { next(); op->shift = get_reg_shift(s1); @@ -337,15 +415,45 @@ static void parse_operand(TCCState *s1, Operand *op) } /* XXX: unify with C code output ? */ -static void gen_expr32(ExprValue *pe) +ST_FUNC void gen_expr32(ExprValue *pe) { - if (pe->sym) - greloc(cur_text_section, pe->sym, ind, R_386_32); - gen_le32(pe->v); + 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; @@ -355,25 +463,19 @@ static void gen_disp32(ExprValue *pe) 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 + (long)sym->next - ind - 4); + gen_le16(pe->v + sym->jnext - ind - 2); } else { - greloc(cur_text_section, sym, ind, R_386_PC32); - gen_le32(pe->v - 4); + 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_PC32, 0); - gen_le32(pe->v - 4); + put_elf_reloc(symtab_section, cur_text_section, + ind, R_386_PC16, 0); + gen_le16(pe->v - 2); } } - - -static void gen_le16(int v) -{ - g(v); - g(v >> 8); -} +#endif /* generate the modrm operand */ static inline void asm_modrm(int reg, Operand *op) @@ -384,8 +486,16 @@ static inline void asm_modrm(int reg, Operand *op) g(0xc0 + (reg << 3) + op->reg); } else if (op->reg == -1 && op->reg2 == -1) { /* displacement only */ - g(0x05 + (reg << 3)); - gen_expr32(&op->e); +#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 */ @@ -403,6 +513,9 @@ static inline void asm_modrm(int reg, Operand *op) 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 */ @@ -411,23 +524,72 @@ static inline void asm_modrm(int reg, Operand *op) 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) { - gen_expr32(&op->e); +#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); } } } -static void asm_opcode(TCCState *s1, int opcode) +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, ss; + 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; @@ -437,19 +599,20 @@ static void asm_opcode(TCCState *s1, int opcode) if (tok == ';' || tok == TOK_LINEFEED) break; if (nb_ops >= MAX_OPERANDS) { - error("incorrect number of operands"); + tcc_error("incorrect number of operands"); } parse_operand(s1, pop); if (tok == ':') { - if (pop->type != OP_SEG || seg_prefix) { - error("incorrect prefix"); - } + 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)) { - error("segment prefix must be followed by memory reference"); + tcc_error("segment prefix must be followed by memory reference"); } +#endif } pop++; nb_ops++; @@ -460,7 +623,7 @@ static void asm_opcode(TCCState *s1, int opcode) 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++) { @@ -470,23 +633,22 @@ static void asm_opcode(TCCState *s1, int opcode) if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) continue; } else if (pa->instr_type & OPC_ARITH) { - if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4)) + if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX)) continue; - goto compute_size; + s = (opcode - pa->sym) % NBWLX; } else if (pa->instr_type & OPC_SHIFT) { - if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4)) + if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX)) continue; - goto compute_size; + 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 + 3)) + if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX)) continue; - compute_size: - s = (opcode - pa->sym) & 3; - } else if (pa->instr_type & OPC_WL) { - if (!(opcode >= pa->sym && opcode <= pa->sym + 2)) + 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 { @@ -502,17 +664,22 @@ static void asm_opcode(TCCState *s1, int opcode) op2 = op1 & 0x1f; switch(op2) { case OPT_IM: - v = OP_IM8 | OP_IM16 | OP_IM32; + v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64; break; case OPT_REG: - v = OP_REG8 | OP_REG16 | OP_REG32; + v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64; break; case OPT_REGW: - v = OP_REG16 | OP_REG32; + 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; @@ -528,39 +695,90 @@ static void asm_opcode(TCCState *s1, int opcode) next: ; } if (pa->sym == 0) { - if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) { + if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) { int b; - b = op0_codes[opcode - TOK_ASM_pusha]; + 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 { - error("unknown opcode '%s'", + 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 == 3) { - for(i = 0; s == 3 && i < nb_ops; i++) { + 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 == 3) { - if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && - (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32))) + 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 - error("cannot infer opcode suffix"); + 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 */ - ss = s; if (s == 1 || (pa->instr_type & OPC_D16)) - g(WORD_PREFIX_OPCODE); - else if (s == 2) - s = 1; + 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); @@ -568,10 +786,11 @@ static void asm_opcode(TCCState *s1, int opcode) g(seg_prefix); v = pa->opcode; - if (v == 0x69 || v == 0x69) { + 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; @@ -585,7 +804,7 @@ static void asm_opcode(TCCState *s1, int opcode) nb_ops = 0; } else if (v <= 0x05) { /* arith case */ - v += ((opcode - TOK_ASM_addb) >> 2) << 3; + 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; @@ -602,9 +821,9 @@ static void asm_opcode(TCCState *s1, int opcode) v += 7; } if (pa->instr_type & OPC_B) - v += s; + v += s >= 1; if (pa->instr_type & OPC_TEST) - v += test_bits[opcode - pa->sym]; + v += test_bits[opcode - pa->sym]; if (pa->instr_type & OPC_SHORTJMP) { Sym *sym; int jmp_disp; @@ -615,7 +834,7 @@ static void asm_opcode(TCCState *s1, int opcode) goto no_short_jump; if (sym->r != cur_text_section->sh_num) goto no_short_jump; - jmp_disp = ops[0].e.v + (long)sym->next - ind - 2; + 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; @@ -627,10 +846,10 @@ static void asm_opcode(TCCState *s1, int opcode) opcode slightly */ if (v == 0xeb) v = 0xe9; - else + else v += 0x0f10; } else { - error("invalid displacement"); + tcc_error("invalid displacement"); } } } @@ -638,15 +857,15 @@ static void asm_opcode(TCCState *s1, int opcode) 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) >> 2; + reg = (opcode - pa->sym) / NBWLX; if (reg == 6) reg = 7; } else if (pa->instr_type & OPC_ARITH) { - reg = (opcode - pa->sym) >> 2; + reg = (opcode - pa->sym) / NBWLX; } else if (pa->instr_type & OPC_FARITH) { reg = (opcode - pa->sym) / 6; } else { @@ -664,15 +883,15 @@ static void asm_opcode(TCCState *s1, int opcode) goto modrm_found; } #ifdef ASM_DEBUG - error("bad op table"); -#endif + 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 && + if (i != modrm_index && (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { reg = ops[i].reg; break; @@ -683,55 +902,96 @@ static void asm_opcode(TCCState *s1, int opcode) } /* emit constants */ +#ifndef TCC_TARGET_X86_64 if (pa->opcode == 0x9a || pa->opcode == 0xea) { /* ljmp or lcall kludge */ - gen_expr32(&ops[1].e); +#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) - error("cannot relocate"); + tcc_error("cannot relocate"); gen_le16(ops[0].e.v); - } else { - for(i = 0;i < nb_ops; i++) { - v = op_type[i]; - if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) { - /* if multiple sizes are given it means we must look - at the op size */ - if (v == (OP_IM8 | OP_IM16 | OP_IM32) || - v == (OP_IM16 | OP_IM32)) { - if (ss == 0) - v = OP_IM8; - else if (ss == 1) - v = OP_IM16; - else - v = OP_IM32; - } - if (v & (OP_IM8 | OP_IM8S)) { - if (ops[i].e.sym) - goto error_relocate; - g(ops[i].e.v); - } else if (v & OP_IM16) { - if (ops[i].e.sym) { - error_relocate: - error("cannot relocate"); - } + 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 { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - if (is_short_jmp) - g(ops[i].e.v); - 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 } -#define NB_SAVED_REGS 3 -#define NB_ASM_REGS 8 - /* return the constraint priority (we allocate first the lowest numbered constraints) */ static inline int constraint_priority(const char *str) @@ -772,7 +1032,7 @@ static inline int constraint_priority(const char *str) pr = 4; break; default: - error("unknown constraint '%c'", c); + tcc_error("unknown constraint '%c'", c); pr = 0; } if (pr > priority) @@ -793,8 +1053,8 @@ static const char *skip_constraint_modifiers(const char *p) #define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) -static void asm_compute_constraints(ASMOperand *operands, - int nb_operands, int nb_outputs, +ST_FUNC void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg) { @@ -803,7 +1063,7 @@ static void asm_compute_constraints(ASMOperand *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= i || i < nb_outputs) - error("invalid reference in constraint %d ('%s')", + tcc_error("invalid reference in constraint %d ('%s')", i, str); op->ref_index = k; if (operands[k].input_index >= 0) - error("cannot reference twice the same operand"); + 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_outputs) - error("'%c' modifier can only be applied to outputs", c); + 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_EAX) || - is_reg_allocated(TREG_EDX)) + if (is_reg_allocated(TREG_XAX) || + is_reg_allocated(TREG_XDX)) goto try_next; op->is_llong = 1; - op->reg = TREG_EAX; - regs_allocated[TREG_EAX] |= reg_mask; - regs_allocated[TREG_EDX] |= reg_mask; + op->reg = TREG_XAX; + regs_allocated[TREG_XAX] |= reg_mask; + regs_allocated[TREG_XDX] |= reg_mask; break; case 'a': - reg = TREG_EAX; + reg = TREG_XAX; goto alloc_reg; case 'b': reg = 3; goto alloc_reg; case 'c': - reg = TREG_ECX; + reg = TREG_XCX; goto alloc_reg; case 'd': - reg = TREG_EDX; + reg = TREG_XDX; goto alloc_reg; case 'S': reg = 6; @@ -978,7 +1238,7 @@ static void asm_compute_constraints(ASMOperand *operands, } break; default: - error("asm constraint %d ('%s') could not be satisfied", + tcc_error("asm constraint %d ('%s') could not be satisfied", j, op->constraint); break; } @@ -988,34 +1248,34 @@ static void asm_compute_constraints(ASMOperand *operands, 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;ireg >= 0 && + 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; } - error("could not find free output register for reloading"); + 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;iid ? get_tok_str(op->id, NULL) : "", + 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); @@ -1025,7 +1285,7 @@ static void asm_compute_constraints(ASMOperand *operands, #endif } -static void subst_asm_operand(CString *add_str, +ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier) { int r, reg, size, val; @@ -1054,21 +1314,25 @@ static void subst_asm_operand(CString *add_str, } else if (r & VT_LVAL) { reg = r & VT_VALMASK; if (reg >= VT_CONST) - error("internal compiler error"); - snprintf(buf, sizeof(buf), "(%%%s)", + 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) - error("internal compiler error"); + 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) @@ -1076,14 +1340,18 @@ static void subst_asm_operand(CString *add_str, if (modifier == 'b') { if (reg >= 4) - error("cannot use byte register"); + tcc_error("cannot use byte register"); size = 1; } else if (modifier == 'h') { if (reg >= 4) - error("cannot use byte register"); + 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) { @@ -1099,6 +1367,11 @@ static void subst_asm_operand(CString *add_str, 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); @@ -1106,7 +1379,7 @@ static void subst_asm_operand(CString *add_str, } /* generate prolog and epilog code for asm statment */ -static void asm_gen_code(ASMOperand *operands, int nb_operands, +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg) @@ -1127,8 +1400,13 @@ static void asm_gen_code(ASMOperand *operands, int nb_operands, /* generate reg save code */ for(i = 0; i < NB_SAVED_REGS; i++) { reg = reg_saved[i]; - if (regs_allocated[reg]) + if (regs_allocated[reg]) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + g(0x66); +#endif g(0x50 + reg); + } } /* generate load code */ @@ -1150,7 +1428,7 @@ static void asm_gen_code(ASMOperand *operands, int nb_operands, SValue sv; sv = *op->vt; sv.c.ul += 4; - load(TREG_EDX, &sv); + load(TREG_XDX, &sv); } } } @@ -1176,7 +1454,7 @@ static void asm_gen_code(ASMOperand *operands, int nb_operands, SValue sv; sv = *op->vt; sv.c.ul += 4; - store(TREG_EDX, &sv); + store(TREG_XDX, &sv); } } } @@ -1184,18 +1462,23 @@ static void asm_gen_code(ASMOperand *operands, int nb_operands, /* generate reg restore code */ for(i = NB_SAVED_REGS - 1; i >= 0; i--) { reg = reg_saved[i]; - if (regs_allocated[reg]) + if (regs_allocated[reg]) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + g(0x66); +#endif g(0x58 + reg); + } } } } -static void asm_clobber(uint8_t *clobber_regs, const char *str) +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) { int reg; TokenSym *ts; - if (!strcmp(str, "memory") || + if (!strcmp(str, "memory") || !strcmp(str, "cc")) return; ts = tok_alloc(str, strlen(str)); @@ -1204,8 +1487,12 @@ static void asm_clobber(uint8_t *clobber_regs, const char *str) 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 { - error("invalid clobber register '%s'", str); + tcc_error("invalid clobber register '%s'", str); } clobber_regs[reg] = 1; } diff --git a/i386-asm.h b/i386-asm.h index a3b28d4..760c06d 100644 --- a/i386-asm.h +++ b/i386-asm.h @@ -1,12 +1,12 @@ - DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */ - DEF_ASM_OP0(popa, 0x61) - DEF_ASM_OP0(clc, 0xf8) + 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) @@ -33,8 +33,8 @@ 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(pause, 0xf390) DEF_ASM_OP0(xlat, 0xd7) /* strings */ @@ -74,10 +74,17 @@ 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) - DEF_ASM_OP0(word, 0x66) + 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) @@ -201,6 +208,9 @@ 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)) @@ -212,6 +222,8 @@ ALT(DEF_ASM_OP1(seto, 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)) @@ -230,6 +242,8 @@ 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)) @@ -272,7 +286,6 @@ ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) 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) @@ -350,6 +363,11 @@ ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG)) 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 )) @@ -364,7 +382,15 @@ ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT /* 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 ) @@ -381,6 +407,7 @@ ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT /* 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 ) diff --git a/i386-gen.c b/i386-gen.c index f958ab5..c3f03c7 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -18,8 +18,11 @@ * 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_REGS 4 +#define NB_ASM_REGS 8 /* a register can belong to several classes. The classes must be sorted from more general to more precise (see gv2() code which does @@ -42,13 +45,6 @@ enum { TREG_ST0, }; -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, -}; - /* 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) */ @@ -70,6 +66,9 @@ int reg_classes[NB_REGS] = { /* maximum alignment (for aligned attribute support) */ #define MAX_ALIGN 8 + +#define psym oad + /******************************************************/ /* ELF defines */ @@ -77,6 +76,7 @@ int reg_classes[NB_REGS] = { /* 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 @@ -84,13 +84,25 @@ int reg_classes[NB_REGS] = { #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 unsigned long func_bound_offset; static int func_ret_sub; +#ifdef CONFIG_TCC_BCHECK +static unsigned long func_bound_offset; +#endif /* XXX: make it faster ? */ -void g(int c) +ST_FUNC void g(int c) { int ind1; ind1 = ind + 1; @@ -100,7 +112,7 @@ void g(int c) ind = ind1; } -void o(unsigned int c) +ST_FUNC void o(unsigned int c) { while (c) { g(c); @@ -108,7 +120,13 @@ void o(unsigned int c) } } -void gen_le32(int c) +ST_FUNC void gen_le16(int v) +{ + g(v); + g(v >> 8); +} + +ST_FUNC void gen_le32(int c) { g(c); g(c >> 8); @@ -117,7 +135,7 @@ void gen_le32(int c) } /* output a symbol and patch all calls to it */ -void gsym_addr(int t, int a) +ST_FUNC void gsym_addr(int t, int a) { int n, *ptr; while (t) { @@ -128,7 +146,7 @@ void gsym_addr(int t, int a) } } -void gsym(int t) +ST_FUNC void gsym(int t) { gsym_addr(t, ind); } @@ -138,7 +156,7 @@ void gsym(int t) #define psym oad /* instruction + 4 bytes data. Return the address of the data */ -static int oad(int c, int s) +ST_FUNC int oad(int c, int s) { int ind1; @@ -153,13 +171,20 @@ static int oad(int c, int s) } /* output constant with relocation if 'r & VT_SYM' is true */ -static void gen_addr32(int r, Sym *sym, int c) +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) @@ -183,13 +208,17 @@ static void gen_modrm(int op_reg, int r, Sym *sym, int c) } } - /* load 'r' from value 'sv' */ -void load(int r, SValue *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; @@ -200,8 +229,10 @@ void load(int r, SValue *sv) v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; - load(r, &v1); 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 */ @@ -229,8 +260,13 @@ void load(int r, SValue *sv) o(0xb8 + r); /* mov $xx, r */ gen_addr32(fr, sv->sym, fc); } else if (v == VT_LOCAL) { - o(0x8d); /* lea xxx(%ebp), r */ - gen_modrm(r, VT_LOCAL, sv->sym, fc); + 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 */ @@ -250,10 +286,15 @@ void load(int r, SValue *sv) } /* store register 'r' in lvalue 'v' */ -void store(int r, SValue *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; @@ -326,7 +367,7 @@ static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; /* 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) +ST_FUNC void gfunc_call(int nb_args) { int size, align, r, args_size, i, func_call; Sym *func_sym; @@ -402,6 +443,11 @@ void gfunc_call(int nb_args) } } gcall_or_jmp(0); + +#ifdef TCC_TARGET_PE + if ((func_sym->type.t & VT_BTYPE) == VT_STRUCT) + args_size -= 4; +#endif if (args_size && func_call != FUNC_STDCALL) gadd_sp(args_size); vtop--; @@ -414,7 +460,7 @@ void gfunc_call(int nb_args) #endif /* generate function prolog of type 't' */ -void gfunc_prolog(CType *func_type) +ST_FUNC void gfunc_prolog(CType *func_type) { int addr, align, size, func_call, fastcall_nb_regs; int param_index, param_addr; @@ -426,6 +472,8 @@ void gfunc_prolog(CType *func_type) func_call = FUNC_CALL(sym->r); 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; @@ -478,17 +526,23 @@ void gfunc_prolog(CType *func_type) /* pascal type call ? */ if (func_call == FUNC_STDCALL) func_ret_sub = addr - 8; +#ifdef 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 */ -void gfunc_epilog(void) +ST_FUNC void gfunc_epilog(void) { int v, saved_ind; @@ -559,13 +613,13 @@ void gfunc_epilog(void) } /* generate a jump to a label */ -int gjmp(int t) +ST_FUNC int gjmp(int t) { return psym(0xe9, t); } /* generate a jump to a fixed address */ -void gjmp_addr(int a) +ST_FUNC void gjmp_addr(int a) { int r; r = a - ind - 2; @@ -578,7 +632,7 @@ void gjmp_addr(int a) } /* generate a test. set 'inv' to invert test. Stack entry is popped */ -int gtst(int inv, int t) +ST_FUNC int gtst(int inv, int t) { int v, *p; @@ -623,7 +677,7 @@ int gtst(int inv, int t) } /* generate an integer binary operation */ -void gen_opi(int op) +ST_FUNC void gen_opi(int op) { int r, fr, opc, c; @@ -639,10 +693,16 @@ void gen_opi(int op) vswap(); c = vtop->c.i; if (c == (char)c) { - /* XXX: generate inc and dec for smaller code ? */ - o(0x83); - o(0xc0 | (opc << 3) | r); - g(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); @@ -757,7 +817,7 @@ void gen_opi(int 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) +ST_FUNC void gen_opf(int op) { int a, ft, fc, swapped, r; @@ -869,7 +929,7 @@ void gen_opf(int op) /* convert integers to fp 't' type. Must handle 'int', 'unsigned int' and 'long long' cases. */ -void gen_cvt_itof(int t) +ST_FUNC void gen_cvt_itof(int t) { save_reg(TREG_ST0); gv(RC_INT); @@ -899,13 +959,14 @@ void gen_cvt_itof(int t) /* convert fp to int 't' type */ /* XXX: handle long long case */ -void gen_cvt_ftoi(int t) +ST_FUNC void gen_cvt_ftoi(int t) { int r, r2, size; Sym *sym; CType ushort_type; ushort_type.t = VT_SHORT | VT_UNSIGNED; + ushort_type.ref = 0; gv(RC_FLOAT); if (t != VT_INT) @@ -949,14 +1010,14 @@ void gen_cvt_ftoi(int t) } /* convert from one floating point type to another */ -void gen_cvt_ftof(int t) +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 */ -void ggoto(void) +ST_FUNC void ggoto(void) { gcall_or_jmp(1); vtop--; @@ -966,7 +1027,7 @@ void ggoto(void) #ifdef CONFIG_TCC_BCHECK /* generate a bounded pointer addition */ -void gen_bounded_ptr_add(void) +ST_FUNC void gen_bounded_ptr_add(void) { Sym *sym; @@ -989,7 +1050,7 @@ void gen_bounded_ptr_add(void) /* patch pointer addition in vtop so that pointer dereferencing is also tested */ -void gen_bounded_ptr_deref(void) +ST_FUNC void gen_bounded_ptr_deref(void) { int func; int size, align; @@ -1014,7 +1075,7 @@ void gen_bounded_ptr_deref(void) case 12: func = TOK___bound_ptr_indir12; break; case 16: func = TOK___bound_ptr_indir16; break; default: - error("unhandled size when derefencing bounded pointer"); + tcc_error("unhandled size when dereferencing bounded pointer"); func = 0; break; } @@ -1031,4 +1092,5 @@ void gen_bounded_ptr_deref(void) /* end of X86 code generator */ /*************************************************************/ - +#endif +/*************************************************************/ diff --git a/i386-tok.h b/i386-tok.h new file mode 100644 index 0000000..d1e4bf3 --- /dev/null +++ b/i386-tok.h @@ -0,0 +1,243 @@ +/* ------------------------------------------------------------------ */ +/* 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(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 existant 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/il-gen.c b/il-gen.c index 29f0526..c9b7806 100644 --- a/il-gen.c +++ b/il-gen.c @@ -41,7 +41,7 @@ enum { REG_ST2, }; -int reg_classes[NB_REGS] = { +const int reg_classes[NB_REGS] = { /* ST0 */ RC_ST | RC_ST0, /* ST1 */ RC_ST | RC_ST1, /* ST2 */ RC_ST, @@ -193,7 +193,7 @@ static void il_type_to_str(char *buf, int buf_size, pstrcat(buf, buf_size, tstr); break; case VT_STRUCT: - error("structures not handled yet"); + tcc_error("structures not handled yet"); break; case VT_FUNC: s = sym_find((unsigned)t >> VT_STRUCT_SHIFT); @@ -387,7 +387,7 @@ void gfunc_start(GFuncContext *c, int func_call) void gfunc_param(GFuncContext *c) { if ((vtop->t & VT_BTYPE) == VT_STRUCT) { - error("structures passed as value not handled yet"); + tcc_error("structures passed as value not handled yet"); } else { /* simply push on stack */ gv(RC_ST0); diff --git a/include/stdarg.h b/include/stdarg.h index 86e556c..666adf7 100644 --- a/include/stdarg.h +++ b/include/stdarg.h @@ -2,62 +2,36 @@ #define _STDARG_H #ifdef __x86_64__ -#include +#ifndef _WIN64 -/* GCC compatible definition of va_list. */ -struct __va_list_struct { - unsigned int gp_offset; - unsigned int fp_offset; - union { - unsigned int overflow_offset; - char *overflow_arg_area; - }; - char *reg_save_area; -}; +typedef void *va_list; -typedef struct __va_list_struct *va_list; +va_list __va_start(void *fp); +void *__va_arg(va_list ap, int arg_type, int size); +va_list __va_copy(va_list src); +void __va_end(va_list ap); -/* we use __builtin_(malloc|free) to avoid #define malloc tcc_malloc */ -/* XXX: this lacks the support of aggregated types. */ -#define va_start(ap, last) \ - (ap = (va_list)__builtin_malloc(sizeof(struct __va_list_struct)), \ - *ap = *(struct __va_list_struct*)( \ - (char*)__builtin_frame_address(0) - 16), \ - ap->overflow_arg_area = ((char *)__builtin_frame_address(0) + \ - ap->overflow_offset), \ - ap->reg_save_area = (char *)__builtin_frame_address(0) - 176 - 16 \ - ) -#define va_arg(ap, type) \ - (*(type*)(__builtin_types_compatible_p(type, long double) \ - ? (ap->overflow_arg_area += 16, \ - ap->overflow_arg_area - 16) \ - : __builtin_types_compatible_p(type, double) \ - ? (ap->fp_offset < 128 + 48 \ - ? (ap->fp_offset += 16, \ - ap->reg_save_area + ap->fp_offset - 16) \ - : (ap->overflow_arg_area += 8, \ - ap->overflow_arg_area - 8)) \ - : (ap->gp_offset < 48 \ - ? (ap->gp_offset += 8, \ - ap->reg_save_area + ap->gp_offset - 8) \ - : (ap->overflow_arg_area += 8, \ - ap->overflow_arg_area - 8)) \ - )) -#define va_copy(dest, src) \ - ((dest) = (va_list)malloc(sizeof(struct __va_list_struct)), \ - *(dest) = *(src)) -#define va_end(ap) __builtin_free(ap) - -#else +#define va_start(ap, last) ((ap) = __va_start(__builtin_frame_address(0))) +#define va_arg(ap, type) \ + (*(type *)(__va_arg(ap, __builtin_va_arg_types(type), sizeof(type)))) +#define va_copy(dest, src) ((dest) = __va_copy(src)) +#define va_end(ap) __va_end(ap) +#else /* _WIN64 */ typedef char *va_list; +#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+7)&~7) +#define va_arg(ap,type) (ap += (sizeof(type)+7)&~7, *(type *)(ap - ((sizeof(type)+7)&~7))) +#define va_copy(dest, src) (dest) = (src) +#define va_end(ap) +#endif +#else /* __i386__ */ +typedef char *va_list; /* only correct for i386 */ #define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3) #define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3))) #define va_copy(dest, src) (dest) = (src) #define va_end(ap) - #endif /* fix a buggy dependency on GCC in libio.h */ diff --git a/include/stddef.h b/include/stddef.h index aef5b39..fbc61fc 100644 --- a/include/stddef.h +++ b/include/stddef.h @@ -1,20 +1,28 @@ #ifndef _STDDEF_H #define _STDDEF_H -#define NULL ((void *)0) typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ssize_t; typedef __WCHAR_TYPE__ wchar_t; typedef __PTRDIFF_TYPE__ ptrdiff_t; -#define offsetof(type, field) ((size_t) &((type *)0)->field) +typedef __PTRDIFF_TYPE__ intptr_t; +typedef __SIZE_TYPE__ uintptr_t; #ifndef __int8_t_defined #define __int8_t_defined -typedef char int8_t; -typedef short int int16_t; -typedef int int32_t; -typedef long long int int64_t; +typedef signed char int8_t; +typedef signed short int int16_t; +typedef signed int int32_t; +typedef signed long long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; #endif +#define NULL ((void*)0) +#define offsetof(type, field) ((size_t)&((type *)0)->field) + void *alloca(size_t size); #endif diff --git a/include/varargs.h b/include/varargs.h index daee29e..d614366 100644 --- a/include/varargs.h +++ b/include/varargs.h @@ -1,11 +1,12 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER within this package. + */ #ifndef _VARARGS_H #define _VARARGS_H -#include - -#define va_dcl -#define va_alist __va_alist -#undef va_start -#define va_start(ap) ap = __builtin_varargs_start +#error "TinyCC no longer implements ." +#error "Revise your code to use ." #endif diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..dfac0f8 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,102 @@ +# +# Tiny C Compiler Makefile for libtcc1.a +# + +TOP = .. +include $(TOP)/Makefile +VPATH = $(top_srcdir)/lib $(top_srcdir)/win32/lib + +ifndef TARGET + ifdef CONFIG_WIN64 + TARGET = x86_64-win32 + else + ifdef CONFIG_WIN32 + TARGET = i386-win32 + else + ifeq ($(ARCH),i386) + TARGET = i386 + ifneq ($(TARGETOS),Darwin) + XCC = $(CC) + endif + else + ifeq ($(ARCH),x86-64) + TARGET = x86_64 + ifneq ($(TARGETOS),Darwin) + XCC = $(CC) + endif + endif + endif + endif + endif + BCHECK_O = bcheck.o +endif + +DIR = $(TARGET) + +native : ../libtcc1.a +cross : $(DIR)/libtcc1.a + +native : TCC = $(TOP)/tcc$(EXESUF) +cross : TCC = $(TOP)/$(TARGET)-tcc$(EXESUF) + +I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BCHECK_O) +X86_64_O = libtcc1.o alloca86_64.o +WIN32_O = $(I386_O) crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o +WIN64_O = $(X86_64_O) crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o + +ifeq "$(TARGET)" "i386-win32" + OBJ = $(addprefix $(DIR)/,$(WIN32_O)) + TGT = -DTCC_TARGET_I386 -DTCC_TARGET_PE + XCC = $(TCC) -B$(top_srcdir)/win32 -I$(top_srcdir)/include + XAR = $(DIR)/tiny_libmaker$(EXESUF) +else +ifeq "$(TARGET)" "x86_64-win32" + OBJ = $(addprefix $(DIR)/,$(WIN64_O)) + TGT = -DTCC_TARGET_X86_64 -DTCC_TARGET_PE + XCC = $(TCC) -B$(top_srcdir)/win32 -I$(top_srcdir)/include + XAR = $(DIR)/tiny_libmaker$(EXESUF) +else +ifeq "$(TARGET)" "i386" + OBJ = $(addprefix $(DIR)/,$(I386_O)) + TGT = -DTCC_TARGET_I386 + XCC ?= $(TCC) -B$(TOP) +else +ifeq "$(TARGET)" "x86_64" + OBJ = $(addprefix $(DIR)/,$(X86_64_O)) + TGT = -DTCC_TARGET_X86_64 + XCC ?= $(TCC) -B$(TOP) +else + $(error libtcc1.a not supported on target '$(TARGET)') +endif +endif +endif +endif + +XFLAGS = $(CPPFLAGS) $(CFLAGS) $(TGT) + +ifeq ($(TARGETOS),Darwin) + XAR = $(DIR)/tiny_libmaker$(EXESUF) + XFLAGS += -D_ANSI_SOURCE + BCHECK_O = +endif + +ifdef XAR +AR = $(XAR) +endif + +$(DIR)/libtcc1.a ../libtcc1.a : $(OBJ) $(XAR) + $(AR) rcs $@ $(OBJ) +$(DIR)/%.o : %.c + $(XCC) -c $< -o $@ $(XFLAGS) +$(DIR)/%.o : %.S + $(XCC) -c $< -o $@ $(XFLAGS) +$(DIR)/%$(EXESUF) : $(TOP)/win32/tools/%.c + $(CC) -o $@ $< $(XFLAGS) $(LDFLAGS) + +$(OBJ) $(XAR) : $(DIR)/exists +$(DIR)/exists : + mkdir -p $(DIR) + @echo $@ > $@ + +clean : + rm -rfv i386-win32 x86_64-win32 i386 x86_64 diff --git a/lib/alloca86-bt.S b/lib/alloca86-bt.S index 994da20..ffad515 100644 --- a/lib/alloca86-bt.S +++ b/lib/alloca86-bt.S @@ -1,7 +1,5 @@ /* ---------------------------------------------- */ -/* alloca86b.S */ - -#include "../config.h" +/* alloca86-bt.S */ .globl __bound_alloca @@ -42,4 +40,8 @@ p6: push %edx ret +/* mark stack as nonexecutable */ +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif /* ---------------------------------------------- */ diff --git a/lib/alloca86.S b/lib/alloca86.S index fb208a0..b836efb 100644 --- a/lib/alloca86.S +++ b/lib/alloca86.S @@ -1,8 +1,6 @@ /* ---------------------------------------------- */ /* alloca86.S */ -#include "../config.h" - .globl alloca alloca: @@ -30,4 +28,8 @@ p3: push %edx ret +/* mark stack as nonexecutable */ +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif /* ---------------------------------------------- */ diff --git a/lib/alloca86_64.S b/lib/alloca86_64.S new file mode 100644 index 0000000..dd46ce6 --- /dev/null +++ b/lib/alloca86_64.S @@ -0,0 +1,42 @@ +/* ---------------------------------------------- */ +/* alloca86_64.S */ + +.globl alloca + +alloca: + pop %rdx +#ifdef TCC_TARGET_PE + mov %rcx,%rax +#else + mov %rdi,%rax +#endif + add $15,%rax + and $-16,%rax + jz p3 + +#ifdef TCC_TARGET_PE +p1: + cmp $4096,%rax + jle p2 + sub $4096,%rsp + sub $4096,%rax + test %rax,(%rsp) + jmp p1 +p2: +#endif + + sub %rax,%rsp + mov %rsp,%rax +#ifdef TCC_TARGET_PE + add $32,%rax +#endif + +p3: + push %rdx + ret + +/* mark stack as nonexecutable */ +#if defined __ELF__ && defined __linux__ + .section .note.GNU-stack,"",@progbits +#endif +/* ---------------------------------------------- */ diff --git a/lib/bcheck.c b/lib/bcheck.c index 0ec2a4b..54124b9 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -21,9 +21,13 @@ #include #include #include -#if !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__OpenBSD__) +#if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) \ + && !defined(__DragonFly__) && !defined(__OpenBSD__) #include #endif +#if !defined(_WIN32) +#include +#endif //#define BOUND_DEBUG @@ -33,12 +37,13 @@ /* use malloc hooks. Currently the code cannot be reliable if no hooks */ #define CONFIG_TCC_MALLOC_HOOKS - #define HAVE_MEMALIGN -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__dietlibc__) \ - || defined(__UCLIBC__) || defined(__OpenBSD__) -#warning Bound checking not fully supported in this environment. +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) \ + || defined(__DragonFly__) || defined(__dietlibc__) \ + || defined(__UCLIBC__) || defined(__OpenBSD__) \ + || defined(_WIN32) || defined(TCC_UCLIBC) +#warning Bound checking does not support malloc (etc.) in this environment. #undef CONFIG_TCC_MALLOC_HOOKS #undef HAVE_MEMALIGN #endif @@ -93,9 +98,6 @@ static void *saved_realloc_hook; static void *saved_memalign_hook; #endif -/* linker definitions */ -extern char _end; - /* TCC definitions */ extern char __bounds_start; /* start of static bounds table */ /* error message, just for TCC */ @@ -152,9 +154,6 @@ static void bound_alloc_error(void) bound_error("not enough memory for bound checking code"); } -/* currently, tcc cannot compile that because we use GNUC extensions */ -#if !defined(__TINYC__) - /* return '(p + offset)' for pointer arithmetic (a pointer can reach the end of a region in this case */ void * FASTCALL __bound_ptr_add(void *p, int offset) @@ -203,17 +202,18 @@ void * FASTCALL __bound_ptr_indir ## dsize (void *p, int offset) \ return p + offset; \ } -#ifdef __i386__ +BOUND_PTR_INDIR(1) +BOUND_PTR_INDIR(2) +BOUND_PTR_INDIR(4) +BOUND_PTR_INDIR(8) +BOUND_PTR_INDIR(12) +BOUND_PTR_INDIR(16) + /* return the frame pointer of the caller */ #define GET_CALLER_FP(fp)\ {\ - unsigned long *fp1;\ - __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\ - fp = fp1[0];\ + fp = (unsigned long)__builtin_frame_address(1);\ } -#else -#error put code to extract the calling frame pointer -#endif /* called when entering a function to add all the local regions */ void FASTCALL __bound_local_new(void *p1) @@ -246,34 +246,6 @@ void FASTCALL __bound_local_delete(void *p1) } } -#else - -void __bound_local_new(void *p) -{ -} -void __bound_local_delete(void *p) -{ -} - -void *__bound_ptr_add(void *p, int offset) -{ - return p + offset; -} - -#define BOUND_PTR_INDIR(dsize) \ -void *__bound_ptr_indir ## dsize (void *p, int offset) \ -{ \ - return p + offset; \ -} -#endif - -BOUND_PTR_INDIR(1) -BOUND_PTR_INDIR(2) -BOUND_PTR_INDIR(4) -BOUND_PTR_INDIR(8) -BOUND_PTR_INDIR(12) -BOUND_PTR_INDIR(16) - static BoundEntry *__bound_new_page(void) { BoundEntry *page; @@ -406,11 +378,34 @@ void __bound_init(void) size = BOUND_T23_SIZE; mark_invalid(start, size); -#if !defined(__TINYC__) && defined(CONFIG_TCC_MALLOC_HOOKS) +#if defined(CONFIG_TCC_MALLOC_HOOKS) /* malloc zone is also marked invalid. can only use that with - hooks because all libs should use the same malloc. The solution - would be to build a new malloc for tcc. */ - start = (unsigned long)&_end; + * hooks because all libs should use the same malloc. The solution + * would be to build a new malloc for tcc. + * + * usually heap (= malloc zone) comes right after bss, i.e. after _end, but + * not always - either if we are running from under `tcc -b -run`, or if + * address space randomization is turned on(a), heap start will be separated + * from bss end. + * + * So sbrk(0) will be a good approximation for start_brk: + * + * - if we are a separately compiled program, __bound_init() runs early, + * and sbrk(0) should be equal or very near to start_brk(b) (in case other + * constructors malloc something), or + * + * - if we are running from under `tcc -b -run`, sbrk(0) will return + * start of heap portion which is under this program control, and not + * mark as invalid earlier allocated memory. + * + * + * (a) /proc/sys/kernel/randomize_va_space = 2, on Linux; + * usually turned on by default. + * + * (b) on Linux >= v3.3, the alternative is to read + * start_brk from /proc/self/stat + */ + start = (unsigned long)sbrk(0); size = 128 * 0x100000; mark_invalid(start, size); #endif @@ -423,6 +418,11 @@ void __bound_init(void) } } +void __bound_exit(void) +{ + restore_malloc_hooks(); +} + static inline void add_region(BoundEntry *e, unsigned long start, unsigned long size) { @@ -616,7 +616,7 @@ int __bound_delete_region(void *p) } } /* last page */ - page = get_page(t2_end); + page = get_page(t1_end); e2 = (BoundEntry *)((char *)page + t2_end); for(e=page;estart = 0; @@ -648,6 +648,9 @@ static unsigned long get_region_size(void *p) /* patched memory functions */ +/* force compiler to perform stores coded up to this point */ +#define barrier() __asm__ __volatile__ ("": : : "memory") + static void install_malloc_hooks(void) { #ifdef CONFIG_TCC_MALLOC_HOOKS @@ -659,6 +662,8 @@ static void install_malloc_hooks(void) __free_hook = __bound_free; __realloc_hook = __bound_realloc; __memalign_hook = __bound_memalign; + + barrier(); #endif } @@ -669,6 +674,8 @@ static void restore_malloc_hooks(void) __free_hook = saved_free_hook; __realloc_hook = saved_realloc_hook; __memalign_hook = saved_memalign_hook; + + barrier(); #endif } diff --git a/lib/libtcc1.c b/lib/libtcc1.c index b079477..dacee28 100644 --- a/lib/libtcc1.c +++ b/lib/libtcc1.c @@ -162,7 +162,7 @@ static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) n0 = nn.s.low; n1 = nn.s.high; -#if !UDIV_NEEDS_NORMALIZATION +#if !defined(UDIV_NEEDS_NORMALIZATION) if (d1 == 0) { if (d0 > n1) @@ -605,3 +605,87 @@ unsigned long long __fixunsxfdi (long double a1) return 0; } +#if defined(__x86_64__) && !defined(_WIN64) + +/* helper functions for stdarg.h */ + +#include +#ifndef __TINYC__ +/* gives "incompatible types for redefinition of __va_arg" below */ +#include +#endif + +enum __va_arg_type { + __va_gen_reg, __va_float_reg, __va_stack +}; + +/* GCC compatible definition of va_list. */ +struct __va_list_struct { + unsigned int gp_offset; + unsigned int fp_offset; + union { + unsigned int overflow_offset; + char *overflow_arg_area; + }; + char *reg_save_area; +}; + +void *__va_start(void *fp) +{ + struct __va_list_struct *ap = + (struct __va_list_struct *)malloc(sizeof(struct __va_list_struct)); + *ap = *(struct __va_list_struct *)((char *)fp - 16); + ap->overflow_arg_area = (char *)fp + ap->overflow_offset; + ap->reg_save_area = (char *)fp - 176 - 16; + return ap; +} + +void *__va_arg(struct __va_list_struct *ap, + enum __va_arg_type arg_type, + int size) +{ + size = (size + 7) & ~7; + switch (arg_type) { + case __va_gen_reg: + if (ap->gp_offset < 48) { + ap->gp_offset += 8; + return ap->reg_save_area + ap->gp_offset - 8; + } + size = 8; + goto use_overflow_area; + + case __va_float_reg: + if (ap->fp_offset < 128 + 48) { + ap->fp_offset += 16; + return ap->reg_save_area + ap->fp_offset - 16; + } + size = 8; + goto use_overflow_area; + + case __va_stack: + use_overflow_area: + ap->overflow_arg_area += size; + return ap->overflow_arg_area - size; + + default: +#ifndef __TINYC__ + fprintf(stderr, "unknown ABI type for __va_arg\n"); +#endif + abort(); + } +} + +void *__va_copy(struct __va_list_struct *src) +{ + struct __va_list_struct *dest = + (struct __va_list_struct *)malloc(sizeof(struct __va_list_struct)); + *dest = *src; + return dest; +} + +void __va_end(struct __va_list_struct *ap) +{ + free(ap); +} + +#endif /* __x86_64__ */ diff --git a/libtcc.c b/libtcc.c index ade77c0..af0fb7f 100644 --- a/libtcc.c +++ b/libtcc.c @@ -23,310 +23,117 @@ /********************************************************/ /* global variables */ -/* display benchmark infos */ -int total_lines; -int total_bytes; - -/* parser */ -static struct BufferedFile *file; -static int ch, tok; -static CValue tokc; -static CString tokcstr; /* current parsed string, if any */ -/* additional informations about token */ -static int tok_flags; -#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 */ - -static int *macro_ptr, *macro_ptr_allocated; -static int *unget_saved_macro_ptr; -static int unget_saved_buffer[TOK_MAX_SIZE + 1]; -static int unget_buffer_enabled; -static int parse_flags; -#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_COMMENTS 0x0008 /* '#' can be used for line comment */ -#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */ - -static Section *text_section, *data_section, *bss_section; /* predefined sections */ -static Section *cur_text_section; /* current section where function code is - generated */ -#ifdef CONFIG_TCC_ASM -static Section *last_text_section; /* to handle .previous asm directive */ -#endif -/* bound check related sections */ -static Section *bounds_section; /* contains global data bound description */ -static Section *lbounds_section; /* contains local data bound description */ -/* symbol sections */ -static Section *symtab_section, *strtab_section; - -/* debug sections */ -static Section *stab_section, *stabstr_section; - -/* loc : local variable index - ind : output code index - rsym: return symbol - anon_sym: anonymous symbol index -*/ -static int rsym, anon_sym, ind, loc; -/* expression generation modifiers */ -static int const_wanted; /* true if constant wanted */ -static int nocode_wanted; /* true if no code generation wanted for an expression */ -static int global_expr; /* true if compound literals must be allocated - globally (used during initializers parsing */ -static CType func_vt; /* current function return type (used by return - instruction) */ -static int func_vc; -static int last_line_num, last_ind, func_ind; /* debug last line number and pc */ -static int tok_ident; -static TokenSym **table_ident; -static TokenSym *hash_ident[TOK_HASH_SIZE]; -static char token_buf[STRING_MAX_SIZE + 1]; -static char *funcname; -static Sym *global_stack, *local_stack; -static Sym *define_stack; -static Sym *global_label_stack, *local_label_stack; -/* symbol allocator */ -#define SYM_POOL_NB (8192 / sizeof(Sym)) -static Sym *sym_free_first; -static void **sym_pools; -static int nb_sym_pools; - -static SValue vstack[VSTACK_SIZE], *vtop; -/* some predefined types */ -static CType char_pointer_type, func_old_type, int_type; - /* use GNU C extensions */ -static int gnu_ext = 1; - -/* use Tiny C extensions */ -static int tcc_ext = 1; +ST_DATA int gnu_ext = 1; -/* max number of callers shown if error */ -#ifdef CONFIG_TCC_BACKTRACE -int num_callers = 6; -const char **rt_bound_error_msg; -#endif +/* use TinyCC extensions */ +ST_DATA int tcc_ext = 1; /* XXX: get rid of this ASAP */ -static struct TCCState *tcc_state; - -/********************************************************/ -/* function prototypes */ - -/* tccpp.c */ -static void next(void); -char *get_tok_str(int v, CValue *cv); - -/* tccgen.c */ -static void parse_expr_type(CType *type); -static void expr_type(CType *type); -static void unary_type(CType *type); -static void block(int *bsym, int *csym, int *case_sym, int *def_sym, - int case_reg, int is_expr); -static int expr_const(void); -static void expr_eq(void); -static void gexpr(void); -static void gen_inline_functions(void); -static void decl(int l); -static void decl_initializer(CType *type, Section *sec, unsigned long c, - int first, int size_only); -static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, - int has_init, int v, int scope); -int gv(int rc); -void gv2(int rc1, int rc2); -void move_reg(int r, int s); -void save_regs(int n); -void save_reg(int r); -void vpop(void); -void vswap(void); -void vdup(void); -int get_reg(int rc); -int get_reg_ex(int rc,int rc2); - -void gen_op(int op); -void force_charshort_cast(int t); -static void gen_cast(CType *type); -void vstore(void); -static Sym *sym_find(int v); -static Sym *sym_push(int v, CType *type, int r, int c); - -/* type handling */ -static int type_size(CType *type, int *a); -static inline CType *pointed_type(CType *type); -static int pointed_size(CType *type); -static int lvalue_type(int t); -static int parse_btype(CType *type, AttributeDef *ad); -static void type_decl(CType *type, AttributeDef *ad, int *v, int td); -static int compare_types(CType *type1, CType *type2, int unqualified); -static int is_compatible_types(CType *type1, CType *type2); -static int is_compatible_parameter_types(CType *type1, CType *type2); - -int ieee_finite(double d); -void vpushi(int v); -void vpushll(long long v); -void vrott(int n); -void vnrott(int n); -void lexpand_nr(void); -static void vpush_global_sym(CType *type, int v); -void vset(CType *type, int r, int v); -void type_to_str(char *buf, int buf_size, - CType *type, const char *varstr); -static Sym *get_sym_ref(CType *type, Section *sec, - unsigned long offset, unsigned long size); -static Sym *external_global_sym(int v, CType *type, int r); - -/* section generation */ -static void section_realloc(Section *sec, unsigned long new_size); -static void *section_ptr_add(Section *sec, unsigned long size); -static void put_extern_sym(Sym *sym, Section *section, - unsigned long value, unsigned long size); -static void greloc(Section *s, Sym *sym, unsigned long addr, int type); -static int put_elf_str(Section *s, const char *sym); -static int put_elf_sym(Section *s, - unsigned long value, unsigned long size, - int info, int other, int shndx, const char *name); -static int add_elf_sym(Section *s, unsigned long value, unsigned long size, - int info, int other, int sh_num, const char *name); -static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, - int type, int symbol); -static void put_stabs(const char *str, int type, int other, int desc, - unsigned long value); -static void put_stabs_r(const char *str, int type, int other, int desc, - unsigned long value, Section *sec, int sym_index); -static void put_stabn(int type, int other, int desc, int value); -static void put_stabd(int type, int other, int desc); -static int tcc_add_dll(TCCState *s, const char *filename, int flags); - -#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 */ -static int tcc_add_file_internal(TCCState *s, const char *filename, int flags); - -/* tcccoff.c */ -int tcc_output_coff(TCCState *s1, FILE *f); - -/* tccpe.c */ -void *resolve_sym(TCCState *s1, const char *sym, int type); -int pe_load_def_file(struct TCCState *s1, int fd); -int pe_test_res_file(void *v, int size); -int pe_load_res_file(struct TCCState *s1, int fd); -void pe_add_runtime(struct TCCState *s1); -void pe_guess_outfile(char *objfilename, int output_type); -int pe_output_file(struct TCCState *s1, const char *filename); - -/* tccasm.c */ -#ifdef CONFIG_TCC_ASM -static void asm_expr(TCCState *s1, ExprValue *pe); -static int asm_int_expr(TCCState *s1); -static int find_constraint(ASMOperand *operands, int nb_operands, - const char *name, const char **pp); - -static int tcc_assemble(TCCState *s1, int do_preprocess); -#endif - -static void asm_instr(void); -static void asm_global_instr(void); +ST_DATA struct TCCState *tcc_state; /********************************************************/ -/* global variables */ +#ifdef ONE_SOURCE +#include "tccpp.c" +#include "tccgen.c" +#include "tccelf.c" +#include "tccrun.c" #ifdef TCC_TARGET_I386 #include "i386-gen.c" #endif - #ifdef TCC_TARGET_ARM #include "arm-gen.c" #endif - #ifdef TCC_TARGET_C67 #include "c67-gen.c" #endif - #ifdef TCC_TARGET_X86_64 #include "x86_64-gen.c" #endif +#ifdef CONFIG_TCC_ASM +#include "tccasm.c" +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 +#include "i386-asm.c" +#endif +#endif +#ifdef TCC_TARGET_COFF +#include "tcccoff.c" +#endif +#ifdef TCC_TARGET_PE +#include "tccpe.c" +#endif +#endif /* ONE_SOURCE */ -#ifdef CONFIG_TCC_STATIC - -#define RTLD_LAZY 0x001 -#define RTLD_NOW 0x002 -#define RTLD_GLOBAL 0x100 -#define RTLD_DEFAULT NULL - -/* dummy function for profiling */ -void *dlopen(const char *filename, int flag) +/********************************************************/ +#ifndef CONFIG_TCC_ASM +ST_FUNC void asm_instr(void) { - return NULL; + tcc_error("inline asm() not supported"); } - -void dlclose(void *p) +ST_FUNC void asm_global_instr(void) { + tcc_error("inline asm() not supported"); } +#endif -const char *dlerror(void) +/********************************************************/ + +#ifdef _WIN32 +static char *normalize_slashes(char *path) { - return "error"; + char *p; + for (p = path; *p; ++p) + if (*p == '\\') + *p = '/'; + return path; } -typedef struct TCCSyms { - char *str; - void *ptr; -} TCCSyms; - -#define TCCSYM(a) { #a, &a, }, - -/* add the symbol you want here if no dynamic linking is done */ -static TCCSyms tcc_syms[] = { -#if !defined(CONFIG_TCCBOOT) - TCCSYM(printf) - TCCSYM(fprintf) - TCCSYM(fopen) - TCCSYM(fclose) -#endif - { NULL, NULL }, -}; +static HMODULE tcc_module; -void *resolve_sym(TCCState *s1, const char *symbol, int type) +/* on win32, we suppose the lib and includes are at the location of 'tcc.exe' */ +static void tcc_set_lib_path_w32(TCCState *s) { - TCCSyms *p; - p = tcc_syms; - while (p->str != NULL) { - if (!strcmp(p->str, symbol)) - return p->ptr; - p++; - } - return NULL; + 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); } -#elif !defined(_WIN32) - -#include - -void *resolve_sym(TCCState *s1, const char *sym, int type) +#ifdef TCC_TARGET_PE +static void tcc_add_systemdir(TCCState *s) { - return dlsym(RTLD_DEFAULT, sym); + 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 -/* we use our own 'finite' function to avoid potential problems with - non standard math libs */ -/* XXX: endianness dependent */ -int ieee_finite(double d) +#ifdef LIBTCC_AS_DLL +BOOL WINAPI DllMain (HANDLE hDll, DWORD dwReason, LPVOID lpReserved) { - int *p = (int *)&d; - return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; + if (DLL_PROCESS_ATTACH == dwReason) + tcc_module = hDll; + return TRUE; } +#endif +#endif +/********************************************************/ /* copy a string and truncate it. */ -char *pstrcpy(char *buf, int buf_size, const char *s) +PUB_FUNC char *pstrcpy(char *buf, int buf_size, const char *s) { char *q, *q_end; int c; @@ -346,7 +153,7 @@ char *pstrcpy(char *buf, int buf_size, const char *s) } /* strcat and truncate. */ -char *pstrcat(char *buf, int buf_size, const char *s) +PUB_FUNC char *pstrcat(char *buf, int buf_size, const char *s) { int len; len = strlen(buf); @@ -355,70 +162,47 @@ char *pstrcat(char *buf, int buf_size, const char *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 */ -char *tcc_basename(const char *name) +PUB_FUNC char *tcc_basename(const char *name) { char *p = strchr(name, 0); - while (p > name && !IS_PATHSEP(p[-1])) + while (p > name && !IS_DIRSEP(p[-1])) --p; return p; } -char *tcc_fileextension (const char *name) +/* 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); } -#ifdef _WIN32 -char *normalize_slashes(char *path) -{ - char *p; - for (p = path; *p; ++p) - if (*p == '\\') - *p = '/'; - return path; -} - -void tcc_set_lib_path_w32(TCCState *s) -{ - /* on win32, we suppose the lib and includes are at the location - of 'tcc.exe' */ - char path[1024], *p; - GetModuleFileNameA(NULL, 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); -} -#endif +/********************************************************/ +/* memory management */ -void set_pages_executable(void *ptr, unsigned long length) -{ -#ifdef _WIN32 - unsigned long old_protect; - VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect); -#else - unsigned long start, end; - start = (unsigned long)ptr & ~(PAGESIZE - 1); - end = (unsigned long)ptr + length; - end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); - mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); -#endif -} +#undef free +#undef malloc +#undef realloc -/* memory management */ #ifdef MEM_DEBUG -int mem_cur_size; -int mem_max_size; +ST_DATA int mem_cur_size; +ST_DATA int mem_max_size; unsigned malloc_usable_size(void*); #endif -void tcc_free(void *ptr) +PUB_FUNC void tcc_free(void *ptr) { #ifdef MEM_DEBUG mem_cur_size -= malloc_usable_size(ptr); @@ -426,12 +210,12 @@ void tcc_free(void *ptr) free(ptr); } -void *tcc_malloc(unsigned long size) +PUB_FUNC void *tcc_malloc(unsigned long size) { void *ptr; ptr = malloc(size); if (!ptr && size) - error("memory full"); + tcc_error("memory full"); #ifdef MEM_DEBUG mem_cur_size += malloc_usable_size(ptr); if (mem_cur_size > mem_max_size) @@ -440,7 +224,7 @@ void *tcc_malloc(unsigned long size) return ptr; } -void *tcc_mallocz(unsigned long size) +PUB_FUNC void *tcc_mallocz(unsigned long size) { void *ptr; ptr = tcc_malloc(size); @@ -448,13 +232,15 @@ void *tcc_mallocz(unsigned long size) return ptr; } -void *tcc_realloc(void *ptr, unsigned long size) +PUB_FUNC void *tcc_realloc(void *ptr, unsigned long size) { void *ptr1; #ifdef MEM_DEBUG mem_cur_size -= malloc_usable_size(ptr); #endif ptr1 = realloc(ptr, size); + if (!ptr1 && size) + tcc_error("memory full"); #ifdef MEM_DEBUG /* NOTE: count not correct if alloc error, but not critical */ mem_cur_size += malloc_usable_size(ptr1); @@ -464,7 +250,7 @@ void *tcc_realloc(void *ptr, unsigned long size) return ptr1; } -char *tcc_strdup(const char *str) +PUB_FUNC char *tcc_strdup(const char *str) { char *ptr; ptr = tcc_malloc(strlen(str) + 1); @@ -472,11 +258,21 @@ char *tcc_strdup(const char *str) return ptr; } +PUB_FUNC void tcc_memstats(void) +{ +#ifdef MEM_DEBUG + printf("memory: %d bytes, max = %d bytes\n", mem_cur_size, mem_max_size); +#endif +} + #define free(p) use_tcc_free(p) #define malloc(s) use_tcc_malloc(s) #define realloc(p, s) use_tcc_realloc(p, s) -void dynarray_add(void ***ptab, int *nb_ptr, void *data) +/********************************************************/ +/* dynarrays */ + +ST_FUNC void dynarray_add(void ***ptab, int *nb_ptr, void *data) { int nb, nb_alloc; void **pp; @@ -490,15 +286,13 @@ void dynarray_add(void ***ptab, int *nb_ptr, void *data) else nb_alloc = nb * 2; pp = tcc_realloc(pp, nb_alloc * sizeof(void *)); - if (!pp) - error("memory full"); *ptab = pp; } pp[nb++] = data; *nb_ptr = nb; } -void dynarray_reset(void *pp, int *n) +ST_FUNC void dynarray_reset(void *pp, int *n) { void **p; for (p = *(void***)pp; *n; ++p, --*n) @@ -508,43 +302,32 @@ void dynarray_reset(void *pp, int *n) *(void**)pp = NULL; } -/* symbol allocator */ -static Sym *__sym_malloc(void) +static void tcc_split_path(TCCState *s, void ***p_ary, int *p_nb_ary, const char *in) { - 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); + const char *p; + do { + int c; + CString str; - 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; + 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); } -static inline void sym_free(Sym *sym) -{ - sym->next = sym_free_first; - sym_free_first = sym; -} +/********************************************************/ -Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags) +ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags) { Section *sec; @@ -585,7 +368,7 @@ static void free_section(Section *s) } /* realloc section and set its content to zero */ -static void section_realloc(Section *sec, unsigned long new_size) +ST_FUNC void section_realloc(Section *sec, unsigned long new_size) { unsigned long size; unsigned char *data; @@ -596,8 +379,6 @@ static void section_realloc(Section *sec, unsigned long new_size) while (size < new_size) size = size * 2; data = tcc_realloc(sec->data, size); - if (!data) - error("memory full"); memset(data + sec->data_allocated, 0, size - sec->data_allocated); sec->data = data; sec->data_allocated = size; @@ -605,7 +386,7 @@ static void section_realloc(Section *sec, unsigned long new_size) /* reserve at least 'size' bytes in section 'sec' from sec->data_offset. */ -static void *section_ptr_add(Section *sec, unsigned long size) +ST_FUNC void *section_ptr_add(Section *sec, unsigned long size) { unsigned long offset, offset1; @@ -617,9 +398,18 @@ static void *section_ptr_add(Section *sec, unsigned long size) 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 */ -Section *find_section(TCCState *s1, const char *name) +ST_FUNC Section *find_section(TCCState *s1, const char *name) { Section *sec; int i; @@ -634,11 +424,11 @@ Section *find_section(TCCState *s1, const char *name) /* update sym->c so that it points to an external symbol in section 'section' with value 'value' */ -static void put_extern_sym2(Sym *sym, Section *section, - unsigned long value, unsigned long size, +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, attr; + int sym_type, sym_bind, sh_num, info, other; ElfW(Sym) *esym; const char *name; char buf1[256]; @@ -650,26 +440,22 @@ static void put_extern_sym2(Sym *sym, Section *section, else sh_num = section->sh_num; - other = attr = 0; - if ((sym->type.t & VT_BTYPE) == VT_FUNC) { sym_type = STT_FUNC; -#ifdef TCC_TARGET_PE - if (sym->type.ref) - attr = sym->type.ref->r; - if (FUNC_EXPORT(attr)) - other |= 1; - if (FUNC_CALL(attr) == FUNC_STDCALL) - other |= 2; -#endif + } 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 - sym_bind = STB_GLOBAL; + 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); @@ -681,7 +467,7 @@ static void put_extern_sym2(Sym *sym, Section *section, /* if bound checking is activated, we change some function names by adding the "__bound" prefix */ switch(sym->v) { -#if 0 +#ifdef TCC_TARGET_PE /* XXX: we rely only on malloc hooks */ case TOK_malloc: case TOK_free: @@ -702,18 +488,36 @@ static void put_extern_sym2(Sym *sym, Section *section, } } #endif + other = 0; #ifdef TCC_TARGET_PE - if ((other & 2) && can_add_underscore) { - sprintf(buf1, "_%s@%d", name, FUNC_ARGS(attr)); - name = buf1; - } else + if (sym->type.t & VT_EXPORT) + other |= 1; + if (sym_type == STT_FUNC && sym->type.ref) { + int attr = sym->type.ref->r; + if (FUNC_EXPORT(attr)) + other |= 1; + if (FUNC_CALL(attr) == FUNC_STDCALL && can_add_underscore) { + sprintf(buf1, "_%s@%d", name, FUNC_ARGS(attr) * PTR_SIZE); + name = buf1; + other |= 2; + can_add_underscore = 0; + } + } else { + if (find_elf_sym(tcc_state->dynsymtab_section, name)) + other |= 4; + if (sym->type.t & VT_IMPORT) + other |= 4; + } #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 { @@ -721,49 +525,29 @@ static void put_extern_sym2(Sym *sym, Section *section, esym->st_value = value; esym->st_size = size; esym->st_shndx = sh_num; - esym->st_other |= other; } } -static void put_extern_sym(Sym *sym, Section *section, - unsigned long value, unsigned long size) +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' */ -static void greloc(Section *s, Sym *sym, unsigned long offset, int type) +ST_FUNC void greloc(Section *s, Sym *sym, unsigned long offset, int type) { - if (!sym->c) - put_extern_sym(sym, NULL, 0, 0); + 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_reloc(symtab_section, s, offset, type, sym->c); -} - -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'; + put_elf_reloc(symtab_section, s, offset, type, c); } -static inline int isoct(int c) -{ - return c >= '0' && c <= '7'; -} - -static inline int toup(int c) -{ - if (c >= 'a' && c <= 'z') - return c - 'a' + 'A'; - else - return c; -} +/********************************************************/ static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap) { @@ -780,29 +564,32 @@ static void strcat_printf(char *buf, int buf_size, const char *fmt, ...) va_end(ap); } -void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) +static void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) { char buf[2048]; - BufferedFile **f; + BufferedFile **pf, *f; buf[0] = '\0'; - if (file) { - for(f = s1->include_stack; f < s1->include_stack_ptr; f++) - strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n", - (*f)->filename, (*f)->line_num); - if (file->line_num > 0) { - strcat_printf(buf, sizeof(buf), - "%s:%d: ", file->filename, file->line_num); + /* 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: ", file->filename); + strcat_printf(buf, sizeof(buf), "%s: ", + f->filename); } } else { - strcat_printf(buf, sizeof(buf), - "tcc: "); + 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) { @@ -815,7 +602,7 @@ void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) s1->nb_errors++; } -void tcc_set_error_func(TCCState *s, void *error_opaque, +LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, void (*error_func)(void *opaque, const char *msg)) { s->error_opaque = error_opaque; @@ -823,7 +610,7 @@ void tcc_set_error_func(TCCState *s, void *error_opaque, } /* error without aborting current compilation */ -void error_noabort(const char *fmt, ...) +PUB_FUNC void tcc_error_noabort(const char *fmt, ...) { TCCState *s1 = tcc_state; va_list ap; @@ -833,7 +620,7 @@ void error_noabort(const char *fmt, ...) va_end(ap); } -void error(const char *fmt, ...) +PUB_FUNC void tcc_error(const char *fmt, ...) { TCCState *s1 = tcc_state; va_list ap; @@ -850,12 +637,7 @@ void error(const char *fmt, ...) } } -void expect(const char *msg) -{ - error("%s expected", msg); -} - -void warning(const char *fmt, ...) +PUB_FUNC void tcc_warning(const char *fmt, ...) { TCCState *s1 = tcc_state; va_list ap; @@ -868,324 +650,114 @@ void warning(const char *fmt, ...) va_end(ap); } -void skip(int c) -{ - if (tok != c) - error("'%c' expected", c); - next(); -} - -static void test_lvalue(void) -{ - if (!(vtop->r & VT_LVAL)) - expect("lvalue"); -} - -/* CString handling */ +/********************************************************/ +/* I/O layer */ -static void cstr_realloc(CString *cstr, int new_size) +ST_FUNC void tcc_open_bf(TCCState *s1, const char *filename, int initlen) { - 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); - if (!data) - error("memory full"); - cstr->data_allocated = data; - cstr->size_allocated = size; - cstr->data = data; -} + BufferedFile *bf; + int buflen = initlen ? initlen : IO_BUF_SIZE; -/* add a byte */ -static inline 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; + bf = tcc_malloc(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->ifndef_macro = 0; + bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; + bf->fd = -1; + bf->prev = file; + file = bf; } -static void cstr_cat(CString *cstr, const char *str) +ST_FUNC void tcc_close(void) { - int c; - for(;;) { - c = *str; - if (c == '\0') - break; - cstr_ccat(cstr, c); - str++; + BufferedFile *bf = file; + if (bf->fd > 0) { + close(bf->fd); + total_lines += bf->line_num; } + file = bf->prev; + tcc_free(bf); } -/* add a wide char */ -static void cstr_wccat(CString *cstr, int ch) +ST_FUNC int tcc_open(TCCState *s1, const char *filename) { - 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; -} + 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; -static void cstr_new(CString *cstr) -{ - memset(cstr, 0, sizeof(CString)); + tcc_open_bf(s1, filename, 0); + file->fd = fd; + return fd; } -/* free string and reset it to NULL */ -static void cstr_free(CString *cstr) +/* compile the C file opened in 'file'. Return non zero if errors. */ +static int tcc_compile(TCCState *s1) { - tcc_free(cstr->data_allocated); - cstr_new(cstr); -} + Sym *define_start; + SValue *pvtop; + char buf[512]; + volatile int section_sym; + +#ifdef INC_DEBUG + printf("%s: **** new file\n", file->filename); +#endif + preprocess_init(s1); -#define cstr_reset(cstr) cstr_free(cstr) + cur_text_section = NULL; + funcname = ""; + anon_sym = SYM_FIRST_ANOM; -/* 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)); - } + /* 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); -/* push, without hashing */ -static Sym *sym_push2(Sym **ps, int v, int t, long c) -{ - Sym *s; - s = sym_malloc(); - s->v = v; - s->type.t = t; - s->c = c; - s->next = NULL; - /* add in stack */ - s->prev = *ps; - *ps = s; - return s; -} + /* define some often used types */ + int_type.t = VT_INT; -/* find a symbol and return its associated structure. 's' is the top - of the symbol stack */ -static Sym *sym_find2(Sym *s, int v) -{ - while (s) { - if (s->v == v) - return s; - s = s->prev; - } - return NULL; -} + char_pointer_type.t = VT_BYTE; + mk_pointer(&char_pointer_type); -/* structure lookup */ -static inline 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 */ -static inline 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 */ -static 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 */ -static 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' */ -static 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; -} - -/* I/O layer */ - -BufferedFile *tcc_open(TCCState *s1, const char *filename) -{ - int fd; - BufferedFile *bf; - - 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":"->", - (s1->include_stack_ptr - s1->include_stack), "", filename); - if (fd < 0) - return NULL; - bf = tcc_malloc(sizeof(BufferedFile)); - bf->fd = fd; - bf->buf_ptr = bf->buffer; - bf->buf_end = bf->buffer; - bf->buffer[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->ifndef_macro = 0; - bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; - // printf("opening '%s'\n", filename); - return bf; -} - -void tcc_close(BufferedFile *bf) -{ - total_lines += bf->line_num; - close(bf->fd); - tcc_free(bf); -} - -#include "tccpp.c" -#include "tccgen.c" - - -/* 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); - -#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) - 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); +#ifdef TCC_TARGET_ARM + arm_init_types(); #endif #if 0 @@ -1212,10 +784,13 @@ static int tcc_compile(TCCState *s1) ch = file->buf_ptr[0]; tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM; + pvtop = vtop; next(); decl(VT_CONST); if (tok != TOK_EOF) expect("declaration"); + if (pvtop != vtop) + tcc_warning("internal compiler error: vstack leak? (%d)", vtop - pvtop); /* end of translation unit info */ if (s1->do_debug) { @@ -1223,6 +798,7 @@ static int tcc_compile(TCCState *s1) text_section->data_offset, text_section, section_sym); } } + s1->error_set_jmp_enabled = 0; /* reset define stack, but leave -Dsymbols (may be incorrect if @@ -1237,504 +813,58 @@ static int tcc_compile(TCCState *s1) return s1->nb_errors != 0 ? -1 : 0; } -int tcc_compile_string(TCCState *s, const char *str) +LIBTCCAPI int tcc_compile_string(TCCState *s, const char *str) { - BufferedFile bf1, *bf = &bf1; - int ret, len; - char *buf; - - /* init file structure */ - bf->fd = -1; - /* XXX: avoid copying */ + int len, ret; len = strlen(str); - buf = tcc_malloc(len + 1); - if (!buf) - return -1; - memcpy(buf, str, len); - buf[len] = CH_EOB; - bf->buf_ptr = buf; - bf->buf_end = buf + len; - pstrcpy(bf->filename, sizeof(bf->filename), ""); - bf->line_num = 1; - file = bf; - ret = tcc_compile(s); - file = NULL; - tcc_free(buf); - /* currently, no need to close */ + tcc_open_bf(s, "", 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 */ -void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) -{ - BufferedFile bf1, *bf = &bf1; - - pstrcpy(bf->buffer, IO_BUF_SIZE, sym); - pstrcat(bf->buffer, IO_BUF_SIZE, " "); - /* default value */ - if (!value) - value = "1"; - pstrcat(bf->buffer, IO_BUF_SIZE, value); - - /* init file structure */ - bf->fd = -1; - bf->buf_ptr = bf->buffer; - bf->buf_end = bf->buffer + strlen(bf->buffer); - *bf->buf_end = CH_EOB; - bf->filename[0] = '\0'; - bf->line_num = 1; - file = bf; - - s1->include_stack_ptr = s1->include_stack; - - /* parse with define parser */ - ch = file->buf_ptr[0]; - next_nomacro(); - parse_define(); - file = NULL; -} - -/* undefine a preprocessor symbol */ -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); -} - -#ifdef CONFIG_TCC_ASM - -#ifdef TCC_TARGET_I386 -#include "i386-asm.c" -#endif -#include "tccasm.c" - -#else -static void asm_instr(void) -{ - error("inline asm() not supported"); -} -static void asm_global_instr(void) -{ - error("inline asm() not supported"); -} -#endif - -#include "tccelf.c" - -#ifdef TCC_TARGET_COFF -#include "tcccoff.c" -#endif - -#ifdef TCC_TARGET_PE -#include "tccpe.c" -#endif - -#ifdef CONFIG_TCC_BACKTRACE -/* print the position in the source file of PC value 'pc' by reading - the stabs debug information */ -static void rt_printline(unsigned long wanted_pc) -{ - 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, i; - const char *str, *p; - - fprintf(stderr, "0x%08lx:", wanted_pc); - - 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); - while (sym < sym_end) { - 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 = 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; - } - 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 = 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 = stabstr_section->data + sym->n_strx; - /* do not add path */ - len = strlen(str); - if (len > 0 && str[len - 1] != '/') - goto add_incl; - } - break; - } - sym++; - } - - /* second pass: we try symtab symbols (no line number info) */ - incl_index = 0; - { - 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) { - if (wanted_pc >= sym->st_value && - wanted_pc < sym->st_value + sym->st_size) { - pstrcpy(last_func_name, sizeof(last_func_name), - strtab_section->data + sym->st_name); - goto found; - } - } - } - } - /* did not find any info: */ - fprintf(stderr, " ???\n"); - return; - found: - if (last_func_name[0] != '\0') { - fprintf(stderr, " %s()", last_func_name); - } - if (incl_index > 0) { - fprintf(stderr, " (%s:%d", - incl_files[incl_index - 1], last_line_num); - for(i = incl_index - 2; i >= 0; i--) - fprintf(stderr, ", included from %s", incl_files[i]); - fprintf(stderr, ")"); - } - fprintf(stderr, "\n"); -} - -#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 non zero if not found */ -static int rt_get_caller_pc(unsigned long *paddr, - ucontext_t *uc, int level) -{ - unsigned long fp; - int i; - - if (level == 0) { -#if defined(__FreeBSD__) - *paddr = uc->uc_mcontext.mc_eip; -#elif defined(__dietlibc__) - *paddr = uc->uc_mcontext.eip; -#else - *paddr = uc->uc_mcontext.gregs[REG_EIP]; -#endif - return 0; - } else { -#if defined(__FreeBSD__) - fp = uc->uc_mcontext.mc_ebp; -#elif defined(__dietlibc__) - fp = uc->uc_mcontext.ebp; -#else - fp = uc->uc_mcontext.gregs[REG_EBP]; -#endif - for(i=1;i= 0xc0000000) - return -1; - fp = ((unsigned long *)fp)[0]; - } - *paddr = ((unsigned long *)fp)[1]; - return 0; - } -} -#elif defined(__x86_64__) -/* return the PC at frame level 'level'. Return non zero if not found */ -static int rt_get_caller_pc(unsigned long *paddr, - ucontext_t *uc, int level) -{ - unsigned long fp; - int i; - - if (level == 0) { - /* XXX: only support linux */ - *paddr = uc->uc_mcontext.gregs[REG_RIP]; - return 0; - } else { - fp = uc->uc_mcontext.gregs[REG_RBP]; - for(i=1;isi_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); -} - -#endif - -/* copy code into memory passed in by the caller and do all relocations - (needed before using tcc_get_symbol()). - returns -1 on error and required size if ptr is NULL */ -int tcc_relocate(TCCState *s1, void *ptr) -{ - Section *s; - unsigned long offset, length, mem; - int i; - - if (0 == s1->runtime_added) { - s1->runtime_added = 1; - s1->nb_errors = 0; -#ifdef TCC_TARGET_PE - pe_add_runtime(s1); - relocate_common_syms(); - tcc_add_linker_symbols(s1); -#else - tcc_add_runtime(s1); - relocate_common_syms(); - tcc_add_linker_symbols(s1); - build_got_entries(s1); -#endif - } - - offset = 0, mem = (unsigned long)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; - } - - /* relocate symbols */ - relocate_syms(s1, 1); - if (s1->nb_errors) - return -1; - -#ifdef TCC_TARGET_X86_64 - s1->runtime_plt_and_got_offset = 0; - s1->runtime_plt_and_got = (char *)(mem + offset); - /* double the size of the buffer for got and plt entries - XXX: calculate exact size for them? */ - offset *= 2; -#endif - - if (0 == mem) - return offset + 15; - - /* relocate each section */ - for(i = 1; i < s1->nb_sections; i++) { - s = s1->sections[i]; - if (s->reloc) - relocate_section(s1, s); - } - - 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 %08x %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 TCC_TARGET_X86_64 - set_pages_executable(s1->runtime_plt_and_got, - s1->runtime_plt_and_got_offset); -#endif - return 0; -} - -/* launch the compiled program with the given arguments */ -int tcc_run(TCCState *s1, int argc, char **argv) -{ - int (*prog_main)(int, char **); - void *ptr; - int ret; - - ret = tcc_relocate(s1, NULL); - if (ret < 0) - return -1; - ptr = tcc_malloc(ret); - tcc_relocate(s1, ptr); - - prog_main = tcc_get_symbol_err(s1, "main"); - - if (s1->do_debug) { -#ifdef CONFIG_TCC_BACKTRACE - 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); -#else - error("debug mode not available"); -#endif - } +} -#ifdef CONFIG_TCC_BCHECK - if (s1->do_bounds_check) { - void (*bound_init)(void); +/* 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); - /* set error function */ - rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg"); + /* init file structure */ + tcc_open_bf(s1, "", len1 + len2 + 1); + memcpy(file->buffer, sym, len1); + file->buffer[len1] = ' '; + memcpy(file->buffer + len1 + 1, value, len2); - /* XXX: use .init section so that it also work in binary ? */ - bound_init = (void *)tcc_get_symbol_err(s1, "__bound_init"); - bound_init(); - } -#endif - ret = (*prog_main)(argc, argv); - tcc_free(ptr); - return ret; + /* parse with define parser */ + ch = file->buf_ptr[0]; + next_nomacro(); + parse_define(); + + tcc_close(); } -void tcc_memstats(void) +/* undefine a preprocessor symbol */ +LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym) { -#ifdef MEM_DEBUG - printf("memory in use: %d\n", mem_cur_size); -#endif + 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) { int i, n; - if (NULL == tcc_state) return; tcc_state = NULL; @@ -1758,9 +888,11 @@ static void tcc_cleanup(void) macro_ptr = NULL; } -TCCState *tcc_new(void) +LIBTCCAPI TCCState *tcc_new(void) { TCCState *s; + char buffer[100]; + int a,b,c; tcc_cleanup(); @@ -1768,10 +900,14 @@ TCCState *tcc_new(void) 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 = TCC_OUTPUT_MEMORY; - s->tcc_lib_path = CONFIG_TCCDIR; - 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() */ @@ -1780,16 +916,23 @@ TCCState *tcc_new(void) 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"); + + /* target defines */ #if defined(TCC_TARGET_I386) tcc_define_symbol(s, "__i386__", NULL); -#endif -#if defined(TCC_TARGET_X86_64) + 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); -#endif -#if defined(TCC_TARGET_ARM) +#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); @@ -1799,33 +942,53 @@ TCCState *tcc_new(void) tcc_define_symbol(s, "arm", NULL); tcc_define_symbol(s, "__APCS_32__", 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); -#if defined(__linux) + 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 - /* tiny C specific defines */ - tcc_define_symbol(s, "__TINYC__", NULL); - /* tiny C & gcc defines */ - tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int"); - tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int"); #ifdef TCC_TARGET_PE tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short"); #else tcc_define_symbol(s, "__WCHAR_TYPE__", "int"); #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"); /* default library paths */ - tcc_add_library_path(s, CONFIG_SYSROOT "/usr/local/lib"); - tcc_add_library_path(s, CONFIG_SYSROOT "/usr/lib"); - tcc_add_library_path(s, CONFIG_SYSROOT "/lib"); + tcc_add_library_path(s, CONFIG_TCC_LIBPATHS); + /* paths for crt objects */ + tcc_split_path(s, (void ***)&s->crt_paths, &s->nb_crt_paths, CONFIG_TCC_CRTPREFIX); #endif /* no section zero */ @@ -1841,24 +1004,30 @@ TCCState *tcc_new(void) ".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->section_align = ELF_PAGE_SIZE; #ifdef CHAR_IS_UNSIGNED s->char_is_unsigned = 1; #endif -#if defined(TCC_TARGET_PE) && 0 - /* XXX: currently the PE linker is not ready to support that */ + /* 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 return s; } -void tcc_delete(TCCState *s1) +LIBTCCAPI void tcc_delete(TCCState *s1) { int i; @@ -1885,150 +1054,171 @@ void tcc_delete(TCCState *s1) /* 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); + +#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); } -int tcc_add_include_path(TCCState *s1, const char *pathname) +LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname) { - char *pathname1; - - pathname1 = tcc_strdup(pathname); - dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1); + tcc_split_path(s, (void ***)&s->include_paths, &s->nb_include_paths, pathname); return 0; } -int tcc_add_sysinclude_path(TCCState *s1, const char *pathname) +LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname) { - char *pathname1; - - pathname1 = tcc_strdup(pathname); - dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1); + tcc_split_path(s, (void ***)&s->sysinclude_paths, &s->nb_sysinclude_paths, pathname); return 0; } -static int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) +ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) { const char *ext; ElfW(Ehdr) ehdr; - int fd, ret; - BufferedFile *saved_file; + int fd, ret, size; /* find source file type with extension */ ext = tcc_fileextension(filename); if (ext[0]) ext++; +#ifdef CONFIG_TCC_ASM + /* if .S file, define __ASSEMBLER__ like gcc does */ + if (!strcmp(ext, "S")) + tcc_define_symbol(s1, "__ASSEMBLER__", NULL); +#endif + /* open the file */ - saved_file = file; - file = tcc_open(s1, filename); - if (!file) { - if (flags & AFF_PRINT_ERROR) { - error_noabort("file '%s' not found", filename); - } - ret = -1; - goto fail1; + 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); - } else if (!ext[0] || !PATHCMP(ext, "c")) { + goto the_end; + } + + if (!ext[0] || !PATHCMP(ext, "c")) { /* C file assumed */ ret = tcc_compile(s1); - } else + goto the_end; + } + #ifdef CONFIG_TCC_ASM if (!strcmp(ext, "S")) { /* preprocessed assembler */ ret = tcc_assemble(s1, 1); - } else if (!strcmp(ext, "s")) { + goto the_end; + } + + if (!strcmp(ext, "s")) { /* non preprocessed assembler */ ret = tcc_assemble(s1, 0); - } else -#endif -#ifdef TCC_TARGET_PE - if (!PATHCMP(ext, "def")) { - ret = pe_load_def_file(s1, file->fd); - } else + goto the_end; + } #endif - { - fd = file->fd; - /* assume executable format: auto guess file type */ - ret = read(fd, &ehdr, sizeof(ehdr)); - lseek(fd, 0, SEEK_SET); - if (ret <= 0) { - error_noabort("could not read header"); - goto fail; - } else if (ret != sizeof(ehdr)) { - goto try_load_script; - } - if (ehdr.e_ident[0] == ELFMAG0 && - ehdr.e_ident[1] == ELFMAG1 && - ehdr.e_ident[2] == ELFMAG2 && - ehdr.e_ident[3] == ELFMAG3) { - file->line_num = 0; /* do not display line number if error */ - if (ehdr.e_type == ET_REL) { - ret = tcc_load_object_file(s1, fd, 0); - } else if (ehdr.e_type == ET_DYN) { - if (s1->output_type == TCC_OUTPUT_MEMORY) { -#ifdef TCC_TARGET_PE - ret = -1; -#else - void *h; - h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); - if (h) - ret = 0; - else - ret = -1; + 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 - } else { - ret = tcc_load_dll(s1, fd, filename, - (flags & AFF_REFERENCED_DLL) != 0); - } + ret = 0; } else { - error_noabort("unrecognized ELF file"); - goto fail; + ret = tcc_load_dll(s1, fd, filename, + (flags & AFF_REFERENCED_DLL) != 0); } - } else if (memcmp((char *)&ehdr, ARMAG, 8) == 0) { - file->line_num = 0; /* do not display line number if error */ - ret = tcc_load_archive(s1, fd); - } else + 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); - } else + if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) { + ret = tcc_load_coff(s1, fd); + goto the_end; + } #endif + #ifdef TCC_TARGET_PE - if (pe_test_res_file(&ehdr, ret)) { - ret = pe_load_res_file(s1, fd); - } else + 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 - { - /* as GNU ld, consider it is an ld script if not recognized */ - try_load_script: - ret = tcc_load_ldscript(s1); - if (ret < 0) { - error_noabort("unrecognized file type"); - goto fail; - } - } - } - the_end: - tcc_close(file); - fail1: - file = saved_file; + if (ret < 0) + tcc_error_noabort("unrecognized file type"); + +the_end: + tcc_close(); return ret; - fail: - ret = -1; - goto the_end; } -int tcc_add_file(TCCState *s, const char *filename) +LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename) { if (s->output_type == TCC_OUTPUT_PREPROCESS) return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS); @@ -2036,85 +1226,85 @@ int tcc_add_file(TCCState *s, const char *filename) return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR); } -int tcc_add_library_path(TCCState *s, const char *pathname) +LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname) { - char *pathname1; - - pathname1 = tcc_strdup(pathname); - dynarray_add((void ***)&s->library_paths, &s->nb_library_paths, pathname1); + tcc_split_path(s, (void ***)&s->library_paths, &s->nb_library_paths, pathname); return 0; } -/* find and load a dll. Return non zero if not found */ -/* XXX: add '-rpath' option support ? */ -static int tcc_add_dll(TCCState *s, const char *filename, int flags) +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 < s->nb_library_paths; i++) { - snprintf(buf, sizeof(buf), "%s/%s", - s->library_paths[i], filename); + for(i = 0; i < nb_paths; i++) { + snprintf(buf, sizeof(buf), fmt, paths[i], filename); if (tcc_add_file_internal(s, buf, flags) == 0) return 0; } return -1; } +/* 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); +} + +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 */ -int tcc_add_library(TCCState *s, const char *libraryname) +LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) { - char buf[1024]; - int i; - - /* first we look for the dynamic library if not static linking */ - if (!s->static_link) { #ifdef TCC_TARGET_PE - snprintf(buf, sizeof(buf), "%s.def", libraryname); + 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 - snprintf(buf, sizeof(buf), "lib%s.so", libraryname); + const char *libs[] = { "%s/lib%s.so", "%s/lib%s.a", NULL }; + const char **pp = s->static_link ? libs + 1 : libs; #endif - if (tcc_add_dll(s, buf, 0) == 0) - return 0; - } - - /* then we look for the static library */ - for(i = 0; i < s->nb_library_paths; i++) { - snprintf(buf, sizeof(buf), "%s/lib%s.a", - s->library_paths[i], libraryname); - if (tcc_add_file_internal(s, buf, 0) == 0) + while (*pp) { + if (0 == tcc_add_library_internal(s, *pp, + libraryname, 0, s->library_paths, s->nb_library_paths)) return 0; + ++pp; } return -1; } -int tcc_add_symbol(TCCState *s, const char *name, void *val) +LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val) { - add_elf_sym(symtab_section, (unsigned long)val, 0, - ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, - SHN_ABS, name); +#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 + /* XXX: Same problem on linux but currently "solved" elsewhere + via the rather dirty 'runtime_plt_and_got' hack. */ + add_elf_sym(symtab_section, (uintptr_t)val, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + SHN_ABS, name); +#endif return 0; } -int tcc_set_output_type(TCCState *s, int output_type) +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) { - char buf[1024]; - s->output_type = output_type; if (!s->nostdinc) { /* default include paths */ - /* XXX: reverse order needed if -isystem support */ -#ifndef TCC_TARGET_PE - tcc_add_sysinclude_path(s, CONFIG_SYSROOT "/usr/local/include"); - tcc_add_sysinclude_path(s, CONFIG_SYSROOT "/usr/include"); -#endif - snprintf(buf, sizeof(buf), "%s/include", s->tcc_lib_path); - tcc_add_sysinclude_path(s, buf); -#ifdef TCC_TARGET_PE - snprintf(buf, sizeof(buf), "%s/include/winapi", s->tcc_lib_path); - tcc_add_sysinclude_path(s, buf); -#endif + /* -isystem paths have already been handled */ + tcc_add_sysinclude_path(s, CONFIG_TCC_SYSINCLUDEPATHS); } /* if bound checking, then add corresponding sections */ @@ -2146,24 +1336,29 @@ int tcc_set_output_type(TCCState *s, int output_type) put_stabs("", 0, 0, 0, 0); } +#ifdef TCC_TARGET_PE + tcc_add_library_path(s, CONFIG_TCC_LIBPATHS); +# ifdef _WIN32 + tcc_add_systemdir(s); +# endif +#else /* add libc crt1/crti objects */ -#ifndef TCC_TARGET_PE if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) && !s->nostdlib) { if (output_type != TCC_OUTPUT_DLL) - tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crt1.o"); - tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crti.o"); + tcc_add_crt(s, "crt1.o"); + tcc_add_crt(s, "crti.o"); } #endif - -#ifdef TCC_TARGET_PE - snprintf(buf, sizeof(buf), "%s/lib", s->tcc_lib_path); - tcc_add_library_path(s, buf); -#endif - 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 */ @@ -2181,7 +1376,7 @@ static const FlagDef warning_defs[] = { "implicit-function-declaration" }, }; -static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags, +ST_FUNC int set_flag(TCCState *s, const FlagDef *flags, int nb_flags, const char *name, int value) { int i; @@ -2205,9 +1400,8 @@ static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags, return 0; } - /* set/reset a warning */ -int tcc_set_warning(TCCState *s, const char *warning_name, int value) +static int tcc_set_warning(TCCState *s, const char *warning_name, int value) { int i; const FlagDef *p; @@ -2232,19 +1426,507 @@ static const FlagDef flag_defs[] = { }; /* set/reset a flag */ -int tcc_set_flag(TCCState *s, const char *flag_name, int value) +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); } -/* set CONFIG_TCCDIR at runtime */ -void tcc_set_lib_path(TCCState *s, const char *path) + +static int strstart(const char *val, const char **str) { - s->tcc_lib_path = tcc_strdup(path); + 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_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, "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_L, + TCC_OPTION_B, + TCC_OPTION_l, + TCC_OPTION_bench, + TCC_OPTION_bt, + TCC_OPTION_b, + TCC_OPTION_g, + TCC_OPTION_c, + TCC_OPTION_static, + TCC_OPTION_shared, + TCC_OPTION_soname, + TCC_OPTION_o, + TCC_OPTION_r, + TCC_OPTION_s, + TCC_OPTION_Wl, + TCC_OPTION_W, + TCC_OPTION_O, + TCC_OPTION_m, + TCC_OPTION_f, + TCC_OPTION_isystem, + 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, + TCC_OPTION_dumpversion, +}; + +#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 }, + { "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 }, + { "static", TCC_OPTION_static, 0 }, + { "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 }, + { "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 }, + { "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 }, + { "dumpversion", TCC_OPTION_dumpversion, 0}, + { 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); +} + +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; + + /* collect -Wl options for input such as "-Wl,-rpath -Wl," */ + CString linker_arg; + cstr_new(&linker_arg); + + while (optind < argc) { + + r = argv[optind++]; + if (r[0] != '-' || r[1] == '\0') { + /* add a new file */ + dynarray_add((void ***)&s->files, &s->nb_files, tcc_strdup(r)); + 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: + if (tcc_add_include_path(s, optarg) < 0) + tcc_error("too many include paths"); + 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: + dynarray_add((void ***)&s->files, &s->nb_files, tcc_strdup(r)); + 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: + s->output_type = TCC_OUTPUT_OBJ; + break; + case TCC_OPTION_static: + s->static_link = 1; + break; + case TCC_OPTION_shared: + 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: + s->outfile = tcc_strdup(optarg); + break; + case TCC_OPTION_r: + /* generate a .o merging several output files */ + 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_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: + 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: + s->output_type = TCC_OUTPUT_PREPROCESS; + 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_O: + case TCC_OPTION_pedantic: + case TCC_OPTION_pipe: + case TCC_OPTION_s: + case TCC_OPTION_x: + /* ignored */ + break; + default: + if (s->warn_unsupported) { + unsupported_option: + tcc_warning("unsupported option '%s'", r); + } + break; + } + } + + if (pthread && s->output_type != TCC_OUTPUT_OBJ) + tcc_set_options(s, "-lpthread"); + + 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; } -void tcc_print_stats(TCCState *s, int64_t total_time) +PUB_FUNC void tcc_print_stats(TCCState *s, int64_t total_time) { double tt; tt = (double)total_time / 1000000.0; diff --git a/libtcc.h b/libtcc.h index 96070e2..e69cc6b 100644 --- a/libtcc.h +++ b/libtcc.h @@ -1,10 +1,8 @@ #ifndef LIBTCC_H #define LIBTCC_H -#ifdef LIBTCC_AS_DLL -#define LIBTCCAPI __declspec(dllexport) -#else -#define LIBTCCAPI +#ifndef LIBTCCAPI +# define LIBTCCAPI #endif #ifdef __cplusplus @@ -21,15 +19,15 @@ LIBTCCAPI TCCState *tcc_new(void); /* free a TCC compilation context */ LIBTCCAPI void tcc_delete(TCCState *s); -/* add debug information in the generated code */ -LIBTCCAPI void tcc_enable_debug(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)); + void (*error_func)(void *opaque, const char *msg)); -/* set/reset a warning */ -LIBTCCAPI int tcc_set_warning(TCCState *s, const char *warning_name, int value); +/* set options as from command line (multiple supported) */ +LIBTCCAPI int tcc_set_options(TCCState *s, const char *str); /*****************************/ /* preprocessor */ @@ -49,29 +47,22 @@ LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym); /*****************************/ /* compiling */ -/* add a file (either a C file, dll, an object, a library or an ld - script). Return -1 if error. */ +/* add a file (C file, dll, object, library, ld script). Return -1 if error. */ LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename); -/* compile a string containing a C source. Return non zero if - error. */ +/* 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 */ -#define TCC_OUTPUT_MEMORY 0 /* output will be ran in memory (no - output file) (default) */ +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type); +#define TCC_OUTPUT_MEMORY 0 /* output will be run in memory (default) */ #define TCC_OUTPUT_EXE 1 /* executable file */ #define TCC_OUTPUT_DLL 2 /* dynamic library */ #define TCC_OUTPUT_OBJ 3 /* object file */ -#define TCC_OUTPUT_PREPROCESS 4 /* preprocessed file (used internally) */ -LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type); - -#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 TCC_OUTPUT_PREPROCESS 4 /* only preprocess (used internally) */ /* equivalent to -Lpath option */ LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname); @@ -80,7 +71,7 @@ LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname); 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, void *val); +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. */ @@ -90,17 +81,18 @@ LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename); tcc_relocate() before. */ LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv); -/* copy code into memory passed in by the caller and do all relocations - (needed before using tcc_get_symbol()). - returns -1 on error and required size if ptr is NULL */ +/* 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); -/* set CONFIG_TCCDIR at runtime */ -LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); - #ifdef __cplusplus } #endif diff --git a/tcc-doc.html b/tcc-doc.html index e40532e..727fa87 100644 --- a/tcc-doc.html +++ b/tcc-doc.html @@ -1,14 +1,13 @@ - - Tiny C Compiler Reference Documentation @@ -17,11 +16,12 @@ - +