authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-25 00:37:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-25 00:55:36-07:00
log31023de6c4b3957ef356be01b5454426844955a9
tree7951b8c7265ace392bde49165b505762f1cc33e8
parent12d18a36e5eed4b0898e93df2fd13b56061049b3

stage2: implement inline while

Introduce "inline" variants of ZIR tags: * block => block_inline * repeat => repeat_inline * break => break_inline * condbr => condbr_inline The inline variants perform control flow at compile-time, and they utilize the return value of `Sema.analyzeBody`. `analyzeBody` now returns an Index, not a Ref, which is the ZIR index of a break instruction. This effectively communicates both the intended break target block as well as the operand, allowing parent blocks to find out whether they, in turn, should return the break instruction up the call stack, or accept the operand as the block's result and continue analyzing instructions in the block. Additionally: * removed the deprecated ZIR tag `block_comptime`. * removed `break_void_node` so that all break instructions use the same Data. * zir.Code: remove the `root_start` and `root_len` fields. There is now implied to be a block at index 0 for the root body. This is so that `break_inline` has something to point at and we no longer need the special instruction `break_flat`. * implement source location byteOffset() for .node_offset_if_cond .node_offset_for_cond is probably redundant and can be deleted. We don't have `comptime var` supported yet, so this commit adds a test that at least makes sure the condition is required to be comptime known for `inline while`.

6 files changed, 227 insertions(+), 176 deletions(-)

