| ... | ... | @@ -483,6 +483,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionIntToEnum *) { |
| 483 | 483 | return IrInstructionIdIntToEnum; |
| 484 | 484 | } |
| 485 | 485 | |
| 486 | static constexpr IrInstructionId ir_instruction_id(IrInstructionCheckSwitchProngs *) { |
| 487 | return IrInstructionIdCheckSwitchProngs; |
| 488 | } |
| 489 | |
| 486 | 490 | template<typename T> |
| 487 | 491 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 488 | 492 | T *special_instruction = allocate<T>(1); |
| ... | ... | @@ -1981,6 +1985,24 @@ static IrInstruction *ir_build_int_to_enum(IrBuilder *irb, Scope *scope, AstNode |
| 1981 | 1985 | return &instruction->base; |
| 1982 | 1986 | } |
| 1983 | 1987 | |
| 1988 | static IrInstruction *ir_build_check_switch_prongs(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 1989 | IrInstruction *target_value, IrInstructionCheckSwitchProngsRange *ranges, size_t range_count) |
| 1990 | { |
| 1991 | IrInstructionCheckSwitchProngs *instruction = ir_build_instruction<IrInstructionCheckSwitchProngs>( |
| 1992 | irb, scope, source_node); |
| 1993 | instruction->target_value = target_value; |
| 1994 | instruction->ranges = ranges; |
| 1995 | instruction->range_count = range_count; |
| 1996 | |
| 1997 | ir_ref_instruction(target_value, irb->current_basic_block); |
| 1998 | for (size_t i = 0; i < range_count; i += 1) { |
| 1999 | ir_ref_instruction(ranges[i].start, irb->current_basic_block); |
| 2000 | ir_ref_instruction(ranges[i].end, irb->current_basic_block); |
| 2001 | } |
| 2002 | |
| 2003 | return &instruction->base; |
| 2004 | } |
| 2005 | |
| 1984 | 2006 | static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) { |
| 1985 | 2007 | return nullptr; |
| 1986 | 2008 | } |
| ... | ... | @@ -2580,6 +2602,18 @@ static IrInstruction *ir_instruction_inttoenum_get_dep(IrInstructionIntToEnum *i |
| 2580 | 2602 | } |
| 2581 | 2603 | } |
| 2582 | 2604 | |
| 2605 | static IrInstruction *ir_instruction_checkswitchprongs_get_dep(IrInstructionCheckSwitchProngs *instruction, |
| 2606 | size_t index) |
| 2607 | { |
| 2608 | if (index == 0) return instruction->target_value; |
| 2609 | size_t range_index = index - 1; |
| 2610 | if (range_index < instruction->range_count * 2) { |
| 2611 | IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_index / 2]; |
| 2612 | return (range_index % 2 == 0) ? range->start : range->end; |
| 2613 | } |
| 2614 | return nullptr; |
| 2615 | } |
| 2616 | |
| 2583 | 2617 | static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) { |
| 2584 | 2618 | switch (instruction->id) { |
| 2585 | 2619 | case IrInstructionIdInvalid: |
| ... | ... | @@ -2752,6 +2786,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t |
| 2752 | 2786 | return ir_instruction_ptrtoint_get_dep((IrInstructionPtrToInt *) instruction, index); |
| 2753 | 2787 | case IrInstructionIdIntToEnum: |
| 2754 | 2788 | return ir_instruction_inttoenum_get_dep((IrInstructionIntToEnum *) instruction, index); |
| 2789 | case IrInstructionIdCheckSwitchProngs: |
| 2790 | return ir_instruction_checkswitchprongs_get_dep((IrInstructionCheckSwitchProngs *) instruction, index); |
| 2755 | 2791 | } |
| 2756 | 2792 | zig_unreachable(); |
| 2757 | 2793 | } |
| ... | ... | @@ -4677,6 +4713,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4677 | 4713 | |
| 4678 | 4714 | ZigList<IrInstruction *> incoming_values = {0}; |
| 4679 | 4715 | ZigList<IrBasicBlock *> incoming_blocks = {0}; |
| 4716 | ZigList<IrInstructionCheckSwitchProngsRange> check_ranges = {0}; |
| 4680 | 4717 | |
| 4681 | 4718 | AstNode *else_prong = nullptr; |
| 4682 | 4719 | for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { |
| ... | ... | @@ -4719,6 +4756,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4719 | 4756 | if (end_value == irb->codegen->invalid_instruction) |
| 4720 | 4757 | return irb->codegen->invalid_instruction; |
| 4721 | 4758 | |
| 4759 | IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one(); |
| 4760 | check_range->start = start_value; |
| 4761 | check_range->end = end_value; |
| 4762 | |
| 4722 | 4763 | IrInstruction *start_value_const = ir_build_static_eval(irb, scope, start_node, start_value); |
| 4723 | 4764 | IrInstruction *end_value_const = ir_build_static_eval(irb, scope, start_node, end_value); |
| 4724 | 4765 | |
| ... | ... | @@ -4738,6 +4779,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4738 | 4779 | if (item_value == irb->codegen->invalid_instruction) |
| 4739 | 4780 | return irb->codegen->invalid_instruction; |
| 4740 | 4781 | |
| 4782 | IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one(); |
| 4783 | check_range->start = item_value; |
| 4784 | check_range->end = item_value; |
| 4785 | |
| 4741 | 4786 | IrInstruction *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq, |
| 4742 | 4787 | item_value, target_value, false); |
| 4743 | 4788 | if (ok_bit) { |
| ... | ... | @@ -4776,6 +4821,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4776 | 4821 | if (item_value == irb->codegen->invalid_instruction) |
| 4777 | 4822 | return irb->codegen->invalid_instruction; |
| 4778 | 4823 | |
| 4824 | IrInstructionCheckSwitchProngsRange *check_range = check_ranges.add_one(); |
| 4825 | check_range->start = item_value; |
| 4826 | check_range->end = item_value; |
| 4827 | |
| 4779 | 4828 | IrInstructionSwitchBrCase *this_case = cases.add_one(); |
| 4780 | 4829 | this_case->value = item_value; |
| 4781 | 4830 | this_case->block = prong_block; |
| ... | ... | @@ -4798,6 +4847,10 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode * |
| 4798 | 4847 | } |
| 4799 | 4848 | } |
| 4800 | 4849 | |
| 4850 | if (!else_prong) { |
| 4851 | ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length); |
| 4852 | } |
| 4853 | |
| 4801 | 4854 | if (cases.length == 0) { |
| 4802 | 4855 | ir_build_br(irb, scope, node, else_block, is_comptime); |
| 4803 | 4856 | } else { |
| ... | ... | @@ -11091,6 +11144,69 @@ static TypeTableEntry *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrIn |
| 11091 | 11144 | return ira->codegen->builtin_types.entry_bool; |
| 11092 | 11145 | } |
| 11093 | 11146 | |
| 11147 | static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, |
| 11148 | IrInstructionCheckSwitchProngs *instruction) |
| 11149 | { |
| 11150 | IrInstruction *target_value = instruction->target_value->other; |
| 11151 | TypeTableEntry *switch_type = target_value->value.type; |
| 11152 | if (switch_type->id == TypeTableEntryIdInvalid) |
| 11153 | return ira->codegen->builtin_types.entry_invalid; |
| 11154 | |
| 11155 | if (switch_type->id == TypeTableEntryIdEnumTag) { |
| 11156 | TypeTableEntry *enum_type = switch_type->data.enum_tag.enum_type; |
| 11157 | size_t *field_use_counts = allocate<size_t>(enum_type->data.enumeration.src_field_count); |
| 11158 | for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { |
| 11159 | IrInstructionCheckSwitchProngsRange *range = &instruction->ranges[range_i]; |
| 11160 | |
| 11161 | IrInstruction *start_value = range->start->other; |
| 11162 | if (start_value->value.type->id == TypeTableEntryIdInvalid) |
| 11163 | return ira->codegen->builtin_types.entry_invalid; |
| 11164 | |
| 11165 | IrInstruction *end_value = range->end->other; |
| 11166 | if (end_value->value.type->id == TypeTableEntryIdInvalid) |
| 11167 | return ira->codegen->builtin_types.entry_invalid; |
| 11168 | |
| 11169 | size_t start_index; |
| 11170 | size_t end_index; |
| 11171 | if (start_value->value.type->id == TypeTableEntryIdEnumTag) { |
| 11172 | start_index = start_value->value.data.x_bignum.data.x_uint; |
| 11173 | } else if (start_value->value.type->id == TypeTableEntryIdEnum) { |
| 11174 | start_index = start_value->value.data.x_enum.tag; |
| 11175 | } else { |
| 11176 | zig_unreachable(); |
| 11177 | } |
| 11178 | if (end_value->value.type->id == TypeTableEntryIdEnumTag) { |
| 11179 | end_index = end_value->value.data.x_bignum.data.x_uint; |
| 11180 | } else if (end_value->value.type->id == TypeTableEntryIdEnum) { |
| 11181 | end_index = end_value->value.data.x_enum.tag; |
| 11182 | } else { |
| 11183 | zig_unreachable(); |
| 11184 | } |
| 11185 | |
| 11186 | for (size_t field_index = start_index; field_index <= end_index; field_index += 1) { |
| 11187 | field_use_counts[field_index] += 1; |
| 11188 | if (field_use_counts[field_index] > 1) { |
| 11189 | TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_index]; |
| 11190 | ir_add_error(ira, start_value, |
| 11191 | buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&enum_type->name), |
| 11192 | buf_ptr(type_enum_field->name))); |
| 11193 | } |
| 11194 | } |
| 11195 | } |
| 11196 | for (uint32_t i = 0; i < enum_type->data.enumeration.src_field_count; i += 1) { |
| 11197 | if (field_use_counts[i] == 0) { |
| 11198 | ir_add_error(ira, &instruction->base, |
| 11199 | buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&enum_type->name), |
| 11200 | buf_ptr(enum_type->data.enumeration.fields[i].name))); |
| 11201 | } |
| 11202 | } |
| 11203 | } else { |
| 11204 | // TODO check prongs of types other than enumtag |
| 11205 | } |
| 11206 | ir_build_const_from(ira, &instruction->base, false); |
| 11207 | return ira->codegen->builtin_types.entry_void; |
| 11208 | } |
| 11209 | |
| 11094 | 11210 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { |
| 11095 | 11211 | switch (instruction->id) { |
| 11096 | 11212 | case IrInstructionIdInvalid: |
| ... | ... | @@ -11246,6 +11362,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 11246 | 11362 | return ir_analyze_instruction_fn_proto(ira, (IrInstructionFnProto *)instruction); |
| 11247 | 11363 | case IrInstructionIdTestComptime: |
| 11248 | 11364 | return ir_analyze_instruction_test_comptime(ira, (IrInstructionTestComptime *)instruction); |
| 11365 | case IrInstructionIdCheckSwitchProngs: |
| 11366 | return ir_analyze_instruction_check_switch_prongs(ira, (IrInstructionCheckSwitchProngs *)instruction); |
| 11249 | 11367 | case IrInstructionIdMaybeWrap: |
| 11250 | 11368 | case IrInstructionIdErrWrapCode: |
| 11251 | 11369 | case IrInstructionIdErrWrapPayload: |
| ... | ... | @@ -11351,6 +11469,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 11351 | 11469 | case IrInstructionIdMemcpy: |
| 11352 | 11470 | case IrInstructionIdBreakpoint: |
| 11353 | 11471 | case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free |
| 11472 | case IrInstructionIdCheckSwitchProngs: |
| 11354 | 11473 | return true; |
| 11355 | 11474 | case IrInstructionIdPhi: |
| 11356 | 11475 | case IrInstructionIdUnOp: |