diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-11-26 00:25:48 -0500 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-11-26 00:25:48 -0500 |
| commit | bbf785bc1d5740488521b0cb90eeae31090e67ae (patch) | |
| tree | 5933254eac0a0d958b5f42b504037a3ae8f7c991 | |
| parent | 0c22358cc1cccacb9a30929fbbc990ba9d82b6b3 (diff) | |
| download | zig-bbf785bc1d5740488521b0cb90eeae31090e67ae.tar.gz zig-bbf785bc1d5740488521b0cb90eeae31090e67ae.zip | |
IR: switch expression works with numbers
| -rw-r--r-- | src/all_types.hpp | 7 | ||||
| -rw-r--r-- | src/analyze.cpp | 126 | ||||
| -rw-r--r-- | src/ast_render.cpp | 46 | ||||
| -rw-r--r-- | src/codegen.cpp | 32 | ||||
| -rw-r--r-- | src/ir.cpp | 281 | ||||
| -rw-r--r-- | src/ir_print.cpp | 45 |
6 files changed, 372 insertions, 165 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index 19624156d9..1587315378 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1455,6 +1455,7 @@ enum IrInstructionId { IrInstructionIdSizeOf, IrInstructionIdTestNull, IrInstructionIdUnwrapMaybe, + IrInstructionIdEnumTag, IrInstructionIdClz, IrInstructionIdCtz, }; @@ -1801,6 +1802,12 @@ struct IrInstructionClz { IrInstruction *value; }; +struct IrInstructionEnumTag { + IrInstruction base; + + IrInstruction *value; +}; + enum LValPurpose { LValPurposeNone, LValPurposeAssign, diff --git a/src/analyze.cpp b/src/analyze.cpp index 56fa6fae63..ff01fbea8f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2108,44 +2108,6 @@ static bool type_has_codegen_value(TypeTableEntry *type_entry) { zig_unreachable(); } -static bool num_lit_fits_in_other_type(CodeGen *g, AstNode *literal_node, TypeTableEntry *other_type) { - TypeTableEntry *other_type_underlying = get_underlying_type(other_type); - - if (other_type_underlying->id == TypeTableEntryIdInvalid) { - return false; - } - - Expr *expr = get_resolved_expr(literal_node); - ConstExprValue *const_val = &expr->instruction->static_value; - assert(const_val->special != ConstValSpecialRuntime); - if (other_type_underlying->id == TypeTableEntryIdFloat) { - return true; - } else if (other_type_underlying->id == TypeTableEntryIdInt && - const_val->data.x_bignum.kind == BigNumKindInt) - { - if (bignum_fits_in_bits(&const_val->data.x_bignum, other_type_underlying->data.integral.bit_count, - other_type_underlying->data.integral.is_signed)) - { - return true; - } - } else if ((other_type_underlying->id == TypeTableEntryIdNumLitFloat && - const_val->data.x_bignum.kind == BigNumKindFloat) || - (other_type_underlying->id == TypeTableEntryIdNumLitInt && - const_val->data.x_bignum.kind == BigNumKindInt)) - { - return true; - } - - const char *num_lit_str = (const_val->data.x_bignum.kind == BigNumKindFloat) ? "float" : "integer"; - - add_node_error(g, literal_node, - buf_sprintf("%s value %s cannot be implicitly casted to type '%s'", - num_lit_str, - buf_ptr(bignum_to_buf(&const_val->data.x_bignum)), - buf_ptr(&other_type->name))); - return false; -} - bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *actual_type) { if (expected_type == actual_type) return true; @@ -2233,94 +2195,6 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * return false; } -static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_type, - TypeTableEntry *actual_type, AstNode *literal_node, bool *reported_err) -{ - if (types_match_const_cast_only(expected_type, actual_type)) { - return true; - } - - // implicit conversion from non maybe type to maybe type - if (expected_type->id == TypeTableEntryIdMaybe && - types_match_with_implicit_cast(g, expected_type->data.maybe.child_type, actual_type, - literal_node, reported_err)) - { - return true; - } - - // implicit conversion from null literal to maybe type - if (expected_type->id == TypeTableEntryIdMaybe && - actual_type->id == TypeTableEntryIdNullLit) - { - return true; - } - - // implicit conversion from error child type to error type - if (expected_type->id == TypeTableEntryIdErrorUnion && - types_match_with_implicit_cast(g, expected_type->data.error.child_type, actual_type, - literal_node, reported_err)) - { - return true; - } - - // implicit conversion from pure error to error union type - if (expected_type->id == TypeTableEntryIdErrorUnion && - actual_type->id == TypeTableEntryIdPureError) - { - return true; - } - - // implicit widening conversion - if (expected_type->id == TypeTableEntryIdInt && - actual_type->id == TypeTableEntryIdInt && - expected_type->data.integral.is_signed == actual_type->data.integral.is_signed && - expected_type->data.integral.bit_count >= actual_type->data.integral.bit_count) - { - return true; - } - - // small enough unsigned ints can get casted to large enough signed ints - if (expected_type->id == TypeTableEntryIdInt && expected_type->data.integral.is_signed && - actual_type->id == TypeTableEntryIdInt && !actual_type->data.integral.is_signed && - expected_type->data.integral.bit_count > actual_type->data.integral.bit_count) - { - return true; - } - - // implicit float widening conversion - if (expected_type->id == TypeTableEntryIdFloat && - actual_type->id == TypeTableEntryIdFloat && - expected_type->data.floating.bit_count >= actual_type->data.floating.bit_count) - { - return true; - } - - // implicit array to slice conversion - if (expected_type->id == TypeTableEntryIdStruct && - expected_type->data.structure.is_slice && - actual_type->id == TypeTableEntryIdArray && - types_match_const_cast_only( - expected_type->data.structure.fields[0].type_entry->data.pointer.child_type, - actual_type->data.array.child_type)) - { - return true; - } - - // implicit number literal to typed number - if ((actual_type->id == TypeTableEntryIdNumLitFloat || - actual_type->id == TypeTableEntryIdNumLitInt)) - { - if (num_lit_fits_in_other_type(g, literal_node, expected_type)) { - return true; - } else { - *reported_err = true; - } - } - - - return false; -} - BlockContext *new_block_context(AstNode *node, BlockContext *parent) { BlockContext *context = allocate<BlockContext>(1); context->node = node; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 1636340a1c..fb7f6bc5cf 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -354,6 +354,9 @@ static void render_node_ungrouped(AstRender *ar, AstNode *node) { static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { switch (node->type) { + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + zig_unreachable(); case NodeTypeRoot: for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) { AstNode *child = node->data.root.top_level_decls.at(i); @@ -728,6 +731,47 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { break; } case NodeTypeSwitchExpr: + { + AstNodeSwitchExpr *switch_expr = &node->data.switch_expr; + fprintf(ar->f, "switch ("); + render_node_grouped(ar, switch_expr->expr); + fprintf(ar->f, ") {\n"); + ar->indent += ar->indent_size; + + for (size_t prong_i = 0; prong_i < switch_expr->prongs.length; prong_i += 1) { + AstNode *prong_node = switch_expr->prongs.at(prong_i); + AstNodeSwitchProng *switch_prong = &prong_node->data.switch_prong; + print_indent(ar); + for (size_t item_i = 0; item_i < switch_prong->items.length; item_i += 1) { + AstNode *item_node = switch_prong->items.at(item_i); + if (item_i != 0) + fprintf(ar->f, ", "); + if (item_node->type == NodeTypeSwitchRange) { + AstNode *start_node = item_node->data.switch_range.start; + AstNode *end_node = item_node->data.switch_range.end; + render_node_grouped(ar, start_node); + fprintf(ar->f, "..."); + render_node_grouped(ar, end_node); + } else { + render_node_grouped(ar, item_node); + } + } + const char *else_str = (switch_prong->items.length == 0) ? "else" : ""; + fprintf(ar->f, "%s => ", else_str); + if (switch_prong->var_symbol) { + const char *star_str = switch_prong->var_is_ptr ? "*" : ""; + Buf *var_name = switch_prong->var_symbol->data.symbol_expr.symbol; + fprintf(ar->f, "|%s%s| ", star_str, buf_ptr(var_name)); + } + render_node_grouped(ar, switch_prong->expr); + fprintf(ar->f, ",\n"); + } + + ar->indent -= ar->indent_size; + print_indent(ar); + fprintf(ar->f, "}"); + break; + } case NodeTypeFnDecl: case NodeTypeParamDecl: case NodeTypeErrorValueDecl: @@ -738,8 +782,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeUse: case NodeTypeZeroesLiteral: case NodeTypeForExpr: - case NodeTypeSwitchProng: - case NodeTypeSwitchRange: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: diff --git a/src/codegen.cpp b/src/codegen.cpp index f7df4a2bc5..434e0a452e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1638,6 +1638,31 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru return LLVMBuildCall(g->builder, fn_val, params, 2, ""); } +static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) { + assert(!instruction->is_inline); + + LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); + LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, instruction->case_count); + for (size_t i = 0; i < instruction->case_count; i += 1) { + IrInstructionSwitchBrCase *this_case = &instruction->cases[i]; + LLVMAddCase(switch_instr, ir_llvm_value(g, this_case->value), this_case->block->llvm_block); + } + return nullptr; +} + +static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstructionPhi *instruction) { + LLVMValueRef phi = LLVMBuildPhi(g->builder, instruction->base.type_entry->type_ref, ""); + LLVMValueRef *incoming_values = allocate<LLVMValueRef>(instruction->incoming_count); + LLVMBasicBlockRef *incoming_blocks = allocate<LLVMBasicBlockRef>(instruction->incoming_count); + for (size_t i = 0; i < instruction->incoming_count; i += 1) { + incoming_values[i] = ir_llvm_value(g, instruction->incoming_values[i]); + incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_block; + } + LLVMAddIncoming(phi, incoming_values, incoming_blocks, instruction->incoming_count); + return phi; +} + static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, IrInstruction *instruction) { set_debug_source_node(g, instruction->source_node); @@ -1655,6 +1680,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdSliceType: case IrInstructionIdCompileVar: case IrInstructionIdSizeOf: + case IrInstructionIdSwitchTarget: zig_unreachable(); case IrInstructionIdReturn: return ir_render_return(g, executable, (IrInstructionReturn *)instruction); @@ -1695,12 +1721,14 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdCtz: return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction); case IrInstructionIdSwitchBr: - case IrInstructionIdSwitchTarget: - case IrInstructionIdSwitchVar: + return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction); case IrInstructionIdPhi: + return ir_render_phi(g, executable, (IrInstructionPhi *)instruction); + case IrInstructionIdSwitchVar: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: case IrInstructionIdReadField: + case IrInstructionIdEnumTag: zig_panic("TODO render more IR instructions to LLVM"); } zig_unreachable(); diff --git a/src/ir.cpp b/src/ir.cpp index 91b0b7a05d..605574d6cc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -88,16 +88,21 @@ static void ir_ref_var(VariableTableEntry *var) { var->ref_count += 1; } -static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint) { +static IrBasicBlock *ir_build_basic_block_raw(IrBuilder *irb, const char *name_hint) { IrBasicBlock *result = allocate<IrBasicBlock>(1); result->name_hint = name_hint; result->debug_id = exec_next_debug_id(irb->exec); + return result; +} + +static IrBasicBlock *ir_build_basic_block(IrBuilder *irb, const char *name_hint) { + IrBasicBlock *result = ir_build_basic_block_raw(irb, name_hint); irb->exec->basic_block_list.append(result); return result; } static IrBasicBlock *ir_build_bb_from(IrBuilder *irb, IrBasicBlock *other_bb) { - IrBasicBlock *new_bb = ir_build_basic_block(irb, other_bb->name_hint); + IrBasicBlock *new_bb = ir_build_basic_block_raw(irb, other_bb->name_hint); ir_link_new_bb(new_bb, other_bb); return new_bb; } @@ -254,6 +259,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) { return IrInstructionIdCtz; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionEnumTag *) { + return IrInstructionIdEnumTag; +} + template<typename T> static T *ir_create_instruction(IrExecutable *exec, AstNode *source_node) { T *special_instruction = allocate<T>(1); @@ -612,6 +621,9 @@ static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_inst static IrInstruction *ir_build_phi(IrBuilder *irb, AstNode *source_node, size_t incoming_count, IrBasicBlock **incoming_blocks, IrInstruction **incoming_values) { + assert(incoming_count != 0); + assert(incoming_count != SIZE_MAX); + IrInstructionPhi *phi_instruction = ir_build_instruction<IrInstructionPhi>(irb, source_node); phi_instruction->incoming_count = incoming_count; phi_instruction->incoming_blocks = incoming_blocks; @@ -993,6 +1005,8 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, AstNode *source_node, I IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, bool is_inline) { IrInstructionSwitchBr *instruction = ir_build_instruction<IrInstructionSwitchBr>(irb, source_node); + instruction->base.type_entry = irb->codegen->builtin_types.entry_unreachable; + instruction->base.static_value.special = ConstValSpecialStatic; instruction->target_value = target_value; instruction->else_block = else_block; instruction->case_count = case_count; @@ -1010,6 +1024,16 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, AstNode *source_node, I return &instruction->base; } +static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction, + IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count, + IrInstructionSwitchBrCase *cases, bool is_inline) +{ + IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->source_node, + target_value, else_block, case_count, cases, is_inline); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_switch_target(IrBuilder *irb, AstNode *source_node, IrInstruction *target_value_ptr) { @@ -1034,6 +1058,21 @@ static IrInstruction *ir_build_switch_var(IrBuilder *irb, AstNode *source_node, return &instruction->base; } +static IrInstruction *ir_build_enum_tag(IrBuilder *irb, AstNode *source_node, IrInstruction *value) { + IrInstructionEnumTag *instruction = ir_build_instruction<IrInstructionEnumTag>(irb, source_node); + instruction->value = value; + + ir_ref_instruction(value); + + return &instruction->base; +} + +static IrInstruction *ir_build_enum_tag_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *value) { + IrInstruction *new_instruction = ir_build_enum_tag(irb, old_instruction->source_node, value); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static void ir_gen_defers_for_block(IrBuilder *irb, BlockContext *inner_block, BlockContext *outer_block, bool gen_error_defers, bool gen_maybe_defers) { @@ -2149,7 +2188,7 @@ static IrInstruction *ir_gen_if_var_expr(IrBuilder *irb, AstNode *node) { static bool ir_gen_switch_prong_expr(IrBuilder *irb, AstNode *switch_node, AstNode *prong_node, IrBasicBlock *end_block, bool is_inline, IrInstruction *target_value_ptr, IrInstruction *prong_value, - ZigList<IrBasicBlock *> incoming_blocks, ZigList<IrInstruction *> incoming_values) + ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values) { assert(switch_node->type == NodeTypeSwitchExpr); assert(prong_node->type == NodeTypeSwitchProng); @@ -2184,8 +2223,8 @@ static bool ir_gen_switch_prong_expr(IrBuilder *irb, AstNode *switch_node, AstNo if (expr_result == irb->codegen->invalid_instruction) return false; ir_build_br(irb, switch_node, end_block, is_inline); - incoming_blocks.append(irb->current_basic_block); - incoming_values.append(expr_result); + incoming_blocks->append(irb->current_basic_block); + incoming_values->append(expr_result); return true; } @@ -2222,11 +2261,14 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { } else_prong = prong_node; + IrBasicBlock *prev_block = irb->current_basic_block; + ir_set_cursor_at_end(irb, else_block); if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block, - is_inline, target_value_ptr, nullptr, incoming_blocks, incoming_values)) + is_inline, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } + ir_set_cursor_at_end(irb, prev_block); } else { if (prong_node->data.switch_prong.any_items_are_range) { IrInstruction *ok_bit = nullptr; @@ -2235,6 +2277,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); last_item_node = item_node; if (item_node->type == NodeTypeSwitchRange) { + item_node->block_context = node->block_context; AstNode *start_node = item_node->data.switch_range.start; AstNode *end_node = item_node->data.switch_range.end; @@ -2252,7 +2295,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { IrInstruction *both_ok = ir_build_bin_op(irb, item_node, IrBinOpBoolAnd, lower_range_ok, upper_range_ok); if (ok_bit) { - ok_bit = ir_build_bin_op(irb, item_node, IrBinOpBoolAnd, both_ok, ok_bit); + ok_bit = ir_build_bin_op(irb, item_node, IrBinOpBoolOr, both_ok, ok_bit); } else { ok_bit = both_ok; } @@ -2264,7 +2307,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { IrInstruction *cmp_ok = ir_build_bin_op(irb, item_node, IrBinOpCmpEq, item_value, target_value); if (ok_bit) { - ok_bit = ir_build_bin_op(irb, item_node, IrBinOpBoolAnd, cmp_ok, ok_bit); + ok_bit = ir_build_bin_op(irb, item_node, IrBinOpBoolOr, cmp_ok, ok_bit); } else { ok_bit = cmp_ok; } @@ -2280,13 +2323,16 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { ir_set_cursor_at_end(irb, range_block_yes); if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block, - is_inline, target_value_ptr, nullptr, incoming_blocks, incoming_values)) + is_inline, target_value_ptr, nullptr, &incoming_blocks, &incoming_values)) { return irb->codegen->invalid_instruction; } ir_set_cursor_at_end(irb, range_block_no); } else { + IrBasicBlock *prong_block = ir_build_basic_block(irb, "SwitchProng"); + IrInstruction *last_item_value = nullptr; + for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); assert(item_node->type != NodeTypeSwitchRange); @@ -2295,22 +2341,24 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, AstNode *node) { if (item_value == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; - IrBasicBlock *prong_block = ir_build_basic_block(irb, "SwitchProng"); - IrBasicBlock *prev_block = irb->current_basic_block; - ir_set_cursor_at_end(irb, prong_block); - - if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block, - is_inline, target_value_ptr, item_value, incoming_blocks, incoming_values)) - { - return irb->codegen->invalid_instruction; - } - IrInstructionSwitchBrCase *this_case = cases.add_one(); this_case->value = item_value; this_case->block = prong_block; - ir_set_cursor_at_end(irb, prev_block); + last_item_value = item_value; } + IrInstruction *only_item_value = (prong_item_count == 1) ? last_item_value : nullptr; + + IrBasicBlock *prev_block = irb->current_basic_block; + ir_set_cursor_at_end(irb, prong_block); + if (!ir_gen_switch_prong_expr(irb, node, prong_node, end_block, + is_inline, target_value_ptr, only_item_value, &incoming_blocks, &incoming_values)) + { + return irb->codegen->invalid_instruction; + } + + ir_set_cursor_at_end(irb, prev_block); + } } } @@ -2712,26 +2760,47 @@ static IrBasicBlock *ir_get_new_bb(IrAnalyze *ira, IrBasicBlock *old_bb) { if (old_bb->other) return old_bb->other; IrBasicBlock *new_bb = ir_build_bb_from(&ira->new_irb, old_bb); + + // We are about to enqueue old_bb for analysis. Before we do so, check old_bb + // for phi instructions. Any incoming blocks in the phi instructions need to be + // queued first. + for (size_t instr_i = 0; instr_i < old_bb->instruction_list.length; instr_i += 1) { + IrInstruction *instruction = old_bb->instruction_list.at(instr_i); + if (instruction->id != IrInstructionIdPhi) + break; + IrInstructionPhi *phi_instruction = (IrInstructionPhi *)instruction; + for (size_t incoming_i = 0; incoming_i < phi_instruction->incoming_count; incoming_i += 1) { + IrBasicBlock *predecessor = phi_instruction->incoming_blocks[incoming_i]; + ir_get_new_bb(ira, predecessor); + } + } ira->old_bb_queue.append(old_bb); + return new_bb; } +static void ir_start_bb(IrAnalyze *ira, IrBasicBlock *old_bb, IrBasicBlock *const_predecessor_bb) { + ira->instruction_index = 0; + ira->old_irb.current_basic_block = old_bb; + ira->const_predecessor_bb = const_predecessor_bb; + + assert(old_bb->other); + ira->new_irb.exec->basic_block_list.append(old_bb->other); +} + static void ir_finish_bb(IrAnalyze *ira) { ira->block_queue_index += 1; if (ira->block_queue_index < ira->old_bb_queue.length) { IrBasicBlock *old_bb = ira->old_bb_queue.at(ira->block_queue_index); - ira->instruction_index = 0; ira->new_irb.current_basic_block = ir_get_new_bb(ira, old_bb); - ira->old_irb.current_basic_block = old_bb; - ira->const_predecessor_bb = nullptr; + + ir_start_bb(ira, old_bb, nullptr); } } static void ir_inline_bb(IrAnalyze *ira, IrBasicBlock *old_bb) { - ira->instruction_index = 0; - ira->const_predecessor_bb = ira->old_irb.current_basic_block; - ira->old_irb.current_basic_block = old_bb; + ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block); } static TypeTableEntry *ir_finish_anal(IrAnalyze *ira, TypeTableEntry *result_type) { @@ -3603,6 +3672,8 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc return explicit_type; } + AstNode *source_node = decl_var_instruction->base.source_node; + IrInstruction *casted_init_value = ir_get_casted_value(ira, init_value, explicit_type); TypeTableEntry *result_type = get_underlying_type(casted_init_value->type_entry); switch (result_type->id) { @@ -3614,7 +3685,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: if (is_export || is_extern || casted_init_value->static_value.special == ConstValSpecialRuntime) { - add_node_error(ira->codegen, var_type->source_node, buf_sprintf("unable to infer variable type")); + add_node_error(ira->codegen, source_node, buf_sprintf("unable to infer variable type")); result_type = ira->codegen->builtin_types.entry_invalid; } break; @@ -3622,14 +3693,14 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc case TypeTableEntryIdVar: case TypeTableEntryIdBlock: case TypeTableEntryIdNullLit: - add_node_error(ira->codegen, var_type->source_node, + add_node_error(ira->codegen, source_node, buf_sprintf("variable of type '%s' not allowed", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; break; case TypeTableEntryIdMetaType: case TypeTableEntryIdNamespace: if (casted_init_value->static_value.special == ConstValSpecialRuntime) { - add_node_error(ira->codegen, var_type->source_node, + add_node_error(ira->codegen, source_node, buf_sprintf("variable of type '%s' must be constant", buf_ptr(&result_type->name))); result_type = ira->codegen->builtin_types.entry_invalid; } @@ -4072,6 +4143,8 @@ static TypeTableEntry *ir_analyze_instruction_br(IrAnalyze *ira, IrInstructionBr static TypeTableEntry *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstructionCondBr *cond_br_instruction) { IrInstruction *condition = cond_br_instruction->condition->other; + if (condition->type_entry->id == TypeTableEntryIdInvalid) + return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); // TODO detect backward jumps @@ -4116,6 +4189,9 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP continue; IrInstruction *value = phi_instruction->incoming_values[i]->other; assert(value->type_entry); + if (value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + if (value->static_value.special != ConstValSpecialRuntime) { ConstExprValue *out_val = ir_build_const_from(ira, &phi_instruction->base, value->static_value.depends_on_compile_var); @@ -4141,7 +4217,10 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP IrInstruction *old_value = phi_instruction->incoming_values[i]; assert(old_value); - new_incoming_values.append(old_value->other); + IrInstruction *new_value = old_value->other; + if (new_value->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + new_incoming_values.append(new_value); } assert(new_incoming_blocks.length != 0); @@ -4156,6 +4235,21 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP if (resolved_type->id == TypeTableEntryIdInvalid) return resolved_type; + if (resolved_type->id == TypeTableEntryIdNumLitFloat || + resolved_type->id == TypeTableEntryIdNumLitInt) + { + add_node_error(ira->codegen, phi_instruction->base.source_node, + buf_sprintf("unable to infer expression type")); + return ira->codegen->builtin_types.entry_invalid; + } + + // cast all literal values to the resolved type + for (size_t i = 0; i < new_incoming_values.length; i += 1) { + IrInstruction *new_value = new_incoming_values.at(i); + IrInstruction *casted_value = ir_get_casted_value(ira, new_value, resolved_type); + new_incoming_values.items[i] = casted_value; + } + ir_build_phi_from(&ira->new_irb, &phi_instruction->base, new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items); return resolved_type; @@ -5114,13 +5208,123 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira, IrInstructionSwitchBr *switch_br_instruction) { - zig_panic("TODO switch br analyze"); + IrInstruction *target_value = switch_br_instruction->target_value->other; + if (target_value->type_entry->id == TypeTableEntryIdInvalid) + return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); + + // TODO detect backward jumps + + size_t case_count = switch_br_instruction->case_count; + bool is_inline = switch_br_instruction->is_inline; + + if (is_inline || target_value->static_value.special != ConstValSpecialRuntime) { + zig_panic("TODO compile time switch br"); + } + + IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(case_count); + for (size_t i = 0; i < case_count; i += 1) { + IrInstructionSwitchBrCase *old_case = &switch_br_instruction->cases[i]; + IrInstructionSwitchBrCase *new_case = &cases[i]; + new_case->block = ir_get_new_bb(ira, old_case->block); + new_case->value = ira->codegen->invalid_instruction; + + IrInstruction *old_value = old_case->value; + IrInstruction *new_value = old_value->other; + if (new_value->type_entry->id == TypeTableEntryIdInvalid) + continue; + + IrInstruction *casted_new_value = ir_get_casted_value(ira, new_value, target_value->type_entry); + if (casted_new_value->type_entry->id == TypeTableEntryIdInvalid) + continue; + + if (casted_new_value->static_value.special != ConstValSpecialStatic) { + add_node_error(ira->codegen, casted_new_value->source_node, + buf_sprintf("unable to evaluate constant expression")); + continue; + } + + new_case->value = casted_new_value; + } + + IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block); + ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base, + target_value, new_else_block, case_count, cases, is_inline); + return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable); } static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, IrInstructionSwitchTarget *switch_target_instruction) { - zig_panic("TODO switch target analyze"); + IrInstruction *target_value_ptr = switch_target_instruction->target_value_ptr->other; + if (target_value_ptr->type_entry->id == TypeTableEntryIdInvalid) + return ira->codegen->builtin_types.entry_invalid; + + assert(target_value_ptr->type_entry->id == TypeTableEntryIdPointer); + TypeTableEntry *target_type = target_value_ptr->type_entry->data.pointer.child_type; + bool depends_on_compile_var = target_value_ptr->static_value.depends_on_compile_var; + ConstExprValue *pointee_val = nullptr; + if (target_value_ptr->static_value.special != ConstValSpecialRuntime) { + pointee_val = const_ptr_pointee(&target_value_ptr->static_value); + if (pointee_val->special == ConstValSpecialRuntime) + pointee_val = nullptr; + } + TypeTableEntry *canon_target_type = get_underlying_type(target_type); + switch (canon_target_type->id) { + case TypeTableEntryIdInvalid: + case TypeTableEntryIdVar: + case TypeTableEntryIdTypeDecl: + zig_unreachable(); + case TypeTableEntryIdMetaType: + case TypeTableEntryIdVoid: + case TypeTableEntryIdBool: + case TypeTableEntryIdInt: + case TypeTableEntryIdFloat: + case TypeTableEntryIdNumLitFloat: + case TypeTableEntryIdNumLitInt: + case TypeTableEntryIdPointer: + case TypeTableEntryIdFn: + case TypeTableEntryIdNamespace: + case TypeTableEntryIdPureError: + if (pointee_val) { + ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base, + depends_on_compile_var); + *out_val = *pointee_val; + return target_type; + } + + ir_build_load_ptr_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); + return target_type; + case TypeTableEntryIdEnum: + { + TypeTableEntry *tag_type = target_type->data.enumeration.tag_type; + if (pointee_val) { + ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base, + depends_on_compile_var); + bignum_init_unsigned(&out_val->data.x_bignum, pointee_val->data.x_enum.tag); + return tag_type; + } + + ir_build_enum_tag_from(&ira->new_irb, &switch_target_instruction->base, target_value_ptr); + return tag_type; + } + case TypeTableEntryIdErrorUnion: + // see https://github.com/andrewrk/zig/issues/83 + zig_panic("TODO switch on error union"); + case TypeTableEntryIdUnreachable: + case TypeTableEntryIdArray: + case TypeTableEntryIdStruct: + case TypeTableEntryIdUndefLit: + case TypeTableEntryIdNullLit: + case TypeTableEntryIdMaybe: + case TypeTableEntryIdUnion: + case TypeTableEntryIdBlock: + case TypeTableEntryIdGenericFn: + add_node_error(ira->codegen, switch_target_instruction->base.source_node, + buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); + // TODO if this is a typedecl, add error note showing the declaration of the type decl + return ira->codegen->builtin_types.entry_invalid; + } + zig_unreachable(); } static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, @@ -5129,6 +5333,12 @@ static TypeTableEntry *ir_analyze_instruction_switch_var(IrAnalyze *ira, zig_panic("TODO switch var analyze"); } +static TypeTableEntry *ir_analyze_instruction_enum_tag(IrAnalyze *ira, + IrInstructionEnumTag *enum_tag_instruction) +{ + zig_panic("TODO ir_analyze_instruction_enum_tag"); +} + static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { switch (instruction->id) { case IrInstructionIdInvalid: @@ -5201,6 +5411,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_switch_target(ira, (IrInstructionSwitchTarget *)instruction); case IrInstructionIdSwitchVar: return ir_analyze_instruction_switch_var(ira, (IrInstructionSwitchVar *)instruction); + case IrInstructionIdEnumTag: + return ir_analyze_instruction_enum_tag(ira, (IrInstructionEnumTag *)instruction); case IrInstructionIdCast: case IrInstructionIdContainerInitList: case IrInstructionIdContainerInitFields: @@ -5247,10 +5459,10 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl IrBasicBlock *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); IrBasicBlock *new_entry_bb = ir_get_new_bb(ira, old_entry_bb); ir_ref_bb(new_entry_bb); - ira->old_irb.current_basic_block = old_entry_bb; ira->new_irb.current_basic_block = new_entry_bb; ira->block_queue_index = 0; - ira->instruction_index = 0; + + ir_start_bb(ira, old_entry_bb, nullptr); while (ira->block_queue_index < ira->old_bb_queue.length) { IrInstruction *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); @@ -5319,6 +5531,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdCtz: case IrInstructionIdSwitchVar: case IrInstructionIdSwitchTarget: + case IrInstructionIdEnumTag: return false; case IrInstructionIdAsm: { diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 6286c688b9..5965e33b0b 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -313,6 +313,8 @@ static void ir_print_br(IrPrint *irp, IrInstructionBr *br_instruction) { } static void ir_print_phi(IrPrint *irp, IrInstructionPhi *phi_instruction) { + assert(phi_instruction->incoming_count != 0); + assert(phi_instruction->incoming_count != SIZE_MAX); for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { IrBasicBlock *incoming_block = phi_instruction->incoming_blocks[i]; IrInstruction *incoming_value = phi_instruction->incoming_values[i]; @@ -534,6 +536,39 @@ static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) { fprintf(irp->f, ")"); } +static void ir_print_switch_br(IrPrint *irp, IrInstructionSwitchBr *instruction) { + const char *inline_kw = instruction->is_inline ? "inline " : ""; + fprintf(irp->f, "%sswitch (", inline_kw); + ir_print_other_instruction(irp, instruction->target_value); + fprintf(irp->f, ") "); + for (size_t i = 0; i < instruction->case_count; i += 1) { + IrInstructionSwitchBrCase *this_case = &instruction->cases[i]; + ir_print_other_instruction(irp, this_case->value); + fprintf(irp->f, " => "); + ir_print_other_block(irp, this_case->block); + fprintf(irp->f, ", "); + } + fprintf(irp->f, "else => "); + ir_print_other_block(irp, instruction->else_block); +} + +static void ir_print_switch_var(IrPrint *irp, IrInstructionSwitchVar *instruction) { + fprintf(irp->f, "switchvar "); + ir_print_other_instruction(irp, instruction->target_value_ptr); + fprintf(irp->f, ", "); + ir_print_other_instruction(irp, instruction->prong_value); +} + +static void ir_print_switch_target(IrPrint *irp, IrInstructionSwitchTarget *instruction) { + fprintf(irp->f, "switchtarget "); + ir_print_other_instruction(irp, instruction->target_value_ptr); +} + +static void ir_print_enum_tag(IrPrint *irp, IrInstructionEnumTag *instruction) { + fprintf(irp->f, "enumtag "); + ir_print_other_instruction(irp, instruction->value); +} + static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_prefix(irp, instruction); switch (instruction->id) { @@ -645,9 +680,17 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { ir_print_clz(irp, (IrInstructionClz *)instruction); break; case IrInstructionIdSwitchBr: + ir_print_switch_br(irp, (IrInstructionSwitchBr *)instruction); + break; case IrInstructionIdSwitchVar: + ir_print_switch_var(irp, (IrInstructionSwitchVar *)instruction); + break; case IrInstructionIdSwitchTarget: - zig_panic("TODO print more IR instructions"); + ir_print_switch_target(irp, (IrInstructionSwitchTarget *)instruction); + break; + case IrInstructionIdEnumTag: + ir_print_enum_tag(irp, (IrInstructionEnumTag *)instruction); + break; } fprintf(irp->f, "\n"); } |
