From 1bbb9971d0b7aebb129b32f760151e11f451f8a2 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Sat, 30 Nov 2024 21:59:16 +0900 Subject: [PATCH 1/5] Add command line option for warnings --- src/cc/cc1.c | 51 +++++++++++++++++++----------------- src/cc/frontend/fe_misc.c | 37 ++++++++++++++++++++++++++- src/cc/frontend/fe_misc.h | 9 +++++++ src/wcc/wcc.c | 54 ++++++++++++++++++++------------------- src/xcc/main.c | 32 +++++++++++++++-------- 5 files changed, 121 insertions(+), 62 deletions(-) diff --git a/src/cc/cc1.c b/src/cc/cc1.c index 67d5ed0be..4efd03833 100644 --- a/src/cc/cc1.c +++ b/src/cc/cc1.c @@ -47,30 +47,10 @@ static void compile1(FILE *ifp, const char *filename, Vector *decls) { parse(decls); } -static bool parse_fopt(const char *optarg, bool value) { - static const struct { - const char *flag_name; - off_t flag_offset; - } kFlagTable[] = { - {"common", offsetof(CcFlags, common)}, - }; - - for (size_t i = 0; i < ARRAY_SIZE(kFlagTable); ++i) { - if (strcmp(optarg, kFlagTable[i].flag_name) == 0) { - size_t len = strlen(kFlagTable[i].flag_name); - if (optarg[len] != '\0') - continue; - bool *p = (bool*)((char*)&cc_flags + kFlagTable[i].flag_offset); - *p = value; - return true; - } - } - return false; -} - int main(int argc, char *argv[]) { enum { OPT_FNO = 128, + OPT_WNO, OPT_SSA, }; @@ -80,8 +60,9 @@ int main(int argc, char *argv[]) { {"O", optional_argument}, // Optimization level // Sub command - {"fno-", required_argument, OPT_FNO}, - {"f", required_argument}, + {"fno-", optional_argument, OPT_FNO}, + {"f", optional_argument}, + {"Wno-", optional_argument, OPT_WNO}, {"W", required_argument}, // Feature flag. @@ -120,6 +101,10 @@ int main(int argc, char *argv[]) { case 'f': case OPT_FNO: + if (optarg == NULL) { + fprintf(stderr, "Warning: missing argument for -f\n"); + break; + } if (!parse_fopt(optarg, opt == 'f')) { // Silently ignored. // fprintf(stderr, "Warning: unknown option for -f: %s\n", optarg); @@ -127,9 +112,27 @@ int main(int argc, char *argv[]) { break; case 'W': + if (optarg != NULL) { + if (strcmp(optarg, "error") == 0) { + cc_flags.warn_as_error = true; + break; + } + if (strcmp(optarg, "all") == 0) { + // Assume all members are bool. + for (bool *p = (bool*)&cc_flags.warn; p < (bool*)(&cc_flags.warn + 1); ++p) + *p = true; + break; + } + } + // Fallthrough + case OPT_WNO: + if (optarg == NULL) { + fprintf(stderr, "Warning: missing argument for -W\n"); + break; + } if (strcmp(optarg, "error") == 0) { cc_flags.warn_as_error = true; - } else { + } else if (!parse_wopt(optarg, opt == 'W')) { // Silently ignored. // fprintf(stderr, "Warning: unknown option for -W: %s\n", optarg); } diff --git a/src/cc/frontend/fe_misc.c b/src/cc/frontend/fe_misc.c index 9f870c57d..ce7c5143e 100644 --- a/src/cc/frontend/fe_misc.c +++ b/src/cc/frontend/fe_misc.c @@ -4,6 +4,7 @@ #include #include #include // exit +#include #include "ast.h" #include "initializer.h" @@ -17,6 +18,7 @@ Function *curfunc; Scope *curscope; +LoopScope loop_scope; int compile_warning_count; int compile_error_count; @@ -27,7 +29,40 @@ CcFlags cc_flags = { .optimize_level = 0, }; -LoopScope loop_scope; +typedef struct { + const char *flag_name; + off_t flag_offset; +} FlagTable; + +static bool parse_flag_table(const char *optarg, bool value, const FlagTable *table, size_t count) { + for (size_t i = 0; i < count; ++i) { + const FlagTable *p = &table[i]; + if (strcmp(optarg, p->flag_name) == 0) { + size_t len = strlen(p->flag_name); + if (optarg[len] != '\0') + continue; + bool *b = (bool*)((char*)&cc_flags + p->flag_offset); + *b = value; + return true; + } + } + return false; +} + +bool parse_fopt(const char *optarg, bool value) { + static const FlagTable kFlagTable[] = { + {"common", offsetof(CcFlags, common)}, + }; + return parse_flag_table(optarg, value, kFlagTable, ARRAY_SIZE(kFlagTable)); +} + +bool parse_wopt(const char *optarg, bool value) { + static const FlagTable kFlagTable[] = { + {"unused-variable", offsetof(CcFlags, warn.unused_variable)}, + {"unused-function", offsetof(CcFlags, warn.unused_function)}, + }; + return parse_flag_table(optarg, value, kFlagTable, ARRAY_SIZE(kFlagTable)); +} void parse_error(enum ParseErrorLevel level, const Token *token, const char *fmt, ...) { va_list ap; diff --git a/src/cc/frontend/fe_misc.h b/src/cc/frontend/fe_misc.h index 3e089e24a..0016fdc11 100644 --- a/src/cc/frontend/fe_misc.h +++ b/src/cc/frontend/fe_misc.h @@ -26,14 +26,23 @@ enum ParseErrorLevel { PE_FATAL, }; +typedef struct { + bool unused_variable; + bool unused_function; +} WarningFlags; + typedef struct { bool warn_as_error; // Treat warnings as errors bool common; int optimize_level; + WarningFlags warn; } CcFlags; extern CcFlags cc_flags; +bool parse_fopt(const char *optarg, bool value); +bool parse_wopt(const char *optarg, bool value); + typedef struct { Stmt *swtch; Stmt *break_; diff --git a/src/wcc/wcc.c b/src/wcc/wcc.c index a2135f545..21eab1293 100644 --- a/src/wcc/wcc.c +++ b/src/wcc/wcc.c @@ -254,27 +254,6 @@ int compile_csource(const char *src, const char *ofn, Vector *obj_files, Options return 0; } -static bool parse_fopt(const char *optarg, bool value) { - static const struct { - const char *flag_name; - off_t flag_offset; - } kFlagTable[] = { - {"common", offsetof(CcFlags, common)}, - }; - - for (size_t i = 0; i < ARRAY_SIZE(kFlagTable); ++i) { - if (strcmp(optarg, kFlagTable[i].flag_name) == 0) { - size_t len = strlen(kFlagTable[i].flag_name); - if (optarg[len] != '\0') - continue; - bool *p = (bool*)((char*)&cc_flags + kFlagTable[i].flag_offset); - *p = value; - return true; - } - } - return false; -} - static void parse_options(int argc, char *argv[], Options *opts) { enum { OPT_HELP = 128, @@ -290,8 +269,8 @@ static void parse_options(int argc, char *argv[], Options *opts) { OPT_ISYSTEM, OPT_IDIRAFTER, OPT_FNO, + OPT_WNO, - OPT_WARNING, OPT_OPTIMIZE, OPT_DEBUGINFO, OPT_ANSI, @@ -324,9 +303,10 @@ static void parse_options(int argc, char *argv[], Options *opts) { {"dumpversion", no_argument, OPT_DUMP_VERSION}, // Sub command - {"fno-", required_argument, OPT_FNO}, - {"f", required_argument}, - {"W", required_argument}, + {"fno-", optional_argument, OPT_FNO}, + {"f", optional_argument}, + {"Wno-", optional_argument, OPT_WNO}, + {"W", optional_argument}, // Suppress warnings {"O", required_argument, OPT_OPTIMIZE}, @@ -474,6 +454,10 @@ static void parse_options(int argc, char *argv[], Options *opts) { case 'f': case OPT_FNO: + if (optarg == NULL) { + fprintf(stderr, "Warning: missing argument for -f\n"); + break; + } if (!parse_fopt(optarg, opt == 'f')) { // Silently ignored. // fprintf(stderr, "Warning: unknown option for -f: %s\n", optarg); @@ -481,9 +465,27 @@ static void parse_options(int argc, char *argv[], Options *opts) { break; case 'W': + if (optarg != NULL) { + if (strcmp(optarg, "error") == 0) { + cc_flags.warn_as_error = true; + break; + } + if (strcmp(optarg, "all") == 0) { + // Assume all members are bool. + for (bool *p = (bool*)&cc_flags.warn; p < (bool*)(&cc_flags.warn + 1); ++p) + *p = true; + break; + } + } + // Fallthrough + case OPT_WNO: + if (optarg == NULL) { + fprintf(stderr, "Warning: missing argument for -W\n"); + break; + } if (strcmp(optarg, "error") == 0) { cc_flags.warn_as_error = true; - } else { + } else if (!parse_wopt(optarg, opt == 'W')) { // Silently ignored. // fprintf(stderr, "Warning: unknown option for -W: %s\n", optarg); } diff --git a/src/xcc/main.c b/src/xcc/main.c index 7c8017151..a12b130f3 100644 --- a/src/xcc/main.c +++ b/src/xcc/main.c @@ -331,8 +331,8 @@ static void parse_options(int argc, char *argv[], Options *opts) { {"dumpversion", no_argument, OPT_DUMP_VERSION}, // Sub command - {"f", required_argument}, - {"W", required_argument}, + {"f", optional_argument}, + {"W", optional_argument}, // Suppress warnings {"g", optional_argument}, // Debug info @@ -472,6 +472,10 @@ static void parse_options(int argc, char *argv[], Options *opts) { break; case 'f': + if (optarg == NULL) { + fprintf(stderr, "Warning: missing argument for -f\n"); + break; + } if (strncmp(optarg, "use-ld", 6) == 0) { if (optarg[6] == '=') { opts->ld_cmd->data[0] = &optarg[7]; @@ -482,21 +486,27 @@ static void parse_options(int argc, char *argv[], Options *opts) { } opts->use_ld = true; } else { - const char *opt = argv[optind - 1]; // TODO: + const char *opt = argv[optind - 1]; vec_push(opts->cc1_cmd, opt); vec_push(opts->linker_options, opt); } break; case 'W': - if (strncmp(argv[optind - 1], "-Wl,", 4) == 0) { - if (opts->use_ld) - vec_push(opts->linker_options, argv[optind - 1]); - else - vec_push(opts->ld_cmd, argv[optind - 1] + 4); - } else { - vec_push(opts->cc1_cmd, "-W"); - vec_push(opts->cc1_cmd, optarg); + { + if (optarg == NULL) { + fprintf(stderr, "Warning: missing argument for -f\n"); + break; + } + const char *opt = argv[optind - 1]; + if (strncmp(opt, "-Wl,", 4) == 0) { + if (opts->use_ld) + vec_push(opts->linker_options, opt); + else + vec_push(opts->ld_cmd, opt + 4); + } else { + vec_push(opts->cc1_cmd, opt); + } } break; From fd9bf5b318fe24b7024cc8ad66d5607d19ebdc62 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Tue, 24 Oct 2023 13:08:30 +0900 Subject: [PATCH 2/5] Check unused variables --- libsrc/_wasm/unistd/mkdir.c | 2 +- libsrc/stdlib/getrandom.c | 1 + src/cc/frontend/fe_misc.c | 47 ++++++++++++++++++++++++++++++- src/cc/frontend/fe_misc.h | 2 ++ src/cc/frontend/initializer.c | 2 +- src/cc/frontend/parser.c | 28 ++++++++++++------ src/cc/frontend/parser_expr.c | 53 +++++++++++++++++++++++++++++++++-- src/cc/frontend/var.h | 1 + src/wcc/gen_wasm.c | 5 ++++ tests/fvaltest.c | 1 + tests/test.sh | 4 +-- tests/valtest.c | 14 ++++----- 12 files changed, 137 insertions(+), 23 deletions(-) diff --git a/libsrc/_wasm/unistd/mkdir.c b/libsrc/_wasm/unistd/mkdir.c index 83cd9d13c..eff878a3f 100644 --- a/libsrc/_wasm/unistd/mkdir.c +++ b/libsrc/_wasm/unistd/mkdir.c @@ -5,6 +5,7 @@ extern int __max_preopen_fd; int mkdir(const char *fn, mode_t mode) { + (void)mode; size_t fnlen = strlen(fn); for (int base_fd = 3; base_fd < __max_preopen_fd; ++base_fd) { Prestat prestat; @@ -29,7 +30,6 @@ int mkdir(const char *fn, mode_t mode) { fnlen2 = fnlen - 1; } - Filestat fs; uint32_t result = path_create_directory(base_fd, fn2, fnlen2); if (result == 0) return 0; diff --git a/libsrc/stdlib/getrandom.c b/libsrc/stdlib/getrandom.c index 32dbf30c0..f22cf139f 100644 --- a/libsrc/stdlib/getrandom.c +++ b/libsrc/stdlib/getrandom.c @@ -4,6 +4,7 @@ #include "../_wasm/wasi.h" ssize_t getrandom(void *buf, size_t buflen, unsigned int flags) { + (void)flags; random_get(buf, buflen); return buflen; } diff --git a/src/cc/frontend/fe_misc.c b/src/cc/frontend/fe_misc.c index ce7c5143e..d82000380 100644 --- a/src/cc/frontend/fe_misc.c +++ b/src/cc/frontend/fe_misc.c @@ -174,7 +174,7 @@ Expr *alloc_tmp_var(Scope *scope, Type *type) { const Token *ident = alloc_dummy_ident(); // No need to use `add_var_to_scope`, because `name` must be unique. const Name *name = ident->ident; - scope_add(scope, name, type, 0); + scope_add(scope, name, type, VS_USED); return new_expr_variable(name, type, ident, scope); } @@ -445,6 +445,25 @@ static bool cast_numbers(Expr **pLhs, Expr **pRhs, bool make_int) { return true; } +void mark_var_used(Expr *expr) { + switch (expr->kind) { + case EX_VAR: + { + VarInfo *varinfo = scope_find(expr->var.scope, expr->var.name, NULL); + assert(varinfo != NULL); + varinfo->storage |= VS_USED; + } + break; + case EX_COMPLIT: + mark_var_used(expr->complit.var); + break; + case EX_ASSIGN: + mark_var_used(expr->bop.lhs); + break; + default: break; + } +} + void check_lval(const Token *tok, Expr *expr, const char *error) { switch (expr->kind) { case EX_VAR: @@ -1556,6 +1575,32 @@ static void check_unreachability(Stmt *stmt) { parse_error(PE_WARNING, stmt->token, "unreachable"); } +void check_unused_variables(Function *func, const Token *tok) { + assert(func->body_block != NULL); + assert(func->body_block->kind == ST_BLOCK); + Vector *stmts = func->body_block->block.stmts; + if (stmts->len > 0) { + // If __asm is used, variables might be used implicitly, so skip checking. + for (int i = 0; i < stmts->len; ++i) { + Stmt *stmt = stmts->data[i]; + if (stmt->kind == ST_ASM) + return; + } + } + + for (int i = 0; i < func->scopes->len; ++i) { + Scope *scope = func->scopes->data[i]; + if (scope->vars == NULL) + continue; + for (int j = 0; j < scope->vars->len; ++j) { + VarInfo *varinfo = scope->vars->data[j]; + if (!(varinfo->storage & (VS_USED | VS_ENUM_MEMBER | VS_EXTERN)) && varinfo->name != NULL) { + parse_error(PE_WARNING, tok, "Unused variable `%.*s'", NAMES(varinfo->name)); + } + } + } +} + static void check_reachability_stmt(Stmt *stmt) { if (stmt == NULL) return; diff --git a/src/cc/frontend/fe_misc.h b/src/cc/frontend/fe_misc.h index 0016fdc11..52a05d30e 100644 --- a/src/cc/frontend/fe_misc.h +++ b/src/cc/frontend/fe_misc.h @@ -89,6 +89,7 @@ bool check_cast(const Type *dst, const Type *src, bool zero, bool is_explicit, c Expr *make_cast(Type *type, const Token *token, Expr *sub, bool is_explicit); const MemberInfo *search_from_anonymous(const Type *type, const Name *name, const Token *ident, Vector *stack); +void mark_var_used(Expr *expr); void check_lval(const Token *tok, Expr *expr, const char *error); Expr *reduce_refer(Expr *expr); Expr *make_refer(const Token *tok, Expr *expr); @@ -115,6 +116,7 @@ Type *choose_ternary_result_type(Expr *tval, Expr *fval); Expr *transform_assign_with(const Token *tok, Expr *lhs, Expr *rhs); Expr *simplify_funcall(Expr *funcall); +void check_unused_variables(Function *func, const Token *tok); void check_func_reachability(Function *func); bool check_funcend_return(Stmt *stmt); diff --git a/src/cc/frontend/initializer.c b/src/cc/frontend/initializer.c index 46e1364b2..24d0d3330 100644 --- a/src/cc/frontend/initializer.c +++ b/src/cc/frontend/initializer.c @@ -78,7 +78,7 @@ static VarInfo *str_to_char_array(Scope *scope, Type *type, Initializer *init) { assert(type->kind == TY_ARRAY && is_char_type(type->pa.ptrof, init->single->str.kind)); type = qualified_type(type, TQ_FORSTRLITERAL); const Token *ident = alloc_dummy_ident(); - VarInfo *varinfo = add_var_to_scope(scope, ident, type, VS_STATIC); + VarInfo *varinfo = add_var_to_scope(scope, ident, type, VS_STATIC | VS_USED); if (is_global_scope(scope)) varinfo->global.init = init; else diff --git a/src/cc/frontend/parser.c b/src/cc/frontend/parser.c index 1c1e6447b..5aa7965f4 100644 --- a/src/cc/frontend/parser.c +++ b/src/cc/frontend/parser.c @@ -134,6 +134,7 @@ Initializer *parse_initializer(void) { result->multi = multi; } else { Expr *value = parse_assign(); + mark_var_used(value); result = new_initializer(IK_SINGLE, value->token); result->single = value; } @@ -302,19 +303,21 @@ static bool parse_vardecl(Vector *stmts) { static Stmt *parse_if(const Token *tok) { consume(TK_LPAR, "`(' expected"); - Expr *cond = make_cond(parse_expr()); + Expr *cond = parse_expr(); + mark_var_used(cond); consume(TK_RPAR, "`)' expected"); Stmt *tblock = parse_stmt(); Stmt *fblock = NULL; if (match(TK_ELSE)) { fblock = parse_stmt(); } - return new_stmt_if(tok, cond, tblock, fblock); + return new_stmt_if(tok, make_cond(cond), tblock, fblock); } static Stmt *parse_switch(const Token *tok) { consume(TK_LPAR, "`(' expected"); Expr *value = parse_expr(); + mark_var_used(value); not_void(value->type, value->token); consume(TK_RPAR, "`)' expected"); @@ -383,6 +386,7 @@ static Stmt *parse_case(const Token *tok) { static Stmt *parse_while(const Token *tok) { consume(TK_LPAR, "`(' expected"); Expr *cond = make_cond(parse_expr()); + mark_var_used(cond); consume(TK_RPAR, "`)' expected"); Stmt *stmt = new_stmt_while(tok, cond, NULL); @@ -407,7 +411,9 @@ static Stmt *parse_do_while(const Token *tok) { consume(TK_WHILE, "`while' expected"); consume(TK_LPAR, "`(' expected"); - stmt->while_.cond = make_cond(parse_expr()); + Expr *cond = parse_expr(); + mark_var_used(cond); + stmt->while_.cond = make_cond(cond); consume(TK_RPAR, "`)' expected"); consume(TK_SEMICOL, "`;' expected"); return stmt; @@ -437,7 +443,9 @@ static Stmt *parse_for(const Token *tok) { Expr *cond = NULL; Expr *post = NULL; if (!match(TK_SEMICOL)) { - cond = make_cond(parse_expr()); + Expr *e = parse_expr(); + mark_var_used(e); + cond = make_cond(e); consume(TK_SEMICOL, "`;' expected"); } if (!match(TK_RPAR)) { @@ -513,6 +521,7 @@ static Stmt *parse_return(const Token *tok) { if (!match(TK_SEMICOL)) { val = parse_expr(); consume(TK_SEMICOL, "`;' expected"); + mark_var_used(val); val = str_to_char_array_var(curscope, val); } @@ -850,6 +859,9 @@ static Declaration *parse_defun(Type *functype, int storage, Token *ident, const check_goto_labels(func); check_func_reachability(func); + if (cc_flags.warn.unused_variable) + check_unused_variables(func, tok); + Declaration *decl = new_decl_defun(func); varinfo->global.funcdecl = decl; return decl; @@ -1001,7 +1013,7 @@ static Function *generate_dtor_caller_func(Vector *dtors) { const Token *functok = alloc_dummy_ident(); Table *attributes = NULL; - Function *func = define_func(functype, functok, top_vars, VS_STATIC, attributes); + Function *func = define_func(functype, functok, top_vars, VS_STATIC | VS_USED, attributes); func->flag |= FUNCF_HAS_FUNCALL; assert(curfunc == NULL); @@ -1040,7 +1052,7 @@ static Function *generate_dtor_register_func(Function *dtor_caller_func) { // Declare: extern void *__dso_handle; const Name *dso_handle_name = alloc_name("__dso_handle", NULL, false); - scope_add(global_scope, dso_handle_name, &tyVoidPtr, VS_EXTERN); + scope_add(global_scope, dso_handle_name, &tyVoidPtr, VS_EXTERN | VS_USED); // Declare: extern int __cxa_atexit(void (*)(void*), void*, void*); const Name *cxa_atexit_name = alloc_name("__cxa_atexit", NULL, false); @@ -1057,7 +1069,7 @@ static Function *generate_dtor_register_func(Function *dtor_caller_func) { var_add(param_vars, alloc_name("z", NULL, false), &tyVoidPtr, VS_PARAM); cxa_atexit_functype = new_func_type(&tyInt, cxa_atexit_param_types, false); - define_func(cxa_atexit_functype, alloc_ident(cxa_atexit_name, NULL, cxa_atexit_name->chars, NULL), param_vars, VS_EXTERN, NULL); + define_func(cxa_atexit_functype, alloc_ident(cxa_atexit_name, NULL, cxa_atexit_name->chars, NULL), param_vars, VS_EXTERN | VS_USED, NULL); } Vector *param_types = new_vector(); @@ -1068,7 +1080,7 @@ static Function *generate_dtor_register_func(Function *dtor_caller_func) { Table *attributes = alloc_table(); assert(attributes != NULL); table_put(attributes, alloc_name("constructor", NULL, false), NULL); - Function *func = define_func(functype, functok, top_vars, VS_STATIC, attributes); + Function *func = define_func(functype, functok, top_vars, VS_STATIC | VS_USED, attributes); func->flag |= FUNCF_HAS_FUNCALL; assert(curfunc == NULL); diff --git a/src/cc/frontend/parser_expr.c b/src/cc/frontend/parser_expr.c index edac04c43..0bafa0ad2 100644 --- a/src/cc/frontend/parser_expr.c +++ b/src/cc/frontend/parser_expr.c @@ -51,6 +51,10 @@ static Expr *parse_funcall(Expr *func) { assert(curfunc != NULL); curfunc->flag |= FUNCF_HAS_FUNCALL; + mark_var_used(func); + for (int i = 0; i < args->len; ++i) + mark_var_used(args->data[i]); + check_funcall_args(func, args, curscope); Type *functype = get_callee_type(func->type); if (functype == NULL) { @@ -76,6 +80,8 @@ static Expr *parse_funcall(Expr *func) { static Expr *parse_array_index(const Token *token, Expr *expr) { Expr *index = parse_expr(); consume(TK_RBRACKET, "`]' expected"); + mark_var_used(expr); + mark_var_used(index); expr = str_to_char_array_var(curscope, expr); index = str_to_char_array_var(curscope, index); if (!ptr_or_array(expr->type)) { @@ -97,6 +103,7 @@ static Expr *parse_array_index(const Token *token, Expr *expr) { static Expr *parse_member_access(Expr *target, Token *acctok) { Token *ident = consume(TK_IDENT, "member name expected"); + mark_var_used(target); // Find member's type from struct info. Type *type = target->type; @@ -582,6 +589,7 @@ static ssize_t parse_array_size(Expr **pvla) { default: if (is_fixnum(expr->type->kind)) { *pvla = expr; + mark_var_used(expr); length = LEN_VLA; } else { parse_error(PE_NOFATAL, expr->token, kConstIntExpected); @@ -918,7 +926,7 @@ static Expr *parse_prim(void) { parse_error(PE_NOFATAL, ident, "`%.*s' undeclared", NAMES(ident->ident)); type = &tyInt; scope = curscope; - add_var_to_scope(scope, ident, type, 0); + add_var_to_scope(scope, ident, type, VS_USED); } return new_expr_variable(name, type, ident, scope); } @@ -934,9 +942,11 @@ static Expr *parse_postfix_cont(Expr *expr) { expr = parse_member_access(expr, tok); else if ((tok = match(TK_INC)) != NULL) { not_const(expr->type, tok); + mark_var_used(expr); expr = incdec_of(EX_POSTINC, expr, tok); } else if ((tok = match(TK_DEC)) != NULL) { not_const(expr->type, tok); + mark_var_used(expr); expr = incdec_of(EX_POSTDEC, expr, tok); } else return expr; @@ -962,6 +972,7 @@ static Expr *parse_sizeof(const Token *token) { } else { unget_token((Token*)tok); Expr *expr = parse_unary(); + mark_var_used(expr); not_bitfield_member(expr); type = expr->type; tok = expr->token; @@ -1009,6 +1020,7 @@ static Expr *parse_cast_expr(void) { } Expr *sub = parse_cast_expr(); + mark_var_used(sub); sub = str_to_char_array_var(curscope, sub); check_cast(type, sub->type, is_zero(sub), true, token); @@ -1028,6 +1040,7 @@ static Expr *parse_unary(void) { Token *tok; if ((tok = match(TK_ADD)) != NULL) { Expr *expr = parse_cast_expr(); + mark_var_used(expr); if (!is_number(expr->type)) { parse_error(PE_NOFATAL, tok, "Cannot apply `+' except number types"); return expr; @@ -1041,6 +1054,7 @@ static Expr *parse_unary(void) { if ((tok = match(TK_SUB)) != NULL) { Expr *expr = parse_cast_expr(); + mark_var_used(expr); if (!is_number(expr->type)) { parse_error(PE_NOFATAL, tok, "Cannot apply `-' except number types"); return expr; @@ -1070,6 +1084,7 @@ static Expr *parse_unary(void) { if ((tok = match(TK_NOT)) != NULL) { Expr *expr = parse_cast_expr(); + mark_var_used(expr); if (!is_number(expr->type) && !ptr_or_array(expr->type)) { parse_error(PE_NOFATAL, tok, "Cannot apply `!' except number or pointer types"); return new_expr_fixlit(&tyBool, tok, false); @@ -1079,6 +1094,7 @@ static Expr *parse_unary(void) { if ((tok = match(TK_TILDA)) != NULL) { Expr *expr = parse_cast_expr(); + mark_var_used(expr); if (!is_fixnum(expr->type->kind)) { parse_error(PE_NOFATAL, tok, "Cannot apply `~' except integer"); return new_expr_fixlit(&tyInt, expr->token, 0); @@ -1094,6 +1110,7 @@ static Expr *parse_unary(void) { if ((tok = match(TK_AND)) != NULL) { Expr *expr = parse_cast_expr(); + mark_var_used(expr); assert(expr->type != NULL); #ifndef __NO_BITFIELD if (expr->kind == EX_MEMBER) { @@ -1108,6 +1125,7 @@ static Expr *parse_unary(void) { if ((tok = match(TK_MUL)) != NULL) { Expr *expr = parse_cast_expr(); + mark_var_used(expr); Type *type = expr->type; assert(type != NULL); switch (type->kind) { @@ -1126,12 +1144,14 @@ static Expr *parse_unary(void) { if ((tok = match(TK_INC)) != NULL) { Expr *expr = parse_unary(); + mark_var_used(expr); not_const(expr->type, tok); return incdec_of(EX_PREINC, expr, tok); } if ((tok = match(TK_DEC)) != NULL) { Expr *expr = parse_unary(); + mark_var_used(expr); not_const(expr->type, tok); return incdec_of(EX_PREDEC, expr, tok); } @@ -1159,6 +1179,8 @@ static Expr *parse_mul(void) { return expr; Expr *lhs = expr, *rhs = parse_cast_expr(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_num_bop(kind, tok, lhs, rhs); } } @@ -1177,6 +1199,8 @@ static Expr *parse_add(void) { return expr; Expr *lhs = expr, *rhs = parse_mul(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_addsub(kind, tok, lhs, rhs); } } @@ -1195,6 +1219,8 @@ static Expr *parse_shift(void) { return expr; Expr *lhs = expr, *rhs = parse_add(); + mark_var_used(lhs); + mark_var_used(rhs); if (!is_fixnum(lhs->type->kind) || !is_fixnum(rhs->type->kind)) parse_error(PE_FATAL, tok, "Cannot use `%.*s' except numbers.", (int)(tok->end - tok->begin), tok->begin); @@ -1240,6 +1266,8 @@ static Expr *parse_cmp(void) { return expr; Expr *lhs = expr, *rhs = parse_shift(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_cmp(kind, tok, lhs, rhs); } } @@ -1258,6 +1286,8 @@ static Expr *parse_eq(void) { return expr; Expr *lhs = expr, *rhs = parse_cmp(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_cmp(kind, tok, lhs, rhs); } } @@ -1270,6 +1300,8 @@ static Expr *parse_and(void) { return expr; Expr *lhs = expr, *rhs = parse_eq(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_int_bop(EX_BITAND, tok, lhs, rhs); } } @@ -1282,6 +1314,8 @@ static Expr *parse_xor(void) { return expr; Expr *lhs = expr, *rhs = parse_and(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_int_bop(EX_BITXOR, tok, lhs, rhs); } } @@ -1294,6 +1328,8 @@ static Expr *parse_or(void) { return expr; Expr *lhs = expr, *rhs = parse_xor(); + mark_var_used(lhs); + mark_var_used(rhs); expr = new_expr_int_bop(EX_BITOR, tok, lhs, rhs); } } @@ -1302,9 +1338,12 @@ static Expr *parse_logand(void) { Expr *expr = parse_or(); Token *tok = match(TK_LOGAND); if (tok != NULL) { + mark_var_used(expr); expr = make_cond(expr); do { - Expr *rhs = make_cond(parse_or()); + Expr *rhs = parse_or(); + mark_var_used(rhs); + rhs = make_cond(rhs); if (expr->kind == EX_FIXNUM) expr = expr->fixnum == 0 ? expr : rhs; else @@ -1318,9 +1357,12 @@ static Expr *parse_logior(void) { Expr *expr = parse_logand(); Token *tok = match(TK_LOGIOR); if (tok != NULL) { + mark_var_used(expr); expr = make_cond(expr); do { - Expr *rhs = make_cond(parse_logand()); + Expr *rhs = parse_logand(); + mark_var_used(rhs); + rhs = make_cond(rhs); if (expr->kind == EX_FIXNUM) expr = expr->fixnum != 0 ? expr : rhs; else @@ -1340,6 +1382,9 @@ static Expr *parse_conditional(void) { consume(TK_COLON, "`:' expected"); Expr *fval = parse_conditional(); + mark_var_used(expr); + mark_var_used(tval); + mark_var_used(fval); tval = str_to_char_array_var(curscope, tval); fval = str_to_char_array_var(curscope, fval); @@ -1380,6 +1425,7 @@ Expr *parse_assign(void) { Token *tok; if ((tok = match(kAssignWithOps[i])) != NULL) { Expr *lhs = expr, *rhs = parse_assign(); + mark_var_used(rhs); check_lval(tok, lhs, "Cannot assign"); not_const(lhs->type, tok); @@ -1410,6 +1456,7 @@ Expr *parse_assign(void) { return new_expr_bop(EX_ASSIGN, lhs->type, tok, lhs, rhs); } + mark_var_used(lhs); return transform_assign_with(tok, lhs, rhs); } } diff --git a/src/cc/frontend/var.h b/src/cc/frontend/var.h index 4eca2f35c..dcd6abd2b 100644 --- a/src/cc/frontend/var.h +++ b/src/cc/frontend/var.h @@ -27,6 +27,7 @@ enum { VS_REF_TAKEN = 1 << 7, // `&x` used. VS_PARAM = 1 << 8, // Function parameter + VS_USED = 1 << 9, // used. }; typedef struct VarInfo { diff --git a/src/wcc/gen_wasm.c b/src/wcc/gen_wasm.c index 0c0c8d822..e50e91f76 100644 --- a/src/wcc/gen_wasm.c +++ b/src/wcc/gen_wasm.c @@ -1927,6 +1927,9 @@ static Expr *proc_builtin_va_start(const Token *ident) { return NULL; } + mark_var_used(ap); + mark_var_used(param); + Scope *top_scope = curscope; for (Scope *p = curscope; p = p->parent, !is_global_scope(p); ) top_scope = p; @@ -1962,6 +1965,8 @@ static Expr *proc_builtin_va_arg(const Token *ident) { Type *type = parse_var_def(NULL, NULL, NULL); consume(TK_RPAR, "`)' expected"); + mark_var_used(ap); + // (ap = (char*)ap + sizeof(type), *(type*)((char*)ap - sizeof(type))) size_t size = type_size(type); Expr *size_lit = new_expr_fixlit(&tySize, ap->token, size); diff --git a/tests/fvaltest.c b/tests/fvaltest.c index 6d7f21f5d..ab2db2f4e 100644 --- a/tests/fvaltest.c +++ b/tests/fvaltest.c @@ -19,6 +19,7 @@ double mix_params(int i0, double d0, int i1, float f1, int i2, double d2, int i3 double mix_many_params(int n, int i1, double d1, int i2, double d2, int i3, double d3, int i4, double d4, int i5, double d5, int i6, double d6) { + (void)n; return i1 * d1 + i2 * d2 + i3 * d3 + i4 * d4 + i5 * d5 + i6 * d6; } #endif diff --git a/tests/test.sh b/tests/test.sh index db5d82b66..9cad734e0 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -145,14 +145,14 @@ link_error() { test_basic() { begin_test_suite "Basic" - try 'data-bss alignment' 0 'static char data = 123; static int bss; return (long)&bss & 3;' + try 'data-bss alignment' 0 'static char data = 123; (void)data; static int bss; return (long)&bss & 3;' try 'cast array to pointer' 44 'int a[3][2] = {11,22,33,44,55,66}; int *p = a; return p[3]; //-WNOERR' try_direct 'variable definition overwrite' 123 'int x; int x = 123; int main(void) {return x;}' compile_error 'variable definition conflict' 'int x = 0; int x = 123; int main(void) {return x;}' compile_error 'illegal type combination' 'int main(void) {long short x = 99; return x;}' compile_error 'assign array to struct' 'int main(void) {int a[3][2] = {11,22,33,44,55,66}; struct { int a[3][2]; } s; s = a; return s.a[1][1]; } //-WNOERR' compile_error 'subtract void pointers' 'int main(void) {char s[16]; void *p = &s[1], *q = &s[15]; return q - p;}' - try_direct 'direct addressing' 99 'int main(int argc, char *argv[]) {if (argc < 0) { *(volatile short*)0x12 = 0x34; return *(volatile int*)0x5678; } return 99;}' + try_direct 'direct addressing' 99 'int main(int argc, char *argv[]) {(void)argv; if (argc < 0) { *(volatile short*)0x12 = 0x34; return *(volatile int*)0x5678; } return 99;}' try_direct 'restrict for array in funparam' 83 'int sub(int arr[restrict]) { return arr[0]; } int main(void) { int a[] = {83}; return sub(a); }' end_test_suite diff --git a/tests/valtest.c b/tests/valtest.c index 18ac70de4..0831fff7e 100644 --- a/tests/valtest.c +++ b/tests/valtest.c @@ -632,7 +632,7 @@ TEST(all) { } { int x = 1; - { int x = 2; } + { int x = 2; (void)x; } EXPECT("block scope", 1, x); } { @@ -959,7 +959,6 @@ TEST(basic) { enum Num2 { Ten = 10, Eleven, }; EXPECT("enum with assign and trailing comma", 11, Eleven); - int x = 0; int y = 1; switch (y) { case One: EXPECT_TRUE("enum can use in case"); break; @@ -1133,7 +1132,7 @@ TTT:; int x = 1; { x = 10; - int x = 100; + int x = 100; (void)x; } EXPECT("shadow var", 10, x); } @@ -1370,7 +1369,7 @@ TEST(struct) { EXPECT("return struct member", 34, y); } { - int dummy[1]; + int dummy[1]; (void)dummy; LongStruct s; s = return_lstruct(); EXPECT("return struct not broken", 222, s.y); @@ -1391,7 +1390,8 @@ TEST(struct) { }; EXPECT("flexible array member", sizeof(int), sizeof(struct FlexibleArrayMember)); - struct FlexibleArrayMember fam; + // Can declare local variable for FAM. + struct FlexibleArrayMember fam; (void)fam; int buf[4]; memset(buf, -1, sizeof(buf)); @@ -1778,7 +1778,7 @@ TEST(initializer) { void empty_function(void){} int more_params(int a, int b, int c, int d, int e, int f, char g, int h) { return a + b + c + d + e + f + g + h; } typedef struct {int x; int y;} MoreParamsReturnsStruct; -MoreParamsReturnsStruct more_params_returns_struct(int a, int b, int c, int d, int e, int f, int g) { return (MoreParamsReturnsStruct){f + g}; } +MoreParamsReturnsStruct more_params_returns_struct(int a, int b, int c, int d, int e, int f, int g) { return (MoreParamsReturnsStruct){a + b + c + d + e + f + g}; } int array_arg_wo_size(int arg[]) { return arg[1]; } long long long_immediate(unsigned long long x) { return x / 11; } @@ -1847,7 +1847,7 @@ TEST(function) { EXPECT("more params", 36, more_params(1, 2, 3, 4, 5, 6, 7, 8)); { MoreParamsReturnsStruct s = more_params_returns_struct(11, 22, 33, 44, 55, 66, 77); - EXPECT("more params w/ struct", 143, s.x); + EXPECT("more params w/ struct", 308, s.x); } // EXPECT("proto in func", 78, 'int main(){ int sub(int); return sub(77); } int sub(int x) { return x + 1; }') From 100a3d57c6f6fc397166892f9bd7d1dd04a270e1 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Tue, 26 Nov 2024 08:31:03 +0900 Subject: [PATCH 3/5] Prevent emitting unused variables/functions #183 --- libsrc/crt0/_start.c | 1 + src/cc/backend/codegen.c | 5 ++- src/cc/backend/emit_util.c | 5 ++- src/cc/frontend/cc_misc.c | 7 ++++ src/cc/frontend/cc_misc.h | 4 ++ src/cc/frontend/fe_misc.c | 70 ++++++++++++++++++++++++++++++++++- src/cc/frontend/fe_misc.h | 2 + src/cc/frontend/initializer.c | 5 ++- src/cc/frontend/parser.c | 9 ++++- src/cc/frontend/parser_expr.c | 2 +- src/cc/frontend/var.h | 13 ++++--- src/wcc/emit_wasm.c | 25 +++++++------ src/wcc/gen_wasm.c | 4 +- src/wcc/traverse.c | 27 ++++++++++---- 14 files changed, 144 insertions(+), 35 deletions(-) diff --git a/libsrc/crt0/_start.c b/libsrc/crt0/_start.c index bafceeed6..3d0de6dd1 100644 --- a/libsrc/crt0/_start.c +++ b/libsrc/crt0/_start.c @@ -39,6 +39,7 @@ static void start2(int argc, char *argv[], char *env[]) { } void _start(void) { + (void)start2; // Avoid warning: unused function 'start2' #if defined(__x86_64__) __asm("mov (%rsp), %rdi\n" "lea 8(%rsp), %rsi\n" diff --git a/src/cc/backend/codegen.c b/src/cc/backend/codegen.c index 249cb3a46..6ab519d9a 100644 --- a/src/cc/backend/codegen.c +++ b/src/cc/backend/codegen.c @@ -10,6 +10,7 @@ #include "arch_config.h" #include "ast.h" +#include "cc_misc.h" // is_function_omitted #include "fe_misc.h" // curfunc, curscope #include "ir.h" #include "optimize.h" @@ -874,8 +875,8 @@ bool gen_defun(Function *func) { return false; VarInfo *funcvi = scope_find(global_scope, func->name, NULL); - if (funcvi != NULL && satisfy_inline_criteria(funcvi, funcvi->storage)) { - // Omit inline function: func->extra preserves the value (NULL). + if (is_function_omitted(funcvi)) { + // Omit function: func->extra preserves the value (NULL). return false; } diff --git a/src/cc/backend/emit_util.c b/src/cc/backend/emit_util.c index 00f60633e..dce00d4fc 100644 --- a/src/cc/backend/emit_util.c +++ b/src/cc/backend/emit_util.c @@ -410,7 +410,10 @@ void emit_code(Vector *decls) { emit_comment(NULL); for (int i = 0; i < global_scope->vars->len; ++i) { VarInfo *varinfo = global_scope->vars->data[i]; - if ((varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER)) || varinfo->type->kind == TY_FUNC) + int storage = varinfo->storage; + if (varinfo->type->kind == TY_FUNC || + (storage & (VS_EXTERN | VS_ENUM_MEMBER)) || + (storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. continue; emit_varinfo(varinfo, varinfo->global.init); } diff --git a/src/cc/frontend/cc_misc.c b/src/cc/frontend/cc_misc.c index ea6f62f1e..a8998d949 100644 --- a/src/cc/frontend/cc_misc.c +++ b/src/cc/frontend/cc_misc.c @@ -5,10 +5,17 @@ #include // PRId64 #include "ast.h" +#include "fe_misc.h" #include "type.h" #include "util.h" #include "var.h" +bool is_function_omitted(const VarInfo *funcvi) { + assert(funcvi != NULL); + return (satisfy_inline_criteria(funcvi, funcvi->storage) || + (funcvi->storage & (VS_STATIC | VS_USED)) == VS_STATIC); // Static function but not used. +} + static void eval_initial_value(Expr *expr, Expr **pvar, int64_t *poffset) { switch (expr->kind) { case EX_FIXNUM: diff --git a/src/cc/frontend/cc_misc.h b/src/cc/frontend/cc_misc.h index 51baa857c..c81201fbe 100644 --- a/src/cc/frontend/cc_misc.h +++ b/src/cc/frontend/cc_misc.h @@ -1,10 +1,14 @@ #pragma once +#include #include typedef struct Expr Expr; typedef struct Initializer Initializer; typedef struct Type Type; +typedef struct VarInfo VarInfo; + +bool is_function_omitted(const VarInfo *funcvi); typedef struct { void (*emit_align)(void *ud, int align); diff --git a/src/cc/frontend/fe_misc.c b/src/cc/frontend/fe_misc.c index d82000380..d63fd9724 100644 --- a/src/cc/frontend/fe_misc.c +++ b/src/cc/frontend/fe_misc.c @@ -18,6 +18,7 @@ Function *curfunc; Scope *curscope; +VarInfo *curvarinfo; LoopScope loop_scope; int compile_warning_count; @@ -446,12 +447,21 @@ static bool cast_numbers(Expr **pLhs, Expr **pRhs, bool make_int) { } void mark_var_used(Expr *expr) { + VarInfo *gvarinfo = NULL; + switch (expr->kind) { case EX_VAR: { - VarInfo *varinfo = scope_find(expr->var.scope, expr->var.name, NULL); + Scope *scope = NULL; + VarInfo *varinfo = scope_find(expr->var.scope, expr->var.name, &scope); assert(varinfo != NULL); - varinfo->storage |= VS_USED; + if (is_global_scope(scope)) { + gvarinfo = varinfo; + } else { + varinfo->storage |= VS_USED; + if (varinfo->storage & VS_STATIC) + gvarinfo = varinfo->static_.svar; + } } break; case EX_COMPLIT: @@ -462,6 +472,62 @@ void mark_var_used(Expr *expr) { break; default: break; } + + if (gvarinfo != NULL && curvarinfo != NULL) { + Vector *refs = curvarinfo->global.referred_globals; + if (refs == NULL) + curvarinfo->global.referred_globals = refs = new_vector(); + vec_push(refs, gvarinfo); + } +} + +void propagate_var_used(void) { + Table checked; + table_init(&checked); + Vector unchecked; + vec_init(&unchecked); + + const Name *constructor_name = alloc_name("constructor", NULL, false); + const Name *destructor_name = alloc_name("destructor", NULL, false); + + // Collect public functions into unchecked. + for (int i = 0; i < global_scope->vars->len; ++i) { + VarInfo *varinfo = global_scope->vars->data[i]; + const Type *type = varinfo->type; + if (type->kind == TY_FUNC) { + Function *func = varinfo->global.func; + if (func == NULL) // Prototype definition + continue; + if (((varinfo->storage & VS_STATIC) || + (varinfo->storage & (VS_INLINE | VS_EXTERN)) == VS_INLINE) && + (func->attributes == NULL || + (!table_try_get(func->attributes, constructor_name, NULL) && + !table_try_get(func->attributes, destructor_name, NULL)))) { + continue; + } + } else { + if (varinfo->storage & (VS_STATIC | VS_EXTERN | VS_ENUM_MEMBER)) + continue; + } + vec_push(&unchecked, varinfo); + } + + // Propagate usage. + while (unchecked.len > 0) { + VarInfo *varinfo = vec_pop(&unchecked); + if (table_try_get(&checked, varinfo->name, NULL)) + continue; + table_put(&checked, varinfo->name, NULL); + varinfo->storage |= VS_USED; + + Vector *refs = varinfo->global.referred_globals; + if (refs == NULL) + continue; + for (int j = 0; j < refs->len; ++j) { + VarInfo *ref = refs->data[j]; + vec_push(&unchecked, ref); + } + } } void check_lval(const Token *tok, Expr *expr, const char *error) { diff --git a/src/cc/frontend/fe_misc.h b/src/cc/frontend/fe_misc.h index 52a05d30e..edfb52d45 100644 --- a/src/cc/frontend/fe_misc.h +++ b/src/cc/frontend/fe_misc.h @@ -16,6 +16,7 @@ typedef struct Vector Vector; extern Function *curfunc; extern Scope *curscope; +extern VarInfo *curvarinfo; extern int compile_warning_count; extern int compile_error_count; @@ -90,6 +91,7 @@ Expr *make_cast(Type *type, const Token *token, Expr *sub, bool is_explicit); const MemberInfo *search_from_anonymous(const Type *type, const Name *name, const Token *ident, Vector *stack); void mark_var_used(Expr *expr); +void propagate_var_used(void); void check_lval(const Token *tok, Expr *expr, const char *error); Expr *reduce_refer(Expr *expr); Expr *make_refer(const Token *tok, Expr *expr); diff --git a/src/cc/frontend/initializer.c b/src/cc/frontend/initializer.c index 24d0d3330..9c940d951 100644 --- a/src/cc/frontend/initializer.c +++ b/src/cc/frontend/initializer.c @@ -98,7 +98,9 @@ Expr *str_to_char_array_var(Scope *scope, Expr *str) { init->single = str; VarInfo *varinfo = str_to_char_array(scope, type, init); - return new_expr_variable(varinfo->name, type, str->token, scope); + Expr *expr = new_expr_variable(varinfo->name, type, str->token, scope); + mark_var_used(expr); + return expr; } static Stmt *build_memcpy(Expr *dst, Expr *src, size_t size) { @@ -152,6 +154,7 @@ static Stmt *init_char_array_by_string(Expr *dst, Initializer *src) { VarInfo *varinfo = str_to_char_array(curscope, strtype, src); Expr *var = new_expr_variable(varinfo->name, strtype, NULL, curscope); assert(str->type->kind == TY_ARRAY); + mark_var_used(var); return build_memcpy(dst, var, len * type_size(str->type->pa.ptrof)); } diff --git a/src/cc/frontend/parser.c b/src/cc/frontend/parser.c index 5aa7965f4..9ca33242f 100644 --- a/src/cc/frontend/parser.c +++ b/src/cc/frontend/parser.c @@ -813,6 +813,7 @@ static Declaration *parse_defun(Type *functype, int storage, Token *ident, const assert(is_global_scope(curscope)); curfunc = func; static_vars = func->static_vars = new_vector(); + curvarinfo = varinfo; Vector *top_vars = new_vector(); for (int i = 0; i < param_vars->len; ++i) { VarInfo *vi = param_vars->data[i]; @@ -843,6 +844,7 @@ static Declaration *parse_defun(Type *functype, int storage, Token *ident, const match(TK_SEMICOL); // Ignore redundant semicolon. curfunc = NULL; static_vars = NULL; + curvarinfo = NULL; #ifndef __NO_VLA if (vla_inits != NULL) { @@ -925,13 +927,16 @@ static void parse_global_var_decl(Type *rawtype, int storage, Type *type, Token } Initializer *init = NULL; - if (match(TK_ASSIGN) != NULL) + if (match(TK_ASSIGN) != NULL) { + curvarinfo = varinfo; init = parse_initializer(); + } if (ident != NULL) { varinfo->global.init = check_vardecl(&type, ident, storage, init); varinfo->type = type; // type might be changed. } + curvarinfo = NULL; } } @@ -1151,6 +1156,8 @@ void parse(Vector *decls) { vec_push(decls, decl); } + propagate_var_used(); + #if XCC_TARGET_PLATFORM == XCC_PLATFORM_APPLE || XCC_TARGET_PLATFORM == XCC_PLATFORM_WASI modify_dtor_func(decls); #endif diff --git a/src/cc/frontend/parser_expr.c b/src/cc/frontend/parser_expr.c index 0bafa0ad2..1e6c5c1ee 100644 --- a/src/cc/frontend/parser_expr.c +++ b/src/cc/frontend/parser_expr.c @@ -836,7 +836,7 @@ static Expr *parse_prim(void) { consume(TK_RPAR, "`)' expected"); if (fetch_token()->kind != TK_LBRACE) { parse_error(PE_NOFATAL, NULL, "`{' expected"); - return new_expr_variable(alloc_label(), type, tok, curscope); // Dummy + return alloc_tmp_var(curscope, type); // Dummy } return parse_compound_literal(type); } else if (match(TK_LBRACE)) { // ({}) diff --git a/src/cc/frontend/var.h b/src/cc/frontend/var.h index dcd6abd2b..0d4fe3fce 100644 --- a/src/cc/frontend/var.h +++ b/src/cc/frontend/var.h @@ -41,12 +41,15 @@ typedef struct VarInfo { VReg *vreg; FrameInfo *frameinfo; } local; - union { - Initializer *init; - struct { - Function *func; - Declaration *funcdecl; + struct { + union { + Initializer *init; + struct { + Function *func; + Declaration *funcdecl; + }; }; + Vector *referred_globals; // } global; struct { struct VarInfo *svar; // which points to static variable. diff --git a/src/wcc/emit_wasm.c b/src/wcc/emit_wasm.c index 7d6ba43d7..89475dcc0 100644 --- a/src/wcc/emit_wasm.c +++ b/src/wcc/emit_wasm.c @@ -181,7 +181,10 @@ static Vector *construct_data_segment(void) { // GVarInfo *info; for (int it = 0; (it = table_iterate(&gvar_info_table, it, &name, (void**)&info)) != -1; ) { const VarInfo *varinfo = info->varinfo; - if (varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER) || varinfo->type->kind == TY_FUNC) + int storage = varinfo->storage; + if (varinfo->type->kind == TY_FUNC || + (storage & (VS_EXTERN | VS_ENUM_MEMBER)) || + (storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. continue; if ((k == 0) == (varinfo->global.init == NULL) || !is_global_datsec_var(varinfo, global_scope)) @@ -347,9 +350,7 @@ static void emit_function_section(EmitWasm *ew) { const Name *name; FuncInfo *info; for (int it = 0; (it = table_iterate(&func_info_table, it, &name, (void**)&info)) != -1; ) { - if (info->func == NULL) - continue; - if (satisfy_inline_criteria(info->varinfo, info->varinfo->storage)) + if (info->func == NULL || is_function_omitted(info->varinfo)) continue; ++function_count; int type_index = info->type_index; @@ -495,10 +496,9 @@ static void emit_code_section(EmitWasm *ew) { size_t offset = codesec.len; for (int it = 0; (it = table_iterate(&func_info_table, it, &name, (void**)&info)) != -1; ) { Function *func = info->func; - if (func == NULL) - continue; - if (satisfy_inline_criteria(info->varinfo, info->varinfo->storage)) + if (func == NULL || is_function_omitted(info->varinfo)) continue; + FuncExtra* extra = func->extra; DataStorage *code = extra->code; data_concat(&codesec, code); @@ -583,7 +583,7 @@ static void emit_linking_section(EmitWasm *ew) { if ((k == 0 && (info->func != NULL || info->flag == 0)) || // Put external function first. (k == 1 && info->func == NULL)) // Defined function later. continue; - if (satisfy_inline_criteria(info->varinfo, info->varinfo->storage)) + if (is_function_omitted(info->varinfo)) continue; int flags = 0; @@ -611,7 +611,10 @@ static void emit_linking_section(EmitWasm *ew) { int flags_bss = k != 2 ? 0 : cc_flags.common ? WASM_SYM_BINDING_WEAK : 0; for (int it = 0; (it = table_iterate(&gvar_info_table, it, &name, (void**)&info)) != -1; ) { const VarInfo *varinfo = info->varinfo; - if (varinfo->storage & VS_ENUM_MEMBER || varinfo->type->kind == TY_FUNC) + int storage = varinfo->storage; + if (varinfo->type->kind == TY_FUNC || + (storage & VS_ENUM_MEMBER) || // VS_EXPORT is not set because of `__stack_pointer`. + (storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. continue; GVarInfo *info = get_gvar_info_from_name(varinfo->name); if (info == NULL) @@ -764,9 +767,7 @@ static void emit_reloc_code_section(EmitWasm *ew) { FuncInfo *info; for (int it = 0; (it = table_iterate(&func_info_table, it, &name, (void**)&info)) != -1; ) { Function *func = info->func; - if (func == NULL) - continue; - if (satisfy_inline_criteria(info->varinfo, info->varinfo->storage)) + if (func == NULL || is_function_omitted(info->varinfo)) continue; FuncExtra *extra = func->extra; diff --git a/src/wcc/gen_wasm.c b/src/wcc/gen_wasm.c index e50e91f76..f208ae0bd 100644 --- a/src/wcc/gen_wasm.c +++ b/src/wcc/gen_wasm.c @@ -13,6 +13,7 @@ #endif #include "ast.h" +#include "cc_misc.h" // is_funciton_no_output #include "fe_misc.h" // curfunc #include "parser.h" #include "table.h" @@ -1599,8 +1600,7 @@ static void gen_defun(Function *func) { return; VarInfo *funcvi = scope_find(global_scope, func->name, NULL); - assert(funcvi != NULL); - if (satisfy_inline_criteria(funcvi, funcvi->storage)) + if (is_function_omitted(funcvi)) return; DataStorage *code = malloc_or_die(sizeof(*code)); diff --git a/src/wcc/traverse.c b/src/wcc/traverse.c index ea26458fc..e87109f64 100644 --- a/src/wcc/traverse.c +++ b/src/wcc/traverse.c @@ -6,6 +6,7 @@ #include // memcpy #include "ast.h" +#include "cc_misc.h" // is_function_omitted #include "fe_misc.h" // curscope #include "lexer.h" #include "table.h" @@ -745,10 +746,15 @@ static void traverse_defun(Function *func) { // Static variables. Vector *static_vars = func->static_vars; if (static_vars != NULL) { - for (int k = 0; k < 2; ++k) { // 0=register, 1=traverse + VarInfo *funcvi = scope_find(global_scope, func->name, NULL); + assert(funcvi != NULL); + int k = (funcvi->storage & (VS_STATIC | VS_USED)) == VS_STATIC ? 1 : 0; // Static function but not used. + for (; k < 2; ++k) { // 0=register, 1=traverse for (int i = 0, len = static_vars->len; i < len; ++i) { VarInfo *varinfo = static_vars->data[i]; assert(!(varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER) || varinfo->type->kind == TY_FUNC)); + if ((varinfo->storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. + continue; if (k == 0) register_gvar_info(varinfo->name, varinfo); else @@ -900,9 +906,8 @@ static int detect_compile_unit_flags(Vector *decls) { continue; Function *func = decl->defun.func; - VarInfo *varinfo = scope_find(global_scope, func->name, NULL); - assert(varinfo != NULL); - if (satisfy_inline_criteria(varinfo, varinfo->storage)) + VarInfo *funcvi = scope_find(global_scope, func->name, NULL); + if (is_function_omitted(funcvi)) continue; if (detect_compile_unit_sp(func)) { @@ -921,7 +926,10 @@ void traverse_ast(Vector *decls) { for (int k = 0; k < 2; ++k) { // 0=register, 1=traverse for (int i = 0, len = global_scope->vars->len; i < len; ++i) { VarInfo *varinfo = global_scope->vars->data[i]; - if (varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER) || varinfo->type->kind == TY_FUNC) + int storage = varinfo->storage; + if (varinfo->type->kind == TY_FUNC || + (storage & (VS_EXTERN | VS_ENUM_MEMBER)) || + (storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. continue; if (k == 0) register_gvar_info(varinfo->name, varinfo); @@ -970,7 +978,7 @@ void traverse_ast(Vector *decls) { for (int it = 0; (it = table_iterate(&func_info_table, it, &name, (void**)&info)) != -1; ) { if (info->flag == 0 && info->func == NULL) continue; - if (satisfy_inline_criteria(info->varinfo, info->varinfo->storage)) + if (is_function_omitted(info->varinfo)) continue; ++symbol_index; } @@ -1019,7 +1027,7 @@ void traverse_ast(Vector *decls) { if ((k == 0 && (info->func != NULL || info->flag == 0)) || // Put external function first. (k == 1 && info->func == NULL)) // Defined function later. continue; - if (satisfy_inline_criteria(info->varinfo, info->varinfo->storage)) + if (is_function_omitted(info->varinfo)) continue; info->index = index++; VERBOSE("%2d: %.*s%s\n", info->index, NAMES(name), k == 0 ? " (import)" : ""); @@ -1041,7 +1049,10 @@ void traverse_ast(Vector *decls) { GVarInfo *info; for (int it = 0; (it = table_iterate(&gvar_info_table, it, &name, (void**)&info)) != -1; ) { const VarInfo *varinfo = info->varinfo; - if (varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER) || varinfo->type->kind == TY_FUNC) + int storage = varinfo->storage; + if (varinfo->type->kind == TY_FUNC || + (storage & (VS_EXTERN | VS_ENUM_MEMBER)) || + (storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. continue; if ((varinfo->global.init == NULL) == (k == 0) || !is_global_datsec_var(varinfo, global_scope)) continue; From 6e5a90c958bc7022b8c3933c2ee132a2007f57dc Mon Sep 17 00:00:00 2001 From: tyfkda Date: Thu, 28 Nov 2024 10:15:32 +0900 Subject: [PATCH 4/5] Raise warning for unused static varibles/functions #130 --- src/cc/frontend/fe_misc.c | 29 +++++++++++++--- tests/test.sh | 70 +++++++++++++++++++-------------------- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/src/cc/frontend/fe_misc.c b/src/cc/frontend/fe_misc.c index d63fd9724..368ccd8ea 100644 --- a/src/cc/frontend/fe_misc.c +++ b/src/cc/frontend/fe_misc.c @@ -482,8 +482,9 @@ void mark_var_used(Expr *expr) { } void propagate_var_used(void) { - Table checked; - table_init(&checked); + Table used, unused; + table_init(&used); + table_init(&unused); Vector unchecked; vec_init(&unchecked); @@ -503,11 +504,16 @@ void propagate_var_used(void) { (func->attributes == NULL || (!table_try_get(func->attributes, constructor_name, NULL) && !table_try_get(func->attributes, destructor_name, NULL)))) { + if (!(varinfo->storage & VS_INLINE)) + table_put(&unused, varinfo->name, varinfo); continue; } } else { - if (varinfo->storage & (VS_STATIC | VS_EXTERN | VS_ENUM_MEMBER)) + if (varinfo->storage & (VS_STATIC | VS_EXTERN | VS_ENUM_MEMBER)) { + if (varinfo->storage & VS_STATIC) + table_put(&unused, varinfo->name, varinfo); continue; + } } vec_push(&unchecked, varinfo); } @@ -515,9 +521,10 @@ void propagate_var_used(void) { // Propagate usage. while (unchecked.len > 0) { VarInfo *varinfo = vec_pop(&unchecked); - if (table_try_get(&checked, varinfo->name, NULL)) + if (table_try_get(&used, varinfo->name, NULL)) continue; - table_put(&checked, varinfo->name, NULL); + table_put(&used, varinfo->name, NULL); + table_delete(&unused, varinfo->name); varinfo->storage |= VS_USED; Vector *refs = varinfo->global.referred_globals; @@ -528,6 +535,18 @@ void propagate_var_used(void) { vec_push(&unchecked, ref); } } + + const Name *name; + VarInfo *varinfo; + for (int it = 0; (it = table_iterate(&unused, it, &name, (void**)&varinfo)) != -1; ) { + if (varinfo->type->kind == TY_FUNC) { + if (cc_flags.warn.unused_function) + parse_error(PE_WARNING, NULL, "Unused function: `%.*s'", NAMES(name)); + } else { + if (cc_flags.warn.unused_variable) + parse_error(PE_WARNING, NULL, "Unused variable: `%.*s'", NAMES(name)); + } + } } void check_lval(const Token *tok, Expr *expr, const char *error) { diff --git a/tests/test.sh b/tests/test.sh index 9cad734e0..4b2852b69 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -166,7 +166,7 @@ test_struct() { compile_error 'same struct name' 'struct Foo{int x;}; struct Foo{int x;}; int main(){}' compile_error 'same struct name in scope' 'int main(){struct Foo{int x;}; struct Foo{int x;}; }' compile_error 'same struct/union name' 'struct Foo{int x;}; union Foo{int y;}; int main(){}' - compile_error 'union for struct' 'struct Foo{int x;}; int main(){ union Foo foo; }' + compile_error 'union for struct' 'struct Foo{int x;}; int main(){ union Foo foo; (void)foo; }' compile_error 'no member name' 'struct Foo{union{int anon;}; int;}; int main(){}' compile_error 'FAM must be last' 'struct Foo{int a; int b[]; int y;}; int main(){}' compile_error 'FAM cannot array' 'struct Foo{int a; int b[];}; struct Foo x[5]; int main(){}' @@ -178,11 +178,11 @@ test_struct() { test_bitfield() { begin_test_suite "Bitfield" - compile_error 'bit width 0' 'int main(){struct {int x:0;} s;}' - compile_error 'bit width neg' 'int main(){struct {int x:-1;} s;}' - compile_error 'require bit width' 'int main(){struct {int x:;} s;}' - compile_error 'bit size over' 'int main(){struct {int x:33;} s;}' - compile_error 'prohibit &' 'int main(){struct {int x:1;} s; int *p = &s.x;}' + compile_error 'bit width 0' 'int main(){struct {int x:0;} s; (void)s;}' + compile_error 'bit width neg' 'int main(){struct {int x:-1;} s; (void)s;}' + compile_error 'require bit width' 'int main(){struct {int x:;} s; (void)s;}' + compile_error 'bit size over' 'int main(){struct {int x:33;} s; (void)s;}' + compile_error 'prohibit &' 'int main(){struct {int x:1;} s; int *p = &s.x; (void)p;}' compile_error 'prohibit sizeof' 'int main(){struct {int x:1;} s; return sizeof(s.x);}' end_test_suite @@ -191,26 +191,26 @@ test_bitfield() { test_initializer() { begin_test_suite "Initializer" - compile_error 'non exist field initializer' 'struct Foo{int x;}; int main(){ struct Foo foo = {.y=1}; }' - compile_error 'initializer for empty struct' 'struct Foo{}; int main(){ struct Foo foo = {1}; }' + compile_error 'non exist field initializer' 'struct Foo{int x;}; int main(){ struct Foo foo = {.y=1}; (void)foo; }' + compile_error 'initializer for empty struct' 'struct Foo{}; int main(){ struct Foo foo = {1}; (void)foo; }' compile_error 'array initializer (global)' 'int a[1] = 1; int main(){}' - compile_error 'array initializer (local)' 'int main(){int a[1] = 1;}' + compile_error 'array initializer (local)' 'int main(){int a[1] = 1; (void)a;}' compile_error 'struct initializer (global)' 'struct S {int x;} s = 1; int main(){}' - compile_error 'struct initializer (local)' 'int main(){struct S {int x;} s = 1;}' + compile_error 'struct initializer (local)' 'int main(){struct S {int x;} s = 1; (void)s;}' compile_error 'excess elements for array' 'int a[3] = {1, 2, 3, 4}; int main(){return 0;}' compile_error 'excess elements for struct' 'struct {char x; short y; long z;} s = {5, 6, 7, 8}; int main(){return 0;}' compile_error 'undeclared struct init' 'struct Foo foo={12}; int main(){}' compile_error 'undeclared struct array init' 'struct Foo arr[]={{1}, {2}, {3}}; int main(){}' compile_error 'extern with init' 'extern int x = 123; int main(){}' - compile_error 'char array init with ptr' 'char* foo = "foo"; int main(){ char bar[] = foo; }' + compile_error 'char array init with ptr' 'char* foo = "foo"; int main(){ char bar[] = foo; (void)bar; }' compile_error 'global pointer init with undefined' 'char *p = &x; int main(){}' compile_error 'global pointer init with other type' 'int x; char *p = &x; int main(){}' compile_error 'global pointer init with fixnum' 'void *main = 1234;' - compile_error 'const cast' 'int main(){const void *p = 0; void *q = p; return 0;}' + compile_error 'const cast' 'int main(){const void *p = 0; void *q = p; (void)q; return 0;}' compile_error 'no name nor defined struct ptr' 'int main(){ struct *p; }' compile_error 'refer undeclared struct member' 'int main(){ struct Foo *p; p->x; }' - compile_error 'dot designator for array' 'int main(){ int a[] = {.x = 1}; }' - compile_error 'bracket designator for struct' 'int main(){ struct {int x; int y;} s = {[1] = 2}; }' + compile_error 'dot designator for array' 'int main(){ int a[] = {.x = 1}; (void)a; }' + compile_error 'bracket designator for struct' 'int main(){ struct {int x; int y;} s = {[1] = 2}; (void)s; }' end_test_suite } @@ -218,8 +218,8 @@ test_initializer() { test_function() { begin_test_suite "Function" - compile_error 'few arg num' 'void foo(int x){} int main(){ foo(); }' - compile_error 'many arg num' 'void foo(int x){} int main(){ foo(1, 2); }' + compile_error 'few arg num' 'void foo(int x){(void)x;} int main(){ foo(); }' + compile_error 'many arg num' 'void foo(int x){(void)x;} int main(){ foo(1, 2); }' compile_error 'zero arg num' 'void foo(void){} int main(){ foo(1); }' compile_error 'void param' 'int main(void x){}' compile_error 'void and param' 'int main(void, int x){}' @@ -228,7 +228,7 @@ test_function() { compile_error 'return non-void' 'int main(){ return; }' compile_error 'no return' 'int sub(){} int main(){return 0;}' try_direct 'no return in main' 0 'int main(){}' - compile_error 'funparam static' 'int main(static int argc){}' + compile_error 'funparam static' 'int main(static int argc){(void)argc;}' compile_error 'funparam extern' 'int main(extern int argc){}' compile_error 'duplicate func' 'int main(){} int main(){}' compile_error 'conflict func' 'void sub(); int sub(int, char**){return 0;} int main(int argc, char** argv){return sub(argc, argv);}' @@ -254,19 +254,19 @@ test_error() { compile_error 'post dec non lval' 'int main(){ 23--; }' compile_error '*num' 'int main(){ *123; }' compile_error '&num' 'int main(){ &123; }' - compile_error '&enum' 'enum Num { Zero }; int main(){ void *p = &Zero; }' + compile_error '&enum' 'enum Num { Zero }; int main(){ void *p = &Zero; (void)p; }' compile_error 'scoped enum name' 'int sub(){enum Num{Zero}; return Zero;} int main(){enum Num n = 0; return n;}' compile_error 'scoped enum value' 'int sub(){enum{Zero}; return Zero;} int main(){return Zero;}' compile_error 'assign to non-lhs' 'int main(){ int x; x + 1 = 3; }' - compile_error 'assign to array' 'int main(){ int a[3], b[3]; a = b; }' + compile_error 'assign to array' 'int main(){ int a[3], b[3]; a = b; (void)a; }' compile_error 'assign to func' 'int main(){ main = main; }' compile_error '+= to non-lhs' 'int main(){ int x; x + 1 += 3; }' compile_error 'implicit cast to ptr' 'void foo(int *p); int main(){ foo(123); }' - compile_error 'cast to array' 'int sub(int a[][3]){return 0;} int main(){ int a[3][2]; return sub((int[][3])a); }' + compile_error 'cast to array' 'int sub(int a[][3]){(void)a; return 0;} int main(){ int a[3][2]; return sub((int[][3])a); }' compile_error 'struct->' 'struct Foo{int x;}; int main(){ struct Foo foo; foo->x; }' compile_error 'struct*.' 'struct Foo{int x;}; int main(){ struct Foo* p; p.x; }' compile_error 'int*->' 'int main(){ int *p; p->x; }' - compile_error 'void var' 'int main(){ void x; }' + compile_error 'void var' 'int main(){ void x; (void)x; }' compile_error 'void expr' 'int main(){ 1 + (void)2; }' compile_error 'empty char' "int main() { return ''; }" compile_error 'no single char' "int main() { return '12'; }" @@ -276,18 +276,18 @@ test_error() { compile_error 'continue outside loop' 'int main(){ continue; }' compile_error 'continue inside switch' 'int main(){ switch (0) {continue;} }' compile_error 'use before decl' 'int main(){ x = 0; int x; }' - compile_error 'scope invisible' 'int main(){ {int x;} return x; }' - compile_error 'array = ptr' 'int main(){ int a[1], *p; a = p; }' + compile_error 'scope invisible' 'int main(){ {int x; (void)x;} return x; }' + compile_error 'array = ptr' 'int main(){ int a[1], *p; a = p; (void)a; }' compile_error 'case w/o switch' 'int main(){ for (;;) { case 0: break; } return 0; }' compile_error 'case outside switch' 'int main(){ switch(0){} case 0: return 0; }' compile_error 'dup cases' 'int main(){ switch(0){case 1: break; case 1: break;} }' compile_error 'case is int only' 'int main(){ switch(0){case "foo": break;} }' compile_error 'default outside switch' 'int main(){ switch(0){} default: return 0; }' compile_error 'non-const case' 'int main(){int x=1; switch (x){case x: x=2;}}' - compile_error 'vardecl is not stmt' 'int main(){ if (1) int x = 0; }' + compile_error 'vardecl is not stmt' 'int main(){ if (1) int x = 0; (void)x; }' compile_error 'extern only' 'extern int x; int main(){ x = 123; }' compile_error 'for-var scoped' 'int main(){ for (int i = 0; i < 5; ++i) ; return i; }' - compile_error 'use void' 'void func(){} int main(){ int a = (int)func(); }' + compile_error 'use void' 'void func(){} int main(){ int a = (int)func(); (void)a; }' compile_error 'goto no-label' 'int main(){ goto label; } //-WCC' compile_error 'goto dup-label' 'int main(){ label: goto label; label:; } //-WCC' @@ -297,13 +297,13 @@ test_error() { # Reachability check. compile_error 'unreachable after return' 'int main(){ int x=0; return x; x=1; }' compile_error 'unreachable break' 'int main(){ for (;;) { break; break; } }' - compile_error 'unreachable after if' 'int main(int x, char *argv[]){ for (;;) { if (x) break; else return 1; ++x; } }' - compile_error 'unreachable after switch' 'int main(int x, char *argv[]){ switch (x){case 0: return 1; default: return 2;} return 3; }' - compile_error 'unreachable after infinite loop' 'int main(int x, char *argv[]){ for (;;) { ++x; } return x; }' - compile_error 'unreachable inner for(;0;)' 'int main(int x, char *argv[]){ for (;0;) { ++x; } return x; }' - compile_error 'unreachable after while(1)' 'int main(int x, char *argv[]){ while (1) {++x;} return x; }' - compile_error 'unreachable inner while(0)' 'int main(int x, char *argv[]){ while (0) {++x;} return x; }' - compile_error 'unreachable after do-while(1)' 'int main(int x, char *argv[]){ do {++x;} while (1); return x; }' + compile_error 'unreachable after if' 'int main(){ int x=0; for (;;) { if (x) break; else return 1; ++x; } }' + compile_error 'unreachable after switch' 'int main(){ int x=0; switch (x){case 0: return 1; default: return 2;} return 3; }' + compile_error 'unreachable after infinite loop' 'int main(){ int x=0; for (;;) { ++x; } return x; }' + compile_error 'unreachable inner for(;0;)' 'int main(){ int x=0; for (;0;) { ++x; } return x; }' + compile_error 'unreachable after while(1)' 'int main(){ int x=0; while (1) {++x;} return x; }' + compile_error 'unreachable inner while(0)' 'int main(){ int x=0; while (0) {++x;} return x; }' + compile_error 'unreachable after do-while(1)' 'int main(){ int x=0; do {++x;} while (1); return x; }' try 'allow switch break after block' 21 'int x=21; switch (x) {case 1: {return -1;} break; case 2: {break;} break;} return x;' try 'use goto to skip first' 54 'int acc=0, i=1; goto inner; for (; i<=10;) {acc += i; inner: ++i;} return acc; //-WCC' compile_error 'after noreturn function' '#include \nint main(){ exit(0); return 1; }' @@ -318,18 +318,18 @@ test_error() { compile_error '(_Bool)x = ' 'int main(){ _Bool x; (_Bool)x = 32; }' compile_error 'compound literal =' 'struct Foo {int x;}; int main(){ struct Foo foo = {1}; (struct Foo){66} = foo; }' compile_error 'compound literal w/o brace' 'int main(){ ++(int)55; }' - compile_error 'param and first scope' 'int main(int x){ int x; }' + compile_error 'param and first scope' 'int main(int x){ int x; (void)x; }' compile_error 'conflict typedef' 'typedef int Foo; typedef long Foo; int main(){}' compile_error 'conflict struct typedef' 'typedef struct{int x;} Foo; typedef struct{int x;} Foo; int main(){}' compile_error 'no VLA in global' "#ifndef __NO_VLA\nint n = 3; int array[n];\n#else\n#error no VLA\n#endif\n int main(){}" compile_error 'no VLA in global typedef' "#ifndef __NO_VLA\nint n = 3; typedef int array[n];\n#else\n#error no VLA\n#endif\n int main(){}" - compile_error 'negative array' "int main(){ int array[-1]; }" + compile_error 'negative array' "int main(){ int array[-1]; (void)array; }" compile_error 'size unknown' 'extern char string[]; int main(){ return sizeof(string); } char string[] = "Hello";' compile_error 'scoped typedef' 'void sub(){typedef int T;} T g=123; int main(){return g;}' compile_error 'typedef and var' 'int main(){typedef int ttt; int ttt = 123; return ttt;}' compile_error 'var and typedef' 'int main(){int ttt = 123; typedef int ttt; return ttt;}' try_direct 'typedef and var in other scope' 123 'typedef int ttt; int main(){ttt ttt = 123; return ttt;}' - compile_error 'func retval ref' 'typedef struct {int x;} S; S func() {return (S){111};} int main(){S *p = &func(); return s->x;}' + compile_error 'func retval ref' 'typedef struct {int x;} S; S func() {return (S){111};} int main(){S *p = &func(); return p->x;}' compile_error 'if void' 'int main(){if ((void)0) {}}' compile_error 'while void' 'int main(){while ((void)1) {}}' compile_error 'do-while void' 'int main(){do {} while ((void)-2);}' From ad6ac9759b9e527d2c2fee625da4e1ca8ac1f3c8 Mon Sep 17 00:00:00 2001 From: tyfkda Date: Sat, 30 Nov 2024 12:39:52 +0900 Subject: [PATCH 5/5] Add unittest Suppress assignment expression if the LHS is `unused`, because unused static variable is not emitted. --- src/cc/backend/codegen_expr.c | 8 +++++++- src/wcc/emit_wasm.c | 2 ++ src/wcc/gen_wasm.c | 10 ++++++++++ src/wcc/traverse.c | 16 ++++++++++------ tests/test.sh | 6 ++++++ 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/cc/backend/codegen_expr.c b/src/cc/backend/codegen_expr.c index fc8b52e7b..5fc935c4b 100644 --- a/src/cc/backend/codegen_expr.c +++ b/src/cc/backend/codegen_expr.c @@ -563,14 +563,20 @@ static VReg *gen_comma(Expr *expr) { static VReg *gen_assign_sub(Expr *lhs, Expr *rhs) { VReg *src = gen_expr(rhs); if (lhs->kind == EX_VAR) { + const VarInfo *varinfo = scope_find(lhs->var.scope, lhs->var.name, NULL); + assert(varinfo != NULL); if (is_prim_type(lhs->type) && !is_global_scope(lhs->var.scope)) { - const VarInfo *varinfo = scope_find(lhs->var.scope, lhs->var.name, NULL); if (is_local_storage(varinfo)) { assert(varinfo->local.vreg != NULL); new_ir_mov(varinfo->local.vreg, src, is_unsigned(rhs->type) ? IRF_UNSIGNED : 0); return src; } } + + if ((varinfo->storage & (VS_STATIC | VS_USED)) == VS_STATIC) { + // Can be omitted. + return src; + } } VReg *dst = gen_lval(lhs); diff --git a/src/wcc/emit_wasm.c b/src/wcc/emit_wasm.c index 89475dcc0..c2cb5f852 100644 --- a/src/wcc/emit_wasm.c +++ b/src/wcc/emit_wasm.c @@ -29,6 +29,7 @@ static void emit_global_number(void *ud, const Type *type, Expr *var, Fixnum off v += get_indirect_function_index(var->var.name); } else { GVarInfo *info = get_gvar_info(var); + assert(info != NULL); assert(!is_prim_type(info->varinfo->type) || (info->varinfo->storage & VS_REF_TAKEN)); v += info->non_prim.address; } @@ -116,6 +117,7 @@ static void emit_number(void *ud, const Type *type, Expr *var, Fixnum offset) { } else { assert(var->kind == EX_VAR); const GVarInfo *info = get_gvar_info(var); + assert(info != NULL); assert(!is_prim_type(info->varinfo->type) || (info->varinfo->storage & VS_REF_TAKEN)); v += info->non_prim.address; diff --git a/src/wcc/gen_wasm.c b/src/wcc/gen_wasm.c index f208ae0bd..ffa997868 100644 --- a/src/wcc/gen_wasm.c +++ b/src/wcc/gen_wasm.c @@ -482,6 +482,7 @@ static void gen_ref_sub(Expr *expr) { ADD_VARUINT32(info->indirect_index); } else { GVarInfo *info = get_gvar_info(expr); + assert(info != NULL); ADD_CODE(OP_I32_CONST); FuncExtra *extra = curfunc->extra; DataStorage *code = extra->code; @@ -554,6 +555,7 @@ static void gen_var(Expr *expr, bool needval) { ADD_ULEB128(vreg->prim.local_index); } else { GVarInfo *info = get_gvar_info(expr); + assert(info != NULL); ADD_CODE(OP_GLOBAL_GET); FuncExtra *extra = curfunc->extra; DataStorage *code = extra->code; @@ -616,6 +618,7 @@ static void gen_set_to_var(Expr *var) { } else { assert(!is_global_datsec_var(varinfo, var->var.scope)); GVarInfo *info = get_gvar_info(var); + assert(info != NULL); ADD_CODE(OP_GLOBAL_SET); FuncExtra *extra = curfunc->extra; DataStorage *code = extra->code; @@ -799,6 +802,7 @@ static void gen_incdec(Expr *expr, bool needval) { } else { assert(!is_global_datsec_var(varinfo, scope)); GVarInfo *info = get_gvar_info(target); + assert(info != NULL); ADD_CODE(OP_GLOBAL_SET); ADD_ULEB128(info->prim.index); if (needval) { @@ -823,6 +827,12 @@ static void gen_assign_sub(Expr *lhs, Expr *rhs) { gen_set_to_var(lhs); break; } + + if ((varinfo->storage & (VS_STATIC | VS_USED)) == VS_STATIC) { + // Assignment can be omitted. + gen_expr(rhs, false); + return; + } } gen_lval(lhs); gen_expr(rhs, true); diff --git a/src/wcc/traverse.c b/src/wcc/traverse.c index e87109f64..a179b258e 100644 --- a/src/wcc/traverse.c +++ b/src/wcc/traverse.c @@ -157,7 +157,7 @@ GVarInfo *get_gvar_info(Expr *expr) { } assert(varinfo != NULL); GVarInfo *info = get_gvar_info_from_name(name); - if (info == NULL) { + if (info == NULL && (!(varinfo->storage & VS_STATIC) || (varinfo->storage & VS_USED))) { // Returns dummy. info = register_gvar_info(name, varinfo); info->flag |= GVF_UNRESOLVED; @@ -165,7 +165,7 @@ GVarInfo *get_gvar_info(Expr *expr) { return info; } -#define add_global_var(type, name) scope_add(global_scope, name, type, 0) +#define add_global_var(type, name) scope_add(global_scope, name, type, VS_USED) void add_builtin_function(const char *str, Type *type, BuiltinFunctionProc *proc, bool add_to_scope) { @@ -629,7 +629,8 @@ static void traverse_varinfo(VarInfo *varinfo) { if (scope_find(global_scope, varinfo->name, NULL) == NULL) { // Register into global to output linking information. GVarInfo *info = register_gvar_info(varinfo->name, varinfo); - info->flag |= GVF_UNRESOLVED; + if (info != NULL) + info->flag |= GVF_UNRESOLVED; } } if (!(varinfo->storage & (VS_EXTERN | VS_STATIC | VS_ENUM_MEMBER))) @@ -832,6 +833,7 @@ static void add_builtins(int flag) { GVarInfo *info = get_gvar_info_from_name(name); if (info == NULL) info = register_gvar_info(name, varinfo); + assert(info != NULL); info->flag |= GVF_UNRESOLVED; } } @@ -889,7 +891,9 @@ static bool detect_compile_unit_sp(Function *func) { static int detect_compile_unit_memory(void) { for (int i = 0, len = global_scope->vars->len; i < len; ++i) { VarInfo *varinfo = global_scope->vars->data[i]; - if (varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER) || varinfo->type->kind == TY_FUNC) + if (varinfo->type->kind == TY_FUNC || + (varinfo->storage & (VS_EXTERN | VS_ENUM_MEMBER)) || + (varinfo->storage & (VS_STATIC | VS_USED)) == VS_STATIC) // Static variable but not used. continue; if (is_global_datsec_var(varinfo, global_scope)) return CUF_LINEAR_MEMORY; @@ -992,8 +996,8 @@ void traverse_ast(Vector *decls) { GVarInfo *info; for (int it = 0; (it = table_iterate(&gvar_info_table, it, &name, (void**)&info)) != -1; ) { const VarInfo *varinfo = info->varinfo; - if (varinfo->storage & VS_ENUM_MEMBER || varinfo->type->kind == TY_FUNC) - continue; + assert(!(varinfo->storage & VS_ENUM_MEMBER || varinfo->type->kind == TY_FUNC)); + assert(!((varinfo->storage & (VS_STATIC | VS_USED)) == VS_STATIC)); if ((k == 0 && !(info->flag & GVF_UNRESOLVED)) || (k != 0 && ((info->flag & GVF_UNRESOLVED) || (varinfo->global.init == NULL) == (k == 1)))) continue; diff --git a/tests/test.sh b/tests/test.sh index 4b2852b69..672a1bb57 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -310,6 +310,12 @@ test_error() { compile_error 'noreturn should not return' 'void sub(void) __attribute__((noreturn));\nvoid sub(void){}\nint main(){ sub(); }' compile_error 'noreturn should be void' '#include \nint sub(void) __attribute__((noreturn));\nint sub(void){exit(0);}\nint main(){ sub(); }' + # Unused check. + compile_error 'unused local variable' 'int main(){ int x = 0; x = 1; return 0; }' + compile_error 'unused static variable' 'int main(){ static int x = 0; x = 1; return 0; }' + compile_error 'unused static global variable' 'static int s = 0; int g; int sub(){g=1; return 2;} int main(){ s = sub(); return g; }' + try_direct 'unused static can run w/o -Werror' 1 'static int s = 0; int g; int sub(){g=1; return 2;} int main(){ s = sub(); return g; } //-WNOERR' + compile_error 'enum and global' 'enum Foo { BAR }; int BAR; int main(){}' compile_error 'global and enum' 'int BAR; enum Foo { BAR }; int main(){}' compile_error 'dup enum elem' 'enum Foo { BAR, BAR }; int main(){}'