nocode, noreturn

A more automatic approach to code suppression (aka. nocode_wanted)

The simple rules are:
- Clear 'nocode_wanted' at (im/explicit) label IF it was used
- Set 'nocode_wanted' after unconditional jumps

Also in order to test this then I did add the "function might
return no value" warning, and then to make that work again I
did add the __attribute__((noreturn)).

Also moved the look ahead label check into the type parser
to gain a little speed.
This commit is contained in:
grischka 2019-04-29 13:53:07 +02:00
parent 8569048031
commit 1b57560502
15 changed files with 263 additions and 325 deletions

View File

@ -382,11 +382,6 @@ void gsym_addr(int t, int a)
} }
} }
void gsym(int t)
{
gsym_addr(t, ind);
}
#ifdef TCC_ARM_VFP #ifdef TCC_ARM_VFP
static uint32_t vfpr(int r) static uint32_t vfpr(int r)
{ {

View File

@ -236,12 +236,6 @@ ST_FUNC void gsym_addr(int t_, int a_)
} }
} }
// Patch all branches in list pointed to by t to branch to current location:
ST_FUNC void gsym(int t)
{
gsym_addr(t, ind);
}
static int arm64_type_size(int t) static int arm64_type_size(int t)
{ {
switch (t & VT_BTYPE) { switch (t & VT_BTYPE) {

View File

@ -226,11 +226,6 @@ void gsym_addr(int t, int a)
} }
} }
void gsym(int t)
{
gsym_addr(t, ind);
}
// these are regs that tcc doesn't really know about, // these are regs that tcc doesn't really know about,
// but assign them unique values so the mapping routines // but assign them unique values so the mapping routines
// can distinguish them // can distinguish them

View File

@ -140,11 +140,6 @@ ST_FUNC void gsym_addr(int t, int a)
} }
} }
ST_FUNC void gsym(int t)
{
gsym_addr(t, ind);
}
/* instruction + 4 bytes data. Return the address of the data */ /* instruction + 4 bytes data. Return the address of the data */
static int oad(int c, int s) static int oad(int c, int s)
{ {

View File

@ -60,6 +60,7 @@ void *__va_arg(__va_list_struct *ap,
default: /* should never happen */ default: /* should never happen */
abort(); abort();
return 0;
} }
} }
#endif #endif

2
tcc.h
View File

@ -453,6 +453,8 @@ struct FuncAttr {
unsigned unsigned
func_call : 3, /* calling convention (0..5), see below */ func_call : 3, /* calling convention (0..5), see below */
func_type : 2, /* FUNC_OLD/NEW/ELLIPSIS */ func_type : 2, /* FUNC_OLD/NEW/ELLIPSIS */
func_noreturn : 1, /* attribute((noreturn)) */
xxxx : 2,
func_args : 8; /* PE __stdcall args */ func_args : 8; /* PE __stdcall args */
}; };

View File

