Commit 56677f2f2d
Changed files (7)
lib/std/zig/parse.zig
@@ -139,6 +139,16 @@ const Parser = struct {
return result;
}
+ fn setNode(p: *Parser, i: usize, elem: ast.NodeList.Elem) Node.Index {
+ p.nodes.set(i, elem);
+ return @intCast(Node.Index, i);
+ }
+
+ fn reserveNode(p: *Parser) !usize {
+ try p.nodes.resize(p.gpa, p.nodes.len + 1);
+ return p.nodes.len - 1;
+ }
+
fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
const fields = std.meta.fields(@TypeOf(extra));
try p.extra_data.ensureCapacity(p.gpa, p.extra_data.items.len + fields.len);
@@ -554,9 +564,10 @@ const Parser = struct {
return fn_proto;
},
.l_brace => {
+ const fn_decl_index = try p.reserveNode();
const body_block = try p.parseBlock();
assert(body_block != 0);
- return p.addNode(.{
+ return p.setNode(fn_decl_index, .{
.tag = .fn_decl,
.main_token = p.nodes.items(.main_token)[fn_proto],
.data = .{
@@ -634,6 +645,10 @@ const Parser = struct {
/// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? CallConv? EXCLAMATIONMARK? (Keyword_anytype / TypeExpr)
fn parseFnProto(p: *Parser) !Node.Index {
const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
+
+ // We want the fn proto node to be before its children in the array.
+ const fn_proto_index = try p.reserveNode();
+
_ = p.eatToken(.identifier);
const params = try p.parseParamDeclList();
defer params.deinit(p.gpa);
@@ -651,7 +666,7 @@ const Parser = struct {
if (align_expr == 0 and section_expr == 0 and callconv_expr == 0) {
switch (params) {
- .zero_or_one => |param| return p.addNode(.{
+ .zero_or_one => |param| return p.setNode(fn_proto_index, .{
.tag = .fn_proto_simple,
.main_token = fn_token,
.data = .{
@@ -661,7 +676,7 @@ const Parser = struct {
}),
.multi => |list| {
const span = try p.listToSpan(list);
- return p.addNode(.{
+ return p.setNode(fn_proto_index, .{
.tag = .fn_proto_multi,
.main_token = fn_token,
.data = .{
@@ -676,7 +691,7 @@ const Parser = struct {
}
}
switch (params) {
- .zero_or_one => |param| return p.addNode(.{
+ .zero_or_one => |param| return p.setNode(fn_proto_index, .{
.tag = .fn_proto_one,
.main_token = fn_token,
.data = .{
@@ -691,7 +706,7 @@ const Parser = struct {
}),
.multi => |list| {
const span = try p.listToSpan(list);
- return p.addNode(.{
+ return p.setNode(fn_proto_index, .{
.tag = .fn_proto,
.main_token = fn_token,
.data = .{
src/astgen.zig
@@ -497,7 +497,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
}
},
.block_two, .block_two_semicolon => {
- if (true) @panic("TODO update for zir-memory-layout");
const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
if (node_datas[node].lhs == 0) {
return blockExpr(mod, scope, rl, node, statements[0..0]);
@@ -508,7 +507,6 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
}
},
.block, .block_semicolon => {
- if (true) @panic("TODO update for zir-memory-layout");
const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
return blockExpr(mod, scope, rl, node, statements);
},
@@ -808,7 +806,7 @@ fn breakExpr(
},
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
- .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent,
+ .gen_suspend => scope = scope.cast(Scope.GenZir).?.parent,
.gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent,
else => if (break_label != 0) {
const label_name = try mod.identifierTokenString(parent_scope, break_label);
@@ -864,7 +862,7 @@ fn continueExpr(
},
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
- .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent,
+ .gen_suspend => scope = scope.cast(Scope.GenZir).?.parent,
.gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent,
else => if (break_label != 0) {
const label_name = try mod.identifierTokenString(parent_scope, break_label);
@@ -939,7 +937,7 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn
},
.local_val => scope = scope.cast(Scope.LocalVal).?.parent,
.local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
- .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent,
+ .gen_suspend => scope = scope.cast(Scope.GenZir).?.parent,
.gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent,
else => return,
}
@@ -971,25 +969,14 @@ fn labeledBlockExpr(
try checkLabelRedefinition(mod, parent_scope, label_token);
- // Create the Block ZIR instruction so that we can put it into the GenZir struct
+ // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
// so that break statements can reference it.
- const gen_zir = parent_scope.getGenZir();
- const block_inst = try gen_zir.arena.create(zir.Inst.Block);
- block_inst.* = .{
- .base = .{
- .tag = zir_tag,
- .src = src,
- },
- .positionals = .{
- .body = .{ .instructions = undefined },
- },
- .kw_args = .{},
- };
+ const gz = parent_scope.getGenZir();
+ const block_inst = try gz.addBlock(zir_tag, block_node);
var block_scope: Scope.GenZir = .{
.parent = parent_scope,
- .decl = parent_scope.ownerDecl().?,
- .arena = gen_zir.arena,
+ .zir_code = gz.zir_code,
.force_comptime = parent_scope.isComptime(),
.instructions = .{},
// TODO @as here is working around a stage1 miscompilation bug :(
@@ -1009,35 +996,40 @@ fn labeledBlockExpr(
return mod.failTok(parent_scope, label_token, "unused block label", .{});
}
- try gen_zir.instructions.append(mod.gpa, &block_inst.base);
+ try gz.instructions.append(mod.gpa, block_inst);
+
+ const zir_tags = gz.zir_code.instructions.items(.tag);
+ const zir_datas = gz.zir_code.instructions.items(.data);
const strat = rlStrategy(rl, &block_scope);
switch (strat.tag) {
.break_void => {
// The code took advantage of the result location as a pointer.
- // Turn the break instructions into break_void instructions.
+ // Turn the break instruction operands into void.
for (block_scope.labeled_breaks.items) |br| {
- br.base.tag = .break_void;
+ zir_datas[br].bin.rhs = 0;
}
// TODO technically not needed since we changed the tag to break_void but
// would be better still to elide the ones that are in this list.
- try copyBodyNoEliding(&block_inst.positionals.body, block_scope);
+ try copyBodyNoEliding(block_inst, block_scope);
- return &block_inst.base;
+ return gz.zir_code.ref_start_index + block_inst;
},
.break_operand => {
// All break operands are values that did not use the result location pointer.
if (strat.elide_store_to_block_ptr_instructions) {
for (block_scope.labeled_store_to_block_ptr_list.items) |inst| {
- inst.base.tag = .void_value;
+ zir_tags[inst] = .elided;
+ zir_datas[inst] = undefined;
}
- // TODO technically not needed since we changed the tag to void_value but
+ // TODO technically not needed since we changed the tag to elided but
// would be better still to elide the ones that are in this list.
}
- try copyBodyNoEliding(&block_inst.positionals.body, block_scope);
+ try copyBodyNoEliding(block_inst, block_scope);
+ const block_ref = gz.zir_code.ref_start_index + block_inst;
switch (rl) {
- .ref => return &block_inst.base,
- else => return rvalue(mod, parent_scope, rl, &block_inst.base),
+ .ref => return block_ref,
+ else => return rvalue(mod, parent_scope, rl, block_ref, block_node),
}
},
}
@@ -1057,15 +1049,16 @@ fn blockExprStmts(
var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
defer block_arena.deinit();
+ const gz = parent_scope.getGenZir();
+
var scope = parent_scope;
for (statements) |statement| {
- const src = token_starts[tree.firstToken(statement)];
- _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
+ _ = try gz.addNode(.dbg_stmt_node, statement);
switch (node_tags[statement]) {
- .global_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.globalVarDecl(statement)),
- .local_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.localVarDecl(statement)),
- .simple_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.simpleVarDecl(statement)),
- .aligned_var_decl => scope = try varDecl(mod, scope, &block_arena.allocator, tree.alignedVarDecl(statement)),
+ .global_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.globalVarDecl(statement)),
+ .local_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.localVarDecl(statement)),
+ .simple_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.simpleVarDecl(statement)),
+ .aligned_var_decl => scope = try varDecl(mod, scope, statement, &block_arena.allocator, tree.alignedVarDecl(statement)),
.assign => try assign(mod, scope, statement),
.assign_bit_and => try assignOp(mod, scope, statement, .bit_and),
@@ -1084,8 +1077,8 @@ fn blockExprStmts(
else => {
const possibly_unused_result = try expr(mod, scope, .none, statement);
- if (!possibly_unused_result.tag.isNoReturn()) {
- _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
+ if (!gz.zir_code.isVoidOrNoReturn(possibly_unused_result)) {
+ _ = try gz.addUnNode(.ensure_result_used, possibly_unused_result, statement);
}
},
}
@@ -1095,22 +1088,24 @@ fn blockExprStmts(
fn varDecl(
mod: *Module,
scope: *Scope,
+ node: ast.Node.Index,
block_arena: *Allocator,
var_decl: ast.full.VarDecl,
) InnerError!*Scope {
+ if (true) @panic("TODO update for zir-memory-layout");
+
if (var_decl.comptime_token) |comptime_token| {
return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
}
if (var_decl.ast.align_node != 0) {
return mod.failNode(scope, var_decl.ast.align_node, "TODO implement alignment on locals", .{});
}
+ const gz = scope.getGenZir();
const tree = scope.tree();
- const main_tokens = tree.nodes.items(.main_token);
- const token_starts = tree.tokens.items(.start);
const token_tags = tree.tokens.items(.tag);
const name_token = var_decl.ast.mut_token + 1;
- const name_src = token_starts[name_token];
+ const name_src = gz.tokSrcLoc(name_token);
const ident_name = try mod.identifierTokenString(scope, name_token);
// Local variables shadowing detection, including function parameters.
@@ -1125,7 +1120,7 @@ fn varDecl(
ident_name,
});
errdefer msg.destroy(mod.gpa);
- try mod.errNote(scope, local_val.inst.src, msg, "previous definition is here", .{});
+ try mod.errNote(scope, local_val.src, msg, "previous definition is here", .{});
break :msg msg;
};
return mod.failWithOwnedErrorMsg(scope, msg);
@@ -1140,7 +1135,7 @@ fn varDecl(
ident_name,
});
errdefer msg.destroy(mod.gpa);
- try mod.errNote(scope, local_ptr.ptr.src, msg, "previous definition is here", .{});
+ try mod.errNote(scope, local_ptr.src, msg, "previous definition is here", .{});
break :msg msg;
};
return mod.failWithOwnedErrorMsg(scope, msg);
@@ -1176,9 +1171,10 @@ fn varDecl(
const sub_scope = try block_arena.create(Scope.LocalVal);
sub_scope.* = .{
.parent = scope,
- .gen_zir = scope.getGenZir(),
+ .gen_zir = gz,
.name = ident_name,
.inst = init_inst,
+ .src = gz.nodeSrcLoc(node),
};
return &sub_scope.base;
}
@@ -1207,7 +1203,7 @@ fn varDecl(
}
const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
const init_inst = try expr(mod, &init_scope.base, init_result_loc, var_decl.ast.init_node);
- const parent_zir = &scope.getGenZir().instructions;
+ const parent_zir = &gz.instructions;
if (init_scope.rvalue_rl_count == 1) {
// Result location pointer not used. We don't need an alloc for this
// const local, and type inference becomes trivial.
@@ -1231,7 +1227,7 @@ fn varDecl(
const sub_scope = try block_arena.create(Scope.LocalVal);
sub_scope.* = .{
.parent = scope,
- .gen_zir = scope.getGenZir(),
+ .gen_zir = gz,
.name = ident_name,
.inst = casted_init,
};
@@ -1258,7 +1254,7 @@ fn varDecl(
const sub_scope = try block_arena.create(Scope.LocalPtr);
sub_scope.* = .{
.parent = scope,
- .gen_zir = scope.getGenZir(),
+ .gen_zir = gz,
.name = ident_name,
.ptr = init_scope.rl_ptr.?,
};
@@ -1285,9 +1281,10 @@ fn varDecl(
const sub_scope = try block_arena.create(Scope.LocalPtr);
sub_scope.* = .{
.parent = scope,
- .gen_zir = scope.getGenZir(),
+ .gen_zir = gz,
.name = ident_name,
.ptr = var_data.alloc,
+ .src = gz.nodeSrcLoc(node),
};
return &sub_scope.base;
},
@@ -2078,10 +2075,10 @@ fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZir)
assert(dst_index == body.instructions.len);
}
-fn copyBodyNoEliding(body: *zir.Body, scope: Module.Scope.GenZir) !void {
- body.* = .{
- .instructions = try scope.arena.dupe(zir.Inst.Ref, scope.instructions.items),
- };
+fn copyBodyNoEliding(block_inst: zir.Inst.Index, gz: Module.Scope.GenZir) !void {
+ const zir_datas = gz.zir_code.instructions.items(.data);
+ zir_datas[block_inst].pl_node.payload_index = @intCast(u32, gz.zir_code.extra.items.len);
+ try gz.zir_code.extra.appendSlice(gz.zir_code.gpa, gz.instructions.items);
}
fn whileExpr(
@@ -3515,7 +3512,7 @@ fn suspendExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!zir
return mod.failWithOwnedErrorMsg(scope, msg);
}
- var suspend_scope: Scope.GenZIR = .{
+ var suspend_scope: Scope.GenZir = .{
.base = .{ .tag = .gen_suspend },
.parent = scope,
.decl = scope.ownerDecl().?,
@@ -3864,7 +3861,10 @@ fn rvalue(
const src_token = tree.firstToken(src_node);
return gz.addUnTok(.ref, result, src_token);
},
- .ty => |ty_inst| return gz.addBin(.as, ty_inst, result),
+ .ty => |ty_inst| return gz.addPlNode(.as_node, src_node, zir.Inst.As{
+ .dest_type = ty_inst,
+ .operand = result,
+ }),
.ptr => |ptr_inst| {
_ = try gz.addBin(.store, ptr_inst, result);
return result;
@@ -3953,17 +3953,17 @@ fn setBlockResultLoc(block_scope: *Scope.GenZir, parent_rl: ResultLoc) void {
},
.inferred_ptr => |ptr| {
- block_scope.rl_ptr = &ptr.base;
+ block_scope.rl_ptr = ptr;
block_scope.break_result_loc = .{ .block_ptr = block_scope };
},
.bitcasted_ptr => |ptr| {
- block_scope.rl_ptr = &ptr.base;
+ block_scope.rl_ptr = ptr;
block_scope.break_result_loc = .{ .block_ptr = block_scope };
},
.block_ptr => |parent_block_scope| {
- block_scope.rl_ptr = parent_block_scope.rl_ptr.?;
+ block_scope.rl_ptr = parent_block_scope.rl_ptr;
block_scope.break_result_loc = .{ .block_ptr = block_scope };
},
}
src/Compilation.zig
@@ -317,7 +317,7 @@ pub const AllErrors = struct {
for (notes) |*note, i| {
const module_note = module_err_msg.notes[i];
const source = try module_note.src_loc.fileScope().getSource(module);
- const byte_offset = try module_note.src_loc.byteOffset(module);
+ const byte_offset = try module_note.src_loc.byteOffset();
const loc = std.zig.findLineColumn(source, byte_offset);
const sub_file_path = module_note.src_loc.fileScope().sub_file_path;
note.* = .{
@@ -331,7 +331,7 @@ pub const AllErrors = struct {
};
}
const source = try module_err_msg.src_loc.fileScope().getSource(module);
- const byte_offset = try module_err_msg.src_loc.byteOffset(module);
+ const byte_offset = try module_err_msg.src_loc.byteOffset();
const loc = std.zig.findLineColumn(source, byte_offset);
const sub_file_path = module_err_msg.src_loc.fileScope().sub_file_path;
try errors.append(.{
src/Module.zig
@@ -241,6 +241,10 @@ pub const Decl = struct {
return .{ .token_offset = token_index - decl.srcToken() };
}
+ pub fn nodeSrcLoc(decl: *Decl, node_index: ast.Node.Index) LazySrcLoc {
+ return .{ .node_offset = node_index - decl.srcNode() };
+ }
+
pub fn srcLoc(decl: *Decl) SrcLoc {
return .{
.container = .{ .decl = decl },
@@ -1003,10 +1007,14 @@ pub const Scope = struct {
};
}
- pub fn tokSrcLoc(gz: *GenZir, token_index: ast.TokenIndex) LazySrcLoc {
+ pub fn tokSrcLoc(gz: GenZir, token_index: ast.TokenIndex) LazySrcLoc {
return gz.zir_code.decl.tokSrcLoc(token_index);
}
+ pub fn nodeSrcLoc(gz: GenZir, node_index: ast.Node.Index) LazySrcLoc {
+ return gz.zir_code.decl.nodeSrcLoc(node_index);
+ }
+
pub fn addFnTypeCc(gz: *GenZir, tag: zir.Inst.Tag, args: struct {
param_types: []const zir.Inst.Ref,
ret_ty: zir.Inst.Ref,
@@ -1092,6 +1100,30 @@ pub const Scope = struct {
});
}
+ pub fn addPlNode(
+ gz: *GenZir,
+ tag: zir.Inst.Tag,
+ /// Absolute node index. This function does the conversion to offset from Decl.
+ abs_node_index: ast.Node.Index,
+ extra: anytype,
+ ) !zir.Inst.Ref {
+ const gpa = gz.zir_code.gpa;
+ try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
+ try gz.zir_code.instructions.ensureCapacity(gpa, gz.zir_code.instructions.len + 1);
+
+ const payload_index = try gz.zir_code.addExtra(extra);
+ const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
+ gz.zir_code.instructions.appendAssumeCapacity(.{
+ .tag = tag,
+ .data = .{ .pl_node = .{
+ .src_node = gz.zir_code.decl.srcNode() - abs_node_index,
+ .payload_index = payload_index,
+ } },
+ });
+ gz.instructions.appendAssumeCapacity(new_index);
+ return new_index + gz.zir_code.ref_start_index;
+ }
+
pub fn addUnTok(
gz: *GenZir,
tag: zir.Inst.Tag,
@@ -1165,6 +1197,21 @@ pub const Scope = struct {
});
}
+ /// Note that this returns a `zir.Inst.Index` not a ref.
+ /// Does *not* append the block instruction to the scope.
+ /// Leaves the `payload_index` field undefined.
+ pub fn addBlock(gz: *GenZir, tag: zir.Inst.Tag, node: ast.Node.Index) !zir.Inst.Index {
+ const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len);
+ try gz.zir_code.instructions.append(gz.zir_code.gpa, .{
+ .tag = tag,
+ .data = .{ .pl_node = .{
+ .src_node = node - gz.zir_code.decl.srcNode(),
+ .payload_index = undefined,
+ } },
+ });
+ return new_index;
+ }
+
fn add(gz: *GenZir, inst: zir.Inst) !zir.Inst.Ref {
const gpa = gz.zir_code.gpa;
try gz.instructions.ensureCapacity(gpa, gz.instructions.items.len + 1);
@@ -1188,6 +1235,8 @@ pub const Scope = struct {
gen_zir: *GenZir,
name: []const u8,
inst: zir.Inst.Index,
+ /// Source location of the corresponding variable declaration.
+ src: LazySrcLoc,
};
/// This could be a `const` or `var` local. It has a pointer instead of a value.
@@ -1201,6 +1250,8 @@ pub const Scope = struct {
gen_zir: *GenZir,
name: []const u8,
ptr: zir.Inst.Index,
+ /// Source location of the corresponding variable declaration.
+ src: LazySrcLoc,
};
pub const Nosuspend = struct {
@@ -1246,6 +1297,169 @@ pub const WipZirCode = struct {
return result;
}
+ /// Returns `true` if and only if the instruction *always* has a void type, or
+ /// *always* has a NoReturn type. Function calls return false because
+ /// the answer depends on their type.
+ /// This is used to elide unnecessary `ensure_result_used` instructions.
+ pub fn isVoidOrNoReturn(wzc: WipZirCode, inst_ref: zir.Inst.Ref) bool {
+ if (inst_ref >= wzc.ref_start_index) {
+ const inst = inst_ref - wzc.ref_start_index;
+ const tags = wzc.instructions.items(.tag);
+ switch (tags[inst]) {
+ .@"const" => {
+ const tv = wzc.instructions.items(.data)[inst].@"const";
+ return switch (tv.ty.zigTypeTag()) {
+ .NoReturn, .Void => true,
+ else => false,
+ };
+ },
+
+ .add,
+ .addwrap,
+ .alloc,
+ .alloc_mut,
+ .alloc_inferred,
+ .alloc_inferred_mut,
+ .array_cat,
+ .array_mul,
+ .array_type,
+ .array_type_sentinel,
+ .indexable_ptr_len,
+ .as,
+ .as_node,
+ .@"asm",
+ .asm_volatile,
+ .bit_and,
+ .bitcast,
+ .bitcast_ref,
+ .bitcast_result_ptr,
+ .bit_or,
+ .block,
+ .block_flat,
+ .block_comptime,
+ .block_comptime_flat,
+ .bool_not,
+ .bool_and,
+ .bool_or,
+ .call,
+ .call_async_kw,
+ .call_no_async,
+ .call_compile_time,
+ .call_none,
+ .cmp_lt,
+ .cmp_lte,
+ .cmp_eq,
+ .cmp_gte,
+ .cmp_gt,
+ .cmp_neq,
+ .coerce_result_ptr,
+ .decl_ref,
+ .decl_val,
+ .deref_node,
+ .div,
+ .elem_ptr,
+ .elem_val,
+ .elem_ptr_node,
+ .elem_val_node,
+ .floatcast,
+ .field_ptr,
+ .field_val,
+ .field_ptr_named,
+ .field_val_named,
+ .fn_type,
+ .fn_type_var_args,
+ .fn_type_cc,
+ .fn_type_cc_var_args,
+ .int,
+ .intcast,
+ .int_type,
+ .is_non_null,
+ .is_null,
+ .is_non_null_ptr,
+ .is_null_ptr,
+ .is_err,
+ .is_err_ptr,
+ .mod_rem,
+ .mul,
+ .mulwrap,
+ .param_type,
+ .ptrtoint,
+ .ref,
+ .ret_ptr,
+ .ret_type,
+ .shl,
+ .shr,
+ .store,
+ .store_to_block_ptr,
+ .store_to_inferred_ptr,
+ .str,
+ .sub,
+ .subwrap,
+ .typeof,
+ .xor,
+ .optional_type,
+ .optional_type_from_ptr_elem,
+ .optional_payload_safe,
+ .optional_payload_unsafe,
+ .optional_payload_safe_ptr,
+ .optional_payload_unsafe_ptr,
+ .err_union_payload_safe,
+ .err_union_payload_unsafe,
+ .err_union_payload_safe_ptr,
+ .err_union_payload_unsafe_ptr,
+ .err_union_code,
+ .err_union_code_ptr,
+ .ptr_type,
+ .ptr_type_simple,
+ .enum_literal,
+ .enum_literal_small,
+ .merge_error_sets,
+ .anyframe_type,
+ .error_union_type,
+ .bit_not,
+ .error_set,
+ .error_value,
+ .slice_start,
+ .slice_end,
+ .slice_sentinel,
+ .import,
+ .typeof_peer,
+ .resolve_inferred_alloc,
+ .@"resume",
+ .@"await",
+ .nosuspend_await,
+ => return false,
+
+ .breakpoint,
+ .dbg_stmt_node,
+ .ensure_result_used,
+ .ensure_result_non_error,
+ .set_eval_branch_quota,
+ .compile_log,
+ .ensure_err_payload_void,
+ .@"break",
+ .break_void_tok,
+ .condbr,
+ .compile_error,
+ .ret_node,
+ .ret_tok,
+ .ret_coerce,
+ .unreachable_unsafe,
+ .unreachable_safe,
+ .loop,
+ .suspend_block,
+ .suspend_block_one,
+ .elided,
+ => return true,
+ }
+ }
+ return switch (inst_ref) {
+ @enumToInt(zir.Const.unused) => unreachable,
+ @enumToInt(zir.Const.void_value), @enumToInt(zir.Const.unreachable_value) => true,
+ else => false,
+ };
+ }
+
pub fn deinit(wzc: *WipZirCode) void {
wzc.instructions.deinit(wzc.gpa);
wzc.extra.deinit(wzc.gpa);
@@ -1348,7 +1562,7 @@ pub const SrcLoc = struct {
};
}
- pub fn byteOffset(src_loc: SrcLoc, mod: *Module) !u32 {
+ pub fn byteOffset(src_loc: SrcLoc) !u32 {
switch (src_loc.lazy) {
.unneeded => unreachable,
.todo => unreachable,
@@ -1373,14 +1587,14 @@ pub const SrcLoc = struct {
.token_offset => |tok_off| {
const decl = src_loc.container.decl;
const tok_index = decl.srcToken() + tok_off;
- const tree = src_loc.container.file_scope.base.tree();
+ const tree = decl.container.file_scope.base.tree();
const token_starts = tree.tokens.items(.start);
return token_starts[tok_index];
},
.node_offset => |node_off| {
const decl = src_loc.container.decl;
const node_index = decl.srcNode() + node_off;
- const tree = src_loc.container.file_scope.base.tree();
+ const tree = decl.container.file_scope.base.tree();
const tok_index = tree.firstToken(node_index);
const token_starts = tree.tokens.items(.start);
return token_starts[tok_index];
@@ -1826,7 +2040,7 @@ fn astgenAndSemaDecl(mod: *Module, decl: *Decl) !bool {
const code = try gen_scope.finish();
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
- code.dump(mod.gpa, "comptime_block", decl.name, 0) catch {};
+ code.dump(mod.gpa, "comptime_block", &gen_scope.base, 0) catch {};
}
break :blk code;
};
@@ -2047,7 +2261,7 @@ fn astgenAndSemaFn(
const fn_type_code = try fn_type_scope.finish();
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
- fn_type_code.dump(mod.gpa, "fn_type", decl.name, 0) catch {};
+ fn_type_code.dump(mod.gpa, "fn_type", &fn_type_scope.base, 0) catch {};
}
var fn_type_sema: Sema = .{
@@ -2146,6 +2360,7 @@ fn astgenAndSemaFn(
.name = param_name,
// Implicit const list first, then implicit arg list.
.inst = @intCast(u32, zir.const_inst_list.len + i),
+ .src = decl.tokSrcLoc(name_token),
};
params_scope = &sub_scope.base;
@@ -2164,13 +2379,16 @@ fn astgenAndSemaFn(
!wip_zir_code.instructions.items(.tag)[gen_scope.instructions.items.len - 1]
.isNoReturn())
{
- const void_operand = @enumToInt(zir.Const.void_value);
- _ = try gen_scope.addUnTok(.ret_tok, void_operand, tree.lastToken(body_node));
+ // astgen uses result location semantics to coerce return operands.
+ // Since we are adding the return instruction here, we must handle the coercion.
+ // We do this by using the `ret_coerce` instruction.
+ const void_inst: zir.Inst.Ref = @enumToInt(zir.Const.void_value);
+ _ = try gen_scope.addUnTok(.ret_coerce, void_inst, tree.lastToken(body_node));
}
const code = try gen_scope.finish();
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
- code.dump(mod.gpa, "fn_body", decl.name, param_count) catch {};
+ code.dump(mod.gpa, "fn_body", &gen_scope.base, param_count) catch {};
}
break :blk code;
@@ -2347,7 +2565,7 @@ fn astgenAndSemaVarDecl(
);
const code = try gen_scope.finish();
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
- code.dump(mod.gpa, "var_init", decl.name, 0) catch {};
+ code.dump(mod.gpa, "var_init", &gen_scope.base, 0) catch {};
}
var sema: Sema = .{
@@ -2409,7 +2627,7 @@ fn astgenAndSemaVarDecl(
const var_type = try astgen.typeExpr(mod, &type_scope.base, var_decl.ast.type_node);
const code = try type_scope.finish();
if (std.builtin.mode == .Debug and mod.comp.verbose_ir) {
- code.dump(mod.gpa, "var_type", decl.name, 0) catch {};
+ code.dump(mod.gpa, "var_type", &type_scope.base, 0) catch {};
}
var sema: Sema = .{
@@ -3475,7 +3693,7 @@ pub fn failNode(
args: anytype,
) InnerError {
const decl_node = scope.srcDecl().?.srcNode();
- const src: LazySrcLoc = .{ .node_offset = node_index - decl_node };
+ const src: LazySrcLoc = .{ .node_offset = decl_node - node_index };
return mod.fail(scope, src, format, args);
}
src/Sema.zig
@@ -108,6 +108,7 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
.dbg_stmt_node => try sema.zirDbgStmtNode(block, zir_inst),
.decl_ref => try sema.zirDeclRef(block, zir_inst),
.decl_val => try sema.zirDeclVal(block, zir_inst),
+ .elided => continue,
.ensure_result_used => try sema.zirEnsureResultUsed(block, zir_inst),
.ensure_result_non_error => try sema.zirEnsureResultNonError(block, zir_inst),
.indexable_ptr_len => try sema.zirIndexablePtrLen(block, zir_inst),
@@ -133,11 +134,13 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde
.field_val_named => try sema.zirFieldValNamed(block, zir_inst),
.deref_node => try sema.zirDerefNode(block, zir_inst),
.as => try sema.zirAs(block, zir_inst),
+ .as_node => try sema.zirAsNode(block, zir_inst),
.@"asm" => try sema.zirAsm(block, zir_inst, false),
.asm_volatile => try sema.zirAsm(block, zir_inst, true),
.unreachable_safe => try sema.zirUnreachable(block, zir_inst, true),
.unreachable_unsafe => try sema.zirUnreachable(block, zir_inst, false),
- .ret_tok => try sema.zirRetTok(block, zir_inst),
+ .ret_coerce => try sema.zirRetTok(block, zir_inst, true),
+ .ret_tok => try sema.zirRetTok(block, zir_inst, false),
.ret_node => try sema.zirRetNode(block, zir_inst),
.fn_type => try sema.zirFnType(block, zir_inst, false),
.fn_type_cc => try sema.zirFnTypeCc(block, zir_inst, false),
@@ -1004,7 +1007,7 @@ fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerE
const src_node = sema.code.instructions.items(.data)[inst].node;
const src: LazySrcLoc = .{ .node_offset = src_node };
const src_loc = src.toSrcLoc(&block.base);
- const abs_byte_off = try src_loc.byteOffset(sema.mod);
+ const abs_byte_off = try src_loc.byteOffset();
return block.addDbgStmt(src, abs_byte_off);
}
@@ -1767,9 +1770,29 @@ fn zirAs(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Ins
defer tracy.end();
const bin_inst = sema.code.instructions.items(.data)[inst].bin;
- const dest_type = try sema.resolveType(block, .todo, bin_inst.lhs);
- const tzir_inst = try sema.resolveInst(bin_inst.rhs);
- return sema.coerce(block, dest_type, tzir_inst, .todo);
+ return sema.analyzeAs(block, .unneeded, bin_inst.lhs, bin_inst.rhs);
+}
+
+fn zirAsNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+ const tracy = trace(@src());
+ defer tracy.end();
+
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const src = inst_data.src();
+ const extra = sema.code.extraData(zir.Inst.As, inst_data.payload_index).data;
+ return sema.analyzeAs(block, src, extra.dest_type, extra.operand);
+}
+
+fn analyzeAs(
+ sema: *Sema,
+ block: *Scope.Block,
+ src: LazySrcLoc,
+ zir_dest_type: zir.Inst.Ref,
+ zir_operand: zir.Inst.Ref,
+) InnerError!*Inst {
+ const dest_type = try sema.resolveType(block, src, zir_dest_type);
+ const operand = try sema.resolveInst(zir_operand);
+ return sema.coerce(block, dest_type, operand, src);
}
fn zirPtrtoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2850,7 +2873,12 @@ fn zirUnreachable(
}
}
-fn zirRetTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
+fn zirRetTok(
+ sema: *Sema,
+ block: *Scope.Block,
+ inst: zir.Inst.Index,
+ need_coercion: bool,
+) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();
@@ -2858,7 +2886,7 @@ fn zirRetTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!
const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
- return sema.analyzeRet(block, operand, src);
+ return sema.analyzeRet(block, operand, src, need_coercion);
}
fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
@@ -2869,10 +2897,16 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError
const operand = try sema.resolveInst(inst_data.operand);
const src = inst_data.src();
- return sema.analyzeRet(block, operand, src);
+ return sema.analyzeRet(block, operand, src, false);
}
-fn analyzeRet(sema: *Sema, block: *Scope.Block, operand: *Inst, src: LazySrcLoc) InnerError!*Inst {
+fn analyzeRet(
+ sema: *Sema,
+ block: *Scope.Block,
+ operand: *Inst,
+ src: LazySrcLoc,
+ need_coercion: bool,
+) InnerError!*Inst {
if (block.inlining) |inlining| {
// We are inlining a function call; rewrite the `ret` as a `break`.
try inlining.merges.results.append(sema.gpa, operand);
@@ -2880,7 +2914,13 @@ fn analyzeRet(sema: *Sema, block: *Scope.Block, operand: *Inst, src: LazySrcLoc)
return &br.base;
}
- try sema.requireFunctionBlock(block, src);
+ if (need_coercion) {
+ if (sema.func) |func| {
+ const fn_ty = func.owner_decl.typed_value.most_recent.typed_value.ty;
+ const casted_operand = try sema.coerce(block, fn_ty.fnReturnType(), operand, src);
+ return block.addUnOp(src, Type.initTag(.noreturn), .ret, casted_operand);
+ }
+ }
return block.addUnOp(src, Type.initTag(.noreturn), .ret, operand);
}
src/zir.zig
@@ -72,7 +72,7 @@ pub const Code = struct {
code: Code,
gpa: *Allocator,
kind: []const u8,
- decl_name: [*:0]const u8,
+ scope: *Module.Scope,
param_count: usize,
) !void {
var arena = std.heap.ArenaAllocator.init(gpa);
@@ -81,11 +81,13 @@ pub const Code = struct {
var writer: Writer = .{
.gpa = gpa,
.arena = &arena.allocator,
+ .scope = scope,
.code = code,
.indent = 4,
.param_count = param_count,
};
+ const decl_name = scope.srcDecl().?.name;
const stderr = std.io.getStdErr().writer();
try stderr.print("ZIR {s} {s} {{\n", .{ kind, decl_name });
@@ -416,9 +418,12 @@ pub const Inst = struct {
/// error if the indexable object is not indexable.
/// Uses the `un_node` field. The AST node is the for loop node.
indexable_ptr_len,
- /// Type coercion.
+ /// Type coercion. No source location attached.
/// Uses the `bin` field.
as,
+ /// Type coercion to the function's return type.
+ /// Uses the `pl_node` field. Payload is `As`. AST node could be many things.
+ as_node,
/// Inline assembly. Non-volatile.
/// Uses the `pl_node` union field. Payload is `Asm`. AST node is the assembly node.
@"asm",
@@ -464,12 +469,14 @@ pub const Inst = struct {
/// Uses the `bin` field.
bool_or,
/// Return a value from a block.
- /// Uses the `bin` union field: `lhs` is `Ref` to the block, `rhs` is operand.
+ /// Uses the `bin` union field: `lhs` is `Index` to the block (*not* `Ref`!),
+ /// `rhs` is operand.
/// Uses the source information from previous instruction.
@"break",
/// Same as `break` but has source information in the form of a token, and
/// the operand is assumed to be the void value.
/// Uses the `un_tok` union field.
+ /// Note that the block operand is a `Index`, not `Ref`.
break_void_tok,
/// Uses the `node` union field.
breakpoint,
@@ -543,6 +550,9 @@ pub const Inst = struct {
/// Same as `elem_val` except also stores a source location node.
/// Uses the `pl_node` union field. AST node is a[b] syntax. Payload is `Bin`.
elem_val_node,
+ /// This instruction has been deleted late in the astgen phase. It must
+ /// be ignored, and the corresponding `Data` is undefined.
+ elided,
/// Emits a compile error if the operand is not `void`.
/// Uses the `un_node` field.
ensure_result_used,
@@ -671,6 +681,9 @@ pub const Inst = struct {
/// Includes a token source location.
/// Uses the `un_tok` union field.
ret_tok,
+ /// Same as `ret_tok` except the operand needs to get coerced to the function's
+ /// return type.
+ ret_coerce,
/// Changes the maximum number of backwards branches that compile-time
/// code execution can use before giving up and making a compile error.
/// Uses the `un_node` union field.
@@ -704,6 +717,7 @@ pub const Inst = struct {
store,
/// Same as `store` but the type of the value being stored will be used to infer
/// the block type. The LHS is the pointer to store to.
+ /// Uses the `bin` union field.
store_to_block_ptr,
/// Same as `store` but the type of the value being stored will be used to infer
/// the pointer type.
@@ -854,6 +868,7 @@ pub const Inst = struct {
.array_type_sentinel,
.indexable_ptr_len,
.as,
+ .as_node,
.@"asm",
.asm_volatile,
.bit_and,
@@ -963,6 +978,7 @@ pub const Inst = struct {
.@"resume",
.@"await",
.nosuspend_await,
+ .elided,
=> false,
.@"break",
@@ -971,6 +987,7 @@ pub const Inst = struct {
.compile_error,
.ret_node,
.ret_tok,
+ .ret_coerce,
.unreachable_unsafe,
.unreachable_safe,
.loop,
@@ -1242,11 +1259,17 @@ pub const Inst = struct {
lhs: Ref,
field_name: Ref,
};
+
+ pub const As = struct {
+ dest_type: Ref,
+ operand: Ref,
+ };
};
const Writer = struct {
gpa: *Allocator,
arena: *Allocator,
+ scope: *Module.Scope,
code: Code,
indent: usize,
param_count: usize,
@@ -1325,6 +1348,7 @@ const Writer = struct {
.is_err_ptr,
.ref,
.ret_tok,
+ .ret_coerce,
.typeof,
.optional_type,
.optional_type_from_ptr_elem,
@@ -1348,6 +1372,7 @@ const Writer = struct {
.ptr_type => try self.writePtrType(stream, inst),
.int => try self.writeInt(stream, inst),
.str => try self.writeStr(stream, inst),
+ .elided => try stream.writeAll(")"),
.@"asm",
.asm_volatile,
@@ -1374,6 +1399,7 @@ const Writer = struct {
.slice_sentinel,
.typeof_peer,
.suspend_block,
+ .as_node,
=> try self.writePlNode(stream, inst),
.breakpoint,
@@ -1641,6 +1667,12 @@ const Writer = struct {
}
fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void {
- try stream.print("TODOsrc({s})", .{@tagName(src)});
+ const tree = self.scope.tree();
+ const src_loc = src.toSrcLoc(self.scope);
+ const abs_byte_off = try src_loc.byteOffset();
+ const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off);
+ try stream.print("{s}:{d}:{d}", .{
+ @tagName(src), delta_line.line + 1, delta_line.column + 1,
+ });
}
};
BRANCH_TODO
@@ -1,9 +1,6 @@
this is my WIP branch scratch pad, to be deleted before merging into master
Merge TODO list:
- * fix discrepancy between TZIR wanting src: usize (byte offset) and Sema
- now providing LazySrcLoc
- * fix compile errors
* don't have an explicit dbg_stmt zir instruction - instead merge it with
var decl and assignment instructions, etc.
- make it set sema.src where appropriate
@@ -13,6 +10,7 @@ Merge TODO list:
* finish implementing SrcLoc byteOffset function
* audit Module.zig for use of token_starts - it should only be when
resolving LazySrcLoc
+ * audit astgen.zig for use of token_starts - I think there should be no uses
* audit all the .unneeded src locations
* audit the calls in codegen toSrcLocWithDecl specifically if there is inlined function
calls from other files.
@@ -29,20 +27,6 @@ Performance optimizations to look into:
- Look into this for enum literals too
* make ret_type and ret_ptr instructions be implied indexes; no need to have
tags associated with them.
-
-
-Random snippets of code that I deleted and need to make sure get
-re-integrated appropriately:
-
-
-
- /// Each Decl gets its own string interning, in order to avoid contention when
- /// using multiple threads to analyze Decls in parallel. Any particular Decl will only
- /// be touched by a single thread at one time.
- strings: StringTable = .{},
-
- /// The string memory referenced here is stored inside the Decl's arena.
- pub const StringTable = std.StringArrayHashMapUnmanaged(void);
-
-
+ * use a smaller encoding for the auto generated return void at the end of
+ function ZIR.