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(
27522752 };
27532753 break :blk &capture_val_scope.base;
27542754 };
2755 block_scope.break_count += 1;
27562755 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
27572756 if (!astgen.refIsNoReturn(case_result)) {
2757 block_scope.break_count += 1;
27582758 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
27592759 }
27602760 // Documentation for this: `zir.Inst.SwitchBlock` and `zir.Inst.SwitchBlockMulti`.
......@@ -2873,9 +2873,9 @@ fn switchExpr(
28732873 });
28742874 }
28752875
2876 block_scope.break_count += 1;
28772876 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
28782877 if (!astgen.refIsNoReturn(case_result)) {
2878 block_scope.break_count += 1;
28792879 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
28802880 }
28812881
......@@ -2886,9 +2886,9 @@ fn switchExpr(
28862886 } else {
28872887 const item_node = case.ast.values[0];
28882888 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
2889 block_scope.break_count += 1;
28902889 const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
28912890 if (!astgen.refIsNoReturn(case_result)) {
2891 block_scope.break_count += 1;
28922892 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
28932893 }
28942894 try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
......@@ -2925,13 +2925,11 @@ fn switchExpr(
29252925 0b1_10_1 => .switch_block_ref_under_multi,
29262926 else => unreachable,
29272927 };
2928 const zir_datas = astgen.instructions.items(.data);
29292928 const payload_index = astgen.extra.items.len;
2929 const zir_datas = astgen.instructions.items(.data);
29302930 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
29312931 try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
29322932 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);
29352933 const strat = rl.strategy(&block_scope);
29362934 switch (strat.tag) {
29372935 .break_operand => {
......@@ -2939,51 +2937,90 @@ fn switchExpr(
29392937 // this is always true.
29402938 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.
29422942 // Elide all the `store_to_block_ptr` instructions.
2943 var extra_index: usize = payload_index;
2943
2944 var extra_index: usize = 0;
29442945 extra_index += 2;
29452946 extra_index += @boolToInt(multi_cases_len != 0);
29462947 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];
29482950 extra_index += 1;
2949 const body = astgen.extra.items[extra_index..][0..body_len];
2950 extra_index += body_len;
2951 const store_inst = body[body.len - 2];
2952 assert(zir_tags[store_inst] == .store_to_block_ptr);
2953 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2954 zir_tags[store_inst] = .elided;
2955 zir_datas[store_inst] = undefined;
2951 extra_index += body_len - 2;
2952 const store_inst = scalar_cases_payload.items[extra_index];
2953 if (zir_tags[store_inst] == .store_to_block_ptr) {
2954 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2955 scalar_cases_payload.items[body_len_index] -= 1;
2956 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
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]);
29562966 }
29572967 var scalar_i: u32 = 0;
29582968 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
2969 const start_index = extra_index;
29592970 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];
29612973 extra_index += 1;
2962 const body = astgen.extra.items[extra_index..][0..body_len];
2963 extra_index += body_len;
2964 const store_inst = body[body.len - 2];
2965 assert(zir_tags[store_inst] == .store_to_block_ptr);
2966 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2967 zir_tags[store_inst] = .elided;
2968 zir_datas[store_inst] = undefined;
2974 if (body_len < 2) {
2975 extra_index += body_len;
2976 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
2977 continue;
2978 }
2979 extra_index += body_len - 2;
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 }
29692992 }
2993 extra_index = 0;
29702994 var multi_i: u32 = 0;
29712995 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];
29732998 extra_index += 1;
2974 const ranges_len = astgen.extra.items[extra_index];
2999 const ranges_len = multi_cases_payload.items[extra_index];
29753000 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];
29773003 extra_index += 1;
29783004 extra_index += items_len;
29793005 extra_index += 2 * ranges_len;
2980 const body = astgen.extra.items[extra_index..][0..body_len];
2981 extra_index += body_len;
2982 const store_inst = body[body.len - 2];
2983 assert(zir_tags[store_inst] == .store_to_block_ptr);
2984 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
2985 zir_tags[store_inst] = .elided;
2986 zir_datas[store_inst] = undefined;
3006 if (body_len < 2) {
3007 extra_index += body_len;
3008 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3009 continue;
3010 }
3011 extra_index += body_len - 2;
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 }
29873024 }
29883025
29893026 const block_ref = astgen.indexToRef(switch_block);
......@@ -2994,6 +3031,8 @@ fn switchExpr(
29943031 },
29953032 .break_void => {
29963033 assert(!strat.elide_store_to_block_ptr_instructions);
3034 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
3035 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
29973036 // Modify all the terminating instruction tags to become `break` variants.
29983037 var extra_index: usize = payload_index;
29993038 extra_index += 2;
......@@ -3004,9 +3043,11 @@ fn switchExpr(
30043043 const body = astgen.extra.items[extra_index..][0..body_len];
30053044 extra_index += body_len;
30063045 const last = body[body.len - 1];
3007 assert(zir_tags[last] == .@"break");
3008 assert(zir_datas[last].@"break".block_inst == switch_block);
3009 zir_datas[last].@"break".operand = .void_value;
3046 if (zir_tags[last] == .@"break" and
3047 zir_datas[last].@"break".block_inst == switch_block)
3048 {
3049 zir_datas[last].@"break".operand = .void_value;
3050 }
30103051 }
30113052 var scalar_i: u32 = 0;
30123053 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
......@@ -3016,9 +3057,11 @@ fn switchExpr(
30163057 const body = astgen.extra.items[extra_index..][0..body_len];
30173058 extra_index += body_len;
30183059 const last = body[body.len - 1];
3019 assert(zir_tags[last] == .@"break");
3020 assert(zir_datas[last].@"break".block_inst == switch_block);
3021 zir_datas[last].@"break".operand = .void_value;
3060 if (zir_tags[last] == .@"break" and
3061 zir_datas[last].@"break".block_inst == switch_block)
3062 {
3063 zir_datas[last].@"break".operand = .void_value;
3064 }
30223065 }
30233066 var multi_i: u32 = 0;
30243067 while (multi_i < multi_cases_len) : (multi_i += 1) {
......@@ -3033,9 +3076,11 @@ fn switchExpr(
30333076 const body = astgen.extra.items[extra_index..][0..body_len];
30343077 extra_index += body_len;
30353078 const last = body[body.len - 1];
3036 assert(zir_tags[last] == .@"break");
3037 assert(zir_datas[last].@"break".block_inst == switch_block);
3038 zir_datas[last].@"break".operand = .void_value;
3079 if (zir_tags[last] == .@"break" and
3080 zir_datas[last].@"break".block_inst == switch_block)
3081 {
3082 zir_datas[last].@"break".operand = .void_value;
3083 }
30393084 }
30403085
30413086 return astgen.indexToRef(switch_block);