authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-03 17:29:59-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-03 17:29:59-07:00
log609b84611dcde382af5d9fbc2345ede468d31a6f
tree9dab23600c4fd136b10ccd65eb3d908dfe6bb206
parent1472dc3ddb6fd7932ff530e7a2fd3f0185c7353f

stage2: rework runtime, comptime, inline function calls

* ZIR function instructions encode the index of the block that contains the function instruction. This allows Zig to later scan the block and find the parameter instructions, which is needed for semantically analyzing function bodies. * Runtime function calls insert AIR arg instructions and then inserts Sema inst_map entries mapping the ZIR param instructions to them. * comptime/inline function call inserts Sema inst_map entries mapping the ZIR param instructions to the AIR callsite arguments. With this commit we are back to the tests passing.

5 files changed, 117 insertions(+), 78 deletions(-)

BRANCH_TODO-2
......@@ -1,6 +1,4 @@
11* update arg instructions:
2 - runtime function call inserts AIR arg instructions and Sema map items for them
3 - comptime/inline function call inserts Sema map items for the args
42 - generic instantiation inserts Sema map items for the comptime args only, re-runs the
53 Decl ZIR to get the new Fn.
64* generic function call where it makes a new function
src/AstGen.zig+7
......@@ -1125,6 +1125,7 @@ fn fnProtoExpr(
11251125
11261126 const result = try gz.addFunc(.{
11271127 .src_node = fn_proto.ast.proto_node,
1128 .param_block = 0,
11281129 .ret_ty = return_type_inst,
11291130 .body = &[0]Zir.Inst.Index{},
11301131 .cc = cc,
......@@ -3035,6 +3036,7 @@ fn fnDecl(
30353036 break :func try decl_gz.addFunc(.{
30363037 .src_node = decl_node,
30373038 .ret_ty = return_type_inst,
3039 .param_block = block_inst,
30383040 .body = &[0]Zir.Inst.Index{},
30393041 .cc = cc,
30403042 .align_inst = .none, // passed in the per-decl data
......@@ -3071,6 +3073,7 @@ fn fnDecl(
30713073
30723074 break :func try decl_gz.addFunc(.{
30733075 .src_node = decl_node,
3076 .param_block = block_inst,
30743077 .ret_ty = return_type_inst,
30753078 .body = fn_gz.instructions.items,
30763079 .cc = cc,
......@@ -3415,6 +3418,7 @@ fn testDecl(
34153418
34163419 const func_inst = try decl_block.addFunc(.{
34173420 .src_node = node,
3421 .param_block = block_inst,
34183422 .ret_ty = .void_type,
34193423 .body = fn_block.instructions.items,
34203424 .cc = .none,
......@@ -9111,6 +9115,7 @@ const GenZir = struct {
91119115 fn addFunc(gz: *GenZir, args: struct {
91129116 src_node: ast.Node.Index,
91139117 body: []const Zir.Inst.Index,
9118 param_block: Zir.Inst.Index,
91149119 ret_ty: Zir.Inst.Ref,
91159120 cc: Zir.Inst.Ref,
91169121 align_inst: Zir.Inst.Ref,
......@@ -9170,6 +9175,7 @@ const GenZir = struct {
91709175 );
91719176 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedFunc{
91729177 .src_node = gz.nodeIndexToRelative(args.src_node),
9178 .param_block = args.param_block,
91739179 .return_type = args.ret_ty,
91749180 .body_len = @intCast(u32, args.body.len),
91759181 });
......@@ -9212,6 +9218,7 @@ const GenZir = struct {
92129218 );
92139219
92149220 const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Func{
9221 .param_block = args.param_block,
92159222 .return_type = args.ret_ty,
92169223 .body_len = @intCast(u32, args.body.len),
92179224 });
src/Module.zig+38-19
......@@ -2899,7 +2899,6 @@ pub fn semaFile(mod: *Module, file: *Scope.File) SemaError!void {
28992899 .namespace = &struct_obj.namespace,
29002900 .func = null,
29012901 .owner_func = null,
2902 .param_inst_list = &.{},
29032902 };
29042903 defer sema.deinit();
29052904 var block_scope: Scope.Block = .{
......@@ -2954,7 +2953,6 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
29542953 .namespace = decl.namespace,
29552954 .func = null,
29562955 .owner_func = null,
2957 .param_inst_list = &.{},
29582956 };
29592957 defer sema.deinit();
29602958
......@@ -3625,8 +3623,6 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
36253623 defer decl.value_arena.?.* = arena.state;
36263624
36273625 const fn_ty = decl.ty;
3628 const param_inst_list = try gpa.alloc(Air.Inst.Ref, fn_ty.fnParamLen());
3629 defer gpa.free(param_inst_list);
36303626
36313627 var sema: Sema = .{
36323628 .mod = mod,
......@@ -3637,7 +3633,6 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
36373633 .namespace = decl.namespace,
36383634 .func = func,
36393635 .owner_func = func,
3640 .param_inst_list = param_inst_list,
36413636 };
36423637 defer sema.deinit();
36433638
......@@ -3656,29 +3651,55 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) SemaError!Air {
36563651 };
36573652 defer inner_block.instructions.deinit(gpa);
36583653
3659 // AIR requires the arg parameters to be the first N instructions.
3660 try inner_block.instructions.ensureTotalCapacity(gpa, param_inst_list.len);
3661 for (param_inst_list) |*param_inst, param_index| {
3654 const fn_info = sema.code.getFnInfo(func.zir_body_inst);
3655 const zir_tags = sema.code.instructions.items(.tag);
3656
3657 // Here we are performing "runtime semantic analysis" for a function body, which means
3658 // we must map the parameter ZIR instructions to `arg` AIR instructions.
3659 // AIR requires the `arg` parameters to be the first N instructions.
3660 const params_len = @intCast(u32, fn_ty.fnParamLen());
3661 try inner_block.instructions.ensureTotalCapacity(gpa, params_len);
3662 try sema.air_instructions.ensureUnusedCapacity(gpa, params_len * 2); // * 2 for the `addType`
3663 try sema.inst_map.ensureUnusedCapacity(gpa, params_len);
3664
3665 var param_index: usize = 0;
3666 for (fn_info.param_body) |inst| {
3667 const name = switch (zir_tags[inst]) {
3668 .param, .param_comptime => blk: {
3669 const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
3670 const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index).data;
3671 break :blk extra.name;
3672 },
3673
3674 .param_anytype, .param_anytype_comptime => blk: {
3675 const str_tok = sema.code.instructions.items(.data)[inst].str_tok;
3676 break :blk str_tok.start;
3677 },
3678
3679 else => continue,
3680 };
36623681 const param_type = fn_ty.fnParamType(param_index);
3682 param_index += 1;
36633683 const ty_ref = try sema.addType(param_type);
36643684 const arg_index = @intCast(u32, sema.air_instructions.len);
36653685 inner_block.instructions.appendAssumeCapacity(arg_index);
3666 param_inst.* = Air.indexToRef(arg_index);
3667 try sema.air_instructions.append(gpa, .{
3686 sema.air_instructions.appendAssumeCapacity(.{
36683687 .tag = .arg,
3669 .data = .{
3670 .ty_str = .{
3671 .ty = ty_ref,
3672 .str = undefined, // Set in the semantic analysis of the arg instruction.
3673 },
3674 },
3688 .data = .{ .ty_str = .{
3689 .ty = ty_ref,
3690 .str = name,
3691 } },
36753692 });
3693 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
36763694 }
36773695
36783696 func.state = .in_progress;
36793697 log.debug("set {s} to in_progress", .{decl.name});
36803698
3681 try sema.analyzeFnBody(&inner_block, func.zir_body_inst);
3699 _ = sema.analyzeBody(&inner_block, fn_info.body) catch |err| switch (err) {
3700 error.NeededSourceLocation => unreachable,
3701 else => |e| return e,
3702 };
36823703
36833704 // Copy the block into place and mark that as the main block.
36843705 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
43304351 .namespace = &struct_obj.namespace,
43314352 .owner_func = null,
43324353 .func = null,
4333 .param_inst_list = &.{},
43344354 };
43354355 defer sema.deinit();
43364356
......@@ -4484,7 +4504,6 @@ pub fn analyzeUnionFields(mod: *Module, union_obj: *Union) CompileError!void {
44844504 .namespace = &union_obj.namespace,
44854505 .owner_func = null,
44864506 .func = null,
4487 .param_inst_list = &.{},
44884507 };
44894508 defer sema.deinit();
44904509
src/Sema.zig+21-54
......@@ -29,13 +29,6 @@ owner_func: ?*Module.Fn,
2929/// This starts out the same as `owner_func` and then diverges in the case of
3030/// an inline or comptime function call.
3131func: ?*Module.Fn,
32/// For now, AIR requires arg instructions to be the first N instructions in the
33/// AIR code. We store references here for the purpose of `resolveInst`.
34/// This can get reworked with AIR memory layout changes, into simply:
35/// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function,
36/// > otherwise it is the number of parameters of the function.
37/// > param_count: u32
38param_inst_list: []const Air.Inst.Ref,
3932branch_quota: u32 = 1000,
4033branch_count: u32 = 0,
4134/// This field is updated when a new source location becomes active, so that
......@@ -85,43 +78,10 @@ pub fn deinit(sema: *Sema) void {
8578 sema.air_values.deinit(gpa);
8679 sema.inst_map.deinit(gpa);
8780 sema.decl_val_table.deinit(gpa);
81 sema.params.deinit(gpa);
8882 sema.* = undefined;
8983}
9084
91pub fn analyzeFnBody(
92 sema: *Sema,
93 block: *Scope.Block,
94 fn_body_inst: Zir.Inst.Index,
95) SemaError!void {
96 const tags = sema.code.instructions.items(.tag);
97 const datas = sema.code.instructions.items(.data);
98 const body: []const Zir.Inst.Index = switch (tags[fn_body_inst]) {
99 .func, .func_inferred => blk: {
100 const inst_data = datas[fn_body_inst].pl_node;
101 const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
102 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
103 break :blk body;
104 },
105 .extended => blk: {
106 const extended = datas[fn_body_inst].extended;
107 assert(extended.opcode == .func);
108 const extra = sema.code.extraData(Zir.Inst.ExtendedFunc, extended.operand);
109 const small = @bitCast(Zir.Inst.ExtendedFunc.Small, extended.small);
110 var extra_index: usize = extra.end;
111 extra_index += @boolToInt(small.has_lib_name);
112 extra_index += @boolToInt(small.has_cc);
113 extra_index += @boolToInt(small.has_align);
114 const body = sema.code.extra[extra_index..][0..extra.data.body_len];
115 break :blk body;
116 },
117 else => unreachable,
118 };
119 _ = sema.analyzeBody(block, body) catch |err| switch (err) {
120 error.NeededSourceLocation => unreachable,
121 else => |e| return e,
122 };
123}
124
12585/// Returns only the result from the body that is specified.
12686/// Only appropriate to call when it is determined at comptime that this body
12787/// has no peers.
......@@ -1066,7 +1026,6 @@ fn zirEnumDecl(
10661026 .namespace = &enum_obj.namespace,
10671027 .owner_func = null,
10681028 .func = null,
1069 .param_inst_list = &.{},
10701029 .branch_quota = sema.branch_quota,
10711030 .branch_count = sema.branch_count,
10721031 };
......@@ -2538,10 +2497,6 @@ fn analyzeCall(
25382497 sema.func = module_fn;
25392498 defer sema.func = parent_func;
25402499
2541 const parent_param_inst_list = sema.param_inst_list;
2542 sema.param_inst_list = args;
2543 defer sema.param_inst_list = parent_param_inst_list;
2544
25452500 const parent_next_arg_index = sema.next_arg_index;
25462501 sema.next_arg_index = 0;
25472502 defer sema.next_arg_index = parent_next_arg_index;
......@@ -2565,12 +2520,23 @@ fn analyzeCall(
25652520 try sema.emitBackwardBranch(&child_block, call_src);
25662521
25672522 // This will have return instructions analyzed as break instructions to
2568 // the block_inst above.
2569 try sema.analyzeFnBody(&child_block, module_fn.zir_body_inst);
2570
2571 const result = try sema.analyzeBlockBody(block, call_src, &child_block, merges);
2572
2573 break :res result;
2523 // the block_inst above. Here we are performing "comptime/inline semantic analysis"
2524 // for a function body, which means we must map the parameter ZIR instructions to
2525 // the AIR instructions of the callsite.
2526 const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst);
2527 const zir_tags = sema.code.instructions.items(.tag);
2528 var arg_i: usize = 0;
2529 try sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, args.len));
2530 for (fn_info.param_body) |inst| {
2531 switch (zir_tags[inst]) {
2532 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {},
2533 else => continue,
2534 }
2535 sema.inst_map.putAssumeCapacityNoClobber(inst, args[arg_i]);
2536 arg_i += 1;
2537 }
2538 _ = try sema.analyzeBody(&child_block, fn_info.body);
2539 break :res try sema.analyzeBlockBody(block, call_src, &child_block, merges);
25742540 } else if (func_ty_info.is_generic) {
25752541 const func_val = try sema.resolveConstValue(block, func_src, func);
25762542 const module_fn = func_val.castTag(.function).?.data;
......@@ -2601,7 +2567,7 @@ fn analyzeCall(
26012567 // TODO
26022568
26032569 // Queue up a `codegen_func` work item for the new Fn, making sure it will have
2604 // `analyzeFnBody` called with the Scope which contains the comptime parameters.
2570 // `analyzeBody` called with the ZIR parameters mapped appropriately.
26052571 // TODO
26062572
26072573 // Save it into the Module's generic function map.
......@@ -3344,11 +3310,12 @@ fn funcCommon(
33443310 // `resolveSwitchItemVal` to avoid resolving the source location unless
33453311 // we actually need to report an error.
33463312 const param_src = src;
3347 param_types[i] = try sema.resolveType(block, param_src, param.ty);
3313 param_types[i] = try sema.analyzeAsType(block, param_src, param.ty);
33483314 }
33493315 comptime_params[i] = param.is_comptime;
33503316 any_are_comptime = any_are_comptime or param.is_comptime;
33513317 }
3318 sema.params.clearRetainingCapacity();
33523319
33533320 if (align_val.tag() != .null_value) {
33543321 return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{});
src/Zir.zig+51-3
......@@ -61,7 +61,7 @@ pub const ExtraIndex = enum(u32) {
6161 _,
6262};
6363
64pub fn getMainStruct(zir: Zir) Zir.Inst.Index {
64pub fn getMainStruct(zir: Zir) Inst.Index {
6565 return zir.extra[@enumToInt(ExtraIndex.main_struct)] -
6666 @intCast(u32, Inst.Ref.typed_value_map.len);
6767}
......@@ -2260,6 +2260,8 @@ pub const Inst = struct {
22602260 pub const ExtendedFunc = struct {
22612261 src_node: i32,
22622262 return_type: Ref,
2263 /// Points to the block that contains the param instructions for this function.
2264 param_block: Index,
22632265 body_len: u32,
22642266
22652267 pub const Small = packed struct {
......@@ -2297,6 +2299,8 @@ pub const Inst = struct {
22972299 /// 1. src_locs: SrcLocs // if body_len != 0
22982300 pub const Func = struct {
22992301 return_type: Ref,
2302 /// Points to the block that contains the param instructions for this function.
2303 param_block: Index,
23002304 body_len: u32,
23012305
23022306 pub const SrcLocs = struct {
......@@ -4894,10 +4898,54 @@ fn findDeclsSwitchMulti(
48944898
48954899fn findDeclsBody(
48964900 zir: Zir,
4897 list: *std.ArrayList(Zir.Inst.Index),
4898 body: []const Zir.Inst.Index,
4901 list: *std.ArrayList(Inst.Index),
4902 body: []const Inst.Index,
48994903) Allocator.Error!void {
49004904 for (body) |member| {
49014905 try zir.findDeclsInner(list, member);
49024906 }
49034907}
4908
4909pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) struct {
4910 param_body: []const Inst.Index,
4911 body: []const Inst.Index,
4912} {
4913 const tags = zir.instructions.items(.tag);
4914 const datas = zir.instructions.items(.data);
4915 const info: struct {
4916 param_block: Inst.Index,
4917 body: []const Inst.Index,
4918 } = switch (tags[fn_inst]) {
4919 .func, .func_inferred => blk: {
4920 const inst_data = datas[fn_inst].pl_node;
4921 const extra = zir.extraData(Inst.Func, inst_data.payload_index);
4922 const body = zir.extra[extra.end..][0..extra.data.body_len];
4923 break :blk .{
4924 .param_block = extra.data.param_block,
4925 .body = body,
4926 };
4927 },
4928 .extended => blk: {
4929 const extended = datas[fn_inst].extended;
4930 assert(extended.opcode == .func);
4931 const extra = zir.extraData(Inst.ExtendedFunc, extended.operand);
4932 const small = @bitCast(Inst.ExtendedFunc.Small, extended.small);
4933 var extra_index: usize = extra.end;
4934 extra_index += @boolToInt(small.has_lib_name);
4935 extra_index += @boolToInt(small.has_cc);
4936 extra_index += @boolToInt(small.has_align);
4937 const body = zir.extra[extra_index..][0..extra.data.body_len];
4938 break :blk .{
4939 .param_block = extra.data.param_block,
4940 .body = body,
4941 };
4942 },
4943 else => unreachable,
4944 };
4945 assert(tags[info.param_block] == .block or tags[info.param_block] == .block_inline);
4946 const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);
4947 return .{
4948 .param_body = zir.extra[param_block.end..][0..param_block.data.body_len],
4949 .body = info.body,
4950 };
4951}