diff --git a/BRANCH_TODO b/BRANCH_TODO index bc0a67f799..1cdd8362e7 100644 --- a/BRANCH_TODO +++ b/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 diff --git a/src/AstGen.zig b/src/AstGen.zig index 0b78c839a0..f88b59d211 100644 --- a/src/AstGen.zig +++ b/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), }); diff --git a/src/Module.zig b/src/Module.zig index fa8b4ca768..6253e2808d 100644 --- a/src/Module.zig +++ b/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(); diff --git a/src/Sema.zig b/src/Sema.zig index 5fd3c149a2..923295069d 100644 --- a/src/Sema.zig +++ b/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", .{}); diff --git a/src/Zir.zig b/src/Zir.zig index 6445d73af5..b7f4c28161 100644 --- a/src/Zir.zig +++ b/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, + }; +}