diff --git a/chibicc.h b/chibicc.h index 4e59139fb..773c7ee94 100644 --- a/chibicc.h +++ b/chibicc.h @@ -209,7 +209,9 @@ typedef enum { ND_CASE, // "case" ND_BLOCK, // { ... } ND_GOTO, // "goto" + ND_GOTO_EXPR, // "goto" labels-as-values ND_LABEL, // Labeled statement + ND_LABEL_VAL, // [GNU] Labels-as-values ND_FUNCALL, // Function call ND_EXPR_STMT, // Expression statement ND_STMT_EXPR, // Statement expression @@ -254,7 +256,7 @@ struct Node { bool pass_by_stack; Obj *ret_buffer; - // Goto or labeled statement + // Goto or labeled statement, or labels-as-values char *label; char *unique_label; Node *goto_next; diff --git a/codegen.c b/codegen.c index 161866f06..f4a93a0ab 100644 --- a/codegen.c +++ b/codegen.c @@ -925,6 +925,9 @@ static void gen_expr(Node *node) { return; } + case ND_LABEL_VAL: + println(" lea %s(%%rip), %%rax", node->unique_label); + return; } switch (node->lhs->ty->kind) { @@ -1194,6 +1197,10 @@ static void gen_stmt(Node *node) { case ND_GOTO: println(" jmp %s", node->unique_label); return; + case ND_GOTO_EXPR: + gen_expr(node->lhs); + println(" jmp *%%rax"); + return; case ND_LABEL: println("%s:", node->unique_label); gen_stmt(node->lhs); diff --git a/parse.c b/parse.c index ed237306c..f92a31d63 100644 --- a/parse.c +++ b/parse.c @@ -1533,7 +1533,7 @@ static Node *asm_stmt(Token **rest, Token *tok) { // | "while" "(" expr ")" stmt // | "do" stmt "while" "(" expr ")" ";" // | "asm" asm-stmt -// | "goto" ident ";" +// | "goto" (ident | "*" expr) ";" // | "break" ";" // | "continue" ";" // | ident ":" stmt @@ -1704,6 +1704,14 @@ static Node *stmt(Token **rest, Token *tok) { return asm_stmt(rest, tok); if (equal(tok, "goto")) { + if (equal(tok->next, "*")) { + // [GNU] `goto *ptr` jumps to the address specified by `ptr`. + Node *node = new_node(ND_GOTO_EXPR, tok); + node->lhs = expr(&tok, tok->next->next); + *rest = skip(tok, ";"); + return node; + } + Node *node = new_node(ND_GOTO, tok); node->label = get_ident(tok->next); node->goto_next = gotos; @@ -2406,6 +2414,7 @@ static Node *cast(Token **rest, Token *tok) { // unary = ("+" | "-" | "*" | "&" | "!" | "~") cast // | ("++" | "--") unary +// | "&&" ident // | postfix static Node *unary(Token **rest, Token *tok) { if (equal(tok, "+")) @@ -2448,6 +2457,16 @@ static Node *unary(Token **rest, Token *tok) { if (equal(tok, "--")) return to_assign(new_sub(unary(rest, tok->next), new_num(1, tok), tok)); + // [GNU] labels-as-values + if (equal(tok, "&&")) { + Node *node = new_node(ND_LABEL_VAL, tok); + node->label = get_ident(tok->next); + node->goto_next = gotos; + gotos = node; + *rest = tok->next->next; + return node; + } + return postfix(rest, tok); } @@ -3005,7 +3024,7 @@ static void create_param_lvars(Type *param) { } } -// This function matches gotos with labels. +// This function matches gotos or labels-as-values with labels. // // We cannot resolve gotos as we parse a function because gotos // can refer a label that appears later in the function. diff --git a/test/control.c b/test/control.c index 3cc434679..6ba918cc7 100644 --- a/test/control.c +++ b/test/control.c @@ -87,6 +87,10 @@ int main() { ASSERT(1, ({ int i=0; switch(7) { case 0 ... 7: i=1; break; case 8 ... 10: i=2; break; } i; })); ASSERT(1, ({ int i=0; switch(7) { case 0: i=1; break; case 7 ... 7: i=1; break; } i; })); + ASSERT(3, ({ void *p = &&v11; int i=0; goto *p; v11:i++; v12:i++; v13:i++; i; })); + ASSERT(2, ({ void *p = &&v22; int i=0; goto *p; v21:i++; v22:i++; v23:i++; i; })); + ASSERT(1, ({ void *p = &&v33; int i=0; goto *p; v31:i++; v32:i++; v33:i++; i; })); + printf("OK\n"); return 0; } diff --git a/type.c b/type.c index 6c1a6d402..1d4400e0b 100644 --- a/type.c +++ b/type.c @@ -284,5 +284,8 @@ void add_type(Node *node) { } error_tok(node->tok, "statement expression returning void is not supported"); return; + case ND_LABEL_VAL: + node->ty = pointer_to(ty_void); + return; } }