lib/std/zig/parse.zig+1-4
...@@ -59,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {...@@ -59,10 +59,7 @@ pub fn parse(gpa: *Allocator, source: []const u8) Allocator.Error!Tree {
59 parser.nodes.appendAssumeCapacity(.{59 parser.nodes.appendAssumeCapacity(.{
60 .tag = .root,60 .tag = .root,
61 .main_token = 0,61 .main_token = 0,
62 .data = .{62 .data = undefined,
63 .lhs = undefined,
64 .rhs = undefined,
65 },
66 });63 });
67 const root_members = try parser.parseContainerMembers();64 const root_members = try parser.parseContainerMembers();
68 const root_decls = try root_members.toSpan(&parser);65 const root_decls = try root_members.toSpan(&parser);
src/Module.zig+58-58
...@@ -952,15 +952,11 @@ pub const Scope = struct {...@@ -952,15 +952,11 @@ pub const Scope = struct {
952 /// initialized, but empty, state.952 /// initialized, but empty, state.
953 pub fn finish(gz: *GenZir) !zir.Code {953 pub fn finish(gz: *GenZir) !zir.Code {
954 const gpa = gz.zir_code.gpa;954 const gpa = gz.zir_code.gpa;
955 const root_start = @intCast(u32, gz.zir_code.extra.items.len);955 try gz.setBlockBody(0);
956 const root_len = @intCast(u32, gz.instructions.items.len);
957 try gz.zir_code.extra.appendSlice(gpa, gz.instructions.items);
958 return zir.Code{956 return zir.Code{
959 .instructions = gz.zir_code.instructions.toOwnedSlice(),957 .instructions = gz.zir_code.instructions.toOwnedSlice(),
960 .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa),958 .string_bytes = gz.zir_code.string_bytes.toOwnedSlice(gpa),
961 .extra = gz.zir_code.extra.toOwnedSlice(gpa),959 .extra = gz.zir_code.extra.toOwnedSlice(gpa),
962 .root_start = root_start,
963 .root_len = root_len,
964 };960 };
965 }961 }
966962
...@@ -1224,11 +1220,12 @@ pub const Scope = struct {...@@ -1224,11 +1220,12 @@ pub const Scope = struct {
12241220
1225 pub fn addBreak(1221 pub fn addBreak(
1226 gz: *GenZir,1222 gz: *GenZir,
1223 tag: zir.Inst.Tag,
1227 break_block: zir.Inst.Index,1224 break_block: zir.Inst.Index,
1228 operand: zir.Inst.Ref,1225 operand: zir.Inst.Ref,
1229 ) !zir.Inst.Index {1226 ) !zir.Inst.Index {
1230 return gz.addAsIndex(.{1227 return gz.addAsIndex(.{
1231 .tag = .@"break",1228 .tag = tag,
1232 .data = .{ .@"break" = .{1229 .data = .{ .@"break" = .{
1233 .block_inst = break_block,1230 .block_inst = break_block,
1234 .operand = operand,1231 .operand = operand,
...@@ -1236,20 +1233,6 @@ pub const Scope = struct {...@@ -1236,20 +1233,6 @@ pub const Scope = struct {
1236 });1233 });
1237 }1234 }
12381235
1239 pub fn addBreakVoid(
1240 gz: *GenZir,
1241 break_block: zir.Inst.Index,
1242 node_index: ast.Node.Index,
1243 ) !zir.Inst.Index {
1244 return gz.addAsIndex(.{
1245 .tag = .break_void_node,
1246 .data = .{ .break_void_node = .{
1247 .src_node = gz.zir_code.decl.nodeIndexToRelative(node_index),
1248 .block_inst = break_block,
1249 } },
1250 });
1251 }
1252
1253 pub fn addBin(1236 pub fn addBin(
1254 gz: *GenZir,1237 gz: *GenZir,
1255 tag: zir.Inst.Tag,1238 tag: zir.Inst.Tag,
...@@ -1323,11 +1306,11 @@ pub const Scope = struct {...@@ -1323,11 +1306,11 @@ pub const Scope = struct {
13231306
1324 /// Note that this returns a `zir.Inst.Index` not a ref.1307 /// Note that this returns a `zir.Inst.Index` not a ref.
1325 /// Leaves the `payload_index` field undefined.1308 /// Leaves the `payload_index` field undefined.
1326 pub fn addCondBr(gz: *GenZir, node: ast.Node.Index) !zir.Inst.Index {1309 pub fn addCondBr(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
1327 try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1);1310 try gz.instructions.ensureCapacity(gz.zir_code.gpa, gz.instructions.items.len + 1);
1328 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);1311 const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
1329 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{1312 try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
1330 .tag = .condbr,1313 .tag = tag,
1331 .data = .{ .pl_node = .{1314 .data = .{ .pl_node = .{
1332 .src_node = gz.zir_code.decl.nodeIndexToRelative(node),1315 .src_node = gz.zir_code.decl.nodeIndexToRelative(node),
1333 .payload_index = undefined,1316 .payload_index = undefined,
...@@ -1462,6 +1445,24 @@ pub const WipZirCode = struct {...@@ -1462,6 +1445,24 @@ pub const WipZirCode = struct {
1462 }1445 }
1463};1446};
14641447
1448/// Call `deinit` on the result.
1449fn initAstGen(mod: *Module, decl: *Decl, arena: *Allocator) !WipZirCode {
1450 var wzc: WipZirCode = .{
1451 .decl = decl,
1452 .arena = arena,
1453 .gpa = mod.gpa,
1454 };
1455 // Must be a block instruction at index 0 with the root body.
1456 try wzc.instructions.append(mod.gpa, .{
1457 .tag = .block,
1458 .data = .{ .pl_node = .{
1459 .src_node = 0,
1460 .payload_index = undefined,
1461 } },
1462 });
1463 return wzc;
1464}
1465
1465/// This struct holds data necessary to construct API-facing `AllErrors.Message`.1466/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
1466/// Its memory is managed with the general purpose allocator so that they1467/// Its memory is managed with the general purpose allocator so that they
1467/// can be created and destroyed in response to incremental updates.1468/// can be created and destroyed in response to incremental updates.
...@@ -1572,10 +1573,10 @@ pub const SrcLoc = struct {...@@ -1572,10 +1573,10 @@ pub const SrcLoc = struct {
1572 const token_starts = tree.tokens.items(.start);1573 const token_starts = tree.tokens.items(.start);
1573 return token_starts[tok_index];1574 return token_starts[tok_index];
1574 },1575 },
1575 .node_abs => |node_index| {1576 .node_abs => |node| {
1576 const tree = src_loc.container.file_scope.base.tree();1577 const tree = src_loc.container.file_scope.base.tree();
1577 const token_starts = tree.tokens.items(.start);1578 const token_starts = tree.tokens.items(.start);
1578 const tok_index = tree.firstToken(node_index);1579 const tok_index = tree.firstToken(node);
1579 return token_starts[tok_index];1580 return token_starts[tok_index];
1580 },1581 },
1581 .byte_offset => |byte_off| {1582 .byte_offset => |byte_off| {
...@@ -1591,15 +1592,14 @@ pub const SrcLoc = struct {...@@ -1591,15 +1592,14 @@ pub const SrcLoc = struct {
1591 },1592 },
1592 .node_offset => |node_off| {1593 .node_offset => |node_off| {
1593 const decl = src_loc.container.decl;1594 const decl = src_loc.container.decl;
1594 const node_index = decl.relativeToNodeIndex(node_off);1595 const node = decl.relativeToNodeIndex(node_off);
1595 const tree = decl.container.file_scope.base.tree();1596 const tree = decl.container.file_scope.base.tree();
1596 const main_tokens = tree.nodes.items(.main_token);1597 const main_tokens = tree.nodes.items(.main_token);
1597 const tok_index = main_tokens[node_index];1598 const tok_index = main_tokens[node];
1598 const token_starts = tree.tokens.items(.start);1599 const token_starts = tree.tokens.items(.start);
1599 return token_starts[tok_index];1600 return token_starts[tok_index];
1600 },1601 },
1601 .node_offset_var_decl_ty => @panic("TODO"),1602 .node_offset_var_decl_ty => @panic("TODO"),
1602 .node_offset_for_cond => @panic("TODO"),
1603 .node_offset_builtin_call_arg0 => @panic("TODO"),1603 .node_offset_builtin_call_arg0 => @panic("TODO"),
1604 .node_offset_builtin_call_arg1 => @panic("TODO"),1604 .node_offset_builtin_call_arg1 => @panic("TODO"),
1605 .node_offset_builtin_call_argn => unreachable, // Handled specially in `Sema`.1605 .node_offset_builtin_call_argn => unreachable, // Handled specially in `Sema`.
...@@ -1610,7 +1610,27 @@ pub const SrcLoc = struct {...@@ -1610,7 +1610,27 @@ pub const SrcLoc = struct {
1610 .node_offset_deref_ptr => @panic("TODO"),1610 .node_offset_deref_ptr => @panic("TODO"),
1611 .node_offset_asm_source => @panic("TODO"),1611 .node_offset_asm_source => @panic("TODO"),
1612 .node_offset_asm_ret_ty => @panic("TODO"),1612 .node_offset_asm_ret_ty => @panic("TODO"),
1613 .node_offset_if_cond => @panic("TODO"),1613
1614 .node_offset_for_cond, .node_offset_if_cond => |node_off| {
1615 const decl = src_loc.container.decl;
1616 const node = decl.relativeToNodeIndex(node_off);
1617 const tree = decl.container.file_scope.base.tree();
1618 const node_tags = tree.nodes.items(.tag);
1619 const cond_expr = switch (node_tags[node]) {
1620 .if_simple => tree.ifSimple(node).ast.cond_expr,
1621 .@"if" => tree.ifFull(node).ast.cond_expr,
1622 .while_simple => tree.whileSimple(node).ast.cond_expr,
1623 .while_cont => tree.whileCont(node).ast.cond_expr,
1624 .@"while" => tree.whileFull(node).ast.cond_expr,
1625 .for_simple => tree.forSimple(node).ast.cond_expr,
1626 .@"for" => tree.forFull(node).ast.cond_expr,
1627 else => unreachable,
1628 };
1629 const main_tokens = tree.nodes.items(.main_token);
1630 const tok_index = main_tokens[cond_expr];
1631 const token_starts = tree.tokens.items(.start);
1632 return token_starts[tok_index];
1633 },
1614 .node_offset_bin_op => @panic("TODO"),1634 .node_offset_bin_op => @panic("TODO"),
1615 .node_offset_bin_lhs => @panic("TODO"),1635 .node_offset_bin_lhs => @panic("TODO"),
1616 .node_offset_bin_rhs => @panic("TODO"),1636 .node_offset_bin_rhs => @panic("TODO"),
...@@ -2034,11 +2054,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {...@@ -2034,11 +2054,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
2034 defer analysis_arena.deinit();2054 defer analysis_arena.deinit();
20352055
2036 var code: zir.Code = blk: {2056 var code: zir.Code = blk: {
2037 var wip_zir_code: WipZirCode = .{2057 var wip_zir_code = try mod.initAstGen(decl, &analysis_arena.allocator);
2038 .decl = decl,
2039 .arena = &analysis_arena.allocator,
2040 .gpa = mod.gpa,
2041 };
2042 defer wip_zir_code.deinit();2058 defer wip_zir_code.deinit();
20432059
2044 var gen_scope: Scope.GenZir = .{2060 var gen_scope: Scope.GenZir = .{
...@@ -2111,11 +2127,7 @@ fn astgenAndSemaFn(...@@ -2111,11 +2127,7 @@ fn astgenAndSemaFn(
2111 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);2127 var fn_type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
2112 defer fn_type_scope_arena.deinit();2128 defer fn_type_scope_arena.deinit();
21132129
2114 var fn_type_wip_zir_code: WipZirCode = .{2130 var fn_type_wip_zir_code = try mod.initAstGen(decl, &fn_type_scope_arena.allocator);
2115 .decl = decl,
2116 .arena = &fn_type_scope_arena.allocator,
2117 .gpa = mod.gpa,
2118 };
2119 defer fn_type_wip_zir_code.deinit();2131 defer fn_type_wip_zir_code.deinit();
21202132
2121 var fn_type_scope: Scope.GenZir = .{2133 var fn_type_scope: Scope.GenZir = .{
...@@ -2270,7 +2282,7 @@ fn astgenAndSemaFn(...@@ -2270,7 +2282,7 @@ fn astgenAndSemaFn(
2270 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;2282 const tag: zir.Inst.Tag = if (is_var_args) .fn_type_var_args else .fn_type;
2271 break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types);2283 break :fn_type try fn_type_scope.addFnType(tag, return_type_inst, param_types);
2272 };2284 };
2273 _ = try fn_type_scope.addUnNode(.break_flat, fn_type_inst, 0);2285 _ = try fn_type_scope.addBreak(.break_inline, 0, fn_type_inst);
22742286
2275 // We need the memory for the Type to go into the arena for the Decl2287 // We need the memory for the Type to go into the arena for the Decl
2276 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);2288 var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
...@@ -2348,12 +2360,8 @@ fn astgenAndSemaFn(...@@ -2348,12 +2360,8 @@ fn astgenAndSemaFn(
23482360
2349 const fn_zir: zir.Code = blk: {2361 const fn_zir: zir.Code = blk: {
2350 // We put the ZIR inside the Decl arena.2362 // We put the ZIR inside the Decl arena.
2351 var wip_zir_code: WipZirCode = .{2363 var wip_zir_code = try mod.initAstGen(decl, &decl_arena.allocator);
2352 .decl = decl,2364 wip_zir_code.ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count);
2353 .arena = &decl_arena.allocator,
2354 .gpa = mod.gpa,
2355 .ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count),
2356 };
2357 defer wip_zir_code.deinit();2365 defer wip_zir_code.deinit();
23582366
2359 var gen_scope: Scope.GenZir = .{2367 var gen_scope: Scope.GenZir = .{
...@@ -2559,11 +2567,7 @@ fn astgenAndSemaVarDecl(...@@ -2559,11 +2567,7 @@ fn astgenAndSemaVarDecl(
2559 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);2567 var gen_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
2560 defer gen_scope_arena.deinit();2568 defer gen_scope_arena.deinit();
25612569
2562 var wip_zir_code: WipZirCode = .{2570 var wip_zir_code = try mod.initAstGen(decl, &gen_scope_arena.allocator);
2563 .decl = decl,
2564 .arena = &gen_scope_arena.allocator,
2565 .gpa = mod.gpa,
2566 };
2567 defer wip_zir_code.deinit();2571 defer wip_zir_code.deinit();
25682572
2569 var gen_scope: Scope.GenZir = .{2573 var gen_scope: Scope.GenZir = .{
...@@ -2583,7 +2587,7 @@ fn astgenAndSemaVarDecl(...@@ -2583,7 +2587,7 @@ fn astgenAndSemaVarDecl(
2583 init_result_loc,2587 init_result_loc,
2584 var_decl.ast.init_node,2588 var_decl.ast.init_node,
2585 );2589 );
2586 _ = try gen_scope.addUnNode(.break_flat, init_inst, var_decl.ast.init_node);2590 _ = try gen_scope.addBreak(.break_inline, 0, init_inst);
2587 var code = try gen_scope.finish();2591 var code = try gen_scope.finish();
2588 defer code.deinit(mod.gpa);2592 defer code.deinit(mod.gpa);
2589 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {2593 if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
...@@ -2611,7 +2615,7 @@ fn astgenAndSemaVarDecl(...@@ -2611,7 +2615,7 @@ fn astgenAndSemaVarDecl(
2611 };2615 };
2612 defer block_scope.instructions.deinit(mod.gpa);2616 defer block_scope.instructions.deinit(mod.gpa);
26132617
2614 const init_inst_zir_ref = try sema.root(&block_scope);2618 const init_inst_zir_ref = try sema.rootAsRef(&block_scope);
2615 // The result location guarantees the type coercion.2619 // The result location guarantees the type coercion.
2616 const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);2620 const analyzed_init_inst = try sema.resolveInst(init_inst_zir_ref);
2617 // The is_comptime in the Scope.Block guarantees the result is comptime-known.2621 // The is_comptime in the Scope.Block guarantees the result is comptime-known.
...@@ -2632,11 +2636,7 @@ fn astgenAndSemaVarDecl(...@@ -2632,11 +2636,7 @@ fn astgenAndSemaVarDecl(
2632 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);2636 var type_scope_arena = std.heap.ArenaAllocator.init(mod.gpa);
2633 defer type_scope_arena.deinit();2637 defer type_scope_arena.deinit();
26342638
2635 var wip_zir_code: WipZirCode = .{2639 var wip_zir_code = try mod.initAstGen(decl, &type_scope_arena.allocator);
2636 .decl = decl,
2637 .arena = &type_scope_arena.allocator,
2638 .gpa = mod.gpa,
2639 };
2640 defer wip_zir_code.deinit();2640 defer wip_zir_code.deinit();
26412641
2642 var type_scope: Scope.GenZir = .{2642 var type_scope: Scope.GenZir = .{
...@@ -2647,7 +2647,7 @@ fn astgenAndSemaVarDecl(...@@ -2647,7 +2647,7 @@ fn astgenAndSemaVarDecl(
2647 defer type_scope.instructions.deinit(mod.gpa);2647 defer type_scope.instructions.deinit(mod.gpa);
26482648
2649 const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);2649 const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
2650 _ = try type_scope.addUnNode(.break_flat, var_type, 0);2650 _ = try type_scope.addBreak(.break_inline, 0, var_type);
26512651
2652 var code = try type_scope.finish();2652 var code = try type_scope.finish();
2653 defer code.deinit(mod.gpa);2653 defer code.deinit(mod.gpa);
src/Sema.zig+72-45
...@@ -60,14 +60,21 @@ const InnerError = Module.InnerError;...@@ -60,14 +60,21 @@ const InnerError = Module.InnerError;
60const Decl = Module.Decl;60const Decl = Module.Decl;
61const LazySrcLoc = Module.LazySrcLoc;61const LazySrcLoc = Module.LazySrcLoc;
6262
63pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {63pub fn root(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Index {
64 const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len];64 const inst_data = sema.code.instructions.items(.data)[0].pl_node;
65 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
66 const root_body = sema.code.extra[extra.end..][0..extra.data.body_len];
65 return sema.analyzeBody(root_block, root_body);67 return sema.analyzeBody(root_block, root_body);
66}68}
6769
68/// Assumes that `root_block` ends with `break_flat`.70pub fn rootAsRef(sema: *Sema, root_block: *Scope.Block) !zir.Inst.Ref {
71 const break_inst = try sema.root(root_block);
72 return sema.code.instructions.items(.data)[break_inst].@"break".operand;
73}
74
75/// Assumes that `root_block` ends with `break_inline`.
69pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {76pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
70 const zir_inst_ref = try sema.root(root_block);77 const zir_inst_ref = try sema.rootAsRef(root_block);
71 // Source location is unneeded because resolveConstValue must have already78 // Source location is unneeded because resolveConstValue must have already
72 // been successfully called when coercing the value to a type, from the79 // been successfully called when coercing the value to a type, from the
73 // result location.80 // result location.
...@@ -78,17 +85,22 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {...@@ -78,17 +85,22 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
78/// return type of `analyzeBody` so that we can tail call them.85/// return type of `analyzeBody` so that we can tail call them.
79/// Only appropriate to return when the instruction is known to be NoReturn86/// Only appropriate to return when the instruction is known to be NoReturn
80/// solely based on the ZIR tag.87/// solely based on the ZIR tag.
81const always_noreturn: InnerError!zir.Inst.Ref = .none;88const always_noreturn: InnerError!zir.Inst.Index = @as(zir.Inst.Index, undefined);
8289
83/// This function is the main loop of `Sema` and it can be used in two different ways:90/// This function is the main loop of `Sema` and it can be used in two different ways:
84/// * The traditional way where there are N breaks out of the block and peer type91/// * The traditional way where there are N breaks out of the block and peer type
85/// resolution is done on the break operands. In this case, the `zir.Inst.Index`92/// resolution is done on the break operands. In this case, the `zir.Inst.Index`
86/// part of the return value will be `undefined`, and callsites should ignore it,93/// part of the return value will be `undefined`, and callsites should ignore it,
87/// finding the block result value via the block scope.94/// finding the block result value via the block scope.
88/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_flat`95/// * The "flat" way. There is only 1 break out of the block, and it is with a `break_inline`
89/// instruction. In this case, the `zir.Inst.Index` part of the return value will be96/// instruction. In this case, the `zir.Inst.Index` part of the return value will be
90/// the block result value. No block scope needs to be created for this strategy.97/// the break instruction. This communicates both which block the break applies to, as
91pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !zir.Inst.Ref {98/// well as the operand. No block scope needs to be created for this strategy.
99pub fn analyzeBody(
100 sema: *Sema,
101 block: *Scope.Block,
102 body: []const zir.Inst.Index,
103) InnerError!zir.Inst.Index {
92 // No tracy calls here, to avoid interfering with the tail call mechanism.104 // No tracy calls here, to avoid interfering with the tail call mechanism.
93105
94 const map = block.sema.inst_map;106 const map = block.sema.inst_map;
...@@ -127,8 +139,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde...@@ -127,8 +139,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
127 .bitcast => try sema.zirBitcast(block, inst),139 .bitcast => try sema.zirBitcast(block, inst),
128 .bitcast_ref => try sema.zirBitcastRef(block, inst),140 .bitcast_ref => try sema.zirBitcastRef(block, inst),
129 .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),141 .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
130 .block => try sema.zirBlock(block, inst, false),142 .block => try sema.zirBlock(block, inst),
131 .block_comptime => try sema.zirBlock(block, inst, true),
132 .bool_not => try sema.zirBoolNot(block, inst),143 .bool_not => try sema.zirBoolNot(block, inst),
133 .bool_and => try sema.zirBoolOp(block, inst, false),144 .bool_and => try sema.zirBoolOp(block, inst, false),
134 .bool_or => try sema.zirBoolOp(block, inst, true),145 .bool_or => try sema.zirBoolOp(block, inst, true),
...@@ -227,8 +238,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde...@@ -227,8 +238,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
227 // tail call them here.238 // tail call them here.
228 .condbr => return sema.zirCondbr(block, inst),239 .condbr => return sema.zirCondbr(block, inst),
229 .@"break" => return sema.zirBreak(block, inst),240 .@"break" => return sema.zirBreak(block, inst),
230 .break_void_node => return sema.zirBreakVoidNode(block, inst),241 .break_inline => return inst,
231 .break_flat => return sema.code.instructions.items(.data)[inst].un_node.operand,
232 .compile_error => return sema.zirCompileError(block, inst),242 .compile_error => return sema.zirCompileError(block, inst),
233 .ret_coerce => return sema.zirRetTok(block, inst, true),243 .ret_coerce => return sema.zirRetTok(block, inst, true),
234 .ret_node => return sema.zirRetNode(block, inst),244 .ret_node => return sema.zirRetNode(block, inst),
...@@ -286,13 +296,43 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde...@@ -286,13 +296,43 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
286 continue;296 continue;
287 },297 },
288298
289 // Special case: send comptime control flow back to the beginning of this block.299 // Special case instructions to handle comptime control flow.
290 .repeat_inline => {300 .repeat_inline => {
301 // Send comptime control flow back to the beginning of this block.
291 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };302 const src: LazySrcLoc = .{ .node_offset = datas[inst].node };
292 try sema.emitBackwardBranch(block, src);303 try sema.emitBackwardBranch(block, src);
293 i = 0;304 i = 0;
294 continue;305 continue;
295 },306 },
307 .block_inline => blk: {
308 // Directly analyze the block body without introducing a new block.
309 const inst_data = datas[inst].pl_node;
310 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
311 const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
312 const break_inst = try sema.analyzeBody(block, inline_body);
313 const break_data = datas[break_inst].@"break";
314 if (inst == break_data.block_inst) {
315 break :blk try sema.resolveInst(break_data.operand);
316 } else {
317 return break_inst;
318 }
319 },
320 .condbr_inline => blk: {
321 const inst_data = datas[inst].pl_node;
322 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
323 const extra = sema.code.extraData(zir.Inst.CondBr, inst_data.payload_index);
324 const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
325 const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
326 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition);
327 const inline_body = if (cond.val.toBool()) then_body else else_body;
328 const break_inst = try sema.analyzeBody(block, inline_body);
329 const break_data = datas[break_inst].@"break";
330 if (inst == break_data.block_inst) {
331 break :blk try sema.resolveInst(break_data.operand);
332 } else {
333 return break_inst;
334 }
335 },
296 };336 };
297 if (map[inst].ty.isNoReturn())337 if (map[inst].ty.isNoReturn())
298 return always_noreturn;338 return always_noreturn;
...@@ -745,7 +785,7 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In...@@ -745,7 +785,7 @@ fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*In
745 return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);785 return sema.mod.constIntUnsigned(sema.arena, .unneeded, Type.initTag(.comptime_int), int);
746}786}
747787
748fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {788fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
749 const tracy = trace(@src());789 const tracy = trace(@src());
750 defer tracy.end();790 defer tracy.end();
751791
...@@ -783,7 +823,7 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr...@@ -783,7 +823,7 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
783 }823 }
784}824}
785825
786fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {826fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
787 const tracy = trace(@src());827 const tracy = trace(@src());
788 defer tracy.end();828 defer tracy.end();
789829
...@@ -854,12 +894,7 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE...@@ -854,12 +894,7 @@ fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerE
854 return sema.analyzeBlockBody(parent_block, &child_block, merges);894 return sema.analyzeBlockBody(parent_block, &child_block, merges);
855}895}
856896
857fn zirBlock(897fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
858 sema: *Sema,
859 parent_block: *Scope.Block,
860 inst: zir.Inst.Index,
861 is_comptime: bool,
862) InnerError!*Inst {
863 const tracy = trace(@src());898 const tracy = trace(@src());
864 defer tracy.end();899 defer tracy.end();
865900
...@@ -896,7 +931,7 @@ fn zirBlock(...@@ -896,7 +931,7 @@ fn zirBlock(
896 },931 },
897 }),932 }),
898 .inlining = parent_block.inlining,933 .inlining = parent_block.inlining,
899 .is_comptime = is_comptime or parent_block.is_comptime,934 .is_comptime = parent_block.is_comptime,
900 };935 };
901 const merges = &child_block.label.?.merges;936 const merges = &child_block.label.?.merges;
902937
...@@ -1000,7 +1035,7 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr...@@ -1000,7 +1035,7 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
1000 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);1035 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
1001}1036}
10021037
1003fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {1038fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
1004 const tracy = trace(@src());1039 const tracy = trace(@src());
1005 defer tracy.end();1040 defer tracy.end();
10061041
...@@ -1009,22 +1044,13 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!z...@@ -1009,22 +1044,13 @@ fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!z
1009 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);1044 return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
1010}1045}
10111046
1012fn zirBreakVoidNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {
1013 const tracy = trace(@src());
1014 defer tracy.end();
1015
1016 const inst_data = sema.code.instructions.items(.data)[inst].break_void_node;
1017 const void_inst = try sema.mod.constVoid(sema.arena, .unneeded);
1018 return sema.analyzeBreak(block, inst_data.src(), inst_data.block_inst, void_inst);
1019}
1020
1021fn analyzeBreak(1047fn analyzeBreak(
1022 sema: *Sema,1048 sema: *Sema,
1023 start_block: *Scope.Block,1049 start_block: *Scope.Block,
1024 src: LazySrcLoc,1050 src: LazySrcLoc,
1025 zir_block: zir.Inst.Index,1051 zir_block: zir.Inst.Index,
1026 operand: *Inst,1052 operand: *Inst,
1027) InnerError!zir.Inst.Ref {1053) InnerError!zir.Inst.Index {
1028 var block = start_block;1054 var block = start_block;
1029 while (true) {1055 while (true) {
1030 if (block.label) |*label| {1056 if (block.label) |*label| {
...@@ -2844,7 +2870,8 @@ fn zirBoolBr(...@@ -2844,7 +2870,8 @@ fn zirBoolBr(
2844 const tracy = trace(@src());2870 const tracy = trace(@src());
2845 defer tracy.end();2871 defer tracy.end();
28462872
2847 const inst_data = sema.code.instructions.items(.data)[inst].bool_br;2873 const datas = sema.code.instructions.items(.data);
2874 const inst_data = datas[inst].bool_br;
2848 const src: LazySrcLoc = .unneeded;2875 const src: LazySrcLoc = .unneeded;
2849 const lhs = try sema.resolveInst(inst_data.lhs);2876 const lhs = try sema.resolveInst(inst_data.lhs);
2850 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);2877 const extra = sema.code.extraData(zir.Inst.Block, inst_data.payload_index);
...@@ -2856,9 +2883,9 @@ fn zirBoolBr(...@@ -2856,9 +2883,9 @@ fn zirBoolBr(
2856 }2883 }
2857 // comptime-known left-hand side. No need for a block here; the result2884 // comptime-known left-hand side. No need for a block here; the result
2858 // is simply the rhs expression. Here we rely on there only being 12885 // is simply the rhs expression. Here we rely on there only being 1
2859 // break instruction (`break_flat`).2886 // break instruction (`break_inline`).
2860 const zir_inst_ref = try sema.analyzeBody(parent_block, body);2887 const break_inst = try sema.analyzeBody(parent_block, body);
2861 return sema.resolveInst(zir_inst_ref);2888 return sema.resolveInst(datas[break_inst].@"break".operand);
2862 }2889 }
28632890
2864 const block_inst = try sema.arena.create(Inst.Block);2891 const block_inst = try sema.arena.create(Inst.Block);
...@@ -2889,8 +2916,8 @@ fn zirBoolBr(...@@ -2889,8 +2916,8 @@ fn zirBoolBr(
2889 });2916 });
2890 _ = try lhs_block.addBr(src, block_inst, lhs_result);2917 _ = try lhs_block.addBr(src, block_inst, lhs_result);
28912918
2892 const rhs_result_zir_ref = try sema.analyzeBody(rhs_block, body);2919 const rhs_break_inst = try sema.analyzeBody(rhs_block, body);
2893 const rhs_result = try sema.resolveInst(rhs_result_zir_ref);2920 const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
2894 _ = try rhs_block.addBr(src, block_inst, rhs_result);2921 _ = try rhs_block.addBr(src, block_inst, rhs_result);
28952922
2896 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };2923 const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
...@@ -2959,7 +2986,7 @@ fn zirCondbr(...@@ -2959,7 +2986,7 @@ fn zirCondbr(
2959 sema: *Sema,2986 sema: *Sema,
2960 parent_block: *Scope.Block,2987 parent_block: *Scope.Block,
2961 inst: zir.Inst.Index,2988 inst: zir.Inst.Index,
2962) InnerError!zir.Inst.Ref {2989) InnerError!zir.Inst.Index {
2963 const tracy = trace(@src());2990 const tracy = trace(@src());
2964 defer tracy.end();2991 defer tracy.end();
29652992
...@@ -3008,7 +3035,7 @@ fn zirCondbr(...@@ -3008,7 +3035,7 @@ fn zirCondbr(
3008 return always_noreturn;3035 return always_noreturn;
3009}3036}
30103037
3011fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {3038fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
3012 const tracy = trace(@src());3039 const tracy = trace(@src());
3013 defer tracy.end();3040 defer tracy.end();
30143041
...@@ -3030,7 +3057,7 @@ fn zirRetTok(...@@ -3030,7 +3057,7 @@ fn zirRetTok(
3030 block: *Scope.Block,3057 block: *Scope.Block,
3031 inst: zir.Inst.Index,3058 inst: zir.Inst.Index,
3032 need_coercion: bool,3059 need_coercion: bool,
3033) InnerError!zir.Inst.Ref {3060) InnerError!zir.Inst.Index {
3034 const tracy = trace(@src());3061 const tracy = trace(@src());
3035 defer tracy.end();3062 defer tracy.end();
30363063
...@@ -3041,7 +3068,7 @@ fn zirRetTok(...@@ -3041,7 +3068,7 @@ fn zirRetTok(
3041 return sema.analyzeRet(block, operand, src, need_coercion);3068 return sema.analyzeRet(block, operand, src, need_coercion);
3042}3069}
30433070
3044fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Ref {3071fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
3045 const tracy = trace(@src());3072 const tracy = trace(@src());
3046 defer tracy.end();3073 defer tracy.end();
30473074
...@@ -3058,7 +3085,7 @@ fn analyzeRet(...@@ -3058,7 +3085,7 @@ fn analyzeRet(
3058 operand: *Inst,3085 operand: *Inst,
3059 src: LazySrcLoc,3086 src: LazySrcLoc,
3060 need_coercion: bool,3087 need_coercion: bool,
3061) InnerError!zir.Inst.Ref {3088) InnerError!zir.Inst.Index {
3062 if (block.inlining) |inlining| {3089 if (block.inlining) |inlining| {
3063 // We are inlining a function call; rewrite the `ret` as a `break`.3090 // We are inlining a function call; rewrite the `ret` as a `break`.
3064 try inlining.merges.results.append(sema.gpa, operand);3091 try inlining.merges.results.append(sema.gpa, operand);
...@@ -3244,7 +3271,7 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:...@@ -3244,7 +3271,7 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id:
3244 try parent_block.instructions.append(sema.gpa, &block_inst.base);3271 try parent_block.instructions.append(sema.gpa, &block_inst.base);
3245}3272}
32463273
3247fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Ref {3274fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !zir.Inst.Index {
3248 // TODO Once we have a panic function to call, call it here instead of breakpoint.3275 // TODO Once we have a panic function to call, call it here instead of breakpoint.
3249 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);3276 _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
3250 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);3277 _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach);
src/astgen.zig+27-20
...@@ -699,7 +699,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro...@@ -699,7 +699,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro
699 };699 };
700700
701 if (rhs == 0) {701 if (rhs == 0) {
702 _ = try parent_gz.addBreakVoid(block_inst, node);702 _ = try parent_gz.addBreak(.@"break", block_inst, .void_value);
703 return zir.Inst.Ref.unreachable_value;703 return zir.Inst.Ref.unreachable_value;
704 }704 }
705 block_gz.break_count += 1;705 block_gz.break_count += 1;
...@@ -707,7 +707,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro...@@ -707,7 +707,7 @@ fn breakExpr(mod: *Module, parent_scope: *Scope, node: ast.Node.Index) InnerErro
707 const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs);707 const operand = try expr(mod, parent_scope, block_gz.break_result_loc, rhs);
708 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;708 const have_store_to_block = block_gz.rvalue_rl_count != prev_rvalue_rl_count;
709709
710 const br = try parent_gz.addBreak(block_inst, operand);710 const br = try parent_gz.addBreak(.@"break", block_inst, operand);
711711
712 if (block_gz.break_result_loc == .block_ptr) {712 if (block_gz.break_result_loc == .block_ptr) {
713 try block_gz.labeled_breaks.append(mod.gpa, br);713 try block_gz.labeled_breaks.append(mod.gpa, br);
...@@ -860,7 +860,7 @@ fn labeledBlockExpr(...@@ -860,7 +860,7 @@ fn labeledBlockExpr(
860 const tracy = trace(@src());860 const tracy = trace(@src());
861 defer tracy.end();861 defer tracy.end();
862862
863 assert(zir_tag == .block or zir_tag == .block_comptime);863 assert(zir_tag == .block);
864864
865 const tree = parent_scope.tree();865 const tree = parent_scope.tree();
866 const main_tokens = tree.nodes.items(.main_token);866 const main_tokens = tree.nodes.items(.main_token);
...@@ -911,8 +911,6 @@ fn labeledBlockExpr(...@@ -911,8 +911,6 @@ fn labeledBlockExpr(
911 for (block_scope.labeled_breaks.items) |br| {911 for (block_scope.labeled_breaks.items) |br| {
912 zir_datas[br].@"break".operand = .void_value;912 zir_datas[br].@"break".operand = .void_value;
913 }913 }
914 // TODO technically not needed since we changed the tag to break_void but
915 // would be better still to elide the ones that are in this list.
916 try block_scope.setBlockBody(block_inst);914 try block_scope.setBlockBody(block_inst);
917915
918 return gz.zir_code.indexToRef(block_inst);916 return gz.zir_code.indexToRef(block_inst);
...@@ -1027,7 +1025,7 @@ fn blockExprStmts(...@@ -1027,7 +1025,7 @@ fn blockExprStmts(
1027 .bitcast_result_ptr,1025 .bitcast_result_ptr,
1028 .bit_or,1026 .bit_or,
1029 .block,1027 .block,
1030 .block_comptime,1028 .block_inline,
1031 .loop,1029 .loop,
1032 .bool_br_and,1030 .bool_br_and,
1033 .bool_br_or,1031 .bool_br_or,
...@@ -1122,9 +1120,9 @@ fn blockExprStmts(...@@ -1122,9 +1120,9 @@ fn blockExprStmts(
1122 .compile_log,1120 .compile_log,
1123 .ensure_err_payload_void,1121 .ensure_err_payload_void,
1124 .@"break",1122 .@"break",
1125 .break_void_node,1123 .break_inline,
1126 .break_flat,
1127 .condbr,1124 .condbr,
1125 .condbr_inline,
1128 .compile_error,1126 .compile_error,
1129 .ret_node,1127 .ret_node,
1130 .ret_tok,1128 .ret_tok,
...@@ -1663,7 +1661,7 @@ fn orelseCatchExpr(...@@ -1663,7 +1661,7 @@ fn orelseCatchExpr(
1663 };1661 };
1664 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);1662 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
1665 const cond = try block_scope.addUnNode(cond_op, operand, node);1663 const cond = try block_scope.addUnNode(cond_op, operand, node);
1666 const condbr = try block_scope.addCondBr(node);1664 const condbr = try block_scope.addCondBr(.condbr, node);
16671665
1668 const block = try parent_gz.addBlock(.block, node);1666 const block = try parent_gz.addBlock(.block, node);
1669 try parent_gz.instructions.append(mod.gpa, block);1667 try parent_gz.instructions.append(mod.gpa, block);
...@@ -1731,6 +1729,7 @@ fn orelseCatchExpr(...@@ -1731,6 +1729,7 @@ fn orelseCatchExpr(
1731 else_result,1729 else_result,
1732 block,1730 block,
1733 block,1731 block,
1732 .@"break",
1734 );1733 );
1735}1734}
17361735
...@@ -1750,6 +1749,7 @@ fn finishThenElseBlock(...@@ -1750,6 +1749,7 @@ fn finishThenElseBlock(
1750 else_result: zir.Inst.Ref,1749 else_result: zir.Inst.Ref,
1751 main_block: zir.Inst.Index,1750 main_block: zir.Inst.Index,
1752 then_break_block: zir.Inst.Index,1751 then_break_block: zir.Inst.Index,
1752 break_tag: zir.Inst.Tag,
1753) InnerError!zir.Inst.Ref {1753) InnerError!zir.Inst.Ref {
1754 // We now have enough information to decide whether the result instruction should1754 // We now have enough information to decide whether the result instruction should
1755 // be communicated via result location pointer or break instructions.1755 // be communicated via result location pointer or break instructions.
...@@ -1758,11 +1758,11 @@ fn finishThenElseBlock(...@@ -1758,11 +1758,11 @@ fn finishThenElseBlock(
1758 switch (strat.tag) {1758 switch (strat.tag) {
1759 .break_void => {1759 .break_void => {
1760 if (!wzc.refIsNoReturn(then_result)) {1760 if (!wzc.refIsNoReturn(then_result)) {
1761 _ = try then_scope.addBreakVoid(then_break_block, then_src);1761 _ = try then_scope.addBreak(break_tag, then_break_block, .void_value);
1762 }1762 }
1763 const elide_else = if (else_result != .none) wzc.refIsNoReturn(else_result) else false;1763 const elide_else = if (else_result != .none) wzc.refIsNoReturn(else_result) else false;
1764 if (!elide_else) {1764 if (!elide_else) {
1765 _ = try else_scope.addBreakVoid(main_block, else_src);1765 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1766 }1766 }
1767 assert(!strat.elide_store_to_block_ptr_instructions);1767 assert(!strat.elide_store_to_block_ptr_instructions);
1768 try setCondBrPayload(condbr, cond, then_scope, else_scope);1768 try setCondBrPayload(condbr, cond, then_scope, else_scope);
...@@ -1770,14 +1770,14 @@ fn finishThenElseBlock(...@@ -1770,14 +1770,14 @@ fn finishThenElseBlock(
1770 },1770 },
1771 .break_operand => {1771 .break_operand => {
1772 if (!wzc.refIsNoReturn(then_result)) {1772 if (!wzc.refIsNoReturn(then_result)) {
1773 _ = try then_scope.addBreak(then_break_block, then_result);1773 _ = try then_scope.addBreak(break_tag, then_break_block, then_result);
1774 }1774 }
1775 if (else_result != .none) {1775 if (else_result != .none) {
1776 if (!wzc.refIsNoReturn(else_result)) {1776 if (!wzc.refIsNoReturn(else_result)) {
1777 _ = try else_scope.addBreak(main_block, else_result);1777 _ = try else_scope.addBreak(break_tag, main_block, else_result);
1778 }1778 }
1779 } else {1779 } else {
1780 _ = try else_scope.addBreakVoid(main_block, else_src);1780 _ = try else_scope.addBreak(break_tag, main_block, .void_value);
1781 }1781 }
1782 if (strat.elide_store_to_block_ptr_instructions) {1782 if (strat.elide_store_to_block_ptr_instructions) {
1783 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);1783 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, else_scope);
...@@ -1944,7 +1944,7 @@ fn boolBinOp(...@@ -1944,7 +1944,7 @@ fn boolBinOp(
1944 };1944 };
1945 defer rhs_scope.instructions.deinit(mod.gpa);1945 defer rhs_scope.instructions.deinit(mod.gpa);
1946 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);1946 const rhs = try expr(mod, &rhs_scope.base, .{ .ty = .bool_type }, node_datas[node].rhs);
1947 _ = try rhs_scope.addUnNode(.break_flat, rhs, node);1947 _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
1948 try rhs_scope.setBoolBrBody(bool_br);1948 try rhs_scope.setBoolBrBody(bool_br);
19491949
1950 const block_ref = gz.zir_code.indexToRef(bool_br);1950 const block_ref = gz.zir_code.indexToRef(bool_br);
...@@ -1979,7 +1979,7 @@ fn ifExpr(...@@ -1979,7 +1979,7 @@ fn ifExpr(
1979 }1979 }
1980 };1980 };
19811981
1982 const condbr = try block_scope.addCondBr(node);1982 const condbr = try block_scope.addCondBr(.condbr, node);
19831983
1984 const block = try parent_gz.addBlock(.block, node);1984 const block = try parent_gz.addBlock(.block, node);
1985 try parent_gz.instructions.append(mod.gpa, block);1985 try parent_gz.instructions.append(mod.gpa, block);
...@@ -2042,6 +2042,7 @@ fn ifExpr(...@@ -2042,6 +2042,7 @@ fn ifExpr(
2042 else_info.result,2042 else_info.result,
2043 block,2043 block,
2044 block,2044 block,
2045 .@"break",
2045 );2046 );
2046}2047}
20472048
...@@ -2108,7 +2109,9 @@ fn whileExpr(...@@ -2108,7 +2109,9 @@ fn whileExpr(
2108 try checkLabelRedefinition(mod, scope, label_token);2109 try checkLabelRedefinition(mod, scope, label_token);
2109 }2110 }
2110 const parent_gz = scope.getGenZir();2111 const parent_gz = scope.getGenZir();
2111 const loop_block = try parent_gz.addBlock(.loop, node);2112 const is_inline = while_full.inline_token != null;
2113 const loop_tag: zir.Inst.Tag = if (is_inline) .block_inline else .loop;
2114 const loop_block = try parent_gz.addBlock(loop_tag, node);
2112 try parent_gz.instructions.append(mod.gpa, loop_block);2115 try parent_gz.instructions.append(mod.gpa, loop_block);
21132116
2114 var loop_scope: Scope.GenZir = .{2117 var loop_scope: Scope.GenZir = .{
...@@ -2140,8 +2143,10 @@ fn whileExpr(...@@ -2140,8 +2143,10 @@ fn whileExpr(
2140 }2143 }
2141 };2144 };
21422145
2143 const condbr = try continue_scope.addCondBr(node);2146 const condbr_tag: zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
2144 const cond_block = try loop_scope.addBlock(.block, node);2147 const condbr = try continue_scope.addCondBr(condbr_tag, node);
2148 const block_tag: zir.Inst.Tag = if (is_inline) .block_inline else .block;
2149 const cond_block = try loop_scope.addBlock(block_tag, node);
2145 try loop_scope.instructions.append(mod.gpa, cond_block);2150 try loop_scope.instructions.append(mod.gpa, cond_block);
2146 try continue_scope.setBlockBody(cond_block);2151 try continue_scope.setBlockBody(cond_block);
21472152
...@@ -2152,7 +2157,6 @@ fn whileExpr(...@@ -2152,7 +2157,6 @@ fn whileExpr(
2152 if (while_full.ast.cont_expr != 0) {2157 if (while_full.ast.cont_expr != 0) {
2153 _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);2158 _ = try expr(mod, &loop_scope.base, .{ .ty = .void_type }, while_full.ast.cont_expr);
2154 }2159 }
2155 const is_inline = while_full.inline_token != null;
2156 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;2160 const repeat_tag: zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
2157 _ = try loop_scope.addNode(repeat_tag, node);2161 _ = try loop_scope.addNode(repeat_tag, node);
21582162
...@@ -2208,6 +2212,7 @@ fn whileExpr(...@@ -2208,6 +2212,7 @@ fn whileExpr(
2208 return mod.failTok(scope, some.token, "unused while loop label", .{});2212 return mod.failTok(scope, some.token, "unused while loop label", .{});
2209 }2213 }
2210 }2214 }
2215 const break_tag: zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
2211 return finishThenElseBlock(2216 return finishThenElseBlock(
2212 mod,2217 mod,
2213 scope,2218 scope,
...@@ -2224,6 +2229,7 @@ fn whileExpr(...@@ -2224,6 +2229,7 @@ fn whileExpr(
2224 else_info.result,2229 else_info.result,
2225 loop_block,2230 loop_block,
2226 cond_block,2231 cond_block,
2232 break_tag,
2227 );2233 );
2228}2234}
22292235
...@@ -2424,6 +2430,7 @@ fn forExpr(...@@ -2424,6 +2430,7 @@ fn forExpr(
2424 else_info.result,2430 else_info.result,
2425 for_block,2431 for_block,
2426 cond_block,2432 cond_block,
2433 .@"break",
2427 );2434 );
2428}2435}
24292436
src/zir.zig+32-49
...@@ -26,6 +26,8 @@ const LazySrcLoc = Module.LazySrcLoc;...@@ -26,6 +26,8 @@ const LazySrcLoc = Module.LazySrcLoc;
26/// handled by the codegen backend, and errors reported there. However for now,26/// handled by the codegen backend, and errors reported there. However for now,
27/// inline assembly is not an exception.27/// inline assembly is not an exception.
28pub const Code = struct {28pub const Code = struct {
29 /// There is always implicitly a `block` instruction at index 0.
30 /// This is so that `break_inline` can break from the root block.
29 instructions: std.MultiArrayList(Inst).Slice,31 instructions: std.MultiArrayList(Inst).Slice,
30 /// In order to store references to strings in fewer bytes, we copy all32 /// In order to store references to strings in fewer bytes, we copy all
31 /// string bytes into here. String bytes can be null. It is up to whomever33 /// string bytes into here. String bytes can be null. It is up to whomever
...@@ -35,11 +37,6 @@ pub const Code = struct {...@@ -35,11 +37,6 @@ pub const Code = struct {
35 string_bytes: []u8,37 string_bytes: []u8,
36 /// The meaning of this data is determined by `Inst.Tag` value.38 /// The meaning of this data is determined by `Inst.Tag` value.
37 extra: []u32,39 extra: []u32,
38 /// First ZIR instruction in this `Code`.
39 /// `extra` at this index contains a `Ref` for every root member.
40 root_start: u32,
41 /// Number of ZIR instructions in the implicit root block of the `Code`.
42 root_len: u32,
4340
44 /// Returns the requested data, as well as the new index which is at the start of the41 /// Returns the requested data, as well as the new index which is at the start of the
45 /// trailers for the object.42 /// trailers for the object.
...@@ -98,17 +95,14 @@ pub const Code = struct {...@@ -98,17 +95,14 @@ pub const Code = struct {
98 .arena = &arena.allocator,95 .arena = &arena.allocator,
99 .scope = scope,96 .scope = scope,
100 .code = code,97 .code = code,
101 .indent = 2,98 .indent = 0,
102 .param_count = param_count,99 .param_count = param_count,
103 };100 };
104101
105 const decl_name = scope.srcDecl().?.name;102 const decl_name = scope.srcDecl().?.name;
106 const stderr = std.io.getStdErr().writer();103 const stderr = std.io.getStdErr().writer();
107 try stderr.print("ZIR {s} {s} {{\n", .{ kind, decl_name });104 try stderr.print("ZIR {s} {s} %0 ", .{ kind, decl_name });
108105 try writer.writeInstToStream(stderr, 0);
109 const root_body = code.extra[code.root_start..][0..code.root_len];
110 try writer.writeBody(stderr, root_body);
111
112 try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name });106 try stderr.print("}} // ZIR {s} {s}\n\n", .{ kind, decl_name });
113 }107 }
114};108};
...@@ -189,8 +183,11 @@ pub const Inst = struct {...@@ -189,8 +183,11 @@ pub const Inst = struct {
189 /// A labeled block of code, which can return a value.183 /// A labeled block of code, which can return a value.
190 /// Uses the `pl_node` union field. Payload is `Block`.184 /// Uses the `pl_node` union field. Payload is `Block`.
191 block,185 block,
192 /// Same as `block` but additionally makes the inner instructions execute at comptime.186 /// A list of instructions which are analyzed in the parent context, without
193 block_comptime,187 /// generating a runtime block. Must terminate with an "inline" variant of
188 /// a noreturn instruction.
189 /// Uses the `pl_node` union field. Payload is `Block`.
190 block_inline,
194 /// Boolean AND. See also `bit_and`.191 /// Boolean AND. See also `bit_and`.
195 /// Uses the `pl_node` union field. Payload is `Bin`.192 /// Uses the `pl_node` union field. Payload is `Bin`.
196 bool_and,193 bool_and,
...@@ -212,16 +209,12 @@ pub const Inst = struct {...@@ -212,16 +209,12 @@ pub const Inst = struct {
212 /// Uses the `break` union field.209 /// Uses the `break` union field.
213 /// Uses the source information from previous instruction.210 /// Uses the source information from previous instruction.
214 @"break",211 @"break",
215 /// Same as `break` but has source information in the form of an AST node, and212 /// Return a value from a block. This instruction is used as the terminator
216 /// the operand is assumed to be the void value.213 /// of a `block_inline`. It allows using the return value from `Sema.analyzeBody`.
217 /// Uses the `break_void_node` union field.214 /// This instruction may also be used when it is known that there is only one
218 break_void_node,215 /// break instruction in a block, and the target block is the parent.
219 /// Return a value from a block. This is a special form that is only valid216 /// Uses the `break` union field.
220 /// when there is exactly 1 break from a block (this one). This instruction217 break_inline,
221 /// allows using the return value from `Sema.analyzeBody`. The block is
222 /// assumed to be the direct parent of this instruction.
223 /// Uses the `un_node` union field. The AST node is unused.
224 break_flat,
225 /// Uses the `node` union field.218 /// Uses the `node` union field.
226 breakpoint,219 breakpoint,
227 /// Function call with modifier `.auto`.220 /// Function call with modifier `.auto`.
...@@ -270,7 +263,11 @@ pub const Inst = struct {...@@ -270,7 +263,11 @@ pub const Inst = struct {
270 /// Uses the `pl_node` union field. AST node is an if, while, for, etc.263 /// Uses the `pl_node` union field. AST node is an if, while, for, etc.
271 /// Payload is `CondBr`.264 /// Payload is `CondBr`.
272 condbr,265 condbr,
273 /// Special case, has no textual representation.266 /// Same as `condbr`, except the condition is coerced to a comptime value, and
267 /// only the taken branch is analyzed. The then block and else block must
268 /// terminate with an "inline" variant of a noreturn instruction.
269 condbr_inline,
270 /// A comptime known value.
274 /// Uses the `const` union field.271 /// Uses the `const` union field.
275 @"const",272 @"const",
276 /// Declares the beginning of a statement. Used for debug info.273 /// Declares the beginning of a statement. Used for debug info.
...@@ -640,7 +637,7 @@ pub const Inst = struct {...@@ -640,7 +637,7 @@ pub const Inst = struct {
640 .bitcast_result_ptr,637 .bitcast_result_ptr,
641 .bit_or,638 .bit_or,
642 .block,639 .block,
643 .block_comptime,640 .block_inline,
644 .loop,641 .loop,
645 .bool_br_and,642 .bool_br_and,
646 .bool_br_or,643 .bool_br_or,
...@@ -744,9 +741,9 @@ pub const Inst = struct {...@@ -744,9 +741,9 @@ pub const Inst = struct {
744 => false,741 => false,
745742
746 .@"break",743 .@"break",
747 .break_void_node,744 .break_inline,
748 .break_flat,
749 .condbr,745 .condbr,
746 .condbr_inline,
750 .compile_error,747 .compile_error,
751 .ret_node,748 .ret_node,
752 .ret_tok,749 .ret_tok,
...@@ -1194,16 +1191,6 @@ pub const Inst = struct {...@@ -1194,16 +1191,6 @@ pub const Inst = struct {
1194 return .{ .node_offset = self.src_node };1191 return .{ .node_offset = self.src_node };
1195 }1192 }
1196 },1193 },
1197 break_void_node: struct {
1198 /// Offset from Decl AST node index.
1199 /// `Tag` determines which kind of AST node this points to.
1200 src_node: i32,
1201 block_inst: Index,
1202
1203 pub fn src(self: @This()) LazySrcLoc {
1204 return .{ .node_offset = self.src_node };
1205 }
1206 },
1207 @"break": struct {1194 @"break": struct {
1208 block_inst: Index,1195 block_inst: Index,
1209 operand: Ref,1196 operand: Ref,
...@@ -1410,7 +1397,6 @@ const Writer = struct {...@@ -1410,7 +1397,6 @@ const Writer = struct {
1410 .err_union_payload_unsafe_ptr,1397 .err_union_payload_unsafe_ptr,
1411 .err_union_code,1398 .err_union_code,
1412 .err_union_code_ptr,1399 .err_union_code_ptr,
1413 .break_flat,
1414 .is_non_null,1400 .is_non_null,
1415 .is_null,1401 .is_null,
1416 .is_non_null_ptr,1402 .is_non_null_ptr,
...@@ -1438,9 +1424,11 @@ const Writer = struct {...@@ -1438,9 +1424,11 @@ const Writer = struct {
1438 .int => try self.writeInt(stream, inst),1424 .int => try self.writeInt(stream, inst),
1439 .str => try self.writeStr(stream, inst),1425 .str => try self.writeStr(stream, inst),
1440 .elided => try stream.writeAll(")"),1426 .elided => try stream.writeAll(")"),
1441 .break_void_node => try self.writeBreakVoidNode(stream, inst),
1442 .int_type => try self.writeIntType(stream, inst),1427 .int_type => try self.writeIntType(stream, inst),
1443 .@"break" => try self.writeBreak(stream, inst),1428
1429 .@"break",
1430 .break_inline,
1431 => try self.writeBreak(stream, inst),
14441432
1445 .@"asm",1433 .@"asm",
1446 .asm_volatile,1434 .asm_volatile,
...@@ -1487,11 +1475,13 @@ const Writer = struct {...@@ -1487,11 +1475,13 @@ const Writer = struct {
1487 => try self.writePlNodeCall(stream, inst),1475 => try self.writePlNodeCall(stream, inst),
14881476
1489 .block,1477 .block,
1490 .block_comptime,1478 .block_inline,
1491 .loop,1479 .loop,
1492 => try self.writePlNodeBlock(stream, inst),1480 => try self.writePlNodeBlock(stream, inst),
14931481
1494 .condbr => try self.writePlNodeCondBr(stream, inst),1482 .condbr,
1483 .condbr_inline,
1484 => try self.writePlNodeCondBr(stream, inst),
14951485
1496 .as_node => try self.writeAs(stream, inst),1486 .as_node => try self.writeAs(stream, inst),
14971487
...@@ -1771,13 +1761,6 @@ const Writer = struct {...@@ -1771,13 +1761,6 @@ const Writer = struct {
1771 return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);1761 return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc);
1772 }1762 }
17731763
1774 fn writeBreakVoidNode(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1775 const inst_data = self.code.instructions.items(.data)[inst].break_void_node;
1776 try self.writeInstIndex(stream, inst_data.block_inst);
1777 try stream.writeAll(") ");
1778 try self.writeSrc(stream, inst_data.src());
1779 }
1780
1781 fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {1764 fn writeIntType(self: *Writer, stream: anytype, inst: Inst.Index) !void {
1782 const int_type = self.code.instructions.items(.data)[inst].int_type;1765 const int_type = self.code.instructions.items(.data)[inst].int_type;
1783 const prefix: u8 = switch (int_type.signedness) {1766 const prefix: u8 = switch (int_type.signedness) {
test/stage2/test.zig+37
...@@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -621,6 +621,43 @@ pub fn addCases(ctx: *TestContext) !void {
621 "hello\nhello\nhello\nhello\n",621 "hello\nhello\nhello\nhello\n",
622 );622 );
623623
624 // inline while requires the condition to be comptime known.
625 case.addError(
626 \\export fn _start() noreturn {
627 \\ var i: u32 = 0;
628 \\ inline while (i < 4) : (i += 1) print();
629 \\ assert(i == 4);
630 \\
631 \\ exit();
632 \\}
633 \\
634 \\fn print() void {
635 \\ asm volatile ("syscall"
636 \\ :
637 \\ : [number] "{rax}" (1),
638 \\ [arg1] "{rdi}" (1),
639 \\ [arg2] "{rsi}" (@ptrToInt("hello\n")),
640 \\ [arg3] "{rdx}" (6)
641 \\ : "rcx", "r11", "memory"
642 \\ );
643 \\ return;
644 \\}
645 \\
646 \\pub fn assert(ok: bool) void {
647 \\ if (!ok) unreachable; // assertion failure
648 \\}
649 \\
650 \\fn exit() noreturn {
651 \\ asm volatile ("syscall"
652 \\ :
653 \\ : [number] "{rax}" (231),
654 \\ [arg1] "{rdi}" (0)
655 \\ : "rcx", "r11", "memory"
656 \\ );
657 \\ unreachable;
658 \\}
659 , &[_][]const u8{":3:21: error: unable to resolve comptime value"});
660
624 // Labeled blocks (no conditional branch)661 // Labeled blocks (no conditional branch)
625 case.addCompareOutput(662 case.addCompareOutput(
626 \\export fn _start() noreturn {663 \\export fn _start() noreturn {