Skip to content

Commit

Permalink
[GNU] Support case ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Dec 7, 2020
1 parent e0bf168 commit d90c73b
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 7 deletions.
6 changes: 5 additions & 1 deletion chibicc.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,14 @@ struct Node {
char *unique_label;
Node *goto_next;

// Switch-cases
// Switch
Node *case_next;
Node *default_case;

// Case
long begin;
long end;

// "asm" string literal
char *asm_str;

Expand Down
17 changes: 14 additions & 3 deletions codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -1160,9 +1160,20 @@ static void gen_stmt(Node *node) {
gen_expr(node->cond);

for (Node *n = node->case_next; n; n = n->case_next) {
char *reg = (node->cond->ty->size == 8) ? "%rax" : "%eax";
println(" cmp $%ld, %s", n->val, reg);
println(" je %s", n->label);
char *ax = (node->cond->ty->size == 8) ? "%rax" : "%eax";
char *di = (node->cond->ty->size == 8) ? "%rdi" : "%edi";

if (n->begin == n->end) {
println(" cmp $%ld, %s", n->begin, ax);
println(" je %s", n->label);
continue;
}

// [GNU] Case ranges
println(" mov %s, %s", ax, di);
println(" sub $%ld, %s", n->begin, di);
println(" cmp $%ld, %s", n->end - n->begin, di);
println(" jbe %s", n->label);
}

if (node->default_case)
Expand Down
18 changes: 15 additions & 3 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -1507,7 +1507,7 @@ static Node *asm_stmt(Token **rest, Token *tok) {
// stmt = "return" expr? ";"
// | "if" "(" expr ")" stmt ("else" stmt)?
// | "switch" "(" expr ")" stmt
// | "case" const-expr ":" stmt
// | "case" const-expr ("..." const-expr)? ":" stmt
// | "default" ":" stmt
// | "for" "(" expr-stmt expr? ";" expr? ")" stmt
// | "while" "(" expr ")" stmt
Expand Down Expand Up @@ -1573,11 +1573,23 @@ static Node *stmt(Token **rest, Token *tok) {
error_tok(tok, "stray case");

Node *node = new_node(ND_CASE, tok);
int val = const_expr(&tok, tok->next);
int begin = const_expr(&tok, tok->next);
int end;

if (equal(tok, "...")) {
// [GNU] Case ranges, e.g. "case 1 ... 5:"
end = const_expr(&tok, tok->next);
if (end < begin)
error_tok(tok, "empty case range specified");
} else {
end = begin;
}

tok = skip(tok, ":");
node->label = new_unique_name();
node->lhs = stmt(rest, tok);
node->val = val;
node->begin = begin;
node->end = end;
node->case_next = current_switch->case_next;
current_switch->case_next = node;
return node;
Expand Down
4 changes: 4 additions & 0 deletions test/control.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ int main() {
ASSERT(10, ({ double i=10.0; int j=0; for (; i; i--, j++); j; }));
ASSERT(10, ({ double i=10.0; int j=0; do j++; while(--i); j; }));

ASSERT(2, ({ int i=0; switch(7) { case 0 ... 5: i=1; break; case 6 ... 20: i=2; break; } i; }));
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; }));

printf("OK\n");
return 0;
}

0 comments on commit d90c73b

Please sign in to comment.