update gen_cast

This commit is contained in:
grischka 2019-12-16 18:51:28 +01:00
parent 35475b5423
commit 89372dc482
10 changed files with 376 additions and 283 deletions

View File

@ -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) {

View File

@ -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)
{

View File

@ -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:

20
tcc.h
View File

@ -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

520
tccgen.c
View File

@ -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)

View File

@ -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"

View File

@ -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;

View File

@ -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)

View File

@ -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:

View File

@ -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)
{