@ -912,9 +912,6 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
next(); next();
if (tok == TOK_EOF) if (tok == TOK_EOF)
break; break;
/* generate line number info */
if (global && s1->do_debug)
tcc_debug_line(s1);
parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
redo: redo:
if (tok == '#') { if (tok == '#') {
@ -1148,7 +1145,6 @@ ST_FUNC void asm_instr(void)
uint8_t clobber_regs[NB_ASM_REGS]; uint8_t clobber_regs[NB_ASM_REGS];
Section *sec; Section *sec;
next();
/* since we always generate the asm() instruction, we can ignore /* since we always generate the asm() instruction, we can ignore
volatile */ volatile */
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) { if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {

539
tccgen.c
View File

@ -44,6 +44,7 @@ static int ncleanups;
static int local_scope; static int local_scope;
static int in_sizeof; static int in_sizeof;
static int in_generic;
static int section_sym; static int section_sym;
ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */ ST_DATA int vlas_in_scope; /* number of VLAs that are currently in scope */
@ -56,6 +57,24 @@ ST_DATA int const_wanted; /* true if constant wanted */
ST_DATA int nocode_wanted; /* no code generation wanted */ ST_DATA int nocode_wanted; /* no code generation wanted */
#define NODATA_WANTED (nocode_wanted > 0) /* no static data output wanted either */ #define NODATA_WANTED (nocode_wanted > 0) /* no static data output wanted either */
#define STATIC_DATA_WANTED (nocode_wanted & 0xC0000000) /* only static data output */ #define STATIC_DATA_WANTED (nocode_wanted & 0xC0000000) /* only static data output */
/* Automagical code suppression ----> */
#define CODE_OFF() (nocode_wanted |= 0x20000000)
#define CODE_ON() (nocode_wanted &= ~0x20000000)
/* Clear 'nocode_wanted' at label if it was used */
ST_FUNC void gsym(int t) { if (t) { gsym_addr(t, ind); CODE_ON(); }}
static int gind(void) { CODE_ON(); return ind; }
/* Set 'nocode_wanted' after unconditional jumps */
static void gjmp_addr_acs(int t) { gjmp_addr(t); CODE_OFF(); }
static int gjmp_acs(int t) { t = gjmp(t); CODE_OFF(); return t; }
/* These are #undef'd at the end of this file */
#define gjmp_addr gjmp_addr_acs
#define gjmp gjmp_acs
/* <---- */
ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */ ST_DATA int global_expr; /* true if compound literals must be allocated globally (used during initializers parsing */
ST_DATA CType func_vt; /* current function return type (used by return instruction) */ ST_DATA CType func_vt; /* current function return type (used by return instruction) */
ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */ ST_DATA int func_var; /* true if current function is variadic (used by return instruction) */
@ -3544,8 +3563,7 @@ redo:
break; break;
case TOK_NORETURN1: case TOK_NORETURN1:
case TOK_NORETURN2: case TOK_NORETURN2:
/* currently, no need to handle it because tcc does not ad->f.func_noreturn = 1;
track unused objects */
break; break;
case TOK_CDECL1: case TOK_CDECL1:
case TOK_CDECL2: case TOK_CDECL2:
@ -4129,7 +4147,7 @@ static void parse_btype_qualify(CType *type, int qualifiers)
*/ */
static int parse_btype(CType *type, AttributeDef *ad) static int parse_btype(CType *type, AttributeDef *ad)
{ {
int t, u, bt, st, type_found, typespec_found, g; int t, u, bt, st, type_found, typespec_found, g, n;
Sym *s; Sym *s;
CType type1; CType type1;
@ -4336,6 +4354,14 @@ static int parse_btype(CType *type, AttributeDef *ad)
s = sym_find(tok); s = sym_find(tok);
if (!s || !(s->type.t & VT_TYPEDEF)) if (!s || !(s->type.t & VT_TYPEDEF))
goto the_end; goto the_end;
n = tok, next();
if (tok == ':' && !in_generic) {
/* ignore if it's a label */
unget_tok(n);
goto the_end;
}
t &= ~(VT_BTYPE|VT_LONG); t &= ~(VT_BTYPE|VT_LONG);
u = t & ~(VT_CONSTANT | VT_VOLATILE), t ^= u; u = t & ~(VT_CONSTANT | VT_VOLATILE), t ^= u;
type->t = (s->type.t & ~VT_TYPEDEF) | u; type->t = (s->type.t & ~VT_TYPEDEF) | u;
@ -4345,7 +4371,6 @@ static int parse_btype(CType *type, AttributeDef *ad)
t = type->t; t = type->t;
/* get attributes from typedef */ /* get attributes from typedef */
sym_to_attr(ad, s); sym_to_attr(ad, s);
next();
typespec_found = 1; typespec_found = 1;
st = bt = -2; st = bt = -2;
break; break;
@ -5237,7 +5262,11 @@ ST_FUNC void unary(void)
AttributeDef ad_tmp; AttributeDef ad_tmp;
int itmp; int itmp;
CType cur_type; CType cur_type;
in_generic++;
parse_btype(&cur_type, &ad_tmp); parse_btype(&cur_type, &ad_tmp);
in_generic--;
type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT); type_decl(&cur_type, &ad_tmp, &itmp, TYPE_ABSTRACT);
if (compare_types(&controlling_type, &cur_type, 0)) { if (compare_types(&controlling_type, &cur_type, 0)) {
if (has_match) { if (has_match) {
@ -5501,6 +5530,8 @@ special_math_val:
} }
vset(&s->type, VT_LOCAL | VT_LVAL, addr); vset(&s->type, VT_LOCAL | VT_LVAL, addr);
} }
if (s->f.func_noreturn)
CODE_OFF();
} else { } else {
break; break;
} }
@ -5603,86 +5634,50 @@ static void expr_or(void)
} }
} }
static int condition_3way(void);
static void expr_landor(void(*e_fn)(void), int e_op, int i)
{
int t = 0, cc = 1, f = 0, c;
for(;;) {
c = f ? i : condition_3way();
if (c < 0) {
save_regs(1), cc = 0;
} else if (c != i) {
nocode_wanted++, f = 1;
}
if (tok != e_op) {
if (cc || f) {
vpop();
vpushi(i ^ f);
gsym(t);
nocode_wanted -= f;
} else {
vseti(VT_JMP + i, gvtst(i, t));
}
break;
}
if (c < 0)
t = gvtst(i, t);
else
vpop();
next();
e_fn();
}
}
static void expr_land(void) static void expr_land(void)
{ {
expr_or(); expr_or();
if (tok == TOK_LAND) { if (tok == TOK_LAND)
int t = 0; expr_landor(expr_or, TOK_LAND, 1);
for(;;) {
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
gen_cast_s(VT_BOOL);
if (vtop->c.i) {
vpop();
} else {
nocode_wanted++;
while (tok == TOK_LAND) {
next();
expr_or();
vpop();
}
nocode_wanted--;
if (t)
gsym(t);
gen_cast_s(VT_INT);
break;
}
} else {
if (!t)
save_regs(1);
t = gvtst(1, t);
}
if (tok != TOK_LAND) {
if (t)
vseti(VT_JMPI, t);
else
vpushi(1);
break;
}
next();
expr_or();
}
}
} }
static void expr_lor(void) static void expr_lor(void)
{ {
expr_land(); expr_land();
if (tok == TOK_LOR) { if (tok == TOK_LOR)
int t = 0; expr_landor(expr_land, TOK_LOR, 0);
for(;;) {
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
gen_cast_s(VT_BOOL);
if (!vtop->c.i) {
vpop();
} else {
nocode_wanted++;
while (tok == TOK_LOR) {
next();
expr_land();
vpop();
}
nocode_wanted--;
if (t)
gsym(t);
gen_cast_s(VT_INT);
break;
}
} else {
if (!t)
save_regs(1);
t = gvtst(0, t);
}
if (tok != TOK_LOR) {
if (t)
vseti(VT_JMP, t);
else
vpushi(0);
break;
}
next();
expr_land();
}
}
} }
/* Assuming vtop is a value used in a conditional context /* Assuming vtop is a value used in a conditional context
@ -5706,15 +5701,25 @@ static void expr_cond(void)
int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g; int tt, u, r1, r2, rc, t1, t2, bt1, bt2, islv, c, g;
SValue sv; SValue sv;
CType type, type1, type2; CType type, type1, type2;
int ncw_prev;
expr_lor(); expr_lor();
if (tok == '?') { if (tok == '?') {
next(); next();
c = condition_3way(); c = condition_3way();
g = (tok == ':' && gnu_ext); g = (tok == ':' && gnu_ext);
if (c < 0) { tt = 0;
if (!g) {
if (c < 0) {
save_regs(1);
tt = gvtst(1, 0);
} else {
vpop();
}
} else if (c < 0) {
/* needed to avoid having different registers saved in /* needed to avoid having different registers saved in
each branch */ each branch */
rc = RC_INT;
if (is_float(vtop->type.t)) { if (is_float(vtop->type.t)) {
rc = RC_FLOAT; rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
@ -5722,20 +5727,14 @@ static void expr_cond(void)
rc = RC_ST0; rc = RC_ST0;
} }
#endif #endif
} else }
rc = RC_INT;
gv(rc); gv(rc);
save_regs(1); save_regs(1);
if (g) gv_dup();
gv_dup(); tt = gvtst(0, 0);
tt = gvtst(1, 0);
} else {
if (!g)
vpop();
tt = 0;
} }
ncw_prev = nocode_wanted;
if (1) { if (1) {
if (c == 0) if (c == 0)
nocode_wanted++; nocode_wanted++;
@ -5747,20 +5746,21 @@ static void expr_cond(void)
type1 = vtop->type; type1 = vtop->type;
sv = *vtop; /* save value to handle it later */ sv = *vtop; /* save value to handle it later */
vtop--; /* no vpop so that FP stack is not flushed */ vtop--; /* no vpop so that FP stack is not flushed */
skip(':');
u = 0; if (g) {
if (c < 0) u = tt;
} else if (c < 0) {
u = gjmp(0); u = gjmp(0);
gsym(tt); gsym(tt);
} else
u = 0;
if (c == 0) nocode_wanted = ncw_prev;
nocode_wanted--;
if (c == 1) if (c == 1)
nocode_wanted++; nocode_wanted++;
skip(':');
expr_cond(); expr_cond();
if (c == 1)
nocode_wanted--;
if ((vtop->type.t & VT_BTYPE) == VT_FUNC) if ((vtop->type.t & VT_BTYPE) == VT_FUNC)
mk_pointer(&vtop->type); mk_pointer(&vtop->type);
@ -5771,7 +5771,6 @@ static void expr_cond(void)
bt2 = t2 & VT_BTYPE; bt2 = t2 & VT_BTYPE;
type.ref = NULL; type.ref = NULL;
/* cast operands to correct type according to ISOC rules */ /* cast operands to correct type according to ISOC rules */
if (bt1 == VT_VOID || bt2 == VT_VOID) { if (bt1 == VT_VOID || bt2 == VT_VOID) {
type.t = VT_VOID; /* NOTE: as an extension, we accept void on only one side */ type.t = VT_VOID; /* NOTE: as an extension, we accept void on only one side */
@ -5902,15 +5901,17 @@ static void expr_cond(void)
gaddrof(); gaddrof();
} }
if (c < 0 || islv) { if (c < 0) {
r1 = gv(rc); r1 = gv(rc);
move_reg(r2, r1, type.t); move_reg(r2, r1, type.t);
vtop->r = r2; vtop->r = r2;
gsym(tt); gsym(tt);
if (islv)
indir();
} }
if (islv)
indir();
} }
nocode_wanted = ncw_prev;
} }
} }
@ -5982,25 +5983,8 @@ ST_FUNC int expr_const(void)
return c; return c;
} }
/* return the label token if current token is a label, otherwise /* ------------------------------------------------------------------------- */
return zero */ /* return from function */
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 == ':') {
return last_tok;
} else {
unget_tok(last_tok);
return 0;
}
}
#ifndef TCC_TARGET_ARM64 #ifndef TCC_TARGET_ARM64
static void gfunc_return(CType *func_type) static void gfunc_return(CType *func_type)
@ -6135,75 +6119,97 @@ static void gcase(struct case_t **base, int len, int *bsym)
} }
} }
/* call 'func' for each __attribute__((cleanup(func))) */
static void block_cleanup(Sym *lcleanup, int lncleanups)
{
int jmp = 0;
Sym *g, **pg;
for (pg = &pending_gotos; (g = *pg) && g->c > lncleanups;) {
if (g->prev_tok->r & LABEL_FORWARD) {
Sym *pcl = g->next;
if (!jmp)
jmp = gjmp(0);
gsym(pcl->jnext);
try_call_scope_cleanup(lcleanup);
pcl->jnext = gjmp(0);
if (!lncleanups)
goto remove_pending;
g->c = lncleanups;
pg = &g->prev;
} else {
remove_pending:
*pg = g->prev;
sym_free(g);
}
}
gsym(jmp);
try_call_scope_cleanup(lcleanup);
current_cleanups = lcleanup;
ncleanups = lncleanups;
}
static void check_func_return(void)
{
if ((func_vt.t & VT_BTYPE) == VT_VOID)
return;
if (!strcmp (funcname, "main")
&& (func_vt.t & VT_BTYPE) == VT_INT) {
/* main returns 0 by default */
vpushi(0);
gen_assign_cast(&func_vt);
gfunc_return(&func_vt);
} else {
tcc_warning("function might return no value: '%s'", funcname);
}
}
static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr) static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
{ {
int a, b, c, d, cond; int a, b, c, d, e, t;
Sym *s; Sym *s;
/* generate line number info */
if (tcc_state->do_debug)
tcc_debug_line(tcc_state);
if (is_expr) { if (is_expr) {
/* default return value is (void) */ /* default return value is (void) */
vpushi(0); vpushi(0);
vtop->type.t = VT_VOID; vtop->type.t = VT_VOID;
} }
if (tok == TOK_IF) { t = tok, next();
/* if test */
int saved_nocode_wanted = nocode_wanted; if (t == TOK_IF) {
next();
skip('('); skip('(');
gexpr(); gexpr();
skip(')'); skip(')');
cond = condition_3way(); a = gvtst(1, 0);
if (cond == 1)
a = 0, vpop();
else
a = gvtst(1, 0);
if (cond == 0)
nocode_wanted |= 0x20000000;
block(bsym, bcl, csym, ccl, 0); block(bsym, bcl, csym, ccl, 0);
if (cond != 1)
nocode_wanted = saved_nocode_wanted;
if (tok == TOK_ELSE) { if (tok == TOK_ELSE) {
next();
d = gjmp(0); d = gjmp(0);
gsym(a); gsym(a);
if (cond == 1) next();
nocode_wanted |= 0x20000000;
block(bsym, bcl, csym, ccl, 0); block(bsym, bcl, csym, ccl, 0);
gsym(d); /* patch else jmp */ gsym(d); /* patch else jmp */
if (cond != 0) } else {
nocode_wanted = saved_nocode_wanted;
} else
gsym(a); gsym(a);
} else if (tok == TOK_WHILE) { }
int saved_nocode_wanted;
nocode_wanted &= ~0x20000000; } else if (t == TOK_WHILE) {
next(); d = gind();
d = ind;
vla_sp_restore(); vla_sp_restore();
skip('('); skip('(');
gexpr(); gexpr();
skip(')'); skip(')');
a = gvtst(1, 0); a = gvtst(1, 0);
b = 0; b = 0;
++local_scope;
saved_nocode_wanted = nocode_wanted;
block(&a, current_cleanups, &b, current_cleanups, 0); block(&a, current_cleanups, &b, current_cleanups, 0);
nocode_wanted = saved_nocode_wanted;
--local_scope;
gjmp_addr(d); gjmp_addr(d);
gsym(a);
gsym_addr(b, d); gsym_addr(b, d);
} else if (tok == '{') { gsym(a);
} else if (t == '{') {
Sym *llabel, *lcleanup; Sym *llabel, *lcleanup;
int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope; int block_vla_sp_loc = vla_sp_loc, saved_vlas_in_scope = vlas_in_scope;
int lncleanups = ncleanups; int lncleanups = ncleanups;
next();
/* record local declaration stack position */ /* record local declaration stack position */
s = local_stack; s = local_stack;
llabel = local_label_stack; llabel = local_label_stack;
@ -6212,25 +6218,18 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
/* handle local labels declarations */ /* handle local labels declarations */
while (tok == TOK_LABEL) { while (tok == TOK_LABEL) {
next(); do {
for(;;) { next();
if (tok < TOK_UIDENT) if (tok < TOK_UIDENT)
expect("label identifier"); expect("label identifier");
label_push(&local_label_stack, tok, LABEL_DECLARED); label_push(&local_label_stack, tok, LABEL_DECLARED);
next(); next();
if (tok == ',') { } while (tok == ',');
next(); skip(';');
} else {
skip(';');
break;
}
}
} }
while (tok != '}') { while (tok != '}') {
if ((a = is_label())) decl(VT_LOCAL);
unget_tok(a);
else
decl(VT_LOCAL);
if (tok != '}') { if (tok != '}') {
if (is_expr) if (is_expr)
vpop(); vpop();
@ -6238,39 +6237,12 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
} }
} }
if (current_cleanups != lcleanup) { if (current_cleanups != lcleanup)
int jmp = 0; block_cleanup(lcleanup, lncleanups);
Sym *g, **pg;
for (pg = &pending_gotos; (g = *pg) && g->c > lncleanups;)
if (g->prev_tok->r & LABEL_FORWARD) {
Sym *pcl = g->next;
if (!jmp)
jmp = gjmp(0);
gsym(pcl->jnext);
try_call_scope_cleanup(lcleanup);
pcl->jnext = gjmp(0);
if (!lncleanups)
goto remove_pending;
g->c = lncleanups;
pg = &g->prev;
} else {
remove_pending:
*pg = g->prev;
sym_free(g);
}
gsym(jmp);
if (!nocode_wanted) {
try_call_scope_cleanup(lcleanup);
}
}
current_cleanups = lcleanup;
ncleanups = lncleanups;
/* pop locally defined labels */ /* pop locally defined labels */
label_pop(&local_label_stack, llabel, is_expr); label_pop(&local_label_stack, llabel, is_expr);
/* pop locally defined symbols */
--local_scope;
/* In the is_expr case (a statement expression is finished here), /* In the is_expr case (a statement expression is finished here),
vtop might refer to symbols on the local_stack. Either via the vtop might refer to symbols on the local_stack. Either via the
type or via vtop->sym. We can't pop those nor any that in turn type or via vtop->sym. We can't pop those nor any that in turn
@ -6278,6 +6250,8 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
any symbols in that case; some upper level call to block() will any symbols in that case; some upper level call to block() will
do that. We do have to remove such symbols from the lookup do that. We do have to remove such symbols from the lookup
tables, though. sym_pop will do that. */ tables, though. sym_pop will do that. */
/* pop locally defined symbols */
sym_pop(&local_stack, s, is_expr); sym_pop(&local_stack, s, is_expr);
/* Pop VLA frames and restore stack pointer if required */ /* Pop VLA frames and restore stack pointer if required */
@ -6287,52 +6261,49 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
} }
vlas_in_scope = saved_vlas_in_scope; vlas_in_scope = saved_vlas_in_scope;
if (0 == --local_scope && !nocode_wanted)
check_func_return();
next(); next();
} else if (tok == TOK_RETURN) {
next(); } else if (t == TOK_RETURN) {
if (tok != ';') { a = tok != ';';
gexpr(); b = (func_vt.t & VT_BTYPE) != VT_VOID;
gen_assign_cast(&func_vt); if (a)
try_call_scope_cleanup(NULL); gexpr(), gen_assign_cast(&func_vt);
if ((func_vt.t & VT_BTYPE) == VT_VOID) try_call_scope_cleanup(NULL);
vtop--; if (a && b)
else gfunc_return(&func_vt);
gfunc_return(&func_vt); else if (a)
} else { vtop--;
try_call_scope_cleanup(NULL); else if (b)
} tcc_warning("'return' with no value.");
skip(';'); skip(';');
/* jump unless last stmt in top-level block */ /* jump unless last stmt in top-level block */
if (tok != '}' || local_scope != 1) if (tok != '}' || local_scope != 1)
rsym = gjmp(rsym); rsym = gjmp(rsym);
nocode_wanted |= 0x20000000; CODE_OFF();
} else if (tok == TOK_BREAK) {
} else if (t == TOK_BREAK) {
/* compute jump */ /* compute jump */
if (!bsym) if (!bsym)
tcc_error("cannot break"); tcc_error("cannot break");
try_call_scope_cleanup(bcl); try_call_scope_cleanup(bcl);
*bsym = gjmp(*bsym); *bsym = gjmp(*bsym);
next();
skip(';'); skip(';');
nocode_wanted |= 0x20000000;
} else if (tok == TOK_CONTINUE) { } else if (t == TOK_CONTINUE) {
/* compute jump */ /* compute jump */
if (!csym) if (!csym)
tcc_error("cannot continue"); tcc_error("cannot continue");
try_call_scope_cleanup(ccl); try_call_scope_cleanup(ccl);
vla_sp_restore_root(); vla_sp_restore_root();
*csym = gjmp(*csym); *csym = gjmp(*csym);
next();
skip(';'); skip(';');
nocode_wanted |= 0x20000000;
} else if (tok == TOK_FOR) { } else if (t == TOK_FOR) {
int e;
int saved_nocode_wanted;
Sym *lcleanup = current_cleanups; Sym *lcleanup = current_cleanups;
int lncleanups = ncleanups; int lncleanups = ncleanups;
nocode_wanted &= ~0x20000000;
next();
skip('('); skip('(');
s = local_stack; s = local_stack;
++local_scope; ++local_scope;
@ -6345,11 +6316,9 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
} }
} }
skip(';'); skip(';');
d = ind; a = b = 0;
c = ind; c = d = gind();
vla_sp_restore(); vla_sp_restore();
a = 0;
b = 0;
if (tok != ';') { if (tok != ';') {
gexpr(); gexpr();
a = gvtst(1, 0); a = gvtst(1, 0);
@ -6357,55 +6326,43 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
skip(';'); skip(';');
if (tok != ')') { if (tok != ')') {
e = gjmp(0); e = gjmp(0);
c = ind; d = gind();
vla_sp_restore(); vla_sp_restore();
gexpr(); gexpr();
vpop(); vpop();
gjmp_addr(d); gjmp_addr(c);
gsym(e); gsym(e);
} }
skip(')'); skip(')');
saved_nocode_wanted = nocode_wanted;
block(&a, current_cleanups, &b, current_cleanups, 0); block(&a, current_cleanups, &b, current_cleanups, 0);
nocode_wanted = saved_nocode_wanted; gjmp_addr(d);
gjmp_addr(c); gsym_addr(b, d);
gsym(a); gsym(a);
gsym_addr(b, c);
--local_scope; --local_scope;
try_call_scope_cleanup(lcleanup); try_call_scope_cleanup(lcleanup);
ncleanups = lncleanups; ncleanups = lncleanups;
current_cleanups = lcleanup; current_cleanups = lcleanup;
sym_pop(&local_stack, s, 0); sym_pop(&local_stack, s, 0);
} else } else if (t == TOK_DO) {
if (tok == TOK_DO) { a = b = 0;
int saved_nocode_wanted; d = gind();
nocode_wanted &= ~0x20000000;
next();
a = 0;
b = 0;
d = ind;
vla_sp_restore(); vla_sp_restore();
saved_nocode_wanted = nocode_wanted;
block(&a, current_cleanups, &b, current_cleanups, 0); block(&a, current_cleanups, &b, current_cleanups, 0);
gsym(b);
skip(TOK_WHILE); skip(TOK_WHILE);
skip('('); skip('(');
gsym(b);
if (b)
nocode_wanted = saved_nocode_wanted;
gexpr(); gexpr();
skip(')');
skip(';');
c = gvtst(0, 0); c = gvtst(0, 0);
gsym_addr(c, d); gsym_addr(c, d);
nocode_wanted = saved_nocode_wanted;
skip(')');
gsym(a); gsym(a);
skip(';');
} else } else if (t == TOK_SWITCH) {
if (tok == TOK_SWITCH) {
struct switch_t *saved, sw; struct switch_t *saved, sw;
int saved_nocode_wanted = nocode_wanted;
SValue switchval; SValue switchval;
next();
skip('('); skip('(');
gexpr(); gexpr();
skip(')'); skip(')');
@ -6416,7 +6373,6 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
saved = cur_switch; saved = cur_switch;
cur_switch = &sw; cur_switch = &sw;
block(&a, current_cleanups, csym, ccl, 0); block(&a, current_cleanups, csym, ccl, 0);
nocode_wanted = saved_nocode_wanted;
a = gjmp(a); /* add implicit break */ a = gjmp(a); /* add implicit break */
/* case lookup */ /* case lookup */
gsym(b); gsym(b);
@ -6437,13 +6393,11 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
cur_switch = saved; cur_switch = saved;
/* break label */ /* break label */
gsym(a); gsym(a);
} else
if (tok == TOK_CASE) { } else if (t == TOK_CASE) {
struct case_t *cr = tcc_malloc(sizeof(struct case_t)); struct case_t *cr = tcc_malloc(sizeof(struct case_t));
if (!cur_switch) if (!cur_switch)
expect("switch"); expect("switch");
nocode_wanted &= ~0x20000000;
next();
cr->v1 = cr->v2 = expr_const64(); cr->v1 = cr->v2 = expr_const64();
if (gnu_ext && tok == TOK_DOTS) { if (gnu_ext && tok == TOK_DOTS) {
next(); next();
@ -6451,25 +6405,23 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
if (cr->v2 < cr->v1) if (cr->v2 < cr->v1)
tcc_warning("empty case range"); tcc_warning("empty case range");
} }
cr->sym = ind; cr->sym = gind();
dynarray_add(&cur_switch->p, &cur_switch->n, cr); dynarray_add(&cur_switch->p, &cur_switch->n, cr);
skip(':'); skip(':');
is_expr = 0; is_expr = 0;
goto block_after_label; goto block_after_label;
} else
if (tok == TOK_DEFAULT) { } else if (t == TOK_DEFAULT) {
next();
skip(':');
if (!cur_switch) if (!cur_switch)
expect("switch"); expect("switch");
if (cur_switch->def_sym) if (cur_switch->def_sym)
tcc_error("too many 'default'"); tcc_error("too many 'default'");
cur_switch->def_sym = ind; cur_switch->def_sym = gind();
skip(':');
is_expr = 0; is_expr = 0;
goto block_after_label; goto block_after_label;
} else
if (tok == TOK_GOTO) { } else if (t == TOK_GOTO) {
next();
if (tok == '*' && gnu_ext) { if (tok == '*' && gnu_ext) {
/* computed goto */ /* computed goto */
next(); next();
@ -6500,18 +6452,20 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
gjmp_addr(s->jnext); gjmp_addr(s->jnext);
} }
next(); next();
} else {
} else {
expect("label identifier"); expect("label identifier");
} }
skip(';'); skip(';');
} else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) {
} else if (t == TOK_ASM1 || t == TOK_ASM2 || t == TOK_ASM3) {
asm_instr(); asm_instr();
} else { } else {
b = is_label(); if (tok == ':' && t >= TOK_UIDENT) {
if (b) {
/* label case */ /* label case */
next(); next();
s = label_find(b); s = label_find(t);
if (s) { if (s) {
if (s->r == LABEL_DEFINED) if (s->r == LABEL_DEFINED)
tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL)); tcc_error("duplicate label '%s'", get_tok_str(s->v, NULL));
@ -6524,14 +6478,14 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
} else } else
gsym(s->jnext); gsym(s->jnext);
} else { } else {
s = label_push(&global_label_stack, b, LABEL_DEFINED); s = label_push(&global_label_stack, t, LABEL_DEFINED);
} }
s->jnext = ind; s->jnext = gind();
s->cleanupstate = current_cleanups; s->cleanupstate = current_cleanups;
block_after_label:
vla_sp_restore(); vla_sp_restore();
/* we accept this, but it is a mistake */ /* we accept this, but it is a mistake */
block_after_label:
nocode_wanted &= ~0x20000000;
if (tok == '}') { if (tok == '}') {
tcc_warning("deprecated use of label at end of compound statement"); tcc_warning("deprecated use of label at end of compound statement");
} else { } else {
@ -6539,9 +6493,11 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
vpop(); vpop();
block(bsym, bcl, csym, ccl, is_expr); block(bsym, bcl, csym, ccl, is_expr);
} }
} else { } else {
/* expression case */ /* expression case */
if (tok != ';') { if (t != ';') {
unget_tok(t);
if (is_expr) { if (is_expr) {
vpop(); vpop();
gexpr(); gexpr();
@ -6549,8 +6505,8 @@ static void block(int *bsym, Sym *bcl, int *csym, Sym *ccl, int is_expr)
gexpr(); gexpr();
vpop(); vpop();
} }
skip(';');
} }
skip(';');
} }
} }
} }
@ -6661,10 +6617,17 @@ static int decl_designator(CType *type, Section *sec, unsigned long c,
elem_size = 0; elem_size = 0;
nb_elems = 1; nb_elems = 1;
if (flags & DIF_HAVE_ELEM) if (flags & DIF_HAVE_ELEM)
goto no_designator; goto no_designator;
if (gnu_ext && (l = is_label()) != 0)
goto struct_field; if (gnu_ext && tok >= TOK_UIDENT) {
l = tok, next();
if (tok == ':')
goto struct_field;
unget_tok(l);
}
/* NOTE: we only support ranges for last designator */ /* NOTE: we only support ranges for last designator */
while (nb_elems == 1 && (tok == '[' || tok == '.')) { while (nb_elems == 1 && (tok == '[' || tok == '.')) {
if (tok == '[') { if (tok == '[') {
@ -7413,19 +7376,10 @@ static void gen_function(Sym *sym)
gfunc_prolog(&sym->type); gfunc_prolog(&sym->type);
reset_local_scope(); reset_local_scope();
rsym = 0; rsym = 0;
clear_temp_local_var_list(); clear_temp_local_var_list();
block(NULL, NULL, NULL, NULL, 0); block(NULL, NULL, NULL, NULL, 0);
if (!(nocode_wanted & 0x20000000)
&& ((func_vt.t & VT_BTYPE) == VT_INT)
&& !strcmp (funcname, "main"))
{
nocode_wanted = 0;
vpushi(0);
gen_assign_cast(&func_vt);
gfunc_return(&func_vt);
}
nocode_wanted = 0;
gsym(rsym); gsym(rsym);
nocode_wanted = 0;
gfunc_epilog(); gfunc_epilog();
cur_text_section->data_offset = ind; cur_text_section->data_offset = ind;
label_pop(&global_label_stack, NULL, 0); label_pop(&global_label_stack, NULL, 0);
@ -7746,3 +7700,6 @@ static void decl(int l)
} }
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
#undef gjmp_addr
#undef gjmp
/* ------------------------------------------------------------------------- */

View File

@ -3527,6 +3527,9 @@ no_subst:
/* return next token with macro substitution */ /* return next token with macro substitution */
ST_FUNC void next(void) ST_FUNC void next(void)
{ {
/* generate line number info */
if (tcc_state->do_debug)
tcc_debug_line(tcc_state);
redo: redo:
if (parse_flags & PARSE_FLAG_SPACES) if (parse_flags & PARSE_FLAG_SPACES)
next_nomacro_spc(); next_nomacro_spc();

View File

@ -209,6 +209,7 @@ int test16()
printf("alloca: %s\n", strcpy(alloca(strlen(demo)+1),demo) ); printf("alloca: %s\n", strcpy(alloca(strlen(demo)+1),demo) );
fprintf(stderr, "%s end\n", __FUNCTION__); fprintf(stderr, "%s end\n", __FUNCTION__);
return 0;
} }
/* error */ /* error */
@ -227,6 +228,7 @@ int test17()
printf("alloca: %s\n", strcpy(alloca(strlen(demo)),demo) ); printf("alloca: %s\n", strcpy(alloca(strlen(demo)),demo) );
fprintf(stderr, "%s end\n", __FUNCTION__); fprintf(stderr, "%s end\n", __FUNCTION__);
return 0;
} }
int (*table_test[])(void) = { int (*table_test[])(void) = {

View File

@ -179,7 +179,7 @@ static int onetwothree = 123;
#ifdef __TINYC__ #ifdef __TINYC__
/* We try to handle this syntax. Make at least sure it doesn't segfault. */ /* We try to handle this syntax. Make at least sure it doesn't segfault. */
char invalid_function_def()[] {} char invalid_function_def()[] {return 0;}
#endif #endif
#define __INT64_C(c) c ## LL #define __INT64_C(c) c ## LL
@ -572,6 +572,7 @@ void goto_test()
printf("goto:\n"); printf("goto:\n");
i = 0; i = 0;
/* This needs to parse as label, not as start of decl. */ /* This needs to parse as label, not as start of decl. */
typedef_and_label x;
typedef_and_label: typedef_and_label:
s_loop: s_loop:
if (i >= 10) if (i >= 10)
@ -3767,6 +3768,7 @@ int fcompare (double a, double b, int code)
case 4: return a > b; case 4: return a > b;
case 5: return a <= b; case 5: return a <= b;
} }
return 0;
} }
void math_cmp_test(void) void math_cmp_test(void)

