fn addFunc(
gz: *GenZir,
args: struct {
src_node: Ast.Node.Index,
lbrace_line: u32 = 0,
lbrace_column: u32 = 0,
param_block: Zir.Inst.Index,
ret_gz: ?*GenZir,
body_gz: ?*GenZir,
cc_gz: ?*GenZir,
ret_param_refs: []Zir.Inst.Index,
param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table`
ret_ty_is_generic: bool,
cc_ref: Zir.Inst.Ref,
ret_ref: Zir.Inst.Ref,
noalias_bits: u32,
is_var_args: bool,
is_inferred_error: bool,
is_noinline: bool,
/// Ignored if `body_gz == null`.
proto_hash: std.zig.SrcHash,
},
) !Zir.Inst.Ref {
assert(args.src_node != .root);
const astgen = gz.astgen;
const gpa = astgen.gpa;
const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
const body, const cc_body, const ret_body = bodies: {
var stacked_gz: ?*GenZir = null;
const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: {
const body = body_gz.instructionsSliceUptoOpt(stacked_gz);
stacked_gz = body_gz;
break :body body;
} else &.{};
const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
stacked_gz = cc_gz;
break :body cc_body;
} else &.{};
const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: {
const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz);
stacked_gz = ret_gz;
break :body ret_body;
} else &.{};
break :bodies .{ body, cc_body, ret_body };
};
var src_locs_and_hash_buffer: [7]u32 = undefined;
const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: {
const tree = astgen.tree;
const fn_decl = args.src_node;
const block = switch (tree.nodeTag(fn_decl)) {
.fn_decl => tree.nodeData(fn_decl).node_and_node[1],
.test_decl => tree.nodeData(fn_decl).opt_token_and_node[1],
else => unreachable,
};
const rbrace_start = tree.tokenStart(tree.lastToken(block));
astgen.advanceSourceCursor(rbrace_start);
const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
const rbrace_column: u32 = @intCast(astgen.source_column);
const columns = args.lbrace_column | (rbrace_column << 16);
const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);
src_locs_and_hash_buffer = .{
args.lbrace_line,
rbrace_line,
columns,
proto_hash_arr[0],
proto_hash_arr[1],
proto_hash_arr[2],
proto_hash_arr[3],
};
break :src_locs_and_hash &src_locs_and_hash_buffer;
} else &.{};
const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts);
const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or
args.is_var_args or args.noalias_bits != 0 or args.is_noinline)
inst_info: {
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.FuncFancy).@"struct".field_names.len +
fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) +
fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
body_len + src_locs_and_hash.len +
@intFromBool(args.noalias_bits != 0),
);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
.param_block = args.param_block,
.body_len = body_len,
.bits = .{
.is_var_args = args.is_var_args,
.is_inferred_error = args.is_inferred_error,
.is_noinline = args.is_noinline,
.has_any_noalias = args.noalias_bits != 0,
.has_cc_ref = args.cc_ref != .none,
.has_ret_ty_ref = ret_ref != .none,
.has_cc_body = cc_body.len != 0,
.has_ret_ty_body = ret_body.len != 0,
.ret_ty_is_generic = args.ret_ty_is_generic,
},
});
const zir_datas = astgen.instructions.items(.data);
if (cc_body.len != 0) {
astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body));
astgen.appendBodyWithFixups(cc_body);
const break_extra = zir_datas[@backingInt(cc_body[cc_body.len - 1])].@"break".payload_index;
astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
@backingInt(new_index);
} else if (args.cc_ref != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(args.cc_ref));
}
if (ret_body.len != 0) {
astgen.extra.appendAssumeCapacity(
astgen.countBodyLenAfterFixups(args.ret_param_refs) +
astgen.countBodyLenAfterFixups(ret_body),
);
astgen.appendBodyWithFixups(args.ret_param_refs);
astgen.appendBodyWithFixups(ret_body);
const break_extra = zir_datas[@backingInt(ret_body[ret_body.len - 1])].@"break".payload_index;
astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
@backingInt(new_index);
} else if (ret_ref != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(ret_ref));
}
if (args.noalias_bits != 0) {
astgen.extra.appendAssumeCapacity(args.noalias_bits);
}
astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
break :inst_info .{ .func_fancy, payload_index };
} else inst_info: {
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.Func).@"struct".field_names.len + 1 +
fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
body_len + src_locs_and_hash.len,
);
const ret_body_len = if (ret_body.len != 0)
countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body)
else
@intFromBool(ret_ref != .none);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
.param_block = args.param_block,
.ret_ty = .{
.body_len = @intCast(ret_body_len),
.is_generic = args.ret_ty_is_generic,
},
.body_len = body_len,
});
const zir_datas = astgen.instructions.items(.data);
if (ret_body.len != 0) {
astgen.appendBodyWithFixups(args.ret_param_refs);
astgen.appendBodyWithFixups(ret_body);
const break_extra = zir_datas[@backingInt(ret_body[ret_body.len - 1])].@"break".payload_index;
astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
@backingInt(new_index);
} else if (ret_ref != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(ret_ref));
}
astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
break :inst_info .{
if (args.is_inferred_error) .func_inferred else .func,
payload_index,
};
};
// Order is important when unstacking.
if (args.body_gz) |body_gz| body_gz.unstack();
if (args.cc_gz) |cc_gz| cc_gz.unstack();
if (args.ret_gz) |ret_gz| ret_gz.unstack();
astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(args.src_node),
.payload_index = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
Must be called with the following stack set up:
gz.