mirror of
https://github.com/ziglang/zig.git
synced 2026-01-04 04:25:05 +00:00
IR: support inline switch
This commit is contained in:
parent
24b65e41ee
commit
4619b5de06
@ -73,7 +73,7 @@ AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&
|
||||
|
||||
BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression
|
||||
|
||||
SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}"
|
||||
SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}"
|
||||
|
||||
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
|
||||
|
||||
|
||||
@ -557,6 +557,7 @@ struct AstNodeForExpr {
|
||||
struct AstNodeSwitchExpr {
|
||||
AstNode *expr;
|
||||
ZigList<AstNode *> prongs;
|
||||
bool is_inline;
|
||||
|
||||
// populated by semantic analyzer
|
||||
Expr resolved_expr;
|
||||
|
||||
34
src/ir.cpp
34
src/ir.cpp
@ -2264,7 +2264,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) {
|
||||
|
||||
size_t prong_count = node->data.switch_expr.prongs.length;
|
||||
ZigList<IrInstructionSwitchBrCase> cases = {0};
|
||||
bool is_inline = (node->block_context->fn_entry == nullptr);
|
||||
bool is_inline = node->data.switch_expr.is_inline || (node->block_context->fn_entry == nullptr);
|
||||
|
||||
ZigList<IrInstruction *> incoming_values = {0};
|
||||
ZigList<IrBasicBlock *> incoming_blocks = {0};
|
||||
@ -5249,7 +5249,37 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
|
||||
bool is_inline = switch_br_instruction->is_inline;
|
||||
|
||||
if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) {
|
||||
zig_panic("TODO compile time switch br");
|
||||
ConstExprValue *target_val = ir_resolve_const(ira, target_value);
|
||||
if (!target_val)
|
||||
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
|
||||
|
||||
for (size_t i = 0; i < case_count; i += 1) {
|
||||
IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i];
|
||||
IrInstruction *case_value = old_case->value->other;
|
||||
if (case_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
|
||||
|
||||
IrInstruction *casted_case_value = ir_get_casted_value(ira, case_value, target_value->type_entry);
|
||||
if (casted_case_value->type_entry->id == TypeTableEntryIdInvalid)
|
||||
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
|
||||
|
||||
ConstExprValue *case_val = ir_resolve_const(ira, casted_case_value);
|
||||
if (!case_val)
|
||||
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
|
||||
|
||||
if (const_values_equal(target_val, case_val, target_value->type_entry)) {
|
||||
IrBasicBlock *old_dest_block = old_case->block;
|
||||
if (is_inline || old_dest_block->ref_count == 1) {
|
||||
ir_inline_bb(ira, old_dest_block);
|
||||
return ira->codegen->builtin_types.entry_unreachable;
|
||||
} else {
|
||||
IrBasicBlock *new_dest_block = ir_get_new_bb(ira, old_dest_block);
|
||||
ir_build_br_from(&ira->new_irb, &switch_br_instruction->base, new_dest_block);
|
||||
return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count);
|
||||
|
||||
@ -1642,23 +1642,36 @@ static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool m
|
||||
}
|
||||
|
||||
/*
|
||||
SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"
|
||||
SwitchExpression = option("inline") "switch" "(" Expression ")" "{" many(SwitchProng) "}"
|
||||
SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Symbol "|") Expression ","
|
||||
SwitchItem : Expression | (Expression "..." Expression)
|
||||
*/
|
||||
static AstNode *ast_parse_switch_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||
Token *token = &pc->tokens->at(*token_index);
|
||||
|
||||
if (token->id != TokenIdKeywordSwitch) {
|
||||
if (mandatory) {
|
||||
ast_expect_token(pc, token, TokenIdKeywordSwitch);
|
||||
Token *first_token = &pc->tokens->at(*token_index);
|
||||
Token *switch_token;
|
||||
bool is_inline;
|
||||
if (first_token->id == TokenIdKeywordInline) {
|
||||
is_inline = true;
|
||||
switch_token = &pc->tokens->at(*token_index + 1);
|
||||
if (switch_token->id == TokenIdKeywordSwitch) {
|
||||
*token_index += 2;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, first_token, TokenIdKeywordSwitch);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (first_token->id == TokenIdKeywordSwitch) {
|
||||
is_inline = false;
|
||||
switch_token = first_token;
|
||||
*token_index += 1;
|
||||
} else if (mandatory) {
|
||||
ast_expect_token(pc, first_token, TokenIdKeywordSwitch);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
*token_index += 1;
|
||||
|
||||
AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, token);
|
||||
AstNode *node = ast_create_node(pc, NodeTypeSwitchExpr, switch_token);
|
||||
node->data.switch_expr.is_inline = is_inline;
|
||||
|
||||
ast_eat_token(pc, token_index, TokenIdLParen);
|
||||
node->data.switch_expr.expr = ast_parse_expression(pc, token_index, true);
|
||||
|
||||
@ -49,6 +49,18 @@ fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 {
|
||||
}
|
||||
}
|
||||
|
||||
fn testInlineSwitch() {
|
||||
const x = 3 + 4;
|
||||
const result = inline switch (x) {
|
||||
3 => 10,
|
||||
4 => 11,
|
||||
5, 6 => 12,
|
||||
7, 8 => 13,
|
||||
else => 14,
|
||||
};
|
||||
assert(result + 1 == 14);
|
||||
}
|
||||
|
||||
fn assert(ok: bool) {
|
||||
if (!ok)
|
||||
@unreachable();
|
||||
@ -60,6 +72,7 @@ fn runAllTests() {
|
||||
inlinedLoop();
|
||||
switchWithNumbers();
|
||||
switchWithAllRanges();
|
||||
testInlineSwitch();
|
||||
}
|
||||
|
||||
export nakedcc fn _start() -> unreachable {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user