Skip to content

Simplify and compact switch ZIR, and resolve union payload captures with PTR #15880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 82 additions & 71 deletions src/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2610,13 +2610,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.slice_length,
.import,
.switch_block,
.switch_cond,
.switch_cond_ref,
.switch_capture,
.switch_capture_ref,
.switch_capture_multi,
.switch_capture_multi_ref,
.switch_capture_tag,
.switch_block_ref,
.struct_init_empty,
.struct_init,
.struct_init_ref,
Expand Down Expand Up @@ -2960,7 +2954,7 @@ fn deferStmt(
try gz.astgen.instructions.append(gz.astgen.gpa, .{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .errdefer_err_code,
.opcode = .value_placeholder,
.small = undefined,
.operand = undefined,
} },
Expand Down Expand Up @@ -6715,6 +6709,7 @@ fn switchExpr(
// for the following variables, make note of the special prong AST node index,
// and bail out with a compile error if there are multiple special prongs present.
var any_payload_is_ref = false;
var any_has_tag_capture = false;
var scalar_cases_len: u32 = 0;
var multi_cases_len: u32 = 0;
var inline_cases_len: u32 = 0;
Expand All @@ -6725,8 +6720,12 @@ fn switchExpr(
for (case_nodes) |case_node| {
const case = tree.fullSwitchCase(case_node).?;
if (case.payload_token) |payload_token| {
if (token_tags[payload_token] == .asterisk) {
const ident = if (token_tags[payload_token] == .asterisk) blk: {
any_payload_is_ref = true;
break :blk payload_token + 1;
} else payload_token;
if (token_tags[ident + 1] == .comma) {
any_has_tag_capture = true;
}
}
// Check for else/`_` prong.
Expand Down Expand Up @@ -6835,13 +6834,7 @@ fn switchExpr(
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };

const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
// Sema expects a dbg_stmt immediately after switch_cond(_ref)
try emitDbgStmt(parent_gz, operand_lc);
// We need the type of the operand to use as the result location for all the prong items.
const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } };
const item_ri: ResultInfo = .{ .rl = .none };

// This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
// except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
Expand All @@ -6860,13 +6853,30 @@ fn switchExpr(
block_scope.instructions_top = GenZir.unstacked_top;
block_scope.setBreakResultInfo(ri);

// Sema expects a dbg_stmt immediately before switch_block(_ref)
try emitDbgStmt(parent_gz, operand_lc);
// This gets added to the parent block later, after the item expressions.
const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);

// We re-use this same scope for all cases, including the special prong, if any.
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
case_scope.instructions_top = GenZir.unstacked_top;

// If any prong has an inline tag capture, allocate a shared dummy instruction for it
const tag_inst = if (any_has_tag_capture) tag_inst: {
const inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
try astgen.instructions.append(astgen.gpa, .{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .value_placeholder,
.small = undefined,
.operand = undefined,
} }, // TODO rename opcode
});
break :tag_inst inst;
} else undefined;

// In this pass we generate all the item and prong expressions.
var multi_case_index: u32 = 0;
var scalar_case_index: u32 = 0;
Expand All @@ -6880,17 +6890,22 @@ fn switchExpr(
var dbg_var_inst: Zir.Inst.Ref = undefined;
var dbg_var_tag_name: ?u32 = null;
var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
var capture_inst: Zir.Inst.Index = 0;
var tag_inst: Zir.Inst.Index = 0;
var has_tag_capture = false;
var capture_val_scope: Scope.LocalVal = undefined;
var tag_scope: Scope.LocalVal = undefined;

var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;

const sub_scope = blk: {
const payload_token = case.payload_token orelse break :blk &case_scope.base;
const ident = if (token_tags[payload_token] == .asterisk)
payload_token + 1
else
payload_token;

const is_ptr = ident != payload_token;
capture = if (is_ptr) .by_ref else .by_val;

const ident_slice = tree.tokenSlice(ident);
var payload_sub_scope: *Scope = undefined;
if (mem.eql(u8, ident_slice, "_")) {
Expand All @@ -6899,53 +6914,18 @@ fn switchExpr(
}
payload_sub_scope = &case_scope.base;
} else {
if (case_node == special_node) {
const capture_tag: Zir.Inst.Tag = if (is_ptr)
.switch_capture_ref
else
.switch_capture;
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
try astgen.instructions.append(gpa, .{
.tag = capture_tag,
.data = .{
.switch_capture = .{
.switch_inst = switch_block,
// Max int communicates that this is the else/underscore prong.
.prong_index = std.math.maxInt(u32),
},
},
});
} else {
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
const is_ptr_bits: u2 = @boolToInt(is_ptr);
const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
0b00 => .switch_capture,
0b01 => .switch_capture_ref,
0b10 => .switch_capture_multi,
0b11 => .switch_capture_multi_ref,
};
const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
try astgen.instructions.append(gpa, .{
.tag = capture_tag,
.data = .{ .switch_capture = .{
.switch_inst = switch_block,
.prong_index = capture_index,
} },
});
}
const capture_name = try astgen.identAsString(ident);
try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
capture_val_scope = .{
.parent = &case_scope.base,
.gen_zir = &case_scope,
.name = capture_name,
.inst = indexToRef(capture_inst),
.inst = indexToRef(switch_block),
.token_src = payload_token,
.id_cat = .capture,
};
dbg_var_name = capture_name;
dbg_var_inst = indexToRef(capture_inst);
dbg_var_inst = indexToRef(switch_block);
payload_sub_scope = &capture_val_scope.base;
}

Expand All @@ -6961,14 +6941,9 @@ fn switchExpr(
}
const tag_name = try astgen.identAsString(tag_token);
try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
try astgen.instructions.append(gpa, .{
.tag = .switch_capture_tag,
.data = .{ .un_tok = .{
.operand = cond,
.src_tok = case_scope.tokenIndexToRelative(tag_token),
} },
});

assert(any_has_tag_capture);
has_tag_capture = true;

tag_scope = .{
.parent = payload_sub_scope,
Expand Down Expand Up @@ -7034,8 +7009,6 @@ fn switchExpr(
case_scope.instructions_top = parent_gz.instructions.items.len;
defer case_scope.unstack();

if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
try case_scope.addDbgBlockBegin();
if (dbg_var_name) |some| {
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
Expand All @@ -7053,10 +7026,42 @@ fn switchExpr(
}

const case_slice = case_scope.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(case_slice);
// Since we use the switch_block instruction itself to refer to the
// capture, which will not be added to the child block, we need to
// handle ref_table manually, and the same for the inline tag
// capture instruction.
const refs_len = refs: {
var n: usize = 0;
var check_inst = switch_block;
while (astgen.ref_table.get(check_inst)) |ref_inst| {
n += 1;
check_inst = ref_inst;
}
if (has_tag_capture) {
check_inst = tag_inst;
while (astgen.ref_table.get(check_inst)) |ref_inst| {
n += 1;
check_inst = ref_inst;
}
}
break :refs n;
};
const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
try payloads.ensureUnusedCapacity(gpa, body_len);
const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
payloads.items[body_len_index] = body_len | inline_bit;
payloads.items[body_len_index] = @bitCast(u32, Zir.Inst.SwitchBlock.ProngInfo{
.body_len = @intCast(u28, body_len),
.capture = capture,
.is_inline = case.inline_token != null,
.has_tag_capture = has_tag_capture,
});
if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
}
if (has_tag_capture) {
if (astgen.ref_table.fetchRemove(tag_inst)) |kv| {
appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
}
}
appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
}
}
Expand All @@ -7065,14 +7070,16 @@ fn switchExpr(

try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
@boolToInt(multi_cases_len != 0) +
@boolToInt(any_has_tag_capture) +
payloads.items.len - case_table_end);

const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
.operand = cond,
.operand = raw_operand,
.bits = Zir.Inst.SwitchBlock.Bits{
.has_multi_cases = multi_cases_len != 0,
.has_else = special_prong == .@"else",
.has_under = special_prong == .under,
.any_has_tag_capture = any_has_tag_capture,
.scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
},
});
Expand All @@ -7081,6 +7088,10 @@ fn switchExpr(
astgen.extra.appendAssumeCapacity(multi_cases_len);
}

