authorgravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-03-28 19:53:38+02:00
committergravatar for mail@isaacfreund.comIsaac Freund <mail@isaacfreund.com> 2021-03-28 19:53:38+02:00
logd123a5ec67b517c6e2a7e7005575a745b511ea92
tree9cd49fe5785231609682b2a107e08ed4f0a2434e
parent402f87a213b9f3d5e19d5a1d412d8963957d8849
signature Commit is signed but in an unrecognized format.

AstGen: scope result location related functions


2 files changed, 76 insertions(+), 76 deletions(-)

src/AstGen.zig+44-75
......@@ -138,7 +138,7 @@ pub const ResultLoc = union(enum) {
138138 /// There is a pointer for the expression to store its result into, however, its type
139139 /// is inferred based on peer type resolution for a `zir.Inst.Block`.
140140 /// The result instruction from the expression must be ignored.
141 block_ptr: *Module.Scope.GenZir,
141 block_ptr: *Scope.GenZir,
142142
143143 pub const Strategy = struct {
144144 elide_store_to_block_ptr_instructions: bool,
......@@ -154,6 +154,41 @@ pub const ResultLoc = union(enum) {
154154 break_operand,
155155 };
156156 };
157
158 fn strategy(rl: ResultLoc, block_scope: *Scope.GenZir) Strategy {
159 var elide_store_to_block_ptr_instructions = false;
160 switch (rl) {
161 // In this branch there will not be any store_to_block_ptr instructions.
162 .discard, .none, .ty, .ref => return .{
163 .tag = .break_operand,
164 .elide_store_to_block_ptr_instructions = false,
165 },
166 // The pointer got passed through to the sub-expressions, so we will use
167 // break_void here.
168 // In this branch there will not be any store_to_block_ptr instructions.
169 .ptr => return .{
170 .tag = .break_void,
171 .elide_store_to_block_ptr_instructions = false,
172 },
173 .inferred_ptr, .bitcasted_ptr, .block_ptr => {
174 if (block_scope.rvalue_rl_count == block_scope.break_count) {
175 // Neither prong of the if consumed the result location, so we can
176 // use break instructions to create an rvalue.
177 return .{
178 .tag = .break_operand,
179 .elide_store_to_block_ptr_instructions = true,
180 };
181 } else {
182 // Allow the store_to_block_ptr instructions to remain so that
183 // semantic analysis can turn them into bitcasts.
184 return .{
185 .tag = .break_void,
186 .elide_store_to_block_ptr_instructions = false,
187 };
188 }
189 },
190 }
191 }
157192};
158193
159194pub fn typeExpr(mod: *Module, scope: *Scope, type_node: ast.Node.Index) InnerError!zir.Inst.Ref {
......@@ -989,7 +1024,7 @@ fn labeledBlockExpr(
9891024 .block_inst = block_inst,
9901025 }),
9911026 };
992 setBlockResultLoc(&block_scope, rl);
1027 block_scope.setBreakResultLoc(rl);
9931028 defer block_scope.instructions.deinit(mod.gpa);
9941029 defer block_scope.labeled_breaks.deinit(mod.gpa);
9951030 defer block_scope.labeled_store_to_block_ptr_list.deinit(mod.gpa);
......@@ -1003,7 +1038,7 @@ fn labeledBlockExpr(
10031038 const zir_tags = gz.astgen.instructions.items(.tag);
10041039 const zir_datas = gz.astgen.instructions.items(.data);
10051040
1006 const strat = rlStrategy(rl, &block_scope);
1041 const strat = rl.strategy(&block_scope);
10071042 switch (strat.tag) {
10081043 .break_void => {
10091044 // The code took advantage of the result location as a pointer.
......@@ -1740,7 +1775,7 @@ fn orelseCatchExpr(
17401775 .force_comptime = parent_gz.force_comptime,
17411776 .instructions = .{},
17421777 };
1743 setBlockResultLoc(&block_scope, rl);
1778 block_scope.setBreakResultLoc(rl);
17441779 defer block_scope.instructions.deinit(mod.gpa);
17451780
17461781 // This could be a pointer or value depending on the `operand_rl` parameter.
......@@ -1856,7 +1891,7 @@ fn finishThenElseBlock(
18561891) InnerError!zir.Inst.Ref {
18571892 // We now have enough information to decide whether the result instruction should
18581893 // be communicated via result location pointer or break instructions.
1859 const strat = rlStrategy(rl, block_scope);
1894 const strat = rl.strategy(block_scope);
18601895 const astgen = block_scope.astgen;
18611896 switch (strat.tag) {
18621897 .break_void => {
......@@ -2035,7 +2070,7 @@ fn ifExpr(
20352070 .force_comptime = parent_gz.force_comptime,
20362071 .instructions = .{},
20372072 };
2038 setBlockResultLoc(&block_scope, rl);
2073 block_scope.setBreakResultLoc(rl);
20392074 defer block_scope.instructions.deinit(mod.gpa);
20402075
20412076 const cond = c: {
......@@ -2190,7 +2225,7 @@ fn whileExpr(
21902225 .force_comptime = parent_gz.force_comptime,
21912226 .instructions = .{},
21922227 };
2193 setBlockResultLoc(&loop_scope, rl);
2228 loop_scope.setBreakResultLoc(rl);
21942229 defer loop_scope.instructions.deinit(mod.gpa);
21952230
21962231 var continue_scope: Scope.GenZir = .{
......@@ -2338,7 +2373,7 @@ fn forExpr(
23382373 .force_comptime = parent_gz.force_comptime,
23392374 .instructions = .{},
23402375 };
2341 setBlockResultLoc(&loop_scope, rl);
2376 loop_scope.setBreakResultLoc(rl);
23422377 defer loop_scope.instructions.deinit(mod.gpa);
23432378
23442379 var cond_scope: Scope.GenZir = .{
......@@ -2520,7 +2555,7 @@ fn switchExpr(
25202555 .force_comptime = parent_gz.force_comptime,
25212556 .instructions = .{},
25222557 };
2523 setBlockResultLoc(&block_scope, rl);
2558 block_scope.setBreakResultLoc(rl);
25242559 defer block_scope.instructions.deinit(mod.gpa);
25252560
25262561 var items = std.ArrayList(zir.Inst.Ref).init(mod.gpa);
......@@ -3911,69 +3946,3 @@ fn rvalue(
39113946 },
39123947 }
39133948}
3914
3915fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZir) ResultLoc.Strategy {
3916 var elide_store_to_block_ptr_instructions = false;
3917 switch (rl) {
3918 // In this branch there will not be any store_to_block_ptr instructions.
3919 .discard, .none, .ty, .ref => return .{
3920 .tag = .break_operand,
3921 .elide_store_to_block_ptr_instructions = false,
3922 },
3923 // The pointer got passed through to the sub-expressions, so we will use
3924 // break_void here.
3925 // In this branch there will not be any store_to_block_ptr instructions.
3926 .ptr => return .{
3927 .tag = .break_void,
3928 .elide_store_to_block_ptr_instructions = false,
3929 },
3930 .inferred_ptr, .bitcasted_ptr, .block_ptr => {
3931 if (block_scope.rvalue_rl_count == block_scope.break_count) {
3932 // Neither prong of the if consumed the result location, so we can
3933 // use break instructions to create an rvalue.
3934 return .{
3935 .tag = .break_operand,
3936 .elide_store_to_block_ptr_instructions = true,
3937 };
3938 } else {
3939 // Allow the store_to_block_ptr instructions to remain so that
3940 // semantic analysis can turn them into bitcasts.
3941 return .{
3942 .tag = .break_void,
3943 .elide_store_to_block_ptr_instructions = false,
3944 };
3945 }
3946 },
3947 }
3948}
3949
3950fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void {
3951 // Depending on whether the result location is a pointer or value, different
3952 // ZIR needs to be generated. In the former case we rely on storing to the
3953 // pointer to communicate the result, and use breakvoid; in the latter case
3954 // the block break instructions will have the result values.
3955 // One more complication: when the result location is a pointer, we detect
3956 // the scenario where the result location is not consumed. In this case
3957 // we emit ZIR for the block break instructions to have the result values,
3958 // and then rvalue() on that to pass the value to the result location.
3959 switch (parent_rl) {
3960 .discard, .none, .ty, .ptr, .ref => {
3961 block_scope.break_result_loc = parent_rl;
3962 },
3963
3964 .inferred_ptr => |ptr| {
3965 block_scope.rl_ptr = ptr;
3966 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3967 },
3968
3969 .bitcasted_ptr => |ptr| {
3970 block_scope.rl_ptr = ptr;
3971 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3972 },
3973
3974 .block_ptr => |parent_block_scope| {
3975 block_scope.rl_ptr = parent_block_scope.rl_ptr;
3976 block_scope.break_result_loc = .{ .block_ptr = block_scope };
3977 },
3978 }
3979}
src/Module.zig+32-1
......@@ -920,7 +920,7 @@ pub const Scope = struct {
920920 label: ?Label = null,
921921 break_block: zir.Inst.Index = 0,
922922 continue_block: zir.Inst.Index = 0,
923 /// Only valid when setBlockResultLoc is called.
923 /// Only valid when setBreakResultLoc is called.
924924 break_result_loc: AstGen.ResultLoc = undefined,
925925 /// When a block has a pointer result location, here it is.
926926 rl_ptr: zir.Inst.Ref = .none,
......@@ -973,6 +973,37 @@ pub const Scope = struct {
973973 return &gz.astgen.decl.container.file_scope.tree;
974974 }
975975
976 pub fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
977 // Depending on whether the result location is a pointer or value, different
978 // ZIR needs to be generated. In the former case we rely on storing to the
979 // pointer to communicate the result, and use breakvoid; in the latter case
980 // the block break instructions will have the result values.
981 // One more complication: when the result location is a pointer, we detect
982 // the scenario where the result location is not consumed. In this case
983 // we emit ZIR for the block break instructions to have the result values,
984 // and then rvalue() on that to pass the value to the result location.
985 switch (parent_rl) {
986 .discard, .none, .ty, .ptr, .ref => {
987 gz.break_result_loc = parent_rl;
988 },
989
990 .inferred_ptr => |ptr| {
991 gz.rl_ptr = ptr;
992 gz.break_result_loc = .{ .block_ptr = gz };
993 },
994
995 .bitcasted_ptr => |ptr| {
996 gz.rl_ptr = ptr;
997 gz.break_result_loc = .{ .block_ptr = gz };
998 },
999
1000 .block_ptr => |parent_block_scope| {
1001 gz.rl_ptr = parent_block_scope.rl_ptr;
1002 gz.break_result_loc = .{ .block_ptr = gz };
1003 },
1004 }
1005 }
1006
9761007 pub fn setBoolBrBody(gz: GenZir, inst: zir.Inst.Index) !void {
9771008 const gpa = gz.astgen.mod.gpa;
9781009 try gz.astgen.extra.ensureCapacity(gpa, gz.astgen.extra.items.len +