authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-11-26 15:38:07-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-11-26 15:38:07-05:00
log4619b5de0609e98c0c98fe352f3bc32f036b6ad4
treeb58718b204a63ba21f48d7eeac067baee53e903f
parent24b65e41ee241c805e0eff8212ef49c5c39e4b8e

IR: support inline switch


5 files changed, 68 insertions(+), 11 deletions(-)

doc/langref.md+1-1
...@@ -73,7 +73,7 @@ AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&...@@ -73,7 +73,7 @@ AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&
7373
74BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression74BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression
7575
76SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}"76SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}"
7777
78SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","78SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
7979
src/all_types.hpp+1
...@@ -557,6 +557,7 @@ struct AstNodeForExpr {...@@ -557,6 +557,7 @@ struct AstNodeForExpr {
557struct AstNodeSwitchExpr {557struct AstNodeSwitchExpr {
558 AstNode *expr;558 AstNode *expr;
559 ZigList<AstNode *> prongs;559 ZigList<AstNode *> prongs;
560 bool is_inline;
560561
561 // populated by semantic analyzer562 // populated by semantic analyzer
562 Expr resolved_expr;563 Expr resolved_expr;
src/ir.cpp+32-2
...@@ -2264,7 +2264,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) {...@@ -2264,7 +2264,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) {
22642264
2265 size_t prong_count = node->data.switch_expr.prongs.length;2265 size_t prong_count = node->data.switch_expr.prongs.length;
2266 ZigList<IrInstructionSwitchBrCase> cases = {0};2266 ZigList<IrInstructionSwitchBrCase> cases = {0};
2267 bool is_inline = (node->block_context->fn_entry == nullptr);2267 bool is_inline = node->data.switch_expr.is_inline || (node->block_context->fn_entry == nullptr);
22682268
2269 ZigList<IrInstruction *> incoming_values = {0};2269 ZigList<IrInstruction *> incoming_values = {0};
2270 ZigList<IrBasicBlock *> incoming_blocks = {0};2270 ZigList<IrBasicBlock *> incoming_blocks = {0};
...@@ -5249,7 +5249,37 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -5249,7 +5249,37 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
5249 bool is_inline = switch_br_instruction->is_inline;5249 bool is_inline = switch_br_instruction->is_inline;
52505250
5251 if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) {5251 if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) {
5252 zig_panic("TODO compile time switch br");5252 ConstExprValue *target_val = ir_resolve_const(ira, target_value);
5253 if (!target_val)
5254 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
5255
5256 for (size_t i = 0; i < case_count; i += 1) {
5257 IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
5258 IrInstruction *case_value = old_case->value->other;
5259 if (case_value->type_entry->id == TypeTableEntryIdInvalid)
5260 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
5261
5262 IrInstruction *casted_case_value = ir_get_casted_value(ira, case_value, target_value->type_entry);
5263 if (casted_case_value->type_entry->id == TypeTableEntryIdInvalid)
5264 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
5265
5266 ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value);
5267 if (!case_val)
5268 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
5269
5270 if (const_values_equal(target_val, case_val, target_value->type_entry)) {
5271 IrBasicBlock *old_dest_block = old_case->block;
5272 if (is_inline || old_dest_block->ref_count == 1) {
5273 ir_inline_bb(ira, old_dest_block);
5274 return ira->codegen->builtin_types.entry_unreachable;
5275 } else {
5276 IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block);
5277 ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block);
5278 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
5279 }
5280 }
5281 }
5282
5253 }5283 }
52545284
5255 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count);5285 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count);
src/parser.cpp+21-8
...@@ -1642,23 +1642,36 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m...@@ -1642,23 +1642,36 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m
1642}1642}
16431643
1644/*1644/*
1645SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"1645SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}"
1646SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","1646SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
1647SwitchItem : Expression | (Expression "..." Expression)1647SwitchItem : Expression | (Expression "..." Expression)
1648*/1648*/
1649static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) {1649static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
1650 Token *token = &pc->tokens->at(*token_index);1650 Token *first_token = &pc->tokens->at(*token_index);
16511651 Token *switch_token;
1652 if (token->id != TokenIdKeywordSwitch) {1652 bool is_inline;
1653 if (mandatory) {1653 if (first_token->id == TokenIdKeywordInline) {
1654 ast_expect_token(pc, token, TokenIdKeywordSwitch);1654 is_inline = true;
1655 switch_token = &pc->tokens->at(*token_index + 1);
1656 if (switch_token->id == TokenIdKeywordSwitch) {
1657 *token_index += 2;
1658 } else if (mandatory) {
1659 ast_expect_token(pc, first_token, TokenIdKeywordSwitch);
1655 } else {1660 } else {
1656 return nullptr;1661 return nullptr;
1657 }1662 }
1663 } else if (first_token->id == TokenIdKeywordSwitch) {
1664 is_inline = false;
1665 switch_token = first_token;
1666 *token_index += 1;
1667 } else if (mandatory) {
1668 ast_expect_token(pc, first_token, TokenIdKeywordSwitch);
1669 } else {
1670 return nullptr;
1658 }1671 }
1659 *token_index += 1;
16601672
1661 AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, token);1673 AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token);
1674 node->data.switch_expr.is_inline = is_inline;
16621675
1663 ast_eat_token(pc, token_index, TokenIdLParen);1676 ast_eat_token(pc, token_index, TokenIdLParen);
1664 node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true);1677 node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true);
test/self_hosted2.zig+13
...@@ -49,6 +49,18 @@ fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 {...@@ -49,6 +49,18 @@ fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 {
49 }49 }
50}50}
5151
52fn testInlineSwitch() {
53 const x = 3 + 4;
54 const result = inline switch (x) {
55 3 => 10,
56 4 => 11,
57 5, 6 => 12,
58 7, 8 => 13,
59 else => 14,
60 };
61 assert(result + 1 == 14);
62}
63
52fn assert(ok: bool) {64fn assert(ok: bool) {
53 if (!ok)65 if (!ok)
54 @unreachable();66 @unreachable();
...@@ -60,6 +72,7 @@ fn runAllTests() {...@@ -60,6 +72,7 @@ fn runAllTests() {
60 inlinedLoop();72 inlinedLoop();
61 switchWithNumbers();73 switchWithNumbers();
62 switchWithAllRanges();74 switchWithAllRanges();
75 testInlineSwitch();
63}76}
6477
65export nakedcc fn _start() -> unreachable {78export nakedcc fn _start() -> unreachable {