if (any_has_tag_capture) {
astgen.extra.appendAssumeCapacity(tag_inst);
}

const zir_datas = astgen.instructions.items(.data);
const zir_tags = astgen.instructions.items(.tag);

Expand All @@ -7103,7 +7114,7 @@ fn switchExpr(
end_index += 3 + items_len + 2 * ranges_len;
}

const body_len = @truncate(u31, payloads.items[body_len_index]);
const body_len = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, payloads.items[body_len_index]).body_len;
end_index += body_len;

switch (strat.tag) {
Expand Down
25 changes: 0 additions & 25 deletions src/Autodoc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1993,31 +1993,6 @@ fn walkInstruction(
.expr = .{ .switchIndex = switch_index },
};
},
.switch_cond => {
const un_node = data[inst_index].un_node;
const operand = try self.walkRef(
file,
parent_scope,
parent_src,
un_node.operand,
need_type,
);
const operand_index = self.exprs.items.len;
try self.exprs.append(self.arena, operand.expr);

// const ast_index = self.ast_nodes.items.len;
// const sep = "=" ** 200;
// log.debug("{s}", .{sep});
// log.debug("SWITCH COND", .{});
// log.debug("ast index = {}", .{ast_index});
// log.debug("ast previous = {}", .{self.ast_nodes.items[ast_index - 1]});
// log.debug("{s}", .{sep});

return DocData.WalkResult{
.typeRef = operand.typeRef,
.expr = .{ .typeOf = operand_index },
};
},

.typeof => {
const un_node = data[inst_index].un_node;
Expand Down
Loading