fix: call frame handling, logical short circuiting

This commit is contained in:
Brett Broadhurst 2026-04-01 21:13:36 -06:00
parent 5c133e5fa2
commit 236acc7d60
Failed to generate hash of commit
8 changed files with 301 additions and 159 deletions

View file

@ -423,19 +423,20 @@ const GenIr = struct {
}
fn addBreak(
gen: *GenIr,
gi: *GenIr,
tag: Ir.Inst.Tag,
node: *const Ast.Node,
block_inst: Ir.Inst.Index,
operand: Ir.Inst.Ref,
) !Ir.Inst.Ref {
const gpa = gen.astgen.gpa;
const extra_len = @typeInfo(Ir.Inst.Break).@"struct".fields.len;
try gen.astgen.extra.ensureUnusedCapacity(gpa, extra_len);
try gi.astgen.extra.ensureUnusedCapacity(gi.astgen.gpa, extra_len);
const extra_index = gen.astgen.addExtraAssumeCapacity(
Ir.Inst.Break{ .block_inst = block_inst },
);
return gen.addPayloadNodeWithIndex(tag, node, extra_index);
const extra_index = gi.astgen.addExtraAssumeCapacity(Ir.Inst.Break{
.operand = operand,
.block_inst = block_inst,
});
return gi.addPayloadNodeWithIndex(tag, node, extra_index);
}
fn makePayloadNode(self: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
@ -450,6 +451,23 @@ const GenIr = struct {
return inst_index;
}
/// Assumes nothing stacked on `gz`. Unstacks `gz`.
fn setBoolBrBody(gi: *GenIr, bool_br: Ir.Inst.Index, bool_br_lhs: Ir.Inst.Ref) !void {
const astgen = gi.astgen;
const body = gi.instructionsSlice();
try astgen.extra.ensureUnusedCapacity(
astgen.gpa,
@typeInfo(Ir.Inst.BoolBr).@"struct".fields.len + body.len,
);
const data = &astgen.instructions.items[@intFromEnum(bool_br)].data;
data.payload.extra_index = astgen.addExtraAssumeCapacity(Ir.Inst.BoolBr{
.lhs = bool_br_lhs,
.body_len = @intCast(body.len),
});
astgen.appendBlockBody(body);
gi.unstack();
}
fn setBlockBody(self: *GenIr, inst: Ir.Inst.Index) !void {
const gpa = self.astgen.gpa;
const body = self.instructionsSlice();
@ -644,6 +662,31 @@ fn binaryOp(
return gi.addBinaryNode(op, lhs, rhs);
}
fn boolBinaryOp(
gi: *GenIr,
scope: *Scope,
node: *const Ast.Node,
ir_tag: Ir.Inst.Tag,
) InnerError!Ir.Inst.Ref {
const data = node.data.bin;
const lhs_node = data.lhs.?;
const rhs_node = data.rhs.?;
const lhs = try expr(gi, scope, lhs_node);
const bool_br = (try gi.addPayloadNodeWithIndex(ir_tag, node, undefined)).toIndex().?;
var rhs_block = gi.makeSubBlock();
defer rhs_block.unstack();
const rhs = try expr(&rhs_block, scope, rhs_node);
if (!rhs_block.endsWithNoReturn()) {
_ = try rhs_block.addBreak(.break_inline, rhs_node, bool_br, rhs);
}
try rhs_block.setBoolBrBody(bool_br, lhs);
const block_ref = bool_br.toRef();
return block_ref;
}
fn parseNumberLiteral(bytes: []const u8) union(enum) {
int: i64,
float: f64,
@ -731,8 +774,8 @@ fn expr(gi: *GenIr, scope: *Scope, optional_node: ?*const Ast.Node) InnerError!I
.mod_expr => return binaryOp(gi, scope, node, .mod),
.negate_expr => return unaryOp(gi, scope, node, .neg),
.logical_not_expr => return unaryOp(gi, scope, node, .not),
.logical_and_expr => return binaryOp(gi, scope, node, .bool_and),
.logical_or_expr => return binaryOp(gi, scope, node, .bool_or),
.logical_and_expr => return boolBinaryOp(gi, scope, node, .bool_br_and),
.logical_or_expr => return boolBinaryOp(gi, scope, node, .bool_br_or),
.logical_equality_expr => return binaryOp(gi, scope, node, .cmp_eq),
.logical_inequality_expr => return binaryOp(gi, scope, node, .cmp_neq),
.logical_greater_expr => return binaryOp(gi, scope, node, .cmp_gt),
@ -819,13 +862,13 @@ fn inlineIfStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.
}
}
if (!then_block.endsWithNoReturn()) {
_ = try then_block.addBreak(.@"break", node, block);
_ = try then_block.addBreak(.@"break", node, block, .void);
}
var else_block = gi.makeSubBlock();
defer else_block.unstack();
_ = try else_block.addBreak(.@"break", node, block);
_ = try else_block.addBreak(.@"break", node, block, .void);
try setCondBrPayload(condbr, cond_inst, &then_block, &else_block);
return condbr.toRef();
}
@ -885,7 +928,7 @@ fn ifStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.R
try blockStmt(&then_block, scope, then_body);
if (!then_block.endsWithNoReturn()) {
_ = try then_block.addBreak(.@"break", then_body, block);
_ = try then_block.addBreak(.@"break", then_body, block, .void);
}
var else_block = gi.makeSubBlock();
@ -898,11 +941,11 @@ fn ifStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.Inst.R
try blockStmt(&else_block, scope, else_body);
if (!else_block.endsWithNoReturn()) {
_ = try else_block.addBreak(.@"break", else_body, block);
_ = try else_block.addBreak(.@"break", else_body, block, .void);
}
}
} else {
_ = try else_block.addBreak(.@"break", then_body, block);
_ = try else_block.addBreak(.@"break", then_body, block, .void);
}
try setCondBrPayload(condbr, cond_inst, &then_block, &else_block);
@ -938,7 +981,7 @@ fn ifChain(gi: *GenIr, scope: *Scope, branch_list: []const *Ast.Node) InnerError
try blockStmt(&then_block, scope, body);
if (!then_block.endsWithNoReturn()) {
_ = try then_block.addBreak(.@"break", body, block_inst);
_ = try then_block.addBreak(.@"break", body, block_inst, .void);
}
var else_block = gi.makeSubBlock();
@ -1006,7 +1049,7 @@ fn switchStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.In
_ = try blockStmt(&case_block, scope, case_data.rhs.?);
if (!case_block.endsWithNoReturn()) {
_ = try case_block.addBreak(.@"break", case_stmt, switch_br);
_ = try case_block.addBreak(.@"break", case_stmt, switch_br, .void);
}
const body = case_block.instructionsSlice();
@ -1032,10 +1075,10 @@ fn switchStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) InnerError!Ir.In
const else_data = else_branch.data.bin;
_ = try blockStmt(&else_block, scope, else_data.rhs.?);
if (!else_block.endsWithNoReturn()) {
_ = try else_block.addBreak(.@"break", else_branch, switch_br);
_ = try else_block.addBreak(.@"break", else_branch, switch_br, .void);
}
} else {
_ = try else_block.addBreak(.@"break", node, switch_br);
_ = try else_block.addBreak(.@"break", node, switch_br, .void);
}
const else_body = else_block.instructionsSlice();
@ -1288,14 +1331,18 @@ fn callExpr(
const callee_node = data.lhs.?;
const callee = try calleeExpr(gi, scope, callee_node);
const scratch_top = astgen.scratch.items.len;
defer astgen.scratch.shrinkRetainingCapacity(scratch_top);
// FIXME: List nodes should not have optional slices.
// This hack is an abomination.
const arguments: ?[]*Ast.Node = if (data.rhs) |args_node| args_node.data.list.items else null;
const args_count = if (arguments) |args| args.len else 0;
const call_index: Ir.Inst.Index = @enumFromInt(astgen.instructions.items.len);
const call_inst = call_index.toRef();
try gi.astgen.instructions.append(gpa, undefined);
try gi.instructions.append(gpa, call_index);
const scratch_top = astgen.scratch.items.len;
defer astgen.scratch.shrinkRetainingCapacity(scratch_top);
try astgen.scratch.resize(gpa, scratch_top + args_count);
var scratch_index = scratch_top;
@ -1304,7 +1351,10 @@ fn callExpr(
var arg_block = gi.makeSubBlock();
defer arg_block.unstack();
_ = try expr(&arg_block, scope, arg);
const arg_ref = try expr(&arg_block, scope, arg);
if (!arg_block.endsWithNoReturn()) {
_ = try arg_block.addBreak(.break_inline, arg, call_index, arg_ref);
}
const body = arg_block.instructionsSlice();
try astgen.scratch.ensureUnusedCapacity(gpa, body.len);
@ -1324,7 +1374,13 @@ fn callExpr(
if (args_count != 0) {
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
}
return gi.addPayloadNodeWithIndex(tag, callee_node, extra_index);
astgen.instructions.items[@intFromEnum(call_index)] = .{
.tag = tag,
.data = .{ .payload = .{
.src_offset = @intCast(node.loc.start),
.extra_index = extra_index,
} },
};
},
.field => |callee_field| {
const tag = if (call == .divert) .field_divert else .field_call;
@ -1336,9 +1392,16 @@ fn callExpr(
if (args_count != 0) {
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
}
return gi.addPayloadNodeWithIndex(tag, callee_node, extra_index);
astgen.instructions.items[@intFromEnum(call_index)] = .{
.tag = tag,
.data = .{ .payload = .{
.src_offset = @intCast(node.loc.start),
.extra_index = extra_index,
} },
};
},
}
return call_inst;
}
fn divertExpr(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
@ -1407,7 +1470,7 @@ fn returnStmt(gi: *GenIr, scope: *Scope, node: *const Ast.Node) !void {
const ret_arg = if (node.data.bin.lhs) |lhs| blk: {
const arg_inst = try expr(gi, scope, lhs);
break :blk arg_inst;
} else .none;
} else .void;
_ = try gi.addUnaryNode(.ret, ret_arg);
}
@ -1444,7 +1507,7 @@ fn varDecl(gi: *GenIr, scope: *Scope, decl_node: *const Ast.Node) !void {
const name_str = try astgen.strFromNode(identifier_node);
const var_inst = try decl_block.makePayloadNode(.decl_var);
const rvalue_inst = try expr(&decl_block, scope, expr_node);
_ = try decl_block.addBinaryNode(.break_inline, var_inst.toRef(), rvalue_inst);
_ = try decl_block.addBreak(.break_inline, decl_node, var_inst, rvalue_inst);
try setDeclVarPayload(var_inst, &decl_block, identifier_node);
try setDeclaration(decl_inst, .{