[GNU] Support case ranges

This commit is contained in:
Rui Ueyama 2020-08-30 12:51:05 +09:00
parent e0bf168041
commit d90c73b605
4 changed files with 38 additions and 7 deletions

View File

@ -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;

View File

@ -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)

18
parse.c
View File

@ -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
@ -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;

View File

@ -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;
}