View File

@ -51,6 +51,7 @@ _Bool chk(unsigned long addr, unsigned long limit, unsigned long size)
only with certain internal checking added that's not committed). */ only with certain internal checking added that's not committed). */
if (0) if (0)
ret = 0 != (!!(addr > limit - size)); ret = 0 != (!!(addr > limit - size));
return 0;
} }
int main() int main()

View File

@ -45,6 +45,7 @@ int convert_like_real (tree convs)
break; break;
}; };
printf("unsigned enum bit-fields broken\n"); printf("unsigned enum bit-fields broken\n");
return 0;
} }
int main() int main()

View File

@ -41,7 +41,7 @@
#define __MSVCRT__ 1 #define __MSVCRT__ 1
#undef _MSVCRT_ #undef _MSVCRT_
#define __MINGW_IMPORT extern __declspec(dllimport) #define __MINGW_IMPORT extern __declspec(dllimport)
#define __MINGW_ATTRIB_NORETURN #define __MINGW_ATTRIB_NORETURN __declspec(noreturn)
#define __MINGW_ATTRIB_CONST #define __MINGW_ATTRIB_CONST
#define __MINGW_ATTRIB_DEPRECATED #define __MINGW_ATTRIB_DEPRECATED
#define __MINGW_ATTRIB_MALLOC #define __MINGW_ATTRIB_MALLOC
@ -138,7 +138,7 @@ typedef struct localeinfo_struct _locale_tstruct,*_locale_t;
/* for winapi */ /* for winapi */
#define _ANONYMOUS_UNION #define _ANONYMOUS_UNION
#define _ANONYMOUS_STRUCT #define _ANONYMOUS_STRUCT
#define DECLSPEC_NORETURN #define DECLSPEC_NORETURN __declspec(noreturn)
#define DECLARE_STDCALL_P(type) __stdcall type #define DECLARE_STDCALL_P(type) __stdcall type
#define NOSERVICE 1 #define NOSERVICE 1
#define NOMCX 1 #define NOMCX 1

View File

@ -211,12 +211,6 @@ ST_FUNC void gsym_addr(int t, int a)
} }
} }
void gsym(int t)
{
gsym_addr(t, ind);
}
static int is64_type(int t) static int is64_type(int t)
{ {
return ((t & VT_BTYPE) == VT_PTR || return ((t & VT_BTYPE) == VT_PTR ||