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

@ -14,6 +14,7 @@ module: *compile.Module,
ir: Ir,
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, ValueInfo) = .empty,
errors: *std.ArrayListUnmanaged(Module.Error),
comptime_break_inst: Ir.Inst.Index = undefined,
const InnerError = error{
OutOfMemory,
@ -160,6 +161,10 @@ pub const Builder = struct {
constants_map: std.AutoHashMapUnmanaged(InternPool.Index, u8) = .empty,
labels: std.ArrayListUnmanaged(Label) = .empty,
fixups: std.ArrayListUnmanaged(Fixup) = .empty,
knot_tag: enum {
knot,
function,
},
const Label = struct {
code_offset: usize,
@ -495,35 +500,50 @@ fn irBinaryOp(
return .stack;
}
fn analyzeInlineBody(
sema: *Sema,
builder: *Builder,
body: []const Ir.Inst.Index,
) !ValueInfo {
if (sema.analyzeBodyInner(builder, body, true)) |_| {} else |err| switch (err) {
error.ComptimeBreak => {},
else => |e| return e,
}
const break_inst = sema.ir.instructions[@intFromEnum(sema.comptime_break_inst)];
const extra = sema.ir.extraData(Ir.Inst.Break, break_inst.data.payload.extra_index).data;
return sema.resolveInst(extra.operand);
}
fn irLogicalOp(
sema: *Sema,
builder: *Builder,
inst: Ir.Inst.Index,
logical_or: bool,
is_logical_or: bool,
) InnerError!ValueInfo {
const ip = &sema.module.intern_pool;
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
const lhs = sema.resolveInst(data.lhs);
const rhs = sema.resolveInst(data.rhs);
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
const extra = sema.ir.extraData(Ir.Inst.BoolBr, data.extra_index);
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
const lhs = sema.resolveInst(extra.data.lhs);
if (sema.resolveValue(lhs)) |lhs_info| {
const lhs_value = lhs_info.unwrap(ip);
if (sema.resolveValue(rhs)) |_| {
return if (logical_or)
if (lhs_value.isTruthy()) lhs else rhs
else if (!lhs_value.isTruthy()) lhs else rhs;
if (is_logical_or and lhs_value.bool) {
const value = ip.getOrPutBool(true);
return .{ .value = value };
} else if (!is_logical_or and !lhs_value.bool) {
const value = ip.getOrPutBool(false);
return .{ .value = value };
}
if (logical_or and lhs_value.isTruthy()) return lhs;
if (!logical_or and !lhs_value.isTruthy()) return lhs;
try builder.ensureLoad(rhs);
return .none;
return try sema.analyzeInlineBody(builder, body);
}
const else_label = try builder.addLabel();
try builder.ensureLoad(lhs);
try builder.addFixup(if (logical_or) .jmp_t else .jmp_f, else_label);
try builder.addFixup(if (is_logical_or) .jmp_t else .jmp_f, else_label);
try builder.addByteOp(.pop);
const rhs = try sema.analyzeInlineBody(builder, body);
try builder.ensureLoad(rhs);
builder.setLabel(else_label);
return .none;
@ -768,15 +788,25 @@ fn irChoiceBr(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!vo
fn irRet(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void {
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
if (data.lhs.toIndexAllowNone()) |index| {
const value = sema.inst_map.get(index).?;
if (value != .none) try builder.ensureLoad(value);
const lhs = sema.resolveInst(data.lhs);
if (lhs != .none) {
try builder.ensureLoad(lhs);
} else {
try builder.addByteOp(.stream_glue);
}
try builder.addByteOp(.ret);
}
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
try builder.addByteOp(.exit);
switch (builder.knot_tag) {
.knot => {
try builder.addByteOp(.exit);
},
.function => {
try builder.addByteOp(.stream_glue);
try builder.addByteOp(.ret);
},
}
}
fn irCall(
@ -823,7 +853,7 @@ fn irCall(
const arg_end = sema.ir.extra[extra.end + i];
defer arg_start = arg_end;
const arg_body = body[arg_start..arg_end];
const arg_value = try analyzeBodyInner(sema, builder, @ptrCast(arg_body), false);
const arg_value = try sema.analyzeInlineBody(builder, @ptrCast(arg_body));
if (arg_value != .none) try builder.ensureLoad(arg_value);
}
try builder.addConstOp(.call, @intCast(args_len));
@ -986,8 +1016,8 @@ fn analyzeBodyInner(
.mod => try irBinaryOp(sema, builder, inst, .mod),
.neg => try irUnaryOp(sema, builder, inst, .neg),
.not => try irUnaryOp(sema, builder, inst, .not),
.bool_and => try irLogicalOp(sema, builder, inst, false),
.bool_or => try irLogicalOp(sema, builder, inst, true),
.bool_br_and => try irLogicalOp(sema, builder, inst, false),
.bool_br_or => try irLogicalOp(sema, builder, inst, true),
.cmp_eq => try irBinaryOp(sema, builder, inst, .cmp_eq),
.cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq),
.cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt),
@ -1008,7 +1038,10 @@ fn analyzeBodyInner(
try irBreak(sema, inst);
continue;
},
.break_inline => try irBreakInline(sema, inst),
.break_inline => {
sema.comptime_break_inst = inst;
return error.ComptimeBreak;
},
.block => {
try irBlock(sema, builder, inst);
continue;
@ -1233,7 +1266,7 @@ fn resolveGlobalDecl(
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
entry.resolution = .in_progress;
const val = try sema.analyzeBodyInner(builder, body, true);
const val = try sema.analyzeInlineBody(builder, body);
entry.resolution = .{ .resolved = val };
return val;
},
@ -1260,6 +1293,7 @@ pub fn scanTopLevelDecls(
.sema = sema,
.code = undefined,
.namespace = namespace,
.knot_tag = .knot,
};
defer builder.deinit(gpa);