Commit 609b84611d
Changed files (5)
src/AstGen.zig
@@ -1125,6 +1125,7 @@ fn fnProtoExpr(
const result = try gz.addFunc(.{
.src_node = fn_proto.ast.proto_node,
+ .param_block = 0,
.ret_ty = return_type_inst,
.body = &[0]Zir.Inst.Index{},
.cc = cc,
@@ -3035,6 +3036,7 @@ fn fnDecl(
break :func try decl_gz.addFunc(.{
.src_node = decl_node,
.ret_ty = return_type_inst,
+ .param_block = block_inst,
.body = &[0]Zir.Inst.Index{},
.cc = cc,
.align_inst = .none, // passed in the per-decl data
@@ -3071,6 +3073,7 @@ fn fnDecl(
break :func try decl_gz.addFunc(.{
.src_node = decl_node,
+ .param_block = block_inst,
.ret_ty = return_type_inst,
.body = fn_gz.instructions.items,
.cc = cc,
@@ -3415,6 +3418,7 @@ fn testDecl(
const func_inst = try decl_block.addFunc(.{
.src_node = node,
+ .param_block = block_inst,
.ret_ty = .void_type,
.body = fn_block.instructions.items,
.cc = .none,
@@ -9111,6 +9115,7 @@ const GenZir = struct {
fn addFunc(gz: *GenZir, args: struct {
src_node: ast.Node.Index,
body: []const Zir.Inst.Index,
+ param_block: Zir.Inst.Index,
ret_ty: Zir.Inst.Ref,
cc: Zir.Inst.Ref,
align_inst: Zir.Inst.Ref,
@@ -9170,6 +9175,7 @@ const GenZir = struct {
);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{
.src_node = gz.nodeIndexToRelative(args.src_node),
+ .param_block = args.param_block,
.return_type = args.ret_ty,
.body_len = @intCast(u32, args.body.len),
});
@@ -9212,6 +9218,7 @@ const GenZir = struct {
);
const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{
+ .param_block = args.param_block,
.return_type = args.ret_ty,
.body_len = @intCast(u32, args.body.len),
});
src/Module.zig
@@ -2899,7 +2899,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
.namespace = &struct_obj.namespace,
.func = null,
.owner_func = null,
- .param_inst_list = &.{},
};
defer sema.deinit();
var block_scope: Scope.Block = .{
@@ -2954,7 +2953,6 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
.namespace = decl.namespace,
.func = null,
.owner_func = null,
- .param_inst_list = &.{},
};
defer sema.deinit();
@@ -3625,8 +3623,6 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
defer decl.value_arena.?.* = arena.state;
const fn_ty = decl.ty;
- const param_inst_list = try gpa.alloc(Air.Inst.Ref, fn_ty.fnParamLen());
- defer gpa.free(param_inst_list);
var sema: Sema = .{
.mod = mod,
@@ -3637,7 +3633,6 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
.namespace = decl.namespace,
.func = func,
.owner_func = func,
- .param_inst_list = param_inst_list,
};
defer sema.deinit();
@@ -3656,29 +3651,55 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
};
defer inner_block.instructions.deinit(gpa);
- // AIR requires the arg parameters to be the first N instructions.
- try inner_block.instructions.ensureTotalCapacity(gpa, param_inst_list.len);
- for (param_inst_list) |*param_inst, param_index| {
+ const fn_info = sema.code.getFnInfo(func.zir_body_inst);
+ const zir_tags = sema.code.instructions.items(.tag);
+
+ // Here we are performing "runtime semantic analysis" for a function body, which means
+ // we must map the parameter ZIR instructions to `arg` AIR instructions.
+ // AIR requires the `arg` parameters to be the first N instructions.
+ const params_len = @intCast(u32, fn_ty.fnParamLen());
+ try inner_block.instructions.ensureTotalCapacity(gpa, params_len);
+ try sema.air_instructions.ensureUnusedCapacity(gpa, params_len * 2); // * 2 for the `addType`
+ try sema.inst_map.ensureUnusedCapacity(gpa, params_len);
+
+ var param_index: usize = 0;
+ for (fn_info.param_body) |inst| {
+ const name = switch (zir_tags[inst]) {
+ .param, .param_comptime => blk: {
+ const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
+ const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
+ break :blk extra.name;
+ },
+
+ .param_anytype, .param_anytype_comptime => blk: {
+ const str_tok = sema.code.instructions.items(.data)[inst].str_tok;
+ break :blk str_tok.start;
+ },
+
+ else => continue,
+ };
const param_type = fn_ty.fnParamType(param_index);
+ param_index += 1;
const ty_ref = try sema.addType(param_type);
const arg_index = @intCast(u32, sema.air_instructions.len);
inner_block.instructions.appendAssumeCapacity(arg_index);
- param_inst.* = Air.indexToRef(arg_index);
- try sema.air_instructions.append(gpa, .{
+ sema.air_instructions.appendAssumeCapacity(.{
.tag = .arg,
- .data = .{
- .ty_str = .{
- .ty = ty_ref,
- .str = undefined, // Set in the semantic analysis of the arg instruction.
- },
- },
+ .data = .{ .ty_str = .{
+ .ty = ty_ref,
+ .str = name,
+ } },
});
+ sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
}
func.state = .in_progress;
log.debug("set {s} to in_progress", .{decl.name});
- try sema.analyzeFnBody(&inner_block, func.zir_body_inst);
+ _ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
+ error.NeededSourceLocation => unreachable,
+ else => |e| return e,
+ };
// Copy the block into place and mark that as the main block.
try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
@@ -4330,7 +4351,6 @@ pub fn analyzeStructFields(mod: *Module, struct_obj: *Struct) CompileError!void
.namespace = &struct_obj.namespace,
.owner_func = null,
.func = null,
- .param_inst_list = &.{},
};
defer sema.deinit();
@@ -4484,7 +4504,6 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
.namespace = &union_obj.namespace,
.owner_func = null,
.func = null,
- .param_inst_list = &.{},
};
defer sema.deinit();
src/Sema.zig
@@ -29,13 +29,6 @@ owner_func: ?*Module.Fn,
/// This starts out the same as `owner_func` and then diverges in the case of
/// an inline or comptime function call.
func: ?*Module.Fn,
-/// For now, AIR requires arg instructions to be the first N instructions in the
-/// AIR code. We store references here for the purpose of `resolveInst`.
-/// This can get reworked with AIR memory layout changes, into simply:
-/// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function,
-/// > otherwise it is the number of parameters of the function.
-/// > param_count: u32
-param_inst_list: []const Air.Inst.Ref,
branch_quota: u32 = 1000,
branch_count: u32 = 0,
/// This field is updated when a new source location becomes active, so that
@@ -85,43 +78,10 @@ pub fn deinit(sema: *Sema) void {
sema.air_values.deinit(gpa);
sema.inst_map.deinit(gpa);
sema.decl_val_table.deinit(gpa);
+ sema.params.deinit(gpa);
sema.* = undefined;
}
-pub fn analyzeFnBody(
- sema: *Sema,
- block: *Scope.Block,
- fn_body_inst: Zir.Inst.Index,
-) SemaError!void {
- const tags = sema.code.instructions.items(.tag);
- const datas = sema.code.instructions.items(.data);
- const body: []const Zir.Inst.Index = switch (tags[fn_body_inst]) {
- .func, .func_inferred => blk: {
- const inst_data = datas[fn_body_inst].pl_node;
- const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
- const body = sema.code.extra[extra.end..][0..extra.data.body_len];
- break :blk body;
- },
- .extended => blk: {
- const extended = datas[fn_body_inst].extended;
- assert(extended.opcode == .func);
- const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand);
- const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small);
- var extra_index: usize = extra.end;
- extra_index += @boolToInt(small.has_lib_name);
- extra_index += @boolToInt(small.has_cc);
- extra_index += @boolToInt(small.has_align);
- const body = sema.code.extra[extra_index..][0..extra.data.body_len];
- break :blk body;
- },
- else => unreachable,
- };
- _ = sema.analyzeBody(block, body) catch |err| switch (err) {
- error.NeededSourceLocation => unreachable,
- else => |e| return e,
- };
-}
-
/// Returns only the result from the body that is specified.
/// Only appropriate to call when it is determined at comptime that this body
/// has no peers.
@@ -1066,7 +1026,6 @@ fn zirEnumDecl(
.namespace = &enum_obj.namespace,
.owner_func = null,
.func = null,
- .param_inst_list = &.{},
.branch_quota = sema.branch_quota,
.branch_count = sema.branch_count,
};
@@ -2538,10 +2497,6 @@ fn analyzeCall(
sema.func = module_fn;
defer sema.func = parent_func;
- const parent_param_inst_list = sema.param_inst_list;
- sema.param_inst_list = args;
- defer sema.param_inst_list = parent_param_inst_list;
-
const parent_next_arg_index = sema.next_arg_index;
sema.next_arg_index = 0;
defer sema.next_arg_index = parent_next_arg_index;
@@ -2565,12 +2520,23 @@ fn analyzeCall(
try sema.emitBackwardBranch(&child_block, call_src);
// This will have return instructions analyzed as break instructions to
- // the block_inst above.
- try sema.analyzeFnBody(&child_block, module_fn.zir_body_inst);
-
- const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges);
-
- break :res result;
+ // the block_inst above. Here we are performing "comptime/inline semantic analysis"
+ // for a function body, which means we must map the parameter ZIR instructions to
+ // the AIR instructions of the callsite.
+ const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
+ const zir_tags = sema.code.instructions.items(.tag);
+ var arg_i: usize = 0;
+ try sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len));
+ for (fn_info.param_body) |inst| {
+ switch (zir_tags[inst]) {
+ .param, .param_comptime, .param_anytype, .param_anytype_comptime => {},
+ else => continue,
+ }
+ sema.inst_map.putAssumeCapacityNoClobber(inst, args[arg_i]);
+ arg_i += 1;
+ }
+ _ = try sema.analyzeBody(&child_block, fn_info.body);
+ break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
} else if (func_ty_info.is_generic) {
const func_val = try sema.resolveConstValue(block, func_src, func);
const module_fn = func_val.castTag(.function).?.data;
@@ -2601,7 +2567,7 @@ fn analyzeCall(
// TODO
// Queue up a `codegen_func` work item for the new Fn, making sure it will have
- // `analyzeFnBody` called with the Scope which contains the comptime parameters.
+ // `analyzeBody` called with the ZIR parameters mapped appropriately.
// TODO
// Save it into the Module's generic function map.
@@ -3344,11 +3310,12 @@ fn funcCommon(
// `resolveSwitchItemVal` to avoid resolving the source location unless
// we actually need to report an error.
const param_src = src;
- param_types[i] = try sema.resolveType(block, param_src, param.ty);
+ param_types[i] = try sema.analyzeAsType(block, param_src, param.ty);
}
comptime_params[i] = param.is_comptime;
any_are_comptime = any_are_comptime or param.is_comptime;
}
+ sema.params.clearRetainingCapacity();
if (align_val.tag() != .null_value) {
return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{});
src/Zir.zig
@@ -61,7 +61,7 @@ pub const ExtraIndex = enum(u32) {
_,
};
-pub fn getMainStruct(zir: Zir) Zir.Inst.Index {
+pub fn getMainStruct(zir: Zir) Inst.Index {
return zir.extra[@enumToInt(ExtraIndex.main_struct)] -
@intCast(u32, Inst.Ref.typed_value_map.len);
}
@@ -2260,6 +2260,8 @@ pub const Inst = struct {
pub const ExtendedFunc = struct {
src_node: i32,
return_type: Ref,
+ /// Points to the block that contains the param instructions for this function.
+ param_block: Index,
body_len: u32,
pub const Small = packed struct {
@@ -2297,6 +2299,8 @@ pub const Inst = struct {
/// 1. src_locs: SrcLocs // if body_len != 0
pub const Func = struct {
return_type: Ref,
+ /// Points to the block that contains the param instructions for this function.
+ param_block: Index,
body_len: u32,
pub const SrcLocs = struct {
@@ -4894,10 +4898,54 @@ fn findDeclsSwitchMulti(
fn findDeclsBody(
zir: Zir,
- list: *std.ArrayList(Zir.Inst.Index),
- body: []const Zir.Inst.Index,
+ list: *std.ArrayList(Inst.Index),
+ body: []const Inst.Index,
) Allocator.Error!void {
for (body) |member| {
try zir.findDeclsInner(list, member);
}
}
+
+pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
+ param_body: []const Inst.Index,
+ body: []const Inst.Index,
+} {
+ const tags = zir.instructions.items(.tag);
+ const datas = zir.instructions.items(.data);
+ const info: struct {
+ param_block: Inst.Index,
+ body: []const Inst.Index,
+ } = switch (tags[fn_inst]) {
+ .func, .func_inferred => blk: {
+ const inst_data = datas[fn_inst].pl_node;
+ const extra = zir.extraData(Inst.Func, inst_data.payload_index);
+ const body = zir.extra[extra.end..][0..extra.data.body_len];
+ break :blk .{
+ .param_block = extra.data.param_block,
+ .body = body,
+ };
+ },
+ .extended => blk: {
+ const extended = datas[fn_inst].extended;
+ assert(extended.opcode == .func);
+ const extra = zir.extraData(Inst.ExtendedFunc, extended.operand);
+ const small = @bitCast(Inst.ExtendedFunc.Small, extended.small);
+ var extra_index: usize = extra.end;
+ extra_index += @boolToInt(small.has_lib_name);
+ extra_index += @boolToInt(small.has_cc);
+ extra_index += @boolToInt(small.has_align);
+ const body = zir.extra[extra_index..][0..extra.data.body_len];
+ break :blk .{
+ .param_block = extra.data.param_block,
+ .body = body,
+ };
+ },
+ else => unreachable,
+ };
+ assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline);
+ const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);
+ return .{
+ .param_body = zir.extra[param_block.end..][0..param_block.data.body_len],
+ .body = info.body,
+ };
+}
BRANCH_TODO
@@ -1,6 +1,4 @@
* update arg instructions:
- - runtime function call inserts AIR arg instructions and Sema map items for them
- - comptime/inline function call inserts Sema map items for the args
- generic instantiation inserts Sema map items for the comptime args only, re-runs the
Decl ZIR to get the new Fn.
* generic function call where it makes a new function