From 826d1afd58c2e064a9c8fdb09eda1b08469de1a8 Mon Sep 17 00:00:00 2001 From: pommicket Date: Fri, 18 Feb 2022 12:36:57 -0500 Subject: newer version of tcc almost working --- 05/tcc-0.9.25/tccgen.c | 5123 ------------------------------------------------ 1 file changed, 5123 deletions(-) delete mode 100644 05/tcc-0.9.25/tccgen.c (limited to '05/tcc-0.9.25/tccgen.c') diff --git a/05/tcc-0.9.25/tccgen.c b/05/tcc-0.9.25/tccgen.c deleted file mode 100644 index 860e580..0000000 --- a/05/tcc-0.9.25/tccgen.c +++ /dev/null @@ -1,5123 +0,0 @@ -/* - * TCC - Tiny C Compiler - * - * Copyright (c) 2001-2004 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -void swap(int *p, int *q) -{ - int t; - t = *p; - *p = *q; - *q = t; -} - -void vsetc(CType *type, int r, CValue *vc) -{ - int v; - - if (vtop >= vstack + (VSTACK_SIZE - 1)) - error("memory full"); - /* cannot let cpu flags if other instruction are generated. Also - avoid leaving VT_JMP anywhere except on the top of the stack - because it would complicate the code generator. */ - if (vtop >= vstack) { - v = vtop->r & VT_VALMASK; - if (v == VT_CMP || (v & ~1) == VT_JMP) - gv(RC_INT); - } - vtop++; - vtop->type = *type; - vtop->r = r; - vtop->r2 = VT_CONST; - vtop->c = *vc; -} - -/* push integer constant */ -void vpushi(int v) -{ - CValue cval; - cval.i = v; - vsetc(&int_type, VT_CONST, &cval); -} - -/* push long long constant */ -void vpushll(long long v) -{ - CValue cval; - CType ctype; - ctype.t = VT_LLONG; - cval.ull = v; - vsetc(&ctype, VT_CONST, &cval); -} - -/* Return a static symbol pointing to a section */ -static Sym *get_sym_ref(CType *type, Section *sec, - unsigned long offset, unsigned long size) -{ - int v; - Sym *sym; - - v = anon_sym++; - sym = global_identifier_push(v, type->t | VT_STATIC, 0); - sym->type.ref = type->ref; - sym->r = VT_CONST | VT_SYM; - put_extern_sym(sym, sec, offset, size); - return sym; -} - -/* push a reference to a section offset by adding a dummy symbol */ -static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size) -{ - CValue cval; - - cval.ul = 0; - vsetc(type, VT_CONST | VT_SYM, &cval); - vtop->sym = get_sym_ref(type, sec, offset, size); -} - -/* define a new external reference to a symbol 'v' of type 'u' */ -static Sym *external_global_sym(int v, CType *type, int r) -{ - Sym *s; - - s = sym_find(v); - if (!s) { - /* push forward reference */ - s = global_identifier_push(v, type->t | VT_EXTERN, 0); - s->type.ref = type->ref; - s->r = r | VT_CONST | VT_SYM; - } - return s; -} - -/* define a new external reference to a symbol 'v' of type 'u' */ -static Sym *external_sym(int v, CType *type, int r) -{ - Sym *s; - - s = sym_find(v); - if (!s) { - /* push forward reference */ - s = sym_push(v, type, r | VT_CONST | VT_SYM, 0); - s->type.t |= VT_EXTERN; - } else { - if (!is_compatible_types(&s->type, type)) - error("incompatible types for redefinition of '%s'", - get_tok_str(v, NULL)); - } - return s; -} - -/* push a reference to global symbol v */ -static void vpush_global_sym(CType *type, int v) -{ - Sym *sym; - CValue cval; - - sym = external_global_sym(v, type, 0); - cval.ul = 0; - vsetc(type, VT_CONST | VT_SYM, &cval); - vtop->sym = sym; -} - -void vset(CType *type, int r, int v) -{ - CValue cval; - - cval.i = v; - vsetc(type, r, &cval); -} - -void vseti(int r, int v) -{ - CType type; - type.t = VT_INT; - vset(&type, r, v); -} - -void vswap(void) -{ - SValue tmp; - - tmp = vtop[0]; - vtop[0] = vtop[-1]; - vtop[-1] = tmp; -} - -void vpushv(SValue *v) -{ - if (vtop >= vstack + (VSTACK_SIZE - 1)) - error("memory full"); - vtop++; - *vtop = *v; -} - -void vdup(void) -{ - vpushv(vtop); -} - -/* save r to the memory stack, and mark it as being free */ -void save_reg(int r) -{ - int l, saved, size, align; - SValue *p, sv; - CType *type; - - /* modify all stack values */ - saved = 0; - l = 0; - for(p=vstack;p<=vtop;p++) { - if ((p->r & VT_VALMASK) == r || - ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) { - /* must save value on stack if not already done */ - if (!saved) { - /* NOTE: must reload 'r' because r might be equal to r2 */ - r = p->r & VT_VALMASK; - /* store register in the stack */ - type = &p->type; - if ((p->r & VT_LVAL) || - (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) -#ifdef TCC_TARGET_X86_64 - type = &char_pointer_type; -#else - type = &int_type; -#endif - size = type_size(type, &align); - loc = (loc - size) & -align; - sv.type.t = type->t; - sv.r = VT_LOCAL | VT_LVAL; - sv.c.ul = loc; - store(r, &sv); -#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) - /* x86 specific: need to pop fp register ST0 if saved */ - if (r == TREG_ST0) { - o(0xd9dd); /* fstp %st(1) */ - } -#endif -#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; - } - p->r2 = VT_CONST; - p->c.ul = l; - } - } -} - -/* find a register of class 'rc2' with at most one reference on stack. - * If none, call get_reg(rc) */ -int get_reg_ex(int rc, int rc2) -{ - int r; - SValue *p; - - for(r=0;rr & VT_VALMASK) == r || - (p->r2 & VT_VALMASK) == r) - n++; - } - if (n <= 1) - return r; - } - } - return get_reg(rc); -} - -/* find a free register of class 'rc'. If none, save one register */ -int get_reg(int rc) -{ - int r; - SValue *p; - - /* find a free register */ - 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++) { - r = p->r & VT_VALMASK; - if (r < VT_CONST && (reg_classes[r] & rc)) - goto save_found; - /* also look at second register (if long long) */ - r = p->r2 & VT_VALMASK; - if (r < VT_CONST && (reg_classes[r] & rc)) { - save_found: - save_reg(r); - return r; - } - } - /* Should never comes here */ - return -1; -} - -/* save registers up to (vtop - n) stack entry */ -void save_regs(int n) -{ - int r; - SValue *p, *p1; - p1 = vtop - n; - for(p = vstack;p <= p1; p++) { - r = p->r & VT_VALMASK; - if (r < VT_CONST) { - save_reg(r); - } - } -} - -/* move register 's' to 'r', and flush previous value of r to memory - if needed */ -void move_reg(int r, int s) -{ - SValue sv; - - if (r != s) { - save_reg(r); - sv.type.t = VT_INT; - sv.r = s; - sv.c.ul = 0; - load(r, &sv); - } -} - -/* get address of vtop (vtop MUST BE an lvalue) */ -void gaddrof(void) -{ - vtop->r &= ~VT_LVAL; - /* tricky: if saved lvalue, then we can go back to lvalue */ - if ((vtop->r & VT_VALMASK) == VT_LLOCAL) - vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL; -} - -#ifdef CONFIG_TCC_BCHECK -/* generate lvalue bound code */ -void gbound(void) -{ - int lval_type; - CType type1; - - vtop->r &= ~VT_MUSTBOUND; - /* if lvalue, then use checking code before dereferencing */ - if (vtop->r & VT_LVAL) { - /* if not VT_BOUNDED value, then make one */ - if (!(vtop->r & VT_BOUNDED)) { - lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL); - /* must save type because we must set it to int to get pointer */ - type1 = vtop->type; - vtop->type.t = VT_INT; - gaddrof(); - vpushi(0); - gen_bounded_ptr_add(); - vtop->r |= lval_type; - vtop->type = type1; - } - /* then check for dereferencing */ - gen_bounded_ptr_deref(); - } -} -#endif - -/* store vtop a register belonging to class 'rc'. lvalues are - converted to values. Cannot be used if cannot be converted to - register value (such as structures). */ -int gv(int rc) -{ - int r, rc2, bit_pos, bit_size, size, align, i; - - /* NOTE: get_reg can modify vstack[] */ - if (vtop->type.t & VT_BITFIELD) { - CType type; - int bits = 32; - bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; - bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; - /* remove bit field info to avoid loops */ - vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); - /* cast to int to propagate signedness in following ops */ - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - type.t = VT_LLONG; - bits = 64; - } else - type.t = VT_INT; - if((vtop->type.t & VT_UNSIGNED) || - (vtop->type.t & VT_BTYPE) == VT_BOOL) - type.t |= VT_UNSIGNED; - gen_cast(&type); - /* generate shifts */ - vpushi(bits - (bit_pos + bit_size)); - gen_op(TOK_SHL); - vpushi(bits - bit_size); - /* NOTE: transformed to SHR if unsigned */ - gen_op(TOK_SAR); - r = gv(rc); - } else { - if (is_float(vtop->type.t) && - (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - Sym *sym; - int *ptr; - unsigned long offset; -#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) - CValue check; -#endif - - /* XXX: unify with initializers handling ? */ - /* CPUs usually cannot use float constants, so we store them - generically in data segment */ - size = type_size(&vtop->type, &align); - offset = (data_section->data_offset + align - 1) & -align; - data_section->data_offset = offset; - /* XXX: not portable yet */ -#if defined(__i386__) || defined(__x86_64__) - /* Zero pad x87 tenbyte long doubles */ - if (size == LDOUBLE_SIZE) - vtop->c.tab[2] &= 0xffff; -#endif - ptr = section_ptr_add(data_section, size); - size = size >> 2; -#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) - check.d = 1; - if(check.tab[0]) - for(i=0;ic.tab[size-1-i]; - else -#endif - for(i=0;ic.tab[i]; - sym = get_sym_ref(&vtop->type, data_section, offset, size << 2); - vtop->r |= VT_LVAL | VT_SYM; - vtop->sym = sym; - vtop->c.ul = 0; - } -#ifdef CONFIG_TCC_BCHECK - if (vtop->r & VT_MUSTBOUND) - gbound(); -#endif - - r = vtop->r & VT_VALMASK; - rc2 = RC_INT; - if (rc == RC_IRET) - rc2 = RC_LRET; - /* 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) || - ((vtop->type.t & VT_BTYPE) == VT_LLONG && - !(reg_classes[vtop->r2] & rc2))) { - r = get_reg(rc); -#ifndef TCC_TARGET_X86_64 - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { - int r2; - unsigned long long ll; - /* two register type load : expand to two words - temporarily */ - if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { - /* load constant */ - ll = vtop->c.ull; - vtop->c.ui = ll; /* first word */ - load(r, vtop); - vtop->r = r; /* save register value */ - vpushi(ll >> 32); /* second word */ - } else if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */ - (vtop->r & VT_LVAL)) { - /* We do not want to modifier the long long - pointer here, so the safest (and less - efficient) is to save all the other registers - in the stack. XXX: totally inefficient. */ - save_regs(1); - /* load from memory */ - load(r, vtop); - vdup(); - vtop[-1].r = r; /* save register value */ - /* increment pointer to get second word */ - vtop->type.t = VT_INT; - gaddrof(); - vpushi(4); - gen_op('+'); - vtop->r |= VT_LVAL; - } else { - /* move registers */ - load(r, vtop); - vdup(); - vtop[-1].r = r; /* save register value */ - vtop->r = vtop[-1].r2; - } - /* allocate second register */ - r2 = get_reg(rc2); - load(r2, vtop); - vpop(); - /* write second register */ - vtop->r2 = r2; - } else -#endif - if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) { - int t1, t; - /* lvalue of scalar type : need to use lvalue type - because of possible cast */ - t = vtop->type.t; - t1 = t; - /* compute memory access type */ - if (vtop->r & VT_LVAL_BYTE) - t = VT_BYTE; - else if (vtop->r & VT_LVAL_SHORT) - t = VT_SHORT; - if (vtop->r & VT_LVAL_UNSIGNED) - t |= VT_UNSIGNED; - vtop->type.t = t; - load(r, vtop); - /* restore wanted type */ - vtop->type.t = t1; - } else { - /* one register type load */ - load(r, vtop); - } - } - vtop->r = r; -#ifdef TCC_TARGET_C67 - /* uses register pairs for doubles */ - if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) - vtop->r2 = r+1; -#endif - } - return r; -} - -/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */ -void gv2(int rc1, int rc2) -{ - int v; - - /* generate more generic register first. But VT_JMP or VT_CMP - values must be generated first in all cases to avoid possible - reload errors */ - v = vtop[0].r & VT_VALMASK; - if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) { - vswap(); - gv(rc1); - vswap(); - gv(rc2); - /* test if reload is needed for first register */ - if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { - vswap(); - gv(rc1); - vswap(); - } - } else { - gv(rc2); - vswap(); - gv(rc1); - vswap(); - /* test if reload is needed for first register */ - if ((vtop[0].r & VT_VALMASK) >= VT_CONST) { - gv(rc2); - } - } -} - -/* wrapper around RC_FRET to return a register by type */ -int rc_fret(int t) -{ -#ifdef TCC_TARGET_X86_64 - if (t == VT_LDOUBLE) { - return RC_ST0; - } -#endif - return RC_FRET; -} - -/* wrapper around REG_FRET to return a register by type */ -int reg_fret(int t) -{ -#ifdef TCC_TARGET_X86_64 - if (t == VT_LDOUBLE) { - return TREG_ST0; - } -#endif - return REG_FRET; -} - -/* expand long long on stack in two int registers */ -void lexpand(void) -{ - int u; - - u = vtop->type.t & VT_UNSIGNED; - gv(RC_INT); - vdup(); - vtop[0].r = vtop[-1].r2; - vtop[0].r2 = VT_CONST; - vtop[-1].r2 = VT_CONST; - vtop[0].type.t = VT_INT | u; - vtop[-1].type.t = VT_INT | u; -} - -#ifdef TCC_TARGET_ARM -/* expand long long on stack */ -void lexpand_nr(void) -{ - int u,v; - - u = vtop->type.t & VT_UNSIGNED; - vdup(); - vtop->r2 = VT_CONST; - vtop->type.t = VT_INT | u; - v=vtop[-1].r & (VT_VALMASK | VT_LVAL); - if (v == VT_CONST) { - vtop[-1].c.ui = vtop->c.ull; - vtop->c.ui = vtop->c.ull >> 32; - vtop->r = VT_CONST; - } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) { - vtop->c.ui += 4; - vtop->r = vtop[-1].r; - } else if (v > VT_CONST) { - vtop--; - lexpand(); - } else - vtop->r = vtop[-1].r2; - vtop[-1].r2 = VT_CONST; - vtop[-1].type.t = VT_INT | u; -} -#endif - -/* build a long long from two ints */ -void lbuild(int t) -{ - gv2(RC_INT, RC_INT); - vtop[-1].r2 = vtop[0].r; - vtop[-1].type.t = t; - vpop(); -} - -/* rotate n first stack elements to the bottom - I1 ... In -> I2 ... In I1 [top is right] -*/ -void vrotb(int n) -{ - int i; - SValue tmp; - - tmp = vtop[-n + 1]; - for(i=-n+1;i!=0;i++) - vtop[i] = vtop[i+1]; - vtop[0] = tmp; -} - -/* rotate n first stack elements to the top - I1 ... In -> In I1 ... I(n-1) [top is right] - */ -void vrott(int n) -{ - int i; - SValue tmp; - - tmp = vtop[0]; - for(i = 0;i < n - 1; i++) - vtop[-i] = vtop[-i - 1]; - vtop[-n + 1] = tmp; -} - -#ifdef TCC_TARGET_ARM -/* like vrott but in other direction - In ... I1 -> I(n-1) ... I1 In [top is right] - */ -void vnrott(int n) -{ - int i; - SValue tmp; - - tmp = vtop[-n + 1]; - for(i = n - 1; i > 0; i--) - vtop[-i] = vtop[-i + 1]; - vtop[0] = tmp; -} -#endif - -/* pop stack value */ -void vpop(void) -{ - int v; - v = vtop->r & VT_VALMASK; -#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) - /* for x86, we need to pop the FP stack */ - if (v == TREG_ST0 && !nocode_wanted) { - o(0xd9dd); /* fstp %st(1) */ - } else -#endif - if (v == VT_JMP || v == VT_JMPI) { - /* need to put correct jump if && or || without test */ - gsym(vtop->c.ul); - } - vtop--; -} - -/* convert stack entry to register and duplicate its value in another - register */ -void gv_dup(void) -{ - int rc, t, r, r1; - SValue sv; - - t = vtop->type.t; - if ((t & VT_BTYPE) == VT_LLONG) { - lexpand(); - gv_dup(); - vswap(); - vrotb(3); - gv_dup(); - vrotb(4); - /* stack: H L L1 H1 */ - lbuild(t); - vrotb(3); - vrotb(3); - vswap(); - lbuild(t); - vswap(); - } else { - /* duplicate value */ - rc = RC_INT; - sv.type.t = VT_INT; - if (is_float(t)) { - rc = RC_FLOAT; -#ifdef TCC_TARGET_X86_64 - if ((t & VT_BTYPE) == VT_LDOUBLE) { - rc = RC_ST0; - } -#endif - sv.type.t = t; - } - r = gv(rc); - r1 = get_reg(rc); - sv.r = r; - sv.c.ul = 0; - load(r1, &sv); /* move r to r1 */ - vdup(); - /* duplicates value */ - vtop->r = r1; - } -} - -#ifndef TCC_TARGET_X86_64 -/* generate CPU independent (unsigned) long long operations */ -void gen_opl(int op) -{ - int t, a, b, op1, c, i; - int func; - unsigned short reg_iret = REG_IRET; - unsigned short reg_lret = REG_LRET; - SValue tmp; - - switch(op) { - case '/': - case TOK_PDIV: - func = TOK___divdi3; - goto gen_func; - case TOK_UDIV: - func = TOK___udivdi3; - goto gen_func; - case '%': - func = TOK___moddi3; - goto gen_mod_func; - case TOK_UMOD: - func = TOK___umoddi3; - gen_mod_func: -#ifdef TCC_ARM_EABI - reg_iret = TREG_R2; - reg_lret = TREG_R3; -#endif - gen_func: - /* call generic long long function */ - vpush_global_sym(&func_old_type, func); - vrott(3); - gfunc_call(2); - vpushi(0); - vtop->r = reg_iret; - vtop->r2 = reg_lret; - break; - case '^': - case '&': - case '|': - case '*': - case '+': - case '-': - t = vtop->type.t; - vswap(); - lexpand(); - vrotb(3); - lexpand(); - /* stack: L1 H1 L2 H2 */ - tmp = vtop[0]; - vtop[0] = vtop[-3]; - vtop[-3] = tmp; - tmp = vtop[-2]; - vtop[-2] = vtop[-3]; - vtop[-3] = tmp; - vswap(); - /* stack: H1 H2 L1 L2 */ - if (op == '*') { - vpushv(vtop - 1); - vpushv(vtop - 1); - gen_op(TOK_UMULL); - lexpand(); - /* stack: H1 H2 L1 L2 ML MH */ - for(i=0;i<4;i++) - vrotb(6); - /* stack: ML MH H1 H2 L1 L2 */ - tmp = vtop[0]; - vtop[0] = vtop[-2]; - vtop[-2] = tmp; - /* stack: ML MH H1 L2 H2 L1 */ - gen_op('*'); - vrotb(3); - vrotb(3); - gen_op('*'); - /* stack: ML MH M1 M2 */ - gen_op('+'); - gen_op('+'); - } else if (op == '+' || op == '-') { - /* XXX: add non carry method too (for MIPS or alpha) */ - if (op == '+') - op1 = TOK_ADDC1; - else - op1 = TOK_SUBC1; - gen_op(op1); - /* stack: H1 H2 (L1 op L2) */ - vrotb(3); - vrotb(3); - gen_op(op1 + 1); /* TOK_xxxC2 */ - } else { - gen_op(op); - /* stack: H1 H2 (L1 op L2) */ - vrotb(3); - vrotb(3); - /* stack: (L1 op L2) H1 H2 */ - gen_op(op); - /* stack: (L1 op L2) (H1 op H2) */ - } - /* stack: L H */ - lbuild(t); - break; - case TOK_SAR: - case TOK_SHR: - case TOK_SHL: - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - t = vtop[-1].type.t; - vswap(); - lexpand(); - vrotb(3); - /* stack: L H shift */ - c = (int)vtop->c.i; - /* constant: simpler */ - /* NOTE: all comments are for SHL. the other cases are - done by swaping words */ - vpop(); - if (op != TOK_SHL) - vswap(); - if (c >= 32) { - /* stack: L H */ - vpop(); - if (c > 32) { - vpushi(c - 32); - gen_op(op); - } - if (op != TOK_SAR) { - vpushi(0); - } else { - gv_dup(); - vpushi(31); - gen_op(TOK_SAR); - } - vswap(); - } else { - vswap(); - gv_dup(); - /* stack: H L L */ - vpushi(c); - gen_op(op); - vswap(); - vpushi(32 - c); - if (op == TOK_SHL) - gen_op(TOK_SHR); - else - gen_op(TOK_SHL); - vrotb(3); - /* stack: L L H */ - vpushi(c); - if (op == TOK_SHL) - gen_op(TOK_SHL); - else - gen_op(TOK_SHR); - gen_op('|'); - } - if (op != TOK_SHL) - vswap(); - lbuild(t); - } else { - /* XXX: should provide a faster fallback on x86 ? */ - switch(op) { - case TOK_SAR: - func = TOK___ashrdi3; - goto gen_func; - case TOK_SHR: - func = TOK___lshrdi3; - goto gen_func; - case TOK_SHL: - func = TOK___ashldi3; - goto gen_func; - } - } - break; - default: - /* compare operations */ - t = vtop->type.t; - vswap(); - lexpand(); - vrotb(3); - lexpand(); - /* stack: L1 H1 L2 H2 */ - tmp = vtop[-1]; - vtop[-1] = vtop[-2]; - vtop[-2] = tmp; - /* stack: L1 L2 H1 H2 */ - /* compare high */ - op1 = op; - /* when values are equal, we need to compare low words. since - the jump is inverted, we invert the test too. */ - if (op1 == TOK_LT) - op1 = TOK_LE; - else if (op1 == TOK_GT) - op1 = TOK_GE; - else if (op1 == TOK_ULT) - op1 = TOK_ULE; - else if (op1 == TOK_UGT) - op1 = TOK_UGE; - a = 0; - b = 0; - gen_op(op1); - if (op1 != TOK_NE) { - a = gtst(1, 0); - } - if (op != TOK_EQ) { - /* generate non equal test */ - /* XXX: NOT PORTABLE yet */ - if (a == 0) { - b = gtst(0, 0); - } else { -#if defined(TCC_TARGET_I386) - b = psym(0x850f, 0); -#elif defined(TCC_TARGET_ARM) - b = ind; - o(0x1A000000 | encbranch(ind, 0, 1)); -#elif defined(TCC_TARGET_C67) - error("not implemented"); -#else -#error not supported -#endif - } - } - /* compare low. Always unsigned */ - op1 = op; - if (op1 == TOK_LT) - op1 = TOK_ULT; - else if (op1 == TOK_LE) - op1 = TOK_ULE; - else if (op1 == TOK_GT) - op1 = TOK_UGT; - else if (op1 == TOK_GE) - op1 = TOK_UGE; - gen_op(op1); - a = gtst(1, a); - gsym(b); - vseti(VT_JMPI, a); - break; - } -} -#endif - -typedef unsigned long long _U; - -/* handle integer constant optimizations and various machine - independent opt */ -void gen_opic(int op) -{ - int c1, c2, t1, t2, n; - SValue *v1, *v2; - long long l1, l2; - - v1 = vtop - 1; - v2 = vtop; - t1 = v1->type.t & VT_BTYPE; - t2 = v2->type.t & VT_BTYPE; - - if (t1 == VT_LLONG) - l1 = v1->c.ll; - else if (v1->type.t & VT_UNSIGNED) - l1 = v1->c.ui; - else - l1 = v1->c.i; - - if (t2 == VT_LLONG) - l2 = v2->c.ll; - else if (v2->type.t & VT_UNSIGNED) - l2 = v2->c.ui; - else - l2 = v2->c.i; - - /* currently, we cannot do computations with forward symbols */ - c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - if (c1 && c2) { - switch(op) { - case '+': l1 += l2; break; - case '-': l1 -= l2; break; - case '&': l1 &= l2; break; - case '^': l1 ^= l2; break; - case '|': l1 |= l2; break; - case '*': l1 *= l2; break; - - case TOK_PDIV: - case '/': - case '%': - case TOK_UDIV: - case TOK_UMOD: - /* if division by zero, generate explicit division */ - if (l2 == 0) { - if (const_wanted) - error("division by zero in constant"); - goto general_case; - } - switch(op) { - case '%': l1 %= l2; break; - case TOK_UDIV: l1 = (_U)l1 / l2; break; - case TOK_UMOD: l1 = (_U)l1 % l2; break; - default: l1 /= l2; break; - } - break; - case TOK_SHL: l1 <<= l2; break; - case TOK_SHR: l1 = (_U)l1 >> l2; break; - case TOK_SAR: l1 >>= l2; break; - /* tests */ - case TOK_ULT: l1 = (_U)l1 < (_U)l2; break; - case TOK_UGE: l1 = (_U)l1 >= (_U)l2; break; - case TOK_EQ: l1 = l1 == l2; break; - case TOK_NE: l1 = l1 != l2; break; - case TOK_ULE: l1 = (_U)l1 <= (_U)l2; break; - case TOK_UGT: l1 = (_U)l1 > (_U)l2; break; - case TOK_LT: l1 = l1 < l2; break; - case TOK_GE: l1 = l1 >= l2; break; - case TOK_LE: l1 = l1 <= l2; break; - case TOK_GT: l1 = l1 > l2; break; - /* logical */ - case TOK_LAND: l1 = l1 && l2; break; - case TOK_LOR: l1 = l1 || l2; break; - default: - goto general_case; - } - v1->c.ll = l1; - vtop--; - } else { - /* if commutative ops, put c2 as constant */ - if (c1 && (op == '+' || op == '&' || op == '^' || - op == '|' || op == '*')) { - vswap(); - c2 = c1; //c = c1, c1 = c2, c2 = c; - l2 = l1; //l = l1, l1 = l2, l2 = l; - } - /* Filter out NOP operations like x*1, x-0, x&-1... */ - if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV || - op == TOK_PDIV) && - l2 == 1) || - ((op == '+' || op == '-' || op == '|' || op == '^' || - op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) && - l2 == 0) || - (op == '&' && - l2 == -1))) { - /* nothing to do */ - vtop--; - } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) { - /* try to use shifts instead of muls or divs */ - if (l2 > 0 && (l2 & (l2 - 1)) == 0) { - n = -1; - while (l2) { - l2 >>= 1; - n++; - } - vtop->c.ll = n; - if (op == '*') - op = TOK_SHL; - else if (op == TOK_PDIV) - op = TOK_SAR; - else - op = TOK_SHR; - } - goto general_case; - } else if (c2 && (op == '+' || op == '-') && - ((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == - (VT_CONST | VT_SYM) || - (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) { - /* symbol + constant case */ - if (op == '-') - l2 = -l2; - vtop--; - vtop->c.ll += l2; - } else { - general_case: - if (!nocode_wanted) { - /* call low level op generator */ - if (t1 == VT_LLONG || t2 == VT_LLONG) - gen_opl(op); - else - gen_opi(op); - } else { - vtop--; - } - } - } -} - -/* generate a floating point operation with constant propagation */ -void gen_opif(int op) -{ - int c1, c2; - SValue *v1, *v2; - long double f1, f2; - - v1 = vtop - 1; - v2 = vtop; - /* currently, we cannot do computations with forward symbols */ - c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - if (c1 && c2) { - if (v1->type.t == VT_FLOAT) { - f1 = v1->c.f; - f2 = v2->c.f; - } else if (v1->type.t == VT_DOUBLE) { - f1 = v1->c.d; - f2 = v2->c.d; - } else { - f1 = v1->c.ld; - f2 = v2->c.ld; - } - - /* NOTE: we only do constant propagation if finite number (not - NaN or infinity) (ANSI spec) */ - if (!ieee_finite(f1) || !ieee_finite(f2)) - goto general_case; - - switch(op) { - case '+': f1 += f2; break; - case '-': f1 -= f2; break; - case '*': f1 *= f2; break; - case '/': - if (f2 == 0.0) { - if (const_wanted) - error("division by zero in constant"); - goto general_case; - } - f1 /= f2; - break; - /* XXX: also handles tests ? */ - default: - goto general_case; - } - /* XXX: overflow test ? */ - if (v1->type.t == VT_FLOAT) { - v1->c.f = f1; - } else if (v1->type.t == VT_DOUBLE) { - v1->c.d = f1; - } else { - v1->c.ld = f1; - } - vtop--; - } else { - general_case: - if (!nocode_wanted) { - gen_opf(op); - } else { - vtop--; - } - } -} - -static int pointed_size(CType *type) -{ - int align; - return type_size(pointed_type(type), &align); -} - -static inline int is_null_pointer(SValue *p) -{ - if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) - return 0; - return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) || - ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0); -} - -static inline int is_integer_btype(int bt) -{ - return (bt == VT_BYTE || bt == VT_SHORT || - bt == VT_INT || bt == VT_LLONG); -} - -/* check types for comparison or substraction of pointers */ -static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op) -{ - CType *type1, *type2, tmp_type1, tmp_type2; - int bt1, bt2; - - /* null pointers are accepted for all comparisons as gcc */ - if (is_null_pointer(p1) || is_null_pointer(p2)) - return; - type1 = &p1->type; - type2 = &p2->type; - bt1 = type1->t & VT_BTYPE; - bt2 = type2->t & VT_BTYPE; - /* accept comparison between pointer and integer with a warning */ - if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') { - if (op != TOK_LOR && op != TOK_LAND ) - warning("comparison between pointer and integer"); - return; - } - - /* both must be pointers or implicit function pointers */ - if (bt1 == VT_PTR) { - type1 = pointed_type(type1); - } else if (bt1 != VT_FUNC) - goto invalid_operands; - - if (bt2 == VT_PTR) { - type2 = pointed_type(type2); - } else if (bt2 != VT_FUNC) { - invalid_operands: - error("invalid operands to binary %s", get_tok_str(op, NULL)); - } - if ((type1->t & VT_BTYPE) == VT_VOID || - (type2->t & VT_BTYPE) == VT_VOID) - return; - tmp_type1 = *type1; - tmp_type2 = *type2; - tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); - tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); - if (!is_compatible_types(&tmp_type1, &tmp_type2)) { - /* gcc-like error if '-' is used */ - if (op == '-') - goto invalid_operands; - else - warning("comparison of distinct pointer types lacks a cast"); - } -} - -/* generic gen_op: handles types problems */ -void gen_op(int op) -{ - int u, t1, t2, bt1, bt2, t; - CType type1; - - t1 = vtop[-1].type.t; - t2 = vtop[0].type.t; - bt1 = t1 & VT_BTYPE; - bt2 = t2 & VT_BTYPE; - - if (bt1 == VT_PTR || bt2 == VT_PTR) { - /* at least one operand is a pointer */ - /* relationnal op: must be both pointers */ - if (op >= TOK_ULT && op <= TOK_LOR) { - check_comparison_pointer_types(vtop - 1, vtop, op); - /* pointers are handled are unsigned */ -#ifdef TCC_TARGET_X86_64 - t = VT_LLONG | VT_UNSIGNED; -#else - t = VT_INT | VT_UNSIGNED; -#endif - goto std_op; - } - /* if both pointers, then it must be the '-' op */ - if (bt1 == VT_PTR && bt2 == VT_PTR) { - if (op != '-') - error("cannot use pointers here"); - check_comparison_pointer_types(vtop - 1, vtop, op); - /* XXX: check that types are compatible */ - u = pointed_size(&vtop[-1].type); - gen_opic(op); - /* set to integer type */ -#ifdef TCC_TARGET_X86_64 - vtop->type.t = VT_LLONG; -#else - vtop->type.t = VT_INT; -#endif - vpushi(u); - gen_op(TOK_PDIV); - } else { - /* exactly one pointer : must be '+' or '-'. */ - if (op != '-' && op != '+') - error("cannot use pointers here"); - /* Put pointer as first operand */ - if (bt2 == VT_PTR) { - vswap(); - swap(&t1, &t2); - } - type1 = vtop[-1].type; -#ifdef TCC_TARGET_X86_64 - vpushll(pointed_size(&vtop[-1].type)); -#else - /* XXX: cast to int ? (long long case) */ - vpushi(pointed_size(&vtop[-1].type)); -#endif - gen_op('*'); -#ifdef CONFIG_TCC_BCHECK - /* if evaluating constant expression, no code should be - generated, so no bound check */ - if (tcc_state->do_bounds_check && !const_wanted) { - /* if bounded pointers, we generate a special code to - test bounds */ - if (op == '-') { - vpushi(0); - vswap(); - gen_op('-'); - } - gen_bounded_ptr_add(); - } else -#endif - { - gen_opic(op); - } - /* put again type if gen_opic() swaped operands */ - vtop->type = type1; - } - } else if (is_float(bt1) || is_float(bt2)) { - /* compute bigger type and do implicit casts */ - if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { - t = VT_LDOUBLE; - } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { - t = VT_DOUBLE; - } else { - t = VT_FLOAT; - } - /* floats can only be used for a few operations */ - if (op != '+' && op != '-' && op != '*' && op != '/' && - (op < TOK_ULT || op > TOK_GT)) - error("invalid operands for binary operation"); - goto std_op; - } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { - /* cast to biggest op */ - t = VT_LLONG; - /* convert to unsigned if it does not fit in a long long */ - if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || - (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) - t |= VT_UNSIGNED; - goto std_op; - } else { - /* integer operations */ - t = VT_INT; - /* convert to unsigned if it does not fit in an integer */ - if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || - (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) - t |= VT_UNSIGNED; - std_op: - /* XXX: currently, some unsigned operations are explicit, so - we modify them here */ - if (t & VT_UNSIGNED) { - if (op == TOK_SAR) - op = TOK_SHR; - else if (op == '/') - op = TOK_UDIV; - else if (op == '%') - op = TOK_UMOD; - else if (op == TOK_LT) - op = TOK_ULT; - else if (op == TOK_GT) - op = TOK_UGT; - else if (op == TOK_LE) - op = TOK_ULE; - else if (op == TOK_GE) - op = TOK_UGE; - } - vswap(); - type1.t = t; - gen_cast(&type1); - vswap(); - /* special case for shifts and long long: we keep the shift as - an integer */ - if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) - type1.t = VT_INT; - gen_cast(&type1); - if (is_float(t)) - gen_opif(op); - else - gen_opic(op); - if (op >= TOK_ULT && op <= TOK_GT) { - /* relationnal op: the result is an int */ - vtop->type.t = VT_INT; - } else { - vtop->type.t = t; - } - } -} - -#ifndef TCC_TARGET_ARM -/* generic itof for unsigned long long case */ -void gen_cvt_itof1(int t) -{ - if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == - (VT_LLONG | VT_UNSIGNED)) { - - if (t == VT_FLOAT) - vpush_global_sym(&func_old_type, TOK___floatundisf); -#if LDOUBLE_SIZE != 8 - else if (t == VT_LDOUBLE) - vpush_global_sym(&func_old_type, TOK___floatundixf); -#endif - else - vpush_global_sym(&func_old_type, TOK___floatundidf); - vrott(2); - gfunc_call(1); - vpushi(0); - vtop->r = reg_fret(t); - } else { - gen_cvt_itof(t); - } -} -#endif - -/* generic ftoi for unsigned long long case */ -void gen_cvt_ftoi1(int t) -{ - int st; - - if (t == (VT_LLONG | VT_UNSIGNED)) { - /* not handled natively */ - st = vtop->type.t & VT_BTYPE; - if (st == VT_FLOAT) - vpush_global_sym(&func_old_type, TOK___fixunssfdi); -#if LDOUBLE_SIZE != 8 - else if (st == VT_LDOUBLE) - vpush_global_sym(&func_old_type, TOK___fixunsxfdi); -#endif - else - vpush_global_sym(&func_old_type, TOK___fixunsdfdi); - vrott(2); - gfunc_call(1); - vpushi(0); - vtop->r = REG_IRET; - vtop->r2 = REG_LRET; - } else { - gen_cvt_ftoi(t); - } -} - -/* force char or short cast */ -void force_charshort_cast(int t) -{ - int bits, dbt; - dbt = t & VT_BTYPE; - /* XXX: add optimization if lvalue : just change type and offset */ - if (dbt == VT_BYTE) - bits = 8; - else - bits = 16; - if (t & VT_UNSIGNED) { - vpushi((1 << bits) - 1); - gen_op('&'); - } else { - bits = 32 - bits; - vpushi(bits); - gen_op(TOK_SHL); - /* result must be signed or the SAR is converted to an SHL - This was not the case when "t" was a signed short - and the last value on the stack was an unsigned int */ - vtop->type.t &= ~VT_UNSIGNED; - vpushi(bits); - gen_op(TOK_SAR); - } -} - -/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */ -static void gen_cast(CType *type) -{ - int sbt, dbt, sf, df, c, p; - - /* special delayed cast for char/short */ - /* XXX: in some cases (multiple cascaded casts), it may still - be incorrect */ - if (vtop->r & VT_MUSTCAST) { - vtop->r &= ~VT_MUSTCAST; - force_charshort_cast(vtop->type.t); - } - - /* bitfields first get cast to ints */ - if (vtop->type.t & VT_BITFIELD) { - gv(RC_INT); - } - - dbt = type->t & (VT_BTYPE | VT_UNSIGNED); - sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED); - - if (sbt != dbt) { - sf = is_float(sbt); - df = is_float(dbt); - c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM); - if (c) { - /* constant case: we can do it now */ - /* XXX: in ISOC, cannot do it if error in convert */ - if (sbt == VT_FLOAT) - vtop->c.ld = vtop->c.f; - else if (sbt == VT_DOUBLE) - vtop->c.ld = vtop->c.d; - - if (df) { - if ((sbt & VT_BTYPE) == VT_LLONG) { - if (sbt & VT_UNSIGNED) - vtop->c.ld = vtop->c.ull; - else - vtop->c.ld = vtop->c.ll; - } else if(!sf) { - if (sbt & VT_UNSIGNED) - vtop->c.ld = vtop->c.ui; - else - vtop->c.ld = vtop->c.i; - } - - if (dbt == VT_FLOAT) - vtop->c.f = (float)vtop->c.ld; - else if (dbt == VT_DOUBLE) - vtop->c.d = (double)vtop->c.ld; - } else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) { - vtop->c.ull = (unsigned long long)vtop->c.ld; - } else if (sf && dbt == VT_BOOL) { - vtop->c.i = (vtop->c.ld != 0); - } else { - if(sf) - vtop->c.ll = (long long)vtop->c.ld; - else if (sbt == (VT_LLONG|VT_UNSIGNED)) - vtop->c.ll = vtop->c.ull; - else if (sbt & VT_UNSIGNED) - vtop->c.ll = vtop->c.ui; - else if (sbt != VT_LLONG) - vtop->c.ll = vtop->c.i; - - if (dbt == (VT_LLONG|VT_UNSIGNED)) - vtop->c.ull = vtop->c.ll; - else if (dbt == VT_BOOL) - vtop->c.i = (vtop->c.ll != 0); - else if (dbt != VT_LLONG) { - int s = 0; - if ((dbt & VT_BTYPE) == VT_BYTE) - s = 24; - else if ((dbt & VT_BTYPE) == VT_SHORT) - s = 16; - - if(dbt & VT_UNSIGNED) - vtop->c.ui = ((unsigned int)vtop->c.ll << s) >> s; - else - vtop->c.i = ((int)vtop->c.ll << s) >> s; - } - } - } else if (p && dbt == VT_BOOL) { - vtop->r = VT_CONST; - vtop->c.i = 1; - } else if (!nocode_wanted) { - /* non constant case: generate code */ - if (sf && df) { - /* convert from fp to fp */ - gen_cvt_ftof(dbt); - } else if (df) { - /* convert int to fp */ - gen_cvt_itof1(dbt); - } else if (sf) { - /* convert fp to int */ - if (dbt == VT_BOOL) { - vpushi(0); - gen_op(TOK_NE); - } else { - /* we handle char/short/etc... with generic code */ - if (dbt != (VT_INT | VT_UNSIGNED) && - dbt != (VT_LLONG | VT_UNSIGNED) && - dbt != VT_LLONG) - dbt = VT_INT; - gen_cvt_ftoi1(dbt); - if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) { - /* additional cast for char/short... */ - vtop->type.t = dbt; - gen_cast(type); - } - } -#ifndef TCC_TARGET_X86_64 - } else if ((dbt & VT_BTYPE) == VT_LLONG) { - if ((sbt & VT_BTYPE) != VT_LLONG) { - /* scalar to long long */ - /* machine independent conversion */ - gv(RC_INT); - /* generate high word */ - if (sbt == (VT_INT | VT_UNSIGNED)) { - vpushi(0); - gv(RC_INT); - } else { - if (sbt == VT_PTR) { - /* cast from pointer to int before we apply - shift operation, which pointers don't support*/ - gen_cast(&int_type); - } - gv_dup(); - vpushi(31); - gen_op(TOK_SAR); - } - /* patch second register */ - vtop[-1].r2 = vtop->r; - vpop(); - } -#else - } else if ((dbt & VT_BTYPE) == VT_LLONG || - (dbt & VT_BTYPE) == VT_PTR) { - /* XXX: not sure if this is perfect... need more tests */ - if ((sbt & VT_BTYPE) != VT_LLONG) { - int r = gv(RC_INT); - if (sbt != (VT_INT | VT_UNSIGNED) && - sbt != VT_PTR && sbt != VT_FUNC) { - /* x86_64 specific: movslq */ - o(0x6348); - o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); - } - } -#endif - } else if (dbt == VT_BOOL) { - /* scalar to bool */ - vpushi(0); - gen_op(TOK_NE); - } else if ((dbt & VT_BTYPE) == VT_BYTE || - (dbt & VT_BTYPE) == VT_SHORT) { - if (sbt == VT_PTR) { - vtop->type.t = VT_INT; - warning("nonportable conversion from pointer to char/short"); - } - force_charshort_cast(dbt); - } else if ((dbt & VT_BTYPE) == VT_INT) { - /* scalar to int */ - if (sbt == VT_LLONG) { - /* from long long: just take low order word */ - lexpand(); - vpop(); - } - /* if lvalue and single word type, nothing to do because - the lvalue already contains the real type size (see - VT_LVAL_xxx constants) */ - } - } - } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) { - /* if we are casting between pointer types, - we must update the VT_LVAL_xxx size */ - vtop->r = (vtop->r & ~VT_LVAL_TYPE) - | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE); - } - vtop->type = *type; -} - -/* return type size. Put alignment at 'a' */ -static int type_size(CType *type, int *a) -{ - Sym *s; - int bt; - - bt = type->t & VT_BTYPE; - if (bt == VT_STRUCT) { - /* struct/union */ - s = type->ref; - *a = s->r; - return s->c; - } else if (bt == VT_PTR) { - if (type->t & VT_ARRAY) { - int ts; - - s = type->ref; - ts = type_size(&s->type, a); - - if (ts < 0 && s->c < 0) - ts = -ts; - - return ts * s->c; - } else { - *a = PTR_SIZE; - return PTR_SIZE; - } - } else if (bt == VT_LDOUBLE) { - *a = LDOUBLE_ALIGN; - return LDOUBLE_SIZE; - } else if (bt == VT_DOUBLE || bt == VT_LLONG) { -#ifdef TCC_TARGET_I386 -#ifdef TCC_TARGET_PE - *a = 8; -#else - *a = 4; -#endif -#elif defined(TCC_TARGET_ARM) -#ifdef TCC_ARM_EABI - *a = 8; -#else - *a = 4; -#endif -#else - *a = 8; -#endif - return 8; - } else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) { - *a = 4; - return 4; - } else if (bt == VT_SHORT) { - *a = 2; - return 2; - } else { - /* char, void, function, _Bool */ - *a = 1; - return 1; - } -} - -/* return the pointed type of t */ -static inline CType *pointed_type(CType *type) -{ - return &type->ref->type; -} - -/* modify type so that its it is a pointer to type. */ -static void mk_pointer(CType *type) -{ - Sym *s; - s = sym_push(SYM_FIELD, type, 0, -1); - type->t = VT_PTR | (type->t & ~VT_TYPE); - type->ref = s; -} - -/* compare function types. OLD functions match any new functions */ -static int is_compatible_func(CType *type1, CType *type2) -{ - Sym *s1, *s2; - - s1 = type1->ref; - s2 = type2->ref; - if (!is_compatible_types(&s1->type, &s2->type)) - return 0; - /* check func_call */ - if (FUNC_CALL(s1->r) != FUNC_CALL(s2->r)) - return 0; - /* XXX: not complete */ - if (s1->c == FUNC_OLD || s2->c == FUNC_OLD) - return 1; - if (s1->c != s2->c) - return 0; - while (s1 != NULL) { - if (s2 == NULL) - return 0; - if (!is_compatible_parameter_types(&s1->type, &s2->type)) - return 0; - s1 = s1->next; - s2 = s2->next; - } - if (s2) - return 0; - return 1; -} - -/* return true if type1 and type2 are the same. If unqualified is - true, qualifiers on the types are ignored. - - - enums are not checked as gcc __builtin_types_compatible_p () - */ -static int compare_types(CType *type1, CType *type2, int unqualified) -{ - int bt1, t1, t2; - - t1 = type1->t & VT_TYPE; - t2 = type2->t & VT_TYPE; - if (unqualified) { - /* strip qualifiers before comparing */ - t1 &= ~(VT_CONSTANT | VT_VOLATILE); - t2 &= ~(VT_CONSTANT | VT_VOLATILE); - } - /* XXX: bitfields ? */ - if (t1 != t2) - return 0; - /* test more complicated cases */ - bt1 = t1 & VT_BTYPE; - if (bt1 == VT_PTR) { - type1 = pointed_type(type1); - type2 = pointed_type(type2); - return is_compatible_types(type1, type2); - } else if (bt1 == VT_STRUCT) { - return (type1->ref == type2->ref); - } else if (bt1 == VT_FUNC) { - return is_compatible_func(type1, type2); - } else { - return 1; - } -} - -/* return true if type1 and type2 are exactly the same (including - qualifiers). -*/ -static int is_compatible_types(CType *type1, CType *type2) -{ - return compare_types(type1,type2,0); -} - -/* return true if type1 and type2 are the same (ignoring qualifiers). -*/ -static int is_compatible_parameter_types(CType *type1, CType *type2) -{ - return compare_types(type1,type2,1); -} - -/* print a type. If 'varstr' is not NULL, then the variable is also - printed in the type */ -/* XXX: union */ -/* XXX: add array and function pointers */ -void type_to_str(char *buf, int buf_size, - CType *type, const char *varstr) -{ - int bt, v, t; - Sym *s, *sa; - char buf1[256]; - const char *tstr; - - t = type->t & VT_TYPE; - bt = t & VT_BTYPE; - buf[0] = '\0'; - if (t & VT_CONSTANT) - pstrcat(buf, buf_size, "const "); - if (t & VT_VOLATILE) - pstrcat(buf, buf_size, "volatile "); - if (t & VT_UNSIGNED) - pstrcat(buf, buf_size, "unsigned "); - switch(bt) { - case VT_VOID: - tstr = "void"; - goto add_tstr; - case VT_BOOL: - tstr = "_Bool"; - goto add_tstr; - case VT_BYTE: - tstr = "char"; - goto add_tstr; - case VT_SHORT: - tstr = "short"; - goto add_tstr; - case VT_INT: - tstr = "int"; - goto add_tstr; - case VT_LONG: - tstr = "long"; - goto add_tstr; - case VT_LLONG: - tstr = "long long"; - goto add_tstr; - case VT_FLOAT: - tstr = "float"; - goto add_tstr; - case VT_DOUBLE: - tstr = "double"; - goto add_tstr; - case VT_LDOUBLE: - tstr = "long double"; - add_tstr: - pstrcat(buf, buf_size, tstr); - break; - case VT_ENUM: - case VT_STRUCT: - if (bt == VT_STRUCT) - tstr = "struct "; - else - tstr = "enum "; - pstrcat(buf, buf_size, tstr); - v = type->ref->v & ~SYM_STRUCT; - if (v >= SYM_FIRST_ANOM) - pstrcat(buf, buf_size, ""); - else - pstrcat(buf, buf_size, get_tok_str(v, NULL)); - break; - case VT_FUNC: - s = type->ref; - type_to_str(buf, buf_size, &s->type, varstr); - pstrcat(buf, buf_size, "("); - sa = s->next; - while (sa != NULL) { - type_to_str(buf1, sizeof(buf1), &sa->type, NULL); - pstrcat(buf, buf_size, buf1); - sa = sa->next; - if (sa) - pstrcat(buf, buf_size, ", "); - } - pstrcat(buf, buf_size, ")"); - goto no_var; - case VT_PTR: - s = type->ref; - pstrcpy(buf1, sizeof(buf1), "*"); - if (varstr) - pstrcat(buf1, sizeof(buf1), varstr); - type_to_str(buf, buf_size, &s->type, buf1); - goto no_var; - } - if (varstr) { - pstrcat(buf, buf_size, " "); - pstrcat(buf, buf_size, varstr); - } - no_var: ; -} - -/* verify type compatibility to store vtop in 'dt' type, and generate - casts if needed. */ -static void gen_assign_cast(CType *dt) -{ - CType *st, *type1, *type2, tmp_type1, tmp_type2; - char buf1[256], buf2[256]; - int dbt, sbt; - - st = &vtop->type; /* source type */ - dbt = dt->t & VT_BTYPE; - sbt = st->t & VT_BTYPE; - if (dt->t & VT_CONSTANT) - warning("assignment of read-only location"); - switch(dbt) { - case VT_PTR: - /* special cases for pointers */ - /* '0' can also be a pointer */ - if (is_null_pointer(vtop)) - goto type_ok; - /* accept implicit pointer to integer cast with warning */ - if (is_integer_btype(sbt)) { - warning("assignment makes pointer from integer without a cast"); - goto type_ok; - } - type1 = pointed_type(dt); - /* a function is implicitely a function pointer */ - if (sbt == VT_FUNC) { - if ((type1->t & VT_BTYPE) != VT_VOID && - !is_compatible_types(pointed_type(dt), st)) - goto error; - else - goto type_ok; - } - if (sbt != VT_PTR) - goto error; - type2 = pointed_type(st); - if ((type1->t & VT_BTYPE) == VT_VOID || - (type2->t & VT_BTYPE) == VT_VOID) { - /* void * can match anything */ - } else { - /* exact type match, except for unsigned */ - tmp_type1 = *type1; - tmp_type2 = *type2; - tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); - tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); - if (!is_compatible_types(&tmp_type1, &tmp_type2)) - warning("assignment from incompatible pointer type"); - } - /* check const and volatile */ - if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || - (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE))) - warning("assignment discards qualifiers from pointer target type"); - break; - case VT_BYTE: - case VT_SHORT: - case VT_INT: - case VT_LLONG: - if (sbt == VT_PTR || sbt == VT_FUNC) { - warning("assignment makes integer from pointer without a cast"); - } - /* XXX: more tests */ - break; - case VT_STRUCT: - tmp_type1 = *dt; - tmp_type2 = *st; - tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE); - tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE); - if (!is_compatible_types(&tmp_type1, &tmp_type2)) { - error: - type_to_str(buf1, sizeof(buf1), st, NULL); - type_to_str(buf2, sizeof(buf2), dt, NULL); - error("cannot cast '%s' to '%s'", buf1, buf2); - } - break; - } - type_ok: - gen_cast(dt); -} - -/* store vtop in lvalue pushed on stack */ -void vstore(void) -{ - int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast; - - ft = vtop[-1].type.t; - sbt = vtop->type.t & VT_BTYPE; - dbt = ft & VT_BTYPE; - if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || - (sbt == VT_INT && dbt == VT_SHORT)) { - /* optimize char/short casts */ - delayed_cast = VT_MUSTCAST; - vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT))); - /* XXX: factorize */ - if (ft & VT_CONSTANT) - warning("assignment of read-only location"); - } else { - delayed_cast = 0; - if (!(ft & VT_BITFIELD)) - gen_assign_cast(&vtop[-1].type); - } - - if (sbt == VT_STRUCT) { - /* if structure, only generate pointer */ - /* structure assignment : generate memcpy */ - /* XXX: optimize if small size */ - if (!nocode_wanted) { - size = type_size(&vtop->type, &align); - -#ifdef TCC_ARM_EABI - if(!(align & 7)) - vpush_global_sym(&func_old_type, TOK_memcpy8); - else if(!(align & 3)) - vpush_global_sym(&func_old_type, TOK_memcpy4); - else -#endif - vpush_global_sym(&func_old_type, TOK_memcpy); - - /* destination */ - vpushv(vtop - 2); - vtop->type.t = VT_PTR; - gaddrof(); - /* source */ - vpushv(vtop - 2); - vtop->type.t = VT_PTR; - gaddrof(); - /* type size */ - vpushi(size); - gfunc_call(3); - - vswap(); - vpop(); - } else { - vswap(); - vpop(); - } - /* leave source on stack */ - } else if (ft & VT_BITFIELD) { - /* bitfield store handling */ - bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f; - bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f; - /* remove bit field info to avoid loops */ - vtop[-1].type.t = ft & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); - - /* duplicate source into other register */ - 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); - } - - /* duplicate destination */ - vdup(); - vtop[-1] = vtop[-2]; - - /* mask and shift source */ - if((ft & VT_BTYPE) != VT_BOOL) { - if((ft & VT_BTYPE) == VT_LLONG) { - vpushll((1ULL << bit_size) - 1ULL); - } else { - vpushi((1 << bit_size) - 1); - } - gen_op('&'); - } - vpushi(bit_pos); - gen_op(TOK_SHL); - /* load destination, mask and or with source */ - vswap(); - if((ft & VT_BTYPE) == VT_LLONG) { - vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos)); - } else { - vpushi(~(((1 << bit_size) - 1) << bit_pos)); - } - gen_op('&'); - gen_op('|'); - /* store result */ - vstore(); - - /* pop off shifted source from "duplicate source..." above */ - vpop(); - - } else { -#ifdef CONFIG_TCC_BCHECK - /* bound check case */ - if (vtop[-1].r & VT_MUSTBOUND) { - vswap(); - gbound(); - vswap(); - } -#endif - if (!nocode_wanted) { - rc = RC_INT; - if (is_float(ft)) { - rc = RC_FLOAT; -#ifdef TCC_TARGET_X86_64 - if ((ft & VT_BTYPE) == VT_LDOUBLE) { - rc = RC_ST0; - } -#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; - } - store(r, vtop - 1); -#ifndef TCC_TARGET_X86_64 - /* two word case handling : store second register at word + 4 */ - if ((ft & VT_BTYPE) == VT_LLONG) { - vswap(); - /* convert to int to increment easily */ - vtop->type.t = VT_INT; - gaddrof(); - vpushi(4); - gen_op('+'); - vtop->r |= VT_LVAL; - vswap(); - /* XXX: it works because r2 is spilled last ! */ - store(vtop->r2, vtop - 1); - } -#endif - } - vswap(); - vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ - vtop->r |= delayed_cast; - } -} - -/* post defines POST/PRE add. c is the token ++ or -- */ -void inc(int post, int c) -{ - test_lvalue(); - vdup(); /* save lvalue */ - if (post) { - gv_dup(); /* duplicate value */ - vrotb(3); - vrotb(3); - } - /* add constant */ - vpushi(c - TOK_MID); - gen_op('+'); - vstore(); /* store value */ - if (post) - vpop(); /* if post op, return saved value */ -} - -/* Parse GNUC __attribute__ extension. Currently, the following - extensions are recognized: - - aligned(n) : set data/function alignment. - - packed : force data alignment to 1 - - section(x) : generate data/code in this section. - - unused : currently ignored, but may be used someday. - - regparm(n) : pass function parameters in registers (i386 only) - */ -static void parse_attribute(AttributeDef *ad) -{ - int t, n; - - while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) { - next(); - skip('('); - skip('('); - while (tok != ')') { - if (tok < TOK_IDENT) - expect("attribute name"); - t = tok; - next(); - switch(t) { - case TOK_SECTION1: - case TOK_SECTION2: - skip('('); - if (tok != TOK_STR) - expect("section name"); - ad->section = find_section(tcc_state, (char *)tokc.cstr->data); - next(); - skip(')'); - break; - case TOK_ALIGNED1: - case TOK_ALIGNED2: - if (tok == '(') { - next(); - n = expr_const(); - if (n <= 0 || (n & (n - 1)) != 0) - error("alignment must be a positive power of two"); - skip(')'); - } else { - n = MAX_ALIGN; - } - ad->aligned = n; - break; - case TOK_PACKED1: - case TOK_PACKED2: - ad->packed = 1; - break; - case TOK_UNUSED1: - case TOK_UNUSED2: - /* currently, no need to handle it because tcc does not - track unused objects */ - break; - case TOK_NORETURN1: - case TOK_NORETURN2: - /* currently, no need to handle it because tcc does not - track unused objects */ - break; - case TOK_CDECL1: - case TOK_CDECL2: - case TOK_CDECL3: - FUNC_CALL(ad->func_attr) = FUNC_CDECL; - break; - case TOK_STDCALL1: - case TOK_STDCALL2: - case TOK_STDCALL3: - FUNC_CALL(ad->func_attr) = FUNC_STDCALL; - break; -#ifdef TCC_TARGET_I386 - case TOK_REGPARM1: - case TOK_REGPARM2: - skip('('); - n = expr_const(); - if (n > 3) - n = 3; - else if (n < 0) - n = 0; - if (n > 0) - FUNC_CALL(ad->func_attr) = FUNC_FASTCALL1 + n - 1; - skip(')'); - break; - case TOK_FASTCALL1: - case TOK_FASTCALL2: - case TOK_FASTCALL3: - FUNC_CALL(ad->func_attr) = FUNC_FASTCALLW; - break; -#endif - case TOK_DLLEXPORT: - FUNC_EXPORT(ad->func_attr) = 1; - break; - default: - if (tcc_state->warn_unsupported) - warning("'%s' attribute ignored", get_tok_str(t, NULL)); - /* skip parameters */ - if (tok == '(') { - int parenthesis = 0; - do { - if (tok == '(') - parenthesis++; - else if (tok == ')') - parenthesis--; - next(); - } while (parenthesis && tok != -1); - } - break; - } - if (tok != ',') - break; - next(); - } - skip(')'); - skip(')'); - } -} - -/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ -static void struct_decl(CType *type, int u) -{ - int a, v, size, align, maxalign, c, offset; - int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; - Sym *s, *ss, *ass, **ps; - AttributeDef ad; - CType type1, btype; - - a = tok; /* save decl type */ - next(); - if (tok != '{') { - v = tok; - next(); - /* struct already defined ? return it */ - if (v < TOK_IDENT) - expect("struct/union/enum name"); - s = struct_find(v); - if (s) { - if (s->type.t != a) - error("invalid type"); - goto do_decl; - } - } else { - v = anon_sym++; - } - type1.t = a; - /* we put an undefined size for struct/union */ - s = sym_push(v | SYM_STRUCT, &type1, 0, -1); - s->r = 0; /* default alignment is zero as gcc */ - /* put struct/union/enum name in type */ - do_decl: - type->t = u; - type->ref = s; - - if (tok == '{') { - next(); - if (s->c != -1) - error("struct/union/enum already defined"); - /* cannot be empty */ - c = 0; - /* non empty enums are not allowed */ - if (a == TOK_ENUM) { - for(;;) { - v = tok; - if (v < TOK_UIDENT) - expect("identifier"); - next(); - if (tok == '=') { - next(); - c = expr_const(); - } - /* enum symbols have static storage */ - ss = sym_push(v, &int_type, VT_CONST, c); - ss->type.t |= VT_STATIC; - if (tok != ',') - break; - next(); - c++; - /* NOTE: we accept a trailing comma */ - if (tok == '}') - break; - } - skip('}'); - } else { - maxalign = 1; - ps = &s->next; - prevbt = VT_INT; - bit_pos = 0; - offset = 0; - while (tok != '}') { - parse_btype(&btype, &ad); - while (1) { - bit_size = -1; - v = 0; - type1 = btype; - if (tok != ':') { - type_decl(&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT); - if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT) - expect("identifier"); - if ((type1.t & VT_BTYPE) == VT_FUNC || - (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE))) - error("invalid type for '%s'", - get_tok_str(v, NULL)); - } - if (tok == ':') { - next(); - bit_size = expr_const(); - /* XXX: handle v = 0 case for messages */ - if (bit_size < 0) - error("negative width in bit-field '%s'", - get_tok_str(v, NULL)); - if (v && bit_size == 0) - error("zero width for bit-field '%s'", - get_tok_str(v, NULL)); - } - size = type_size(&type1, &align); - if (ad.aligned) { - if (align < ad.aligned) - align = ad.aligned; - } else if (ad.packed) { - align = 1; - } else if (*tcc_state->pack_stack_ptr) { - if (align > *tcc_state->pack_stack_ptr) - align = *tcc_state->pack_stack_ptr; - } - lbit_pos = 0; - if (bit_size >= 0) { - bt = type1.t & VT_BTYPE; - if (bt != VT_INT && - bt != VT_BYTE && - bt != VT_SHORT && - bt != VT_BOOL && - bt != VT_ENUM && - bt != VT_LLONG) - error("bitfields must have scalar type"); - bsize = size * 8; - if (bit_size > bsize) { - error("width of '%s' exceeds its type", - get_tok_str(v, NULL)); - } else if (bit_size == bsize) { - /* no need for bit fields */ - bit_pos = 0; - } else if (bit_size == 0) { - /* XXX: what to do if only padding in a - structure ? */ - /* zero size: means to pad */ - bit_pos = 0; - } else { - /* we do not have enough room ? - did the type change? - is it a union? */ - if ((bit_pos + bit_size) > bsize || - bt != prevbt || a == TOK_UNION) - bit_pos = 0; - lbit_pos = bit_pos; - /* XXX: handle LSB first */ - type1.t |= VT_BITFIELD | - (bit_pos << VT_STRUCT_SHIFT) | - (bit_size << (VT_STRUCT_SHIFT + 6)); - bit_pos += bit_size; - } - prevbt = bt; - } else { - bit_pos = 0; - } - if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { - /* add new memory data only if starting - bit field */ - if (lbit_pos == 0) { - if (a == TOK_STRUCT) { - c = (c + align - 1) & -align; - offset = c; - if (size > 0) - c += size; - } else { - offset = 0; - if (size > c) - c = size; - } - if (align > maxalign) - maxalign = align; - } -#if 0 - printf("add field %s offset=%d", - get_tok_str(v, NULL), offset); - if (type1.t & VT_BITFIELD) { - printf(" pos=%d size=%d", - (type1.t >> VT_STRUCT_SHIFT) & 0x3f, - (type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f); - } - printf("\n"); -#endif - } - if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) { - ass = type1.ref; - while ((ass = ass->next) != NULL) { - ss = sym_push(ass->v, &ass->type, 0, offset + ass->c); - *ps = ss; - ps = &ss->next; - } - } else if (v) { - ss = sym_push(v | SYM_FIELD, &type1, 0, offset); - *ps = ss; - ps = &ss->next; - } - if (tok == ';' || tok == TOK_EOF) - break; - skip(','); - } - skip(';'); - } - skip('}'); - /* store size and alignment */ - s->c = (c + maxalign - 1) & -maxalign; - s->r = maxalign; - } - } -} - -/* return 0 if no type declaration. otherwise, return the basic type - and skip it. - */ -static int parse_btype(CType *type, AttributeDef *ad) -{ - int t, u, type_found, typespec_found, typedef_found; - Sym *s; - CType type1; - - memset(ad, 0, sizeof(AttributeDef)); - type_found = 0; - typespec_found = 0; - typedef_found = 0; - t = 0; - while(1) { - switch(tok) { - case TOK_EXTENSION: - /* currently, we really ignore extension */ - next(); - continue; - - /* basic types */ - case TOK_CHAR: - u = VT_BYTE; - basic_type: - next(); - basic_type1: - if ((t & VT_BTYPE) != 0) - error("too many basic types"); - t |= u; - typespec_found = 1; - break; - case TOK_VOID: - u = VT_VOID; - goto basic_type; - case TOK_SHORT: - u = VT_SHORT; - goto basic_type; - case TOK_INT: - next(); - typespec_found = 1; - break; - case TOK_LONG: - next(); - if ((t & VT_BTYPE) == VT_DOUBLE) { - t = (t & ~VT_BTYPE) | VT_LDOUBLE; - } else if ((t & VT_BTYPE) == VT_LONG) { - t = (t & ~VT_BTYPE) | VT_LLONG; - } else { - u = VT_LONG; - goto basic_type1; - } - break; - case TOK_BOOL: - u = VT_BOOL; - goto basic_type; - case TOK_FLOAT: - u = VT_FLOAT; - goto basic_type; - case TOK_DOUBLE: - next(); - if ((t & VT_BTYPE) == VT_LONG) { - t = (t & ~VT_BTYPE) | VT_LDOUBLE; - } else { - u = VT_DOUBLE; - goto basic_type1; - } - break; - case TOK_ENUM: - struct_decl(&type1, VT_ENUM); - basic_type2: - u = type1.t; - type->ref = type1.ref; - goto basic_type1; - case TOK_STRUCT: - case TOK_UNION: - struct_decl(&type1, VT_STRUCT); - goto basic_type2; - - /* type modifiers */ - case TOK_CONST1: - case TOK_CONST2: - case TOK_CONST3: - t |= VT_CONSTANT; - next(); - break; - case TOK_VOLATILE1: - case TOK_VOLATILE2: - case TOK_VOLATILE3: - t |= VT_VOLATILE; - next(); - break; - case TOK_SIGNED1: - case TOK_SIGNED2: - case TOK_SIGNED3: - typespec_found = 1; - t |= VT_SIGNED; - next(); - break; - case TOK_REGISTER: - case TOK_AUTO: - case TOK_RESTRICT1: - case TOK_RESTRICT2: - case TOK_RESTRICT3: - next(); - break; - case TOK_UNSIGNED: - t |= VT_UNSIGNED; - next(); - typespec_found = 1; - break; - - /* storage */ - case TOK_EXTERN: - t |= VT_EXTERN; - next(); - break; - case TOK_STATIC: - t |= VT_STATIC; - next(); - break; - case TOK_TYPEDEF: - t |= VT_TYPEDEF; - next(); - break; - case TOK_INLINE1: - case TOK_INLINE2: - case TOK_INLINE3: - t |= VT_INLINE; - next(); - break; - - /* GNUC attribute */ - case TOK_ATTRIBUTE1: - case TOK_ATTRIBUTE2: - parse_attribute(ad); - break; - /* GNUC typeof */ - case TOK_TYPEOF1: - case TOK_TYPEOF2: - case TOK_TYPEOF3: - next(); - parse_expr_type(&type1); - goto basic_type2; - default: - if (typespec_found || typedef_found) - goto the_end; - s = sym_find(tok); - if (!s || !(s->type.t & VT_TYPEDEF)) - goto the_end; - typedef_found = 1; - t |= (s->type.t & ~VT_TYPEDEF); - type->ref = s->type.ref; - next(); - typespec_found = 1; - break; - } - type_found = 1; - } -the_end: - if ((t & (VT_SIGNED|VT_UNSIGNED)) == (VT_SIGNED|VT_UNSIGNED)) - error("signed and unsigned modifier"); - if (tcc_state->char_is_unsigned) { - if ((t & (VT_SIGNED|VT_UNSIGNED|VT_BTYPE)) == VT_BYTE) - t |= VT_UNSIGNED; - } - t &= ~VT_SIGNED; - - /* long is never used as type */ - if ((t & VT_BTYPE) == VT_LONG) -#ifndef TCC_TARGET_X86_64 - t = (t & ~VT_BTYPE) | VT_INT; -#else - t = (t & ~VT_BTYPE) | VT_LLONG; -#endif - type->t = t; - return type_found; -} - -/* convert a function parameter type (array to pointer and function to - function pointer) */ -static inline void convert_parameter_type(CType *pt) -{ - /* remove const and volatile qualifiers (XXX: const could be used - to indicate a const function parameter */ - pt->t &= ~(VT_CONSTANT | VT_VOLATILE); - /* array must be transformed to pointer according to ANSI C */ - pt->t &= ~VT_ARRAY; - if ((pt->t & VT_BTYPE) == VT_FUNC) { - mk_pointer(pt); - } -} - -static void post_type(CType *type, AttributeDef *ad) -{ - int n, l, t1, arg_size, align; - Sym **plast, *s, *first; - AttributeDef ad1; - CType pt; - - if (tok == '(') { - /* function declaration */ - next(); - l = 0; - first = NULL; - plast = &first; - arg_size = 0; - if (tok != ')') { - for(;;) { - /* read param name and compute offset */ - if (l != FUNC_OLD) { - if (!parse_btype(&pt, &ad1)) { - if (l) { - error("invalid type"); - } else { - l = FUNC_OLD; - goto old_proto; - } - } - l = FUNC_NEW; - if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')') - break; - type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT); - if ((pt.t & VT_BTYPE) == VT_VOID) - error("parameter declared as void"); - arg_size += (type_size(&pt, &align) + 3) & ~3; - } else { - old_proto: - n = tok; - if (n < TOK_UIDENT) - expect("identifier"); - pt.t = VT_INT; - next(); - } - convert_parameter_type(&pt); - s = sym_push(n | SYM_FIELD, &pt, 0, 0); - *plast = s; - plast = &s->next; - if (tok == ')') - break; - skip(','); - if (l == FUNC_NEW && tok == TOK_DOTS) { - l = FUNC_ELLIPSIS; - next(); - break; - } - } - } - /* if no parameters, then old type prototype */ - if (l == 0) - l = FUNC_OLD; - skip(')'); - t1 = type->t & VT_STORAGE; - /* NOTE: const is ignored in returned type as it has a special - meaning in gcc / C++ */ - type->t &= ~(VT_STORAGE | VT_CONSTANT); - post_type(type, ad); - /* we push a anonymous symbol which will contain the function prototype */ - FUNC_ARGS(ad->func_attr) = arg_size; - s = sym_push(SYM_FIELD, type, ad->func_attr, l); - s->next = first; - type->t = t1 | VT_FUNC; - type->ref = s; - } else if (tok == '[') { - /* array definition */ - next(); - if (tok == TOK_RESTRICT1) - next(); - n = -1; - if (tok != ']') { - n = expr_const(); - if (n < 0) - error("invalid array size"); - } - skip(']'); - /* parse next post type */ - t1 = type->t & VT_STORAGE; - type->t &= ~VT_STORAGE; - post_type(type, ad); - - /* we push a anonymous symbol which will contain the array - element type */ - s = sym_push(SYM_FIELD, type, 0, n); - type->t = t1 | VT_ARRAY | VT_PTR; - type->ref = s; - } -} - -/* Parse a type declaration (except basic type), and return the type - in 'type'. 'td' is a bitmask indicating which kind of type decl is - expected. 'type' should contain the basic type. 'ad' is the - attribute definition of the basic type. It can be modified by - type_decl(). - */ -static void type_decl(CType *type, AttributeDef *ad, int *v, int td) -{ - Sym *s; - CType type1, *type2; - int qualifiers; - - while (tok == '*') { - qualifiers = 0; - redo: - next(); - switch(tok) { - case TOK_CONST1: - case TOK_CONST2: - case TOK_CONST3: - qualifiers |= VT_CONSTANT; - goto redo; - case TOK_VOLATILE1: - case TOK_VOLATILE2: - case TOK_VOLATILE3: - qualifiers |= VT_VOLATILE; - goto redo; - case TOK_RESTRICT1: - case TOK_RESTRICT2: - case TOK_RESTRICT3: - goto redo; - } - mk_pointer(type); - type->t |= qualifiers; - } - - /* XXX: clarify attribute handling */ - if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) - parse_attribute(ad); - - /* recursive type */ - /* XXX: incorrect if abstract type for functions (e.g. 'int ()') */ - type1.t = 0; /* XXX: same as int */ - if (tok == '(') { - next(); - /* XXX: this is not correct to modify 'ad' at this point, but - the syntax is not clear */ - if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) - parse_attribute(ad); - type_decl(&type1, ad, v, td); - skip(')'); - } else { - /* type identifier */ - if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) { - *v = tok; - next(); - } else { - if (!(td & TYPE_ABSTRACT)) - expect("identifier"); - *v = 0; - } - } - post_type(type, ad); - if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) - parse_attribute(ad); - if (!type1.t) - return; - /* append type at the end of type1 */ - type2 = &type1; - for(;;) { - s = type2->ref; - type2 = &s->type; - if (!type2->t) { - *type2 = *type; - break; - } - } - *type = type1; -} - -/* compute the lvalue VT_LVAL_xxx needed to match type t. */ -static int lvalue_type(int t) -{ - int bt, r; - r = VT_LVAL; - bt = t & VT_BTYPE; - if (bt == VT_BYTE || bt == VT_BOOL) - r |= VT_LVAL_BYTE; - else if (bt == VT_SHORT) - r |= VT_LVAL_SHORT; - else - return r; - if (t & VT_UNSIGNED) - r |= VT_LVAL_UNSIGNED; - return r; -} - -/* indirection with full error checking and bound check */ -static void indir(void) -{ - if ((vtop->type.t & VT_BTYPE) != VT_PTR) { - if ((vtop->type.t & VT_BTYPE) == VT_FUNC) - return; - expect("pointer"); - } - if ((vtop->r & VT_LVAL) && !nocode_wanted) - gv(RC_INT); - vtop->type = *pointed_type(&vtop->type); - /* Arrays and functions are never lvalues */ - if (!(vtop->type.t & VT_ARRAY) - && (vtop->type.t & VT_BTYPE) != VT_FUNC) { - vtop->r |= lvalue_type(vtop->type.t); - /* if bound checking, the referenced pointer must be checked */ - if (tcc_state->do_bounds_check) - vtop->r |= VT_MUSTBOUND; - } -} - -/* pass a parameter to a function and do type checking and casting */ -static void gfunc_param_typed(Sym *func, Sym *arg) -{ - int func_type; - CType type; - - func_type = func->c; - if (func_type == FUNC_OLD || - (func_type == FUNC_ELLIPSIS && arg == NULL)) { - /* default casting : only need to convert float to double */ - if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) { - type.t = VT_DOUBLE; - gen_cast(&type); - } - } else if (arg == NULL) { - error("too many arguments to function"); - } else { - type = arg->type; - type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ - gen_assign_cast(&type); - } -} - -/* parse an expression of the form '(type)' or '(expr)' and return its - type */ -static void parse_expr_type(CType *type) -{ - int n; - AttributeDef ad; - - skip('('); - if (parse_btype(type, &ad)) { - type_decl(type, &ad, &n, TYPE_ABSTRACT); - } else { - expr_type(type); - } - skip(')'); -} - -static void parse_type(CType *type) -{ - AttributeDef ad; - int n; - - if (!parse_btype(type, &ad)) { - expect("type"); - } - type_decl(type, &ad, &n, TYPE_ABSTRACT); -} - -static void vpush_tokc(int t) -{ - CType type; - type.t = t; - vsetc(&type, VT_CONST, &tokc); -} - -static void unary(void) -{ - int n, t, align, size, r; - CType type; - Sym *s; - AttributeDef ad; - - /* XXX: GCC 2.95.3 does not generate a table although it should be - better here */ - tok_next: - switch(tok) { - case TOK_EXTENSION: - next(); - goto tok_next; - case TOK_CINT: - case TOK_CCHAR: - case TOK_LCHAR: - vpushi(tokc.i); - next(); - break; - case TOK_CUINT: - vpush_tokc(VT_INT | VT_UNSIGNED); - next(); - break; - case TOK_CLLONG: - vpush_tokc(VT_LLONG); - next(); - break; - case TOK_CULLONG: - vpush_tokc(VT_LLONG | VT_UNSIGNED); - next(); - break; - case TOK_CFLOAT: - vpush_tokc(VT_FLOAT); - next(); - break; - case TOK_CDOUBLE: - vpush_tokc(VT_DOUBLE); - next(); - break; - case TOK_CLDOUBLE: - vpush_tokc(VT_LDOUBLE); - next(); - break; - case TOK___FUNCTION__: - if (!gnu_ext) - goto tok_identifier; - /* fall thru */ - case TOK___FUNC__: - { - void *ptr; - int len; - /* special function name identifier */ - len = strlen(funcname) + 1; - /* generate char[len] type */ - type.t = VT_BYTE; - mk_pointer(&type); - type.t |= VT_ARRAY; - type.ref->c = len; - vpush_ref(&type, data_section, data_section->data_offset, len); - ptr = section_ptr_add(data_section, len); - memcpy(ptr, funcname, len); - next(); - } - break; - case TOK_LSTR: -#ifdef TCC_TARGET_PE - t = VT_SHORT | VT_UNSIGNED; -#else - t = VT_INT; -#endif - goto str_init; - case TOK_STR: - /* string parsing */ - t = VT_BYTE; - str_init: - if (tcc_state->warn_write_strings) - t |= VT_CONSTANT; - type.t = t; - mk_pointer(&type); - type.t |= VT_ARRAY; - memset(&ad, 0, sizeof(AttributeDef)); - decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0); - break; - case '(': - next(); - /* cast ? */ - if (parse_btype(&type, &ad)) { - type_decl(&type, &ad, &n, TYPE_ABSTRACT); - skip(')'); - /* check ISOC99 compound literal */ - if (tok == '{') { - /* data is allocated locally by default */ - if (global_expr) - r = VT_CONST; - else - r = VT_LOCAL; - /* all except arrays are lvalues */ - if (!(type.t & VT_ARRAY)) - r |= lvalue_type(type.t); - memset(&ad, 0, sizeof(AttributeDef)); - decl_initializer_alloc(&type, &ad, r, 1, 0, 0); - } else { - unary(); - gen_cast(&type); - } - } else if (tok == '{') { - /* save all registers */ - save_regs(0); - /* statement expression : we do not accept break/continue - inside as GCC does */ - block(NULL, NULL, NULL, NULL, 0, 1); - skip(')'); - } else { - gexpr(); - skip(')'); - } - break; - case '*': - next(); - unary(); - indir(); - break; - case '&': - next(); - unary(); - /* functions names must be treated as function pointers, - except for unary '&' and sizeof. Since we consider that - functions are not lvalues, we only have to handle it - there and in function calls. */ - /* arrays can also be used although they are not lvalues */ - if ((vtop->type.t & VT_BTYPE) != VT_FUNC && - !(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_LLOCAL)) - test_lvalue(); - mk_pointer(&vtop->type); - gaddrof(); - break; - case '!': - next(); - unary(); - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - CType boolean; - boolean.t = VT_BOOL; - gen_cast(&boolean); - vtop->c.i = !vtop->c.i; - } else if ((vtop->r & VT_VALMASK) == VT_CMP) - vtop->c.i = vtop->c.i ^ 1; - else { - save_regs(1); - vseti(VT_JMP, gtst(1, 0)); - } - break; - case '~': - next(); - unary(); - vpushi(-1); - gen_op('^'); - break; - case '+': - next(); - /* in order to force cast, we add zero */ - unary(); - if ((vtop->type.t & VT_BTYPE) == VT_PTR) - error("pointer not accepted for unary plus"); - vpushi(0); - gen_op('+'); - break; - case TOK_SIZEOF: - case TOK_ALIGNOF1: - case TOK_ALIGNOF2: - t = tok; - next(); - if (tok == '(') { - parse_expr_type(&type); - } else { - unary_type(&type); - } - size = type_size(&type, &align); - if (t == TOK_SIZEOF) { - if (size < 0) - error("sizeof applied to an incomplete type"); - vpushi(size); - } else { - vpushi(align); - } - vtop->type.t |= VT_UNSIGNED; - break; - - case TOK_builtin_types_compatible_p: - { - CType type1, type2; - next(); - skip('('); - parse_type(&type1); - skip(','); - parse_type(&type2); - skip(')'); - type1.t &= ~(VT_CONSTANT | VT_VOLATILE); - type2.t &= ~(VT_CONSTANT | VT_VOLATILE); - vpushi(is_compatible_types(&type1, &type2)); - } - break; - case TOK_builtin_constant_p: - { - int saved_nocode_wanted, res; - next(); - skip('('); - saved_nocode_wanted = nocode_wanted; - nocode_wanted = 1; - gexpr(); - res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; - vpop(); - nocode_wanted = saved_nocode_wanted; - skip(')'); - vpushi(res); - } - break; - case TOK_builtin_frame_address: - { - CType type; - next(); - skip('('); - if (tok != TOK_CINT) { - error("__builtin_frame_address only takes integers"); - } - if (tokc.i != 0) { - error("TCC only supports __builtin_frame_address(0)"); - } - next(); - skip(')'); - type.t = VT_VOID; - mk_pointer(&type); - vset(&type, VT_LOCAL, 0); - } - break; -#ifdef TCC_TARGET_X86_64 - case TOK_builtin_malloc: - tok = TOK_malloc; - goto tok_identifier; - case TOK_builtin_free: - tok = TOK_free; - goto tok_identifier; -#endif - case TOK_INC: - case TOK_DEC: - t = tok; - next(); - unary(); - inc(0, t); - break; - case '-': - next(); - vpushi(0); - unary(); - gen_op('-'); - break; - case TOK_LAND: - if (!gnu_ext) - goto tok_identifier; - next(); - /* allow to take the address of a label */ - if (tok < TOK_UIDENT) - expect("label identifier"); - s = label_find(tok); - if (!s) { - s = label_push(&global_label_stack, tok, LABEL_FORWARD); - } else { - if (s->r == LABEL_DECLARED) - s->r = LABEL_FORWARD; - } - if (!s->type.t) { - s->type.t = VT_VOID; - mk_pointer(&s->type); - s->type.t |= VT_STATIC; - } - vset(&s->type, VT_CONST | VT_SYM, 0); - vtop->sym = s; - next(); - break; - default: - tok_identifier: - t = tok; - next(); - if (t < TOK_UIDENT) - expect("identifier"); - s = sym_find(t); - if (!s) { - if (tok != '(') - error("'%s' undeclared", get_tok_str(t, NULL)); - /* for simple function calls, we tolerate undeclared - external reference to int() function */ - if (tcc_state->warn_implicit_function_declaration) - warning("implicit declaration of function '%s'", - get_tok_str(t, NULL)); - s = external_global_sym(t, &func_old_type, 0); - } - if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) == - (VT_STATIC | VT_INLINE | VT_FUNC)) { - /* if referencing an inline function, then we generate a - symbol to it if not already done. It will have the - effect to generate code for it at the end of the - compilation unit. Inline function as always - generated in the text section. */ - if (!s->c) - put_extern_sym(s, text_section, 0, 0); - r = VT_SYM | VT_CONST; - } else { - r = s->r; - } - vset(&s->type, r, s->c); - /* if forward reference, we must point to s */ - if (vtop->r & VT_SYM) { - vtop->sym = s; - vtop->c.ul = 0; - } - break; - } - - /* post operations */ - while (1) { - if (tok == TOK_INC || tok == TOK_DEC) { - inc(1, tok); - next(); - } else if (tok == '.' || tok == TOK_ARROW) { - /* field */ - if (tok == TOK_ARROW) - indir(); - 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) - 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; - /* an array is never an lvalue */ - if (!(vtop->type.t & VT_ARRAY)) { - vtop->r |= lvalue_type(vtop->type.t); - /* if bound checking, the referenced pointer must be checked */ - if (tcc_state->do_bounds_check) - vtop->r |= VT_MUSTBOUND; - } - next(); - } else if (tok == '[') { - next(); - gexpr(); - gen_op('+'); - indir(); - skip(']'); - } else if (tok == '(') { - SValue ret; - Sym *sa; - int nb_args; - - /* function call */ - if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { - /* pointer test (no array accepted) */ - if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) { - vtop->type = *pointed_type(&vtop->type); - if ((vtop->type.t & VT_BTYPE) != VT_FUNC) - goto error_func; - } else { - error_func: - expect("function pointer"); - } - } else { - vtop->r &= ~VT_LVAL; /* no lvalue */ - } - /* get return type */ - s = vtop->type.ref; - next(); - sa = s->next; /* first parameter */ - nb_args = 0; - ret.r2 = VT_CONST; - /* compute first implicit argument if a structure is returned */ - if ((s->type.t & VT_BTYPE) == VT_STRUCT) { - /* get some space for the returned structure */ - size = type_size(&s->type, &align); - loc = (loc - size) & -align; - ret.type = s->type; - ret.r = VT_LOCAL | VT_LVAL; - /* pass it as 'int' to avoid structure arg passing - problems */ - vseti(VT_LOCAL, loc); - ret.c = vtop->c; - nb_args++; - } else { - ret.type = s->type; - /* return in register */ - if (is_float(ret.type.t)) { - ret.r = reg_fret(ret.type.t); - } else { - if ((ret.type.t & VT_BTYPE) == VT_LLONG) - ret.r2 = REG_LRET; - ret.r = REG_IRET; - } - ret.c.i = 0; - } - if (tok != ')') { - for(;;) { - expr_eq(); - gfunc_param_typed(s, sa); - nb_args++; - if (sa) - sa = sa->next; - if (tok == ')') - break; - skip(','); - } - } - if (sa) - error("too few arguments to function"); - skip(')'); - if (!nocode_wanted) { - gfunc_call(nb_args); - } else { - vtop -= (nb_args + 1); - } - /* return value */ - vsetc(&ret.type, ret.r, &ret.c); - vtop->r2 = ret.r2; - } else { - break; - } - } -} - -static void uneq(void) -{ - int t; - - unary(); - if (tok == '=' || - (tok >= TOK_A_MOD && tok <= TOK_A_DIV) || - tok == TOK_A_XOR || tok == TOK_A_OR || - tok == TOK_A_SHL || tok == TOK_A_SAR) { - test_lvalue(); - t = tok; - next(); - if (t == '=') { - expr_eq(); - } else { - vdup(); - expr_eq(); - gen_op(t & 0x7f); - } - vstore(); - } -} - -static void expr_prod(void) -{ - int t; - - uneq(); - while (tok == '*' || tok == '/' || tok == '%') { - t = tok; - next(); - uneq(); - gen_op(t); - } -} - -static void expr_sum(void) -{ - int t; - - expr_prod(); - while (tok == '+' || tok == '-') { - t = tok; - next(); - expr_prod(); - gen_op(t); - } -} - -static void expr_shift(void) -{ - int t; - - expr_sum(); - while (tok == TOK_SHL || tok == TOK_SAR) { - t = tok; - next(); - expr_sum(); - gen_op(t); - } -} - -static void expr_cmp(void) -{ - int t; - - expr_shift(); - while ((tok >= TOK_ULE && tok <= TOK_GT) || - tok == TOK_ULT || tok == TOK_UGE) { - t = tok; - next(); - expr_shift(); - gen_op(t); - } -} - -static void expr_cmpeq(void) -{ - int t; - - expr_cmp(); - while (tok == TOK_EQ || tok == TOK_NE) { - t = tok; - next(); - expr_cmp(); - gen_op(t); - } -} - -static void expr_and(void) -{ - expr_cmpeq(); - while (tok == '&') { - next(); - expr_cmpeq(); - gen_op('&'); - } -} - -static void expr_xor(void) -{ - expr_and(); - while (tok == '^') { - next(); - expr_and(); - gen_op('^'); - } -} - -static void expr_or(void) -{ - expr_xor(); - while (tok == '|') { - next(); - expr_xor(); - gen_op('|'); - } -} - -/* XXX: fix this mess */ -static void expr_land_const(void) -{ - expr_or(); - while (tok == TOK_LAND) { - next(); - expr_or(); - gen_op(TOK_LAND); - } -} - -/* XXX: fix this mess */ -static void expr_lor_const(void) -{ - expr_land_const(); - while (tok == TOK_LOR) { - next(); - expr_land_const(); - gen_op(TOK_LOR); - } -} - -/* only used if non constant */ -static void expr_land(void) -{ - int t; - - expr_or(); - if (tok == TOK_LAND) { - t = 0; - save_regs(1); - for(;;) { - t = gtst(1, t); - if (tok != TOK_LAND) { - vseti(VT_JMPI, t); - break; - } - next(); - expr_or(); - } - } -} - -static void expr_lor(void) -{ - int t; - - expr_land(); - if (tok == TOK_LOR) { - t = 0; - save_regs(1); - for(;;) { - t = gtst(0, t); - if (tok != TOK_LOR) { - vseti(VT_JMP, t); - break; - } - next(); - expr_land(); - } - } -} - -/* XXX: better constant handling */ -static void expr_eq(void) -{ - int tt, u, r1, r2, rc, t1, t2, bt1, bt2; - SValue sv; - CType type, type1, type2; - - if (const_wanted) { - expr_lor_const(); - if (tok == '?') { - CType boolean; - int c; - boolean.t = VT_BOOL; - vdup(); - gen_cast(&boolean); - c = vtop->c.i; - vpop(); - next(); - if (tok != ':' || !gnu_ext) { - vpop(); - gexpr(); - } - if (!c) - vpop(); - skip(':'); - expr_eq(); - if (c) - vpop(); - } - } else { - expr_lor(); - if (tok == '?') { - next(); - if (vtop != vstack) { - /* needed to avoid having different registers saved in - each branch */ - if (is_float(vtop->type.t)) { - rc = RC_FLOAT; -#ifdef TCC_TARGET_X86_64 - if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { - rc = RC_ST0; - } -#endif - } - else - rc = RC_INT; - gv(rc); - save_regs(1); - } - if (tok == ':' && gnu_ext) { - gv_dup(); - tt = gtst(1, 0); - } else { - tt = gtst(1, 0); - gexpr(); - } - type1 = vtop->type; - sv = *vtop; /* save value to handle it later */ - vtop--; /* no vpop so that FP stack is not flushed */ - skip(':'); - u = gjmp(0); - gsym(tt); - expr_eq(); - type2 = vtop->type; - - t1 = type1.t; - bt1 = t1 & VT_BTYPE; - t2 = type2.t; - bt2 = t2 & VT_BTYPE; - /* cast operands to correct type according to ISOC rules */ - if (is_float(bt1) || is_float(bt2)) { - if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { - type.t = VT_LDOUBLE; - } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { - type.t = VT_DOUBLE; - } else { - type.t = VT_FLOAT; - } - } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { - /* cast to biggest op */ - type.t = VT_LLONG; - /* convert to unsigned if it does not fit in a long long */ - if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || - (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) - type.t |= VT_UNSIGNED; - } else if (bt1 == VT_PTR || bt2 == VT_PTR) { - /* XXX: test pointer compatibility */ - type = type1; - } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { - /* XXX: test function pointer compatibility */ - type = type1; - } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { - /* XXX: test structure compatibility */ - type = type1; - } else if (bt1 == VT_VOID || bt2 == VT_VOID) { - /* NOTE: as an extension, we accept void on only one side */ - type.t = VT_VOID; - } else { - /* integer operations */ - type.t = VT_INT; - /* convert to unsigned if it does not fit in an integer */ - if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || - (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) - type.t |= VT_UNSIGNED; - } - - /* now we convert second operand */ - gen_cast(&type); - if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) - gaddrof(); - rc = RC_INT; - if (is_float(type.t)) { - rc = RC_FLOAT; -#ifdef TCC_TARGET_X86_64 - if ((type.t & VT_BTYPE) == VT_LDOUBLE) { - rc = RC_ST0; - } -#endif - } else if ((type.t & VT_BTYPE) == VT_LLONG) { - /* for long longs, we use fixed registers to avoid having - to handle a complicated move */ - rc = RC_IRET; - } - - r2 = gv(rc); - /* this is horrible, but we must also convert first - operand */ - tt = gjmp(0); - gsym(u); - /* put again first value and cast it */ - *vtop = sv; - gen_cast(&type); - if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) - gaddrof(); - r1 = gv(rc); - move_reg(r2, r1); - vtop->r = r2; - gsym(tt); - } - } -} - -static void gexpr(void) -{ - while (1) { - expr_eq(); - if (tok != ',') - break; - vpop(); - next(); - } -} - -/* parse an expression and return its type without any side effect. */ -static void expr_type(CType *type) -{ - int saved_nocode_wanted; - - saved_nocode_wanted = nocode_wanted; - nocode_wanted = 1; - gexpr(); - *type = vtop->type; - vpop(); - nocode_wanted = saved_nocode_wanted; -} - -/* parse a unary expression and return its type without any side - effect. */ -static void unary_type(CType *type) -{ - int a; - - a = nocode_wanted; - nocode_wanted = 1; - unary(); - *type = vtop->type; - vpop(); - nocode_wanted = a; -} - -/* parse a constant expression and return value in vtop. */ -static void expr_const1(void) -{ - int a; - a = const_wanted; - const_wanted = 1; - expr_eq(); - const_wanted = a; -} - -/* parse an integer constant and return its value. */ -static int expr_const(void) -{ - int c; - expr_const1(); - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) - expect("constant expression"); - c = vtop->c.i; - vpop(); - return c; -} - -/* return the label token if current token is a label, otherwise - return zero */ -static int is_label(void) -{ - int last_tok; - - /* fast test first */ - if (tok < TOK_UIDENT) - return 0; - /* no need to save tokc because tok is an identifier */ - last_tok = tok; - next(); - if (tok == ':') { - next(); - return last_tok; - } else { - unget_tok(last_tok); - return 0; - } -} - -static void block(int *bsym, int *csym, int *case_sym, int *def_sym, - int case_reg, int is_expr) -{ - int a, b, c, d; - Sym *s; - - /* generate line number info */ - if (tcc_state->do_debug && - (last_line_num != file->line_num || last_ind != ind)) { - put_stabn(N_SLINE, 0, file->line_num, ind - func_ind); - last_ind = ind; - last_line_num = file->line_num; - } - - if (is_expr) { - /* default return value is (void) */ - vpushi(0); - vtop->type.t = VT_VOID; - } - - if (tok == TOK_IF) { - /* if test */ - next(); - skip('('); - gexpr(); - skip(')'); - a = gtst(1, 0); - block(bsym, csym, case_sym, def_sym, case_reg, 0); - c = tok; - if (c == TOK_ELSE) { - next(); - d = gjmp(0); - gsym(a); - block(bsym, csym, case_sym, def_sym, case_reg, 0); - gsym(d); /* patch else jmp */ - } else - gsym(a); - } else if (tok == TOK_WHILE) { - next(); - d = ind; - skip('('); - gexpr(); - skip(')'); - a = gtst(1, 0); - b = 0; - block(&a, &b, case_sym, def_sym, case_reg, 0); - gjmp_addr(d); - gsym(a); - gsym_addr(b, d); - } else if (tok == '{') { - Sym *llabel; - - next(); - /* record local declaration stack position */ - s = local_stack; - llabel = local_label_stack; - /* handle local labels declarations */ - if (tok == TOK_LABEL) { - next(); - for(;;) { - if (tok < TOK_UIDENT) - expect("label identifier"); - label_push(&local_label_stack, tok, LABEL_DECLARED); - next(); - if (tok == ',') { - next(); - } else { - skip(';'); - break; - } - } - } - while (tok != '}') { - decl(VT_LOCAL); - if (tok != '}') { - if (is_expr) - vpop(); - block(bsym, csym, case_sym, def_sym, case_reg, is_expr); - } - } - /* pop locally defined labels */ - label_pop(&local_label_stack, llabel); - /* pop locally defined symbols */ - if(is_expr) { - /* XXX: this solution makes only valgrind happy... - triggered by gcc.c-torture/execute/20000917-1.c */ - Sym *p; - switch(vtop->type.t & VT_BTYPE) { - case VT_PTR: - case VT_STRUCT: - case VT_ENUM: - case VT_FUNC: - for(p=vtop->type.ref;p;p=p->prev) - if(p->prev==s) - error("unsupported expression type"); - } - } - sym_pop(&local_stack, s); - next(); - } else if (tok == TOK_RETURN) { - next(); - if (tok != ';') { - gexpr(); - gen_assign_cast(&func_vt); - if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { - CType type; - /* if returning structure, must copy it to implicit - first pointer arg location */ -#ifdef TCC_ARM_EABI - int align, size; - size = type_size(&func_vt,&align); - if(size <= 4) - { - if((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & 3)) - && (align & 3)) - { - int addr; - loc = (loc - size) & -4; - addr = loc; - type = func_vt; - vset(&type, VT_LOCAL | VT_LVAL, addr); - vswap(); - vstore(); - vset(&int_type, VT_LOCAL | VT_LVAL, addr); - } - vtop->type = int_type; - gv(RC_IRET); - } else { -#endif - type = func_vt; - mk_pointer(&type); - vset(&type, VT_LOCAL | VT_LVAL, func_vc); - indir(); - vswap(); - /* copy structure value to pointer */ - vstore(); -#ifdef TCC_ARM_EABI - } -#endif - } else if (is_float(func_vt.t)) { - gv(rc_fret(func_vt.t)); - } else { - gv(RC_IRET); - } - vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ - } - skip(';'); - rsym = gjmp(rsym); /* jmp */ - } else if (tok == TOK_BREAK) { - /* compute jump */ - if (!bsym) - error("cannot break"); - *bsym = gjmp(*bsym); - next(); - skip(';'); - } else if (tok == TOK_CONTINUE) { - /* compute jump */ - if (!csym) - error("cannot continue"); - *csym = gjmp(*csym); - next(); - skip(';'); - } else if (tok == TOK_FOR) { - int e; - next(); - skip('('); - if (tok != ';') { - gexpr(); - vpop(); - } - skip(';'); - d = ind; - c = ind; - a = 0; - b = 0; - if (tok != ';') { - gexpr(); - a = gtst(1, 0); - } - skip(';'); - if (tok != ')') { - e = gjmp(0); - c = ind; - gexpr(); - vpop(); - gjmp_addr(d); - gsym(e); - } - skip(')'); - block(&a, &b, case_sym, def_sym, case_reg, 0); - gjmp_addr(c); - gsym(a); - gsym_addr(b, c); - } else - if (tok == TOK_DO) { - next(); - a = 0; - b = 0; - d = ind; - block(&a, &b, case_sym, def_sym, case_reg, 0); - skip(TOK_WHILE); - skip('('); - gsym(b); - gexpr(); - c = gtst(0, 0); - gsym_addr(c, d); - skip(')'); - gsym(a); - skip(';'); - } else - if (tok == TOK_SWITCH) { - next(); - skip('('); - gexpr(); - /* XXX: other types than integer */ - case_reg = gv(RC_INT); - vpop(); - skip(')'); - a = 0; - b = gjmp(0); /* jump to first case */ - c = 0; - block(&a, csym, &b, &c, case_reg, 0); - /* if no default, jmp after switch */ - if (c == 0) - c = ind; - /* default label */ - gsym_addr(b, c); - /* break label */ - gsym(a); - } else - if (tok == TOK_CASE) { - int v1, v2; - if (!case_sym) - expect("switch"); - next(); - v1 = expr_const(); - v2 = v1; - if (gnu_ext && tok == TOK_DOTS) { - next(); - v2 = expr_const(); - if (v2 < v1) - warning("empty case range"); - } - /* since a case is like a label, we must skip it with a jmp */ - b = gjmp(0); - gsym(*case_sym); - vseti(case_reg, 0); - vpushi(v1); - if (v1 == v2) { - gen_op(TOK_EQ); - *case_sym = gtst(1, 0); - } else { - gen_op(TOK_GE); - *case_sym = gtst(1, 0); - vseti(case_reg, 0); - vpushi(v2); - gen_op(TOK_LE); - *case_sym = gtst(1, *case_sym); - } - gsym(b); - skip(':'); - is_expr = 0; - goto block_after_label; - } else - if (tok == TOK_DEFAULT) { - next(); - skip(':'); - if (!def_sym) - expect("switch"); - if (*def_sym) - error("too many 'default'"); - *def_sym = ind; - is_expr = 0; - goto block_after_label; - } else - if (tok == TOK_GOTO) { - next(); - if (tok == '*' && gnu_ext) { - /* computed goto */ - next(); - gexpr(); - if ((vtop->type.t & VT_BTYPE) != VT_PTR) - expect("pointer"); - ggoto(); - } else if (tok >= TOK_UIDENT) { - s = label_find(tok); - /* put forward definition if needed */ - if (!s) { - s = label_push(&global_label_stack, tok, LABEL_FORWARD); - } else { - if (s->r == LABEL_DECLARED) - s->r = LABEL_FORWARD; - } - /* label already defined */ - if (s->r & LABEL_FORWARD) - s->next = (void *)gjmp((long)s->next); - else - gjmp_addr((long)s->next); - next(); - } else { - expect("label identifier"); - } - skip(';'); - } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) { - asm_instr(); - } else { - b = is_label(); - if (b) { - /* label case */ - s = label_find(b); - if (s) { - if (s->r == LABEL_DEFINED) - error("duplicate label '%s'", get_tok_str(s->v, NULL)); - gsym((long)s->next); - s->r = LABEL_DEFINED; - } else { - s = label_push(&global_label_stack, b, LABEL_DEFINED); - } - s->next = (void *)ind; - /* we accept this, but it is a mistake */ - block_after_label: - if (tok == '}') { - warning("deprecated use of label at end of compound statement"); - } else { - if (is_expr) - vpop(); - block(bsym, csym, case_sym, def_sym, case_reg, is_expr); - } - } else { - /* expression case */ - if (tok != ';') { - if (is_expr) { - vpop(); - gexpr(); - } else { - gexpr(); - vpop(); - } - } - skip(';'); - } - } -} - -/* t is the array or struct type. c is the array or struct - address. cur_index/cur_field is the pointer to the current - value. 'size_only' is true if only size info is needed (only used - in arrays) */ -static void decl_designator(CType *type, Section *sec, unsigned long c, - int *cur_index, Sym **cur_field, - int size_only) -{ - Sym *s, *f; - int notfirst, index, index_last, align, l, nb_elems, elem_size; - CType type1; - - notfirst = 0; - elem_size = 0; - nb_elems = 1; - if (gnu_ext && (l = is_label()) != 0) - goto struct_field; - while (tok == '[' || tok == '.') { - if (tok == '[') { - if (!(type->t & VT_ARRAY)) - expect("array type"); - s = type->ref; - next(); - index = expr_const(); - if (index < 0 || (s->c >= 0 && index >= s->c)) - expect("invalid index"); - if (tok == TOK_DOTS && gnu_ext) { - next(); - index_last = expr_const(); - if (index_last < 0 || - (s->c >= 0 && index_last >= s->c) || - index_last < index) - expect("invalid index"); - } else { - index_last = index; - } - skip(']'); - if (!notfirst) - *cur_index = index_last; - type = pointed_type(type); - elem_size = type_size(type, &align); - c += index * elem_size; - /* NOTE: we only support ranges for last designator */ - nb_elems = index_last - index + 1; - if (nb_elems != 1) { - notfirst = 1; - break; - } - } else { - next(); - l = tok; - next(); - struct_field: - if ((type->t & VT_BTYPE) != VT_STRUCT) - expect("struct/union type"); - s = type->ref; - l |= SYM_FIELD; - f = s->next; - while (f) { - if (f->v == l) - break; - f = f->next; - } - if (!f) - expect("field"); - if (!notfirst) - *cur_field = f; - /* XXX: fix this mess by using explicit storage field */ - type1 = f->type; - type1.t |= (type->t & ~VT_TYPE); - type = &type1; - c += f->c; - } - notfirst = 1; - } - if (notfirst) { - if (tok == '=') { - next(); - } else { - if (!gnu_ext) - expect("="); - } - } else { - if (type->t & VT_ARRAY) { - index = *cur_index; - type = pointed_type(type); - c += index * type_size(type, &align); - } else { - f = *cur_field; - if (!f) - error("too many field init"); - /* XXX: fix this mess by using explicit storage field */ - type1 = f->type; - type1.t |= (type->t & ~VT_TYPE); - type = &type1; - c += f->c; - } - } - decl_initializer(type, sec, c, 0, size_only); - - /* XXX: make it more general */ - if (!size_only && nb_elems > 1) { - unsigned long c_end; - uint8_t *src, *dst; - int i; - - if (!sec) - error("range init not supported yet for dynamic storage"); - c_end = c + nb_elems * elem_size; - if (c_end > sec->data_allocated) - section_realloc(sec, c_end); - src = sec->data + c; - dst = src; - for(i = 1; i < nb_elems; i++) { - dst += elem_size; - memcpy(dst, src, elem_size); - } - } -} - -#define EXPR_VAL 0 -#define EXPR_CONST 1 -#define EXPR_ANY 2 - -/* store a value or an expression directly in global data or in local array */ -static void init_putv(CType *type, Section *sec, unsigned long c, - int v, int expr_type) -{ - int saved_global_expr, bt, bit_pos, bit_size; - void *ptr; - unsigned long long bit_mask; - CType dtype; - - switch(expr_type) { - case EXPR_VAL: - vpushi(v); - break; - case EXPR_CONST: - /* compound literals must be allocated globally in this case */ - saved_global_expr = global_expr; - global_expr = 1; - expr_const1(); - global_expr = saved_global_expr; - /* NOTE: symbols are accepted */ - if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST) - error("initializer element is not constant"); - break; - case EXPR_ANY: - expr_eq(); - break; - } - - dtype = *type; - dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ - - if (sec) { - /* XXX: not portable */ - /* XXX: generate error if incorrect relocation */ - gen_assign_cast(&dtype); - bt = type->t & VT_BTYPE; - /* we'll write at most 12 bytes */ - if (c + 12 > sec->data_allocated) { - section_realloc(sec, c + 12); - } - ptr = sec->data + c; - /* XXX: make code faster ? */ - if (!(type->t & VT_BITFIELD)) { - bit_pos = 0; - bit_size = 32; - bit_mask = -1LL; - } else { - bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; - bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; - bit_mask = (1LL << bit_size) - 1; - } - if ((vtop->r & VT_SYM) && - (bt == VT_BYTE || - bt == VT_SHORT || - bt == VT_DOUBLE || - bt == VT_LDOUBLE || - bt == VT_LLONG || - (bt == VT_INT && bit_size != 32))) - error("initializer element is not computable at load time"); - switch(bt) { - case VT_BOOL: - vtop->c.i = (vtop->c.i != 0); - case VT_BYTE: - *(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos; - break; - case VT_SHORT: - *(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos; - break; - case VT_DOUBLE: - *(double *)ptr = vtop->c.d; - break; - case VT_LDOUBLE: - *(long double *)ptr = vtop->c.ld; - break; - case VT_LLONG: - *(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos; - break; - default: - if (vtop->r & VT_SYM) { - greloc(sec, vtop->sym, c, R_DATA_32); - } - *(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos; - break; - } - vtop--; - } else { - vset(&dtype, VT_LOCAL|VT_LVAL, c); - vswap(); - vstore(); - vpop(); - } -} - -/* put zeros for variable based init */ -static void init_putz(CType *t, Section *sec, unsigned long c, int size) -{ - if (sec) { - /* nothing to do because globals are already set to zero */ - } else { - vpush_global_sym(&func_old_type, TOK_memset); - vseti(VT_LOCAL, c); - vpushi(0); - vpushi(size); - gfunc_call(3); - } -} - -/* 't' contains the type and storage info. 'c' is the offset of the - object in section 'sec'. If 'sec' is NULL, it means stack based - allocation. 'first' is true if array '{' must be read (multi - dimension implicit array init handling). 'size_only' is true if - size only evaluation is wanted (only for arrays). */ -static void decl_initializer(CType *type, Section *sec, unsigned long c, - int first, int size_only) -{ - int index, array_length, n, no_oblock, nb, parlevel, i; - int size1, align1, expr_type; - Sym *s, *f; - CType *t1; - - if (type->t & VT_ARRAY) { - s = type->ref; - n = s->c; - array_length = 0; - t1 = pointed_type(type); - size1 = type_size(t1, &align1); - - no_oblock = 1; - if ((first && tok != TOK_LSTR && tok != TOK_STR) || - tok == '{') { - skip('{'); - no_oblock = 0; - } - - /* only parse strings here if correct type (otherwise: handle - them as ((w)char *) expressions */ - if ((tok == TOK_LSTR && -#ifdef TCC_TARGET_PE - (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED) -#else - (t1->t & VT_BTYPE) == VT_INT -#endif - ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) { - while (tok == TOK_STR || tok == TOK_LSTR) { - int cstr_len, ch; - CString *cstr; - - cstr = tokc.cstr; - /* compute maximum number of chars wanted */ - if (tok == TOK_STR) - cstr_len = cstr->size; - else - cstr_len = cstr->size / sizeof(nwchar_t); - cstr_len--; - nb = cstr_len; - if (n >= 0 && nb > (n - array_length)) - nb = n - array_length; - if (!size_only) { - if (cstr_len > nb) - warning("initializer-string for array is too long"); - /* in order to go faster for common case (char - string in global variable, we handle it - specifically */ - if (sec && tok == TOK_STR && size1 == 1) { - memcpy(sec->data + c + array_length, cstr->data, nb); - } else { - for(i=0;idata)[i]; - else - ch = ((nwchar_t *)cstr->data)[i]; - init_putv(t1, sec, c + (array_length + i) * size1, - ch, EXPR_VAL); - } - } - } - array_length += nb; - next(); - } - /* only add trailing zero if enough storage (no - warning in this case since it is standard) */ - if (n < 0 || array_length < n) { - if (!size_only) { - init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL); - } - array_length++; - } - } else { - index = 0; - while (tok != '}') { - decl_designator(type, sec, c, &index, NULL, size_only); - if (n >= 0 && index >= n) - error("index too large"); - /* must put zero in holes (note that doing it that way - ensures that it even works with designators) */ - if (!size_only && array_length < index) { - init_putz(t1, sec, c + array_length * size1, - (index - array_length) * size1); - } - index++; - if (index > array_length) - array_length = index; - /* special test for multi dimensional arrays (may not - be strictly correct if designators are used at the - same time) */ - if (index >= n && no_oblock) - break; - if (tok == '}') - break; - skip(','); - } - } - if (!no_oblock) - skip('}'); - /* put zeros at the end */ - if (!size_only && n >= 0 && array_length < n) { - init_putz(t1, sec, c + array_length * size1, - (n - array_length) * size1); - } - /* patch type size if needed */ - if (n < 0) - s->c = array_length; - } else if ((type->t & VT_BTYPE) == VT_STRUCT && - (sec || !first || tok == '{')) { - int par_count; - - /* NOTE: the previous test is a specific case for automatic - struct/union init */ - /* XXX: union needs only one init */ - - /* XXX: this test is incorrect for local initializers - beginning with ( without {. It would be much more difficult - to do it correctly (ideally, the expression parser should - be used in all cases) */ - par_count = 0; - if (tok == '(') { - AttributeDef ad1; - CType type1; - next(); - while (tok == '(') { - par_count++; - next(); - } - if (!parse_btype(&type1, &ad1)) - expect("cast"); - type_decl(&type1, &ad1, &n, TYPE_ABSTRACT); -#if 0 - if (!is_assignable_types(type, &type1)) - error("invalid type for cast"); -#endif - skip(')'); - } - no_oblock = 1; - if (first || tok == '{') { - skip('{'); - no_oblock = 0; - } - s = type->ref; - f = s->next; - array_length = 0; - index = 0; - n = s->c; - while (tok != '}') { - decl_designator(type, sec, c, NULL, &f, size_only); - index = f->c; - if (!size_only && array_length < index) { - init_putz(type, sec, c + array_length, - index - array_length); - } - index = index + type_size(&f->type, &align1); - if (index > array_length) - array_length = index; - f = f->next; - if (no_oblock && f == NULL) - break; - if (tok == '}') - break; - skip(','); - } - /* put zeros at the end */ - if (!size_only && array_length < n) { - init_putz(type, sec, c + array_length, - n - array_length); - } - if (!no_oblock) - skip('}'); - while (par_count) { - skip(')'); - par_count--; - } - } else if (tok == '{') { - next(); - decl_initializer(type, sec, c, first, size_only); - skip('}'); - } else if (size_only) { - /* just skip expression */ - parlevel = 0; - while ((parlevel > 0 || (tok != '}' && tok != ',')) && - tok != -1) { - if (tok == '(') - parlevel++; - else if (tok == ')') - parlevel--; - next(); - } - } else { - /* currently, we always use constant expression for globals - (may change for scripting case) */ - expr_type = EXPR_CONST; - if (!sec) - expr_type = EXPR_ANY; - init_putv(type, sec, c, 0, expr_type); - } -} - -/* parse an initializer for type 't' if 'has_init' is non zero, and - allocate space in local or global data space ('r' is either - VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated - variable 'v' of scope 'scope' is declared before initializers are - parsed. If 'v' is zero, then a reference to the new object is put - in the value stack. If 'has_init' is 2, a special parsing is done - to handle string constants. */ -static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, - int has_init, int v, int scope) -{ - int size, align, addr, data_offset; - int level; - ParseState saved_parse_state = {0}; - TokenString init_str; - Section *sec; - - size = type_size(type, &align); - /* If unknown size, we must evaluate it before - evaluating initializers because - initializers can generate global data too - (e.g. string pointers or ISOC99 compound - literals). It also simplifies local - initializers handling */ - tok_str_new(&init_str); - if (size < 0) { - if (!has_init) - error("unknown type size"); - /* get all init string */ - if (has_init == 2) { - /* only get strings */ - while (tok == TOK_STR || tok == TOK_LSTR) { - tok_str_add_tok(&init_str); - next(); - } - } else { - level = 0; - while (level > 0 || (tok != ',' && tok != ';')) { - if (tok < 0) - error("unexpected end of file in initializer"); - tok_str_add_tok(&init_str); - if (tok == '{') - level++; - else if (tok == '}') { - level--; - if (level <= 0) { - next(); - break; - } - } - next(); - } - } - tok_str_add(&init_str, -1); - tok_str_add(&init_str, 0); - - /* compute size */ - save_parse_state(&saved_parse_state); - - macro_ptr = init_str.str; - next(); - decl_initializer(type, NULL, 0, 1, 1); - /* prepare second initializer parsing */ - macro_ptr = init_str.str; - next(); - - /* if still unknown size, error */ - size = type_size(type, &align); - if (size < 0) - error("unknown type size"); - } - /* take into account specified alignment if bigger */ - if (ad->aligned) { - if (ad->aligned > align) - align = ad->aligned; - } else if (ad->packed) { - align = 1; - } - if ((r & VT_VALMASK) == VT_LOCAL) { - sec = NULL; - if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) - loc--; - loc = (loc - size) & -align; - addr = loc; - /* handles bounds */ - /* XXX: currently, since we do only one pass, we cannot track - '&' operators, so we add only arrays */ - if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) { - unsigned long *bounds_ptr; - /* add padding between regions */ - loc--; - /* then add local bound info */ - bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(unsigned long)); - bounds_ptr[0] = addr; - bounds_ptr[1] = size; - } - if (v) { - /* local variable */ - sym_push(v, type, r, addr); - } else { - /* push local reference */ - vset(type, r, addr); - } - } else { - Sym *sym; - - sym = NULL; - if (v && scope == VT_CONST) { - /* see if the symbol was already defined */ - sym = sym_find(v); - if (sym) { - if (!is_compatible_types(&sym->type, type)) - error("incompatible types for redefinition of '%s'", - get_tok_str(v, NULL)); - if (sym->type.t & VT_EXTERN) { - /* if the variable is extern, it was not allocated */ - sym->type.t &= ~VT_EXTERN; - /* set array size if it was ommited in extern - declaration */ - if ((sym->type.t & VT_ARRAY) && - sym->type.ref->c < 0 && - type->ref->c >= 0) - sym->type.ref->c = type->ref->c; - } else { - /* we accept several definitions of the same - global variable. this is tricky, because we - must play with the SHN_COMMON type of the symbol */ - /* XXX: should check if the variable was already - initialized. It is incorrect to initialized it - twice */ - /* no init data, we won't add more to the symbol */ - if (!has_init) - goto no_alloc; - } - } - } - - /* allocate symbol in corresponding section */ - sec = ad->section; - if (!sec) { - if (has_init) - sec = data_section; - else if (tcc_state->nocommon) - sec = bss_section; - } - if (sec) { - data_offset = sec->data_offset; - data_offset = (data_offset + align - 1) & -align; - addr = data_offset; - /* very important to increment global pointer at this time - because initializers themselves can create new initializers */ - data_offset += size; - /* add padding if bound check */ - if (tcc_state->do_bounds_check) - data_offset++; - sec->data_offset = data_offset; - /* allocate section space to put the data */ - if (sec->sh_type != SHT_NOBITS && - data_offset > sec->data_allocated) - section_realloc(sec, data_offset); - /* align section if needed */ - if (align > sec->sh_addralign) - sec->sh_addralign = align; - } else { - addr = 0; /* avoid warning */ - } - - if (v) { - if (scope != VT_CONST || !sym) { - sym = sym_push(v, type, r | VT_SYM, 0); - } - /* update symbol definition */ - if (sec) { - put_extern_sym(sym, sec, addr, size); - } else { - ElfW(Sym) *esym; - /* put a common area */ - put_extern_sym(sym, NULL, align, size); - /* XXX: find a nicer way */ - esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; - esym->st_shndx = SHN_COMMON; - } - } else { - CValue cval; - - /* push global reference */ - sym = get_sym_ref(type, sec, addr, size); - cval.ul = 0; - vsetc(type, VT_CONST | VT_SYM, &cval); - vtop->sym = sym; - } - - /* handles bounds now because the symbol must be defined - before for the relocation */ - if (tcc_state->do_bounds_check) { - unsigned long *bounds_ptr; - - greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_32); - /* then add global bound info */ - bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(long)); - bounds_ptr[0] = 0; /* relocated */ - bounds_ptr[1] = size; - } - } - if (has_init) { - decl_initializer(type, sec, addr, 1, 0); - /* restore parse state if needed */ - if (init_str.str) { - tok_str_free(init_str.str); - restore_parse_state(&saved_parse_state); - } - } - no_alloc: ; -} - -void put_func_debug(Sym *sym) -{ - char buf[512]; - - /* stabs info */ - /* XXX: we put here a dummy type */ - snprintf(buf, sizeof(buf), "%s:%c1", - funcname, sym->type.t & VT_STATIC ? 'f' : 'F'); - put_stabs_r(buf, N_FUN, 0, file->line_num, 0, - cur_text_section, sym->c); - /* //gr gdb wants a line at the function */ - put_stabn(N_SLINE, 0, file->line_num, 0); - last_ind = 0; - last_line_num = 0; -} - -/* parse an old style function declaration list */ -/* XXX: check multiple parameter */ -static void func_decl_list(Sym *func_sym) -{ - AttributeDef ad; - int v; - Sym *s; - CType btype, type; - - /* parse each declaration */ - while (tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF) { - if (!parse_btype(&btype, &ad)) - expect("declaration list"); - if (((btype.t & VT_BTYPE) == VT_ENUM || - (btype.t & VT_BTYPE) == VT_STRUCT) && - tok == ';') { - /* we accept no variable after */ - } else { - for(;;) { - type = btype; - type_decl(&type, &ad, &v, TYPE_DIRECT); - /* find parameter in function parameter list */ - s = func_sym->next; - while (s != NULL) { - if ((s->v & ~SYM_FIELD) == v) - goto found; - s = s->next; - } - error("declaration for parameter '%s' but no such parameter", - get_tok_str(v, NULL)); - found: - /* check that no storage specifier except 'register' was given */ - if (type.t & VT_STORAGE) - error("storage class specified for '%s'", get_tok_str(v, NULL)); - convert_parameter_type(&type); - /* we can add the type (NOTE: it could be local to the function) */ - s->type = type; - /* accept other parameters */ - if (tok == ',') - next(); - else - break; - } - } - skip(';'); - } -} - -/* parse a function defined by symbol 'sym' and generate its code in - 'cur_text_section' */ -static void gen_function(Sym *sym) -{ - int saved_nocode_wanted = nocode_wanted; - nocode_wanted = 0; - ind = cur_text_section->data_offset; - /* NOTE: we patch the symbol size later */ - put_extern_sym(sym, cur_text_section, ind, 0); - funcname = get_tok_str(sym->v, NULL); - func_ind = ind; - /* put debug symbol */ - if (tcc_state->do_debug) - put_func_debug(sym); - /* push a dummy symbol to enable local sym storage */ - sym_push2(&local_stack, SYM_FIELD, 0, 0); - gfunc_prolog(&sym->type); - rsym = 0; - block(NULL, NULL, NULL, NULL, 0, 0); - gsym(rsym); - gfunc_epilog(); - cur_text_section->data_offset = ind; - label_pop(&global_label_stack, NULL); - sym_pop(&local_stack, NULL); /* reset local stack */ - /* end of function */ - /* patch symbol size */ - ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = - ind - func_ind; - if (tcc_state->do_debug) { - put_stabn(N_FUN, 0, 0, ind - func_ind); - } - /* It's better to crash than to generate wrong code */ - cur_text_section = NULL; - funcname = ""; /* for safety */ - func_vt.t = VT_VOID; /* for safety */ - ind = 0; /* for safety */ - nocode_wanted = saved_nocode_wanted; -} - -static void gen_inline_functions(void) -{ - Sym *sym; - CType *type; - int *str, inline_generated; - - /* iterate while inline function are referenced */ - for(;;) { - inline_generated = 0; - for(sym = global_stack; sym != NULL; sym = sym->prev) { - type = &sym->type; - if (((type->t & VT_BTYPE) == VT_FUNC) && - (type->t & (VT_STATIC | VT_INLINE)) == - (VT_STATIC | VT_INLINE) && - sym->c != 0) { - /* the function was used: generate its code and - convert it to a normal function */ - str = INLINE_DEF(sym->r); - sym->r = VT_SYM | VT_CONST; - sym->type.t &= ~VT_INLINE; - - macro_ptr = str; - next(); - cur_text_section = text_section; - gen_function(sym); - macro_ptr = NULL; /* fail safe */ - - tok_str_free(str); - inline_generated = 1; - } - } - if (!inline_generated) - break; - } - - /* free all remaining inline function tokens */ - for(sym = global_stack; sym != NULL; sym = sym->prev) { - type = &sym->type; - if (((type->t & VT_BTYPE) == VT_FUNC) && - (type->t & (VT_STATIC | VT_INLINE)) == - (VT_STATIC | VT_INLINE)) { - //gr printf("sym %d %s\n", sym->r, get_tok_str(sym->v, NULL)); - if (sym->r == (VT_SYM | VT_CONST)) //gr beware! - continue; - str = INLINE_DEF(sym->r); - tok_str_free(str); - sym->r = 0; /* fail safe */ - } - } -} - -/* 'l' is VT_LOCAL or VT_CONST to define default storage type */ -static void decl(int l) -{ - int v, has_init, r; - CType type, btype; - Sym *sym; - AttributeDef ad; - - while (1) { - if (!parse_btype(&btype, &ad)) { - /* skip redundant ';' */ - /* XXX: find more elegant solution */ - if (tok == ';') { - next(); - continue; - } - if (l == VT_CONST && - (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) { - /* global asm block */ - asm_global_instr(); - continue; - } - /* special test for old K&R protos without explicit int - type. Only accepted when defining global data */ - if (l == VT_LOCAL || tok < TOK_DEFINE) - break; - btype.t = VT_INT; - } - if (((btype.t & VT_BTYPE) == VT_ENUM || - (btype.t & VT_BTYPE) == VT_STRUCT) && - tok == ';') { - /* we accept no variable after */ - next(); - continue; - } - while (1) { /* iterate thru each declaration */ - type = btype; - type_decl(&type, &ad, &v, TYPE_DIRECT); -#if 0 - { - char buf[500]; - type_to_str(buf, sizeof(buf), t, get_tok_str(v, NULL)); - printf("type = '%s'\n", buf); - } -#endif - if ((type.t & VT_BTYPE) == VT_FUNC) { - /* if old style function prototype, we accept a - declaration list */ - sym = type.ref; - if (sym->c == FUNC_OLD) - func_decl_list(sym); - } - - if (tok == '{') { - if (l == VT_LOCAL) - error("cannot use local functions"); - if ((type.t & VT_BTYPE) != VT_FUNC) - expect("function definition"); - - /* reject abstract declarators in function definition */ - sym = type.ref; - while ((sym = sym->next) != NULL) - if (!(sym->v & ~SYM_FIELD)) - expect("identifier"); - - /* XXX: cannot do better now: convert extern line to static inline */ - if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) - type.t = (type.t & ~VT_EXTERN) | VT_STATIC; - - sym = sym_find(v); - if (sym) { - if ((sym->type.t & VT_BTYPE) != VT_FUNC) - goto func_error1; - /* specific case: if not func_call defined, we put - the one of the prototype */ - /* XXX: should have default value */ - r = sym->type.ref->r; - if (FUNC_CALL(r) != FUNC_CDECL - && FUNC_CALL(type.ref->r) == FUNC_CDECL) - FUNC_CALL(type.ref->r) = FUNC_CALL(r); - if (FUNC_EXPORT(r)) - FUNC_EXPORT(type.ref->r) = 1; - - if (!is_compatible_types(&sym->type, &type)) { - func_error1: - error("incompatible types for redefinition of '%s'", - get_tok_str(v, NULL)); - } - /* if symbol is already defined, then put complete type */ - sym->type = type; - } else { - /* put function symbol */ - sym = global_identifier_push(v, type.t, 0); - sym->type.ref = type.ref; - } - - /* static inline functions are just recorded as a kind - of macro. Their code will be emitted at the end of - the compilation unit only if they are used */ - if ((type.t & (VT_INLINE | VT_STATIC)) == - (VT_INLINE | VT_STATIC)) { - TokenString func_str; - int block_level; - - tok_str_new(&func_str); - - block_level = 0; - for(;;) { - int t; - if (tok == TOK_EOF) - error("unexpected end of file"); - tok_str_add_tok(&func_str); - t = tok; - next(); - if (t == '{') { - block_level++; - } else if (t == '}') { - block_level--; - if (block_level == 0) - break; - } - } - tok_str_add(&func_str, -1); - tok_str_add(&func_str, 0); - INLINE_DEF(sym->r) = func_str.str; - } else { - /* compute text section */ - cur_text_section = ad.section; - if (!cur_text_section) - cur_text_section = text_section; - sym->r = VT_SYM | VT_CONST; - gen_function(sym); - } - break; - } else { - if (btype.t & VT_TYPEDEF) { - /* save typedefed type */ - /* XXX: test storage specifiers ? */ - sym = sym_push(v, &type, 0, 0); - sym->type.t |= VT_TYPEDEF; - } else if ((type.t & VT_BTYPE) == VT_FUNC) { - /* external function definition */ - /* specific case for func_call attribute */ - if (ad.func_attr) - type.ref->r = ad.func_attr; - external_sym(v, &type, 0); - } else { - /* not lvalue if array */ - r = 0; - if (!(type.t & VT_ARRAY)) - r |= lvalue_type(type.t); - has_init = (tok == '='); - if ((btype.t & VT_EXTERN) || - ((type.t & VT_ARRAY) && (type.t & VT_STATIC) && - !has_init && l == VT_CONST && type.ref->c < 0)) { - /* external variable */ - /* NOTE: as GCC, uninitialized global static - arrays of null size are considered as - extern */ - external_sym(v, &type, r); - } else { - type.t |= (btype.t & VT_STATIC); /* Retain "static". */ - if (type.t & VT_STATIC) - r |= VT_CONST; - else - r |= l; - if (has_init) - next(); - decl_initializer_alloc(&type, &ad, r, - has_init, v, l); - } - } - if (tok != ',') { - skip(';'); - break; - } - next(); - } - } - } -} - -- cgit v1.2.3