Skip to content

Commit

Permalink
Apply noreturn attribute to function
Browse files Browse the repository at this point in the history
Fix #116
  • Loading branch information
tyfkda committed Oct 24, 2023
1 parent 2f70c0d commit a44b724
Show file tree
Hide file tree
Showing 20 changed files with 116 additions and 43 deletions.
2 changes: 1 addition & 1 deletion include/setjmp.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ typedef uintptr_t jmp_buf[200 / 8]; // GCC
#endif

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int result);
void longjmp(jmp_buf env, int result) __attribute__((noreturn));

#ifdef __WASM
#define setjmp(env) __builtin_setjmp(env)
Expand Down
2 changes: 1 addition & 1 deletion include/stdlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ void free(void* ptr);
void *realloc(void* ptr, size_t size);
void *calloc(size_t size, size_t n);

void exit(int code);
void exit(int code) __attribute__((noreturn));
int atexit(void (*func)(void));

long strtol(const char *p, char **pp, int base);
Expand Down
4 changes: 4 additions & 0 deletions include/stdnoreturn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#pragma once

// _Noreturn keyword
// __noreturn__ macro
4 changes: 1 addition & 3 deletions libsrc/stdlib/exit.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@ void exit(int code) {
#include "../unistd/_syscall.h"

#if defined(__GNUC__)
static void proc_exit(int code) __attribute((noreturn));
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
static void proc_exit(int code) __attribute__((noreturn));
static void proc_exit(int code) {
#ifdef __NR_exit_group
SYSCALL(__NR_exit_group);
#endif
SYSCALL(__NR_exit);
#if defined(__GNUC__) // Avoid `noreturn` warning.
for (;;)
;
#endif
}
#endif

Expand Down
4 changes: 1 addition & 3 deletions libsrc/tests/longjmp_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,8 @@ TEST(zero) {
int result = setjmp(env);
if (result == 0) {
longjmp(env, 0);
fail("unreachable");
} else {
EXPECT_TRUE(result != 0);
}
EXPECT_TRUE(result != 0);
} END_TEST()

TEST(multiple_times) {
Expand Down
2 changes: 0 additions & 2 deletions src/as/ir_asm.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,13 @@ static Value calc_expr(Table *label_table, const Expr *expr) {
}
if (expr->kind != EX_ADD || lhs.label != NULL) {
error("Illegal expression");
return rhs;
}
// offset + label
return (Value){.label = rhs.label, .offset = lhs.offset + rhs.offset};
}
if (lhs.label != NULL) {
if (expr->kind != EX_ADD) {
error("Illegal expression");
return lhs;
}
// label + offset
return (Value){.label = lhs.label, .offset = lhs.offset + rhs.offset};
Expand Down
4 changes: 2 additions & 2 deletions src/cc/frontend/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ Declaration *new_decl_vardecl(Vector *decls) {

// Function

Function *new_func(Type *type, const Name *name) {
Function *new_func(Type *type, const Name *name, int flag) {
assert(type->kind == TY_FUNC);
Function *func = malloc_or_die(sizeof(*func));
func->type = type;
Expand All @@ -332,7 +332,7 @@ Function *new_func(Type *type, const Name *name) {
func->label_table = NULL;
func->gotos = NULL;
func->extra = NULL;
func->flag = 0;
func->flag = flag;

return func;
}
8 changes: 5 additions & 3 deletions src/cc/frontend/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ enum TokenKind {
TK_ELLIPSIS, // ...
TK_FUNCNAME,
TK_ASM,
TK_ATTRIBUTE,

// For preprocessor.
PPTK_CONCAT, // ##
Expand Down Expand Up @@ -448,10 +449,11 @@ typedef struct Function {
int flag;
} Function;

#define FUNCF_STACK_MODIFIED (1 << 0)
#define FUNCF_HAS_FUNCALL (1 << 1)
#define FUNCF_NORETURN (1 << 0)
#define FUNCF_STACK_MODIFIED (1 << 1)
#define FUNCF_HAS_FUNCALL (1 << 2)

Function *new_func(Type *type, const Name *name);
Function *new_func(Type *type, const Name *name, int flag);

// Declaration

Expand Down
22 changes: 21 additions & 1 deletion src/cc/frontend/fe_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1296,7 +1296,27 @@ void check_reachability(Stmt *stmt) {
case ST_CONTINUE:
stmt->reach |= REACH_STOP;
break;
case ST_EMPTY: case ST_EXPR: case ST_CASE: case ST_VARDECL: case ST_ASM:
case ST_EXPR:
{
// Lazily, check noreturn function call only top of the expression statement.
Expr *expr = stmt->expr;
if (expr->kind == EX_FUNCALL) {
Expr *fexpr = expr->funcall.func;
if (fexpr->kind == EX_VAR && is_global_scope(fexpr->var.scope)) {
VarInfo *varinfo = scope_find(fexpr->var.scope, fexpr->var.name, NULL);
assert(varinfo != NULL);
Declaration *decl = varinfo->global.funcdecl;
if (decl != NULL) {
assert(decl->kind == DCL_DEFUN && decl->defun.func != NULL);
if (decl->defun.func->flag & FUNCF_NORETURN) {
stmt->reach |= REACH_STOP;
}
}
}
}
}
break;
case ST_EMPTY: case ST_CASE: case ST_VARDECL: case ST_ASM:
stmt->reach = 0;
break;
}
Expand Down
3 changes: 1 addition & 2 deletions src/cc/frontend/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ static const struct {
#endif
{"__FUNCTION__", TK_FUNCNAME},
{"__func__", TK_FUNCNAME},
{"__attribute__", TK_ATTRIBUTE},
};

static const struct {
Expand Down Expand Up @@ -505,7 +506,6 @@ const char *read_ident(const char *p_) {
if (ucc > 0) {
if (!isutf8follow(uc)) {
lex_error(p_, "Illegal byte sequence");
break;
}
--ucc;
continue;
Expand Down Expand Up @@ -665,7 +665,6 @@ static Token *get_token(void) {
} else {
if (!for_preprocess) {
lex_error(p, "Unexpected character `%c'(%d)", *p, *p);
return NULL;
}

assert(*p != '\0');
Expand Down
4 changes: 0 additions & 4 deletions src/cc/frontend/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ void unget_token(Token *token);
const char *read_ident(const char *p);
Token *alloc_dummy_ident(void);
const char *get_lex_p(void);
#ifdef __GNUC__
void lex_error(const char *p, const char *fmt, ...) __attribute__((noreturn));
#else
void lex_error(const char *p, const char *fmt, ...);
#endif

const char *block_comment_start(const char *p);
const char *block_comment_end(const char *p);
Expand Down
75 changes: 71 additions & 4 deletions src/cc/frontend/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <assert.h>
#include <inttypes.h> // PRId64
#include <stdbool.h>
#include <string.h>

#include "ast.h"
#include "fe_misc.h"
Expand Down Expand Up @@ -562,9 +563,52 @@ static Stmt *parse_stmt(void) {
return new_stmt_expr(str_to_char_array_var(curscope, val));
}

static int parse_attribute(void) {
if (consume(TK_LPAR, "`(' expected") == NULL) {
match(TK_RPAR);
return 0;
}

int paren_count = 0;
int flag = 0;
for (bool loop = true; loop; ) {
Token *tok = match(-1);
if (tok->kind == TK_EOF) {
parse_error(PE_NOFATAL, tok, "`)' expected");
break;
}
switch (tok->kind) {
case TK_LPAR:
++paren_count;
break;
case TK_RPAR:
if (paren_count <= 0) {
loop = false;
break;
}
--paren_count;
break;
case TK_IDENT:
{
static const char kNoreturn[] = "noreturn";
if (tok->end - tok->begin == sizeof(kNoreturn) -1 &&
memcmp(tok->begin, kNoreturn, sizeof(kNoreturn) - 1) == 0)
flag |= FUNCF_NORETURN;
}
break;
default: break;
}
}
return flag;
}

static Declaration *parse_defun(Type *functype, int storage, Token *ident) {
assert(functype->kind == TY_FUNC);

int flag = 0;
if (match(TK_ATTRIBUTE))
flag = parse_attribute();

bool prototype = match(TK_SEMICOL) != NULL;
if (!prototype && functype->func.params == NULL) { // Old-style
// Treat it as a zero-parameter function.
Expand All @@ -573,12 +617,22 @@ static Declaration *parse_defun(Type *functype, int storage, Token *ident) {
functype->func.vaargs = false;
}

Function *func = new_func(functype, ident->ident);
Function *func = new_func(functype, ident->ident, flag);
VarInfo *varinfo = scope_find(global_scope, func->name, NULL);
bool err = false;
if (varinfo == NULL) {
varinfo = add_var_to_scope(global_scope, ident, functype, storage);
} else {
Declaration *predecl = varinfo->global.funcdecl;
if (predecl != NULL) {
assert(predecl->kind == DCL_DEFUN);
if (predecl->defun.func != NULL) {
int merge_flag = (flag | predecl->defun.func->flag) & FUNCF_NORETURN;
func->flag |= merge_flag;
predecl->defun.func->flag |= merge_flag;
}
}

if (varinfo->type->kind != TY_FUNC ||
!same_type(varinfo->type->func.ret, functype->func.ret) ||
(varinfo->type->func.params != NULL && !same_type(varinfo->type, functype))) {
Expand Down Expand Up @@ -623,8 +677,19 @@ static Declaration *parse_defun(Type *functype, int storage, Token *ident) {
check_goto_labels(func);

check_reachability(func->body_block);
if (functype->func.ret->kind != TY_VOID &&
!(func->body_block->reach & REACH_STOP)) {
if (func->flag & FUNCF_NORETURN) {
if (functype->func.ret->kind != TY_VOID) {
parse_error(PE_WARNING, ident,
"`noreturn' function should not return value");
} else if (!(func->body_block->reach & REACH_STOP)) {
Vector *stmts = func->body_block->block.stmts;
if (stmts->len == 0 || ((Stmt*)stmts->data[stmts->len - 1])->kind != ST_ASM) {
parse_error(PE_WARNING, func->body_block->block.rbrace,
"`noreturn' function should not return");
}
}
} else if (functype->func.ret->kind != TY_VOID &&
!(func->body_block->reach & REACH_STOP)) {
Vector *stmts = func->body_block->block.stmts;
if (stmts->len == 0 || ((Stmt*)stmts->data[stmts->len - 1])->kind != ST_ASM) {
if (equal_name(func->name, alloc_name("main", NULL, false))) {
Expand All @@ -643,7 +708,9 @@ static Declaration *parse_defun(Type *functype, int storage, Token *ident) {

curfunc = NULL;
}
return new_decl_defun(func);
Declaration *decl = new_decl_defun(func);
varinfo->global.funcdecl = decl;
return decl;
}

static Declaration *parse_global_var_decl(Type *rawtype, int storage, Type *type, Token *ident) {
Expand Down
6 changes: 5 additions & 1 deletion src/cc/frontend/var.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <stdbool.h>

typedef struct Declaration Declaration;
typedef struct Function Function;
typedef struct FrameInfo FrameInfo;
typedef struct Initializer Initializer;
Expand Down Expand Up @@ -41,7 +42,10 @@ typedef struct VarInfo {
} local;
union {
Initializer *init;
Function *func;
struct {
Function *func;
Declaration *funcdecl;
};
} global;
struct {
struct VarInfo *gvar; // which points to global(static) variable.
Expand Down
1 change: 0 additions & 1 deletion src/cpp/pp_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,6 @@ Vector *pp_funargs(Vector *tokens, int *pindex, int vaarg) {
Token *tok = match3(-1, tokens, pindex);
if (tok == NULL /*|| tok->kind == TK_EOF*/) {
pp_parse_error(NULL, "`)' expected");
break;
}

if (tok->kind == TK_LPAR) {
Expand Down
4 changes: 0 additions & 4 deletions src/cpp/pp_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,7 @@ Vector *pp_funargs(Vector *tokens, int *pindex, int vaarg); // <Vector*<Token*>
Token *pp_match(enum TokenKind kind);
Token *pp_consume(enum TokenKind kind, const char *error);

#ifdef __GNUC__
void pp_parse_error(const Token *token, const char *fmt, ...) __attribute__((noreturn));
#else
void pp_parse_error(const Token *token, const char *fmt, ...);
#endif

Macro *can_expand_ident(const Name *ident);
void push_lex(const Name *ident, void (*callback)(void));
4 changes: 0 additions & 4 deletions src/cpp/preprocessor.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,6 @@ static void handle_include(const char *p, Stream *stream, bool is_next) {
break;
default:
error("illegal include: %s", orgp);
return;
}

const char *q;
Expand All @@ -203,7 +202,6 @@ static void handle_include(const char *p, Stream *stream, bool is_next) {
break;
if (!err) {
error("Illegal token after include: %s", tok->begin);
err = true;
}
}
}
Expand Down Expand Up @@ -432,7 +430,6 @@ static const char *find_double_quote_end(const char *p) {
switch (*p++) {
case '\0':
lex_error(start, "Quote not closed");
return p - 1;
case '"':
return p;
case '\\':
Expand All @@ -456,7 +453,6 @@ static const char *find_block_comment_end(const char *comment_start, Stream *str
ssize_t len = getline_cont(&line, &capa, stream->fp, &stream->lineno);
if (len == -1) {
lex_error(comment_start, "Block comment not closed");
return strchr(p, '\0');
}
p = line;
OUTPUT_PPLINE("\n");
Expand Down
2 changes: 0 additions & 2 deletions src/ld/archive.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ Archive *load_archive(const char *filename) {
char *q = memchr(p, '\0', &strtab[strtablen] - p);
if (q == NULL) {
error("Illegal strtab");
return false;
}
p = q + 1;
}
Expand Down Expand Up @@ -113,7 +112,6 @@ ElfObj *load_archive_elfobj(Archive *ar, uint32_t offset) {
elfobj_init(elfobj);
if (!read_elf(elfobj, ar->fp, content->name)) {
error("Failed to extract .o: %s", content->name);
return NULL;
}

vec_push(contents, (void*)(intptr_t)offset);
Expand Down
1 change: 0 additions & 1 deletion src/ld/ld.c
Original file line number Diff line number Diff line change
Expand Up @@ -501,7 +501,6 @@ int main(int argc, char *argv[]) {
file->archive = archive;
} else {
error("Unsupported file: %s", src);
exit(1);
}
++nfiles;
}
Expand Down
Loading

0 comments on commit a44b724

Please sign in to comment.