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(...@@ -2122,16 +2122,18 @@ pub fn addSwitchBr(
2122 src: usize,2122 src: usize,
2123 target_ptr: *Inst,2123 target_ptr: *Inst,
2124 cases: []Inst.SwitchBr.Case,2124 cases: []Inst.SwitchBr.Case,
2125 else_body: ir.Body,
2125) !*Inst {2126) !*Inst {
2126 const inst = try block.arena.create(Inst.SwitchBr);2127 const inst = try block.arena.create(Inst.SwitchBr);
2127 inst.* = .{2128 inst.* = .{
2128 .base = .{2129 .base = .{
2129 .tag = .switchbr,2130 .tag = .switchbr,
2130 .ty = Type.initTag(.void),2131 .ty = Type.initTag(.noreturn),
2131 .src = src,2132 .src = src,
2132 },2133 },
2133 .target_ptr = target_ptr,2134 .target_ptr = target_ptr,
2134 .cases = cases,2135 .cases = cases,
2136 .else_body = else_body,
2135 };2137 };
2136 try block.instructions.append(self.gpa, &inst.base);2138 try block.instructions.append(self.gpa, &inst.base);
2137 return &inst.base;2139 return &inst.base;
src/astgen.zig+35-28
...@@ -1581,14 +1581,6 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1581,14 +1581,6 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1581 };1581 };
1582 defer block_scope.instructions.deinit(mod.gpa);1582 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
1592 const tree = scope.tree();1584 const tree = scope.tree();
1593 const switch_src = tree.token_locs[switch_node.switch_token].start;1585 const switch_src = tree.token_locs[switch_node.switch_token].start;
1594 const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);1586 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...@@ -1598,6 +1590,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1598 .target_ptr = target_ptr,1590 .target_ptr = target_ptr,
1599 .cases = undefined, // populated below1591 .cases = undefined, // populated below
1600 .items = &[_]*zir.Inst{}, // populated below1592 .items = &[_]*zir.Inst{}, // populated below
1593 .else_body = undefined, // populated below
1601 }, .{})).castTag(.switchbr).?;1594 }, .{})).castTag(.switchbr).?;
16021595
1603 var items = std.ArrayList(*zir.Inst).init(mod.gpa);1596 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...@@ -1611,7 +1604,7 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1611 });1604 });
1612 // then add block containing the switch.1605 // then add block containing the switch.
1613 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{1606 const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
1614 .instructions = undefined, // populated below1607 .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
1615 });1608 });
16161609
1617 // Most result location types can be forwarded directly; however1610 // 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...@@ -1622,6 +1615,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1622 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },1615 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
1623 };1616 };
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
1625 var case_scope: Scope.GenZIR = .{1626 var case_scope: Scope.GenZIR = .{
1626 .parent = scope,1627 .parent = scope,
1627 .decl = block_scope.decl,1628 .decl = block_scope.decl,
...@@ -1630,6 +1631,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1630,6 +1631,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1630 };1631 };
1631 defer case_scope.instructions.deinit(mod.gpa);1632 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
1633 // first we gather all the switch items and check else/'_' prongs1642 // first we gather all the switch items and check else/'_' prongs
1634 var else_src: ?usize = null;1643 var else_src: ?usize = null;
1635 var underscore_src: ?usize = null;1644 var underscore_src: ?usize = null;
...@@ -1701,12 +1710,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1701,12 +1710,12 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1701 if (first_range == null) first_range = range_inst;1710 if (first_range == null) first_range = range_inst;
17021711
1703 // target >= start and target <= end1712 // target >= start and target <= end
1704 const range_start_ok = try addZIRBinOp(mod, &block_scope.base, range_src, .cmp_gte, target, start);1713 const range_start_ok = try addZIRBinOp(mod, &else_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);1714 const range_end_ok = try addZIRBinOp(mod, &else_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);1715 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .booland, range_start_ok, range_end_ok);
17071716
1708 if (any_ok) |some| {1717 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);
1710 } else {1719 } else {
1711 any_ok = range_ok;1720 any_ok = range_ok;
1712 }1721 }
...@@ -1715,16 +1724,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1715,16 +1724,16 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
17151724
1716 const item_inst = try expr(mod, &item_scope.base, .none, item);1725 const item_inst = try expr(mod, &item_scope.base, .none, item);
1717 try items.append(item_inst);1726 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
1720 if (any_ok) |some| {1729 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);
1722 } else {1731 } else {
1723 any_ok = cpm_ok;1732 any_ok = cpm_ok;
1724 }1733 }
1725 }1734 }
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, .{
1728 .condition = any_ok.?,1737 .condition = any_ok.?,
1729 .then_body = undefined, // populated below1738 .then_body = undefined, // populated below
1730 .else_body = undefined, // populated below1739 .else_body = undefined, // populated below
...@@ -1754,6 +1763,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node...@@ -1754,6 +1763,14 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1754 };1763 };
1755 }1764 }
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
1757 // All items have been generated, add the instructions to the comptime block.1774 // All items have been generated, add the instructions to the comptime block.
1758 item_block.positionals.body = .{1775 item_block.positionals.body = .{
1759 .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),1776 .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...@@ -1765,18 +1782,8 @@ fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node
1765 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);1782 switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
1766 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);1783 switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
1767 switch_inst.kw_args.range = first_range;1784 switch_inst.kw_args.range = first_range;
17681785 switch_inst.positionals.else_body = .{
1769 // Generate else block or a break last to finish the block.1786 .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
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),
1780 };1787 };
1781 return &block.base;1788 return &block.base;
1782}1789}
src/ir.zig+7-1
...@@ -472,8 +472,11 @@ pub const Inst = struct {...@@ -472,8 +472,11 @@ pub const Inst = struct {
472 target_ptr: *Inst,472 target_ptr: *Inst,
473 cases: []Case,473 cases: []Case,
474 /// Set of instructions whose lifetimes end at the start of one of the cases.474 /// 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, ... ].
476 deaths: [*]*Inst = undefined,476 deaths: [*]*Inst = undefined,
477 else_index: u32 = 0,
478 else_deaths: u32 = 0,
479 else_body: Body,
477480
478 pub const Case = struct {481 pub const Case = struct {
479 item: Value,482 item: Value,
...@@ -498,6 +501,9 @@ pub const Inst = struct {...@@ -498,6 +501,9 @@ pub const Inst = struct {
498 const case = self.cases[case_index];501 const case = self.cases[case_index];
499 return (self.deaths + case.index)[0..case.deaths];502 return (self.deaths + case.index)[0..case.deaths];
500 }503 }
504 pub fn elseDeaths(self: *const SwitchBr) []*Inst {
505 return (self.deaths + self.else_index)[0..self.else_deaths];
506 }
501 };507 };
502};508};
503509
src/zir.zig+10-2
...@@ -509,7 +509,6 @@ pub const Inst = struct {...@@ -509,7 +509,6 @@ pub const Inst = struct {
509 .slice,509 .slice,
510 .slice_start,510 .slice_start,
511 .import,511 .import,
512 .switchbr,
513 .switch_range,512 .switch_range,
514 => false,513 => false,
515514
...@@ -522,6 +521,7 @@ pub const Inst = struct {...@@ -522,6 +521,7 @@ pub const Inst = struct {
522 .unreach_nocheck,521 .unreach_nocheck,
523 .@"unreachable",522 .@"unreachable",
524 .loop,523 .loop,
524 .switchbr,
525 => true,525 => true,
526 };526 };
527 }527 }
...@@ -1012,9 +1012,10 @@ pub const Inst = struct {...@@ -1012,9 +1012,10 @@ pub const Inst = struct {
10121012
1013 positionals: struct {1013 positionals: struct {
1014 target_ptr: *Inst,1014 target_ptr: *Inst,
1015 cases: []Case,
1016 /// List of all individual items and ranges1015 /// List of all individual items and ranges
1017 items: []*Inst,1016 items: []*Inst,
1017 cases: []Case,
1018 else_body: Module.Body,
1018 },1019 },
1019 kw_args: struct {1020 kw_args: struct {
1020 /// Pointer to first range if such exists.1021 /// Pointer to first range if such exists.
...@@ -2569,6 +2570,7 @@ const EmitZIR = struct {...@@ -2569,6 +2570,7 @@ const EmitZIR = struct {
2569 .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),2570 .target_ptr = try self.resolveInst(new_body, old_inst.target_ptr),
2570 .cases = cases,2571 .cases = cases,
2571 .items = &[_]*Inst{}, // TODO this should actually be populated2572 .items = &[_]*Inst{}, // TODO this should actually be populated
2573 .else_body = undefined, // populated below
2572 },2574 },
2573 .kw_args = .{},2575 .kw_args = .{},
2574 };2576 };
...@@ -2590,6 +2592,12 @@ const EmitZIR = struct {...@@ -2590,6 +2592,12 @@ const EmitZIR = struct {
2590 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },2592 .body = .{ .instructions = try self.arena.allocator.dupe(*Inst, body_tmp.items) },
2591 };2593 };
2592 }2594 }
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 };
2593 break :blk &new_inst.base;2601 break :blk &new_inst.base;
2594 },2602 },
2595 .varptr => @panic("TODO"),2603 .varptr => @panic("TODO"),
src/zir_sema.zig+27-7
...@@ -1238,7 +1238,20 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1238,7 +1238,20 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1238 const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);1238 const target = try mod.analyzeDeref(scope, inst.base.src, target_ptr, inst.positionals.target_ptr.src);
1239 try validateSwitch(mod, scope, target, inst);1239 try validateSwitch(mod, scope, target, inst);
12401240
1241 // TODO comptime execution1241 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
1243 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);1256 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);
1244 const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len);1257 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...@@ -1253,7 +1266,7 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1253 };1266 };
1254 defer case_block.instructions.deinit(mod.gpa);1267 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| {
1257 // Reset without freeing.1270 // Reset without freeing.
1258 case_block.instructions.items.len = 0;1271 case_block.instructions.items.len = 0;
12591272
...@@ -1269,7 +1282,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In...@@ -1269,7 +1282,14 @@ fn analyzeInstSwitchBr(mod: *Module, scope: *Scope, inst: *zir.Inst.SwitchBr) In
1269 };1282 };
1270 }1283 }
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);
1273}1293}
12741294
1275fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.SwitchBr) InnerError!void {1295fn 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...@@ -1354,14 +1374,14 @@ fn validateSwitch(mod: *Module, scope: *Scope, target: *Inst, inst: *zir.Inst.Sw
1354 false_count += 1;1374 false_count += 1;
1355 }1375 }
13561376
1357 if (true_count > 1 or false_count > 1) {1377 if (true_count + false_count > 2) {
1358 return mod.fail(scope, item.src, "duplicate switch value", .{});1378 return mod.fail(scope, item.src, "duplicate switch value", .{});
1359 }1379 }
1360 }1380 }
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") {
1362 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});1382 return mod.fail(scope, inst.base.src, "switch must handle all possibilities", .{});
1363 }1383 }
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") {
1365 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});1385 return mod.fail(scope, inst.base.src, "unreachable else prong, all cases already handled", .{});
1366 }1386 }
1367 },1387 },
...@@ -1696,7 +1716,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE...@@ -1696,7 +1716,7 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE
1696 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {1716 if (try mod.resolveDefinedValue(scope, cond)) |cond_val| {
1697 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;1717 const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body;
1698 try analyzeBody(mod, scope, body.*);1718 try analyzeBody(mod, scope, body.*);
1699 return mod.constVoid(scope, inst.base.src);1719 return mod.constNoReturn(scope, inst.base.src);
1700 }1720 }
17011721
1702 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);1722 const parent_block = try mod.requireRuntimeBlock(scope, inst.base.src);