authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-11-26 23:16:38-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-11-26 23:16:38-05:00
loga52ede6494d44865fcf05591eaf5715d8bd1dc4a
tree54d336044dce404d17283f01c4643f83afa0572f
parenta3db60b5d726004cbb5d7235893a3e148b493096

IR: support goto and labels


4 files changed, 143 insertions(+), 172 deletions(-)

src/all_types.hpp+6-12
......@@ -37,6 +37,8 @@ struct IrExecutable {
3737 size_t mem_slot_count;
3838 size_t next_debug_id;
3939 bool invalid;
40 ZigList<LabelTableEntry *> all_labels;
41 ZigList<AstNode *> goto_list;
4042};
4143
4244enum OutType {
......@@ -554,16 +556,15 @@ struct AstNodeSwitchRange {
554556
555557struct AstNodeLabel {
556558 Buf *name;
557
558 // populated by semantic analyzer
559 LabelTableEntry *label_entry;
560559};
561560
562561struct AstNodeGoto {
563562 Buf *name;
563 bool is_inline;
564564
565565 // populated by semantic analyzer
566 LabelTableEntry *label_entry;
566 IrBasicBlock *bb;
567 size_t instruction_index;
567568};
568569
569570struct AsmOutput {
......@@ -1059,7 +1060,6 @@ struct FnTableEntry {
10591060 ImportTableEntry *import_entry;
10601061 // Required to be a pre-order traversal of the AST. (parents must come before children)
10611062 ZigList<BlockContext *> all_block_contexts;
1062 ZigList<LabelTableEntry *> all_labels;
10631063 Buf symbol_name;
10641064 TypeTableEntry *type_entry; // function type
10651065 bool internal_linkage;
......@@ -1082,7 +1082,6 @@ struct FnTableEntry {
10821082 ZigList<IrInstructionCast *> cast_alloca_list;
10831083 ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list;
10841084 ZigList<VariableTableEntry *> variable_list;
1085 ZigList<AstNode *> goto_list;
10861085};
10871086
10881087enum BuiltinFnId {
......@@ -1319,9 +1318,8 @@ struct ErrorTableEntry {
13191318
13201319struct LabelTableEntry {
13211320 AstNode *decl_node;
1322 LLVMBasicBlockRef basic_block;
1321 IrBasicBlock *bb;
13231322 bool used;
1324 bool entered_from_fallthrough;
13251323};
13261324
13271325struct BlockContext {
......@@ -1427,10 +1425,6 @@ struct IrInstruction {
14271425 size_t ref_count;
14281426 IrInstruction *other;
14291427 ReturnKnowledge return_knowledge;
1430 // if this is true, this instruction should not cause compile errors.
1431 // for example, we can write to a variable with src_is_const true but
1432 // gen_is_const false.
1433 bool gen_only;
14341428};
14351429
14361430struct IrInstructionCondBr {
src/ast_render.cpp+13-2
......@@ -356,6 +356,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
356356 switch (node->type) {
357357 case NodeTypeSwitchProng:
358358 case NodeTypeSwitchRange:
359 case NodeTypeLabel:
359360 zig_unreachable();
360361 case NodeTypeRoot:
361362 for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) {
......@@ -426,6 +427,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
426427 ar->indent += ar->indent_size;
427428 for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
428429 AstNode *statement = node->data.block.statements.at(i);
430 if (statement->type == NodeTypeLabel) {
431 ar->indent -= ar->indent_size;
432 print_indent(ar);
433 fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name));
434 ar->indent += ar->indent_size;
435 continue;
436 }
429437 print_indent(ar);
430438 render_node_grouped(ar, statement);
431439 if (i != node->data.block.statements.length - 1)
......@@ -771,6 +779,11 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
771779 fprintf(ar->f, "}");
772780 break;
773781 }
782 case NodeTypeGoto:
783 {
784 fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name));
785 break;
786 }
774787 case NodeTypeFnDecl:
775788 case NodeTypeParamDecl:
776789 case NodeTypeErrorValueDecl:
......@@ -781,8 +794,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
781794 case NodeTypeUse:
782795 case NodeTypeZeroesLiteral:
783796 case NodeTypeForExpr:
784 case NodeTypeLabel:
785 case NodeTypeGoto:
786797 case NodeTypeBreak:
787798 case NodeTypeContinue:
788799 zig_panic("TODO more ast rendering");
src/ir.cpp+106-158
......@@ -643,8 +643,8 @@ static IrInstruction *ir_build_phi_from(IrBuilder *irb, IrInstruction *old_instr
643643 return new_instruction;
644644}
645645
646static IrInstruction *ir_build_br(IrBuilder *irb, AstNode *source_node, IrBasicBlock *dest_block, bool is_inline) {
647 IrInstructionBr *br_instruction = ir_build_instruction<IrInstructionBr>(irb, source_node);
646static IrInstruction *ir_create_br(IrBuilder *irb, AstNode *source_node, IrBasicBlock *dest_block, bool is_inline) {
647 IrInstructionBr *br_instruction = ir_create_instruction<IrInstructionBr>(irb->exec, source_node);
648648 br_instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable;
649649 br_instruction->base.static_value.special = ConstValSpecialStatic;
650650 br_instruction->dest_block = dest_block;
......@@ -655,6 +655,12 @@ static IrInstruction *ir_build_br(IrBuilder *irb, AstNode *source_node, IrBasicB
655655 return &br_instruction->base;
656656}
657657
658static IrInstruction *ir_build_br(IrBuilder *irb, AstNode *source_node, IrBasicBlock *dest_block, bool is_inline) {
659 IrInstruction *instruction = ir_create_br(irb, source_node, dest_block, is_inline);
660 ir_instruction_append(irb->current_basic_block, instruction);
661 return instruction;
662}
663
658664static IrInstruction *ir_build_br_from(IrBuilder *irb, IrInstruction *old_instruction, IrBasicBlock *dest_block) {
659665 IrInstruction *new_instruction = ir_build_br(irb, old_instruction->source_node, dest_block, false);
660666 ir_link_new_instruction(new_instruction, old_instruction);
......@@ -2438,6 +2444,55 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) {
24382444 return ir_build_phi(irb, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
24392445}
24402446
2447static LabelTableEntry *find_label(IrExecutable *exec, BlockContext *orig_context, Buf *name) {
2448 BlockContext *context = orig_context;
2449 while (context) {
2450 auto entry = context->label_table.maybe_get(name);
2451 if (entry) {
2452 return entry->value;
2453 }
2454 context = context->parent;
2455 }
2456 return nullptr;
2457}
2458
2459static IrInstruction *ir_gen_label(IrBuilder *irb, AstNode *node) {
2460 assert(node->type == NodeTypeLabel);
2461
2462 Buf *label_name = node->data.label.name;
2463 IrBasicBlock *label_block = ir_build_basic_block(irb, buf_ptr(label_name));
2464 LabelTableEntry *label = allocate<LabelTableEntry>(1);
2465 label->decl_node = node;
2466 label->bb = label_block;
2467 irb->exec->all_labels.append(label);
2468
2469 LabelTableEntry *existing_label = find_label(irb->exec, node->block_context, label_name);
2470 if (existing_label) {
2471 ErrorMsg *msg = add_node_error(irb->codegen, node,
2472 buf_sprintf("duplicate label name '%s'", buf_ptr(label_name)));
2473 add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here"));
2474 return irb->codegen->invalid_instruction;
2475 } else {
2476 node->block_context->label_table.put(label_name, label);
2477 }
2478
2479 bool is_inline = (node->block_context->fn_entry == nullptr);
2480 ir_build_br(irb, node, label_block, is_inline);
2481 ir_set_cursor_at_end(irb, label_block);
2482 return ir_build_const_void(irb, node);
2483}
2484
2485static IrInstruction *ir_gen_goto(IrBuilder *irb, AstNode *node) {
2486 assert(node->type == NodeTypeGoto);
2487
2488 // make a placeholder unreachable statement and a note to come back and
2489 // replace the instruction with a branch instruction
2490 node->data.goto_expr.bb = irb->current_basic_block;
2491 node->data.goto_expr.instruction_index = irb->current_basic_block->instruction_list.length;
2492 irb->exec->goto_list.append(node);
2493 return ir_build_unreachable(irb, node);
2494}
2495
24412496static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContext *block_context,
24422497 LValPurpose lval)
24432498{
......@@ -2491,13 +2546,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, BlockContex
24912546 return ir_gen_if_var_expr(irb, node);
24922547 case NodeTypeSwitchExpr:
24932548 return ir_gen_switch_expr(irb, node);
2549 case NodeTypeLabel:
2550 return ir_gen_label(irb, node);
2551 case NodeTypeGoto:
2552 return ir_gen_goto(irb, node);
24942553 case NodeTypeUnwrapErrorExpr:
24952554 case NodeTypeDefer:
24962555 case NodeTypeSliceExpr:
2497 case NodeTypeGoto:
24982556 case NodeTypeBreak:
24992557 case NodeTypeContinue:
2500 case NodeTypeLabel:
25012558 case NodeTypeCharLiteral:
25022559 case NodeTypeZeroesLiteral:
25032560 case NodeTypeErrorType:
......@@ -2533,11 +2590,46 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, BlockContext *s
25332590 return ir_gen_node_extra(irb, node, scope, LValPurposeNone);
25342591}
25352592
2593static bool ir_goto_pass2(IrBuilder *irb) {
2594 for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) {
2595 AstNode *goto_node = irb->exec->goto_list.at(i);
2596 size_t instruction_index = goto_node->data.goto_expr.instruction_index;
2597 IrInstruction **slot = &goto_node->data.goto_expr.bb->instruction_list.at(instruction_index);
2598 IrInstruction *old_instruction = *slot;
2599
2600 Buf *label_name = goto_node->data.goto_expr.name;
2601 LabelTableEntry *label = find_label(irb->exec, goto_node->block_context, label_name);
2602 if (!label) {
2603 add_node_error(irb->codegen, goto_node,
2604 buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
2605 return false;
2606 }
2607 label->used = true;
2608
2609 bool is_inline = goto_node->data.goto_expr.is_inline || (goto_node->block_context->fn_entry == nullptr);
2610 IrInstruction *new_instruction = ir_create_br(irb, goto_node, label->bb, is_inline);
2611 new_instruction->ref_count = old_instruction->ref_count;
2612 *slot = new_instruction;
2613 }
2614
2615 for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) {
2616 LabelTableEntry *label = irb->exec->all_labels.at(i);
2617 if (!label->used) {
2618 add_node_error(irb->codegen, label->decl_node,
2619 buf_sprintf("label '%s' defined but not used",
2620 buf_ptr(label->decl_node->data.label.name)));
2621 return false;
2622 }
2623 }
2624
2625 return true;
2626}
2627
25362628IrInstruction *ir_gen(CodeGen *codegen, AstNode *node, BlockContext *scope, IrExecutable *ir_executable) {
25372629 assert(node->owner);
25382630
2539 IrBuilder ir_gen = {0};
2540 IrBuilder *irb = &ir_gen;
2631 IrBuilder ir_builder = {0};
2632 IrBuilder *irb = &ir_builder;
25412633
25422634 irb->codegen = codegen;
25432635 irb->exec = ir_executable;
......@@ -2549,10 +2641,16 @@ IrInstruction *ir_gen(CodeGen *codegen, AstNode *node, BlockContext *scope, IrEx
25492641 IrInstruction *result = ir_gen_node_extra(irb, node, scope, LValPurposeNone);
25502642 assert(result);
25512643
2644 IrInstruction *return_instruction = ir_build_return(irb, result->source_node, result);
2645 assert(return_instruction);
2646
25522647 if (result == codegen->invalid_instruction)
2553 return result;
2648 return codegen->invalid_instruction;
25542649
2555 return ir_build_return(irb, result->source_node, result);
2650 if (!ir_goto_pass2(irb))
2651 return codegen->invalid_instruction;
2652
2653 return return_instruction;
25562654}
25572655
25582656IrInstruction *ir_gen_fn(CodeGen *codegn, FnTableEntry *fn_entry) {
......@@ -7241,19 +7339,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
72417339// }
72427340// zig_unreachable();
72437341//}
7244//static TypeTableEntry *analyze_goto_pass1(CodeGen *g, ImportTableEntry *import, BlockContext *context,
7245// TypeTableEntry *expected_type, AstNode *node)
7246//{
7247// assert(node->type == NodeTypeGoto);
7248//
7249// FnTableEntry *fn_table_entry = context->fn_entry;
7250// assert(fn_table_entry);
7251//
7252// fn_table_entry->goto_list.append(node);
7253//
7254// return g->builtin_types.entry_unreachable;
7255//}
7256//
72577342//static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
72587343// AstNode *field_access_node, AstNode *value_node, TypeTableEntry *enum_type, Buf *field_name,
72597344// AstNode *out_node)
......@@ -7710,69 +7795,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
77107795// return g->builtin_types.entry_void;
77117796//}
77127797//
7713//static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
7714// TypeTableEntry *expected_type, AstNode *node)
7715//{
7716// BlockContext *child_context = new_block_context(node, parent_context);
7717// node->data.block.child_block = child_context;
7718// TypeTableEntry *return_type = g->builtin_types.entry_void;
7719//
7720// for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
7721// AstNode *child = node->data.block.statements.at(i);
7722// if (child->type == NodeTypeLabel) {
7723// FnTableEntry *fn_table_entry = child_context->fn_entry;
7724// assert(fn_table_entry);
7725//
7726// LabelTableEntry *label = allocate<LabelTableEntry>(1);
7727// label->decl_node = child;
7728// label->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable);
7729//
7730// child->block_context = child_context;
7731// child->data.label.label_entry = label;
7732// fn_table_entry->all_labels.append(label);
7733//
7734// child_context->label_table.put(child->data.label.name, label);
7735//
7736// return_type = g->builtin_types.entry_void;
7737// continue;
7738// }
7739// if (return_type->id == TypeTableEntryIdUnreachable) {
7740// if (is_node_void_expr(child)) {
7741// // {unreachable;void;void} is allowed.
7742// // ignore void statements once we enter unreachable land.
7743// analyze_expression(g, import, child_context, g->builtin_types.entry_void, child);
7744// continue;
7745// }
7746// add_node_error(g, first_executing_node(child), buf_sprintf("unreachable code"));
7747// break;
7748// }
7749// bool is_last = (i == node->data.block.statements.length - 1);
7750// TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr;
7751// return_type = analyze_expression(g, import, child_context, passed_expected_type, child);
7752// if (child->type == NodeTypeDefer && return_type->id != TypeTableEntryIdInvalid) {
7753// // defer starts a new block context
7754// child_context = child->data.defer.child_block;
7755// assert(child_context);
7756// }
7757// if (!is_last) {
7758// validate_voided_expr(g, child, return_type);
7759// }
7760// }
7761// node->data.block.nested_block = child_context;
7762//
7763// ConstExprValue *const_val = &node->data.block.resolved_expr.const_val;
7764// if (node->data.block.statements.length == 0) {
7765// const_val->ok = true;
7766// } else if (node->data.block.statements.length == 1) {
7767// AstNode *only_node = node->data.block.statements.at(0);
7768// ConstExprValue *other_const_val = &get_resolved_expr(only_node)->const_val;
7769// if (other_const_val->ok) {
7770// *const_val = *other_const_val;
7771// }
7772// }
7773//
7774// return return_type;
7775//}
77767798//
77777799//static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import,
77787800// BlockContext *context, AstNode *node, Buf *err_name)
......@@ -7841,51 +7863,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
78417863//
78427864// return ir_build_return(irb, source_node, value);
78437865//}
7844/*
7845static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *node) {
7846 assert(node->type == NodeTypeGoto);
7847 Buf *label_name = node->data.goto_expr.name;
7848 BlockContext *context = node->block_context;
7849 assert(context);
7850 LabelTableEntry *label = find_label(g, context, label_name);
7851
7852 if (!label) {
7853 add_node_error(g, node, buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
7854 return;
7855 }
7856
7857 label->used = true;
7858 node->data.goto_expr.label_entry = label;
7859}
7860
7861 for (size_t i = 0; i < fn_table_entry->goto_list.length; i += 1) {
7862 AstNode *goto_node = fn_table_entry->goto_list.at(i);
7863 assert(goto_node->type == NodeTypeGoto);
7864 analyze_goto_pass2(g, import, goto_node);
7865 }
7866
7867 for (size_t i = 0; i < fn_table_entry->all_labels.length; i += 1) {
7868 LabelTableEntry *label = fn_table_entry->all_labels.at(i);
7869 if (!label->used) {
7870 add_node_error(g, label->decl_node,
7871 buf_sprintf("label '%s' defined but not used",
7872 buf_ptr(label->decl_node->data.label.name)));
7873 }
7874 }
7875*/
7876
7877//static LabelTableEntry *find_label(CodeGen *g, BlockContext *orig_context, Buf *name) {
7878// BlockContext *context = orig_context;
7879// while (context && context->fn_entry) {
7880// auto entry = context->label_table.maybe_get(name);
7881// if (entry) {
7882// return entry->value;
7883// }
7884// context = context->parent;
7885// }
7886// return nullptr;
7887//}
7888
78897866//
78907867//static LLVMValueRef gen_err_name(CodeGen *g, AstNode *node) {
78917868// assert(node->type == NodeTypeFnCallExpr);
......@@ -9180,20 +9157,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
91809157// return nullptr;
91819158//}
91829159//
9183//static LLVMValueRef gen_goto(CodeGen *g, AstNode *node) {
9184// assert(node->type == NodeTypeGoto);
9185//
9186// // generate defers for blocks that we exit
9187// LabelTableEntry *label = node->data.goto_expr.label_entry;
9188// BlockContext *this_context = node->block_context;
9189// BlockContext *target_context = label->decl_node->block_context;
9190// gen_defers_for_block(g, this_context, target_context, false, false);
9191//
9192// set_debug_source_node(g, node);
9193// LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block);
9194// return nullptr;
9195//}
9196//
91979160//static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) {
91989161// AstNode *init_expr = node->data.variable_declaration.expr;
91999162// if (node->data.variable_declaration.is_const && init_expr) {
......@@ -9249,18 +9212,3 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
92499212// }
92509213// return result;
92519214//}
9252//
9253//static LLVMValueRef gen_label(CodeGen *g, AstNode *node) {
9254// assert(node->type == NodeTypeLabel);
9255//
9256// LabelTableEntry *label = node->data.label.label_entry;
9257// assert(label);
9258//
9259// LLVMBasicBlockRef basic_block = label->basic_block;
9260// if (label->entered_from_fallthrough) {
9261// set_debug_source_node(g, node);
9262// LLVMBuildBr(g->builder, basic_block);
9263// }
9264// LLVMPositionBuilderAtEnd(g->builder, basic_block);
9265// return nullptr;
9266//}
test/self_hosted2.zig+18
......@@ -67,6 +67,23 @@ fn testNamespaceFnCall() {
6767 assert(case_namespace_fn_call.foo() == 1234);
6868}
6969
70fn gotoAndLabels() {
71 gotoLoop();
72 assert(goto_counter == 10);
73}
74fn gotoLoop() {
75 var i: i32 = 0;
76 goto cond;
77loop:
78 i += 1;
79cond:
80 if (!(i < 10)) goto end;
81 goto_counter += 1;
82 goto loop;
83end:
84}
85var goto_counter: i32 = 0;
86
7087fn assert(ok: bool) {
7188 if (!ok)
7289 @unreachable();
......@@ -80,6 +97,7 @@ fn runAllTests() {
8097 switchWithAllRanges();
8198 testInlineSwitch();
8299 testNamespaceFnCall();
100 gotoAndLabels();
83101}
84102
85103export nakedcc fn _start() -> unreachable {