From 5e56fb635a23484d8fda8b54a40900d0a54b0ba1 Mon Sep 17 00:00:00 2001 From: jiang <30155751@qq.com> Date: Sun, 4 May 2014 13:18:31 +0800 Subject: Return to: e20c1eb99e1003c1e59522c136dbb15c52d7cc7c 1: The new patch for the other machines still have the problem. 2: libcrt Rename (what if gcc had libcrt as well) 3: parse_number exact problem 4: VT_VLS is to allow tcc Compile the following int b = 9; struct st { int a; int b [b] }; struct st st1; st1.b [8] = 9; printf ("% d \ n", st1.b [8]); tcc a problem. Due to problems in front, and now can not be improved 5: they commit much, bug difficult to lock, you can not let other people help develop. 6: ('\ t') too Thanks to Michael and Ray Their criticism I have benefited! --- Makefile | 58 +- arm-gen.c | 93 +-- asmx86-tok.h | 243 ------- asmx86.c | 1531 ------------------------------------------- c67-gen.c | 44 +- i386-asm.c | 1502 ++++++++++++++++++++++++++++++++++++++++++ i386-gen.c | 190 ++---- i386-tok.h | 243 +++++++ il-gen.c | 15 +- lib/Makefile | 16 +- lib/libcrt.c | 743 --------------------- lib/libtcc1.c | 739 +++++++++++++++++++++ libtcc.c | 10 +- tcc.h | 40 +- tccasm.c | 49 +- tccelf.c | 12 +- tccgen.c | 850 +++++++++++------------- tccpe.c | 2 +- tccpp.c | 302 +++------ tcctok.h | 4 +- tests/Makefile | 20 +- tests/abitest.c | 2 +- tests/libtcc_test.c | 2 +- tests/tcctest.c | 54 +- win32/tools/tiny_libmaker.c | 2 +- x86_64-gen.c | 1376 +++++++++++++++++++------------------- 26 files changed, 3872 insertions(+), 4270 deletions(-) delete mode 100644 asmx86-tok.h delete mode 100644 asmx86.c create mode 100644 i386-asm.c create mode 100644 i386-tok.h delete mode 100644 lib/libcrt.c create mode 100644 lib/libtcc1.c diff --git a/Makefile b/Makefile index 0f3002b..4f18567 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ endif endif else # not GCC ifeq (-$(findstring clang,$(CC))-,-clang-) -# make clang accept gnuisms in libcrt.c +# make clang accept gnuisms in libtcc1.c CFLAGS+=-fheinous-gnu-extensions endif endif @@ -101,11 +101,11 @@ $(ARM_EABI_CROSS)_LINK = arm-eabi-tcc$(EXESUF) 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 asmx86.c i386-asm.h asmx86-tok.h -WIN32_FILES = $(CORE_FILES) i386-gen.c asmx86.c i386-asm.h asmx86-tok.h tccpe.c -WIN64_FILES = $(CORE_FILES) x86_64-gen.c asmx86.c x86_64-asm.h tccpe.c +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 asmx86.c x86_64-asm.h +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 @@ -113,29 +113,29 @@ 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/libcrt.a -LIBCRT=libcrt.a +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=$(WIN64_CROSS) $(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(C67_CROSS) -LIBTCC1_CROSS=lib/x86_64-win32/libcrt.a -LIBCRT=libcrt.a +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) $(WIN64_CROSS) $(ARM_CROSS) $(C67_CROSS) -LIBTCC1_CROSS=lib/i386-win32/libcrt.a lib/x86_64-win32/libcrt.a -LIBCRT=libcrt.a +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) $(WIN64_CROSS) $(ARM_CROSS) $(C67_CROSS) -LIBTCC1_CROSS=lib/i386-win32/libcrt.a lib/x86_64-win32/libcrt.a lib/i386/libcrt.a -LIBCRT=libcrt.a +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) $(WIN64_CROSS) $(C67_CROSS) -LIBCRT=libcrt.a -LIBTCC1_CROSS=lib/i386-win32/libcrt.a lib/x86_64-win32/libcrt.a lib/i386/libcrt.a +LIBTCC1=libtcc1.a +LIBTCC1_CROSS=lib/i386-win32/libtcc1.a lib/x86_64-win32/libtcc1.a lib/i386/libtcc1.a endif PROGS_CROSS_LINK=$(foreach PROG_CROSS,$(PROGS_CROSS),$($(PROG_CROSS)_LINK)) @@ -143,7 +143,7 @@ ifeq ($(TARGETOS),Darwin) PROGS+=tiny_libmaker$(EXESUF) endif -TCCLIBS = $(LIBCRT) $(LIBTCC) $(LIBTCC_EXTRA) +TCCLIBS = $(LIBTCC1) $(LIBTCC) $(LIBTCC_EXTRA) TCCDOCS = tcc.1 tcc-doc.html tcc-doc.info ifdef CONFIG_CROSS @@ -225,9 +225,9 @@ tiny_libmaker$(EXESUF): win32/tools/tiny_libmaker.c $(CC) -o $@ $< $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) # TinyCC runtime libraries -libcrt.a : FORCE +libtcc1.a : FORCE $(MAKE) -C lib native -lib/%/libcrt.a : FORCE $(PROGS_CROSS) +lib/%/libtcc1.a : FORCE $(PROGS_CROSS) $(MAKE) -C lib cross TARGET=$* FORCE: @@ -258,8 +258,8 @@ endif -$(INSTALL) -m644 tcc-doc.info "$(infodir)" mkdir -p "$(tccdir)" mkdir -p "$(tccdir)/include" -ifneq ($(LIBCRT),) - $(INSTALL) -m644 $(LIBCRT) "$(tccdir)" +ifneq ($(LIBTCC1),) + $(INSTALL) -m644 $(LIBTCC1) "$(tccdir)" endif $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) $(top_srcdir)/tcclib.h "$(tccdir)/include" mkdir -p "$(libdir)" @@ -277,19 +277,19 @@ ifdef CONFIG_CROSS mkdir -p "$(tccdir)/win32/lib/64" ifneq ($(ARCH),i386) mkdir -p "$(tccdir)/i386" - $(INSTALL) -m644 lib/i386/libcrt.a "$(tccdir)/i386" + $(INSTALL) -m644 lib/i386/libtcc1.a "$(tccdir)/i386" cp -r "$(tccdir)/include" "$(tccdir)/i386" endif $(INSTALL) -m644 $(top_srcdir)/win32/lib/*.def "$(tccdir)/win32/lib" - $(INSTALL) -m644 lib/i386-win32/libcrt.a "$(tccdir)/win32/lib/32" - $(INSTALL) -m644 lib/x86_64-win32/libcrt.a "$(tccdir)/win32/lib/64" + $(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 $(top_srcdir)/win32/include/. "$(tccdir)/win32/include" cp -r "$(tccdir)/include" "$(tccdir)/win32" endif uninstall: rm -fv $(foreach P,$(PROGS),"$(bindir)/$P") - rm -fv $(foreach P,$(LIBCRT),"$(tccdir)/$P") + rm -fv $(foreach P,$(LIBTCC1),"$(tccdir)/$P") rm -fv $(foreach P,$(TCC_INCLUDES),"$(tccdir)/include/$P") rm -fv "$(tccdir)/include/tcclib.h" rm -fv "$(docdir)/tcc-doc.html" "$(mandir)/man1/tcc.1" "$(infodir)/tcc-doc.info" @@ -310,7 +310,7 @@ install: $(PROGS) $(TCCLIBS) $(TCCDOCS) mkdir -p "$(tccdir)/doc" mkdir -p "$(tccdir)/libtcc" $(INSTALLBIN) -m755 $(PROGS) "$(tccdir)" - $(INSTALL) -m644 $(LIBCRT) $(top_srcdir)/win32/lib/*.def "$(tccdir)/lib" + $(INSTALL) -m644 $(LIBTCC1) $(top_srcdir)/win32/lib/*.def "$(tccdir)/lib" cp -r $(top_srcdir)/win32/include/. "$(tccdir)/include" cp -r $(top_srcdir)/win32/examples/. "$(tccdir)/examples" $(INSTALL) -m644 $(addprefix $(top_srcdir)/include/,$(TCC_INCLUDES)) $(top_srcdir)/tcclib.h "$(tccdir)/include" @@ -320,8 +320,8 @@ install: $(PROGS) $(TCCLIBS) $(TCCDOCS) ifdef CONFIG_CROSS mkdir -p "$(tccdir)/lib/32" mkdir -p "$(tccdir)/lib/64" - -$(INSTALL) -m644 lib/i386-win32/libcrt.a "$(tccdir)/lib/32" - -$(INSTALL) -m644 lib/x86_64-win32/libcrt.a "$(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: @@ -340,7 +340,7 @@ tcc-doc.info: tcc-doc.texi -makeinfo $< # in tests subdir -export LIBCRT +export LIBTCC1 %est: $(MAKE) -C tests $@ 'PROGS_CROSS=$(PROGS_CROSS)' @@ -348,7 +348,7 @@ export LIBCRT clean: rm -vf $(PROGS) tcc_p$(EXESUF) tcc.pod *~ *.o *.a *.so* *.out *.exe libtcc_test$(EXESUF) $(MAKE) -C tests $@ -ifneq ($(LIBCRT),) +ifneq ($(LIBTCC1),) $(MAKE) -C lib $@ endif diff --git a/arm-gen.c b/arm-gen.c index 567c868..680a490 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -61,7 +61,7 @@ #define RC_IRET RC_R0 /* function return: integer register */ #define RC_LRET RC_R1 /* function return: second integer register */ #define RC_FRET RC_F0 /* function return: float register */ -#define RC_MASK (RC_INT|RC_FLOAT) + /* pretty names for the registers */ enum { TREG_R0 = 0, @@ -540,14 +540,6 @@ void load(int r, SValue *sv) v = fr & VT_VALMASK; if (fr & VT_LVAL) { uint32_t base = 0xB; // fp - if(fr & VT_TMP){ - int size, align; - if((ft & VT_BTYPE) == VT_FUNC) - size = PTR_SIZE; - else - size = type_size(&sv->type, &align); - loc_stack(size, 0); - } if(v == VT_LLOCAL) { v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; @@ -1417,60 +1409,37 @@ void gjmp_addr(int a) /* generate a test. set 'inv' to invert test. Stack entry is popped */ int gtst(int inv, int t) { - int v, r; - uint32_t op; - v = vtop->r & VT_VALMASK; - r=ind; - if (v == VT_CMP) { - op=mapcc(inv?negcc(vtop->c.i):vtop->c.i); - op|=encbranch(r,t,1); - o(op); - t=r; - } else if (v == VT_JMP || v == VT_JMPI) { - if ((v & 1) == inv) { - if(!vtop->c.i) - vtop->c.i=t; - else { - uint32_t *x; - int p,lp; - if(t) { - p = vtop->c.i; - do { - p = decbranch(lp=p); - } while(p); - x = (uint32_t *)(cur_text_section->data + lp); - *x &= 0xff000000; - *x |= encbranch(lp,t,1); - } - t = vtop->c.i; - } - } else { - t = gjmp(t); - gsym(vtop->c.i); + int v, r; + uint32_t op; + v = vtop->r & VT_VALMASK; + r=ind; + if (v == VT_CMP) { + op=mapcc(inv?negcc(vtop->c.i):vtop->c.i); + op|=encbranch(r,t,1); + o(op); + t=r; + } else { /* VT_JMP || VT_JMPI */ + if ((v & 1) == inv) { + if(!vtop->c.i) + vtop->c.i=t; + else { + uint32_t *x; + int p,lp; + if(t) { + p = vtop->c.i; + do { + p = decbranch(lp=p); + } while(p); + x = (uint32_t *)(cur_text_section->data + lp); + *x &= 0xff000000; + *x |= encbranch(lp,t,1); + } + t = vtop->c.i; + } + } else { + t = gjmp(t); + gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t)) { - r=gv(RC_FLOAT); -#ifdef TCC_ARM_VFP - o(0xEEB50A40|(vfpr(r)<<12)|T2CPR(vtop->type.t)); /* fcmpzX */ - o(0xEEF1FA10); /* fmstat */ -#else - o(0xEE90F118|(fpr(r)<<16)); -#endif - vtop->r = VT_CMP; - vtop->c.i = TOK_NE; - return gtst(inv, t); - } else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - o(0xE3300000|(intr(v)<<16)); - vtop->r = VT_CMP; - vtop->c.i = TOK_NE; - return gtst(inv, t); - } } vtop--; return t; diff --git a/asmx86-tok.h b/asmx86-tok.h deleted file mode 100644 index 6ba865d..0000000 --- a/asmx86-tok.h +++ /dev/null @@ -1,243 +0,0 @@ -/* ------------------------------------------------------------------ */ -/* 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 existent op, just to have a regular table */ - DEF_FP1(com) - - DEF_FP(comp) - DEF_FP(sub) - DEF_FP(subr) - DEF_FP(div) - DEF_FP(divr) - - DEF_BWLX(xadd) - DEF_BWLX(cmpxchg) - - /* string ops */ - DEF_BWLX(cmps) - DEF_BWLX(scmp) - DEF_BWL(ins) - DEF_BWL(outs) - DEF_BWLX(lods) - DEF_BWLX(slod) - DEF_BWLX(movs) - DEF_BWLX(smov) - DEF_BWLX(scas) - DEF_BWLX(ssca) - DEF_BWLX(stos) - DEF_BWLX(ssto) - - /* generic asm ops */ -#define ALT(x) -#define DEF_ASM_OP0(name, opcode) DEF_ASM(name) -#define DEF_ASM_OP0L(name, opcode, group, instr_type) -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif - -#define ALT(x) -#define DEF_ASM_OP0(name, opcode) -#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name) -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name) -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name) -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name) -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif diff --git a/asmx86.c b/asmx86.c deleted file mode 100644 index 9010c03..0000000 --- a/asmx86.c +++ /dev/null @@ -1,1531 +0,0 @@ -/* - * i386 specific functions for TCC assembler - * - * Copyright (c) 2001, 2002 Fabrice Bellard - * Copyright (c) 2009 Frédéric Feret (x86_64 support) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include "tcc.h" - -/* #define NB_ASM_REGS 8 */ -#define MAX_OPERANDS 3 -#define NB_SAVED_REGS 3 - -#define TOK_ASM_first TOK_ASM_clc -#define TOK_ASM_last TOK_ASM_emms -#define TOK_ASM_alllast TOK_ASM_pxor - -#define OPC_JMP 0x01 /* jmp operand */ -#define OPC_B 0x02 /* only used with OPC_WL */ -#define OPC_WL 0x04 /* accepts w, l or no suffix */ -#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ -#define OPC_REG 0x08 /* register is added to opcode */ -#define OPC_MODRM 0x10 /* modrm encoding */ -#define OPC_FWAIT 0x20 /* add fwait opcode */ -#define OPC_TEST 0x40 /* test opcodes */ -#define OPC_SHIFT 0x80 /* shift opcodes */ -#define OPC_D16 0x0100 /* generate data16 prefix */ -#define OPC_ARITH 0x0200 /* arithmetic opcodes */ -#define OPC_SHORTJMP 0x0400 /* short jmp operand */ -#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ -#ifdef TCC_TARGET_X86_64 -# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */ -# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */ -# define OPC_WLX OPC_WLQ -#else -# define OPC_WLX OPC_WL -#endif - -#define OPC_GROUP_SHIFT 13 - -/* in order to compress the operand type, we use specific operands and - we or only with EA */ -enum { - OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */ -#ifdef TCC_TARGET_X86_64 - OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */ -#endif - OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ - OPT_SEG, - OPT_ST, - OPT_IM8, - OPT_IM8S, - OPT_IM16, - OPT_IM32, -#ifdef TCC_TARGET_X86_64 - OPT_IM64, -#endif - OPT_EAX, /* %al, %ax, %eax or %rax register */ - OPT_ST0, /* %st(0) register */ - OPT_CL, /* %cl register */ - OPT_DX, /* %dx register */ - OPT_ADDR, /* OP_EA with only offset */ - OPT_INDIR, /* *(expr) */ - /* composite types */ - OPT_COMPOSITE_FIRST, - OPT_IM, /* IM8 | IM16 | IM32 | IM64 */ - OPT_REG, /* REG8 | REG16 | REG32 | REG64 */ - OPT_REGW, /* REG16 | REG32 | REG64 */ - OPT_IMW, /* IM16 | IM32 | IM64 */ -#ifdef TCC_TARGET_X86_64 - OPT_IMNO64, /* IM16 | IM32 */ -#endif - /* can be ored with any OPT_xxx */ - OPT_EA = 0x80 -}; - -#define OP_REG8 (1 << OPT_REG8) -#define OP_REG16 (1 << OPT_REG16) -#define OP_REG32 (1 << OPT_REG32) -#define OP_MMX (1 << OPT_MMX) -#define OP_SSE (1 << OPT_SSE) -#define OP_CR (1 << OPT_CR) -#define OP_TR (1 << OPT_TR) -#define OP_DB (1 << OPT_DB) -#define OP_SEG (1 << OPT_SEG) -#define OP_ST (1 << OPT_ST) -#define OP_IM8 (1 << OPT_IM8) -#define OP_IM8S (1 << OPT_IM8S) -#define OP_IM16 (1 << OPT_IM16) -#define OP_IM32 (1 << OPT_IM32) -#define OP_EAX (1 << OPT_EAX) -#define OP_ST0 (1 << OPT_ST0) -#define OP_CL (1 << OPT_CL) -#define OP_DX (1 << OPT_DX) -#define OP_ADDR (1 << OPT_ADDR) -#define OP_INDIR (1 << OPT_INDIR) -#ifdef TCC_TARGET_X86_64 -# define OP_REG64 (1 << OPT_REG64) -# define OP_IM64 (1 << OPT_IM64) -#else -# define OP_REG64 0 -# define OP_IM64 0 -#endif - -#define OP_EA 0x40000000 -#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64) - -#ifdef TCC_TARGET_X86_64 -# define OP_IM OP_IM64 -# define TREG_XAX TREG_RAX -# define TREG_XCX TREG_RCX -# define TREG_XDX TREG_RDX -#else -# define OP_IM OP_IM32 -# define TREG_XAX TREG_EAX -# define TREG_XCX TREG_ECX -# define TREG_XDX TREG_EDX -#endif - -typedef struct ASMInstr { - uint16_t sym; - uint16_t opcode; - uint16_t instr_type; - uint8_t nb_ops; - uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ -} ASMInstr; - -typedef struct Operand { - uint32_t type; - int8_t reg; /* register, -1 if none */ - int8_t reg2; /* second register, -1 if none */ - uint8_t shift; - ExprValue e; -} Operand; - -static const uint8_t reg_to_size[9] = { -/* - [OP_REG8] = 0, - [OP_REG16] = 1, - [OP_REG32] = 2, -#ifdef TCC_TARGET_X86_64 - [OP_REG64] = 3, -#endif -*/ - 0, 0, 1, 0, 2, 0, 0, 0, 3 -}; - -#define NB_TEST_OPCODES 30 - -static const uint8_t test_bits[NB_TEST_OPCODES] = { - 0x00, /* o */ - 0x01, /* no */ - 0x02, /* b */ - 0x02, /* c */ - 0x02, /* nae */ - 0x03, /* nb */ - 0x03, /* nc */ - 0x03, /* ae */ - 0x04, /* e */ - 0x04, /* z */ - 0x05, /* ne */ - 0x05, /* nz */ - 0x06, /* be */ - 0x06, /* na */ - 0x07, /* nbe */ - 0x07, /* a */ - 0x08, /* s */ - 0x09, /* ns */ - 0x0a, /* p */ - 0x0a, /* pe */ - 0x0b, /* np */ - 0x0b, /* po */ - 0x0c, /* l */ - 0x0c, /* nge */ - 0x0d, /* nl */ - 0x0d, /* ge */ - 0x0e, /* le */ - 0x0e, /* ng */ - 0x0f, /* nle */ - 0x0f, /* g */ -}; - -static const uint8_t segment_prefixes[] = { - 0x26, /* es */ - 0x2e, /* cs */ - 0x36, /* ss */ - 0x3e, /* ds */ - 0x64, /* fs */ - 0x65 /* gs */ -}; - -static const ASMInstr asm_instrs[] = { -#define ALT(x) x -#define DEF_ASM_OP0(name, opcode) -#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }}, -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif - /* last operation */ - { 0, }, -}; - -static const uint16_t op0_codes[] = { -#define ALT(x) -#define DEF_ASM_OP0(x, opcode) opcode, -#define DEF_ASM_OP0L(name, opcode, group, instr_type) -#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) -#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) -#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) -#ifdef TCC_TARGET_X86_64 -# include "x86_64-asm.h" -#else -# include "i386-asm.h" -#endif -}; - -#ifdef PRINTF_ASM_CODE -void printf_asm_opcode(){ - const ASMInstr *pa; - int freq[4]; - int op_vals[500]; - int nb_op_vals, i, j; - nb_op_vals = 0; - memset(freq, 0, sizeof(freq)); - for(pa = asm_instrs; pa->sym != 0; pa++) { - freq[pa->nb_ops]++; - for(i=0;inb_ops;i++) { - for(j=0;jop_type[i] == op_vals[j]) - goto found; - } - op_vals[nb_op_vals++] = pa->op_type[i]; - found: ; - } - } - for(i=0;iseg_size == 16) - tcc_error("invalid effective address"); -#endif - v = asm_int_expr(s1); - switch(v) { - case 1: - shift = 0; - break; - case 2: - shift = 1; - break; - case 4: - shift = 2; - break; - case 8: - shift = 3; - break; - default: - expect("1, 2, 4 or 8 constant"); - shift = 0; - break; - } - return shift; -} - -static int asm_parse_reg(void) -{ - int reg = 0; - if (tok != '%') - goto error_32; - next(); - if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { - reg = tok - TOK_ASM_eax; -#ifdef TCC_TARGET_X86_64 - } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { - reg = tok - TOK_ASM_rax; -#endif -#ifdef I386_ASM_16 - } else if (tok >= TOK_ASM_ax && tok <= TOK_ASM_di) { - reg = tok - TOK_ASM_ax; -#endif - } else { - error_32: - expect("register"); - } - next(); - return reg; -} - -static void parse_operand(TCCState *s1, Operand *op) -{ - ExprValue e; - int reg, indir; - const char *p; - - indir = 0; - if (tok == '*') { - next(); - indir = OP_INDIR; - } - - if (tok == '%') { - next(); - if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { - reg = tok - TOK_ASM_al; - op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ - op->reg = reg & 7; - if ((op->type & OP_REG) && op->reg == TREG_XAX) - op->type |= OP_EAX; - else if (op->type == OP_REG8 && op->reg == TREG_XCX) - op->type |= OP_CL; - else if (op->type == OP_REG16 && op->reg == TREG_XDX) - op->type |= OP_DX; - } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { - op->type = OP_DB; - op->reg = tok - TOK_ASM_dr0; - } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { - op->type = OP_SEG; - op->reg = tok - TOK_ASM_es; - } else if (tok == TOK_ASM_st) { - op->type = OP_ST; - op->reg = 0; - next(); - if (tok == '(') { - next(); - if (tok != TOK_PPNUM) - goto reg_error; - p = tokc.cstr->data; - reg = p[0] - '0'; - if ((unsigned)reg >= 8 || p[1] != '\0') - goto reg_error; - op->reg = reg; - next(); - skip(')'); - } - if (op->reg == 0) - op->type |= OP_ST0; - goto no_skip; - } else { - reg_error: - tcc_error("unknown register"); - } - next(); - no_skip: ; - } else if (tok == '$') { - /* constant value */ - next(); - asm_expr(s1, &e); - op->type = OP_IM; - op->e.v = e.v; - op->e.sym = e.sym; - if (!op->e.sym) { - if (op->e.v == (uint8_t)op->e.v) - op->type |= OP_IM8; - if (op->e.v == (int8_t)op->e.v) - op->type |= OP_IM8S; - if (op->e.v == (uint16_t)op->e.v) - op->type |= OP_IM16; -#ifdef TCC_TARGET_X86_64 - if (op->e.v == (uint32_t)op->e.v) - op->type |= OP_IM32; -#endif - } - } else { - /* address(reg,reg2,shift) with all variants */ - op->type = OP_EA; - op->reg = -1; - op->reg2 = -1; - op->shift = 0; - if (tok != '(') { - asm_expr(s1, &e); - op->e.v = e.v; - op->e.sym = e.sym; - } else { - next(); - if (tok == '%') { - unget_tok('('); - op->e.v = 0; - op->e.sym = NULL; - } else { - /* bracketed offset expression */ - asm_expr(s1, &e); - if (tok != ')') - expect(")"); - next(); - op->e.v = e.v; - op->e.sym = e.sym; - } - } - if (tok == '(') { - next(); - if (tok != ',') { - op->reg = asm_parse_reg(); - } - if (tok == ',') { - next(); - if (tok != ',') { - op->reg2 = asm_parse_reg(); - } - if (tok == ',') { - next(); - op->shift = get_reg_shift(s1); - } - } - skip(')'); - } - if (op->reg == -1 && op->reg2 == -1) - op->type |= OP_ADDR; - } - op->type |= indir; -} - -/* XXX: unify with C code output ? */ -ST_FUNC void gen_expr32(ExprValue *pe) -{ - gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); -} - -#ifdef TCC_TARGET_X86_64 -static void gen_expr64(ExprValue *pe) -{ - gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v); -} -#endif - -/* XXX: unify with C code output ? */ -static void gen_disp32(ExprValue *pe) -{ - Sym *sym = pe->sym; - if (sym && sym->r == cur_text_section->sh_num) { - /* same section: we can output an absolute value. Note - that the TCC compiler behaves differently here because - it always outputs a relocation to ease (future) code - elimination in the linker */ - gen_le32(pe->v + sym->jnext - ind - 4); - } else { - if (sym && sym->type.t == VT_VOID) { - sym->type.t = VT_FUNC; - sym->type.ref = NULL; - } - gen_addrpc32(VT_SYM, sym, pe->v); - } -} - -#ifdef I386_ASM_16 -static void gen_expr16(ExprValue *pe) -{ - if (pe->sym) - greloc(cur_text_section, pe->sym, ind, R_386_16); - gen_le16(pe->v); -} -static void gen_disp16(ExprValue *pe) -{ - Sym *sym; - sym = pe->sym; - if (sym) { - if (sym->r == cur_text_section->sh_num) { - /* same section: we can output an absolute value. Note - that the TCC compiler behaves differently here because - it always outputs a relocation to ease (future) code - elimination in the linker */ - gen_le16(pe->v + sym->jnext - ind - 2); - } else { - greloc(cur_text_section, sym, ind, R_386_PC16); - gen_le16(pe->v - 2); - } - } else { - /* put an empty PC32 relocation */ - put_elf_reloc(symtab_section, cur_text_section, - ind, R_386_PC16, 0); - gen_le16(pe->v - 2); - } -} -#endif - -/* generate the modrm operand */ -static inline void asm_modrm(int reg, Operand *op) -{ - int mod, reg1, reg2, sib_reg1; - - if (op->type & (OP_REG | OP_MMX | OP_SSE)) { - g(0xc0 + (reg << 3) + op->reg); - } else if (op->reg == -1 && op->reg2 == -1) { - /* displacement only */ -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) { - g(0x06 + (reg << 3)); - gen_expr16(&op->e); - } else if (tcc_state->seg_size == 32) -#endif - { - g(0x05 + (reg << 3)); - gen_expr32(&op->e); - } - } else { - sib_reg1 = op->reg; - /* fist compute displacement encoding */ - if (sib_reg1 == -1) { - sib_reg1 = 5; - mod = 0x00; - } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { - mod = 0x00; - } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { - mod = 0x40; - } else { - mod = 0x80; - } - /* compute if sib byte needed */ - reg1 = op->reg; - if (op->reg2 != -1) - reg1 = 4; -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 32) { -#endif - g(mod + (reg << 3) + reg1); - if (reg1 == 4) { - /* add sib byte */ - reg2 = op->reg2; - if (reg2 == -1) - reg2 = 4; /* indicate no index */ - g((op->shift << 6) + (reg2 << 3) + sib_reg1); - } -#ifdef I386_ASM_16 - } else if (tcc_state->seg_size == 16) { - /* edi = 7, esi = 6 --> di = 5, si = 4 */ - if ((reg1 == 6) || (reg1 == 7)) { - reg1 -= 2; - /* ebx = 3 --> bx = 7 */ - } else if (reg1 == 3) { - reg1 = 7; - /* o32 = 5 --> o16 = 6 */ - } else if (reg1 == 5) { - reg1 = 6; - /* sib not valid in 16-bit mode */ - } else if (reg1 == 4) { - reg2 = op->reg2; - /* bp + si + offset */ - if ((sib_reg1 == 5) && (reg2 == 6)) { - reg1 = 2; - /* bp + di + offset */ - } else if ((sib_reg1 == 5) && (reg2 == 7)) { - reg1 = 3; - /* bx + si + offset */ - } else if ((sib_reg1 == 3) && (reg2 == 6)) { - reg1 = 0; - /* bx + di + offset */ - } else if ((sib_reg1 == 3) && (reg2 == 7)) { - reg1 = 1; - } else { - tcc_error("invalid effective address"); - } - if (op->e.v == 0) - mod = 0; - } else { - tcc_error("invalid register"); - } - g(mod + (reg << 3) + reg1); - } -#endif - /* add offset */ - if (mod == 0x40) { - g(op->e.v); - } else if (mod == 0x80 || op->reg == -1) { -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) - gen_expr16(&op->e); - else if (tcc_state->seg_size == 32) -#endif - gen_expr32(&op->e); - } - } -} - -ST_FUNC void asm_opcode(TCCState *s1, int opcode) -{ - const ASMInstr *pa; - int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix; - int nb_ops, s; - Operand ops[MAX_OPERANDS], *pop; - int op_type[3]; /* decoded op type */ -#ifdef I386_ASM_16 - static int a32 = 0, o32 = 0, addr32 = 0, data32 = 0; -#endif - - /* force synthetic ';' after prefix instruction, so we can handle */ - /* one-line things like "rep stosb" instead of only "rep\nstosb" */ - if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz) - unget_tok(';'); - - /* get operands */ - pop = ops; - nb_ops = 0; - seg_prefix = 0; - for(;;) { - if (tok == ';' || tok == TOK_LINEFEED) - break; - if (nb_ops >= MAX_OPERANDS) { - tcc_error("incorrect number of operands"); - } - parse_operand(s1, pop); - if (tok == ':') { - if (pop->type != OP_SEG || seg_prefix) - tcc_error("incorrect prefix"); - seg_prefix = segment_prefixes[pop->reg]; - next(); - parse_operand(s1, pop); -#ifndef I386_ASM_16 - if (!(pop->type & OP_EA)) { - tcc_error("segment prefix must be followed by memory reference"); - } -#endif - } - pop++; - nb_ops++; - if (tok != ',') - break; - next(); - } - - is_short_jmp = 0; - s = 0; /* avoid warning */ - - /* optimize matching by using a lookup table (no hashing is needed - !) */ - for(pa = asm_instrs; pa->sym != 0; pa++) { - s = 0; - if (pa->instr_type & OPC_FARITH) { - v = opcode - pa->sym; - if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) - continue; - } else if (pa->instr_type & OPC_ARITH) { - if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX)) - continue; - s = (opcode - pa->sym) % NBWLX; - } else if (pa->instr_type & OPC_SHIFT) { - if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX)) - continue; - s = (opcode - pa->sym) % NBWLX; - } else if (pa->instr_type & OPC_TEST) { - if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) - continue; - } else if (pa->instr_type & OPC_B) { - if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX)) - continue; - s = opcode - pa->sym; - } else if (pa->instr_type & OPC_WLX) { - if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1)) - continue; - s = opcode - pa->sym + 1; - } else { - if (pa->sym != opcode) - continue; - } - if (pa->nb_ops != nb_ops) - continue; - /* now decode and check each operand */ - for(i = 0; i < nb_ops; i++) { - int op1, op2; - op1 = pa->op_type[i]; - op2 = op1 & 0x1f; - switch(op2) { - case OPT_IM: - v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64; - break; - case OPT_REG: - v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64; - break; - case OPT_REGW: - v = OP_REG16 | OP_REG32 | OP_REG64; - break; - case OPT_IMW: - v = OP_IM16 | OP_IM32 | OP_IM64; - break; -#ifdef TCC_TARGET_X86_64 - case OPT_IMNO64: - v = OP_IM16 | OP_IM32; - break; -#endif - default: - v = 1 << op2; - break; - } - if (op1 & OPT_EA) - v |= OP_EA; - op_type[i] = v; - if ((ops[i].type & v) == 0) - goto next; - } - /* all is matching ! */ - break; - next: ; - } - if (pa->sym == 0) { - if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) { - int b; - b = op0_codes[opcode - TOK_ASM_first]; -#ifdef I386_ASM_16 - if (opcode == TOK_ASM_o32) { - if (s1->seg_size == 32) - tcc_error("incorrect prefix"); - else - o32 = data32 = 1; - } else if (opcode == TOK_ASM_a32) { - if (s1->seg_size == 32) - tcc_error("incorrect prefix"); - else - a32 = addr32 = 1; - } -#endif - if (b & 0xff00) - g(b >> 8); - g(b); - return; - } else if (opcode <= TOK_ASM_alllast) { - tcc_error("bad operand with opcode '%s'", get_tok_str(opcode, NULL)); - } else { - tcc_error("unknown opcode '%s'", - get_tok_str(opcode, NULL)); - } - } - /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ - if (s == NBWLX-1) { - for(i = 0; s == NBWLX-1 && i < nb_ops; i++) { - if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) - s = reg_to_size[ops[i].type & OP_REG]; - } - if (s == NBWLX-1) { - if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && - (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64))) - s = 2; - else - tcc_error("cannot infer opcode suffix"); - } - } - -#ifdef I386_ASM_16 - for(i = 0; i < nb_ops; i++) { - if (ops[i].type & OP_REG32) { - if (s1->seg_size == 16) - o32 = 1; - } else if (!(ops[i].type & OP_REG32)) { - if (s1->seg_size == 32) - o32 = 1; - } - } - - - if (s == 1 || (pa->instr_type & OPC_D16)) { - if (s1->seg_size == 32) - o32 = 1; - } else if (s == 2) { - if (s1->seg_size == 16) { - if (!(pa->instr_type & OPC_D16)) - o32 = 1; - } - } - - /* generate a16/a32 prefix if needed */ - if ((a32 == 1) && (addr32 == 0)) - g(0x67); - /* generate o16/o32 prefix if needed */ - if ((o32 == 1) && (data32 == 0)) - g(0x66); - - addr32 = data32 = 0; -#else - /* generate data16 prefix if needed */ - if (s == 1 || (pa->instr_type & OPC_D16)) - g(0x66); -#ifdef TCC_TARGET_X86_64 - else if (s == 3) { - /* generate REX prefix */ - if ((opcode != TOK_ASM_push && opcode != TOK_ASM_pop) - || !(ops[0].type & OP_REG64)) - g(0x48); - } -#endif -#endif - - /* now generates the operation */ - if (pa->instr_type & OPC_FWAIT) - g(0x9b); - if (seg_prefix) - g(seg_prefix); - - v = pa->opcode; - if ((v == 0x69 || v == 0x6b) && nb_ops == 2) { - /* kludge for imul $im, %reg */ - nb_ops = 3; - ops[2] = ops[1]; - op_type[2] = op_type[1]; - } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { - v--; /* int $3 case */ - nb_ops = 0; - } else if ((v == 0x06 || v == 0x07)) { - if (ops[0].reg >= 4) { - /* push/pop %fs or %gs */ - v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); - } else { - v += ops[0].reg << 3; - } - nb_ops = 0; - } else if (v <= 0x05) { - /* arith case */ - v += ((opcode - TOK_ASM_addb) / NBWLX) << 3; - } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { - /* fpu arith case */ - v += ((opcode - pa->sym) / 6) << 3; - } - if (pa->instr_type & OPC_REG) { - for(i = 0; i < nb_ops; i++) { - if (op_type[i] & (OP_REG | OP_ST)) { - v += ops[i].reg; - break; - } - } - /* mov $im, %reg case */ - if (pa->opcode == 0xb0 && s >= 1) - v += 7; - } - if (pa->instr_type & OPC_B) - v += s >= 1; - if (pa->instr_type & OPC_TEST) - v += test_bits[opcode - pa->sym]; - if (pa->instr_type & OPC_SHORTJMP) { - Sym *sym; - int jmp_disp; - - /* see if we can really generate the jump with a byte offset */ - sym = ops[0].e.sym; - if (!sym) - goto no_short_jump; - if (sym->r != cur_text_section->sh_num) - goto no_short_jump; - jmp_disp = ops[0].e.v + sym->jnext - ind - 2; - if (jmp_disp == (int8_t)jmp_disp) { - /* OK to generate jump */ - is_short_jmp = 1; - ops[0].e.v = jmp_disp; - } else { - no_short_jump: - if (pa->instr_type & OPC_JMP) { - /* long jump will be allowed. need to modify the - opcode slightly */ - if (v == 0xeb) - v = 0xe9; - else - v += 0x0f10; - } else { - tcc_error("invalid displacement"); - } - } - } - op1 = v >> 8; - if (op1) - g(op1); - g(v); - - /* search which operand will used for modrm */ - modrm_index = 0; - if (pa->instr_type & OPC_SHIFT) { - reg = (opcode - pa->sym) / NBWLX; - if (reg == 6) - reg = 7; - } else if (pa->instr_type & OPC_ARITH) { - reg = (opcode - pa->sym) / NBWLX; - } else if (pa->instr_type & OPC_FARITH) { - reg = (opcode - pa->sym) / 6; - } else { - reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; - } - if (pa->instr_type & OPC_MODRM) { - /* first look for an ea operand */ - for(i = 0;i < nb_ops; i++) { - if (op_type[i] & OP_EA) - goto modrm_found; - } - /* then if not found, a register or indirection (shift instructions) */ - for(i = 0;i < nb_ops; i++) { - if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) - goto modrm_found; - } -#ifdef ASM_DEBUG - tcc_error("bad op table"); -#endif - modrm_found: - modrm_index = i; - /* if a register is used in another operand then it is - used instead of group */ - for(i = 0;i < nb_ops; i++) { - v = op_type[i]; - if (i != modrm_index && - (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { - reg = ops[i].reg; - break; - } - } - - asm_modrm(reg, &ops[modrm_index]); - } - - /* emit constants */ -#ifndef TCC_TARGET_X86_64 - if (pa->opcode == 0x9a || pa->opcode == 0xea) { - /* ljmp or lcall kludge */ -#ifdef I386_ASM_16 - if (s1->seg_size == 16 && o32 == 0) - gen_expr16(&ops[1].e); - else -#endif - gen_expr32(&ops[1].e); - if (ops[0].e.sym) - tcc_error("cannot relocate"); - gen_le16(ops[0].e.v); - return; - } -#endif - for(i = 0;i < nb_ops; i++) { - v = op_type[i]; - if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) { - /* if multiple sizes are given it means we must look - at the op size */ - if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) { - if (s == 0) - v = OP_IM8; - else if (s == 1) - v = OP_IM16; - else if (s == 2 || (v & OP_IM64) == 0) - v = OP_IM32; - else - v = OP_IM64; - } - if (v & (OP_IM8 | OP_IM8S)) { - if (ops[i].e.sym) - goto error_relocate; - g(ops[i].e.v); - } else if (v & OP_IM16) { -#ifdef I386_ASM_16 - if (s1->seg_size == 16) - gen_expr16(&ops[i].e); - else -#endif - if (ops[i].e.sym) - error_relocate: - tcc_error("cannot relocate"); - else - gen_le16(ops[i].e.v); - } else { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - if (is_short_jmp) - g(ops[i].e.v); -#ifdef I386_ASM_16 - else if (s1->seg_size == 16) - gen_disp16(&ops[i].e); -#endif - else - gen_disp32(&ops[i].e); - } else { -#ifdef I386_ASM_16 - if (s1->seg_size == 16 && !((o32 == 1) && (v & OP_IM32))) - gen_expr16(&ops[i].e); - else -#endif -#ifdef TCC_TARGET_X86_64 - if (v & OP_IM64) - gen_expr64(&ops[i].e); - else -#endif - gen_expr32(&ops[i].e); - } - } -#ifdef I386_ASM_16 - } else if (v & (OP_REG16 | OP_REG32)) { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - /* jmp $r */ - g(0xE0 + ops[i].reg); - } -#endif -#ifdef TCC_TARGET_X86_64 - } else if (v & (OP_REG32 | OP_REG64)) { - if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { - /* jmp $r */ - g(0xE0 + ops[i].reg); - } -#endif - } - } -#ifdef I386_ASM_16 - a32 = o32 = 0; -#endif -} - -/* return the constraint priority (we allocate first the lowest - numbered constraints) */ -static inline int constraint_priority(const char *str) -{ - int priority, c, pr; - - /* we take the lowest priority */ - priority = 0; - for(;;) { - c = *str; - if (c == '\0') - break; - str++; - switch(c) { - case 'A': - pr = 0; - break; - case 'a': - case 'b': - case 'c': - case 'd': - case 'S': - case 'D': - pr = 1; - break; - case 'q': - pr = 2; - break; - case 'r': - pr = 3; - break; - case 'N': - case 'M': - case 'I': - case 'i': - case 'm': - case 'g': - pr = 4; - break; - default: - tcc_error("unknown constraint '%c'", c); - pr = 0; - } - if (pr > priority) - priority = pr; - } - return priority; -} - -static const char *skip_constraint_modifiers(const char *p) -{ - while (*p == '=' || *p == '&' || *p == '+' || *p == '%') - p++; - return p; -} - -#define REG_OUT_MASK 0x01 -#define REG_IN_MASK 0x02 - -#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) - -ST_FUNC void asm_compute_constraints(ASMOperand *operands, - int nb_operands, int nb_outputs, - const uint8_t *clobber_regs, - int *pout_reg) -{ - ASMOperand *op; - int sorted_op[MAX_ASM_OPERANDS]; - int i, j, k, p1, p2, tmp, reg, c, reg_mask; - const char *str; - uint8_t regs_allocated[NB_ASM_REGS]; - - /* init fields */ - for(i=0; iinput_index = -1; - op->ref_index = -1; - op->reg = -1; - op->is_memory = 0; - op->is_rw = 0; - } - /* compute constraint priority and evaluate references to output - constraints if input constraints */ - for(i=0; iconstraint; - str = skip_constraint_modifiers(str); - if (isnum(*str) || *str == '[') { - /* this is a reference to another constraint */ - k = find_constraint(operands, nb_operands, str, NULL); - if ((unsigned)k >= i || i < nb_outputs) - tcc_error("invalid reference in constraint %d ('%s')", - i, str); - op->ref_index = k; - if (operands[k].input_index >= 0) - tcc_error("cannot reference twice the same operand"); - operands[k].input_index = i; - op->priority = 5; - } else { - op->priority = constraint_priority(str); - } - } - - /* sort operands according to their priority */ - for(i=0;iconstraint; - /* no need to allocate references */ - if (op->ref_index >= 0) - continue; - /* select if register is used for output, input or both */ - if (op->input_index >= 0) { - reg_mask = REG_IN_MASK | REG_OUT_MASK; - } else if (j < nb_outputs) { - reg_mask = REG_OUT_MASK; - } else { - reg_mask = REG_IN_MASK; - } - try_next: - c = *str++; - switch(c) { - case '=': - goto try_next; - case '+': - op->is_rw = 1; - /* FALL THRU */ - case '&': - if (j >= nb_outputs) - tcc_error("'%c' modifier can only be applied to outputs", c); - reg_mask = REG_IN_MASK | REG_OUT_MASK; - goto try_next; - case 'A': - /* allocate both eax and edx */ - if (is_reg_allocated(TREG_XAX) || - is_reg_allocated(TREG_XDX)) - goto try_next; - op->is_llong = 1; - op->reg = TREG_XAX; - regs_allocated[TREG_XAX] |= reg_mask; - regs_allocated[TREG_XDX] |= reg_mask; - break; - case 'a': - reg = TREG_XAX; - goto alloc_reg; - case 'b': - reg = 3; - goto alloc_reg; - case 'c': - reg = TREG_XCX; - goto alloc_reg; - case 'd': - reg = TREG_XDX; - goto alloc_reg; - case 'S': - reg = 6; - goto alloc_reg; - case 'D': - reg = 7; - alloc_reg: - if (is_reg_allocated(reg)) - goto try_next; - goto reg_found; - case 'q': - /* eax, ebx, ecx or edx */ - for(reg = 0; reg < 4; reg++) { - if (!is_reg_allocated(reg)) - goto reg_found; - } - goto try_next; - case 'r': - /* any general register */ - for(reg = 0; reg < 8; reg++) { - if (!is_reg_allocated(reg)) - goto reg_found; - } - goto try_next; - reg_found: - /* now we can reload in the register */ - op->is_llong = 0; - op->reg = reg; - regs_allocated[reg] |= reg_mask; - break; - case 'i': - if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) - goto try_next; - break; - case 'I': - case 'N': - case 'M': - if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) - goto try_next; - break; - case 'm': - case 'g': - /* nothing special to do because the operand is already in - memory, except if the pointer itself is stored in a - memory variable (VT_LLOCAL case) */ - /* XXX: fix constant case */ - /* if it is a reference to a memory zone, it must lie - in a register, so we reserve the register in the - input registers and a load will be generated - later */ - if (j < nb_outputs || c == 'm') { - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { - /* any general register */ - for(reg = 0; reg < 8; reg++) { - if (!(regs_allocated[reg] & REG_IN_MASK)) - goto reg_found1; - } - goto try_next; - reg_found1: - /* now we can reload in the register */ - regs_allocated[reg] |= REG_IN_MASK; - op->reg = reg; - op->is_memory = 1; - } - } - break; - default: - tcc_error("asm constraint %d ('%s') could not be satisfied", - j, op->constraint); - break; - } - /* if a reference is present for that operand, we assign it too */ - if (op->input_index >= 0) { - operands[op->input_index].reg = op->reg; - operands[op->input_index].is_llong = op->is_llong; - } - } - - /* compute out_reg. It is used to store outputs registers to memory - locations references by pointers (VT_LLOCAL case) */ - *pout_reg = -1; - for(i=0;ireg >= 0 && - (op->vt->r & VT_VALMASK) == VT_LLOCAL && - !op->is_memory) { - for(reg = 0; reg < 8; reg++) { - if (!(regs_allocated[reg] & REG_OUT_MASK)) - goto reg_found2; - } - tcc_error("could not find free output register for reloading"); - reg_found2: - *pout_reg = reg; - break; - } - } - - /* print sorted constraints */ -#ifdef ASM_DEBUG - for(i=0;iid ? get_tok_str(op->id, NULL) : "", - op->constraint, - op->vt->r, - op->reg); - } - if (*pout_reg >= 0) - printf("out_reg=%d\n", *pout_reg); -#endif -} - -ST_FUNC void subst_asm_operand(CString *add_str, - SValue *sv, int modifier) -{ - int r, reg, size, val; - char buf[64]; - - r = sv->r; - if ((r & VT_VALMASK) == VT_CONST) { - if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') - cstr_ccat(add_str, '$'); - if (r & VT_SYM) { - cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); - if (sv->c.i != 0) { - cstr_ccat(add_str, '+'); - } else { - return; - } - } - val = sv->c.i; - if (modifier == 'n') - val = -val; - snprintf(buf, sizeof(buf), "%d", sv->c.i); - cstr_cat(add_str, buf); - } else if ((r & VT_VALMASK) == VT_LOCAL) { - snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); - cstr_cat(add_str, buf); - } else if (r & VT_LVAL) { - reg = r & VT_VALMASK; - if (reg >= VT_CONST) - tcc_error("internal compiler error"); - snprintf(buf, sizeof(buf), "(%%%s)", - get_tok_str(TOK_ASM_eax + reg, NULL)); - cstr_cat(add_str, buf); - } else { - /* register case */ - reg = r & VT_VALMASK; - if (reg >= VT_CONST) - tcc_error("internal compiler error"); - - /* choose register operand size */ - if ((sv->type.t & VT_BTYPE) == VT_BYTE) - size = 1; - else if ((sv->type.t & VT_BTYPE) == VT_SHORT) - size = 2; -#ifdef TCC_TARGET_X86_64 - else if ((sv->type.t & VT_BTYPE) == VT_LLONG) - size = 8; -#endif - else - size = 4; - if (size == 1 && reg >= 4) - size = 4; - - if (modifier == 'b') { - if (reg >= 4) - tcc_error("cannot use byte register"); - size = 1; - } else if (modifier == 'h') { - if (reg >= 4) - tcc_error("cannot use byte register"); - size = -1; - } else if (modifier == 'w') { - size = 2; -#ifdef TCC_TARGET_X86_64 - } else if (modifier == 'q') { - size = 8; -#endif - } - - switch(size) { - case -1: - reg = TOK_ASM_ah + reg; - break; - case 1: - reg = TOK_ASM_al + reg; - break; - case 2: - reg = TOK_ASM_ax + reg; - break; - default: - reg = TOK_ASM_eax + reg; - break; -#ifdef TCC_TARGET_X86_64 - case 8: - reg = TOK_ASM_rax + reg; - break; -#endif - } - snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); - cstr_cat(add_str, buf); - } -} - -/* generate prolog and epilog code for asm statement */ -ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, - int nb_outputs, int is_output, - uint8_t *clobber_regs, - int out_reg) -{ - uint8_t regs_allocated[NB_ASM_REGS]; - ASMOperand *op; - int i, reg; - static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; - - /* mark all used registers */ - memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); - for(i = 0; i < nb_operands;i++) { - op = &operands[i]; - if (op->reg >= 0) - regs_allocated[op->reg] = 1; - } - if (!is_output) { - /* generate reg save code */ - for(i = 0; i < NB_SAVED_REGS; i++) { - reg = reg_saved[i]; - if (regs_allocated[reg]) { -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) - g(0x66); -#endif - g(0x50 + reg); - } - } - - /* generate load code */ - for(i = 0; i < nb_operands; i++) { - op = &operands[i]; - if (op->reg >= 0) { - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && - op->is_memory) { - /* memory reference case (for both input and - output cases) */ - SValue sv; - sv = *op->vt; - sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; - load(op->reg, &sv); - } else if (i >= nb_outputs || op->is_rw) { - /* load value in register */ - load(op->reg, op->vt); - if (op->is_llong) { - SValue sv; - sv = *op->vt; - sv.c.ul += 4; - load(TREG_XDX, &sv); - } - } - } - } - } else { - /* generate save code */ - for(i = 0 ; i < nb_outputs; i++) { - op = &operands[i]; - if (op->reg >= 0) { - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { - if (!op->is_memory) { - SValue sv; - sv = *op->vt; - sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; - load(out_reg, &sv); - - sv.r = (sv.r & ~VT_VALMASK) | out_reg; - store(op->reg, &sv); - } - } else { - store(op->reg, op->vt); - if (op->is_llong) { - SValue sv; - sv = *op->vt; - sv.c.ul += 4; - store(TREG_XDX, &sv); - } - } - } - } - /* generate reg restore code */ - for(i = NB_SAVED_REGS - 1; i >= 0; i--) { - reg = reg_saved[i]; - if (regs_allocated[reg]) { -#ifdef I386_ASM_16 - if (tcc_state->seg_size == 16) - g(0x66); -#endif - g(0x58 + reg); - } - } - } -} - -ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) -{ - int reg; - TokenSym *ts; - - if (!strcmp(str, "memory") || - !strcmp(str, "cc")) - return; - ts = tok_alloc(str, strlen(str)); - reg = ts->tok; - if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { - reg -= TOK_ASM_eax; - } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { - reg -= TOK_ASM_ax; -#ifdef TCC_TARGET_X86_64 - } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) { - reg -= TOK_ASM_rax; -#endif - } else { - tcc_error("invalid clobber register '%s'", str); - } - clobber_regs[reg] = 1; -} \ No newline at end of file diff --git a/c67-gen.c b/c67-gen.c index 07ae259..a26dfaa 100644 --- a/c67-gen.c +++ b/c67-gen.c @@ -58,7 +58,7 @@ #define RC_IRET RC_C67_A4 /* function return: integer register */ #define RC_LRET RC_C67_A5 /* function return: second integer register */ #define RC_FRET RC_C67_A4 /* function return: float register */ -#define RC_MASK (RC_INT|RC_FLOAT) + /* pretty names for the registers */ enum { TREG_EAX = 0, // really A2 @@ -1571,21 +1571,12 @@ void load(int r, SValue * sv) v = fr & VT_VALMASK; if (fr & VT_LVAL) { - if(fr & VT_TMP){ - int size, align; - if((ft & VT_BTYPE) == VT_FUNC) - size = PTR_SIZE; - else - size = type_size(&sv->type, &align); - loc_stack(size, 0); - } if (v == VT_LLOCAL) { v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; v1.c.ul = fc; load(r, &v1); fr = r; - fc = 0; } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { tcc_error("long double not supported"); } else if ((ft & VT_TYPE) == VT_BYTE) { @@ -2111,7 +2102,7 @@ int gtst(int inv, int t) C67_NOP(5); t = ind1; //return where we need to patch - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -2137,37 +2128,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t)) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - // I think we need to get the value on the stack - // into a register, test it, and generate a branch - // return the address of the branch, so it can be - // later patched - - v = gv(RC_INT); // get value into a reg - ind1 = ind; - C67_MVKL(C67_A0, t); //r=reg to load, constant - C67_MVKH(C67_A0, t); //r=reg to load, constant - - if (v != TREG_EAX && // check if not already in a conditional test reg - v != TREG_EDX && v != TREG_ST0 && v != C67_B2) { - C67_MV(v, C67_B2); - v = C67_B2; - } - - C67_IREG_B_REG(inv, v, C67_A0); // [!R] B.S2x A0 - C67_NOP(5); - t = ind1; //return where we need to patch - ind1 = ind; - } } vtop--; return t; diff --git a/i386-asm.c b/i386-asm.c new file mode 100644 index 0000000..a524658 --- /dev/null +++ b/i386-asm.c @@ -0,0 +1,1502 @@ +/* + * i386 specific functions for TCC assembler + * + * Copyright (c) 2001, 2002 Fabrice Bellard + * Copyright (c) 2009 Frédéric Feret (x86_64 support) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tcc.h" + +/* #define NB_ASM_REGS 8 */ +#define MAX_OPERANDS 3 +#define NB_SAVED_REGS 3 + +#define TOK_ASM_first TOK_ASM_clc +#define TOK_ASM_last TOK_ASM_emms +#define TOK_ASM_alllast TOK_ASM_pxor + +#define OPC_JMP 0x01 /* jmp operand */ +#define OPC_B 0x02 /* only used with OPC_WL */ +#define OPC_WL 0x04 /* accepts w, l or no suffix */ +#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ +#define OPC_REG 0x08 /* register is added to opcode */ +#define OPC_MODRM 0x10 /* modrm encoding */ +#define OPC_FWAIT 0x20 /* add fwait opcode */ +#define OPC_TEST 0x40 /* test opcodes */ +#define OPC_SHIFT 0x80 /* shift opcodes */ +#define OPC_D16 0x0100 /* generate data16 prefix */ +#define OPC_ARITH 0x0200 /* arithmetic opcodes */ +#define OPC_SHORTJMP 0x0400 /* short jmp operand */ +#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ +#ifdef TCC_TARGET_X86_64 +# define OPC_WLQ 0x1000 /* accepts w, l, q or no suffix */ +# define OPC_BWLQ (OPC_B | OPC_WLQ) /* accepts b, w, l, q or no suffix */ +# define OPC_WLX OPC_WLQ +#else +# define OPC_WLX OPC_WL +#endif + +#define OPC_GROUP_SHIFT 13 + +/* in order to compress the operand type, we use specific operands and + we or only with EA */ +enum { + OPT_REG8=0, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_REG16, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_REG32, /* warning: value is hardcoded from TOK_ASM_xxx */ +#ifdef TCC_TARGET_X86_64 + OPT_REG64, /* warning: value is hardcoded from TOK_ASM_xxx */ +#endif + OPT_MMX, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_SSE, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_CR, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_TR, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_DB, /* warning: value is hardcoded from TOK_ASM_xxx */ + OPT_SEG, + OPT_ST, + OPT_IM8, + OPT_IM8S, + OPT_IM16, + OPT_IM32, +#ifdef TCC_TARGET_X86_64 + OPT_IM64, +#endif + OPT_EAX, /* %al, %ax, %eax or %rax register */ + OPT_ST0, /* %st(0) register */ + OPT_CL, /* %cl register */ + OPT_DX, /* %dx register */ + OPT_ADDR, /* OP_EA with only offset */ + OPT_INDIR, /* *(expr) */ + /* composite types */ + OPT_COMPOSITE_FIRST, + OPT_IM, /* IM8 | IM16 | IM32 | IM64 */ + OPT_REG, /* REG8 | REG16 | REG32 | REG64 */ + OPT_REGW, /* REG16 | REG32 | REG64 */ + OPT_IMW, /* IM16 | IM32 | IM64 */ +#ifdef TCC_TARGET_X86_64 + OPT_IMNO64, /* IM16 | IM32 */ +#endif + /* can be ored with any OPT_xxx */ + OPT_EA = 0x80 +}; + +#define OP_REG8 (1 << OPT_REG8) +#define OP_REG16 (1 << OPT_REG16) +#define OP_REG32 (1 << OPT_REG32) +#define OP_MMX (1 << OPT_MMX) +#define OP_SSE (1 << OPT_SSE) +#define OP_CR (1 << OPT_CR) +#define OP_TR (1 << OPT_TR) +#define OP_DB (1 << OPT_DB) +#define OP_SEG (1 << OPT_SEG) +#define OP_ST (1 << OPT_ST) +#define OP_IM8 (1 << OPT_IM8) +#define OP_IM8S (1 << OPT_IM8S) +#define OP_IM16 (1 << OPT_IM16) +#define OP_IM32 (1 << OPT_IM32) +#define OP_EAX (1 << OPT_EAX) +#define OP_ST0 (1 << OPT_ST0) +#define OP_CL (1 << OPT_CL) +#define OP_DX (1 << OPT_DX) +#define OP_ADDR (1 << OPT_ADDR) +#define OP_INDIR (1 << OPT_INDIR) +#ifdef TCC_TARGET_X86_64 +# define OP_REG64 (1 << OPT_REG64) +# define OP_IM64 (1 << OPT_IM64) +#else +# define OP_REG64 0 +# define OP_IM64 0 +#endif + +#define OP_EA 0x40000000 +#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64) + +#ifdef TCC_TARGET_X86_64 +# define OP_IM OP_IM64 +# define TREG_XAX TREG_RAX +# define TREG_XCX TREG_RCX +# define TREG_XDX TREG_RDX +#else +# define OP_IM OP_IM32 +# define TREG_XAX TREG_EAX +# define TREG_XCX TREG_ECX +# define TREG_XDX TREG_EDX +#endif + +typedef struct ASMInstr { + uint16_t sym; + uint16_t opcode; + uint16_t instr_type; + uint8_t nb_ops; + uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ +} ASMInstr; + +typedef struct Operand { + uint32_t type; + int8_t reg; /* register, -1 if none */ + int8_t reg2; /* second register, -1 if none */ + uint8_t shift; + ExprValue e; +} Operand; + +static const uint8_t reg_to_size[9] = { +/* + [OP_REG8] = 0, + [OP_REG16] = 1, + [OP_REG32] = 2, +#ifdef TCC_TARGET_X86_64 + [OP_REG64] = 3, +#endif +*/ + 0, 0, 1, 0, 2, 0, 0, 0, 3 +}; + +#define NB_TEST_OPCODES 30 + +static const uint8_t test_bits[NB_TEST_OPCODES] = { + 0x00, /* o */ + 0x01, /* no */ + 0x02, /* b */ + 0x02, /* c */ + 0x02, /* nae */ + 0x03, /* nb */ + 0x03, /* nc */ + 0x03, /* ae */ + 0x04, /* e */ + 0x04, /* z */ + 0x05, /* ne */ + 0x05, /* nz */ + 0x06, /* be */ + 0x06, /* na */ + 0x07, /* nbe */ + 0x07, /* a */ + 0x08, /* s */ + 0x09, /* ns */ + 0x0a, /* p */ + 0x0a, /* pe */ + 0x0b, /* np */ + 0x0b, /* po */ + 0x0c, /* l */ + 0x0c, /* nge */ + 0x0d, /* nl */ + 0x0d, /* ge */ + 0x0e, /* le */ + 0x0e, /* ng */ + 0x0f, /* nle */ + 0x0f, /* g */ +}; + +static const uint8_t segment_prefixes[] = { + 0x26, /* es */ + 0x2e, /* cs */ + 0x36, /* ss */ + 0x3e, /* ds */ + 0x64, /* fs */ + 0x65 /* gs */ +}; + +static const ASMInstr asm_instrs[] = { +#define ALT(x) x +#define DEF_ASM_OP0(name, opcode) +#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }}, +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif + /* last operation */ + { 0, }, +}; + +static const uint16_t op0_codes[] = { +#define ALT(x) +#define DEF_ASM_OP0(x, opcode) opcode, +#define DEF_ASM_OP0L(name, opcode, group, instr_type) +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif +}; + +static inline int get_reg_shift(TCCState *s1) +{ + int shift, v; +#ifdef I386_ASM_16 + if (s1->seg_size == 16) + tcc_error("invalid effective address"); +#endif + v = asm_int_expr(s1); + switch(v) { + case 1: + shift = 0; + break; + case 2: + shift = 1; + break; + case 4: + shift = 2; + break; + case 8: + shift = 3; + break; + default: + expect("1, 2, 4 or 8 constant"); + shift = 0; + break; + } + return shift; +} + +static int asm_parse_reg(void) +{ + int reg = 0; + if (tok != '%') + goto error_32; + next(); + if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { + reg = tok - TOK_ASM_eax; +#ifdef TCC_TARGET_X86_64 + } else if (tok >= TOK_ASM_rax && tok <= TOK_ASM_rdi) { + reg = tok - TOK_ASM_rax; +#endif +#ifdef I386_ASM_16 + } else if (tok >= TOK_ASM_ax && tok <= TOK_ASM_di) { + reg = tok - TOK_ASM_ax; +#endif + } else { + error_32: + expect("register"); + } + next(); + return reg; +} + +static void parse_operand(TCCState *s1, Operand *op) +{ + ExprValue e; + int reg, indir; + const char *p; + + indir = 0; + if (tok == '*') { + next(); + indir = OP_INDIR; + } + + if (tok == '%') { + next(); + if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { + reg = tok - TOK_ASM_al; + op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ + op->reg = reg & 7; + if ((op->type & OP_REG) && op->reg == TREG_XAX) + op->type |= OP_EAX; + else if (op->type == OP_REG8 && op->reg == TREG_XCX) + op->type |= OP_CL; + else if (op->type == OP_REG16 && op->reg == TREG_XDX) + op->type |= OP_DX; + } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { + op->type = OP_DB; + op->reg = tok - TOK_ASM_dr0; + } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { + op->type = OP_SEG; + op->reg = tok - TOK_ASM_es; + } else if (tok == TOK_ASM_st) { + op->type = OP_ST; + op->reg = 0; + next(); + if (tok == '(') { + next(); + if (tok != TOK_PPNUM) + goto reg_error; + p = tokc.cstr->data; + reg = p[0] - '0'; + if ((unsigned)reg >= 8 || p[1] != '\0') + goto reg_error; + op->reg = reg; + next(); + skip(')'); + } + if (op->reg == 0) + op->type |= OP_ST0; + goto no_skip; + } else { + reg_error: + tcc_error("unknown register"); + } + next(); + no_skip: ; + } else if (tok == '$') { + /* constant value */ + next(); + asm_expr(s1, &e); + op->type = OP_IM; + op->e.v = e.v; + op->e.sym = e.sym; + if (!op->e.sym) { + if (op->e.v == (uint8_t)op->e.v) + op->type |= OP_IM8; + if (op->e.v == (int8_t)op->e.v) + op->type |= OP_IM8S; + if (op->e.v == (uint16_t)op->e.v) + op->type |= OP_IM16; +#ifdef TCC_TARGET_X86_64 + if (op->e.v == (uint32_t)op->e.v) + op->type |= OP_IM32; +#endif + } + } else { + /* address(reg,reg2,shift) with all variants */ + op->type = OP_EA; + op->reg = -1; + op->reg2 = -1; + op->shift = 0; + if (tok != '(') { + asm_expr(s1, &e); + op->e.v = e.v; + op->e.sym = e.sym; + } else { + next(); + if (tok == '%') { + unget_tok('('); + op->e.v = 0; + op->e.sym = NULL; + } else { + /* bracketed offset expression */ + asm_expr(s1, &e); + if (tok != ')') + expect(")"); + next(); + op->e.v = e.v; + op->e.sym = e.sym; + } + } + if (tok == '(') { + next(); + if (tok != ',') { + op->reg = asm_parse_reg(); + } + if (tok == ',') { + next(); + if (tok != ',') { + op->reg2 = asm_parse_reg(); + } + if (tok == ',') { + next(); + op->shift = get_reg_shift(s1); + } + } + skip(')'); + } + if (op->reg == -1 && op->reg2 == -1) + op->type |= OP_ADDR; + } + op->type |= indir; +} + +/* XXX: unify with C code output ? */ +ST_FUNC void gen_expr32(ExprValue *pe) +{ + gen_addr32(pe->sym ? VT_SYM : 0, pe->sym, pe->v); +} + +#ifdef TCC_TARGET_X86_64 +static void gen_expr64(ExprValue *pe) +{ + gen_addr64(pe->sym ? VT_SYM : 0, pe->sym, pe->v); +} +#endif + +/* XXX: unify with C code output ? */ +static void gen_disp32(ExprValue *pe) +{ + Sym *sym = pe->sym; + if (sym && sym->r == cur_text_section->sh_num) { + /* same section: we can output an absolute value. Note + that the TCC compiler behaves differently here because + it always outputs a relocation to ease (future) code + elimination in the linker */ + gen_le32(pe->v + sym->jnext - ind - 4); + } else { + if (sym && sym->type.t == VT_VOID) { + sym->type.t = VT_FUNC; + sym->type.ref = NULL; + } + gen_addrpc32(VT_SYM, sym, pe->v); + } +} + +#ifdef I386_ASM_16 +static void gen_expr16(ExprValue *pe) +{ + if (pe->sym) + greloc(cur_text_section, pe->sym, ind, R_386_16); + gen_le16(pe->v); +} +static void gen_disp16(ExprValue *pe) +{ + Sym *sym; + sym = pe->sym; + if (sym) { + if (sym->r == cur_text_section->sh_num) { + /* same section: we can output an absolute value. Note + that the TCC compiler behaves differently here because + it always outputs a relocation to ease (future) code + elimination in the linker */ + gen_le16(pe->v + sym->jnext - ind - 2); + } else { + greloc(cur_text_section, sym, ind, R_386_PC16); + gen_le16(pe->v - 2); + } + } else { + /* put an empty PC32 relocation */ + put_elf_reloc(symtab_section, cur_text_section, + ind, R_386_PC16, 0); + gen_le16(pe->v - 2); + } +} +#endif + +/* generate the modrm operand */ +static inline void asm_modrm(int reg, Operand *op) +{ + int mod, reg1, reg2, sib_reg1; + + if (op->type & (OP_REG | OP_MMX | OP_SSE)) { + g(0xc0 + (reg << 3) + op->reg); + } else if (op->reg == -1 && op->reg2 == -1) { + /* displacement only */ +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) { + g(0x06 + (reg << 3)); + gen_expr16(&op->e); + } else if (tcc_state->seg_size == 32) +#endif + { + g(0x05 + (reg << 3)); + gen_expr32(&op->e); + } + } else { + sib_reg1 = op->reg; + /* fist compute displacement encoding */ + if (sib_reg1 == -1) { + sib_reg1 = 5; + mod = 0x00; + } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { + mod = 0x00; + } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { + mod = 0x40; + } else { + mod = 0x80; + } + /* compute if sib byte needed */ + reg1 = op->reg; + if (op->reg2 != -1) + reg1 = 4; +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 32) { +#endif + g(mod + (reg << 3) + reg1); + if (reg1 == 4) { + /* add sib byte */ + reg2 = op->reg2; + if (reg2 == -1) + reg2 = 4; /* indicate no index */ + g((op->shift << 6) + (reg2 << 3) + sib_reg1); + } +#ifdef I386_ASM_16 + } else if (tcc_state->seg_size == 16) { + /* edi = 7, esi = 6 --> di = 5, si = 4 */ + if ((reg1 == 6) || (reg1 == 7)) { + reg1 -= 2; + /* ebx = 3 --> bx = 7 */ + } else if (reg1 == 3) { + reg1 = 7; + /* o32 = 5 --> o16 = 6 */ + } else if (reg1 == 5) { + reg1 = 6; + /* sib not valid in 16-bit mode */ + } else if (reg1 == 4) { + reg2 = op->reg2; + /* bp + si + offset */ + if ((sib_reg1 == 5) && (reg2 == 6)) { + reg1 = 2; + /* bp + di + offset */ + } else if ((sib_reg1 == 5) && (reg2 == 7)) { + reg1 = 3; + /* bx + si + offset */ + } else if ((sib_reg1 == 3) && (reg2 == 6)) { + reg1 = 0; + /* bx + di + offset */ + } else if ((sib_reg1 == 3) && (reg2 == 7)) { + reg1 = 1; + } else { + tcc_error("invalid effective address"); + } + if (op->e.v == 0) + mod = 0; + } else { + tcc_error("invalid register"); + } + g(mod + (reg << 3) + reg1); + } +#endif + /* add offset */ + if (mod == 0x40) { + g(op->e.v); + } else if (mod == 0x80 || op->reg == -1) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + gen_expr16(&op->e); + else if (tcc_state->seg_size == 32) +#endif + gen_expr32(&op->e); + } + } +} + +ST_FUNC void asm_opcode(TCCState *s1, int opcode) +{ + const ASMInstr *pa; + int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix; + int nb_ops, s; + Operand ops[MAX_OPERANDS], *pop; + int op_type[3]; /* decoded op type */ +#ifdef I386_ASM_16 + static int a32 = 0, o32 = 0, addr32 = 0, data32 = 0; +#endif + + /* force synthetic ';' after prefix instruction, so we can handle */ + /* one-line things like "rep stosb" instead of only "rep\nstosb" */ + if (opcode >= TOK_ASM_wait && opcode <= TOK_ASM_repnz) + unget_tok(';'); + + /* get operands */ + pop = ops; + nb_ops = 0; + seg_prefix = 0; + for(;;) { + if (tok == ';' || tok == TOK_LINEFEED) + break; + if (nb_ops >= MAX_OPERANDS) { + tcc_error("incorrect number of operands"); + } + parse_operand(s1, pop); + if (tok == ':') { + if (pop->type != OP_SEG || seg_prefix) + tcc_error("incorrect prefix"); + seg_prefix = segment_prefixes[pop->reg]; + next(); + parse_operand(s1, pop); +#ifndef I386_ASM_16 + if (!(pop->type & OP_EA)) { + tcc_error("segment prefix must be followed by memory reference"); + } +#endif + } + pop++; + nb_ops++; + if (tok != ',') + break; + next(); + } + + is_short_jmp = 0; + s = 0; /* avoid warning */ + + /* optimize matching by using a lookup table (no hashing is needed + !) */ + for(pa = asm_instrs; pa->sym != 0; pa++) { + s = 0; + if (pa->instr_type & OPC_FARITH) { + v = opcode - pa->sym; + if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) + continue; + } else if (pa->instr_type & OPC_ARITH) { + if (!(opcode >= pa->sym && opcode < pa->sym + 8*NBWLX)) + continue; + s = (opcode - pa->sym) % NBWLX; + } else if (pa->instr_type & OPC_SHIFT) { + if (!(opcode >= pa->sym && opcode < pa->sym + 7*NBWLX)) + continue; + s = (opcode - pa->sym) % NBWLX; + } else if (pa->instr_type & OPC_TEST) { + if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) + continue; + } else if (pa->instr_type & OPC_B) { + if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX)) + continue; + s = opcode - pa->sym; + } else if (pa->instr_type & OPC_WLX) { + if (!(opcode >= pa->sym && opcode < pa->sym + NBWLX-1)) + continue; + s = opcode - pa->sym + 1; + } else { + if (pa->sym != opcode) + continue; + } + if (pa->nb_ops != nb_ops) + continue; + /* now decode and check each operand */ + for(i = 0; i < nb_ops; i++) { + int op1, op2; + op1 = pa->op_type[i]; + op2 = op1 & 0x1f; + switch(op2) { + case OPT_IM: + v = OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64; + break; + case OPT_REG: + v = OP_REG8 | OP_REG16 | OP_REG32 | OP_REG64; + break; + case OPT_REGW: + v = OP_REG16 | OP_REG32 | OP_REG64; + break; + case OPT_IMW: + v = OP_IM16 | OP_IM32 | OP_IM64; + break; +#ifdef TCC_TARGET_X86_64 + case OPT_IMNO64: + v = OP_IM16 | OP_IM32; + break; +#endif + default: + v = 1 << op2; + break; + } + if (op1 & OPT_EA) + v |= OP_EA; + op_type[i] = v; + if ((ops[i].type & v) == 0) + goto next; + } + /* all is matching ! */ + break; + next: ; + } + if (pa->sym == 0) { + if (opcode >= TOK_ASM_first && opcode <= TOK_ASM_last) { + int b; + b = op0_codes[opcode - TOK_ASM_first]; +#ifdef I386_ASM_16 + if (opcode == TOK_ASM_o32) { + if (s1->seg_size == 32) + tcc_error("incorrect prefix"); + else + o32 = data32 = 1; + } else if (opcode == TOK_ASM_a32) { + if (s1->seg_size == 32) + tcc_error("incorrect prefix"); + else + a32 = addr32 = 1; + } +#endif + if (b & 0xff00) + g(b >> 8); + g(b); + return; + } else if (opcode <= TOK_ASM_alllast) { + tcc_error("bad operand with opcode '%s'", + get_tok_str(opcode, NULL)); + } else { + tcc_error("unknown opcode '%s'", + get_tok_str(opcode, NULL)); + } + } + /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ + if (s == NBWLX-1) { + for(i = 0; s == NBWLX-1 && i < nb_ops; i++) { + if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) + s = reg_to_size[ops[i].type & OP_REG]; + } + if (s == NBWLX-1) { + if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && + (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32 | OP_IM64))) + s = 2; + else + tcc_error("cannot infer opcode suffix"); + } + } + +#ifdef I386_ASM_16 + for(i = 0; i < nb_ops; i++) { + if (ops[i].type & OP_REG32) { + if (s1->seg_size == 16) + o32 = 1; + } else if (!(ops[i].type & OP_REG32)) { + if (s1->seg_size == 32) + o32 = 1; + } + } + + + if (s == 1 || (pa->instr_type & OPC_D16)) { + if (s1->seg_size == 32) + o32 = 1; + } else if (s == 2) { + if (s1->seg_size == 16) { + if (!(pa->instr_type & OPC_D16)) + o32 = 1; + } + } + + /* generate a16/a32 prefix if needed */ + if ((a32 == 1) && (addr32 == 0)) + g(0x67); + /* generate o16/o32 prefix if needed */ + if ((o32 == 1) && (data32 == 0)) + g(0x66); + + addr32 = data32 = 0; +#else + /* generate data16 prefix if needed */ + if (s == 1 || (pa->instr_type & OPC_D16)) + g(0x66); +#ifdef TCC_TARGET_X86_64 + else if (s == 3) { + /* generate REX prefix */ + if ((opcode != TOK_ASM_push && opcode != TOK_ASM_pop) + || !(ops[0].type & OP_REG64)) + g(0x48); + } +#endif +#endif + + /* now generates the operation */ + if (pa->instr_type & OPC_FWAIT) + g(0x9b); + if (seg_prefix) + g(seg_prefix); + + v = pa->opcode; + if ((v == 0x69 || v == 0x6b) && nb_ops == 2) { + /* kludge for imul $im, %reg */ + nb_ops = 3; + ops[2] = ops[1]; + op_type[2] = op_type[1]; + } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { + v--; /* int $3 case */ + nb_ops = 0; + } else if ((v == 0x06 || v == 0x07)) { + if (ops[0].reg >= 4) { + /* push/pop %fs or %gs */ + v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); + } else { + v += ops[0].reg << 3; + } + nb_ops = 0; + } else if (v <= 0x05) { + /* arith case */ + v += ((opcode - TOK_ASM_addb) / NBWLX) << 3; + } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { + /* fpu arith case */ + v += ((opcode - pa->sym) / 6) << 3; + } + if (pa->instr_type & OPC_REG) { + for(i = 0; i < nb_ops; i++) { + if (op_type[i] & (OP_REG | OP_ST)) { + v += ops[i].reg; + break; + } + } + /* mov $im, %reg case */ + if (pa->opcode == 0xb0 && s >= 1) + v += 7; + } + if (pa->instr_type & OPC_B) + v += s >= 1; + if (pa->instr_type & OPC_TEST) + v += test_bits[opcode - pa->sym]; + if (pa->instr_type & OPC_SHORTJMP) { + Sym *sym; + int jmp_disp; + + /* see if we can really generate the jump with a byte offset */ + sym = ops[0].e.sym; + if (!sym) + goto no_short_jump; + if (sym->r != cur_text_section->sh_num) + goto no_short_jump; + jmp_disp = ops[0].e.v + sym->jnext - ind - 2; + if (jmp_disp == (int8_t)jmp_disp) { + /* OK to generate jump */ + is_short_jmp = 1; + ops[0].e.v = jmp_disp; + } else { + no_short_jump: + if (pa->instr_type & OPC_JMP) { + /* long jump will be allowed. need to modify the + opcode slightly */ + if (v == 0xeb) + v = 0xe9; + else + v += 0x0f10; + } else { + tcc_error("invalid displacement"); + } + } + } + op1 = v >> 8; + if (op1) + g(op1); + g(v); + + /* search which operand will used for modrm */ + modrm_index = 0; + if (pa->instr_type & OPC_SHIFT) { + reg = (opcode - pa->sym) / NBWLX; + if (reg == 6) + reg = 7; + } else if (pa->instr_type & OPC_ARITH) { + reg = (opcode - pa->sym) / NBWLX; + } else if (pa->instr_type & OPC_FARITH) { + reg = (opcode - pa->sym) / 6; + } else { + reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; + } + if (pa->instr_type & OPC_MODRM) { + /* first look for an ea operand */ + for(i = 0;i < nb_ops; i++) { + if (op_type[i] & OP_EA) + goto modrm_found; + } + /* then if not found, a register or indirection (shift instructions) */ + for(i = 0;i < nb_ops; i++) { + if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) + goto modrm_found; + } +#ifdef ASM_DEBUG + tcc_error("bad op table"); +#endif + modrm_found: + modrm_index = i; + /* if a register is used in another operand then it is + used instead of group */ + for(i = 0;i < nb_ops; i++) { + v = op_type[i]; + if (i != modrm_index && + (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { + reg = ops[i].reg; + break; + } + } + + asm_modrm(reg, &ops[modrm_index]); + } + + /* emit constants */ +#ifndef TCC_TARGET_X86_64 + if (pa->opcode == 0x9a || pa->opcode == 0xea) { + /* ljmp or lcall kludge */ +#ifdef I386_ASM_16 + if (s1->seg_size == 16 && o32 == 0) + gen_expr16(&ops[1].e); + else +#endif + gen_expr32(&ops[1].e); + if (ops[0].e.sym) + tcc_error("cannot relocate"); + gen_le16(ops[0].e.v); + return; + } +#endif + for(i = 0;i < nb_ops; i++) { + v = op_type[i]; + if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64 | OP_IM8S | OP_ADDR)) { + /* if multiple sizes are given it means we must look + at the op size */ + if ((v | OP_IM8 | OP_IM64) == (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM64)) { + if (s == 0) + v = OP_IM8; + else if (s == 1) + v = OP_IM16; + else if (s == 2 || (v & OP_IM64) == 0) + v = OP_IM32; + else + v = OP_IM64; + } + if (v & (OP_IM8 | OP_IM8S)) { + if (ops[i].e.sym) + goto error_relocate; + g(ops[i].e.v); + } else if (v & OP_IM16) { +#ifdef I386_ASM_16 + if (s1->seg_size == 16) + gen_expr16(&ops[i].e); + else +#endif + if (ops[i].e.sym) + error_relocate: + tcc_error("cannot relocate"); + else + gen_le16(ops[i].e.v); + } else { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + if (is_short_jmp) + g(ops[i].e.v); +#ifdef I386_ASM_16 + else if (s1->seg_size == 16) + gen_disp16(&ops[i].e); +#endif + else + gen_disp32(&ops[i].e); + } else { +#ifdef I386_ASM_16 + if (s1->seg_size == 16 && !((o32 == 1) && (v & OP_IM32))) + gen_expr16(&ops[i].e); + else +#endif +#ifdef TCC_TARGET_X86_64 + if (v & OP_IM64) + gen_expr64(&ops[i].e); + else +#endif + gen_expr32(&ops[i].e); + } + } +#ifdef I386_ASM_16 + } else if (v & (OP_REG16 | OP_REG32)) { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + /* jmp $r */ + g(0xE0 + ops[i].reg); + } +#endif +#ifdef TCC_TARGET_X86_64 + } else if (v & (OP_REG32 | OP_REG64)) { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + /* jmp $r */ + g(0xE0 + ops[i].reg); + } +#endif + } + } +#ifdef I386_ASM_16 + a32 = o32 = 0; +#endif +} + +/* return the constraint priority (we allocate first the lowest + numbered constraints) */ +static inline int constraint_priority(const char *str) +{ + int priority, c, pr; + + /* we take the lowest priority */ + priority = 0; + for(;;) { + c = *str; + if (c == '\0') + break; + str++; + switch(c) { + case 'A': + pr = 0; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'S': + case 'D': + pr = 1; + break; + case 'q': + pr = 2; + break; + case 'r': + pr = 3; + break; + case 'N': + case 'M': + case 'I': + case 'i': + case 'm': + case 'g': + pr = 4; + break; + default: + tcc_error("unknown constraint '%c'", c); + pr = 0; + } + if (pr > priority) + priority = pr; + } + return priority; +} + +static const char *skip_constraint_modifiers(const char *p) +{ + while (*p == '=' || *p == '&' || *p == '+' || *p == '%') + p++; + return p; +} + +#define REG_OUT_MASK 0x01 +#define REG_IN_MASK 0x02 + +#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) + +ST_FUNC void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) +{ + ASMOperand *op; + int sorted_op[MAX_ASM_OPERANDS]; + int i, j, k, p1, p2, tmp, reg, c, reg_mask; + const char *str; + uint8_t regs_allocated[NB_ASM_REGS]; + + /* init fields */ + for(i=0;iinput_index = -1; + op->ref_index = -1; + op->reg = -1; + op->is_memory = 0; + op->is_rw = 0; + } + /* compute constraint priority and evaluate references to output + constraints if input constraints */ + for(i=0;iconstraint; + str = skip_constraint_modifiers(str); + if (isnum(*str) || *str == '[') { + /* this is a reference to another constraint */ + k = find_constraint(operands, nb_operands, str, NULL); + if ((unsigned)k >= i || i < nb_outputs) + tcc_error("invalid reference in constraint %d ('%s')", + i, str); + op->ref_index = k; + if (operands[k].input_index >= 0) + tcc_error("cannot reference twice the same operand"); + operands[k].input_index = i; + op->priority = 5; + } else { + op->priority = constraint_priority(str); + } + } + + /* sort operands according to their priority */ + for(i=0;iconstraint; + /* no need to allocate references */ + if (op->ref_index >= 0) + continue; + /* select if register is used for output, input or both */ + if (op->input_index >= 0) { + reg_mask = REG_IN_MASK | REG_OUT_MASK; + } else if (j < nb_outputs) { + reg_mask = REG_OUT_MASK; + } else { + reg_mask = REG_IN_MASK; + } + try_next: + c = *str++; + switch(c) { + case '=': + goto try_next; + case '+': + op->is_rw = 1; + /* FALL THRU */ + case '&': + if (j >= nb_outputs) + tcc_error("'%c' modifier can only be applied to outputs", c); + reg_mask = REG_IN_MASK | REG_OUT_MASK; + goto try_next; + case 'A': + /* allocate both eax and edx */ + if (is_reg_allocated(TREG_XAX) || + is_reg_allocated(TREG_XDX)) + goto try_next; + op->is_llong = 1; + op->reg = TREG_XAX; + regs_allocated[TREG_XAX] |= reg_mask; + regs_allocated[TREG_XDX] |= reg_mask; + break; + case 'a': + reg = TREG_XAX; + goto alloc_reg; + case 'b': + reg = 3; + goto alloc_reg; + case 'c': + reg = TREG_XCX; + goto alloc_reg; + case 'd': + reg = TREG_XDX; + goto alloc_reg; + case 'S': + reg = 6; + goto alloc_reg; + case 'D': + reg = 7; + alloc_reg: + if (is_reg_allocated(reg)) + goto try_next; + goto reg_found; + case 'q': + /* eax, ebx, ecx or edx */ + for(reg = 0; reg < 4; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + case 'r': + /* any general register */ + for(reg = 0; reg < 8; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + reg_found: + /* now we can reload in the register */ + op->is_llong = 0; + op->reg = reg; + regs_allocated[reg] |= reg_mask; + break; + case 'i': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) + goto try_next; + break; + case 'I': + case 'N': + case 'M': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + break; + case 'm': + case 'g': + /* nothing special to do because the operand is already in + memory, except if the pointer itself is stored in a + memory variable (VT_LLOCAL case) */ + /* XXX: fix constant case */ + /* if it is a reference to a memory zone, it must lie + in a register, so we reserve the register in the + input registers and a load will be generated + later */ + if (j < nb_outputs || c == 'm') { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + /* any general register */ + for(reg = 0; reg < 8; reg++) { + if (!(regs_allocated[reg] & REG_IN_MASK)) + goto reg_found1; + } + goto try_next; + reg_found1: + /* now we can reload in the register */ + regs_allocated[reg] |= REG_IN_MASK; + op->reg = reg; + op->is_memory = 1; + } + } + break; + default: + tcc_error("asm constraint %d ('%s') could not be satisfied", + j, op->constraint); + break; + } + /* if a reference is present for that operand, we assign it too */ + if (op->input_index >= 0) { + operands[op->input_index].reg = op->reg; + operands[op->input_index].is_llong = op->is_llong; + } + } + + /* compute out_reg. It is used to store outputs registers to memory + locations references by pointers (VT_LLOCAL case) */ + *pout_reg = -1; + for(i=0;ireg >= 0 && + (op->vt->r & VT_VALMASK) == VT_LLOCAL && + !op->is_memory) { + for(reg = 0; reg < 8; reg++) { + if (!(regs_allocated[reg] & REG_OUT_MASK)) + goto reg_found2; + } + tcc_error("could not find free output register for reloading"); + reg_found2: + *pout_reg = reg; + break; + } + } + + /* print sorted constraints */ +#ifdef ASM_DEBUG + for(i=0;iid ? get_tok_str(op->id, NULL) : "", + op->constraint, + op->vt->r, + op->reg); + } + if (*pout_reg >= 0) + printf("out_reg=%d\n", *pout_reg); +#endif +} + +ST_FUNC void subst_asm_operand(CString *add_str, + SValue *sv, int modifier) +{ + int r, reg, size, val; + char buf[64]; + + r = sv->r; + if ((r & VT_VALMASK) == VT_CONST) { + if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') + cstr_ccat(add_str, '$'); + if (r & VT_SYM) { + cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); + if (sv->c.i != 0) { + cstr_ccat(add_str, '+'); + } else { + return; + } + } + val = sv->c.i; + if (modifier == 'n') + val = -val; + snprintf(buf, sizeof(buf), "%d", sv->c.i); + cstr_cat(add_str, buf); + } else if ((r & VT_VALMASK) == VT_LOCAL) { + snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); + cstr_cat(add_str, buf); + } else if (r & VT_LVAL) { + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + tcc_error("internal compiler error"); + snprintf(buf, sizeof(buf), "(%%%s)", + get_tok_str(TOK_ASM_eax + reg, NULL)); + cstr_cat(add_str, buf); + } else { + /* register case */ + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + tcc_error("internal compiler error"); + + /* choose register operand size */ + if ((sv->type.t & VT_BTYPE) == VT_BYTE) + size = 1; + else if ((sv->type.t & VT_BTYPE) == VT_SHORT) + size = 2; +#ifdef TCC_TARGET_X86_64 + else if ((sv->type.t & VT_BTYPE) == VT_LLONG) + size = 8; +#endif + else + size = 4; + if (size == 1 && reg >= 4) + size = 4; + + if (modifier == 'b') { + if (reg >= 4) + tcc_error("cannot use byte register"); + size = 1; + } else if (modifier == 'h') { + if (reg >= 4) + tcc_error("cannot use byte register"); + size = -1; + } else if (modifier == 'w') { + size = 2; +#ifdef TCC_TARGET_X86_64 + } else if (modifier == 'q') { + size = 8; +#endif + } + + switch(size) { + case -1: + reg = TOK_ASM_ah + reg; + break; + case 1: + reg = TOK_ASM_al + reg; + break; + case 2: + reg = TOK_ASM_ax + reg; + break; + default: + reg = TOK_ASM_eax + reg; + break; +#ifdef TCC_TARGET_X86_64 + case 8: + reg = TOK_ASM_rax + reg; + break; +#endif + } + snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); + cstr_cat(add_str, buf); + } +} + +/* generate prolog and epilog code for asm statement */ +ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) +{ + uint8_t regs_allocated[NB_ASM_REGS]; + ASMOperand *op; + int i, reg; + static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; + + /* mark all used registers */ + memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); + for(i = 0; i < nb_operands;i++) { + op = &operands[i]; + if (op->reg >= 0) + regs_allocated[op->reg] = 1; + } + if (!is_output) { + /* generate reg save code */ + for(i = 0; i < NB_SAVED_REGS; i++) { + reg = reg_saved[i]; + if (regs_allocated[reg]) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + g(0x66); +#endif + g(0x50 + reg); + } + } + + /* generate load code */ + for(i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && + op->is_memory) { + /* memory reference case (for both input and + output cases) */ + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + load(op->reg, &sv); + } else if (i >= nb_outputs || op->is_rw) { + /* load value in register */ + load(op->reg, op->vt); + if (op->is_llong) { + SValue sv; + sv = *op->vt; + sv.c.ul += 4; + load(TREG_XDX, &sv); + } + } + } + } + } else { + /* generate save code */ + for(i = 0 ; i < nb_outputs; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + if (!op->is_memory) { + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + load(out_reg, &sv); + + sv.r = (sv.r & ~VT_VALMASK) | out_reg; + store(op->reg, &sv); + } + } else { + store(op->reg, op->vt); + if (op->is_llong) { + SValue sv; + sv = *op->vt; + sv.c.ul += 4; + store(TREG_XDX, &sv); + } + } + } + } + /* generate reg restore code */ + for(i = NB_SAVED_REGS - 1; i >= 0; i--) { + reg = reg_saved[i]; + if (regs_allocated[reg]) { +#ifdef I386_ASM_16 + if (tcc_state->seg_size == 16) + g(0x66); +#endif + g(0x58 + reg); + } + } + } +} + +ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) +{ + int reg; + TokenSym *ts; + + if (!strcmp(str, "memory") || + !strcmp(str, "cc")) + return; + ts = tok_alloc(str, strlen(str)); + reg = ts->tok; + if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { + reg -= TOK_ASM_eax; + } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { + reg -= TOK_ASM_ax; +#ifdef TCC_TARGET_X86_64 + } else if (reg >= TOK_ASM_rax && reg <= TOK_ASM_rdi) { + reg -= TOK_ASM_rax; +#endif + } else { + tcc_error("invalid clobber register '%s'", str); + } + clobber_regs[reg] = 1; +} diff --git a/i386-gen.c b/i386-gen.c index cfa9cc6..ece054b 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -21,7 +21,7 @@ #ifdef TARGET_DEFS_ONLY /* number of available registers */ -#define NB_REGS 8 +#define NB_REGS 4 #define NB_ASM_REGS 8 /* a register can belong to several classes. The classes must be @@ -33,24 +33,17 @@ #define RC_ST0 0x0008 #define RC_ECX 0x0010 #define RC_EDX 0x0020 -#define RC_EBX 0x0040 -#define RC_ESI 0x0080 -#define RC_EDI 0x0100 -#define RC_INT2 0x0200 #define RC_IRET RC_EAX /* function return: integer register */ #define RC_LRET RC_EDX /* function return: second integer register */ #define RC_FRET RC_ST0 /* function return: float register */ -#define RC_MASK (RC_INT|RC_INT2|RC_FLOAT) + /* pretty names for the registers */ enum { TREG_EAX = 0, TREG_ECX, TREG_EDX, - TREG_EBX, - TREG_ESP, TREG_ST0, - TREG_ESI, - TREG_EDI, + TREG_ESP = 4 }; /* return registers for function */ @@ -97,14 +90,10 @@ enum { #include "tcc.h" ST_DATA const int reg_classes[NB_REGS] = { - /* eax */ RC_INT | RC_EAX | RC_INT2, - /* ecx */ RC_INT | RC_ECX | RC_INT2, + /* eax */ RC_INT | RC_EAX, + /* ecx */ RC_INT | RC_ECX, /* edx */ RC_INT | RC_EDX, - RC_INT|RC_INT2|RC_EBX, - 0, /* st0 */ RC_FLOAT | RC_ST0, - RC_ESI|RC_INT2, - RC_EDI|RC_INT2, }; static unsigned long func_sub_sp_offset; @@ -237,14 +226,6 @@ ST_FUNC void load(int r, SValue *sv) v = fr & VT_VALMASK; if (fr & VT_LVAL) { - if(fr & VT_TMP){ - int size, align; - if((ft & VT_BTYPE) == VT_FUNC) - size = PTR_SIZE; - else - size = type_size(&sv->type, &align); - loc_stack(size, 0); - } if (v == VT_LLOCAL) { v1.type.t = VT_INT; v1.r = VT_LOCAL | VT_LVAL; @@ -253,7 +234,6 @@ ST_FUNC void load(int r, SValue *sv) if (!(reg_classes[fr] & RC_INT)) fr = get_reg(RC_INT); load(fr, &v1); - fc = 0; } if ((ft & VT_BTYPE) == VT_FLOAT) { o(0xd9); /* flds */ @@ -697,7 +677,7 @@ ST_FUNC int gtst(int inv, int t) /* fast case : can jump directly since flags are set */ g(0x0f); t = psym((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -710,23 +690,6 @@ ST_FUNC int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t) || - (vtop->type.t & VT_BTYPE) == VT_LLONG) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - o(0x85); - o(0xc0 + v * 9); - g(0x0f); - t = psym(0x85 ^ inv, t); - } } vtop--; return t; @@ -735,48 +698,40 @@ ST_FUNC int gtst(int inv, int t) /* generate an integer binary operation */ ST_FUNC void gen_opi(int op) { - int r, fr, opc, fc, c; - int cc, uu, tt2; - - fr = vtop[0].r; - fc = vtop->c.ul; - cc = (fr & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - tt2 = (fr & (VT_LVAL | VT_LVAL_TYPE)) == VT_LVAL; + int r, fr, opc, c; switch(op) { case '+': case TOK_ADDC1: /* add with carry generation */ opc = 0; gen_op8: - vswap(); - r = gv(RC_INT); - vswap(); - if (cc) { + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); c = vtop->c.i; if (c == (char)c) { /* generate inc and dec for smaller code */ - if (c == 1 && opc == 0) { + if (c==1 && opc==0) { o (0x40 | r); // inc - } else if (c == 1 && opc == 5) { + } else if (c==1 && opc==5) { o (0x48 | r); // dec } else { o(0x83); - o(0xc0 + r + opc*8); + o(0xc0 | (opc << 3) | r); g(c); } } else { o(0x81); - oad(0xc0 + r+ opc*8, c); + oad(0xc0 | (opc << 3) | r, c); } } else { - if(!tt2) - fr = gv(RC_INT); - o(0x03 + opc*8); - if(fr >= VT_CONST) - gen_modrm(r, fr, vtop->sym, fc); - else - o(0xc0 + fr + r*8); + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + o((opc << 3) | 0x01); + o(0xc0 + r + fr * 8); } vtop--; if (op >= TOK_ULT && op <= TOK_GT) { @@ -804,28 +759,12 @@ ST_FUNC void gen_opi(int op) opc = 1; goto gen_op8; case '*': - opc = 5; - vswap(); - r = gv(RC_INT); - vswap(); - if(!tt2) - fr = gv(RC_INT); - if(r == TREG_EAX){ - if(fr != TREG_EDX) - save_reg(TREG_EDX); - o(0xf7); - if(fr >= VT_CONST) - gen_modrm(opc, fr, vtop->sym, fc); - else - o(0xc0 + fr + opc*8); - }else{ - o(0xaf0f); /* imul fr, r */ - if(fr >= VT_CONST) - gen_modrm(r, fr, vtop->sym, fc); - else - o(0xc0 + fr + r*8); - } + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; vtop--; + o(0xaf0f); /* imul fr, r */ + o(0xc0 + fr + r * 8); break; case TOK_SHL: opc = 4; @@ -836,71 +775,56 @@ ST_FUNC void gen_opi(int op) case TOK_SAR: opc = 7; gen_shift: - if (cc) { + opc = 0xc0 | (opc << 3); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { /* constant case */ vswap(); r = gv(RC_INT); vswap(); - c = vtop->c.i; - if(c == 1){ - o(0xd1); - o(0xc0 + r + opc*8); - }else{ - o(0xc1); /* shl/shr/sar $xxx, r */ - o(0xc0 + r + opc*8); - g(c & 0x1f); - } + c = vtop->c.i & 0x1f; + o(0xc1); /* shl/shr/sar $xxx, r */ + o(opc | r); + g(c); } else { /* we generate the shift in ecx */ gv2(RC_INT, RC_ECX); r = vtop[-1].r; o(0xd3); /* shl/shr/sar %cl, r */ - o(0xc0 + r + opc*8); + o(opc | r); } vtop--; break; - case TOK_UMOD: - opc = 4; - uu = 1; - goto divmod; - case TOK_UDIV: - case TOK_UMULL: - opc = 6; - uu = 1; - goto divmod; case '/': - case '%': + case TOK_UDIV: case TOK_PDIV: - opc = 7; - uu = 0; - divmod: + case '%': + case TOK_UMOD: + case TOK_UMULL: /* first operand must be in eax */ /* XXX: need better constraint for second operand */ - if(!tt2){ - gv2(RC_EAX, RC_INT2); - fr = vtop[0].r; - }else{ - vswap(); - gv(RC_EAX); - vswap(); - } - save_reg(TREG_EDX); - if (op == TOK_UMULL) { + gv2(RC_EAX, RC_ECX); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + save_reg(TREG_EDX); + if (op == TOK_UMULL) { o(0xf7); /* mul fr */ - vtop->r2 = TREG_EDX; - }else{ - o(uu ? 0xd231 : 0x99); /* xor %edx,%edx : cdq RDX:RAX <- sign-extend of RAX. */ - o(0xf7); /* div fr, %eax */ - } - if(fr >= VT_CONST) - gen_modrm(opc, fr, vtop->sym, fc); - else - o(0xc0 + fr + opc*8); - if (op == '%' || op == TOK_UMOD) - r = TREG_EDX; - else + o(0xe0 + fr); + vtop->r2 = TREG_EDX; r = TREG_EAX; - vtop--; + } else { + if (op == TOK_UDIV || op == TOK_UMOD) { + o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ + o(0xf0 + fr); + } else { + o(0xf799); /* cltd, idiv fr, %eax */ + o(0xf8 + fr); + } + if (op == '%' || op == TOK_UMOD) + r = TREG_EDX; + else + r = TREG_EAX; + } vtop->r = r; break; default: diff --git a/i386-tok.h b/i386-tok.h new file mode 100644 index 0000000..6ba865d --- /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 existent op, just to have a regular table */ + DEF_FP1(com) + + DEF_FP(comp) + DEF_FP(sub) + DEF_FP(subr) + DEF_FP(div) + DEF_FP(divr) + + DEF_BWLX(xadd) + DEF_BWLX(cmpxchg) + + /* string ops */ + DEF_BWLX(cmps) + DEF_BWLX(scmp) + DEF_BWL(ins) + DEF_BWL(outs) + DEF_BWLX(lods) + DEF_BWLX(slod) + DEF_BWLX(movs) + DEF_BWLX(smov) + DEF_BWLX(scas) + DEF_BWLX(ssca) + DEF_BWLX(stos) + DEF_BWLX(ssto) + + /* generic asm ops */ +#define ALT(x) +#define DEF_ASM_OP0(name, opcode) DEF_ASM(name) +#define DEF_ASM_OP0L(name, opcode, group, instr_type) +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif + +#define ALT(x) +#define DEF_ASM_OP0(name, opcode) +#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name) +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name) +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name) +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name) +#ifdef TCC_TARGET_X86_64 +# include "x86_64-asm.h" +#else +# include "i386-asm.h" +#endif diff --git a/il-gen.c b/il-gen.c index 9dafbbf..9e1ec64 100644 --- a/il-gen.c +++ b/il-gen.c @@ -516,7 +516,7 @@ int gtst(int inv, int t) break; } t = out_opj(c, t); - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -529,19 +529,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->t)) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - t = out_opj(IL_OP_BRTRUE - inv, t); - } } vtop--; return t; diff --git a/lib/Makefile b/lib/Makefile index 4bf2c7e..e9e12f1 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,5 +1,5 @@ # -# Tiny C Compiler Makefile for libcrt.a +# Tiny C Compiler Makefile for libtcc1.a # TOP = .. @@ -38,15 +38,15 @@ endif DIR = $(TARGET) -native : ../libcrt.a -cross : $(DIR)/libcrt.a +native : ../libtcc1.a +cross : $(DIR)/libtcc1.a native : TCC = $(TOP)/tcc$(EXESUF) cross : TCC = $(TOP)/$(TARGET)-tcc$(EXESUF) -I386_O = libcrt.o alloca86.o alloca86-bt.o $(BCHECK_O) -X86_64_O = libcrt.o alloca86_64.o -ARM_O = libcrt.o armeabi.o alloca-arm.o +I386_O = libtcc1.o alloca86.o alloca86-bt.o $(BCHECK_O) +X86_64_O = libtcc1.o alloca86_64.o +ARM_O = libtcc1.o armeabi.o alloca-arm.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 @@ -83,7 +83,7 @@ ifeq "$(TARGET)" "arm" TGT = -DTCC_TARGET_ARM XCC ?= $(TCC) -B$(TOP) else - $(error libcrt.a not supported on target '$(TARGET)') + $(error libtcc1.a not supported on target '$(TARGET)') endif endif endif @@ -102,7 +102,7 @@ ifdef XAR AR = $(XAR) endif -$(DIR)/libcrt.a ../libcrt.a : $(OBJ) $(XAR) +$(DIR)/libtcc1.a ../libtcc1.a : $(OBJ) $(XAR) $(AR) rcs $@ $(OBJ) $(DIR)/%.o : %.c $(XCC) -c $< -o $@ $(XFLAGS) diff --git a/lib/libcrt.c b/lib/libcrt.c deleted file mode 100644 index 642927a..0000000 --- a/lib/libcrt.c +++ /dev/null @@ -1,743 +0,0 @@ -/* TCC runtime library. - Parts of this code are (c) 2002 Fabrice Bellard - - Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc. - -This file is free software; you can redistribute it and/or modify it -under the terms of the GNU General Public License as published by the -Free Software Foundation; either version 2, or (at your option) any -later version. - -In addition to the permissions in the GNU General Public License, the -Free Software Foundation gives you unlimited permission to link the -compiled version of this file into combinations with other programs, -and to distribute those combinations without any restriction coming -from the use of this file. (The General Public License restrictions -do apply in other respects; for example, they cover modification of -the file, and distribution when not linked into a combine -executable.) - -This file is distributed in the hope that it will be useful, but -WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. -*/ - -#include - -#define W_TYPE_SIZE 32 -#define BITS_PER_UNIT 8 - -typedef int Wtype; -typedef unsigned int UWtype; -typedef unsigned int USItype; -typedef long long DWtype; -typedef unsigned long long UDWtype; - -struct DWstruct { - Wtype low, high; -}; - -typedef union -{ - struct DWstruct s; - DWtype ll; -} DWunion; - -typedef long double XFtype; -#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) -#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) - -/* the following deal with IEEE single-precision numbers */ -#define EXCESS 126 -#define SIGNBIT 0x80000000 -#define HIDDEN (1 << 23) -#define SIGN(fp) ((fp) & SIGNBIT) -#define EXP(fp) (((fp) >> 23) & 0xFF) -#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) -#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) - -/* the following deal with IEEE double-precision numbers */ -#define EXCESSD 1022 -#define HIDDEND (1 << 20) -#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) -#define SIGND(fp) ((fp.l.upper) & SIGNBIT) -#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \ - (fp.l.lower >> 22)) -#define HIDDEND_LL ((long long)1 << 52) -#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) -#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) - -/* the following deal with x86 long double-precision numbers */ -#define EXCESSLD 16382 -#define EXPLD(fp) (fp.l.upper & 0x7fff) -#define SIGNLD(fp) ((fp.l.upper) & 0x8000) - -/* only for x86 */ -union ldouble_long { - long double ld; - struct { - unsigned long long lower; - unsigned short upper; - } l; -}; - -union double_long { - double d; -#if 1 - struct { - unsigned int lower; - int upper; - } l; -#else - struct { - int upper; - unsigned int lower; - } l; -#endif - long long ll; -}; - -union float_long { - float f; - long l; -}; - -/* XXX: we don't support several builtin supports for now */ -#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM) - -/* XXX: use gcc/tcc intrinsic ? */ -#if defined(TCC_TARGET_I386) -#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ - : "=r" ((USItype) (sh)), \ - "=&r" ((USItype) (sl)) \ - : "0" ((USItype) (ah)), \ - "g" ((USItype) (bh)), \ - "1" ((USItype) (al)), \ - "g" ((USItype) (bl))) -#define umul_ppmm(w1, w0, u, v) \ - __asm__ ("mull %3" \ - : "=a" ((USItype) (w0)), \ - "=d" ((USItype) (w1)) \ - : "%0" ((USItype) (u)), \ - "rm" ((USItype) (v))) -#define udiv_qrnnd(q, r, n1, n0, dv) \ - __asm__ ("divl %4" \ - : "=a" ((USItype) (q)), \ - "=d" ((USItype) (r)) \ - : "0" ((USItype) (n0)), \ - "1" ((USItype) (n1)), \ - "rm" ((USItype) (dv))) -#define count_leading_zeros(count, x) \ - do { \ - USItype __cbtmp; \ - __asm__ ("bsrl %1,%0" \ - : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ - (count) = __cbtmp ^ 31; \ - } while (0) -#else -#error unsupported CPU type -#endif - -/* most of this code is taken from libgcc2.c from gcc */ - -static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) -{ - DWunion ww; - DWunion nn, dd; - DWunion rr; - UWtype d0, d1, n0, n1, n2; - UWtype q0, q1; - UWtype b, bm; - - nn.ll = n; - dd.ll = d; - - d0 = dd.s.low; - d1 = dd.s.high; - n0 = nn.s.low; - n1 = nn.s.high; - -#if !defined(UDIV_NEEDS_NORMALIZATION) - if (d1 == 0) - { - if (d0 > n1) - { - /* 0q = nn / 0D */ - - udiv_qrnnd (q0, n0, n1, n0, d0); - q1 = 0; - - /* Remainder in n0. */ - } - else - { - /* qq = NN / 0d */ - - if (d0 == 0) - d0 = 1 / d0; /* Divide intentionally by zero. */ - - udiv_qrnnd (q1, n1, 0, n1, d0); - udiv_qrnnd (q0, n0, n1, n0, d0); - - /* Remainder in n0. */ - } - - if (rp != 0) - { - rr.s.low = n0; - rr.s.high = 0; - *rp = rr.ll; - } - } - -#else /* UDIV_NEEDS_NORMALIZATION */ - - if (d1 == 0) - { - if (d0 > n1) - { - /* 0q = nn / 0D */ - - count_leading_zeros (bm, d0); - - if (bm != 0) - { - /* Normalize, i.e. make the most significant bit of the - denominator set. */ - - d0 = d0 << bm; - n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); - n0 = n0 << bm; - } - - udiv_qrnnd (q0, n0, n1, n0, d0); - q1 = 0; - - /* Remainder in n0 >> bm. */ - } - else - { - /* qq = NN / 0d */ - - if (d0 == 0) - d0 = 1 / d0; /* Divide intentionally by zero. */ - - count_leading_zeros (bm, d0); - - if (bm == 0) - { - /* From (n1 >= d0) /\ (the most significant bit of d0 is set), - conclude (the most significant bit of n1 is set) /\ (the - leading quotient digit q1 = 1). - - This special case is necessary, not an optimization. - (Shifts counts of W_TYPE_SIZE are undefined.) */ - - n1 -= d0; - q1 = 1; - } - else - { - /* Normalize. */ - - b = W_TYPE_SIZE - bm; - - d0 = d0 << bm; - n2 = n1 >> b; - n1 = (n1 << bm) | (n0 >> b); - n0 = n0 << bm; - - udiv_qrnnd (q1, n1, n2, n1, d0); - } - - /* n1 != d0... */ - - udiv_qrnnd (q0, n0, n1, n0, d0); - - /* Remainder in n0 >> bm. */ - } - - if (rp != 0) - { - rr.s.low = n0 >> bm; - rr.s.high = 0; - *rp = rr.ll; - } - } -#endif /* UDIV_NEEDS_NORMALIZATION */ - - else - { - if (d1 > n1) - { - /* 00 = nn / DD */ - - q0 = 0; - q1 = 0; - - /* Remainder in n1n0. */ - if (rp != 0) - { - rr.s.low = n0; - rr.s.high = n1; - *rp = rr.ll; - } - } - else - { - /* 0q = NN / dd */ - - count_leading_zeros (bm, d1); - if (bm == 0) - { - /* From (n1 >= d1) /\ (the most significant bit of d1 is set), - conclude (the most significant bit of n1 is set) /\ (the - quotient digit q0 = 0 or 1). - - This special case is necessary, not an optimization. */ - - /* The condition on the next line takes advantage of that - n1 >= d1 (true due to program flow). */ - if (n1 > d1 || n0 >= d0) - { - q0 = 1; - sub_ddmmss (n1, n0, n1, n0, d1, d0); - } - else - q0 = 0; - - q1 = 0; - - if (rp != 0) - { - rr.s.low = n0; - rr.s.high = n1; - *rp = rr.ll; - } - } - else - { - UWtype m1, m0; - /* Normalize. */ - - b = W_TYPE_SIZE - bm; - - d1 = (d1 << bm) | (d0 >> b); - d0 = d0 << bm; - n2 = n1 >> b; - n1 = (n1 << bm) | (n0 >> b); - n0 = n0 << bm; - - udiv_qrnnd (q0, n1, n2, n1, d1); - umul_ppmm (m1, m0, q0, d0); - - if (m1 > n1 || (m1 == n1 && m0 > n0)) - { - q0--; - sub_ddmmss (m1, m0, m1, m0, d1, d0); - } - - q1 = 0; - - /* Remainder in (n1n0 - m1m0) >> bm. */ - if (rp != 0) - { - sub_ddmmss (n1, n0, n1, n0, m1, m0); - rr.s.low = (n1 << b) | (n0 >> bm); - rr.s.high = n1 >> bm; - *rp = rr.ll; - } - } - } - } - - ww.s.low = q0; - ww.s.high = q1; - return ww.ll; -} - -#define __negdi2(a) (-(a)) - -long long __divdi3(long long u, long long v) -{ - int c = 0; - DWunion uu, vv; - DWtype w; - - uu.ll = u; - vv.ll = v; - - if (uu.s.high < 0) { - c = ~c; - uu.ll = __negdi2 (uu.ll); - } - if (vv.s.high < 0) { - c = ~c; - vv.ll = __negdi2 (vv.ll); - } - w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); - if (c) - w = __negdi2 (w); - return w; -} - -long long __moddi3(long long u, long long v) -{ - int c = 0; - DWunion uu, vv; - DWtype w; - - uu.ll = u; - vv.ll = v; - - if (uu.s.high < 0) { - c = ~c; - uu.ll = __negdi2 (uu.ll); - } - if (vv.s.high < 0) - vv.ll = __negdi2 (vv.ll); - - __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w); - if (c) - w = __negdi2 (w); - return w; -} - -unsigned long long __udivdi3(unsigned long long u, unsigned long long v) -{ - return __udivmoddi4 (u, v, (UDWtype *) 0); -} - -unsigned long long __umoddi3(unsigned long long u, unsigned long long v) -{ - UDWtype w; - - __udivmoddi4 (u, v, &w); - return w; -} - -/* XXX: fix tcc's code generator to do this instead */ -long long __ashrdi3(long long a, int b) -{ -#ifdef __TINYC__ - DWunion u; - u.ll = a; - if (b >= 32) { - u.s.low = u.s.high >> (b - 32); - u.s.high = u.s.high >> 31; - } else if (b != 0) { - u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); - u.s.high = u.s.high >> b; - } - return u.ll; -#else - return a >> b; -#endif -} - -/* XXX: fix tcc's code generator to do this instead */ -unsigned long long __lshrdi3(unsigned long long a, int b) -{ -#ifdef __TINYC__ - DWunion u; - u.ll = a; - if (b >= 32) { - u.s.low = (unsigned)u.s.high >> (b - 32); - u.s.high = 0; - } else if (b != 0) { - u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); - u.s.high = (unsigned)u.s.high >> b; - } - return u.ll; -#else - return a >> b; -#endif -} - -/* XXX: fix tcc's code generator to do this instead */ -long long __ashldi3(long long a, int b) -{ -#ifdef __TINYC__ - DWunion u; - u.ll = a; - if (b >= 32) { - u.s.high = (unsigned)u.s.low << (b - 32); - u.s.low = 0; - } else if (b != 0) { - u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b)); - u.s.low = (unsigned)u.s.low << b; - } - return u.ll; -#else - return a << b; -#endif -} - -#endif /* !__x86_64__ */ - -/* XXX: fix tcc's code generator to do this instead */ -float __floatundisf(unsigned long long a) -{ - DWunion uu; - XFtype r; - - uu.ll = a; - if (uu.s.high >= 0) { - return (float)uu.ll; - } else { - r = (XFtype)uu.ll; - r += 18446744073709551616.0; - return (float)r; - } -} - -double __floatundidf(unsigned long long a) -{ - DWunion uu; - XFtype r; - - uu.ll = a; - if (uu.s.high >= 0) { - return (double)uu.ll; - } else { - r = (XFtype)uu.ll; - r += 18446744073709551616.0; - return (double)r; - } -} - -long double __floatundixf(unsigned long long a) -{ - DWunion uu; - XFtype r; - - uu.ll = a; - if (uu.s.high >= 0) { - return (long double)uu.ll; - } else { - r = (XFtype)uu.ll; - r += 18446744073709551616.0; - return (long double)r; - } -} - -unsigned long long __fixunssfdi (float a1) -{ - register union float_long fl1; - register int exp; - register unsigned long l; - int s; - fl1.f = a1; - - if (fl1.l == 0) - return 0; - - exp = EXP (fl1.l) - EXCESS - 24; - - l = MANT(fl1.l); - s = SIGN(fl1.l)? -1: 1; - if (exp >= 64) - return (unsigned long long)-1; - else if (exp >= 0) - return ((unsigned long long)l << exp)*s; - else if (exp >= -23) - return (l >> -exp)*s; - else - return 0; -} - -unsigned long long __fixunsdfdi (double a1) -{ - register union double_long dl1; - register int exp; - register unsigned long long l; - int s; - dl1.d = a1; - - if (dl1.ll == 0) - return (0); - - exp = EXPD (dl1) - EXCESSD - 53; - - l = MANTD_LL(dl1); - s = SIGND(dl1)? -1: 1; - if (exp >= 64) - return (unsigned long long)-1; - else if (exp >= 0) - return (l << exp)*s; - else if (exp >= -52) - return (l >> -exp)*s; - else - return 0; -} - -unsigned long long __fixunsxfdi (long double a1) -{ - register union ldouble_long dl1; - register int exp; - register unsigned long long l; - int s; - dl1.ld = a1; - - if (dl1.l.lower == 0 && dl1.l.upper == 0) - return (0); - - exp = EXPLD (dl1) - EXCESSLD - 64; - s = SIGNLD(dl1)? -1: 1; - l = dl1.l.lower; - - if (exp >= 64) - return (unsigned long long)-1; - else if (exp >= 0) - return ((unsigned long long)l << exp)*s; - else if (exp >= -64) - return (l >> -exp)*s; - else - return 0; -} - -long long __fixsfdi (float a1) -{ - long long ret; int s; - ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1); - return s ? ret : -ret; -} - -long long __fixdfdi (double a1) -{ - long long ret; int s; - ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1); - return s ? ret : -ret; -} - -long long __fixxfdi (long double a1) -{ - long long ret; int s; - ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1); - return s ? ret : -ret; -} - -#if defined(TCC_TARGET_X86_64) && !defined(_WIN64) - -#ifndef __TINYC__ -#include -#include -#include -#else -/* Avoid including stdlib.h because it is not easily available when - cross compiling */ -extern void *malloc(unsigned long long); -void *memset(void *s, int c, size_t n); -extern void free(void*); -extern void abort(void); -#endif - -enum __va_arg_type { - __va_gen_reg, __va_float_reg, __va_ld_reg, __va_stack -}; - -//This should be in sync with the declaration on our include/stdarg.h -/* GCC compatible definition of va_list. */ -typedef struct { - unsigned int gp_offset; - unsigned int fp_offset; - union { - unsigned int overflow_offset; - char *overflow_arg_area; - }; - char *reg_save_area; -} __va_list_struct; - -#undef __va_start -#undef __va_arg -#undef __va_copy -#undef __va_end - -void __va_start(__va_list_struct *ap, void *fp) -{ - memset(ap, 0, sizeof(__va_list_struct)); - *ap = *(__va_list_struct *)((char *)fp - 16); - ap->overflow_arg_area = (char *)fp + ap->overflow_offset; - ap->reg_save_area = (char *)fp - 176 - 16; -} - -void *__va_arg(__va_list_struct *ap, - enum __va_arg_type arg_type, - int size, int align) -{ - size = (size + 7) & ~7; - align = (align + 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_ld_reg: - ap->overflow_arg_area = (char*)((intptr_t)(ap->overflow_arg_area + align - 1) & -(intptr_t)align); - 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(); - } -} - -#endif /* __x86_64__ */ - -/* Flushing for tccrun */ -#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_I386) - -void __clear_cache(char *beginning, char *end) -{ -} - -#elif defined(TCC_TARGET_ARM) - -#define _GNU_SOURCE -#include -#include -#include - -void __clear_cache(char *beginning, char *end) -{ -/* __ARM_NR_cacheflush is kernel private and should not be used in user space. - * However, there is no ARM asm parser in tcc so we use it for now */ -#if 1 - syscall(__ARM_NR_cacheflush, beginning, end, 0); -#else - __asm__ ("push {r7}\n\t" - "mov r7, #0xf0002\n\t" - "mov r2, #0\n\t" - "swi 0\n\t" - "pop {r7}\n\t" - "ret"); -#endif -} - -#else -#warning __clear_cache not defined for this architecture, avoid using tcc -run -#endif diff --git a/lib/libtcc1.c b/lib/libtcc1.c new file mode 100644 index 0000000..284965e --- /dev/null +++ b/lib/libtcc1.c @@ -0,0 +1,739 @@ +/* TCC runtime library. + Parts of this code are (c) 2002 Fabrice Bellard + + Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#include + +#define W_TYPE_SIZE 32 +#define BITS_PER_UNIT 8 + +typedef int Wtype; +typedef unsigned int UWtype; +typedef unsigned int USItype; +typedef long long DWtype; +typedef unsigned long long UDWtype; + +struct DWstruct { + Wtype low, high; +}; + +typedef union +{ + struct DWstruct s; + DWtype ll; +} DWunion; + +typedef long double XFtype; +#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) +#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) + +/* the following deal with IEEE single-precision numbers */ +#define EXCESS 126 +#define SIGNBIT 0x80000000 +#define HIDDEN (1 << 23) +#define SIGN(fp) ((fp) & SIGNBIT) +#define EXP(fp) (((fp) >> 23) & 0xFF) +#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) +#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) + +/* the following deal with IEEE double-precision numbers */ +#define EXCESSD 1022 +#define HIDDEND (1 << 20) +#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) +#define SIGND(fp) ((fp.l.upper) & SIGNBIT) +#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \ + (fp.l.lower >> 22)) +#define HIDDEND_LL ((long long)1 << 52) +#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) +#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) + +/* the following deal with x86 long double-precision numbers */ +#define EXCESSLD 16382 +#define EXPLD(fp) (fp.l.upper & 0x7fff) +#define SIGNLD(fp) ((fp.l.upper) & 0x8000) + +/* only for x86 */ +union ldouble_long { + long double ld; + struct { + unsigned long long lower; + unsigned short upper; + } l; +}; + +union double_long { + double d; +#if 1 + struct { + unsigned int lower; + int upper; + } l; +#else + struct { + int upper; + unsigned int lower; + } l; +#endif + long long ll; +}; + +union float_long { + float f; + long l; +}; + +/* XXX: we don't support several builtin supports for now */ +#if !defined(TCC_TARGET_X86_64) && !defined(TCC_TARGET_ARM) + +/* XXX: use gcc/tcc intrinsic ? */ +#if defined(TCC_TARGET_I386) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("mull %3" \ + : "=a" ((USItype) (w0)), \ + "=d" ((USItype) (w1)) \ + : "%0" ((USItype) (u)), \ + "rm" ((USItype) (v))) +#define udiv_qrnnd(q, r, n1, n0, dv) \ + __asm__ ("divl %4" \ + : "=a" ((USItype) (q)), \ + "=d" ((USItype) (r)) \ + : "0" ((USItype) (n0)), \ + "1" ((USItype) (n1)), \ + "rm" ((USItype) (dv))) +#define count_leading_zeros(count, x) \ + do { \ + USItype __cbtmp; \ + __asm__ ("bsrl %1,%0" \ + : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ + (count) = __cbtmp ^ 31; \ + } while (0) +#else +#error unsupported CPU type +#endif + +/* most of this code is taken from libgcc2.c from gcc */ + +static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) +{ + DWunion ww; + DWunion nn, dd; + DWunion rr; + UWtype d0, d1, n0, n1, n2; + UWtype q0, q1; + UWtype b, bm; + + nn.ll = n; + dd.ll = d; + + d0 = dd.s.low; + d1 = dd.s.high; + n0 = nn.s.low; + n1 = nn.s.high; + +#if !defined(UDIV_NEEDS_NORMALIZATION) + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + udiv_qrnnd (q1, n1, 0, n1, d0); + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0. */ + } + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = 0; + *rp = rr.ll; + } + } + +#else /* UDIV_NEEDS_NORMALIZATION */ + + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + count_leading_zeros (bm, d0); + + if (bm != 0) + { + /* Normalize, i.e. make the most significant bit of the + denominator set. */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + count_leading_zeros (bm, d0); + + if (bm == 0) + { + /* From (n1 >= d0) /\ (the most significant bit of d0 is set), + conclude (the most significant bit of n1 is set) /\ (the + leading quotient digit q1 = 1). + + This special case is necessary, not an optimization. + (Shifts counts of W_TYPE_SIZE are undefined.) */ + + n1 -= d0; + q1 = 1; + } + else + { + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + if (rp != 0) + { + rr.s.low = n0 >> bm; + rr.s.high = 0; + *rp = rr.ll; + } + } +#endif /* UDIV_NEEDS_NORMALIZATION */ + + else + { + if (d1 > n1) + { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + /* 0q = NN / dd */ + + count_leading_zeros (bm, d1); + if (bm == 0) + { + /* From (n1 >= d1) /\ (the most significant bit of d1 is set), + conclude (the most significant bit of n1 is set) /\ (the + quotient digit q0 = 0 or 1). + + This special case is necessary, not an optimization. */ + + /* The condition on the next line takes advantage of that + n1 >= d1 (true due to program flow). */ + if (n1 > d1 || n0 >= d0) + { + q0 = 1; + sub_ddmmss (n1, n0, n1, n0, d1, d0); + } + else + q0 = 0; + + q1 = 0; + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + UWtype m1, m0; + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q0, n1, n2, n1, d1); + umul_ppmm (m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) + { + q0--; + sub_ddmmss (m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + if (rp != 0) + { + sub_ddmmss (n1, n0, n1, n0, m1, m0); + rr.s.low = (n1 << b) | (n0 >> bm); + rr.s.high = n1 >> bm; + *rp = rr.ll; + } + } + } + } + + ww.s.low = q0; + ww.s.high = q1; + return ww.ll; +} + +#define __negdi2(a) (-(a)) + +long long __divdi3(long long u, long long v) +{ + int c = 0; + DWunion uu, vv; + DWtype w; + + uu.ll = u; + vv.ll = v; + + if (uu.s.high < 0) { + c = ~c; + uu.ll = __negdi2 (uu.ll); + } + if (vv.s.high < 0) { + c = ~c; + vv.ll = __negdi2 (vv.ll); + } + w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); + if (c) + w = __negdi2 (w); + return w; +} + +long long __moddi3(long long u, long long v) +{ + int c = 0; + DWunion uu, vv; + DWtype w; + + uu.ll = u; + vv.ll = v; + + if (uu.s.high < 0) { + c = ~c; + uu.ll = __negdi2 (uu.ll); + } + if (vv.s.high < 0) + vv.ll = __negdi2 (vv.ll); + + __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w); + if (c) + w = __negdi2 (w); + return w; +} + +unsigned long long __udivdi3(unsigned long long u, unsigned long long v) +{ + return __udivmoddi4 (u, v, (UDWtype *) 0); +} + +unsigned long long __umoddi3(unsigned long long u, unsigned long long v) +{ + UDWtype w; + + __udivmoddi4 (u, v, &w); + return w; +} + +/* XXX: fix tcc's code generator to do this instead */ +long long __ashrdi3(long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.low = u.s.high >> (b - 32); + u.s.high = u.s.high >> 31; + } else if (b != 0) { + u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); + u.s.high = u.s.high >> b; + } + return u.ll; +#else + return a >> b; +#endif +} + +/* XXX: fix tcc's code generator to do this instead */ +unsigned long long __lshrdi3(unsigned long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.low = (unsigned)u.s.high >> (b - 32); + u.s.high = 0; + } else if (b != 0) { + u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); + u.s.high = (unsigned)u.s.high >> b; + } + return u.ll; +#else + return a >> b; +#endif +} + +/* XXX: fix tcc's code generator to do this instead */ +long long __ashldi3(long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.high = (unsigned)u.s.low << (b - 32); + u.s.low = 0; + } else if (b != 0) { + u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b)); + u.s.low = (unsigned)u.s.low << b; + } + return u.ll; +#else + return a << b; +#endif +} + +#endif /* !__x86_64__ */ + +/* XXX: fix tcc's code generator to do this instead */ +float __floatundisf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (float)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (float)r; + } +} + +double __floatundidf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (double)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (double)r; + } +} + +long double __floatundixf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (long double)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (long double)r; + } +} + +unsigned long long __fixunssfdi (float a1) +{ + register union float_long fl1; + register int exp; + register unsigned long l; + + fl1.f = a1; + + if (fl1.l == 0) + return (0); + + exp = EXP (fl1.l) - EXCESS - 24; + + l = MANT(fl1.l); + if (exp >= 41) + return (unsigned long long)-1; + else if (exp >= 0) + return (unsigned long long)l << exp; + else if (exp >= -23) + return l >> -exp; + else + return 0; +} + +unsigned long long __fixunsdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long long l; + + dl1.d = a1; + + if (dl1.ll == 0) + return (0); + + exp = EXPD (dl1) - EXCESSD - 53; + + l = MANTD_LL(dl1); + + if (exp >= 12) + return (unsigned long long)-1; + else if (exp >= 0) + return l << exp; + else if (exp >= -52) + return l >> -exp; + else + return 0; +} + +unsigned long long __fixunsxfdi (long double a1) +{ + register union ldouble_long dl1; + register int exp; + register unsigned long long l; + + dl1.ld = a1; + + if (dl1.l.lower == 0 && dl1.l.upper == 0) + return (0); + + exp = EXPLD (dl1) - EXCESSLD - 64; + + l = dl1.l.lower; + + if (exp > 0) + return (unsigned long long)-1; + else if (exp >= -63) + return l >> -exp; + else + return 0; +} + +long long __fixsfdi (float a1) +{ + long long ret; int s; + ret = __fixunssfdi((s = a1 >= 0) ? a1 : -a1); + return s ? ret : -ret; +} + +long long __fixdfdi (double a1) +{ + long long ret; int s; + ret = __fixunsdfdi((s = a1 >= 0) ? a1 : -a1); + return s ? ret : -ret; +} + +long long __fixxfdi (long double a1) +{ + long long ret; int s; + ret = __fixunsxfdi((s = a1 >= 0) ? a1 : -a1); + return s ? ret : -ret; +} + +#if defined(TCC_TARGET_X86_64) && !defined(_WIN64) + +#ifndef __TINYC__ +#include +#include +#include +#else +/* Avoid including stdlib.h because it is not easily available when + cross compiling */ +extern void *malloc(unsigned long long); +void *memset(void *s, int c, size_t n); +extern void free(void*); +extern void abort(void); +#endif + +enum __va_arg_type { + __va_gen_reg, __va_float_reg, __va_stack +}; + +//This should be in sync with the declaration on our include/stdarg.h +/* GCC compatible definition of va_list. */ +typedef struct { + unsigned int gp_offset; + unsigned int fp_offset; + union { + unsigned int overflow_offset; + char *overflow_arg_area; + }; + char *reg_save_area; +} __va_list_struct; + +#undef __va_start +#undef __va_arg +#undef __va_copy +#undef __va_end + +void __va_start(__va_list_struct *ap, void *fp) +{ + memset(ap, 0, sizeof(__va_list_struct)); + *ap = *(__va_list_struct *)((char *)fp - 16); + ap->overflow_arg_area = (char *)fp + ap->overflow_offset; + ap->reg_save_area = (char *)fp - 176 - 16; +} + +void *__va_arg(__va_list_struct *ap, + enum __va_arg_type arg_type, + int size, int align) +{ + size = (size + 7) & ~7; + align = (align + 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; + ap->overflow_arg_area = (char*)((intptr_t)(ap->overflow_arg_area + align - 1) & -(intptr_t)align); + return ap->overflow_arg_area - size; + + default: +#ifndef __TINYC__ + fprintf(stderr, "unknown ABI type for __va_arg\n"); +#endif + abort(); + } +} + +#endif /* __x86_64__ */ + +/* Flushing for tccrun */ +#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_I386) + +void __clear_cache(char *beginning, char *end) +{ +} + +#elif defined(TCC_TARGET_ARM) + +#define _GNU_SOURCE +#include +#include +#include + +void __clear_cache(char *beginning, char *end) +{ +/* __ARM_NR_cacheflush is kernel private and should not be used in user space. + * However, there is no ARM asm parser in tcc so we use it for now */ +#if 1 + syscall(__ARM_NR_cacheflush, beginning, end, 0); +#else + __asm__ ("push {r7}\n\t" + "mov r7, #0xf0002\n\t" + "mov r2, #0\n\t" + "swi 0\n\t" + "pop {r7}\n\t" + "ret"); +#endif +} + +#else +#warning __clear_cache not defined for this architecture, avoid using tcc -run +#endif diff --git a/libtcc.c b/libtcc.c index 28b6407..deda7e6 100644 --- a/libtcc.c +++ b/libtcc.c @@ -52,10 +52,10 @@ ST_DATA struct TCCState *tcc_state; #include "x86_64-gen.c" #endif #ifdef CONFIG_TCC_ASM +#include "tccasm.c" #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 -#include "asmx86.c" +#include "i386-asm.c" #endif -#include "tccasm.c" #endif #ifdef TCC_TARGET_COFF #include "tcccoff.c" @@ -868,7 +868,6 @@ LIBTCCAPI void tcc_undefine_symbol(TCCState *s1, const char *sym) static void tcc_cleanup(void) { int i, n; - CSym *def; if (NULL == tcc_state) return; tcc_state = NULL; @@ -878,11 +877,8 @@ static void tcc_cleanup(void) /* free tokens */ n = tok_ident - TOK_IDENT; - for(i = 0; i < n; i++){ - def = &table_ident[i]->sym_define; - tcc_free(def->data); + for(i = 0; i < n; i++) tcc_free(table_ident[i]); - } tcc_free(table_ident); /* free sym_pools */ diff --git a/tcc.h b/tcc.h index 1cead42..c93cedf 100644 --- a/tcc.h +++ b/tcc.h @@ -39,7 +39,6 @@ #include #include #include -#include #ifdef CONFIG_TCCASSERT #include @@ -148,7 +147,6 @@ /* #define MEM_DEBUG */ /* assembler debug */ /* #define ASM_DEBUG */ -/* #define PRINTF_ASM_CODE */ /* target selection */ /* #define TCC_TARGET_I386 *//* i386 code generator */ @@ -276,7 +274,7 @@ # define DEFAULT_ELFINTERP(s) default_elfinterp(s) #endif -/* library to use with CONFIG_USE_LIBGCC instead of libcrt.a */ +/* library to use with CONFIG_USE_LIBGCC instead of libtcc1.a */ #define TCC_LIBGCC USE_MUADIR(CONFIG_SYSROOT "/" CONFIG_LDDIR) "/libgcc_s.so.1" /* -------------------------------------------- */ @@ -305,22 +303,15 @@ #define VSTACK_SIZE 256 #define STRING_MAX_SIZE 1024 #define PACK_STACK_SIZE 8 -#define MACRO_STACK_SIZE 4 #define TOK_HASH_SIZE 8192 /* must be a power of two */ #define TOK_ALLOC_INCR 512 /* must be a power of two */ #define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */ -typedef struct CSym { - int off; - int size;/* size in *sym */ - struct Sym **data; /* if non NULL, data has been malloced */ -} CSym; - /* token symbol management */ typedef struct TokenSym { struct TokenSym *hash_next; - struct CSym sym_define; /* direct pointer to define */ + struct Sym *sym_define; /* direct pointer to define */ struct Sym *sym_label; /* direct pointer to label */ struct Sym *sym_struct; /* direct pointer to structure */ struct Sym *sym_identifier; /* direct pointer to identifier */ @@ -366,8 +357,8 @@ typedef union CValue { /* value on stack */ typedef struct SValue { CType type; /* type */ - unsigned int r; /* register + flags */ - unsigned int r2; /* second register, used for 'long long' + unsigned short r; /* register + flags */ + unsigned short r2; /* second register, used for 'long long' type. If not used, set to VT_CONST */ CValue c; /* constant, if VT_CONST */ struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */ @@ -747,21 +738,19 @@ struct TCCState { #define VT_CMP 0x0033 /* the value is stored in processor flags (in vc) */ #define VT_JMP 0x0034 /* value is the consequence of jmp true (even) */ #define VT_JMPI 0x0035 /* value is the consequence of jmp false (odd) */ -#define TREG_MEM 0x0040 /* x86_64-gen.c add for tcc.h: The current value can be */ -#define VT_REF 0x0080 /* value is pointer to structure rather than address */ +#define VT_REF 0x0040 /* value is pointer to structure rather than address */ #define VT_LVAL 0x0100 /* var is an lvalue */ #define VT_SYM 0x0200 /* a symbol value is added */ #define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for char/short stored in integer registers) */ #define VT_MUSTBOUND 0x0800 /* bound checking must be done before dereferencing value */ +#define VT_BOUNDED 0x8000 /* value is bounded. The address of the + bounding function call point is in vc */ #define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */ #define VT_LVAL_SHORT 0x2000 /* lvalue is a short */ #define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */ #define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED) -#define VT_BOUNDED 0x8000 /* value is bounded. The address of the - bounding function call point is in vc */ -#define VT_TMP 0x10000 /* luck or tmp stack */ /* types */ #define VT_BTYPE 0x000f /* mask for basic type */ @@ -789,7 +778,6 @@ struct TCCState { #define VT_VOLATILE 0x1000 /* volatile modifier */ #define VT_DEFSIGN 0x2000 /* signed type */ #define VT_VLA 0x00020000 /* VLA type (also has VT_PTR and VT_ARRAY) */ -#define VT_VLS 0x00080000 /* VLA type (also has VT_PTR and VT_STRUCT) */ /* storage */ #define VT_EXTERN 0x00000080 /* extern definition */ @@ -800,14 +788,14 @@ struct TCCState { #define VT_EXPORT 0x00008000 /* win32: data exported from dll */ #define VT_WEAK 0x00010000 /* weak symbol */ #define VT_TLS 0x00040000 /* thread-local storage */ -#define VT_VIS_SHIFT 20 /* shift for symbol visibility, overlapping +#define VT_VIS_SHIFT 19 /* shift for symbol visibility, overlapping bitfield values, because bitfields never have linkage and hence never have visibility. */ #define VT_VIS_SIZE 2 /* We have four visibilities. */ #define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT) -#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (max: 32 - 2*6) */ +#define VT_STRUCT_SHIFT 19 /* shift for bitfield shift values (max: 32 - 2*6) */ /* type mask (except storage) */ @@ -1136,8 +1124,7 @@ ST_DATA TokenSym **table_ident; 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) */ -#define PARSE_FLAG_PACK 0x0020 /* #pragma pack */ +#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */ ST_FUNC TokenSym *tok_alloc(const char *str, int len); ST_FUNC char *get_tok_str(int v, CValue *cv); @@ -1195,7 +1182,7 @@ ST_DATA Sym *define_stack; ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; ST_DATA SValue __vstack[1+/*to make bcheck happy*/ VSTACK_SIZE], *vtop; #define vstack (__vstack + 1) -ST_DATA int rsym, anon_sym, ind, loc, ex_rc; +ST_DATA int rsym, anon_sym, ind, loc; ST_DATA int const_wanted; /* true if constant wanted */ ST_DATA int nocode_wanted; /* true if no code generation wanted for an expression */ @@ -1205,14 +1192,12 @@ ST_DATA int func_var; /* true if current function is variadic */ ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ ST_DATA char *funcname; -ST_DATA int pop_stack; ST_INLN int is_float(int t); ST_FUNC int ieee_finite(double d); ST_FUNC void test_lvalue(void); ST_FUNC void swap(int *p, int *q); ST_FUNC void vpushi(int v); -ST_FUNC void vpushs(addr_t v); ST_FUNC Sym *external_global_sym(int v, CType *type, int r); ST_FUNC void vset(CType *type, int r, int v); ST_FUNC void vswap(void); @@ -1246,9 +1231,6 @@ ST_FUNC void gexpr(void); ST_FUNC int expr_const(void); ST_FUNC void gen_inline_functions(void); ST_FUNC void decl(int l); -ST_FUNC void vdup(void); -ST_FUNC void gaddrof(void); -ST_FUNC int loc_stack(int size, int is_sub); #if defined CONFIG_TCC_BCHECK || defined TCC_TARGET_C67 ST_FUNC Sym *get_sym_ref(CType *type, Section *sec, unsigned long offset, unsigned long size); #endif diff --git a/tccasm.c b/tccasm.c index eec4208..38efe1c 100644 --- a/tccasm.c +++ b/tccasm.c @@ -20,6 +20,7 @@ #include "tcc.h" #ifdef CONFIG_TCC_ASM + ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n) { char buf[64]; @@ -482,7 +483,7 @@ static void asm_parse_directive(TCCState *s1) case TOK_ASM_globl: case TOK_ASM_global: case TOK_ASM_weak: - case TOK_ASM_hidden: + case TOK_ASM_hidden: tok1 = tok; do { Sym *sym; @@ -493,12 +494,12 @@ static void asm_parse_directive(TCCState *s1) sym = label_push(&s1->asm_labels, tok, 0); sym->type.t = VT_VOID; } - if (tok1 != TOK_ASM_hidden) + if (tok1 != TOK_ASM_hidden) sym->type.t &= ~VT_STATIC; if (tok1 == TOK_ASM_weak) sym->type.t |= VT_WEAK; - else if (tok1 == TOK_ASM_hidden) - sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT; + else if (tok1 == TOK_ASM_hidden) + sym->type.t |= STV_HIDDEN << VT_VIS_SHIFT; next(); } while (tok == ','); break; @@ -696,15 +697,42 @@ static void asm_parse_directive(TCCState *s1) } } + /* assemble a file */ static int tcc_assemble_internal(TCCState *s1, int do_preprocess) { int opcode; -#ifdef PRINTF_ASM_CODE - ST_FUNC void printf_asm_opcode(); +#if 0 /* print stats about opcodes */ - printf_asm_opcode(); + { + const ASMInstr *pa; + int freq[4]; + int op_vals[500]; + int nb_op_vals, i, j; + + nb_op_vals = 0; + memset(freq, 0, sizeof(freq)); + for(pa = asm_instrs; pa->sym != 0; pa++) { + freq[pa->nb_ops]++; + for(i=0;inb_ops;i++) { + for(j=0;jop_type[i] == op_vals[j]) + goto found; + } + op_vals[nb_op_vals++] = pa->op_type[i]; + found: ; + } + } + for(i=0;ifilename); + put_elf_sym(symtab_section, 0, 0, + ELFW(ST_INFO)(STB_LOCAL, STT_FILE), 0, + SHN_ABS, file->filename); ret = tcc_assemble_internal(s1, do_preprocess); @@ -1090,4 +1119,4 @@ ST_FUNC void asm_global_instr(void) cstr_free(&astr); } -#endif /* CONFIG_TCC_ASM */ \ No newline at end of file +#endif /* CONFIG_TCC_ASM */ diff --git a/tccelf.c b/tccelf.c index 9faf27f..f0ed22b 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1443,16 +1443,16 @@ ST_FUNC void tcc_add_runtime(TCCState *s1) #ifdef CONFIG_USE_LIBGCC if (!s1->static_link) { tcc_add_file(s1, TCC_LIBGCC); - tcc_add_support(s1, "libcrt.a"); + tcc_add_support(s1, "libtcc1.a"); } else - tcc_add_support(s1, "libcrt.a"); + tcc_add_support(s1, "libtcc1.a"); #else - tcc_add_support(s1, "libcrt.a"); + tcc_add_support(s1, "libtcc1.a"); #endif } /* tcc_add_bcheck tries to relocate a call to __bound_init in _init so - libcrt.a must be loaded before for __bound_init to be defined and + libtcc1.a must be loaded before for __bound_init to be defined and crtn.o must be loaded after to not finalize _init too early. */ tcc_add_bcheck(s1); @@ -1596,7 +1596,7 @@ ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel) put32(s1->got->data + offset, sym->st_value & 0xffffffff); } -/* Perform relocation to GOT or PLT entries */ +/* Perform relocation to GOT or PLT entries */ ST_FUNC void fill_got(TCCState *s1) { Section *s; @@ -2469,7 +2469,7 @@ static int elf_output_file(TCCState *s1, const char *filename) goto the_end; } - /* Perform relocation to GOT or PLT entries */ + /* Perform relocation to GOT or PLT entries */ if (file_type == TCC_OUTPUT_EXE && s1->static_link) fill_got(s1); diff --git a/tccgen.c b/tccgen.c index c2481b0..1a89d4a 100644 --- a/tccgen.c +++ b/tccgen.c @@ -28,7 +28,7 @@ rsym: return symbol anon_sym: anonymous symbol index */ -ST_DATA int rsym, anon_sym, ind, loc, ex_rc; +ST_DATA int rsym, anon_sym, ind, loc; ST_DATA Section *text_section, *data_section, *bss_section; /* predefined sections */ ST_DATA Section *cur_text_section; /* current section where function code is generated */ @@ -70,7 +70,6 @@ ST_DATA int func_var; /* true if current function is variadic (used by return in ST_DATA int func_vc; ST_DATA int last_line_num, last_ind, func_ind; /* debug last line number and pc */ ST_DATA char *funcname; -ST_DATA int pop_stack; ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; @@ -93,7 +92,7 @@ static int is_compatible_parameter_types(CType *type1, CType *type2); static void expr_type(CType *type); ST_FUNC void vpush64(int ty, unsigned long long v); ST_FUNC void vpush(CType *type); -ST_FUNC int gtst(int inv, int t); +ST_FUNC int gvtst(int inv, int t); ST_FUNC int is_btype_size(int bt); ST_INLN int is_float(int t) @@ -370,7 +369,7 @@ ST_FUNC void vpushi(int v) } /* push a pointer sized constant */ -ST_FUNC void vpushs(addr_t v) +static void vpushs(addr_t v) { CValue cval; cval.ptr_offset = v; @@ -520,46 +519,11 @@ ST_FUNC void vpushv(SValue *v) *vtop = *v; } -ST_FUNC void vdup(void) +static void vdup(void) { vpushv(vtop); } -static int align_size(int size) -{ -#ifdef TCC_TARGET_X86_64 - if(size > 4) - return 8; - else -#endif - if(size > 2) - return 4; - else if(size > 1) - return 2; - else - return 1; -} - -int loc_stack(int size, int is_sub){ - int l, align; - align = align_size(size); - size = (size + align - 1) & - align; - if(is_sub){ - pop_stack -= size; - if(pop_stack >= 0) - l = loc + pop_stack; - else{ - loc += pop_stack; - l = loc &= -align; - pop_stack = 0; - } - }else{ - pop_stack += size; - l = loc + pop_stack; - } - return l; -} - /* save r to the memory stack, and mark it as being free */ ST_FUNC void save_reg(int r) { @@ -568,76 +532,57 @@ ST_FUNC void save_reg(int r) CType *type; /* modify all stack values */ - l = saved = 0; - for(p = vstack; p <= vtop; p++) { -#ifdef TCC_TARGET_X86_64 - if ((p->r & VT_VALMASK) == r || - ((((p->type.t & VT_BTYPE) == VT_QLONG) || ((p->type.t & VT_BTYPE) == VT_QFLOAT)) && - ((p->r2 & VT_VALMASK) == r))) -#else - if ((p->r & VT_VALMASK) == r || ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) -#endif - { + saved = 0; + l = 0; + for(p=vstack;p<=vtop;p++) { + if ((p->r & VT_VALMASK) == r || + ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) { /* must save value on stack if not already done */ if (!saved) { /* NOTE: must reload 'r' because r might be equal to r2 */ r = p->r & VT_VALMASK; /* store register in the stack */ type = &p->type; - if((type->t & VT_BTYPE) == VT_STRUCT){ - int ret_align; - SValue ret; - gfunc_sret(type, func_var, &ret.type, &ret_align); - type = &ret.type; - } - if ((p->r & VT_LVAL) || ((type->t & VT_BTYPE) == VT_FUNC)) + if ((p->r & VT_LVAL) || + (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) #ifdef TCC_TARGET_X86_64 type = &char_pointer_type; #else type = &int_type; #endif - size = type_size(type, &align); - l = loc_stack(size, 1); + size = type_size(type, &align); + loc = (loc - size) & -align; + sv.type.t = type->t; sv.r = VT_LOCAL | VT_LVAL; - sv.c.ul = l; -#ifdef TCC_TARGET_X86_64 - if (((type->t & VT_BTYPE) == VT_QLONG) || ((type->t & VT_BTYPE) == VT_QFLOAT)) -#else - if ((type->t & VT_BTYPE) == VT_LLONG) -#endif - { -#ifdef TCC_TARGET_X86_64 - int load_size = 8, load_type = ((type->t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; -#else - int load_size = 4, load_type = VT_INT; -#endif - sv.type.t = load_type; - store(r, &sv); - sv.c.ul += load_size; - store(p->r2, &sv); - }else{ - sv.type.t = type->t; - store(r, &sv); - } + sv.c.ul = loc; + store(r, &sv); #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) /* x86 specific: need to pop fp register ST0 if saved */ if (r == TREG_ST0) { o(0xd8dd); /* fstp %st(0) */ } #endif - saved = 1; +#ifndef TCC_TARGET_X86_64 + /* special long long case */ + if ((type->t & VT_BTYPE) == VT_LLONG) { + sv.c.ul += 4; + store(p->r2, &sv); + } +#endif + l = loc; + saved = 1; + } + /* mark that stack entry as being saved on the stack */ + if (p->r & VT_LVAL) { + /* also clear the bounded flag because the + relocation address of the function was stored in + p->c.ul */ + p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL; + } else { + p->r = lvalue_type(p->type.t) | VT_LOCAL; } - /* mark that stack entry as being saved on the stack */ - if (p->r & VT_LVAL) { - /* also clear the bounded flag because the - relocation address of the function was stored in - p->c.ul */ - p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL; - } else { - p->r = lvalue_type(p->type.t) | VT_LOCAL | VT_TMP; - } - p->r2 = VT_CONST; - p->c.ul = l; + p->r2 = VT_CONST; + p->c.ul = l; } } } @@ -667,57 +612,42 @@ ST_FUNC int get_reg_ex(int rc, int rc2) } #endif -static int for_reg(int rc) -{ - int r; - SValue *p; - if(rc){ - for(r = 0; r < NB_REGS; r++) { - if (reg_classes[r] & rc) { - for(p = vstack; p <= vtop; p++) { - if ((p->r & VT_VALMASK) == r || (p->r2 & VT_VALMASK) == r) - goto notfound; - } - goto found; - } - notfound:; - } - } - r = -1; -found: - return r; -} - /* find a free register of class 'rc'. If none, save one register */ -int get_reg(int rc) +ST_FUNC int get_reg(int rc) { - int r; + int r; SValue *p; /* find a free register */ - r = for_reg(rc); - if (r != -1) - return r; + for(r=0;rr & VT_VALMASK) == r || + (p->r2 & VT_VALMASK) == r) + goto notfound; + } + return r; + } + notfound: ; + } + /* no register left : free the first one on the stack (VERY IMPORTANT to start from the bottom to ensure that we don't spill registers used in gen_opi()) */ - for(p = vstack; p <= vtop; p++) { + for(p=vstack;p<=vtop;p++) { /* look at second register (if long long) */ - if(p->r & VT_TMP) - continue; r = p->r2 & VT_VALMASK; if (r < VT_CONST && (reg_classes[r] & rc)) goto save_found; r = p->r & VT_VALMASK; if (r < VT_CONST && (reg_classes[r] & rc)) { -save_found: - save_reg(r); + save_found: + save_reg(r); return r; } } /* Should never comes here */ - assert(0); - return -1; + return -1; } /* save registers up to (vtop - n) stack entry */ @@ -751,14 +681,16 @@ static void move_reg(int r, int s, int t) } /* get address of vtop (vtop MUST BE an lvalue) */ -ST_FUNC void gaddrof(void) +static void gaddrof(void) { if (vtop->r & VT_REF) gv(RC_INT); vtop->r &= ~VT_LVAL; /* tricky: if saved lvalue, then we can go back to lvalue */ if ((vtop->r & VT_VALMASK) == VT_LLOCAL) - vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL| VT_TMP; + vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL; + + } #ifdef CONFIG_TCC_BCHECK @@ -794,28 +726,25 @@ static void gbound(void) register value (such as structures). */ ST_FUNC int gv(int rc) { - int r, bit_pos, bit_size, size, align, i, ft, sbt; + int r, bit_pos, bit_size, size, align, i; int rc2; - ft = vtop->type.t; - sbt = ft & VT_BTYPE; /* NOTE: get_reg can modify vstack[] */ - if (ft & VT_BITFIELD) { + if (vtop->type.t & VT_BITFIELD) { CType type; - int bits; - bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f; - bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f; + int bits = 32; + bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; + bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; /* remove bit field info to avoid loops */ - ft = vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); + vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); /* cast to int to propagate signedness in following ops */ - if (sbt == VT_LLONG) { + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { type.t = VT_LLONG; bits = 64; - } else{ + } else type.t = VT_INT; - bits = 32; - } - if((ft & VT_UNSIGNED) || sbt == VT_BOOL) + if((vtop->type.t & VT_UNSIGNED) || + (vtop->type.t & VT_BTYPE) == VT_BOOL) type.t |= VT_UNSIGNED; gen_cast(&type); /* generate shifts */ @@ -872,39 +801,41 @@ ST_FUNC int gv(int rc) gbound(); #endif - r = vtop->r & VT_VALMASK; - if(rc & ~RC_MASK) - rc2 = ex_rc; - else - rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT; + r = vtop->r & VT_VALMASK; + rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT; + if (rc == RC_IRET) + rc2 = RC_LRET; +#ifdef TCC_TARGET_X86_64 + else if (rc == RC_FRET) + rc2 = RC_QRET; +#endif /* need to reload if: - constant - lvalue (need to dereference pointer) - already a register, but not in the right class */ - if (r >= VT_CONST || (vtop->r & VT_LVAL) || !(reg_classes[r] & rc) + if (r >= VT_CONST + || (vtop->r & VT_LVAL) + || !(reg_classes[r] & rc) #ifdef TCC_TARGET_X86_64 - || (sbt == VT_QLONG && !(reg_classes[vtop->r2] & rc2)) - || (sbt == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2)) + || ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2)) + || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2)) #else - || (sbt == VT_LLONG && !(reg_classes[vtop->r2] & rc2)) + || ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2)) #endif - || vtop->c.i) + ) { r = get_reg(rc); #ifdef TCC_TARGET_X86_64 - if ((sbt == VT_QLONG) || (sbt == VT_QFLOAT)) -#else - if (sbt == VT_LLONG) -#endif - { -#ifdef TCC_TARGET_X86_64 - int load_size = 8, load_type = (sbt == VT_QLONG) ? VT_LLONG : VT_DOUBLE; + if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) { + int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; #else - int load_size = 4, load_type = VT_INT; + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + int addr_type = VT_INT, load_size = 4, load_type = VT_INT; unsigned long long ll; #endif - int r2; + int r2, original_type; + original_type = vtop->type.t; /* two register type load : expand to two words temporarily */ #ifndef TCC_TARGET_X86_64 @@ -917,19 +848,20 @@ ST_FUNC int gv(int rc) vpushi(ll >> 32); /* second word */ } else #endif - /* XXX: test to VT_CONST incorrect ? */ - if (r >= VT_CONST || (vtop->r & VT_LVAL)) { + if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */ + (vtop->r & VT_LVAL)) { /* We do not want to modifier the long long pointer here, so the safest (and less efficient) is to save all the other registers - in the regs. use VT_TMP XXX: totally inefficient. */ + in the stack. XXX: totally inefficient. */ + save_regs(1); /* load from memory */ vtop->type.t = load_type; load(r, vtop); vdup(); - vtop[-1].r = r | VT_TMP; /* lock register value */ + vtop[-1].r = r; /* save register value */ /* increment pointer to get second word */ - vtop->type = char_pointer_type; + vtop->type.t = addr_type; gaddrof(); vpushi(load_size); gen_op('+'); @@ -939,23 +871,23 @@ ST_FUNC int gv(int rc) /* move registers */ load(r, vtop); vdup(); - vtop[-1].r = r | VT_TMP; /* lock register value */ + vtop[-1].r = r; /* save register value */ vtop->r = vtop[-1].r2; } /* Allocate second register. Here we rely on the fact that get_reg() tries first to free r2 of an SValue. */ r2 = get_reg(rc2); load(r2, vtop); - vtop--; + vpop(); /* write second register */ vtop->r2 = r2; - vtop->r &= ~VT_TMP; - vtop->type.t = ft; - } else if ((vtop->r & VT_LVAL) && !is_float(ft)) { - int t; + vtop->type.t = original_type; + } else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) { + int t1, t; /* lvalue of scalar type : need to use lvalue type because of possible cast */ - t = ft; + t = vtop->type.t; + t1 = t; /* compute memory access type */ if (vtop->r & VT_REF) #ifdef TCC_TARGET_X86_64 @@ -972,14 +904,13 @@ ST_FUNC int gv(int rc) vtop->type.t = t; load(r, vtop); /* restore wanted type */ - vtop->type.t = ft; + vtop->type.t = t1; } else { /* one register type load */ load(r, vtop); } - vtop->r = r; - vtop->c.ptr_offset = 0; } + vtop->r = r; #ifdef TCC_TARGET_C67 /* uses register pairs for doubles */ if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) @@ -992,10 +923,13 @@ ST_FUNC int gv(int rc) /* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */ ST_FUNC void gv2(int rc1, int rc2) { + int v; + /* generate more generic register first. But VT_JMP or VT_CMP values must be generated first in all cases to avoid possible reload errors */ - if (rc1 <= rc2) { + v = vtop[0].r & VT_VALMASK; + if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) { vswap(); gv(rc1); vswap(); @@ -1025,7 +959,6 @@ static int rc_fret(int t) if (t == VT_LDOUBLE) { return RC_ST0; } - ex_rc = RC_QRET; #endif return RC_FRET; } @@ -1084,7 +1017,6 @@ ST_FUNC void lexpand_nr(void) } #endif -#ifndef TCC_TARGET_X86_64 /* build a long long from two ints */ static void lbuild(int t) { @@ -1093,7 +1025,6 @@ static void lbuild(int t) vtop[-1].type.t = t; vpop(); } -#endif /* rotate n first stack elements to the bottom I1 ... In -> I2 ... In I1 [top is right] @@ -1134,9 +1065,8 @@ ST_FUNC void vrott(int n) /* pop stack value */ ST_FUNC void vpop(void) { - int v, fr; - fr = vtop->r; - v = fr & VT_VALMASK; + int v; + v = vtop->r & VT_VALMASK; #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) /* for x86, we need to pop the FP stack */ if (v == TREG_ST0 && !nocode_wanted) { @@ -1147,17 +1077,6 @@ ST_FUNC void vpop(void) /* need to put correct jump if && or || without test */ gsym(vtop->c.ul); } - if(fr & VT_TMP){ - int size, align; - SValue ret; - if((vtop->type.t & VT_BTYPE) == VT_FUNC) - size = 8; - else{ - gfunc_sret(&vtop->type, func_var, &ret.type, &align); - size = type_size(&ret.type, &align); - } - loc_stack(size, 0); - } vtop--; } @@ -1167,8 +1086,8 @@ static void gv_dup(void) { int rc, t, r, r1; SValue sv; + t = vtop->type.t; -#ifndef TCC_TARGET_X86_64 if ((t & VT_BTYPE) == VT_LLONG) { lexpand(); gv_dup(); @@ -1178,14 +1097,15 @@ static void gv_dup(void) vrotb(4); /* stack: H L L1 H1 */ lbuild(t); - vrott(3); + vrotb(3); + vrotb(3); vswap(); lbuild(t); vswap(); - } else -#endif - { + } else { /* duplicate value */ + rc = RC_INT; + sv.type.t = VT_INT; if (is_float(t)) { rc = RC_FLOAT; #ifdef TCC_TARGET_X86_64 @@ -1193,9 +1113,8 @@ static void gv_dup(void) rc = RC_ST0; } #endif - }else - rc = RC_INT; - sv.type.t = t; + sv.type.t = t; + } r = gv(rc); r1 = get_reg(rc); sv.r = r; @@ -1207,6 +1126,27 @@ static void gv_dup(void) vtop->r = r1; } } + +/* Generate value test + * + * Generate a test for any value (jump, comparison and integers) */ +ST_FUNC int gvtst(int inv, int t) +{ + int v = vtop->r & VT_VALMASK; + if (v != VT_CMP && v != VT_JMP && v != VT_JMPI) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + vtop--; + return t; + } + return gtst(inv, t); +} + #ifndef TCC_TARGET_X86_64 /* generate CPU independent (unsigned) long long operations */ static void gen_opl(int op) @@ -1405,13 +1345,13 @@ static void gen_opl(int op) b = 0; gen_op(op1); if (op1 != TOK_NE) { - a = gtst(1, 0); + a = gvtst(1, 0); } if (op != TOK_EQ) { /* generate non equal test */ /* XXX: NOT PORTABLE yet */ if (a == 0) { - b = gtst(0, 0); + b = gvtst(0, 0); } else { #if defined(TCC_TARGET_I386) b = psym(0x850f, 0); @@ -1436,7 +1376,7 @@ static void gen_opl(int op) else if (op1 == TOK_GE) op1 = TOK_UGE; gen_op(op1); - a = gtst(1, a); + a = gvtst(1, a); gsym(b); vseti(VT_JMPI, a); break; @@ -2161,30 +2101,31 @@ ST_FUNC int type_size(CType *type, int *a) { Sym *s; int bt; - size_t size; bt = type->t & VT_BTYPE; if (bt == VT_STRUCT) { - assert(!(type->t & VT_VLS)); /* struct/union */ s = type->ref; *a = s->r; - size = s->c; + return s->c; } else if (bt == VT_PTR) { if (type->t & VT_ARRAY) { int ts; + s = type->ref; ts = type_size(&s->type, a); + if (ts < 0 && s->c < 0) ts = -ts; - size = (size_t)ts * s->c; + + return ts * s->c; } else { *a = PTR_SIZE; - size = PTR_SIZE; + return PTR_SIZE; } } else if (bt == VT_LDOUBLE) { *a = LDOUBLE_ALIGN; - size = LDOUBLE_SIZE; + return LDOUBLE_SIZE; } else if (bt == VT_DOUBLE || bt == VT_LLONG) { #ifdef TCC_TARGET_I386 #ifdef TCC_TARGET_PE @@ -2201,23 +2142,21 @@ ST_FUNC int type_size(CType *type, int *a) #else *a = 8; #endif - size = 8; + return 8; } else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) { *a = 4; - size = 4; + return 4; } else if (bt == VT_SHORT) { *a = 2; - size = 2; - } else if (bt == VT_QLONG || bt == VT_QFLOAT) { + return 2; + } else if (bt == VT_QLONG || bt == VT_QFLOAT) { *a = 8; - size = 16; + return 16; } else { /* char, void, function, _Bool */ *a = 1; - size = 1; + return 1; } - assert(size == (int)size); - return (int)size; } /* push type size as known at runtime time on top of value stack. Put @@ -2233,7 +2172,7 @@ ST_FUNC void vla_runtime_type_size(CType *type, int *a) static void vla_sp_save(void) { if (!(vla_flags & VLA_SP_LOC_SET)) { - *vla_sp_loc = loc_stack(PTR_SIZE, 1); + *vla_sp_loc = (loc -= PTR_SIZE); vla_flags |= VLA_SP_LOC_SET; } if (!(vla_flags & VLA_SP_SAVED)) { @@ -2502,8 +2441,6 @@ static void gen_assign_cast(CType *dt) if (sbt == VT_PTR || sbt == VT_FUNC) { tcc_warning("assignment makes integer from pointer without a cast"); } - if (sbt == VT_STRUCT) - goto error; /* XXX: more tests */ break; case VT_STRUCT: @@ -2523,78 +2460,17 @@ static void gen_assign_cast(CType *dt) gen_cast(dt); } -static void vstore_im(){ - int rc, ft, sbt, dbt, t, r; - ft = vtop[-1].type.t; - sbt = vtop->type.t & VT_BTYPE; - dbt = ft & VT_BTYPE; - if (is_float(ft)) { - rc = RC_FLOAT; -#ifdef TCC_TARGET_X86_64 - if (dbt == VT_LDOUBLE) { - rc = RC_ST0; - } -#endif - }else - rc = RC_INT; - r = gv(rc); /* generate value */ - /* if lvalue was saved on stack, must read it */ - if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { - SValue sv; - t = get_reg(RC_INT); -#ifdef TCC_TARGET_X86_64 - sv.type.t = VT_PTR; -#else - sv.type.t = VT_INT; -#endif - sv.r = VT_LOCAL | VT_LVAL | VT_TMP; - sv.c.ul = vtop[-1].c.ul; - load(t, &sv); - vtop[-1].r = t | VT_LVAL; - vtop[-1].c.ul = 0; - } - /* two word case handling : store second register at word + 4 */ -#ifdef TCC_TARGET_X86_64 - if ((dbt == VT_QLONG) || (dbt == VT_QFLOAT)) -#else - if (dbt == VT_LLONG) -#endif - { -#ifdef TCC_TARGET_X86_64 - int load_size = 8, load_type = (sbt == VT_QLONG) ? VT_LLONG : VT_DOUBLE; -#else - int load_size = 4, load_type = VT_INT; -#endif - vtop[-1].type.t = load_type; - store(r, vtop - 1); - vswap(); - /* convert to int to increment easily */ - vtop->type = char_pointer_type; - gaddrof(); - vpushi(load_size); - gen_op('+'); - vtop->r |= VT_LVAL; - vswap(); - vtop[-1].type.t = load_type; - /* XXX: it works because r2 is spilled last ! */ - store(vtop->r2, vtop - 1); - vtop->type.t = ft; - vtop[-1].type.t = ft; - } else { - store(r, vtop - 1); - } -} - /* store vtop in lvalue pushed on stack */ ST_FUNC void vstore(void) { - int sbt, dbt, ft, size, align, bit_size, bit_pos, delayed_cast; + int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast; ft = vtop[-1].type.t; sbt = vtop->type.t & VT_BTYPE; dbt = ft & VT_BTYPE; if ((((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || - (sbt == VT_INT && dbt == VT_SHORT)) && !(vtop->type.t & VT_BITFIELD)) { + (sbt == VT_INT && dbt == VT_SHORT)) + && !(vtop->type.t & VT_BITFIELD)) { /* optimize char/short casts */ delayed_cast = VT_MUSTCAST; vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT))); @@ -2612,42 +2488,31 @@ ST_FUNC void vstore(void) /* structure assignment : generate memcpy */ /* XXX: optimize if small size */ if (!nocode_wanted) { - SValue ret; - int ret_nregs, ret_align; - ret_nregs = gfunc_sret(&vtop->type, func_var, &ret.type, &ret_align); - if(ret_nregs){ - vswap(); - vpushv(vtop - 1); - vtop[0].type = ret.type; - vtop[-1].type = ret.type; - vstore_im(); - vtop -=2; - }else{ - size = type_size(&vtop->type, &align); - /* destination */ - vswap(); - vtop->type.t = VT_PTR; - gaddrof(); + size = type_size(&vtop->type, &align); + + /* destination */ + vswap(); + vtop->type.t = VT_PTR; + gaddrof(); - /* address of memcpy() */ + /* address of memcpy() */ #ifdef TCC_ARM_EABI - if(!(align & 7)) - vpush_global_sym(&func_old_type, TOK_memcpy8); - else if(!(align & 3)) - vpush_global_sym(&func_old_type, TOK_memcpy4); - else + if(!(align & 7)) + vpush_global_sym(&func_old_type, TOK_memcpy8); + else if(!(align & 3)) + vpush_global_sym(&func_old_type, TOK_memcpy4); + else #endif - vpush_global_sym(&func_old_type, TOK_memcpy); + vpush_global_sym(&func_old_type, TOK_memcpy); - vswap(); - /* source */ - vpushv(vtop - 2); - vtop->type.t = VT_PTR; - gaddrof(); - /* type size */ - vpushi(size); - gfunc_call(3); - } + vswap(); + /* source */ + vpushv(vtop - 2); + vtop->type.t = VT_PTR; + gaddrof(); + /* type size */ + vpushi(size); + gfunc_call(3); } else { vswap(); vpop(); @@ -2659,8 +2524,13 @@ ST_FUNC void vstore(void) bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f; /* remove bit field info to avoid loops */ vtop[-1].type.t = ft & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); + /* duplicate source into other register */ - if(dbt == VT_BOOL) { + gv_dup(); + vswap(); + vrott(3); + + if((ft & VT_BTYPE) == VT_BOOL) { gen_cast(&vtop[-1].type); vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED); } @@ -2670,8 +2540,8 @@ ST_FUNC void vstore(void) vtop[-1] = vtop[-2]; /* mask and shift source */ - if(dbt != VT_BOOL) { - if(dbt == VT_LLONG) { + if((ft & VT_BTYPE) != VT_BOOL) { + if((ft & VT_BTYPE) == VT_LLONG) { vpushll((1ULL << bit_size) - 1ULL); } else { vpushi((1 << bit_size) - 1); @@ -2682,7 +2552,7 @@ ST_FUNC void vstore(void) gen_op(TOK_SHL); /* load destination, mask and or with source */ vswap(); - if(dbt == VT_LLONG) { + if((ft & VT_BTYPE) == VT_LLONG) { vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos)); } else { vpushi(~(((1 << bit_size) - 1) << bit_pos)); @@ -2691,6 +2561,10 @@ ST_FUNC void vstore(void) gen_op('|'); /* store result */ vstore(); + + /* pop off shifted source from "duplicate source..." above */ + vpop(); + } else { #ifdef CONFIG_TCC_BCHECK /* bound check case */ @@ -2701,7 +2575,56 @@ ST_FUNC void vstore(void) } #endif if (!nocode_wanted) { - vstore_im(); + rc = RC_INT; + if (is_float(ft)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } else if ((ft & VT_BTYPE) == VT_QFLOAT) { + rc = RC_FRET; + } +#endif + } + r = gv(rc); /* generate value */ + /* if lvalue was saved on stack, must read it */ + if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { + SValue sv; + t = get_reg(RC_INT); +#ifdef TCC_TARGET_X86_64 + sv.type.t = VT_PTR; +#else + sv.type.t = VT_INT; +#endif + sv.r = VT_LOCAL | VT_LVAL; + sv.c.ul = vtop[-1].c.ul; + load(t, &sv); + vtop[-1].r = t | VT_LVAL; + } + /* two word case handling : store second register at word + 4 (or +8 for x86-64) */ +#ifdef TCC_TARGET_X86_64 + if (((ft & VT_BTYPE) == VT_QLONG) || ((ft & VT_BTYPE) == VT_QFLOAT)) { + int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE; +#else + if ((ft & VT_BTYPE) == VT_LLONG) { + int addr_type = VT_INT, load_size = 4, load_type = VT_INT; +#endif + vtop[-1].type.t = load_type; + store(r, vtop - 1); + vswap(); + /* convert to int to increment easily */ + vtop->type.t = addr_type; + gaddrof(); + vpushi(load_size); + gen_op('+'); + vtop->r |= VT_LVAL; + vswap(); + vtop[-1].type.t = load_type; + /* XXX: it works because r2 is spilled last ! */ + store(vtop->r2, vtop - 1); + } else { + store(r, vtop - 1); + } } vswap(); vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ @@ -3364,7 +3287,7 @@ static void asm_label_instr(CString *astr) static void post_type(CType *type, AttributeDef *ad) { - int n, l, t1, arg_size, size, align; + int n, l, t1, arg_size, align; Sym **plast, *s, *first; AttributeDef ad1; CType pt; @@ -3468,12 +3391,13 @@ static void post_type(CType *type, AttributeDef *ad) t1 |= type->t & VT_VLA; if (t1 & VT_VLA) { - size = type_size(&int_type, &align); - n = loc_stack(size, 1); + loc -= type_size(&int_type, &align); + loc &= -align; + n = loc; vla_runtime_type_size(type, &align); gen_op('*'); - vset(&int_type, VT_LOCAL|VT_LVAL, n); + vset(&int_type, VT_LOCAL|VT_LVAL, loc); vswap(); vstore(); } @@ -3835,7 +3759,7 @@ ST_FUNC void unary(void) vtop->c.i = vtop->c.i ^ 1; else { save_regs(1); - vseti(VT_JMP, gtst(1, 0)); + vseti(VT_JMP, gvtst(1, 0)); } break; case '~': @@ -4069,94 +3993,47 @@ ST_FUNC void unary(void) /* post operations */ while (1) { - SValue ret; - int ret_nregs, ret_align; if (tok == TOK_INC || tok == TOK_DEC) { inc(1, tok); next(); } else if (tok == '.' || tok == TOK_ARROW) { - int qualifiers, add, is_lval; + int qualifiers; /* field */ - qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE); - add = 0; - if (tok == TOK_ARROW) - indir(); - - type = vtop->type; - is_lval = (vtop->r & (VT_VALMASK | VT_LVAL)) >= VT_CONST; - if(is_lval){ - test_lvalue(); - gaddrof(); - vtop->type = char_pointer_type; /* change type to 'char *' */ - }else - gfunc_sret(&vtop->type, func_var, &ret.type, &ret_align); - do{ - next(); - /* expect pointer on structure */ - if ((type.t & VT_BTYPE) != VT_STRUCT) - expect("struct or union"); - s = type.ref; - /* find field */ - tok |= SYM_FIELD; - while ((s = s->next) != NULL) { - if (s->v == tok) - break; - } - if (!s) - tcc_error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, NULL)); - /* add bit */ - add += s->c; - /* change type to field type, and set to lvalue */ - type = s->type; - next(); - }while(tok == '.'); - - type.t |= qualifiers; - if (is_lval){ - p_lval: - vpushi(add); - gen_op('+'); - /* an array is never an lvalue */ - if (!(type.t & VT_ARRAY)) { - vtop->r |= lvalue_type(type.t); - #ifdef CONFIG_TCC_BCHECK - /* if bound checking, the referenced pointer must be checked */ - if (tcc_state->do_bounds_check) - vtop->r |= VT_MUSTBOUND; - #endif - } - }else{ - gfunc_sret(&vtop->type, func_var, &ret.type, &ret_align); - if(is_float(ret.type.t) || (type.t & VT_ARRAY)){ -#ifdef TCC_TARGET_X86_64 - if((ret.type.t & VT_BTYPE) != VT_LDOUBLE) -#endif - { - save_reg(vtop->r); - vtop->r &= ~VT_TMP; - gaddrof(); - vtop->type = char_pointer_type; /* change type to 'char *' */ - goto p_lval; - } - }else{ -#ifdef TCC_TARGET_X86_64 - int load_size = 8; -#else - int load_size = 4; + if (tok == TOK_ARROW) + indir(); + qualifiers = vtop->type.t & (VT_CONSTANT | VT_VOLATILE); + test_lvalue(); + gaddrof(); + next(); + /* expect pointer on structure */ + if ((vtop->type.t & VT_BTYPE) != VT_STRUCT) + expect("struct or union"); + s = vtop->type.ref; + /* find field */ + tok |= SYM_FIELD; + while ((s = s->next) != NULL) { + if (s->v == tok) + break; + } + if (!s) + tcc_error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, NULL)); + /* add field offset to pointer */ + vtop->type = char_pointer_type; /* change type to 'char *' */ + vpushi(s->c); + gen_op('+'); + /* change type to field type, and set to lvalue */ + vtop->type = s->type; + vtop->type.t |= qualifiers; + /* an array is never an lvalue */ + if (!(vtop->type.t & VT_ARRAY)) { + vtop->r |= lvalue_type(vtop->type.t); +#ifdef CONFIG_TCC_BCHECK + /* if bound checking, the referenced pointer must be checked */ + if (tcc_state->do_bounds_check) + vtop->r |= VT_MUSTBOUND; #endif - if(add & load_size){ - add -= load_size; - vtop->r = vtop->r2; - vtop->r2 = VT_CONST; - } - if(add){ - vtop->type.t = VT_LLONG; - vpushi(add*8); - gen_op(TOK_SAR); - } - } - } - vtop->type = type; + } + next(); } else if (tok == '[') { next(); gexpr(); @@ -4164,8 +4041,9 @@ ST_FUNC void unary(void) indir(); skip(']'); } else if (tok == '(') { + SValue ret; Sym *sa; - int nb_args, variadic, addr; + int nb_args, ret_nregs, ret_align, variadic; /* function call */ if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { @@ -4183,6 +4061,7 @@ ST_FUNC void unary(void) } /* get return type */ s = vtop->type.ref; + next(); sa = s->next; /* first parameter */ nb_args = 0; ret.r2 = VT_CONST; @@ -4194,12 +4073,12 @@ ST_FUNC void unary(void) if (!ret_nregs) { /* get some space for the returned structure */ size = type_size(&s->type, &align); - addr = loc_stack(size, 1); + loc = (loc - size) & -align; ret.type = s->type; ret.r = VT_LOCAL | VT_LVAL; /* pass it as 'int' to avoid structure arg passing problems */ - vseti(VT_LOCAL, addr); + vseti(VT_LOCAL, loc); ret.c = vtop->c; nb_args++; } @@ -4227,7 +4106,6 @@ ST_FUNC void unary(void) } ret.c.i = 0; } - next(); if (tok != ')') { for(;;) { expr_eq(); @@ -4256,8 +4134,25 @@ ST_FUNC void unary(void) } /* handle packed struct return */ - if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) - vtop->type = s->type; + if (((s->type.t & VT_BTYPE) == VT_STRUCT) && ret_nregs) { + int addr, offset; + + size = type_size(&s->type, &align); + loc = (loc - size) & -align; + addr = loc; + offset = 0; + for (;;) { + vset(&ret.type, VT_LOCAL | VT_LVAL, addr + offset); + vswap(); + vstore(); + vtop--; + if (--ret_nregs == 0) + break; + /* XXX: compatible with arm only: ret_align == register_size */ + offset += ret_align; + } + vset(&s->type, VT_LOCAL | VT_LVAL, addr); + } } else { break; } @@ -4392,7 +4287,7 @@ static void expr_land(void) t = 0; save_regs(1); for(;;) { - t = gtst(1, t); + t = gvtst(1, t); if (tok != TOK_LAND) { vseti(VT_JMPI, t); break; @@ -4412,7 +4307,7 @@ static void expr_lor(void) t = 0; save_regs(1); for(;;) { - t = gtst(0, t); + t = gvtst(0, t); if (tok != TOK_LOR) { vseti(VT_JMP, t); break; @@ -4426,8 +4321,8 @@ static void expr_lor(void) /* XXX: better constant handling */ static void expr_cond(void) { - int tt, u, r, rc, t1, t2, bt1, bt2, ret_nregs, ret_align; - SValue sv, ret; + int tt, u, r1, r2, rc, t1, t2, bt1, bt2; + SValue sv; CType type, type1, type2; if (const_wanted) { @@ -4469,13 +4364,14 @@ static void expr_cond(void) } else rc = RC_INT; - save_regs(1); + gv(rc); + save_regs(1); } if (tok == ':' && gnu_ext) { gv_dup(); - tt = gtst(1, 0); + tt = gvtst(1, 0); } else { - tt = gtst(1, 0); + tt = gvtst(1, 0); gexpr(); } type1 = vtop->type; @@ -4508,14 +4404,16 @@ static void expr_cond(void) (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) type.t |= VT_UNSIGNED; } else if (bt1 == VT_PTR || bt2 == VT_PTR) { - /* If one is a null ptr constant the result type is the other. */ - if (is_null_pointer (vtop)) - type = type1; - else if (is_null_pointer (&sv)) - type = type2; - /* XXX: test pointer compatibility, C99 has more elaborate rules here. */ - else - type = type1; + /* If one is a null ptr constant the result type + is the other. */ + if (is_null_pointer (vtop)) + type = type1; + else if (is_null_pointer (&sv)) + type = type2; + /* XXX: test pointer compatibility, C99 has more elaborate + rules here. */ + else + type = type1; } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { /* XXX: test function pointer compatibility */ type = bt1 == VT_FUNC ? type1 : type2; @@ -4533,35 +4431,26 @@ static void expr_cond(void) (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) type.t |= VT_UNSIGNED; } - + /* now we convert second operand */ gen_cast(&type); - ret_nregs = 0; - if (VT_STRUCT == (type.t & VT_BTYPE)){ - ret_nregs = gfunc_sret(&type, func_var, &ret.type, &ret_align); - if(ret_nregs) - vtop->type = ret.type; - else - gaddrof(); - } - - if (is_float(vtop->type.t)) { + if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + rc = RC_INT; + if (is_float(type.t)) { rc = RC_FLOAT; #ifdef TCC_TARGET_X86_64 if ((type.t & VT_BTYPE) == VT_LDOUBLE) { rc = RC_ST0; } #endif - } else - rc = RC_INT; - r = gv(rc); - rc = reg_classes[r] & ~RC_MASK; -#ifdef TCC_TARGET_X86_64 - if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) -#else - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) -#endif - ex_rc = reg_classes[vtop->r2] & ~RC_MASK; + } else if ((type.t & VT_BTYPE) == VT_LLONG) { + /* for long longs, we use fixed registers to avoid having + to handle a complicated move */ + rc = RC_IRET; + } + + r2 = gv(rc); /* this is horrible, but we must also convert first operand */ tt = gjmp(0); @@ -4569,21 +4458,12 @@ static void expr_cond(void) /* put again first value and cast it */ *vtop = sv; gen_cast(&type); - if (VT_STRUCT == (type.t & VT_BTYPE)){ - if(ret_nregs) - vtop->type = ret.type; - else - gaddrof(); - } - gv(rc); + if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + r1 = gv(rc); + move_reg(r2, r1, type.t); + vtop->r = r2; gsym(tt); - - if (VT_STRUCT == (type.t & VT_BTYPE)){ - if(ret_nregs) - vtop->type = type; - else - vtop->r |= VT_LVAL; - } } } } @@ -4737,7 +4617,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, skip('('); gexpr(); skip(')'); - a = gtst(1, 0); + a = gvtst(1, 0); block(bsym, csym, case_sym, def_sym, case_reg, 0); c = tok; if (c == TOK_ELSE) { @@ -4754,7 +4634,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, skip('('); gexpr(); skip(')'); - a = gtst(1, 0); + a = gvtst(1, 0); b = 0; block(&a, &b, case_sym, def_sym, case_reg, 0); gjmp_addr(d); @@ -4771,7 +4651,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, frame_bottom->next = scope_stack_bottom; scope_stack_bottom = frame_bottom; llabel = local_label_stack; - + /* save VLA state */ block_vla_sp_loc = *(saved_vla_sp_loc = vla_sp_loc); if (saved_vla_sp_loc != &vla_sp_root_loc) @@ -4823,7 +4703,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, /* pop locally defined symbols */ scope_stack_bottom = scope_stack_bottom->next; sym_pop(&local_stack, s); - + /* Pop VLA frames and restore stack pointer if required */ if (saved_vla_sp_loc != &vla_sp_root_loc) *saved_vla_sp_loc = block_vla_sp_loc; @@ -4856,23 +4736,32 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, vstore(); } else { /* returning structure packed into registers */ - int rc; + int r, size, addr, align; + size = type_size(&func_vt,&align); + if ((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & (ret_align-1))) + && (align & (ret_align-1))) { + loc = (loc - size) & -align; + addr = loc; + type = func_vt; + vset(&type, VT_LOCAL | VT_LVAL, addr); + vswap(); + vstore(); + vset(&ret_type, VT_LOCAL | VT_LVAL, addr); + } vtop->type = ret_type; if (is_float(ret_type.t)) - rc = rc_fret(ret_type.t); - else{ - rc = RC_IRET; - ex_rc = RC_LRET; - } + r = rc_fret(ret_type.t); + else + r = RC_IRET; for (;;) { - gv(rc); + gv(r); if (--ret_nregs == 0) break; /* We assume that when a structure is returned in multiple registers, their classes are consecutive values of the suite s(n) = 2^n */ - rc <<= 1; + r <<= 1; /* XXX: compatible with arm only: ret_align == register_size */ vtop->c.i += ret_align; vtop->r = VT_LOCAL | VT_LVAL; @@ -4924,7 +4813,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, b = 0; if (tok != ';') { gexpr(); - a = gtst(1, 0); + a = gvtst(1, 0); } skip(';'); if (tok != ')') { @@ -4953,7 +4842,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym, skip('('); gsym(b); gexpr(); - c = gtst(0, 0); + c = gvtst(0, 0); gsym_addr(c, d); skip(')'); gsym(a); @@ -5698,10 +5587,11 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, sec = NULL; #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) { - loc_stack(1, 1); + loc--; } #endif - addr = loc_stack(size, 1); + loc = (loc - size) & -align; + addr = loc; #ifdef CONFIG_TCC_BCHECK /* handles bounds */ /* XXX: currently, since we do only one pass, we cannot track @@ -5709,7 +5599,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) { unsigned long *bounds_ptr; /* add padding between regions */ - loc_stack(1, 1); + loc--; /* then add local bound info */ bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(unsigned long)); bounds_ptr[0] = addr; diff --git a/tccpe.c b/tccpe.c index f7ef99e..b972d75 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1773,7 +1773,7 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe) if (0 == s1->nostdlib) { static const char *libs[] = { - "libcrt.a", "msvcrt", "kernel32", "", "user32", "gdi32", NULL + "libtcc1.a", "msvcrt", "kernel32", "", "user32", "gdi32", NULL }; const char **pp, *p; for (pp = libs; 0 != (p = *pp); ++pp) { diff --git a/tccpp.c b/tccpp.c index 538f671..732c5ea 100644 --- a/tccpp.c +++ b/tccpp.c @@ -233,10 +233,7 @@ static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len) ts = tcc_malloc(sizeof(TokenSym) + len); table_ident[i] = ts; ts->tok = tok_ident++; - ts->sym_define.data = tcc_malloc(sizeof(Sym**)); - ts->sym_define.off = 0; - ts->sym_define.data[0] = NULL; - ts->sym_define.size = 1; + ts->sym_define = NULL; ts->sym_label = NULL; ts->sym_struct = NULL; ts->sym_identifier = NULL; @@ -1055,62 +1052,52 @@ static int macro_is_equal(const int *a, const int *b) ST_INLN void define_push(int v, int macro_type, int *str, Sym *first_arg) { Sym *s; - CSym *def; + s = define_find(v); if (s && !macro_is_equal(s->d, str)) tcc_warning("%s redefined", get_tok_str(v, NULL)); + s = sym_push2(&define_stack, v, macro_type, 0); s->d = str; s->next = first_arg; - def = &table_ident[v - TOK_IDENT]->sym_define; - def->data[def->off] = s; + table_ident[v - TOK_IDENT]->sym_define = s; } /* undefined a define symbol. Its name is just set to zero */ ST_FUNC void define_undef(Sym *s) { int v; - CSym *def; - v = s->v - TOK_IDENT; - if ((unsigned)v < (unsigned)(tok_ident - TOK_IDENT)){ - def = &table_ident[v]->sym_define; - def->data[def->off] = NULL; - } + v = s->v; + if (v >= TOK_IDENT && v < tok_ident) + table_ident[v - TOK_IDENT]->sym_define = NULL; + s->v = 0; } ST_INLN Sym *define_find(int v) { - CSym *def; v -= TOK_IDENT; if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) return NULL; - def = &table_ident[v]->sym_define; - return def->data[def->off]; + return table_ident[v]->sym_define; } /* free define stack until top reaches 'b' */ ST_FUNC void free_defines(Sym *b) { - Sym *top, *tmp; + Sym *top, *top1; int v; - CSym *def; top = define_stack; while (top != b) { - tmp = top->prev; + top1 = top->prev; /* do not free args or predefined defines */ if (top->d) tok_str_free(top->d); - v = top->v - TOK_IDENT; - if ((unsigned)v < (unsigned)(tok_ident - TOK_IDENT)){ - def = &table_ident[v]->sym_define; - if(def->off) - def->off = 0; - if(def->data[0]) - def->data[0] = NULL; - } + v = top->v; + if (v >= TOK_IDENT && v < tok_ident) + table_ident[v - TOK_IDENT]->sym_define = NULL; sym_free(top); - top = tmp; + top = top1; } define_stack = b; } @@ -1351,18 +1338,66 @@ static inline void add_cached_include(TCCState *s1, const char *filename, int if s1->cached_includes_hash[h] = s1->nb_cached_includes; } +static void pragma_parse(TCCState *s1) +{ + int val; + + next(); + if (tok == TOK_pack) { + /* + This may be: + #pragma pack(1) // set + #pragma pack() // reset to default + #pragma pack(push,1) // push & set + #pragma pack(pop) // restore previous + */ + next(); + skip('('); + if (tok == TOK_ASM_pop) { + next(); + if (s1->pack_stack_ptr <= s1->pack_stack) { + stk_error: + tcc_error("out of pack stack"); + } + s1->pack_stack_ptr--; + } else { + val = 0; + if (tok != ')') { + if (tok == TOK_ASM_push) { + next(); + if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1) + goto stk_error; + s1->pack_stack_ptr++; + skip(','); + } + if (tok != TOK_CINT) { + pack_error: + tcc_error("invalid pack pragma"); + } + val = tokc.i; + if (val < 1 || val > 16 || (val & (val - 1)) != 0) + goto pack_error; + next(); + } + *s1->pack_stack_ptr = val; + skip(')'); + } + } +} + /* is_bof is true if first non space token at beginning of file */ ST_FUNC void preprocess(int is_bof) { TCCState *s1 = tcc_state; int i, c, n, saved_parse_flags; - uint8_t buf[1024], *p; + char buf[1024], *q; Sym *s; saved_parse_flags = parse_flags; - parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | PARSE_FLAG_LINEFEED; + parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | + PARSE_FLAG_LINEFEED; next_nomacro(); -redo: + redo: switch(tok) { case TOK_DEFINE: next_nomacro(); @@ -1385,21 +1420,19 @@ redo: goto read_name; } else if (ch == '\"') { c = ch; -read_name: + read_name: inp(); - p = buf; + q = buf; while (ch != c && ch != '\n' && ch != CH_EOF) { - if ((p - buf) < sizeof(buf) - 1) - *p++ = ch; + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; if (ch == '\\') { if (handle_stray_noerror() == 0) - --p; + --q; } else inp(); } - if (ch != c) - goto include_syntax; - *p = '\0'; + *q = '\0'; minp(); #if 0 /* eat all spaces and comments after include */ @@ -1437,8 +1470,6 @@ read_name: c = '>'; } } - if(!buf[0]) - tcc_error(" empty filename in #include"); if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE) tcc_error("#include recursion too deep"); @@ -1505,7 +1536,8 @@ include_trynext: printf("%s: including %s\n", file->prev->filename, file->filename); #endif /* update target deps */ - dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, tcc_strdup(buf1)); + dynarray_add((void ***)&s1->target_deps, &s1->nb_target_deps, + tcc_strdup(buf1)); /* push current file in stack */ ++s1->include_stack_ptr; /* add include file debug info */ @@ -1538,7 +1570,7 @@ include_done: file->ifndef_macro = tok; } } - c = !!define_find(tok) ^ c; + c = (define_find(tok) != 0) ^ c; do_if: if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE) tcc_error("memory full (ifdef)"); @@ -1562,12 +1594,12 @@ include_done: goto skip; c = expr_preprocess(); s1->ifdef_stack_ptr[-1] = c; -test_else: + test_else: if (s1->ifdef_stack_ptr == file->ifdef_stack_ptr + 1) file->ifndef_macro = 0; -test_skip: + test_skip: if (!(c & 1)) { -skip: + skip: preprocess_skip(); is_bof = 0; goto redo; @@ -1585,11 +1617,11 @@ skip: /* need to set to zero to avoid false matches if another #ifndef at middle of file */ file->ifndef_macro = 0; + while (tok != TOK_LINEFEED) + next_nomacro(); tok_flags |= TOK_FLAG_ENDIF; + goto the_end; } - next_nomacro(); - if (tok != TOK_LINEFEED) - tcc_warning("Ignoring: %s", get_tok_str(tok, &tokc)); break; case TOK_LINE: next(); @@ -1600,7 +1632,8 @@ skip: if (tok != TOK_LINEFEED) { if (tok != TOK_STR) tcc_error("#line"); - pstrcpy(file->filename, sizeof(file->filename), (char *)tokc.cstr->data); + pstrcpy(file->filename, sizeof(file->filename), + (char *)tokc.cstr->data); } break; case TOK_ERROR: @@ -1608,161 +1641,24 @@ skip: c = tok; ch = file->buf_ptr[0]; skip_spaces(); - p = buf; + q = buf; while (ch != '\n' && ch != CH_EOF) { - if ((p - buf) < sizeof(buf) - 1) - *p++ = ch; + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; if (ch == '\\') { if (handle_stray_noerror() == 0) - --p; + --q; } else inp(); } - *p = '\0'; + *q = '\0'; if (c == TOK_ERROR) tcc_error("#error %s", buf); else tcc_warning("#warning %s", buf); break; case TOK_PRAGMA: - next(); - if (tok == TOK_pack && parse_flags & PARSE_FLAG_PACK) { - /* - This may be: - #pragma pack(1) // set - #pragma pack() // reset to default - #pragma pack(push,1) // push & set - #pragma pack(pop) // restore previous - */ - next(); - skip('('); - if (tok == TOK_ASM_pop) { - next(); - if (s1->pack_stack_ptr <= s1->pack_stack) { -stk_error: - tcc_error("out of pack stack"); - } - s1->pack_stack_ptr--; - } else { - int val = 0; - if (tok != ')') { - if (tok == TOK_ASM_push) { - next(); - s1->pack_stack_ptr++; - if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE) - goto stk_error; - skip(','); - } - if (tok != TOK_CINT) { -pack_error: - tcc_error("invalid pack pragma"); - } - val = tokc.i; - if (val < 1 || val > 16) - goto pack_error; - if (val < 1 || val > 16) - tcc_error("Value must be greater than 1 is less than or equal to 16"); - if ((val & (val - 1)) != 0) - tcc_error("Value must be a power of 2 curtain"); - next(); - } - *s1->pack_stack_ptr = val; - skip(')'); - } - }else if (tok == TOK_PUSH_MACRO || tok == TOK_POP_MACRO) { - TokenSym *ts; - CSym *def; - uint8_t *p1; - int len, t; - t = tok; - ch = file->buf_ptr[0]; - skip_spaces(); - if (ch != '(') - goto macro_xxx_syntax; - /* XXX: incorrect if comments : use next_nomacro with a special mode */ - inp(); - skip_spaces(); - if (ch == '\"'){ - inp(); - p = buf; - while (ch != '\"' && ch != '\n' && ch != CH_EOF) { - if ((p - buf) < sizeof(buf) - 1) - *p++ = ch; - if (ch == CH_EOB) { - --p; - handle_stray(); - }else - inp(); - } - if(ch != '\"') - goto macro_xxx_syntax; - *p = '\0'; - minp(); - next(); - }else{ - /* computed #pragma macro_xxx for #define xxx */ - next(); - buf[0] = '\0'; - while (tok != ')') { - if (tok != TOK_STR) { - macro_xxx_syntax: - tcc_error("'macro_xxx' expects (\"NAME\")"); - } - pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data); - next(); - } - } - skip (')'); - if(!buf[0]) - tcc_error(" empty string in #pragma"); - /* find TokenSym */ - p = buf; - while (is_space(*p)) - p++; - p1 = p; - for(;;){ - if (!isidnum_table[p[0] - CH_EOF]) - break; - ++p; - } - len = p - p1; - while (is_space(*p)) - p++; - if(!p) //'\0' - tcc_error("unrecognized string: %s", buf); - ts = tok_alloc(p1, len); - if(ts){ - def = &ts->sym_define; - if(t == TOK_PUSH_MACRO){ - void *tmp = def->data[def->off]; - if(tmp){ - def->off++; - if(def->off >= def->size){ - int size = def->size; - size *= 2; - if (size >= MACRO_STACK_SIZE) - tcc_error("stack full"); - def->data = tcc_realloc(def->data, size*sizeof(Sym**)); - def->size = size; - } - def->data[def->off] = tmp; - } - }else{ - if(def->off){ - --def->off; - }else{ - tcc_warning("stack empty"); - } - } - } - }else{ - fputs("#pragma ", s1->ppfp); - while (tok != TOK_LINEFEED){ - fputs(get_tok_str(tok, &tokc), s1->ppfp); - next(); - } - goto the_end; - } + pragma_parse(s1); break; default: if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_PPNUM) { @@ -1782,7 +1678,7 @@ pack_error: /* ignore other preprocess commands or #! for C scripts */ while (tok != TOK_LINEFEED) next_nomacro(); -the_end: + the_end: parse_flags = saved_parse_flags; } @@ -3134,13 +3030,12 @@ ST_FUNC int tcc_preprocess(TCCState *s1) ch = file->buf_ptr[0]; tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; parse_flags = PARSE_FLAG_ASM_COMMENTS | PARSE_FLAG_PREPROCESS | - PARSE_FLAG_LINEFEED | PARSE_FLAG_SPACES; + PARSE_FLAG_LINEFEED | PARSE_FLAG_SPACES; token_seen = 0; line_ref = 0; file_ref = NULL; iptr = s1->include_stack_ptr; - tok = TOK_LINEFEED; /* print line */ - goto print_line; + for (;;) { next(); if (tok == TOK_EOF) { @@ -3148,11 +3043,11 @@ ST_FUNC int tcc_preprocess(TCCState *s1) } else if (file != file_ref) { goto print_line; } else if (tok == TOK_LINEFEED) { - if (token_seen) + if (!token_seen) continue; ++line_ref; - token_seen = 1; - } else if (token_seen) { + token_seen = 0; + } else if (!token_seen) { d = file->line_num - line_ref; if (file != file_ref || d < 0 || d >= 8) { print_line: @@ -3160,7 +3055,8 @@ print_line: s = iptr_new > iptr ? " 1" : iptr_new < iptr ? " 2" : iptr_new > s1->include_stack ? " 3" - : ""; + : "" + ; iptr = iptr_new; fprintf(s1->ppfp, "# %d \"%s\"%s\n", file->line_num, file->filename, s); } else { @@ -3168,8 +3064,8 @@ print_line: fputs("\n", s1->ppfp), --d; } line_ref = (file_ref = file)->line_num; - token_seen = tok == TOK_LINEFEED; - if (token_seen) + token_seen = tok != TOK_LINEFEED; + if (!token_seen) continue; } fputs(get_tok_str(tok, &tokc), s1->ppfp); diff --git a/tcctok.h b/tcctok.h index c2aa040..735ccdd 100644 --- a/tcctok.h +++ b/tcctok.h @@ -138,8 +138,6 @@ /* pragma */ DEF(TOK_pack, "pack") - DEF(TOK_PUSH_MACRO, "push_macro") - DEF(TOK_POP_MACRO, "pop_macro") #if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_X86_64) /* already defined for assembler */ DEF(TOK_ASM_push, "push") @@ -287,5 +285,5 @@ #endif #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 -#include "asmx86-tok.h" +#include "i386-tok.h" #endif diff --git a/tests/Makefile b/tests/Makefile index 4c728eb..e3824ba 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -66,11 +66,11 @@ RUN_TCC = $(NATIVE_DEFINES) -DONE_SOURCE -run $(top_srcdir)/tcc.c $(TCCFLAGS) DISAS = objdump -d # libtcc test -ifdef LIBCRT - LIBCRT:=$(TOP)/$(LIBCRT) +ifdef LIBTCC1 + LIBTCC1:=$(TOP)/$(LIBTCC1) endif -all test : clean $(TESTS) +all test : $(TESTS) hello-exe: ../examples/ex1.c @echo ------------ $@ ------------ @@ -89,7 +89,7 @@ hello-run: ../examples/ex1.c @echo ------------ $@ ------------ $(TCC) -run $< -libtest: libtcc_test$(EXESUF) $(LIBCRT) +libtest: libtcc_test$(EXESUF) $(LIBTCC1) @echo ------------ $@ ------------ ./libtcc_test$(EXESUF) lib_path=.. @@ -101,7 +101,7 @@ moretests: $(MAKE) -C tests2 w32-prep: - cp ../libcrt.a ../lib + cp ../libtcc1.a ../lib # test.ref - generate using cc test.ref: tcctest.c @@ -210,14 +210,10 @@ abitest-cc$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC) abitest-tcc$(EXESUF): abitest.c libtcc.c $(TCC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) -DONE_SOURCE $(LIBS) $(LDFLAGS) -I$(top_srcdir) -abitest-tcc1$(EXESUF): abitest.c $(top_builddir)/$(LIBTCC) - $(CC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) $(NATIVE_DEFINES) $(LIBS) $(LINK_LIBTCC) $(LDFLAGS) -I$(top_srcdir) - -abitest: abitest-cc$(EXESUF) abitest-tcc$(EXESUF) abitest-tcc1$(EXESUF) +abitest: abitest-cc$(EXESUF) abitest-tcc$(EXESUF) @echo ------------ $@ ------------ ./abitest-cc$(EXESUF) lib_path=.. include="$(top_srcdir)/include" ./abitest-tcc$(EXESUF) lib_path=.. include="$(top_srcdir)/include" - ./abitest-tcc1$(EXESUF) lib_path=.. include="$(top_srcdir)/include" vla_test$(EXESUF): vla_test.c $(TCC) -o $@ $^ $(CPPFLAGS) $(CFLAGS) @@ -244,6 +240,6 @@ cache: tcc_g clean: $(MAKE) -C tests2 $@ rm -vf *~ *.o *.a *.bin *.i *.ref *.out *.out? *.out?b *.cc \ - *-cc *-tcc *.exe *-tcc1\ + *-cc *-tcc *.exe \ hello libtcc_test vla_test tcctest[1234] ex? tcc_g tcclib.h \ - ../lib/libcrt.a + ../lib/libtcc1.a diff --git a/tests/abitest.c b/tests/abitest.c index d840d85..3ad707a 100644 --- a/tests/abitest.c +++ b/tests/abitest.c @@ -468,7 +468,7 @@ int main(int argc, char **argv) { const char *testname = NULL; int retval = EXIT_SUCCESS; - /* if tcclib.h and libcrt.a are not installed, where can we find them */ + /* if tcclib.h and libtcc1.a are not installed, where can we find them */ for (i = 1; i < argc; ++i) { if (!memcmp(argv[i], "lib_path=",9)) tccdir = argv[i] + 9; diff --git a/tests/libtcc_test.c b/tests/libtcc_test.c index 4449afa..bead0ff 100644 --- a/tests/libtcc_test.c +++ b/tests/libtcc_test.c @@ -43,7 +43,7 @@ int main(int argc, char **argv) exit(1); } - /* if tcclib.h and libcrt.a are not installed, where can we find them */ + /* if tcclib.h and libtcc1.a are not installed, where can we find them */ if (argc == 2 && !memcmp(argv[1], "lib_path=",9)) tcc_set_lib_path(s, argv[1]+9); diff --git a/tests/tcctest.c b/tests/tcctest.c index 5fac82e..cc8ffd8 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -235,7 +235,7 @@ void intdiv_test(void) void macro_test(void) { - printf("macro:\n"); + printf("macro:\n"); pf("N=%d\n", N); printf("aaa=%d\n", AAA); @@ -379,23 +379,6 @@ comment /* And again when the name and parenthes are separated by a comment. */ TEST2 /* the comment */ (); - /* macro_push and macro_pop test */ - #define MACRO_TEST "macro_test1\n" - #pragma push_macro("MACRO_TEST") - #undef MACRO_TEST - #define MACRO_TEST "macro_test2\n" - printf(MACRO_TEST); - #pragma pop_macro("MACRO_TEST") - printf(MACRO_TEST); -/* gcc does not support - #define MACRO_TEST_MACRO "MACRO_TEST" - #pragma push_macro(MACRO_TEST_MACRO) - #undef MACRO_TEST - #define MACRO_TEST "macro_test3\n" - printf(MACRO_TEST); - #pragma pop_macro(MACRO_TEST_MACRO) - printf(MACRO_TEST); -*/ } @@ -1697,6 +1680,7 @@ void prefix ## fcast(type a)\ printf("ftof: %f %f %Lf\n", fa, da, la);\ ia = (int)a;\ llia = (long long)a;\ + a = (a >= 0) ? a : -a;\ ua = (unsigned int)a;\ llua = (unsigned long long)a;\ printf("ftoi: %d %u %lld %llu\n", ia, ua, llia, llua);\ @@ -1726,18 +1710,6 @@ void prefix ## call(void)\ printf("strto%s: %f\n", #prefix, (double)strto ## prefix("1.2", NULL));\ }\ \ -void prefix ## calc(type x, type y)\ -{\ - x=x*x;y=y*y;\ - printf("%d, %d\n", (int)x, (int)y);\ - x=x-y;y=y-x;\ - printf("%d, %d\n", (int)x, (int)y);\ - x=x/y;y=y/x;\ - printf("%d, %d\n", (int)x, (int)y);\ - x=x+x;y=y+y;\ - printf("%d, %d\n", (int)x, (int)y);\ -}\ -\ void prefix ## signed_zeros(void) \ {\ type x = 0.0, y = -0.0, n, p;\ @@ -1760,7 +1732,7 @@ void prefix ## signed_zeros(void) \ 1.0 / x != 1.0 / p);\ else\ printf ("x != +y; this is wrong!\n");\ - p = -y;\ + p = -y;\ if (x == p)\ printf ("Test 1.0 / x != 1.0 / -y returns %d (should be 0).\n",\ 1.0 / x != 1.0 / p);\ @@ -1776,8 +1748,7 @@ void prefix ## test(void)\ prefix ## fcast(234.6);\ prefix ## fcast(-2334.6);\ prefix ## call();\ - prefix ## calc(1, 1.0000000000000001);\ - prefix ## signed_zeros();\ + prefix ## signed_zeros();\ } FTEST(f, float, float, "%f") @@ -2184,15 +2155,14 @@ void whitespace_test(void) { char *str; - -#if 1 + #if 1 pri\ -ntf("whitspace:\n"); +ntf("whitspace:\n"); #endif pf("N=%d\n", 2); #ifdef CORRECT_CR_HANDLING - pri\ + pri\ ntf("aaa=%d\n", 3); #endif @@ -2204,12 +2174,11 @@ ntf("min=%d\n", 4); printf("len1=%d\n", strlen(" ")); #ifdef CORRECT_CR_HANDLING - str = " + str = " "; printf("len1=%d str[0]=%d\n", strlen(str), str[0]); #endif - printf("len1=%d\n", strlen(" -a + printf("len1=%d\n", strlen(" a ")); #endif /* ACCEPT_CR_IN_STRINGS */ } @@ -2603,6 +2572,7 @@ int constant_p_var; void builtin_test(void) { +#if GCC_MAJOR >= 3 COMPAT_TYPE(int, int); COMPAT_TYPE(int, unsigned int); COMPAT_TYPE(int, char); @@ -2612,9 +2582,9 @@ void builtin_test(void) COMPAT_TYPE(int *, void *); COMPAT_TYPE(int *, const int *); COMPAT_TYPE(char *, unsigned char *); - COMPAT_TYPE(char, unsigned char); /* space is needed because tcc preprocessor introduces a space between each token */ - COMPAT_TYPE(char **, void *); + COMPAT_TYPE(char * *, void *); +#endif printf("res = %d\n", __builtin_constant_p(1)); printf("res = %d\n", __builtin_constant_p(1 + 2)); printf("res = %d\n", __builtin_constant_p(&constant_p_var)); diff --git a/win32/tools/tiny_libmaker.c b/win32/tools/tiny_libmaker.c index 29a692a..62d2a2e 100644 --- a/win32/tools/tiny_libmaker.c +++ b/win32/tools/tiny_libmaker.c @@ -1,5 +1,5 @@ /* - * This program is for making libcrt.a without ar + * This program is for making libtcc1.a without ar * tiny_libmaker - tiny elf lib maker * usage: tiny_libmaker [lib] files... * Copyright (c) 2007 Timppa diff --git a/x86_64-gen.c b/x86_64-gen.c index 2b52bb1..ae65328 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -29,38 +29,28 @@ /* a register can belong to several classes. The classes must be sorted from more general to more precise (see gv2() code which does assumptions on it). */ -#define RC_INT 0x0001 /* generic integer register */ -#define RC_FLOAT 0x0002 /* generic float register */ -#define RC_RAX 0x0004 -#define RC_RCX 0x0008 -#define RC_RDX 0x0010 -#define RC_ST0 0x0020 /* only for long double */ -#define RC_R8 0x0040 -#define RC_R9 0x0080 -#define RC_XMM0 0x0100 -#define RC_XMM1 0x0200 -#define RC_XMM2 0x0400 -#define RC_XMM3 0x0800 -#define RC_XMM4 0x1000 -#define RC_XMM5 0x2000 -#define RC_XMM6 0x4000 -#define RC_XMM7 0x8000 -#define RC_RSI 0x10000 -#define RC_RDI 0x20000 -#define RC_INT1 0x40000 /* function_pointer */ -#define RC_INT2 0x80000 -#define RC_RBX 0x100000 -#define RC_R10 0x200000 -#define RC_R11 0x400000 -#define RC_R12 0x800000 -#define RC_R13 0x1000000 -#define RC_R14 0x2000000 -#define RC_R15 0x4000000 -#define RC_IRET RC_RAX /* function return: integer register */ -#define RC_LRET RC_RDX /* function return: second integer register */ -#define RC_FRET RC_XMM0 /* function return: float register */ -#define RC_QRET RC_XMM1 /* function return: second float register */ -#define RC_MASK (RC_INT|RC_INT1|RC_INT2|RC_FLOAT) +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_RAX 0x0004 +#define RC_RCX 0x0008 +#define RC_RDX 0x0010 +#define RC_ST0 0x0080 /* only for long double */ +#define RC_R8 0x0100 +#define RC_R9 0x0200 +#define RC_R10 0x0400 +#define RC_R11 0x0800 +#define RC_XMM0 0x1000 +#define RC_XMM1 0x2000 +#define RC_XMM2 0x4000 +#define RC_XMM3 0x8000 +#define RC_XMM4 0x10000 +#define RC_XMM5 0x20000 +#define RC_XMM6 0x40000 +#define RC_XMM7 0x80000 +#define RC_IRET RC_RAX /* function return: integer register */ +#define RC_LRET RC_RDX /* function return: second integer register */ +#define RC_FRET RC_XMM0 /* function return: float register */ +#define RC_QRET RC_XMM1 /* function return: second float register */ /* pretty names for the registers */ enum { @@ -68,7 +58,6 @@ enum { TREG_RCX = 1, TREG_RDX = 2, TREG_RSP = 4, - TREG_ST0 = 5, TREG_RSI = 6, TREG_RDI = 7, @@ -86,11 +75,13 @@ enum { TREG_XMM6 = 22, TREG_XMM7 = 23, + TREG_ST0 = 24, + + TREG_MEM = 0x20, }; #define REX_BASE(reg) (((reg) >> 3) & 1) #define REG_VALUE(reg) ((reg) & 7) -#define FLAG_GOT 0X01 /* return registers for function */ #define REG_IRET TREG_RAX /* single word int return register */ @@ -131,30 +122,34 @@ enum { #include ST_DATA const int reg_classes[NB_REGS] = { - /* eax */ RC_INT|RC_RAX|RC_INT2, - /* ecx */ RC_INT|RC_RCX|RC_INT2, - /* edx */ RC_INT|RC_RDX, - RC_INT|RC_INT1|RC_INT2|RC_RBX, + /* eax */ RC_INT | RC_RAX, + /* ecx */ RC_INT | RC_RCX, + /* edx */ RC_INT | RC_RDX, + 0, + 0, + 0, + 0, + 0, + RC_R8, + RC_R9, + RC_R10, + RC_R11, + 0, + 0, 0, - /* st0 */ RC_ST0, - RC_RSI|RC_INT2, - RC_RDI|RC_INT2, - RC_INT|RC_R8|RC_INT2, - RC_INT|RC_R9|RC_INT2, - RC_INT|RC_INT1|RC_INT2|RC_R10, - RC_INT|RC_INT1|RC_INT2|RC_R11, - RC_INT|RC_INT1|RC_INT2|RC_R12, - RC_INT|RC_INT1|RC_INT2|RC_R13, - RC_INT|RC_INT1|RC_INT2|RC_R14, - RC_INT|RC_INT1|RC_INT2|RC_R15, - /* xmm0 */ RC_FLOAT | RC_XMM0, - RC_FLOAT|RC_XMM1, - RC_FLOAT|RC_XMM2, - RC_FLOAT|RC_XMM3, - RC_FLOAT|RC_XMM4, - RC_FLOAT|RC_XMM5, - RC_FLOAT|RC_XMM6, - RC_FLOAT|RC_XMM7, + 0, + /* xmm0 */ RC_FLOAT | RC_XMM0, + /* xmm1 */ RC_FLOAT | RC_XMM1, + /* xmm2 */ RC_FLOAT | RC_XMM2, + /* xmm3 */ RC_FLOAT | RC_XMM3, + /* xmm4 */ RC_FLOAT | RC_XMM4, + /* xmm5 */ RC_FLOAT | RC_XMM5, + /* xmm6 an xmm7 are included so gv() can be used on them, + but they are not tagged with RC_FLOAT because they are + callee saved on Windows */ + RC_XMM6, + RC_XMM7, + /* st0 */ RC_ST0 }; static unsigned long func_sub_sp_offset; @@ -309,19 +304,18 @@ static void gen_gotpcrel(int r, Sym *sym, int c) } } -static void gen_modrm_impl(int op_reg, int fr, Sym *sym, int c, int flag) +static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got) { - int r = fr & VT_VALMASK; op_reg = REG_VALUE(op_reg) << 3; - if (r == VT_CONST) { + if ((r & VT_VALMASK) == VT_CONST) { /* constant memory reference */ o(0x05 | op_reg); - if (flag & FLAG_GOT) { - gen_gotpcrel(fr, sym, c); + if (is_got) { + gen_gotpcrel(r, sym, c); } else { - gen_addrpc32(fr, sym, c); + gen_addrpc32(r, sym, c); } - } else if (r == VT_LOCAL) { + } else if ((r & VT_VALMASK) == VT_LOCAL) { /* currently, we use only ebp as base */ if (c == (char)c) { /* short reference */ @@ -330,23 +324,15 @@ static void gen_modrm_impl(int op_reg, int fr, Sym *sym, int c, int flag) } else { oad(0x85 | op_reg, c); } - } else if (c) { - if (c == (char)c) { - /* short reference */ - g(0x40 | op_reg | REG_VALUE(fr)); - if(r == TREG_RSP) - g(0x24); - g(c); + } else if ((r & VT_VALMASK) >= TREG_MEM) { + if (c) { + g(0x80 | op_reg | REG_VALUE(r)); + gen_le32(c); } else { - g(0x80 | op_reg | REG_VALUE(fr)); - if(r == TREG_RSP) - g(0x24); - gen_le32(c); + g(0x00 | op_reg | REG_VALUE(r)); } } else { - g(0x00 | op_reg | REG_VALUE(fr)); - if(r == TREG_RSP) - g(0x24); + g(0x00 | op_reg | REG_VALUE(r)); } } @@ -361,18 +347,17 @@ static void gen_modrm(int op_reg, int r, Sym *sym, int c) opcode bits */ static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c) { - int flag; - if((op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC)) - flag = FLAG_GOT; + int is_got; + is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC); orex(1, r, op_reg, opcode); - gen_modrm_impl(op_reg, r, sym, c, flag); + gen_modrm_impl(op_reg, r, sym, c, is_got); } /* load 'r' from value 'sv' */ void load(int r, SValue *sv) { - int v, t, ft, fc, fr, ll; + int v, t, ft, fc, fr; SValue v1; #ifdef TCC_TARGET_PE @@ -383,21 +368,19 @@ void load(int r, SValue *sv) fr = sv->r; ft = sv->type.t & ~VT_DEFSIGN; fc = sv->c.ul; - ll = is64_type(ft); #ifndef TCC_TARGET_PE /* we use indirect access via got */ if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) && (fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) { /* use the result register as a temporal register */ - int tr; + int tr = r | TREG_MEM; if (is_float(ft)) { /* we cannot use float registers as a temporal register */ tr = get_reg(RC_INT) | TREG_MEM; - }else{ - tr = r | TREG_MEM; - } + } gen_modrm64(0x8b, tr, fr, sv->sym, 0); + /* load from the temporal register */ fr = tr | VT_LVAL; } @@ -405,14 +388,7 @@ void load(int r, SValue *sv) v = fr & VT_VALMASK; if (fr & VT_LVAL) { - if(fr & VT_TMP){ - int size, align; - if((ft & VT_BTYPE) == VT_FUNC) - size = PTR_SIZE; - else - size = type_size(&sv->type, &align); - loc_stack(size, 0); - } + int b, ll; if (v == VT_LLOCAL) { v1.type.t = VT_PTR; v1.r = VT_LOCAL | VT_LVAL; @@ -421,13 +397,14 @@ void load(int r, SValue *sv) if (!(reg_classes[fr] & RC_INT)) fr = get_reg(RC_INT); load(fr, &v1); - fc = 0; } - int b; + ll = 0; if ((ft & VT_BTYPE) == VT_FLOAT) { - b = 0x100ff3; /* movss */ + b = 0x6e0f66; + r = REG_VALUE(r); /* movd */ } else if ((ft & VT_BTYPE) == VT_DOUBLE) { - b = 0x100ff2; /* movds */ + b = 0x7e0ff3; /* movq */ + r = REG_VALUE(r); } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { b = 0xdb, r = 5; /* fldt */ } else if ((ft & VT_TYPE) == VT_BYTE || (ft & VT_TYPE) == VT_BOOL) { @@ -439,13 +416,18 @@ void load(int r, SValue *sv) } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { b = 0xb70f; /* movzwl */ } else { - assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG) + assert(((ft & VT_BTYPE) == VT_INT) || ((ft & VT_BTYPE) == VT_LLONG) || ((ft & VT_BTYPE) == VT_PTR) || ((ft & VT_BTYPE) == VT_ENUM) || ((ft & VT_BTYPE) == VT_FUNC)); + ll = is64_type(ft); b = 0x8b; } - orex(ll, fr, r, b); - gen_modrm(r, fr, sv->sym, fc); + if (ll) { + gen_modrm64(b, r, fr, sv->sym, fc); + } else { + orex(ll, fr, r, b); + gen_modrm(r, fr, sv->sym, fc); + } } else { if (v == VT_CONST) { if (fr & VT_SYM) { @@ -464,33 +446,33 @@ void load(int r, SValue *sv) gen_gotpcrel(r, sv->sym, fc); } #endif + } else if (is64_type(ft)) { + orex(1,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ + gen_le64(sv->c.ull); } else { - orex(ll,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ - if (ll) - gen_le64(sv->c.ull); - else - gen_le32(fc); - } + orex(0,r,0, 0xb8 + REG_VALUE(r)); /* mov $xx, r */ + gen_le32(fc); + } } else if (v == VT_LOCAL) { orex(1,0,r,0x8d); /* lea xxx(%ebp), r */ gen_modrm(r, VT_LOCAL, sv->sym, fc); } else if (v == VT_CMP) { - orex(0, r, 0, 0xb8 + REG_VALUE(r)); - if ((fc & ~0x100) == TOK_NE){ - gen_le32(1);/* mov $0, r */ - }else{ - gen_le32(0);/* mov $1, r */ - } - if (fc & 0x100){ - fc &= ~0x100; - /* This was a float compare. If the parity bit is - set the result was unordered, meaning false for everything - except TOK_NE, and true for TOK_NE. */ - o(0x037a + (REX_BASE(r) << 8));/* jp 3*/ - } - orex(0,r,0, 0x0f); /* setxx %br */ - o(fc); - o(0xc0 + REG_VALUE(r)); + orex(0,r,0,0); + if ((fc & ~0x100) != TOK_NE) + oad(0xb8 + REG_VALUE(r), 0); /* mov $0, r */ + else + oad(0xb8 + REG_VALUE(r), 1); /* mov $1, r */ + if (fc & 0x100) + { + /* This was a float compare. If the parity bit is + set the result was unordered, meaning false for everything + except TOK_NE, and true for TOK_NE. */ + fc &= ~0x100; + o(0x037a + (REX_BASE(r) << 8)); + } + orex(0,r,0, 0x0f); /* setxx %br */ + o(fc); + o(0xc0 + REG_VALUE(r)); } else if (v == VT_JMP || v == VT_JMPI) { t = v & 1; orex(0,r,0,0); @@ -500,89 +482,117 @@ void load(int r, SValue *sv) orex(0,r,0,0); oad(0xb8 + REG_VALUE(r), t ^ 1); /* mov $0, r */ } else if (v != r) { - if (reg_classes[r] & RC_FLOAT) { - if(v == TREG_ST0){ - /* gen_cvt_ftof(VT_DOUBLE); */ - o(0xf0245cdd); /* fstpl -0x10(%rsp) */ - /* movsd -0x10(%rsp),%xmm0 */ - o(0x100ff2); - o(0xf02444 + REG_VALUE(r)*8); - }else if(reg_classes[v] & RC_FLOAT){ - o(0x7e0ff3); - o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8); - }else - assert(0); + if ((r >= TREG_XMM0) && (r <= TREG_XMM7)) { + if (v == TREG_ST0) { + /* gen_cvt_ftof(VT_DOUBLE); */ + o(0xf0245cdd); /* fstpl -0x10(%rsp) */ + /* movsd -0x10(%rsp),%xmmN */ + o(0x100ff2); + o(0x44 + REG_VALUE(r)*8); /* %xmmN */ + o(0xf024); + } else { + assert((v >= TREG_XMM0) && (v <= TREG_XMM7)); + if ((ft & VT_BTYPE) == VT_FLOAT) { + o(0x100ff3); + } else { + assert((ft & VT_BTYPE) == VT_DOUBLE); + o(0x100ff2); + } + o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8); + } } else if (r == TREG_ST0) { - assert(reg_classes[v] & RC_FLOAT); + assert((v >= TREG_XMM0) && (v <= TREG_XMM7)); /* gen_cvt_ftof(VT_LDOUBLE); */ - /* movsd %xmm0,-0x10(%rsp) */ - o(0x110ff2); - o(0xf02444 + REG_VALUE(v)*8); + /* movsd %xmmN,-0x10(%rsp) */ + o(0x110ff2); + o(0x44 + REG_VALUE(r)*8); /* %xmmN */ + o(0xf024); o(0xf02444dd); /* fldl -0x10(%rsp) */ } else { - if(fc){ - orex(1,fr,r,0x8d); /* lea xxx(%ebp), r */ - gen_modrm(r, fr, sv->sym, fc); - }else{ - orex(ll,v,r, 0x8b); - o(0xc0 + REG_VALUE(v) + REG_VALUE(r) * 8); /* mov v, r */ - } + orex(1,r,v, 0x89); + o(0xc0 + REG_VALUE(r) + REG_VALUE(v) * 8); /* mov v, r */ } } } } /* store register 'r' in lvalue 'v' */ -void store(int r, SValue *sv) +void store(int r, SValue *v) { - int fr, bt, ft, fc, ll, v; + int fr, bt, ft, fc; + int op64 = 0; + /* store the REX prefix in this variable when PIC is enabled */ + int pic = 0; #ifdef TCC_TARGET_PE SValue v2; - sv = pe_getimport(sv, &v2); + v = pe_getimport(v, &v2); #endif - ft = sv->type.t & ~VT_DEFSIGN; - fc = sv->c.ul; - fr = sv->r; + + ft = v->type.t; + fc = v->c.ul; + fr = v->r & VT_VALMASK; bt = ft & VT_BTYPE; - ll = is64_type(ft); - v = fr & VT_VALMASK; -//#ifndef TCC_TARGET_PE +#ifndef TCC_TARGET_PE /* we need to access the variable via got */ - // if (fr == VT_CONST && (v->r & VT_SYM)) { + if (fr == VT_CONST && (v->r & VT_SYM)) { /* mov xx(%rip), %r11 */ - // o(0x1d8b4c); - // gen_gotpcrel(TREG_R11, v->sym, v->c.ul); - //pic = is64_type(bt) ? 0x49 : 0x41; - // } -//#endif + o(0x1d8b4c); + gen_gotpcrel(TREG_R11, v->sym, v->c.ul); + pic = is64_type(bt) ? 0x49 : 0x41; + } +#endif /* XXX: incorrect if float reg to reg */ if (bt == VT_FLOAT) { - orex(0, fr, r, 0x110ff3); /* movss */ + o(0x66); + o(pic); + o(0x7e0f); /* movd */ + r = REG_VALUE(r); } else if (bt == VT_DOUBLE) { - orex(0, fr, r, 0x110ff2);/* movds */ + o(0x66); + o(pic); + o(0xd60f); /* movq */ + r = REG_VALUE(r); } else if (bt == VT_LDOUBLE) { o(0xc0d9); /* fld %st(0) */ - orex(0, fr, r, 0xdb);/* fstpt */ + o(pic); + o(0xdb); /* fstpt */ r = 7; } else { if (bt == VT_SHORT) o(0x66); - if (bt == VT_BYTE || bt == VT_BOOL) - orex(ll, fr, r, 0x88); - else{ - orex(ll, fr, r, 0x89); - } + o(pic); + if (bt == VT_BYTE || bt == VT_BOOL) + orex(0, 0, r, 0x88); + else if (is64_type(bt)) + op64 = 0x89; + else + orex(0, 0, r, 0x89); + } + if (pic) { + /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */ + if (op64) + o(op64); + o(3 + (r << 3)); + } else if (op64) { + if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { + gen_modrm64(op64, r, v->r, v->sym, fc); + } else if (fr != r) { + /* XXX: don't we really come here? */ + abort(); + o(0xc0 + fr + r * 8); /* mov r, fr */ + } + } else { + if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { + gen_modrm(r, v->r, v->sym, fc); + } else if (fr != r) { + /* XXX: don't we really come here? */ + abort(); + o(0xc0 + fr + r * 8); /* mov r, fr */ + } } - if (v == VT_CONST || v == VT_LOCAL || (fr & VT_LVAL)) { - gen_modrm(r, fr, sv->sym, fc); - } else if (v != r) { - /* XXX: don't we really come here? */ - abort(); - o(0xc0 + REG_VALUE(v) + REG_VALUE(r)*8); /* mov r, fr */ - } } /* 'is_jmp' is '1' if it is a jump */ @@ -603,76 +613,14 @@ static void gcall_or_jmp(int is_jmp) oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ } else { /* otherwise, indirect call */ - r = get_reg(RC_INT1); + r = TREG_R11; load(r, vtop); - orex(0, r, 0, 0xff); /* REX call/jmp *r */ + o(0x41); /* REX */ + o(0xff); /* call/jmp *r */ o(0xd0 + REG_VALUE(r) + (is_jmp << 4)); } } -static int func_scratch; -static int r_loc; - -int reloc_add(int inds) -{ - return psym(0, inds); -} - -void reloc_use(int t, int data) -{ - int *ptr; - while (t) { - ptr = (int *)(cur_text_section->data + t); - t = *ptr; /* next value */ - *ptr = data; - } -} - -void struct_copy(SValue *d, SValue *s, SValue *c) -{ - if(!c->c.i) - return; - save_reg(TREG_RCX); - load(TREG_RCX, c); - load(TREG_RDI, d); - load(TREG_RSI, s); - o(0xa4f3);// rep movsb -} - -void gen_putz(SValue *d, int size) -{ - if(!size) - return; - save_reg(TREG_RAX); - o(0xb0); - g(0x00); - save_reg(TREG_RCX); - o(0xb8 + REG_VALUE(TREG_RCX)); /* mov $xx, r */ - gen_le32(size); - load(TREG_RDI, d); - o(0xaaf3);//rep stos -} - -/* Generate function call. The function address is pushed first, then - all the parameters in call order. This functions pops all the - parameters and the function address. */ -void gen_offs_sp(int b, int r, int off) -{ - if(r & 0x100) - o(b); - else - orex(1, 0, r, b); - if(!off){ - o(0x2404 | (REG_VALUE(r) << 3)); - }else if (off == (char)off) { - o(0x2444 | (REG_VALUE(r) << 3)); - g(off); - } else { - o(0x2484 | (REG_VALUE(r) << 3)); - gen_le32(off); - } -} - #ifdef TCC_TARGET_PE #define REGN 4 @@ -690,6 +638,24 @@ static int arg_prepare_reg(int idx) { return arg_regs[idx]; } +static int func_scratch; + +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ + +void gen_offs_sp(int b, int r, int d) +{ + orex(1,0,r & 0x100 ? 0 : r, b); + if (d == (char)d) { + o(0x2444 | (REG_VALUE(r) << 3)); + g(d); + } else { + o(0x2484 | (REG_VALUE(r) << 3)); + gen_le32(d); + } +} + /* Return the number of registers needed to return the struct, or 0 if returning via struct pointer. */ ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, int *ret_align) @@ -853,8 +819,9 @@ void gfunc_prolog(CType *func_type) Sym *sym; CType *type; - func_ret_sub = func_scratch = r_loc = 0; - pop_stack = loc = 0; + func_ret_sub = 0; + func_scratch = 0; + loc = 0; addr = PTR_SIZE * 2; ind += FUNC_PROLOG_SIZE; @@ -928,7 +895,7 @@ void gfunc_epilog(void) ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; /* align local size to word & save local variables */ v = (func_scratch + -loc + 15) & -16; - reloc_use(r_loc, func_scratch); + if (v >= 4096) { Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); oad(0xb8, v); /* mov stacksize, %eax */ @@ -948,6 +915,16 @@ void gfunc_epilog(void) #else +static void gadd_sp(int val) +{ + if (val == (char)val) { + o(0xc48348); + g(val); + } else { + oad(0xc48148, val); /* add $xxx, %rsp */ + } +} + typedef enum X86_64_Mode { x86_64_mode_none, x86_64_mode_memory, @@ -986,14 +963,12 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) case VT_BYTE: case VT_SHORT: case VT_LLONG: - case VT_QLONG: case VT_BOOL: case VT_PTR: case VT_FUNC: case VT_ENUM: return x86_64_mode_integer; case VT_FLOAT: - case VT_QFLOAT: case VT_DOUBLE: return x86_64_mode_sse; case VT_LDOUBLE: return x86_64_mode_x87; @@ -1004,7 +979,7 @@ static X86_64_Mode classify_x86_64_inner(CType *ty) // Detect union if (f->next && (f->c == f->next->c)) return x86_64_mode_memory; - + mode = x86_64_mode_none; for (f = f->next; f; f = f->next) mode = classify_x86_64_merge(mode, classify_x86_64_inner(&f->type)); @@ -1022,7 +997,7 @@ static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *p if (ty->t & (VT_BITFIELD|VT_ARRAY)) { *psize = 8; - *palign = 8; + *palign = 8; *reg_count = 1; ret_t = ty->t; mode = x86_64_mode_integer; @@ -1033,7 +1008,6 @@ static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *p if (size > 16) { mode = x86_64_mode_memory; - ret_t = ty->t; } else { mode = classify_x86_64_inner(ty); switch (mode) { @@ -1042,22 +1016,16 @@ static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *p *reg_count = 2; ret_t = VT_QLONG; } else { - *reg_count = 1; - if(size > 4) - ret_t = VT_LLONG; - else if(size > 2){ - ret_t = VT_INT; - }else if(size > 1) - ret_t = VT_SHORT; - else - ret_t = VT_BYTE; - } - ret_t |= (ty->t & VT_UNSIGNED); + *reg_count = 1; + ret_t = (size > 4) ? VT_LLONG : VT_INT; + } break; + case x86_64_mode_x87: *reg_count = 1; ret_t = VT_LDOUBLE; break; + case x86_64_mode_sse: if (size > 8) { *reg_count = 2; @@ -1067,15 +1035,13 @@ static X86_64_Mode classify_x86_64_arg(CType *ty, CType *ret, int *psize, int *p ret_t = (size > 4) ? VT_DOUBLE : VT_FLOAT; } break; - default: - ret_t = ty->t; - break; /* nothing to be done for x86_64_mode_memory and x86_64_mode_none*/ + default: break; /* nothing to be done for x86_64_mode_memory and x86_64_mode_none*/ } } } if (ret) { - ret->ref = ty->ref; + ret->ref = NULL; ret->t = ret_t; } @@ -1086,13 +1052,12 @@ ST_FUNC int classify_x86_64_va_arg(CType *ty) { /* This definition must be synced with stdarg.h */ enum __va_arg_type { - __va_gen_reg, __va_float_reg, __va_ld_reg, __va_stack - }; + __va_gen_reg, __va_float_reg, __va_stack + }; int size, align, reg_count; X86_64_Mode mode = classify_x86_64_arg(ty, NULL, &size, &align, ®_count); switch (mode) { default: return __va_stack; - case x86_64_mode_x87: return __va_ld_reg; case x86_64_mode_integer: return __va_gen_reg; case x86_64_mode_sse: return __va_float_reg; } @@ -1112,21 +1077,26 @@ static const uint8_t arg_regs[REGN] = { TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9 }; +static int arg_prepare_reg(int idx) { + if (idx == 2 || idx == 3) + /* idx=2: r10, idx=3: r11 */ + return idx + 8; + else + return arg_regs[idx]; +} + /* Generate function call. The function address is pushed first, then all the parameters in call order. This functions pops all the parameters and the function address. */ void gfunc_call(int nb_args) { - X86_64_Mode mode; - int size, align, args_size, s, e, i, reg_count; + X86_64_Mode mode; + CType type; + int size, align, r, args_size, stack_adjust, run_start, run_end, i, reg_count; int nb_reg_args = 0; int nb_sse_args = 0; - int gen_reg, sse_reg; - CType type; + int sse_reg, gen_reg; - /* fetch cpu flag before the following sub will change the value */ - if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP) - gv(RC_INT); /* calculate the number of integer/float register arguments */ for(i = 0; i < nb_args; i++) { mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); @@ -1136,197 +1106,260 @@ void gfunc_call(int nb_args) nb_reg_args += reg_count; } + /* arguments are collected in runs. Each run is a collection of 8-byte aligned arguments + and ended by a 16-byte aligned argument. This is because, from the point of view of + the callee, argument alignment is computed from the bottom up. */ + /* for struct arguments, we need to call memcpy and the function + call breaks register passing arguments we are preparing. + So, we process arguments which will be passed by stack first. */ + gen_reg = nb_reg_args; + sse_reg = nb_sse_args; + run_start = 0; args_size = 0; - gen_reg = nb_reg_args; - sse_reg = nb_sse_args; - /* for struct arguments, we need to call memcpy and the function - call breaks register passing arguments we are preparing. - So, we process arguments which will be passed by stack first. */ - for(i = 0; i < nb_args; i++) { - mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); - switch (mode) { - case x86_64_mode_x87: - if((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT) - goto stack_arg1; - else - args_size = (args_size + 15) & ~15; - case x86_64_mode_memory: - stack_arg1: - args_size += size; - break; - case x86_64_mode_sse: - sse_reg -= reg_count; - if (sse_reg + reg_count > 8) - goto stack_arg1; - break; - case x86_64_mode_integer: - gen_reg -= reg_count; - if (gen_reg + reg_count > REGN) - goto stack_arg1; - break; - default: break; /* nothing to be done for x86_64_mode_none */ - } - } + while (run_start != nb_args) { + int run_gen_reg = gen_reg, run_sse_reg = sse_reg; + + run_end = nb_args; + stack_adjust = 0; + for(i = run_start; (i < nb_args) && (run_end == nb_args); i++) { + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); + switch (mode) { + case x86_64_mode_memory: + case x86_64_mode_x87: + stack_arg: + if (align == 16) + run_end = i; + else + stack_adjust += size; + break; + + case x86_64_mode_sse: + sse_reg -= reg_count; + if (sse_reg + reg_count > 8) goto stack_arg; + break; + + case x86_64_mode_integer: + gen_reg -= reg_count; + if (gen_reg + reg_count > REGN) goto stack_arg; + break; + default: break; /* nothing to be done for x86_64_mode_none */ + } + } + + gen_reg = run_gen_reg; + sse_reg = run_sse_reg; + + /* adjust stack to align SSE boundary */ + if (stack_adjust &= 15) { + /* fetch cpu flag before the following sub will change the value */ + if (vtop >= vstack && (vtop->r & VT_VALMASK) == VT_CMP) + gv(RC_INT); + + stack_adjust = 16 - stack_adjust; + o(0x48); + oad(0xec81, stack_adjust); /* sub $xxx, %rsp */ + args_size += stack_adjust; + } + + for(i = run_start; i < run_end;) { + /* Swap argument to top, it will possibly be changed here, + and might use more temps. At the end of the loop we keep + in on the stack and swap it back to its original position + if it is a register. */ + SValue tmp = vtop[0]; + vtop[0] = vtop[-i]; + vtop[-i] = tmp; + + mode = classify_x86_64_arg(&vtop->type, NULL, &size, &align, ®_count); + + int arg_stored = 1; + switch (vtop->type.t & VT_BTYPE) { + case VT_STRUCT: + if (mode == x86_64_mode_sse) { + if (sse_reg > 8) + sse_reg -= reg_count; + else + arg_stored = 0; + } else if (mode == x86_64_mode_integer) { + if (gen_reg > REGN) + gen_reg -= reg_count; + else + arg_stored = 0; + } + + if (arg_stored) { + /* allocate the necessary size on stack */ + o(0x48); + oad(0xec81, size); /* sub $xxx, %rsp */ + /* generate structure store */ + r = get_reg(RC_INT); + orex(1, r, 0, 0x89); /* mov %rsp, r */ + o(0xe0 + REG_VALUE(r)); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + args_size += size; + } + break; + + case VT_LDOUBLE: + assert(0); + break; + + case VT_FLOAT: + case VT_DOUBLE: + assert(mode == x86_64_mode_sse); + if (sse_reg > 8) { + --sse_reg; + r = gv(RC_FLOAT); + o(0x50); /* push $rax */ + /* movq %xmmN, (%rsp) */ + o(0xd60f66); + o(0x04 + REG_VALUE(r)*8); + o(0x24); + args_size += size; + } else { + arg_stored = 0; + } + break; + + default: + assert(mode == x86_64_mode_integer); + /* simple type */ + /* XXX: implicit cast ? */ + if (gen_reg > REGN) { + --gen_reg; + r = gv(RC_INT); + orex(0,r,0,0x50 + REG_VALUE(r)); /* push r */ + args_size += size; + } else { + arg_stored = 0; + } + break; + } + + /* And swap the argument back to it's original position. */ + tmp = vtop[0]; + vtop[0] = vtop[-i]; + vtop[-i] = tmp; + + if (arg_stored) { + vrotb(i+1); + assert((vtop->type.t == tmp.type.t) && (vtop->r == tmp.r)); + vpop(); + --nb_args; + --run_end; + } else { + ++i; + } + } - args_size = (args_size + 15) & ~15; - if (func_scratch < args_size) - func_scratch = args_size; + /* handle 16 byte aligned arguments at end of run */ + run_start = i = run_end; + while (i < nb_args) { + /* Rotate argument to top since it will always be popped */ + mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); + if (align != 16) + break; - gen_reg = nb_reg_args; - sse_reg = nb_sse_args; - for(s = e = 0; s < nb_args; s = e){ - int run_gen, run_sse, st_size; - run_gen = gen_reg; - run_sse = sse_reg; - st_size = 0; - for(i = s; i < nb_args; i++) { - mode = classify_x86_64_arg(&vtop[-i].type, NULL, &size, &align, ®_count); - switch (mode) { - case x86_64_mode_x87: - if((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT){ - goto stack_arg2; - }else{ - ++i; - goto doing; - } - case x86_64_mode_memory: - stack_arg2: - st_size += size; - break; - case x86_64_mode_sse: - sse_reg -= reg_count; - if (sse_reg + reg_count > 8) - goto stack_arg2; - break; - case x86_64_mode_integer: - gen_reg -= reg_count; - if (gen_reg + reg_count > REGN) - goto stack_arg2; - break; - default: break; /* nothing to be done for x86_64_mode_none */ - } - } -doing: - e = i; - st_size = -st_size & 15;// 16 - (size & 15) - if(st_size) - args_size -= st_size; - - gen_reg = run_gen; - sse_reg = run_sse; - for(i = s; i < e; i++) { - SValue tmp; - /* Swap argument to top, it will possibly be changed here, - and might use more temps. All arguments must remain on the - stack, so that get_reg can correctly evict some of them onto - stack. We could use also use a vrott(nb_args) at the end - of this loop, but this seems faster. */ - if(i != 0){ - tmp = vtop[0]; - vtop[0] = vtop[-i]; - vtop[-i] = tmp; - } - - mode = classify_x86_64_arg(&vtop->type, &type, &size, &align, ®_count); - switch (mode) { - case x86_64_mode_x87: - /* Must ensure TREG_ST0 only */ - if((vtop->type.t & VT_BTYPE) == VT_STRUCT){ - vdup(); - vtop[-1].r = VT_CONST; - vtop->type = type; - gv(RC_ST0); - args_size -= size; - gen_offs_sp(0xdb, 0x107, args_size); - vtop--;//Release TREG_ST0 - }else{ - gv(RC_ST0); - args_size -= size; - gen_offs_sp(0xdb, 0x107, args_size); - vtop->r = VT_CONST;//Release TREG_ST0 - } - break; - case x86_64_mode_memory: - args_size -= size; - vset(&char_pointer_type, TREG_RSP, args_size);/* generate memcpy RSP */ - vpushv(&vtop[-1]); - vtop->type = char_pointer_type; - gaddrof(); - vpushs(size); - struct_copy(&vtop[-2], &vtop[-1], &vtop[0]); - vtop -= 3; - break; - case x86_64_mode_sse: - sse_reg -= reg_count; - if (sse_reg + reg_count > 8){ - args_size -= size; - goto gen_code; - } - break; - case x86_64_mode_integer: - gen_reg -= reg_count; - if (gen_reg + reg_count > REGN){ - args_size -= size; - gen_code: - vset(&type, TREG_RSP | VT_LVAL, args_size); - vpushv(&vtop[-1]); - vtop->type = type; - vstore(); - vtop--; - } - break; - default: break; /* nothing to be done for x86_64_mode_none */ - } - if(i != 0){ - tmp = vtop[0]; - vtop[0] = vtop[-i]; - vtop[-i] = tmp; - } - } - run_gen = gen_reg; - run_sse = sse_reg; - } - - gen_reg = nb_reg_args; - sse_reg = nb_sse_args; + vrotb(i+1); + + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + gv(RC_ST0); + oad(0xec8148, size); /* sub $xxx, %rsp */ + o(0x7cdb); /* fstpt 0(%rsp) */ + g(0x24); + g(0x00); + args_size += size; + } else { + //assert(mode == x86_64_mode_memory); + + /* allocate the necessary size on stack */ + o(0x48); + oad(0xec81, size); /* sub $xxx, %rsp */ + /* generate structure store */ + r = get_reg(RC_INT); + orex(1, r, 0, 0x89); /* mov %rsp, r */ + o(0xe0 + REG_VALUE(r)); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + args_size += size; + } + + vpop(); + --nb_args; + } + } + + /* XXX This should be superfluous. */ + save_regs(0); /* save used temporary registers */ + + /* then, we prepare register passing arguments. + Note that we cannot set RDX and RCX in this loop because gv() + may break these temporary registers. Let's use R10 and R11 + instead of them */ + assert(gen_reg <= REGN); + assert(sse_reg <= 8); for(i = 0; i < nb_args; i++) { - int d; - mode = classify_x86_64_arg(&vtop->type, &type, &size, &align, ®_count); + mode = classify_x86_64_arg(&vtop->type, &type, &size, &align, ®_count); /* Alter stack entry type so that gv() knows how to treat it */ vtop->type = type; - /* Alter stack entry type so that gv() knows how to treat it */ if (mode == x86_64_mode_sse) { - sse_reg -= reg_count; - if (sse_reg + reg_count <= 8) { - if (reg_count == 2) { - ex_rc = RC_XMM0 << (sse_reg + 1); - gv(RC_XMM0 << sse_reg); - }else{ - assert(reg_count == 1); - /* Load directly to register */ - gv(RC_XMM0 << sse_reg); - } - } + if (reg_count == 2) { + sse_reg -= 2; + gv(RC_FRET); /* Use pair load into xmm0 & xmm1 */ + if (sse_reg) { /* avoid redundant movaps %xmm0, %xmm0 */ + /* movaps %xmm0, %xmmN */ + o(0x280f); + o(0xc0 + (sse_reg << 3)); + /* movaps %xmm1, %xmmN */ + o(0x280f); + o(0xc1 + ((sse_reg+1) << 3)); + } + } else { + assert(reg_count == 1); + --sse_reg; + /* Load directly to register */ + gv(RC_XMM0 << sse_reg); + } } else if (mode == x86_64_mode_integer) { - gen_reg -= reg_count; - if (gen_reg + reg_count <= REGN) { - if (reg_count == 2) { - d = arg_regs[gen_reg+1]; - ex_rc = reg_classes[d] & ~RC_MASK; - d = arg_regs[gen_reg]; - gv(reg_classes[d] & ~RC_MASK); - }else{ - assert(reg_count == 1); - d = arg_regs[gen_reg]; - gv(reg_classes[d] & ~RC_MASK); - } - } + /* simple type */ + /* XXX: implicit cast ? */ + gen_reg -= reg_count; + r = gv(RC_INT); + int d = arg_prepare_reg(gen_reg); + orex(1,d,r,0x89); /* mov */ + o(0xc0 + REG_VALUE(r) * 8 + REG_VALUE(d)); + if (reg_count == 2) { + d = arg_prepare_reg(gen_reg+1); + orex(1,d,vtop->r2,0x89); /* mov */ + o(0xc0 + REG_VALUE(vtop->r2) * 8 + REG_VALUE(d)); + } + } + vtop--; + } + assert(gen_reg == 0); + assert(sse_reg == 0); + + /* We shouldn't have many operands on the stack anymore, but the + call address itself is still there, and it might be in %eax + (or edx/ecx) currently, which the below writes would clobber. + So evict all remaining operands here. */ + save_regs(0); + + /* Copy R10 and R11 into RDX and RCX, respectively */ + if (nb_reg_args > 2) { + o(0xd2894c); /* mov %r10, %rdx */ + if (nb_reg_args > 3) { + o(0xd9894c); /* mov %r11, %rcx */ } - vpop(); } - save_regs(0); + oad(0xb8, nb_sse_args < 8 ? nb_sse_args : 8); /* mov nb_sse_args, %eax */ gcall_or_jmp(0); + if (args_size) + gadd_sp(args_size); vtop--; } @@ -1349,8 +1382,7 @@ void gfunc_prolog(CType *func_type) sym = func_type->ref; addr = PTR_SIZE * 2; - pop_stack = loc = 0; - func_scratch = r_loc = 0; + loc = 0; ind += FUNC_PROLOG_SIZE; func_sub_sp_offset = ind; func_ret_sub = 0; @@ -1361,6 +1393,7 @@ void gfunc_prolog(CType *func_type) /* frame pointer and return address */ seen_stack_size = PTR_SIZE * 2; /* count the number of seen parameters */ + sym = func_type->ref; while ((sym = sym->next) != NULL) { type = &sym->type; mode = classify_x86_64_arg(type, NULL, &size, &align, ®_count); @@ -1371,7 +1404,7 @@ void gfunc_prolog(CType *func_type) break; case x86_64_mode_integer: - if (seen_reg_num + reg_count <= REGN) { + if (seen_reg_num + reg_count <= 8) { seen_reg_num += reg_count; } else { seen_reg_num = 8; @@ -1401,19 +1434,19 @@ void gfunc_prolog(CType *func_type) o(0xf845c7); gen_le32(seen_stack_size); - o(0xc084);/* test %al,%al */ - o(0x74);/* je */ - g(4*(8 - seen_sse_num) + 3); - /* save all register passing arguments */ for (i = 0; i < 8; i++) { loc -= 16; - o(0x290f);/* movaps %xmm1-7,-XXX(%rbp) */ + o(0xd60f66); /* movq */ gen_modrm(7 - i, VT_LOCAL, NULL, loc); + /* movq $0, loc+8(%rbp) */ + o(0x85c748); + gen_le32(loc + 8); + gen_le32(0); + } + for (i = 0; i < REGN; i++) { + push_arg_reg(REGN-1-i); } - for (i = 0; i < (REGN - seen_reg_num); i++) { - push_arg_reg(REGN-1 - i); - } } sym = func_type->ref; @@ -1497,8 +1530,7 @@ void gfunc_epilog(void) g(func_ret_sub >> 8); } /* align local size to word & save local variables */ - v = (func_scratch -loc + 15) & -16; - reloc_use(r_loc, func_scratch); + v = (-loc + 15) & -16; saved_ind = ind; ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; o(0xe5894855); /* push %rbp, mov %rsp, %rbp */ @@ -1556,7 +1588,7 @@ int gtst(int inv, int t) } g(0x0f); t = psym((vtop->c.i - 16) ^ inv, t); - } else if (v == VT_JMP || v == VT_JMPI) { + } else { /* VT_JMP || VT_JMPI */ /* && or || optimization */ if ((v & 1) == inv) { /* insert vtop->c jump list in t */ @@ -1569,23 +1601,6 @@ int gtst(int inv, int t) t = gjmp(t); gsym(vtop->c.i); } - } else { - if (is_float(vtop->type.t) || - (vtop->type.t & VT_BTYPE) == VT_LLONG) { - vpushi(0); - gen_op(TOK_NE); - } - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - /* constant jmp optimization */ - if ((vtop->c.i != 0) != inv) - t = gjmp(t); - } else { - v = gv(RC_INT); - orex(0,v,v,0x85); - o(0xc0 + REG_VALUE(v) * 9); - g(0x0f); - t = psym(0x85 ^ inv, t); - } } vtop--; return t; @@ -1594,42 +1609,39 @@ int gtst(int inv, int t) /* generate an integer binary operation */ void gen_opi(int op) { - int r, fr, opc, fc, c, ll, uu, cc, tt2; + int r, fr, opc, c; + int ll, uu, cc; - fr = vtop[0].r; - fc = vtop->c.ul; ll = is64_type(vtop[-1].type.t); - cc = (fr & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - tt2 = (fr & (VT_LVAL | VT_LVAL_TYPE)) == VT_LVAL; + uu = (vtop[-1].type.t & VT_UNSIGNED) != 0; + cc = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; switch(op) { case '+': case TOK_ADDC1: /* add with carry generation */ opc = 0; gen_op8: - vswap(); - r = gv(RC_INT); - vswap(); if (cc && (!ll || (int)vtop->c.ll == vtop->c.ll)) { /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); c = vtop->c.i; if (c == (char)c) { /* XXX: generate inc and dec for smaller code ? */ - orex(ll, r, 0, 0x83); - o(0xc0 + REG_VALUE(r) + opc*8); - g(c); + orex(ll, r, 0, 0x83); + o(0xc0 | (opc << 3) | REG_VALUE(r)); + g(c); } else { orex(ll, r, 0, 0x81); - oad(0xc0 + REG_VALUE(r) + opc*8, c); + oad(0xc0 | (opc << 3) | REG_VALUE(r), c); } } else { - if(!tt2) - fr = gv(RC_INT); - orex(ll, fr, r, 0x03 + opc*8); - if(fr >= VT_CONST) - gen_modrm(r, fr, vtop->sym, fc); - else - o(0xc0 + REG_VALUE(fr) + REG_VALUE(r)*8); + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + orex(ll, r, fr, (opc << 3) | 0x01); + o(0xc0 + REG_VALUE(r) + REG_VALUE(fr) * 8); } vtop--; if (op >= TOK_ULT && op <= TOK_GT) { @@ -1657,27 +1669,11 @@ void gen_opi(int op) opc = 1; goto gen_op8; case '*': - opc = 5; - vswap(); - r = gv(RC_INT); - vswap(); - if(!tt2) - fr = gv(RC_INT); - if(r == TREG_RAX){ - if(fr != TREG_RDX) - save_reg(TREG_RDX); - orex(ll, fr, r, 0xf7); - if(fr >= VT_CONST) - gen_modrm(opc, fr, vtop->sym, fc); - else - o(0xc0 + REG_VALUE(fr) + opc*8); - }else{ - orex(ll, fr, r, 0xaf0f); /* imul fr, r */ - if(fr >= VT_CONST) - gen_modrm(r, fr, vtop->sym, fc); - else - o(0xc0 + REG_VALUE(fr) + REG_VALUE(r)*8); - } + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + orex(ll, fr, r, 0xaf0f); /* imul fr, r */ + o(0xc0 + REG_VALUE(fr) + REG_VALUE(r) * 8); vtop--; break; case TOK_SHL: @@ -1689,62 +1685,47 @@ void gen_opi(int op) case TOK_SAR: opc = 7; gen_shift: + opc = 0xc0 | (opc << 3); if (cc) { /* constant case */ vswap(); r = gv(RC_INT); vswap(); - c = vtop->c.i; - if(c == 1){ - orex(ll, r, 0, 0xd1); - o(0xc0 + REG_VALUE(r) + opc*8); - }else{ - orex(ll, r, 0, 0xc1); /* shl/shr/sar $xxx, r */ - o(0xc0 + REG_VALUE(r) + opc*8); - g(c & (ll ? 0x3f : 0x1f)); - } + orex(ll, r, 0, 0xc1); /* shl/shr/sar $xxx, r */ + o(opc | REG_VALUE(r)); + g(vtop->c.i & (ll ? 63 : 31)); } else { /* we generate the shift in ecx */ gv2(RC_INT, RC_RCX); r = vtop[-1].r; orex(ll, r, 0, 0xd3); /* shl/shr/sar %cl, r */ - o(0xc0 + REG_VALUE(r) + opc*8); + o(opc | REG_VALUE(r)); } vtop--; break; case TOK_UDIV: case TOK_UMOD: - opc = 6; uu = 1; goto divmod; case '/': case '%': case TOK_PDIV: - opc = 7; uu = 0; divmod: /* first operand must be in eax */ /* XXX: need better constraint for second operand */ - if(!tt2){ - gv2(RC_RAX, RC_INT2); - fr = vtop[0].r; - }else{ - vswap(); - gv(RC_RAX); - vswap(); - } - save_reg(TREG_RDX); - orex(ll, 0, 0, uu ? 0xd231 : 0x99); /* xor %edx,%edx : cdq RDX:RAX <- sign-extend of RAX. */ - orex(ll, fr, 0, 0xf7); /* div fr, %eax */ - if(fr >= VT_CONST) - gen_modrm(opc, fr, vtop->sym, fc); - else - o(0xc0 + REG_VALUE(fr) + opc*8); + gv2(RC_RAX, RC_RCX); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + save_reg(TREG_RDX); + orex(ll, 0, 0, uu ? 0xd231 : 0x99); /* xor %edx,%edx : cqto */ + orex(ll, fr, 0, 0xf7); /* div fr, %eax */ + o((uu ? 0xf0 : 0xf8) + REG_VALUE(fr)); if (op == '%' || op == TOK_UMOD) r = TREG_RDX; else r = TREG_RAX; - vtop--; vtop->r = r; break; default: @@ -1763,8 +1744,9 @@ void gen_opl(int op) /* XXX: need to use ST1 too */ void gen_opf(int op) { - int a, ft, fc, swapped, fr, r; - int float_type = (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT; + int a, ft, fc, swapped, r; + int float_type = + (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT; /* convert constants to memory references */ if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { @@ -1775,23 +1757,21 @@ void gen_opf(int op) if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) gv(float_type); - swapped = 0; - fc = vtop->c.ul; - ft = vtop->type.t; - - if ((ft & VT_BTYPE) == VT_LDOUBLE) { - /* swap the stack if needed so that t1 is the register and t2 is - the memory reference */ - /* must put at least one value in the floating point register */ - if ((vtop[-1].r & VT_LVAL) && (vtop[0].r & VT_LVAL)) { - vswap(); - gv(float_type); - vswap(); - } - if (vtop[-1].r & VT_LVAL) { - vswap(); - swapped = 1; - } + /* must put at least one value in the floating point register */ + if ((vtop[-1].r & VT_LVAL) && + (vtop[0].r & VT_LVAL)) { + vswap(); + gv(float_type); + vswap(); + } + swapped = 0; + /* swap the stack if needed so that t1 is the register and t2 is + the memory reference */ + if (vtop[-1].r & VT_LVAL) { + vswap(); + swapped = 1; + } + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { if (op >= TOK_ULT && op <= TOK_GT) { /* load on stack second operand */ load(TREG_ST0, vtop); @@ -1802,10 +1782,10 @@ void gen_opf(int op) swapped = 0; if (swapped) o(0xc9d9); /* fxch %st(1) */ - if (op == TOK_EQ || op == TOK_NE) - o(0xe9da); /* fucompp */ - else - o(0xd9de); /* fcompp */ + if (op == TOK_EQ || op == TOK_NE) + o(0xe9da); /* fucompp */ + else + o(0xd9de); /* fcompp */ o(0xe0df); /* fnstsw %ax */ if (op == TOK_EQ) { o(0x45e480); /* and $0x45, %ah */ @@ -1828,6 +1808,7 @@ void gen_opf(int op) /* no memory reference possible for long double operations */ load(TREG_ST0, vtop); swapped = !swapped; + switch(op) { default: case '+': @@ -1847,45 +1828,63 @@ void gen_opf(int op) a++; break; } + ft = vtop->type.t; + fc = vtop->c.ul; o(0xde); /* fxxxp %st, %st(1) */ o(0xc1 + (a << 3)); vtop--; } } else { - vswap(); - gv(float_type); - vswap(); - fr = vtop->r; - r = vtop[-1].r; if (op >= TOK_ULT && op <= TOK_GT) { - switch(op){ - case TOK_LE: - op = TOK_ULE; /* setae */ - break; - case TOK_LT: - op = TOK_ULT; - break; - case TOK_GE: - op = TOK_UGE; - break; - case TOK_GT: - op = TOK_UGT; /* seta */ - break; - } - assert(!(vtop[-1].r & VT_LVAL)); - if ((ft & VT_BTYPE) == VT_DOUBLE) - o(0x66); - o(0x2e0f); /* ucomisd */ - if(fr >= VT_CONST) - gen_modrm(r, fr, vtop->sym, fc); - else - o(0xc0 + REG_VALUE(fr) + REG_VALUE(r)*8); + /* if saved lvalue, then we must reload it */ + r = vtop->r; + fc = vtop->c.ul; + if ((r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + + if (op == TOK_EQ || op == TOK_NE) { + swapped = 0; + } else { + if (op == TOK_LE || op == TOK_LT) + swapped = !swapped; + if (op == TOK_LE || op == TOK_GE) { + op = 0x93; /* setae */ + } else { + op = 0x97; /* seta */ + } + } + + if (swapped) { + gv(RC_FLOAT); + vswap(); + } + assert(!(vtop[-1].r & VT_LVAL)); + + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + o(0x66); + if (op == TOK_EQ || op == TOK_NE) + o(0x2e0f); /* ucomisd */ + else + o(0x2f0f); /* comisd */ + + if (vtop->r & VT_LVAL) { + gen_modrm(vtop[-1].r, r, vtop->sym, fc); + } else { + o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8); + } + vtop--; vtop->r = VT_CMP; vtop->c.i = op | 0x100; } else { - assert((vtop->type.t & VT_BTYPE) != VT_LDOUBLE); - /* no memory reference possible for long double operations */ + assert((vtop->type.t & VT_BTYPE) != VT_LDOUBLE); switch(op) { default: case '+': @@ -1901,20 +1900,44 @@ void gen_opf(int op) a = 6; break; } - assert((ft & VT_BTYPE) != VT_LDOUBLE); - assert(!(vtop[-1].r & VT_LVAL)); - if ((ft & VT_BTYPE) == VT_DOUBLE) { - o(0xf2); - } else { - o(0xf3); - } - o(0x0f); - o(0x58 + a); - if(fr >= VT_CONST) - gen_modrm(r, fr, vtop->sym, fc); - else - o(0xc0 + REG_VALUE(fr) + REG_VALUE(r)*8); - vtop--; + ft = vtop->type.t; + fc = vtop->c.ul; + assert((ft & VT_BTYPE) != VT_LDOUBLE); + + r = vtop->r; + /* if saved lvalue, then we must reload it */ + if ((vtop->r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + + assert(!(vtop[-1].r & VT_LVAL)); + if (swapped) { + assert(vtop->r & VT_LVAL); + gv(RC_FLOAT); + vswap(); + } + + if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0xf2); + } else { + o(0xf3); + } + o(0x0f); + o(0x58 + a); + + if (vtop->r & VT_LVAL) { + gen_modrm(vtop[-1].r, r, vtop->sym, fc); + } else { + o(0xc0 + REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8); + } + + vtop--; } } } @@ -1923,96 +1946,103 @@ void gen_opf(int op) and 'long long' cases. */ void gen_cvt_itof(int t) { - int ft, bt, tbt, r; - - ft = vtop->type.t; - bt = ft & VT_BTYPE; - tbt = t & VT_BTYPE; - r = gv(RC_INT); - - if (tbt == VT_LDOUBLE) { + if ((t & VT_BTYPE) == VT_LDOUBLE) { save_reg(TREG_ST0); - if ((ft & VT_BTYPE) == VT_LLONG) { + gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { /* signed long long to float/double/long double (unsigned case is handled generically) */ - o(0x50 + REG_VALUE(r)); /* push r */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ o(0x242cdf); /* fildll (%rsp) */ o(0x08c48348); /* add $8, %rsp */ - } else if ((ft & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) { + } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED)) { /* unsigned int to float/double/long double */ o(0x6a); /* push $0 */ g(0x00); - o(0x50 + REG_VALUE(r)); /* push r */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ o(0x242cdf); /* fildll (%rsp) */ o(0x10c48348); /* add $16, %rsp */ } else { /* int to float/double/long double */ - o(0x50 + REG_VALUE(r)); /* push r */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ o(0x2404db); /* fildl (%rsp) */ o(0x08c48348); /* add $8, %rsp */ } vtop->r = TREG_ST0; } else { - int r_xmm; - r_xmm = get_reg(RC_FLOAT); - o(0xf2 + (tbt == VT_FLOAT)); - if ((ft & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || bt == VT_LLONG) { + int r = get_reg(RC_FLOAT); + gv(RC_INT); + o(0xf2 + ((t & VT_BTYPE) == VT_FLOAT?1:0)); + if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED) || + (vtop->type.t & VT_BTYPE) == VT_LLONG) { o(0x48); /* REX */ } o(0x2a0f); - o(0xc0 + REG_VALUE(r) + REG_VALUE(r_xmm)*8); /* cvtsi2sd or cvtsi2ss */ - vtop->r = r_xmm; + o(0xc0 + (vtop->r & VT_VALMASK) + REG_VALUE(r)*8); /* cvtsi2sd */ + vtop->r = r; } } /* convert from one floating point type to another */ void gen_cvt_ftof(int t) { - int ft, bt, tbt, r; + int ft, bt, tbt; ft = vtop->type.t; bt = ft & VT_BTYPE; tbt = t & VT_BTYPE; - - if(bt == VT_LDOUBLE) - r = get_reg(RC_FLOAT); - else - r = gv(RC_FLOAT); - if (bt == VT_FLOAT) { + + if (bt == VT_FLOAT) { + gv(RC_FLOAT); if (tbt == VT_DOUBLE) { + o(0x140f); /* unpcklps */ + o(0xc0 + REG_VALUE(vtop->r)*9); o(0x5a0f); /* cvtps2pd */ - o(0xc0 + REG_VALUE(r) + REG_VALUE(r) * 8); + o(0xc0 + REG_VALUE(vtop->r)*9); } else if (tbt == VT_LDOUBLE) { - /* movss %xmm0-7,-0x10(%rsp) */ + save_reg(RC_ST0); + /* movss %xmm0,-0x10(%rsp) */ o(0x110ff3); - o(0xf02444 + REG_VALUE(r)*8); + o(0x44 + REG_VALUE(vtop->r)*8); + o(0xf024); o(0xf02444d9); /* flds -0x10(%rsp) */ vtop->r = TREG_ST0; } } else if (bt == VT_DOUBLE) { + gv(RC_FLOAT); if (tbt == VT_FLOAT) { + o(0x140f66); /* unpcklpd */ + o(0xc0 + REG_VALUE(vtop->r)*9); o(0x5a0f66); /* cvtpd2ps */ - o(0xc0 + REG_VALUE(r) + REG_VALUE(r) * 8); + o(0xc0 + REG_VALUE(vtop->r)*9); } else if (tbt == VT_LDOUBLE) { - /* movsd %xmm0-7,-0x10(%rsp) */ + save_reg(RC_ST0); + /* movsd %xmm0,-0x10(%rsp) */ o(0x110ff2); - o(0xf02444 + REG_VALUE(r)*8); + o(0x44 + REG_VALUE(vtop->r)*8); + o(0xf024); o(0xf02444dd); /* fldl -0x10(%rsp) */ vtop->r = TREG_ST0; } } else { + int r; gv(RC_ST0); + r = get_reg(RC_FLOAT); if (tbt == VT_DOUBLE) { o(0xf0245cdd); /* fstpl -0x10(%rsp) */ - /* movsd -0x10(%rsp),%xmm0-7 */ + /* movsd -0x10(%rsp),%xmm0 */ o(0x100ff2); - o(0xf02444 + REG_VALUE(r)*8); + o(0x44 + REG_VALUE(r)*8); + o(0xf024); vtop->r = r; } else if (tbt == VT_FLOAT) { o(0xf0245cd9); /* fstps -0x10(%rsp) */ - /* movss -0x10(%rsp),%xmm0-7 */ + /* movss -0x10(%rsp),%xmm0 */ o(0x100ff3); - o(0xf02444 + REG_VALUE(r)*8); + o(0x44 + REG_VALUE(r)*8); + o(0xf024); vtop->r = r; } } @@ -2021,20 +2051,20 @@ void gen_cvt_ftof(int t) /* convert fp to int 't' type */ void gen_cvt_ftoi(int t) { - int ft, bt, ll, r, r_xmm; - + int ft, bt, size, r; ft = vtop->type.t; bt = ft & VT_BTYPE; - if (bt == VT_LDOUBLE) { gen_cvt_ftof(VT_DOUBLE); bt = VT_DOUBLE; } - r_xmm = gv(RC_FLOAT); - if ((t & VT_BTYPE) == VT_INT) - ll = 0; + + gv(RC_FLOAT); + if (t != VT_INT) + size = 8; else - ll = 1; + size = 4; + r = get_reg(RC_INT); if (bt == VT_FLOAT) { o(0xf3); @@ -2043,8 +2073,8 @@ void gen_cvt_ftoi(int t) } else { assert(0); } - orex(ll, r, r_xmm, 0x2c0f); /* cvttss2si or cvttsd2si */ - o(0xc0 + REG_VALUE(r_xmm) + (REG_VALUE(r) << 3)); + orex(size == 8, r, 0, 0x2c0f); /* cvttss2si or cvttsd2si */ + o(0xc0 + REG_VALUE(vtop->r) + REG_VALUE(r)*8); vtop->r = r; } @@ -2068,19 +2098,27 @@ ST_FUNC void gen_vla_sp_restore(int addr) { /* Subtract from the stack pointer, and push the resulting value onto the stack */ ST_FUNC void gen_vla_alloc(CType *type, int align) { +#ifdef TCC_TARGET_PE + /* alloca does more than just adjust %rsp on Windows */ + vpush_global_sym(&func_old_type, TOK_alloca); + vswap(); /* Move alloca ref past allocation size */ + gfunc_call(1); + vset(type, REG_IRET, 0); +#else int r; r = gv(RC_INT); /* allocation size */ /* sub r,%rsp */ o(0x2b48); o(0xe0 | REG_VALUE(r)); - /* and ~15, %rsp */ + /* We align to 16 bytes rather than align */ + /* and ~15, %rsp */ o(0xf0e48348); /* mov %rsp, r */ - orex(1, 0, r, 0x8d); - o(0x2484 | (REG_VALUE(r)*8)); - r_loc = reloc_add(r_loc); + o(0x8948); + o(0xe0 | REG_VALUE(r)); vpop(); vset(type, r, 0); +#endif } -- cgit v1.3.1