authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-20 22:00:30+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-10-30 15:58:13+02:00
log769d5a9c435c5c145983e4d3af1706924248e367
tree1aa427c706261ccf5256e8699be331601821251e
parent12e4c648ccc68f5190dd5076088b3959ebeee65d
signaturelock-open Commit is signed but in an unrecognized format.

stage2: switch comptime execution


5 files changed, 82 insertions(+), 39 deletions(-)

src/Module.zig+3-1
......@@ -2122,16 +2122,18 @@ pub fn addSwitchBr(
21222122 src: usize,
21232123 target_ptr: *Inst,
21242124 cases: []Inst.SwitchBr.Case,
2125 else_body: ir.Body,
21252126) !*Inst {
21262127 const inst = try block.arena.create(Inst.SwitchBr);
21272128 inst.* = .{
21282129 .base = .{
21292130 .tag = .switchbr,
2130 .ty = Type.initTag(.void),
2131 .ty = Type.initTag(.noreturn),
21312132 .src = src,
21322133 },
21332134 .target_ptr = target_ptr,
21342135 .cases = cases,
2136 .else_body = else_body,
21352137 };
21362138 try block.instructions.append(self.gpa, &inst.base);
21372139 return &inst.base;
src/astgen.zig+35-28
......@@ -1581,14 +1581,6 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
15811581 };
15821582 defer block_scope.instructions.deinit(mod.gpa);
15831583
1584 var item_scope: Scope.GenZIR = .{
1585 .parent = scope,
1586 .decl = scope.decl().?,
1587 .arena = scope.arena(),
1588 .instructions = .{},
1589 };
1590 defer item_scope.instructions.deinit(mod.gpa);
1591
15921584 const tree = scope.tree();
15931585 const switch_src = tree.token_locs[switch_node.switch_token].start;
15941586 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
......@@ -1598,6 +1590,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
15981590 .target_ptr = target_ptr,
15991591 .cases = undefined, // populated below
16001592 .items = &[_]*zir.Inst{}, // populated below
1593 .else_body = undefined, // populated below
16011594 }, .{})).castTag(.switchbr).?;
16021595
16031596 var items = std.ArrayList(*zir.Inst).init(mod.gpa);
......@@ -1611,7 +1604,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
16111604 });
16121605 // then add block containing the switch.
16131606 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
1614 .instructions = undefined, // populated below
1607 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
16151608 });
16161609
16171610 // Most result location types can be forwarded directly; however
......@@ -1622,6 +1615,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
16221615 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
16231616 };
16241617
1618 var item_scope: Scope.GenZIR = .{
1619 .parent = scope,
1620 .decl = scope.decl().?,
1621 .arena = scope.arena(),
1622 .instructions = .{},
1623 };
1624 defer item_scope.instructions.deinit(mod.gpa);
1625
16251626 var case_scope: Scope.GenZIR = .{
16261627 .parent = scope,
16271628 .decl = block_scope.decl,
......@@ -1630,6 +1631,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
16301631 };
16311632 defer case_scope.instructions.deinit(mod.gpa);
16321633
1634 var else_scope: Scope.GenZIR = .{
1635 .parent = scope,
1636 .decl = block_scope.decl,
1637 .arena = block_scope.arena,
1638 .instructions = .{},
1639 };
1640 defer else_scope.instructions.deinit(mod.gpa);
1641
16331642 // first we gather all the switch items and check else/'_' prongs
16341643 var else_src: ?usize = null;
16351644 var underscore_src: ?usize = null;
......@@ -1701,12 +1710,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
17011710 if (first_range == null) first_range = range_inst;
17021711
17031712 // target >= start and target <= end
1704 const range_start_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_gte, target, start);
1705 const range_end_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_lte, target, end);
1706 const range_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .booland, range_start_ok, range_end_ok);
1713 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start);
1714 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end);
1715 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .booland, range_start_ok, range_end_ok);
17071716
17081717 if (any_ok) |some| {
1709 any_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .boolor, some, range_ok);
1718 any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .boolor, some, range_ok);
17101719 } else {
17111720 any_ok = range_ok;
17121721 }
......@@ -1715,16 +1724,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
17151724
17161725 const item_inst = try expr(mod, &item_scope.base, .none, item);
17171726 try items.append(item_inst);
1718 const cpm_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .cmp_eq, target, item_inst);
1727 const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
17191728
17201729 if (any_ok) |some| {
1721 any_ok = try addZIRBinOp(mod, &block_scope.base, item_inst.src, .boolor, some, cpm_ok);
1730 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .boolor, some, cpm_ok);
17221731 } else {
17231732 any_ok = cpm_ok;
17241733 }
17251734 }
17261735
1727 const condbr = try addZIRInstSpecial(mod, &block_scope.base, case_src, zir.Inst.CondBr, .{
1736 const condbr = try addZIRInstSpecial(mod, &else_scope.base, case_src, zir.Inst.CondBr, .{
17281737 .condition = any_ok.?,
17291738 .then_body = undefined, // populated below
17301739 .else_body = undefined, // populated below
......@@ -1754,6 +1763,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
17541763 };
17551764 }
17561765
1766 // Generate else block or a break last to finish the block.
1767 if (special_case) |case| {
1768 try switchCaseExpr(mod, &else_scope.base, case_rl, block, case);
1769 } else {
1770 // Not handling all possible cases is a compile error.
1771 _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreach_nocheck);
1772 }
1773
17571774 // All items have been generated, add the instructions to the comptime block.
17581775 item_block.positionals.body = .{
17591776 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
......@@ -1765,18 +1782,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
17651782 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
17661783 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
17671784 switch_inst.kw_args.range = first_range;
1768
1769 // Generate else block or a break last to finish the block.
1770 if (special_case) |case| {
1771 try switchCaseExpr(mod, &block_scope.base, case_rl, block, case);
1772 } else {
1773 // Not handling all possible cases is a compile error.
1774 _ = try addZIRNoOp(mod, &block_scope.base, switch_src, .unreach_nocheck);
1775 }
1776
1777 // Set block instructions now that it is finished.
1778 block.positionals.body = .{
1779 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1785 switch_inst.positionals.else_body = .{
1786 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
17801787 };
17811788 return &block.base;
17821789}
src/ir.zig+7-1
......@@ -472,8 +472,11 @@ pub const Inst = struct {
472472 target_ptr: *Inst,
473473 cases: []Case,
474474 /// Set of instructions whose lifetimes end at the start of one of the cases.
475 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... , case_n_count ... else_count].
475 /// In same order as cases, deaths[0..case_0_count, case_0_count .. case_1_count, ... ].
476476 deaths: [*]*Inst = undefined,
477 else_index: u32 = 0,
478 else_deaths: u32 = 0,
479 else_body: Body,
477480
478481 pub const Case = struct {
479482 item: Value,
......@@ -498,6 +501,9 @@ pub const Inst = struct {
498501 const case = self.cases[case_index];
499502 return (self.deaths + case.index)[0..case.deaths];
500503 }
504 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
505 return (self.deaths + self.else_index)[0..self.else_deaths];
506 }
501507 };
502508};
503509
src/zir.zig+10-2
......@@ -509,7 +509,6 @@ pub const Inst = struct {
509509 .slice,
510510 .slice_start,
511511 .import,
512 .switchbr,
513512 .switch_range,
514513 => false,
515514
......@@ -522,6 +521,7 @@ pub const Inst = struct {
522521 .unreach_nocheck,
523522 .@"unreachable",
524523 .loop,
524 .switchbr,
525525 => true,
526526 };
527527 }
......@@ -1012,9 +1012,10 @@ pub const Inst = struct {
10121012
10131013 positionals: struct {
10141014 target_ptr: *Inst,
1015 cases: []Case,
10161015 /// List of all individual items and ranges
10171016 items: []*Inst,
1017 cases: []Case,
1018 else_body: Module.Body,
10181019 },
10191020 kw_args: struct {
10201021 /// Pointer to first range if such exists.
......@@ -2569,6 +2570,7 @@ const EmitZIR = struct {
25692570 .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
25702571 .cases = cases,
25712572 .items = &[_]*Inst{}, // TODO this should actually be populated
2573 .else_body = undefined, // populated below
25722574 },
25732575 .kw_args = .{},
25742576 };
......@@ -2590,6 +2592,12 @@ const EmitZIR = struct {
25902592 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
25912593 };
25922594 }
2595
2596 body_tmp.items.len = 0;
2597 try self.emitBody(old_inst.else_body, inst_table, &body_tmp);
2598 new_inst.positionals.else_body = .{
2599 .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items),
2600 };
25932601 break :blk &new_inst.base;
25942602 },
25952603 .varptr => @panic("TODO"),
src/zir_sema.zig+27-7
......@@ -1238,7 +1238,20 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
12381238 const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
12391239 try validateSwitch(mod, scope, target, inst);
12401240
1241 // TODO comptime execution
1241 if (try mod.resolveDefinedValue(scope, target)) |target_val| {
1242 for (inst.positionals.cases) |case| {
1243 const resolved = try resolveInst(mod, scope, case.item);
1244 const casted = try mod.coerce(scope, target.ty, resolved);
1245 const item = try mod.resolveConstValue(scope, casted);
1246
1247 if (target_val.eql(item)) {
1248 try analyzeBody(mod, scope, case.body);
1249 return mod.constNoReturn(scope, inst.base.src);
1250 }
1251 }
1252 try analyzeBody(mod, scope, inst.positionals.else_body);
1253 return mod.constNoReturn(scope, inst.base.src);
1254 }
12421255
12431256 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
12441257 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);
......@@ -1253,7 +1266,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
12531266 };
12541267 defer case_block.instructions.deinit(mod.gpa);
12551268
1256 for (inst.positionals.cases[0..inst.positionals.cases.len]) |case, i| {
1269 for (inst.positionals.cases) |case, i| {
12571270 // Reset without freeing.
12581271 case_block.instructions.items.len = 0;
12591272
......@@ -1269,7 +1282,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
12691282 };
12701283 }
12711284
1272 return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases);
1285 case_block.instructions.items.len = 0;
1286 try analyzeBody(mod, &case_block.base, inst.positionals.else_body);
1287
1288 const else_body: ir.Body = .{
1289 .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items),
1290 };
1291
1292 return mod.addSwitchBr(parent_block, inst.base.src, target_ptr, cases, else_body);
12731293}
12741294
12751295fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {
......@@ -1354,14 +1374,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
13541374 false_count += 1;
13551375 }
13561376
1357 if (true_count > 1 or false_count > 1) {
1377 if (true_count + false_count > 2) {
13581378 return mod.fail(scope, item.src, "duplicate switch value", .{});
13591379 }
13601380 }
1361 if ((true_count == 0 or false_count == 0) and inst.kw_args.special_prong != .@"else") {
1381 if ((true_count + false_count < 2) and inst.kw_args.special_prong != .@"else") {
13621382 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
13631383 }
1364 if ((true_count == 1 and false_count == 1) and inst.kw_args.special_prong == .@"else") {
1384 if ((true_count + false_count == 2) and inst.kw_args.special_prong == .@"else") {
13651385 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
13661386 }
13671387 },
......@@ -1696,7 +1716,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
16961716 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
16971717 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
16981718 try analyzeBody(mod, scope, body.*);
1699 return mod.constVoid(scope, inst.base.src);
1719 return mod.constNoReturn(scope, inst.base.src);
17001720 }
17011721
17021722 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);