authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-31 12:37:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-31 12:37:20-07:00
logc7b09be8de946d18c2f1afb532beb1b2426fab18
tree5f92b32f5acb493587c5d4e22176b60c827af696
parent549af582e722c4798076acef7948a1f0d2896289

AstGen: improve switch expressions

* use the proper result location strategy even when there are noreturn prongs in the switch expression * when using break_operand strategy, actually omit the store_to_block_ptr instructions rather than eliding them. * for both strategies, properly handle noreturn prongs.

1 files changed, 87 insertions(+), 42 deletions(-)

src/AstGen.zig+87-42
...@@ -2752,9 +2752,9 @@ fn switchExpr(...@@ -2752,9 +2752,9 @@ fn switchExpr(
2752 };2752 };
2753 break :blk &capture_val_scope.base;2753 break :blk &capture_val_scope.base;
2754 };2754 };
2755 block_scope.break_count += 1;
2756 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);2755 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2757 if (!astgen.refIsNoReturn(case_result)) {2756 if (!astgen.refIsNoReturn(case_result)) {
2757 block_scope.break_count += 1;
2758 _ = try case_scope.addBreak(.@"break", switch_block, case_result);2758 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2759 }2759 }
2760 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.2760 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
...@@ -2873,9 +2873,9 @@ fn switchExpr(...@@ -2873,9 +2873,9 @@ fn switchExpr(
2873 });2873 });
2874 }2874 }
28752875
2876 block_scope.break_count += 1;
2877 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);2876 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2878 if (!astgen.refIsNoReturn(case_result)) {2877 if (!astgen.refIsNoReturn(case_result)) {
2878 block_scope.break_count += 1;
2879 _ = try case_scope.addBreak(.@"break", switch_block, case_result);2879 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2880 }2880 }
28812881
...@@ -2886,9 +2886,9 @@ fn switchExpr(...@@ -2886,9 +2886,9 @@ fn switchExpr(
2886 } else {2886 } else {
2887 const item_node = case.ast.values[0];2887 const item_node = case.ast.values[0];
2888 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);2888 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
2889 block_scope.break_count += 1;
2890 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);2889 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
2891 if (!astgen.refIsNoReturn(case_result)) {2890 if (!astgen.refIsNoReturn(case_result)) {
2891 block_scope.break_count += 1;
2892 _ = try case_scope.addBreak(.@"break", switch_block, case_result);2892 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
2893 }2893 }
2894 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +2894 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
...@@ -2925,13 +2925,11 @@ fn switchExpr(...@@ -2925,13 +2925,11 @@ fn switchExpr(
2925 0b1_10_1 => .switch_block_ref_under_multi,2925 0b1_10_1 => .switch_block_ref_under_multi,
2926 else => unreachable,2926 else => unreachable,
2927 };2927 };
2928 const zir_datas = astgen.instructions.items(.data);
2929 const payload_index = astgen.extra.items.len;2928 const payload_index = astgen.extra.items.len;
2929 const zir_datas = astgen.instructions.items(.data);
2930 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);2930 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
2931 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +2931 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
2932 scalar_cases_payload.items.len + multi_cases_payload.items.len);2932 scalar_cases_payload.items.len + multi_cases_payload.items.len);
2933 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
2934 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
2935 const strat = rl.strategy(&block_scope);2933 const strat = rl.strategy(&block_scope);
2936 switch (strat.tag) {2934 switch (strat.tag) {
2937 .break_operand => {2935 .break_operand => {
...@@ -2939,51 +2937,90 @@ fn switchExpr(...@@ -2939,51 +2937,90 @@ fn switchExpr(
2939 // this is always true.2937 // this is always true.
2940 assert(strat.elide_store_to_block_ptr_instructions);2938 assert(strat.elide_store_to_block_ptr_instructions);
29412939
2940 // There will necessarily be a store_to_block_ptr for
2941 // all prongs, except for prongs that ended with a noreturn instruction.
2942 // Elide all the `store_to_block_ptr` instructions.2942 // Elide all the `store_to_block_ptr` instructions.
2943 var extra_index: usize = payload_index;2943
2944 var extra_index: usize = 0;
2944 extra_index += 2;2945 extra_index += 2;
2945 extra_index += @boolToInt(multi_cases_len != 0);2946 extra_index += @boolToInt(multi_cases_len != 0);
2946 if (special_prong != .none) {2947 if (special_prong != .none) {
2947 const body_len = astgen.extra.items[extra_index];2948 const body_len_index = extra_index;
2949 const body_len = scalar_cases_payload.items[extra_index];
2948 extra_index += 1;2950 extra_index += 1;
2949 const body = astgen.extra.items[extra_index..][0..body_len];2951 extra_index += body_len - 2;
2950 extra_index += body_len;2952 const store_inst = scalar_cases_payload.items[extra_index];
2951 const store_inst = body[body.len - 2];2953 if (zir_tags[store_inst] == .store_to_block_ptr) {
2952 assert(zir_tags[store_inst] == .store_to_block_ptr);2954 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2953 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);2955 scalar_cases_payload.items[body_len_index] -= 1;
2954 zir_tags[store_inst] = .elided;2956 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
2955 zir_datas[store_inst] = undefined;2957 extra_index += 1;
2958 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
2959 extra_index += 1;
2960 } else {
2961 extra_index += 2;
2962 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
2963 }
2964 } else {
2965 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
2956 }2966 }
2957 var scalar_i: u32 = 0;2967 var scalar_i: u32 = 0;
2958 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {2968 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2969 const start_index = extra_index;
2959 extra_index += 1;2970 extra_index += 1;
2960 const body_len = astgen.extra.items[extra_index];2971 const body_len_index = extra_index;
2972 const body_len = scalar_cases_payload.items[extra_index];
2961 extra_index += 1;2973 extra_index += 1;
2962 const body = astgen.extra.items[extra_index..][0..body_len];2974 if (body_len < 2) {
2963 extra_index += body_len;2975 extra_index += body_len;
2964 const store_inst = body[body.len - 2];2976 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
2965 assert(zir_tags[store_inst] == .store_to_block_ptr);2977 continue;
2966 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);2978 }
2967 zir_tags[store_inst] = .elided;2979 extra_index += body_len - 2;
2968 zir_datas[store_inst] = undefined;2980 const store_inst = scalar_cases_payload.items[extra_index];
2981 if (zir_tags[store_inst] == .store_to_block_ptr) {
2982 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2983 scalar_cases_payload.items[body_len_index] -= 1;
2984 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
2985 extra_index += 1;
2986 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
2987 extra_index += 1;
2988 } else {
2989 extra_index += 2;
2990 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
2991 }
2969 }2992 }
2993 extra_index = 0;
2970 var multi_i: u32 = 0;2994 var multi_i: u32 = 0;
2971 while (multi_i < multi_cases_len) : (multi_i += 1) {2995 while (multi_i < multi_cases_len) : (multi_i += 1) {
2972 const items_len = astgen.extra.items[extra_index];2996 const start_index = extra_index;
2997 const items_len = multi_cases_payload.items[extra_index];
2973 extra_index += 1;2998 extra_index += 1;
2974 const ranges_len = astgen.extra.items[extra_index];2999 const ranges_len = multi_cases_payload.items[extra_index];
2975 extra_index += 1;3000 extra_index += 1;
2976 const body_len = astgen.extra.items[extra_index];3001 const body_len_index = extra_index;
3002 const body_len = multi_cases_payload.items[extra_index];
2977 extra_index += 1;3003 extra_index += 1;
2978 extra_index += items_len;3004 extra_index += items_len;
2979 extra_index += 2 * ranges_len;3005 extra_index += 2 * ranges_len;
2980 const body = astgen.extra.items[extra_index..][0..body_len];3006 if (body_len < 2) {
2981 extra_index += body_len;3007 extra_index += body_len;
2982 const store_inst = body[body.len - 2];3008 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
2983 assert(zir_tags[store_inst] == .store_to_block_ptr);3009 continue;
2984 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);3010 }
2985 zir_tags[store_inst] = .elided;3011 extra_index += body_len - 2;
2986 zir_datas[store_inst] = undefined;3012 const store_inst = multi_cases_payload.items[extra_index];
3013 if (zir_tags[store_inst] == .store_to_block_ptr) {
3014 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3015 multi_cases_payload.items[body_len_index] -= 1;
3016 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3017 extra_index += 1;
3018 astgen.extra.appendAssumeCapacity(multi_cases_payload.items[extra_index]);
3019 extra_index += 1;
3020 } else {
3021 extra_index += 2;
3022 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3023 }
2987 }3024 }
29883025
2989 const block_ref = astgen.indexToRef(switch_block);3026 const block_ref = astgen.indexToRef(switch_block);
...@@ -2994,6 +3031,8 @@ fn switchExpr(...@@ -2994,6 +3031,8 @@ fn switchExpr(
2994 },3031 },
2995 .break_void => {3032 .break_void => {
2996 assert(!strat.elide_store_to_block_ptr_instructions);3033 assert(!strat.elide_store_to_block_ptr_instructions);
3034 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
3035 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
2997 // Modify all the terminating instruction tags to become `break` variants.3036 // Modify all the terminating instruction tags to become `break` variants.
2998 var extra_index: usize = payload_index;3037 var extra_index: usize = payload_index;
2999 extra_index += 2;3038 extra_index += 2;
...@@ -3004,9 +3043,11 @@ fn switchExpr(...@@ -3004,9 +3043,11 @@ fn switchExpr(
3004 const body = astgen.extra.items[extra_index..][0..body_len];3043 const body = astgen.extra.items[extra_index..][0..body_len];
3005 extra_index += body_len;3044 extra_index += body_len;
3006 const last = body[body.len - 1];3045 const last = body[body.len - 1];
3007 assert(zir_tags[last] == .@"break");3046 if (zir_tags[last] == .@"break" and
3008 assert(zir_datas[last].@"break".block_inst == switch_block);3047 zir_datas[last].@"break".block_inst == switch_block)
3009 zir_datas[last].@"break".operand = .void_value;3048 {
3049 zir_datas[last].@"break".operand = .void_value;
3050 }
3010 }3051 }
3011 var scalar_i: u32 = 0;3052 var scalar_i: u32 = 0;
3012 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {3053 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
...@@ -3016,9 +3057,11 @@ fn switchExpr(...@@ -3016,9 +3057,11 @@ fn switchExpr(
3016 const body = astgen.extra.items[extra_index..][0..body_len];3057 const body = astgen.extra.items[extra_index..][0..body_len];
3017 extra_index += body_len;3058 extra_index += body_len;
3018 const last = body[body.len - 1];3059 const last = body[body.len - 1];
3019 assert(zir_tags[last] == .@"break");3060 if (zir_tags[last] == .@"break" and
3020 assert(zir_datas[last].@"break".block_inst == switch_block);3061 zir_datas[last].@"break".block_inst == switch_block)
3021 zir_datas[last].@"break".operand = .void_value;3062 {
3063 zir_datas[last].@"break".operand = .void_value;
3064 }
3022 }3065 }
3023 var multi_i: u32 = 0;3066 var multi_i: u32 = 0;
3024 while (multi_i < multi_cases_len) : (multi_i += 1) {3067 while (multi_i < multi_cases_len) : (multi_i += 1) {
...@@ -3033,9 +3076,11 @@ fn switchExpr(...@@ -3033,9 +3076,11 @@ fn switchExpr(
3033 const body = astgen.extra.items[extra_index..][0..body_len];3076 const body = astgen.extra.items[extra_index..][0..body_len];
3034 extra_index += body_len;3077 extra_index += body_len;
3035 const last = body[body.len - 1];3078 const last = body[body.len - 1];
3036 assert(zir_tags[last] == .@"break");3079 if (zir_tags[last] == .@"break" and
3037 assert(zir_datas[last].@"break".block_inst == switch_block);3080 zir_datas[last].@"break".block_inst == switch_block)
3038 zir_datas[last].@"break".operand = .void_value;3081 {
3082 zir_datas[last].@"break".operand = .void_value;
3083 }
3039 }3084 }
30403085
3041 return astgen.indexToRef(switch_block);3086 return astgen.indexToRef(switch_block);