diff --git a/arm64-gen.c b/arm64-gen.c index 760e70e0..da54b3d7 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -1718,6 +1718,16 @@ ST_FUNC void gen_cvt_sxtw(void) o(0x93407c00 | r | r << 5); // sxtw x(r),w(r) } +/* char/short to int conversion */ +ST_FUNC void gen_cvt_csti(int t) +{ + int r = intr(gv(RC_INT)); + o(0x13001c00 + | ((t & VT_BTYPE) == VT_SHORT) << 13 + | (uint32_t)!!(t & VT_UNSIGNED) << 30 + | r | r << 5); // [su]xt[bh] w(r),w(r) +} + ST_FUNC void gen_cvt_itof(int t) { if (t == VT_LDOUBLE) { diff --git a/i386-gen.c b/i386-gen.c index 7fdc90cc..47568b6e 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -1048,6 +1048,19 @@ ST_FUNC void gen_cvt_ftof(int t) gv(RC_FLOAT); } +/* char/short to int conversion */ +ST_FUNC void gen_cvt_csti(int t) +{ + int r, sz, xl; + r = gv(RC_INT); + sz = !(t & VT_UNSIGNED); + xl = (t & VT_BTYPE) == VT_SHORT; + o(0xc0b60f /* mov[sz] %a[xl], %eax */ + | (sz << 3 | xl) << 8 + | (r << 3 | r) << 16 + ); +} + /* computed goto support */ ST_FUNC void ggoto(void) { diff --git a/libtcc.c b/libtcc.c index 355f6ecd..608e515a 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1965,7 +1965,8 @@ reparse: } break; case TCC_OPTION_W: - if (set_flag(s, options_W, optarg) < 0) + s->warn_none = 0; + if (optarg[0] && set_flag(s, options_W, optarg) < 0) goto unsupported_option; break; case TCC_OPTION_w: diff --git a/tcc.h b/tcc.h index a61c5ba5..038a1234 100644 --- a/tcc.h +++ b/tcc.h @@ -926,9 +926,9 @@ struct filespec { #define VT_JMPI 0x0035 /* value is the consequence of jmp false (odd) */ #define VT_LVAL 0x0100 /* var is an lvalue */ #define VT_SYM 0x0200 /* a symbol value is added */ -#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for +#define VT_MUSTCAST 0x0C00 /* value must be casted to be correct (used for char/short stored in integer registers) */ -#define VT_MUSTBOUND 0x0800 /* bound checking must be done before +#define VT_MUSTBOUND 0x4000 /* bound checking must be done before dereferencing value */ #define VT_BOUNDED 0x8000 /* value is bounded. The address of the bounding function call point is in vc */ @@ -986,6 +986,11 @@ struct filespec { #define VT_ASM (VT_VOID | VT_UNSIGNED) #define IS_ASM_SYM(sym) (((sym)->type.t & (VT_BTYPE | VT_ASM)) == VT_ASM) +/* general: set/get the pseudo-bitfield value for bit-mask M */ +#define BFVAL(M,N) ((unsigned)((M) & ~((M) << 1)) * (N)) +#define BFGET(X,M) (((X) & (M)) / BFVAL(M,1)) +#define BFSET(X,M,N) ((X) = ((X) & ~(M)) | BFVAL(M,N)) + /* token values */ /* warning: the following compare tokens depend on i386 asm code */ @@ -1359,7 +1364,7 @@ ST_DATA Sym *local_stack; ST_DATA Sym *local_label_stack; ST_DATA Sym *global_label_stack; ST_DATA Sym *define_stack; -ST_DATA CType char_pointer_type, func_old_type, int_type, size_type; +ST_DATA CType int_type, func_old_type, char_pointer_type; ST_DATA SValue *vtop; ST_DATA int rsym, anon_sym, ind, loc; @@ -1598,6 +1603,7 @@ ST_FUNC void gen_le16(int c); ST_FUNC void gen_le32(int c); ST_FUNC void gen_addr32(int r, Sym *sym, int c); ST_FUNC void gen_addrpc32(int r, Sym *sym, int c); +ST_FUNC void gen_cvt_csti(int t); #endif #ifdef CONFIG_TCC_BCHECK @@ -1612,6 +1618,8 @@ ST_FUNC void gen_opl(int op); #ifdef TCC_TARGET_PE ST_FUNC void gen_vla_result(int addr); #endif +ST_FUNC void gen_cvt_sxtw(void); +ST_FUNC void gen_cvt_csti(int t); #endif /* ------------ arm-gen.c ------------ */ @@ -1624,22 +1632,24 @@ ST_FUNC void arm_init(struct TCCState *s); /* ------------ arm64-gen.c ------------ */ #ifdef TCC_TARGET_ARM64 -ST_FUNC void gen_cvt_sxtw(void); ST_FUNC void gen_opl(int op); ST_FUNC void gfunc_return(CType *func_type); ST_FUNC void gen_va_start(void); ST_FUNC void gen_va_arg(CType *t); ST_FUNC void gen_clear_cache(void); +ST_FUNC void gen_cvt_sxtw(void); +ST_FUNC void gen_cvt_csti(int t); #endif /* ------------ riscv64-gen.c ------------ */ #ifdef TCC_TARGET_RISCV64 -ST_FUNC void gen_cvt_sxtw(void); ST_FUNC void gen_opl(int op); //ST_FUNC void gfunc_return(CType *func_type); ST_FUNC void gen_va_start(void); ST_FUNC void arch_transfer_ret_regs(int); +ST_FUNC void gen_cvt_sxtw(void); #endif + /* ------------ c67-gen.c ------------ */ #ifdef TCC_TARGET_C67 #endif diff --git a/tccgen.c b/tccgen.c index f26dad9a..43c142b2 100644 --- a/tccgen.c +++ b/tccgen.c @@ -79,8 +79,18 @@ ST_DATA int func_var; /* true if current function is variadic (used by return in ST_DATA int func_vc; static int last_line_num, new_file, func_ind; /* debug info control */ ST_DATA const char *funcname; +ST_DATA CType int_type, func_old_type, char_pointer_type; -ST_DATA CType char_pointer_type, func_old_type, int_type, size_type, ptrdiff_type; +#if PTR_SIZE == 4 +#define VT_SIZE_T (VT_INT | VT_UNSIGNED) +#define VT_PTRDIFF_T VT_INT +#elif LONG_SIZE == 4 +#define VT_SIZE_T (VT_LLONG | VT_UNSIGNED) +#define VT_PTRDIFF_T VT_LLONG +#else +#define VT_SIZE_T (VT_LONG | VT_LLONG | VT_UNSIGNED) +#define VT_PTRDIFF_T (VT_LONG | VT_LLONG) +#endif ST_DATA struct switch_t { struct case_t { @@ -149,6 +159,7 @@ static void skip_or_save_block(TokenString **str); static void gv_dup(void); static int get_temp_local_var(int size,int align); static void clear_temp_local_var_list(); +static void cast_error(CType *st, CType *dt); ST_INLN int is_float(int t) { @@ -162,6 +173,7 @@ ST_INLN int is_float(int t) static inline int is_integer_btype(int bt) { return bt == VT_BYTE + || bt == VT_BOOL || bt == VT_SHORT || bt == VT_INT || bt == VT_LLONG; @@ -439,16 +451,6 @@ ST_FUNC void tccgen_init(TCCState *s1) int_type.t = VT_INT; char_pointer_type.t = VT_BYTE; mk_pointer(&char_pointer_type); -#if PTR_SIZE == 4 - size_type.t = VT_INT | VT_UNSIGNED; - ptrdiff_type.t = VT_INT; -#elif LONG_SIZE == 4 - size_type.t = VT_LLONG | VT_UNSIGNED; - ptrdiff_type.t = VT_LLONG; -#else - size_type.t = VT_LONG | VT_LLONG | VT_UNSIGNED; - ptrdiff_type.t = VT_LONG | VT_LLONG; -#endif func_old_type.t = VT_FUNC; func_old_type.ref = sym_push(SYM_FIELD, &int_type, 0, 0); func_old_type.ref->f.func_call = FUNC_CDECL; @@ -922,29 +924,13 @@ ST_FUNC void vpop(void) } /* push constant of type "type" with useless value */ -ST_FUNC void vpush(CType *type) +static void vpush(CType *type) { vset(type, VT_CONST, 0); } -/* push integer constant */ -ST_FUNC void vpushi(int v) -{ - CValue cval; - cval.i = v; - vsetc(&int_type, VT_CONST, &cval); -} - -/* push a pointer sized constant */ -static void vpushs(addr_t v) -{ - CValue cval; - cval.i = v; - vsetc(&size_type, VT_CONST, &cval); -} - /* push arbitrary 64bit constant */ -ST_FUNC void vpush64(int ty, unsigned long long v) +static void vpush64(int ty, unsigned long long v) { CValue cval; CType ctype; @@ -954,6 +940,18 @@ ST_FUNC void vpush64(int ty, unsigned long long v) vsetc(&ctype, VT_CONST, &cval); } +/* push integer constant */ +ST_FUNC void vpushi(int v) +{ + vpush64(VT_INT, v); +} + +/* push a pointer sized constant */ +static void vpushs(addr_t v) +{ + vpush64(VT_SIZE_T, v); +} + /* push long long constant */ static inline void vpushll(long long v) { @@ -963,7 +961,6 @@ static inline void vpushll(long long v) ST_FUNC void vset(CType *type, int r, int v) { CValue cval; - cval.i = v; vsetc(type, r, &cval); } @@ -1099,6 +1096,23 @@ static int gvtst(int inv, int t) return t; } +/* generate a zero or nozero test */ +static void gen_test_zero(int op) +{ + if (vtop->r == VT_CMP) { + int j; + if (op == TOK_EQ) { + j = vtop->jfalse; + vtop->jfalse = vtop->jtrue; + vtop->jtrue = j; + vtop->cmp_op ^= 1; + } + } else { + vpushi(0); + gen_op(op); + } +} + /* ------------------------------------------------------------------------- */ /* push a symbol value of TYPE */ static inline void vpushsym(CType *type, Sym *sym) @@ -1350,44 +1364,34 @@ ST_FUNC void save_reg(int r) if seen up to (vtop - n) stack entry */ ST_FUNC void save_reg_upstack(int r, int n) { - int l, saved, size, align; + int l, size, align, bt; SValue *p, *p1, sv; - CType *type; if ((r &= VT_VALMASK) >= VT_CONST) return; if (nocode_wanted) return; - - /* modify all stack values */ - saved = 0; l = 0; for(p = vstack, p1 = vtop - n; p <= p1; p++) { - if ((p->r & VT_VALMASK) == r || (p->r2 & VT_VALMASK) == r) { + if ((p->r & VT_VALMASK) == r || p->r2 == r) { /* must save value on stack if not already done */ - if (!saved) { - /* NOTE: must reload 'r' because r might be equal to r2 */ - r = p->r & VT_VALMASK; - /* store register in the stack */ - type = &p->type; - if ((p->r & VT_LVAL) || - (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) -#if PTR_SIZE == 8 - type = &char_pointer_type; -#else - type = &int_type; -#endif - size = type_size(type, &align); + if (!l) { + bt = p->type.t & VT_BTYPE; + if (bt == VT_VOID) + continue; + if ((p->r & VT_LVAL) || bt == VT_FUNC) + bt = VT_PTR; + sv.type.t = bt; + size = type_size(&sv.type, &align); #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) l = loc = (loc - size) & -align; else #endif - l=get_temp_local_var(size,align); - sv.type.t = type->t; + l = get_temp_local_var(size,align); sv.r = VT_LOCAL | VT_LVAL; sv.c.i = l; - store(r, &sv); + store(p->r & VT_VALMASK, &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) { @@ -1395,11 +1399,10 @@ ST_FUNC void save_reg_upstack(int r, int n) } #endif /* special long long case */ - if (p->r2 < VT_CONST && USING_TWO_WORDS(type->t)) { + if (p->r2 < VT_CONST && USING_TWO_WORDS(bt)) { sv.c.i += PTR_SIZE; store(p->r2, &sv); } - saved = 1; } /* mark that stack entry as being saved on the stack */ if (p->r & VT_LVAL) { @@ -1771,7 +1774,7 @@ ST_FUNC int gv(int rc) if (!r_ok) r = get_reg(rc); if (rc2) { - int load_type = (bt == VT_QFLOAT) ? VT_DOUBLE : ptrdiff_type.t; + int load_type = (bt == VT_QFLOAT) ? VT_DOUBLE : VT_PTRDIFF_T; int original_type = vtop->type.t; /* two register type load : @@ -1793,9 +1796,9 @@ ST_FUNC int gv(int rc) vdup(); vtop[-1].r = r; /* save register value */ /* increment pointer to get second word */ - vtop->type.t = ptrdiff_type.t; + vtop->type.t = VT_PTRDIFF_T; gaddrof(); - vpushi(PTR_SIZE); + vpushs(PTR_SIZE); gen_op('+'); vtop->r |= VT_LVAL; vtop->type.t = load_type; @@ -2521,7 +2524,7 @@ redo: } vrott(3); gen_opic(op); - vtop->type.t = ptrdiff_type.t; + vtop->type.t = VT_PTRDIFF_T; vswap(); gen_op(TOK_PDIV); } else { @@ -2712,41 +2715,17 @@ static void gen_cvt_ftoi1(int t) } #endif -/* force char or short cast */ -static void force_charshort_cast(int t) +/* special delayed cast for char/short */ +static void force_charshort_cast(void) { - int bits, dbt; - - /* cannot cast static initializers */ - if (STATIC_DATA_WANTED) - return; - - 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 { - if ((vtop->type.t & VT_BTYPE) == VT_LLONG) - bits = 64 - bits; - 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); - } + int sbt = BFGET(vtop->r, VT_MUSTCAST) == 2 ? VT_LLONG : VT_INT; + int dbt = vtop->type.t; + vtop->r &= ~VT_MUSTCAST; + vtop->type.t = sbt; + gen_cast_s(dbt == VT_BOOL ? VT_BYTE|VT_UNSIGNED : dbt); + vtop->type.t = dbt; } -/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */ static void gen_cast_s(int t) { CType type; @@ -2755,31 +2734,33 @@ static void gen_cast_s(int t) gen_cast(&type); } +/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */ static void gen_cast(CType *type) { - int sbt, dbt, sf, df, c, p; + int sbt, dbt, sf, df, c; + int dbt_bt, sbt_bt, ds, ss, bits, trunc; /* 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); - } + if (vtop->r & VT_MUSTCAST) + force_charshort_cast(); /* bitfields first get cast to ints */ - if (vtop->type.t & VT_BITFIELD) { + 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 == VT_FUNC) + sbt = VT_PTR; +again: if (sbt != dbt) { sf = is_float(sbt); df = is_float(dbt); + dbt_bt = dbt & VT_BTYPE; + sbt_bt = sbt & VT_BTYPE; + 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 !defined TCC_IS_NATIVE && !defined TCC_IS_NATIVE_387 c &= (dbt != VT_LDOUBLE) | !!nocode_wanted; #endif @@ -2792,7 +2773,7 @@ static void gen_cast(CType *type) vtop->c.ld = vtop->c.d; if (df) { - if ((sbt & VT_BTYPE) == VT_LLONG) { + if (sbt_bt == VT_LLONG) { if ((sbt & VT_UNSIGNED) || !(vtop->c.i >> 63)) vtop->c.ld = vtop->c.i; else @@ -2808,164 +2789,173 @@ static void gen_cast(CType *type) 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.i = vtop->c.ld; } else if (sf && dbt == VT_BOOL) { vtop->c.i = (vtop->c.ld != 0); } else { if(sf) vtop->c.i = vtop->c.ld; - else if (sbt == (VT_LLONG|VT_UNSIGNED)) + else if (sbt_bt == VT_LLONG || (PTR_SIZE == 8 && sbt == VT_PTR)) ; else if (sbt & VT_UNSIGNED) vtop->c.i = (uint32_t)vtop->c.i; -#if PTR_SIZE == 8 - else if (sbt == VT_PTR) - ; -#endif - else if (sbt != VT_LLONG) - vtop->c.i = ((uint32_t)vtop->c.i | - -(vtop->c.i & 0x80000000)); + else + vtop->c.i = ((uint32_t)vtop->c.i | -(vtop->c.i & 0x80000000)); - if (dbt == (VT_LLONG|VT_UNSIGNED)) + if (dbt_bt == VT_LLONG || (PTR_SIZE == 8 && dbt == VT_PTR)) ; else if (dbt == VT_BOOL) vtop->c.i = (vtop->c.i != 0); -#if PTR_SIZE == 8 - else if (dbt == VT_PTR) - ; -#endif - else if (dbt != VT_LLONG) { - uint32_t m = ((dbt & VT_BTYPE) == VT_BYTE ? 0xff : - (dbt & VT_BTYPE) == VT_SHORT ? 0xffff : - 0xffffffff); + else { + uint32_t m = dbt_bt == VT_BYTE ? 0xff : + dbt_bt == VT_SHORT ? 0xffff : + 0xffffffff; vtop->c.i &= m; if (!(dbt & VT_UNSIGNED)) vtop->c.i |= -(vtop->c.i & ((m >> 1) + 1)); } } - } else if (p && dbt == VT_BOOL) { + goto done; + + } else if (dbt == VT_BOOL + && (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) + == (VT_CONST | VT_SYM)) { + /* addresses are considered non-zero (see tcctest.c:sinit23) */ vtop->r = VT_CONST; vtop->c.i = 1; - } else { - /* non constant case: generate code */ + goto done; + } + + /* cannot generate code for global or static initializers */ + if (STATIC_DATA_WANTED) + goto done; + + /* non constant case: generate code */ + if (dbt == VT_BOOL) { + gen_test_zero(TOK_NE); + goto done; + } + + if (sf || df) { 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) { + } else { /* convert fp to int */ - if (dbt == VT_BOOL) { - vpushi(0); - gen_op(TOK_NE); - } else { - /* we handle char/short/etc... with generic code */ - if (dbt != (VT_INT | VT_UNSIGNED) && - dbt != (VT_LLONG | VT_UNSIGNED) && - dbt != VT_LLONG) - dbt = VT_INT; - gen_cvt_ftoi1(dbt); - if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) { - /* additional cast for char/short... */ - vtop->type.t = dbt; - gen_cast(type); - } - } -#if PTR_SIZE == 4 - } 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_s(VT_INT); - } - gv_dup(); - vpushi(31); - gen_op(TOK_SAR); - } - /* patch second register */ - vtop[-1].r2 = vtop->r; - vpop(); - } -#else - } else if ((dbt & VT_BTYPE) == VT_LLONG || - (dbt & VT_BTYPE) == VT_PTR || - (dbt & VT_BTYPE) == VT_FUNC) { - if ((sbt & VT_BTYPE) != VT_LLONG && - (sbt & VT_BTYPE) != VT_PTR && - (sbt & VT_BTYPE) != VT_FUNC) { - /* need to convert from 32bit to 64bit */ - gv(RC_INT); - if (sbt != (VT_INT | VT_UNSIGNED)) { -#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_RISCV64) - gen_cvt_sxtw(); -#elif defined(TCC_TARGET_X86_64) - int r = gv(RC_INT); - /* x86_64 specific: movslq */ - o(0x6348); - o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); -#else -#error -#endif - } else if (sbt & VT_UNSIGNED) { -#if defined(TCC_TARGET_RISCV64) - /* RISC-V keeps 32bit vals in registers sign-extended. - So here we need a zero-extension. */ - vtop->type.t = VT_LLONG; - vpushi(32); - gen_op(TOK_SHL); - vpushi(32); - gen_op(TOK_SHR); -#endif - } - } -#endif - } else if (dbt == VT_BOOL) { - /* scalar to bool */ - vpushi(0); - gen_op(TOK_NE); - } else if ((dbt & VT_BTYPE) == VT_BYTE || - (dbt & VT_BTYPE) == VT_SHORT) { - if (sbt == VT_PTR) { - vtop->type.t = VT_INT; - tcc_warning("nonportable conversion from pointer to char/short"); - } - force_charshort_cast(dbt); - } else if ((dbt & VT_BTYPE) == VT_INT) { - /* scalar to int */ - if ((sbt & VT_BTYPE) == VT_LLONG) { -#if PTR_SIZE == 4 - /* from long long: just take low order word */ - lexpand(); - vpop(); -#else - if (dbt & VT_UNSIGNED) { - /* XXX some architectures (e.g. risc-v) would like it - better for this merely being a 32-to-64 sign or zero- - extension. */ - vpushi(0xffffffff); - vtop->type.t |= VT_UNSIGNED; - gen_op('&'); - } else { - } -#endif - } + sbt = dbt; + if (dbt_bt != VT_LLONG && dbt_bt != VT_INT) + sbt = VT_INT; + gen_cvt_ftoi1(sbt); + goto again; /* may need char/short cast */ } - if ((vtop->r & VT_LVAL) - && (dbt & VT_BTYPE) != (sbt & VT_BTYPE)) - gv(RC_INT); + goto done; } + + ds = btype_size(dbt_bt); + ss = btype_size(sbt_bt); + if (ds == 0 || ss == 0) { + if (dbt_bt == VT_VOID) + goto done; + cast_error(&vtop->type, type); + } + if (IS_ENUM(type->t) && type->ref->c < 0) + tcc_error("cast to incomplete type"); + + /* same size and no sign conversion needed */ + if (ds == ss && ds >= 4) + goto done; + if (dbt_bt == VT_PTR || sbt_bt == VT_PTR) { + tcc_warning("cast between pointer and integer of different size"); + if (sbt_bt == VT_PTR) { + /* put integer type to allow logical operations below */ + vtop->type.t = (PTR_SIZE == 8 ? VT_LLONG : VT_INT); + } + } + + /* processor allows { int a = 0, b = *(char*)&a; } + That means that if we cast to less width, we can just + change the type and read it still later. */ + #define ALLOW_SUBTYPE_ACCESS 1 + + if (ALLOW_SUBTYPE_ACCESS && (vtop->r & VT_LVAL)) { + /* value still in memory */ + if (ds <= ss) + goto done; + /* ss <= 4 here */ + if (ds <= 4) { + gv(RC_INT); + goto done; /* no 64bit envolved */ + } + } + gv(RC_INT); + + trunc = 0; +#if PTR_SIZE == 4 + if (ds == 8) { + /* generate high word */ + if (sbt & VT_UNSIGNED) { + vpushi(0); + gv(RC_INT); + } else { + gv_dup(); + vpushi(31); + gen_op(TOK_SAR); + } + lbuild(dbt); + } else if (ss == 8) { + /* from long long: just take low order word */ + lexpand(); + vpop(); + } + ss = 4; + +#elif PTR_SIZE == 8 + if (ds == 8) { + /* need to convert from 32bit to 64bit */ + if (sbt & VT_UNSIGNED) { +#if defined(TCC_TARGET_RISCV64) + /* RISC-V keeps 32bit vals in registers sign-extended. + So here we need a zero-extension. */ + trunc = 32; +#else + goto done; +#endif + } else { + gen_cvt_sxtw(); + goto done; + } + ss = ds, ds = 4, dbt = sbt; + } else if (ss == 8) { + /* XXX some architectures (e.g. risc-v) would like it + better for this merely being a 32-to-64 sign or zero- + extension. */ + trunc = 32; /* zero upper 32 bits */ + } else { + ss = 4; + } +#endif + + if (ds >= ss) + goto done; +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 || defined TCC_TARGET_ARM64 + if (ss == 4) { + gen_cvt_csti(dbt); + goto done; + } +#endif + bits = (ss - ds) * 8; + /* for unsigned, gen_op will convert SAR to SHR */ + vtop->type.t = (ss == 8 ? VT_LLONG : VT_INT) | (dbt & VT_UNSIGNED); + vpushi(bits); + gen_op(TOK_SHL); + vpushi(bits - trunc); + gen_op(TOK_SAR); + vpushi(trunc); + gen_op(TOK_SHR); } +done: vtop->type = *type; vtop->type.t &= ~ ( VT_CONSTANT | VT_VOLATILE | VT_ARRAY ); } @@ -3293,25 +3283,30 @@ static void type_to_str(char *buf, int buf_size, no_var: ; } +static void cast_error(CType *st, CType *dt) +{ + char buf1[256], buf2[256]; + type_to_str(buf1, sizeof(buf1), st, NULL); + type_to_str(buf2, sizeof(buf2), dt, NULL); + tcc_error("cannot convert '%s' to '%s'", buf1, buf2); +} + /* verify type compatibility to store vtop in 'dt' type */ static void verify_assign_cast(CType *dt) { CType *st, *type1, *type2; - char buf1[256], buf2[256]; int dbt, sbt, qualwarn, lvl; st = &vtop->type; /* source type */ dbt = dt->t & VT_BTYPE; sbt = st->t & VT_BTYPE; - if (sbt == VT_VOID || dbt == VT_VOID) { - if (sbt == VT_VOID && dbt == VT_VOID) - ; /* It is Ok if both are void */ - else - tcc_error("cannot cast from/to void"); - } if (dt->t & VT_CONSTANT) tcc_warning("assignment of read-only location"); switch(dbt) { + case VT_VOID: + if (sbt != dbt) + tcc_error("assignment to void expression"); + break; case VT_PTR: /* special cases for pointers */ /* '0' can also be a pointer */ @@ -3375,10 +3370,8 @@ static void verify_assign_cast(CType *dt) case VT_STRUCT: case_VT_STRUCT: if (!is_compatible_unqualified_types(dt, st)) { - error: - type_to_str(buf1, sizeof(buf1), st, NULL); - type_to_str(buf2, sizeof(buf2), dt, NULL); - tcc_error("cannot cast '%s' to '%s'", buf1, buf2); + error: + cast_error(st, dt); } break; } @@ -3495,7 +3488,7 @@ ST_FUNC void vstore(void) if ((vtop->r & VT_MUSTCAST) && btype_size(dbt) > btype_size(sbt) ) - force_charshort_cast(dbt); + force_charshort_cast(); delayed_cast = 1; } else { gen_cast(&vtop[-1].type); @@ -3512,7 +3505,7 @@ ST_FUNC void vstore(void) gv(RC_TYPE(dbt)); /* generate value */ if (delayed_cast) { - vtop->r |= VT_MUSTCAST; + vtop->r |= BFVAL(VT_MUSTCAST, (sbt == VT_LLONG) + 1); //tcc_warning("deley cast %x -> %x", sbt, dbt); vtop->type.t = ft & VT_TYPE; } @@ -3521,7 +3514,7 @@ ST_FUNC void vstore(void) if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { SValue sv; r = get_reg(RC_INT); - sv.type.t = ptrdiff_type.t; + sv.type.t = VT_PTRDIFF_T; sv.r = VT_LOCAL | VT_LVAL; sv.c.i = vtop[-1].c.i; load(r, &sv); @@ -3532,12 +3525,12 @@ ST_FUNC void vstore(void) /* two word case handling : store second register at word + 4 (or +8 for x86-64) */ if (USING_TWO_WORDS(dbt)) { - int load_type = (dbt == VT_QFLOAT) ? VT_DOUBLE : ptrdiff_type.t; + int load_type = (dbt == VT_QFLOAT) ? VT_DOUBLE : VT_PTRDIFF_T; vtop[-1].type.t = load_type; store(r, vtop - 1); vswap(); /* convert to int to increment easily */ - vtop->type.t = ptrdiff_type.t; + vtop->type.t = VT_PTRDIFF_T; gaddrof(); vpushs(PTR_SIZE); gen_op('+'); @@ -4861,6 +4854,8 @@ static void gfunc_param_typed(Sym *func, Sym *arg) type.t = vtop->type.t & (VT_BTYPE | VT_UNSIGNED); type.ref = vtop->type.ref; gen_cast(&type); + } else if (vtop->r & VT_MUSTCAST) { + force_charshort_cast(); } } else if (arg == NULL) { tcc_error("too many arguments to function"); @@ -5094,16 +5089,7 @@ ST_FUNC void unary(void) case '!': next(); unary(); - if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { - gen_cast_s(VT_BOOL); - vtop->c.i = !vtop->c.i; - } else if (vtop->r == VT_CMP) { - vtop->cmp_op ^= 1; - n = vtop->jfalse, vtop->jfalse = vtop->jtrue, vtop->jtrue = n; - } else { - vpushi(0); - gen_op(TOK_EQ); - } + gen_test_zero(TOK_EQ); break; case '~': next(); @@ -6491,17 +6477,23 @@ again: next(); } else if (t == TOK_RETURN) { - a = tok != ';'; b = (func_vt.t & VT_BTYPE) != VT_VOID; - if (a) - gexpr(), gen_assign_cast(&func_vt); + if (tok != ';') { + gexpr(); + if (b) { + gen_assign_cast(&func_vt); + } else { + if (vtop->type.t != VT_VOID) + tcc_warning("void function returns a value"); + vtop--; + } + } else if (b) { + tcc_warning("'return' with no value"); + b = 0; + } leave_scope(root_scope); - if (a && b) + if (b) gfunc_return(&func_vt); - else if (a) - vtop--; - else if (b) - tcc_warning("'return' with no value."); skip(';'); /* jump unless last stmt in top-level block */ if (tok != '}' || local_scope != 1) diff --git a/tests/Makefile b/tests/Makefile index ce434b10..b3aaea8b 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -269,6 +269,7 @@ cross-test : $(TOP)/arm-tcc$(EXESUF) $(TCCFLAGS-unx) -c $(TOPSRC)/examples/ex3.c && echo "ok" $(TOP)/arm-wince-tcc$(EXESUF) $(TCCFLAGS-win) -c $(TOPSRC)/examples/ex3.c && echo "ok" $(TOP)/arm64-tcc$(EXESUF) $(TCCFLAGS-unx) -c $(TOPSRC)/examples/ex3.c && echo "ok" + $(TOP)/riscv64-tcc$(EXESUF) $(TCCFLAGS-unx) -c $(TOPSRC)/examples/ex3.c && echo "ok" $(TOP)/c67-tcc$(EXESUF) $(TCCFLAGS-unx) -c $(TOPSRC)/examples/ex3.c && echo "ok" $(TOP)/i386-win32-tcc$(EXESUF) $(TCCFLAGS-win) $(TOPSRC)/win32/examples/hello_win.c && echo "ok" $(TOP)/x86_64-win32-tcc$(EXESUF) $(TCCFLAGS-win) $(TOPSRC)/win32/examples/hello_win.c && echo "ok" diff --git a/tests/tcctest.c b/tests/tcctest.c index 2a6fe6a7..0c28d316 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -16,6 +16,10 @@ #endif +#ifndef __TINYC__ +typedef __SIZE_TYPE__ uintptr_t; +#endif + #if defined(_WIN32) #define LONG_LONG_FORMAT "%lld" #define ULONG_LONG_FORMAT "%llu" @@ -1163,7 +1167,7 @@ void struct_test() s->f3 = 1; printf("st2: %d %d %d\n", s->f1, s->f2, s->f3); - printf("str_addr=%x\n", (int)st1.str - (int)&st1.f1); + printf("str_addr=%x\n", (int)(uintptr_t)st1.str - (int)(uintptr_t)&st1.f1); /* align / size tests */ printf("aligntest1 sizeof=%d alignof=%d\n", @@ -1203,10 +1207,17 @@ void struct_test() printf("Large: offsetof(compound_head)=%d\n", (int)((char*)&ls.compound_head - (char*)&ls)); } +/* simulate char/short return value with undefined upper bits */ +static int __csf(int x) { return x; } +static void *_csf = __csf; +#define csf(t,n) ((t(*)(int))_csf)(n) + /* XXX: depend on endianness */ void char_short_test() { int var1, var2; + char var3; + long long var4; printf("char_short:\n"); @@ -1230,6 +1241,34 @@ void char_short_test() printf("var1=%x\n", var1); *(int *)&var1 = 0x08090a0b; printf("var1=%x\n", var1); + + var1 = 0x778899aa; + var4 = 0x11223344aa998877ULL; + var1 = var3 = var1 + 1; + var4 = var3 = var4 + 1; + printf("promote char/short assign %d "LONG_LONG_FORMAT"\n", var1, var4); + var1 = 0x778899aa; + var4 = 0x11223344aa998877ULL; + printf("promote char/short assign VA %d %d\n", var3 = var1 + 1, var3 = var4 + 1); + printf("promote char/short cast VA %d %d\n", (char)(var1 + 1), (char)(var4 + 1)); + var1 = csf(unsigned char,0x89898989); + var4 = csf(char,0xabababab); + printf("promote char/short funcret %d "LONG_LONG_FORMAT"\n", var1, var4); + printf("promote char/short fumcret VA %d %d %d %d\n", + csf(unsigned short,0xcdcdcdcd), + csf(short,0xefefefef), + csf(_Bool,0x33221100), + csf(_Bool,0x33221101)); + var3 = -10; + var1 = (char)(unsigned char)(var3 + 1); + var4 = (char)(unsigned char)(var3 + 1); + printf("promote multicast (char)(unsigned char) %d "LONG_LONG_FORMAT"\n", var1, var4); + var4 = 0x11223344aa998877ULL; + var4 = (unsigned)(int)(var4 + 1); + printf("promote multicast (unsigned)(int) "LONG_LONG_FORMAT"\n", var4); + var4 = 0x11223344bbaa9988ULL; + var4 = (unsigned)(char)(var4 + 1); + printf("promote multicast (unsigned)(char) "LONG_LONG_FORMAT"\n", var4); } /******************/ @@ -1679,6 +1718,10 @@ void cast_test() printf("sizeof(-(char)'a') = %d\n", sizeof(-(char)'a')); printf("sizeof(~(char)'a') = %d\n", sizeof(-(char)'a')); +#ifdef __TINYC__ +# pragma comment(option, "-w") +#endif + /* from pointer to integer types */ printf("%d %d %ld %ld %lld %lld\n", (int)p, (unsigned int)p, @@ -1689,6 +1732,10 @@ void cast_test() printf("%p %p %p %p\n", (void *)a, (void *)b, (void *)c, (void *)d); +#ifdef __TINYC__ +# pragma comment(option, "-W") +#endif + /* int to int with sign set */ printf("0x%lx\n", (unsigned long)(int)ul); } @@ -2347,7 +2394,7 @@ void funcptr_test() /* Check that we can align functions */ func = aligned_function; - printf("aligned_function (should be zero): %d\n", ((int)func) & 15); + printf("aligned_function (should be zero): %d\n", ((int)(uintptr_t)func) & 15); } void lloptest(long long a, long long b) @@ -2549,6 +2596,9 @@ void longlong_test(void) unsigned long long u = 0x8000000000000001ULL; u = (unsigned)(u + 1); printf("long long u=" ULONG_LONG_FORMAT "\n", u); + u = 0x11223344aa998877ULL; + u = (unsigned)(int)(u + 1); + printf("long long u=" ULONG_LONG_FORMAT "\n", u); /* was a problem with missing save_regs in gen_opl on 32-bit platforms */ char cc = 78; @@ -2897,10 +2947,6 @@ void c99_vla_test(int size1, int size2) #endif } -#ifndef __TINYC__ -typedef __SIZE_TYPE__ uintptr_t; -#endif - void sizeof_test(void) { int a; diff --git a/tests/tests2/88_codeopt.c b/tests/tests2/88_codeopt.c index 0da88b5e..2ab4c8a8 100644 --- a/tests/tests2/88_codeopt.c +++ b/tests/tests2/88_codeopt.c @@ -1,7 +1,7 @@ /* Check some way in where code suppression caused various miscompilations. */ extern int printf (const char *, ...); -typedef unsigned long size_t; +typedef __SIZE_TYPE__ size_t; size_t _brk_start, _brk_end; void * extend_brk(size_t size, size_t align) diff --git a/tests/tests2/96_nodata_wanted.expect b/tests/tests2/96_nodata_wanted.expect index 499b36e9..92dc9c4c 100644 --- a/tests/tests2/96_nodata_wanted.expect +++ b/tests/tests2/96_nodata_wanted.expect @@ -9,7 +9,7 @@ [test_local_data_noerror] 96_nodata_wanted.c:25: warning: assignment makes integer from pointer without a cast -96_nodata_wanted.c:25: warning: nonportable conversion from pointer to char/short +96_nodata_wanted.c:25: warning: cast between pointer and integer of different size [test_data_suppression_off] data: diff --git a/x86_64-gen.c b/x86_64-gen.c index 6ee0eba8..5d42f195 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -524,7 +524,7 @@ void load(int r, SValue *sv) o(0xf024); o(0xf02444dd); /* fldl -0x10(%rsp) */ } else { - orex(1,r,v, 0x89); + orex(is64_type(ft), r, v, 0x89); o(0xc0 + REG_VALUE(r) + REG_VALUE(v) * 8); /* mov v, r */ } } @@ -598,16 +598,13 @@ void store(int r, SValue *v) if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { gen_modrm64(op64, r, v->r, v->sym, fc); } else if (fr != r) { - /* XXX: don't we really come here? */ - abort(); + orex(1, fr, r, op64); o(0xc0 + fr + r * 8); /* mov r, fr */ } } else { if (fr == VT_CONST || fr == VT_LOCAL || (v->r & VT_LVAL)) { gen_modrm(r, v->r, v->sym, fc); } else if (fr != r) { - /* XXX: don't we really come here? */ - abort(); o(0xc0 + fr + r * 8); /* mov r, fr */ } } @@ -2248,6 +2245,29 @@ void gen_cvt_ftoi(int t) vtop->r = r; } +// Generate sign extension from 32 to 64 bits: +ST_FUNC void gen_cvt_sxtw(void) +{ + int r = gv(RC_INT); + /* x86_64 specific: movslq */ + o(0x6348); + o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); +} + +/* char/short to int conversion */ +ST_FUNC void gen_cvt_csti(int t) +{ + int r, sz, xl, ll; + r = gv(RC_INT); + sz = !(t & VT_UNSIGNED); + xl = (t & VT_BTYPE) == VT_SHORT; + ll = (vtop->type.t & VT_BTYPE) == VT_LLONG; + orex(ll, r, 0, 0xc0b60f /* mov[sz] %a[xl], %eax */ + | (sz << 3 | xl) << 8 + | (REG_VALUE(r) << 3 | REG_VALUE(r)) << 16 + ); +} + /* computed goto support */ void ggoto(void) {