fix: call frame handling, logical short circuiting
This commit is contained in:
parent
5c133e5fa2
commit
236acc7d60
8 changed files with 301 additions and 159 deletions
115
src/AstGen.zig
115
src/AstGen.zig
|
|
@ -423,19 +423,20 @@ const GenIr = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn addBreak(
|
fn addBreak(
|
||||||
gen: *GenIr,
|
gi: *GenIr,
|
||||||
tag: Ir.Inst.Tag,
|
tag: Ir.Inst.Tag,
|
||||||
node: *const Ast.Node,
|
node: *const Ast.Node,
|
||||||
block_inst: Ir.Inst.Index,
|
block_inst: Ir.Inst.Index,
|
||||||
|
operand: Ir.Inst.Ref,
|
||||||
) !Ir.Inst.Ref {
|
) !Ir.Inst.Ref {
|
||||||
const gpa = gen.astgen.gpa;
|
|
||||||
const extra_len = @typeInfo(Ir.Inst.Break).@"struct".fields.len;
|
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(
|
const extra_index = gi.astgen.addExtraAssumeCapacity(Ir.Inst.Break{
|
||||||
Ir.Inst.Break{ .block_inst = block_inst },
|
.operand = operand,
|
||||||
);
|
.block_inst = block_inst,
|
||||||
return gen.addPayloadNodeWithIndex(tag, node, extra_index);
|
});
|
||||||
|
return gi.addPayloadNodeWithIndex(tag, node, extra_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn makePayloadNode(self: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
|
fn makePayloadNode(self: *GenIr, tag: Ir.Inst.Tag) !Ir.Inst.Index {
|
||||||
|
|
@ -450,6 +451,23 @@ const GenIr = struct {
|
||||||
return inst_index;
|
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 {
|
fn setBlockBody(self: *GenIr, inst: Ir.Inst.Index) !void {
|
||||||
const gpa = self.astgen.gpa;
|
const gpa = self.astgen.gpa;
|
||||||
const body = self.instructionsSlice();
|
const body = self.instructionsSlice();
|
||||||
|
|
@ -644,6 +662,31 @@ fn binaryOp(
|
||||||
return gi.addBinaryNode(op, lhs, rhs);
|
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) {
|
fn parseNumberLiteral(bytes: []const u8) union(enum) {
|
||||||
int: i64,
|
int: i64,
|
||||||
float: f64,
|
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),
|
.mod_expr => return binaryOp(gi, scope, node, .mod),
|
||||||
.negate_expr => return unaryOp(gi, scope, node, .neg),
|
.negate_expr => return unaryOp(gi, scope, node, .neg),
|
||||||
.logical_not_expr => return unaryOp(gi, scope, node, .not),
|
.logical_not_expr => return unaryOp(gi, scope, node, .not),
|
||||||
.logical_and_expr => return binaryOp(gi, scope, node, .bool_and),
|
.logical_and_expr => return boolBinaryOp(gi, scope, node, .bool_br_and),
|
||||||
.logical_or_expr => return binaryOp(gi, scope, node, .bool_or),
|
.logical_or_expr => return boolBinaryOp(gi, scope, node, .bool_br_or),
|
||||||
.logical_equality_expr => return binaryOp(gi, scope, node, .cmp_eq),
|
.logical_equality_expr => return binaryOp(gi, scope, node, .cmp_eq),
|
||||||
.logical_inequality_expr => return binaryOp(gi, scope, node, .cmp_neq),
|
.logical_inequality_expr => return binaryOp(gi, scope, node, .cmp_neq),
|
||||||
.logical_greater_expr => return binaryOp(gi, scope, node, .cmp_gt),
|
.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()) {
|
if (!then_block.endsWithNoReturn()) {
|
||||||
_ = try then_block.addBreak(.@"break", node, block);
|
_ = try then_block.addBreak(.@"break", node, block, .void);
|
||||||
}
|
}
|
||||||
|
|
||||||
var else_block = gi.makeSubBlock();
|
var else_block = gi.makeSubBlock();
|
||||||
defer else_block.unstack();
|
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);
|
try setCondBrPayload(condbr, cond_inst, &then_block, &else_block);
|
||||||
return condbr.toRef();
|
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);
|
try blockStmt(&then_block, scope, then_body);
|
||||||
if (!then_block.endsWithNoReturn()) {
|
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();
|
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);
|
try blockStmt(&else_block, scope, else_body);
|
||||||
|
|
||||||
if (!else_block.endsWithNoReturn()) {
|
if (!else_block.endsWithNoReturn()) {
|
||||||
_ = try else_block.addBreak(.@"break", else_body, block);
|
_ = try else_block.addBreak(.@"break", else_body, block, .void);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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);
|
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);
|
try blockStmt(&then_block, scope, body);
|
||||||
if (!then_block.endsWithNoReturn()) {
|
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();
|
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.?);
|
_ = try blockStmt(&case_block, scope, case_data.rhs.?);
|
||||||
if (!case_block.endsWithNoReturn()) {
|
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();
|
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;
|
const else_data = else_branch.data.bin;
|
||||||
_ = try blockStmt(&else_block, scope, else_data.rhs.?);
|
_ = try blockStmt(&else_block, scope, else_data.rhs.?);
|
||||||
if (!else_block.endsWithNoReturn()) {
|
if (!else_block.endsWithNoReturn()) {
|
||||||
_ = try else_block.addBreak(.@"break", else_branch, switch_br);
|
_ = try else_block.addBreak(.@"break", else_branch, switch_br, .void);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_ = try else_block.addBreak(.@"break", node, switch_br);
|
_ = try else_block.addBreak(.@"break", node, switch_br, .void);
|
||||||
}
|
}
|
||||||
|
|
||||||
const else_body = else_block.instructionsSlice();
|
const else_body = else_block.instructionsSlice();
|
||||||
|
|
@ -1288,14 +1331,18 @@ fn callExpr(
|
||||||
const callee_node = data.lhs.?;
|
const callee_node = data.lhs.?;
|
||||||
const callee = try calleeExpr(gi, scope, callee_node);
|
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.
|
// FIXME: List nodes should not have optional slices.
|
||||||
// This hack is an abomination.
|
// This hack is an abomination.
|
||||||
const arguments: ?[]*Ast.Node = if (data.rhs) |args_node| args_node.data.list.items else null;
|
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 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);
|
try astgen.scratch.resize(gpa, scratch_top + args_count);
|
||||||
var scratch_index = scratch_top;
|
var scratch_index = scratch_top;
|
||||||
|
|
||||||
|
|
@ -1304,7 +1351,10 @@ fn callExpr(
|
||||||
var arg_block = gi.makeSubBlock();
|
var arg_block = gi.makeSubBlock();
|
||||||
defer arg_block.unstack();
|
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();
|
const body = arg_block.instructionsSlice();
|
||||||
try astgen.scratch.ensureUnusedCapacity(gpa, body.len);
|
try astgen.scratch.ensureUnusedCapacity(gpa, body.len);
|
||||||
|
|
@ -1324,7 +1374,13 @@ fn callExpr(
|
||||||
if (args_count != 0) {
|
if (args_count != 0) {
|
||||||
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
|
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| {
|
.field => |callee_field| {
|
||||||
const tag = if (call == .divert) .field_divert else .field_call;
|
const tag = if (call == .divert) .field_divert else .field_call;
|
||||||
|
|
@ -1336,9 +1392,16 @@ fn callExpr(
|
||||||
if (args_count != 0) {
|
if (args_count != 0) {
|
||||||
try astgen.extra.appendSlice(gpa, astgen.scratch.items[scratch_top..]);
|
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 {
|
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 ret_arg = if (node.data.bin.lhs) |lhs| blk: {
|
||||||
const arg_inst = try expr(gi, scope, lhs);
|
const arg_inst = try expr(gi, scope, lhs);
|
||||||
break :blk arg_inst;
|
break :blk arg_inst;
|
||||||
} else .none;
|
} else .void;
|
||||||
_ = try gi.addUnaryNode(.ret, ret_arg);
|
_ = 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 name_str = try astgen.strFromNode(identifier_node);
|
||||||
const var_inst = try decl_block.makePayloadNode(.decl_var);
|
const var_inst = try decl_block.makePayloadNode(.decl_var);
|
||||||
const rvalue_inst = try expr(&decl_block, scope, expr_node);
|
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 setDeclVarPayload(var_inst, &decl_block, identifier_node);
|
||||||
try setDeclaration(decl_inst, .{
|
try setDeclaration(decl_inst, .{
|
||||||
|
|
|
||||||
23
src/Ir.zig
23
src/Ir.zig
|
|
@ -126,6 +126,7 @@ pub const Inst = struct {
|
||||||
pub const Ref = enum(u32) {
|
pub const Ref = enum(u32) {
|
||||||
bool_true,
|
bool_true,
|
||||||
bool_false,
|
bool_false,
|
||||||
|
void,
|
||||||
none = std.math.maxInt(u32),
|
none = std.math.maxInt(u32),
|
||||||
_,
|
_,
|
||||||
|
|
||||||
|
|
@ -177,10 +178,6 @@ pub const Inst = struct {
|
||||||
/// Uses the `un` union field.
|
/// Uses the `un` union field.
|
||||||
not,
|
not,
|
||||||
/// Uses the `bin` union field.
|
/// Uses the `bin` union field.
|
||||||
bool_and,
|
|
||||||
/// Uses the `bin` union field.
|
|
||||||
bool_or,
|
|
||||||
/// Uses the `bin` union field.
|
|
||||||
cmp_eq,
|
cmp_eq,
|
||||||
/// Uses the `bin` union field.
|
/// Uses the `bin` union field.
|
||||||
cmp_neq,
|
cmp_neq,
|
||||||
|
|
@ -198,6 +195,14 @@ pub const Inst = struct {
|
||||||
float,
|
float,
|
||||||
/// Uses the `str` union field.
|
/// Uses the `str` union field.
|
||||||
str,
|
str,
|
||||||
|
/// Short-circuiting boolean `and`. `lhs` is a boolean `Ref` and the other operand
|
||||||
|
/// is a block, which is evaluated if `lhs` is `true`.
|
||||||
|
/// Uses the `payload` union field. Payload is `BoolBr`.
|
||||||
|
bool_br_and,
|
||||||
|
/// Short-circuiting boolean `or`. `lhs` is a boolean `Ref` and the other operand
|
||||||
|
/// is a block, which is evaluated if `lhs` is `false`.
|
||||||
|
/// Uses the `payload` union field. Payload is `BoolBr`.
|
||||||
|
bool_br_or,
|
||||||
block,
|
block,
|
||||||
condbr,
|
condbr,
|
||||||
@"break",
|
@"break",
|
||||||
|
|
@ -290,7 +295,13 @@ pub const Inst = struct {
|
||||||
body_len: u32,
|
body_len: u32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const BoolBr = struct {
|
||||||
|
lhs: Ref,
|
||||||
|
body_len: u32,
|
||||||
|
};
|
||||||
|
|
||||||
pub const Break = struct {
|
pub const Break = struct {
|
||||||
|
operand: Ref,
|
||||||
block_inst: Index,
|
block_inst: Index,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -361,8 +372,8 @@ pub const Inst = struct {
|
||||||
.mod,
|
.mod,
|
||||||
.neg,
|
.neg,
|
||||||
.not,
|
.not,
|
||||||
.bool_and,
|
.bool_br_and,
|
||||||
.bool_or,
|
.bool_br_or,
|
||||||
.cmp_eq,
|
.cmp_eq,
|
||||||
.cmp_neq,
|
.cmp_neq,
|
||||||
.cmp_gt,
|
.cmp_gt,
|
||||||
|
|
|
||||||
78
src/Sema.zig
78
src/Sema.zig
|
|
@ -14,6 +14,7 @@ module: *compile.Module,
|
||||||
ir: Ir,
|
ir: Ir,
|
||||||
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, ValueInfo) = .empty,
|
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, ValueInfo) = .empty,
|
||||||
errors: *std.ArrayListUnmanaged(Module.Error),
|
errors: *std.ArrayListUnmanaged(Module.Error),
|
||||||
|
comptime_break_inst: Ir.Inst.Index = undefined,
|
||||||
|
|
||||||
const InnerError = error{
|
const InnerError = error{
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
|
|
@ -160,6 +161,10 @@ pub const Builder = struct {
|
||||||
constants_map: std.AutoHashMapUnmanaged(InternPool.Index, u8) = .empty,
|
constants_map: std.AutoHashMapUnmanaged(InternPool.Index, u8) = .empty,
|
||||||
labels: std.ArrayListUnmanaged(Label) = .empty,
|
labels: std.ArrayListUnmanaged(Label) = .empty,
|
||||||
fixups: std.ArrayListUnmanaged(Fixup) = .empty,
|
fixups: std.ArrayListUnmanaged(Fixup) = .empty,
|
||||||
|
knot_tag: enum {
|
||||||
|
knot,
|
||||||
|
function,
|
||||||
|
},
|
||||||
|
|
||||||
const Label = struct {
|
const Label = struct {
|
||||||
code_offset: usize,
|
code_offset: usize,
|
||||||
|
|
@ -495,35 +500,50 @@ fn irBinaryOp(
|
||||||
return .stack;
|
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(
|
fn irLogicalOp(
|
||||||
sema: *Sema,
|
sema: *Sema,
|
||||||
builder: *Builder,
|
builder: *Builder,
|
||||||
inst: Ir.Inst.Index,
|
inst: Ir.Inst.Index,
|
||||||
logical_or: bool,
|
is_logical_or: bool,
|
||||||
) InnerError!ValueInfo {
|
) InnerError!ValueInfo {
|
||||||
const ip = &sema.module.intern_pool;
|
const ip = &sema.module.intern_pool;
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.bin;
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||||
const lhs = sema.resolveInst(data.lhs);
|
const extra = sema.ir.extraData(Ir.Inst.BoolBr, data.extra_index);
|
||||||
const rhs = sema.resolveInst(data.rhs);
|
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
const lhs = sema.resolveInst(extra.data.lhs);
|
||||||
|
|
||||||
if (sema.resolveValue(lhs)) |lhs_info| {
|
if (sema.resolveValue(lhs)) |lhs_info| {
|
||||||
const lhs_value = lhs_info.unwrap(ip);
|
const lhs_value = lhs_info.unwrap(ip);
|
||||||
if (sema.resolveValue(rhs)) |_| {
|
if (is_logical_or and lhs_value.bool) {
|
||||||
return if (logical_or)
|
const value = ip.getOrPutBool(true);
|
||||||
if (lhs_value.isTruthy()) lhs else rhs
|
return .{ .value = value };
|
||||||
else if (!lhs_value.isTruthy()) lhs else rhs;
|
} else if (!is_logical_or and !lhs_value.bool) {
|
||||||
|
const value = ip.getOrPutBool(false);
|
||||||
|
return .{ .value = value };
|
||||||
}
|
}
|
||||||
|
return try sema.analyzeInlineBody(builder, body);
|
||||||
if (logical_or and lhs_value.isTruthy()) return lhs;
|
|
||||||
if (!logical_or and !lhs_value.isTruthy()) return lhs;
|
|
||||||
try builder.ensureLoad(rhs);
|
|
||||||
return .none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const else_label = try builder.addLabel();
|
const else_label = try builder.addLabel();
|
||||||
try builder.ensureLoad(lhs);
|
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);
|
try builder.addByteOp(.pop);
|
||||||
|
|
||||||
|
const rhs = try sema.analyzeInlineBody(builder, body);
|
||||||
try builder.ensureLoad(rhs);
|
try builder.ensureLoad(rhs);
|
||||||
builder.setLabel(else_label);
|
builder.setLabel(else_label);
|
||||||
return .none;
|
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 {
|
fn irRet(sema: *Sema, builder: *Builder, inst: Ir.Inst.Index) InnerError!void {
|
||||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
||||||
if (data.lhs.toIndexAllowNone()) |index| {
|
const lhs = sema.resolveInst(data.lhs);
|
||||||
const value = sema.inst_map.get(index).?;
|
if (lhs != .none) {
|
||||||
if (value != .none) try builder.ensureLoad(value);
|
try builder.ensureLoad(lhs);
|
||||||
|
} else {
|
||||||
|
try builder.addByteOp(.stream_glue);
|
||||||
}
|
}
|
||||||
try builder.addByteOp(.ret);
|
try builder.addByteOp(.ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
fn irImplicitRet(_: *Sema, builder: *Builder, _: Ir.Inst.Index) InnerError!void {
|
||||||
|
switch (builder.knot_tag) {
|
||||||
|
.knot => {
|
||||||
try builder.addByteOp(.exit);
|
try builder.addByteOp(.exit);
|
||||||
|
},
|
||||||
|
.function => {
|
||||||
|
try builder.addByteOp(.stream_glue);
|
||||||
|
try builder.addByteOp(.ret);
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irCall(
|
fn irCall(
|
||||||
|
|
@ -823,7 +853,7 @@ fn irCall(
|
||||||
const arg_end = sema.ir.extra[extra.end + i];
|
const arg_end = sema.ir.extra[extra.end + i];
|
||||||
defer arg_start = arg_end;
|
defer arg_start = arg_end;
|
||||||
const arg_body = body[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);
|
if (arg_value != .none) try builder.ensureLoad(arg_value);
|
||||||
}
|
}
|
||||||
try builder.addConstOp(.call, @intCast(args_len));
|
try builder.addConstOp(.call, @intCast(args_len));
|
||||||
|
|
@ -986,8 +1016,8 @@ fn analyzeBodyInner(
|
||||||
.mod => try irBinaryOp(sema, builder, inst, .mod),
|
.mod => try irBinaryOp(sema, builder, inst, .mod),
|
||||||
.neg => try irUnaryOp(sema, builder, inst, .neg),
|
.neg => try irUnaryOp(sema, builder, inst, .neg),
|
||||||
.not => try irUnaryOp(sema, builder, inst, .not),
|
.not => try irUnaryOp(sema, builder, inst, .not),
|
||||||
.bool_and => try irLogicalOp(sema, builder, inst, false),
|
.bool_br_and => try irLogicalOp(sema, builder, inst, false),
|
||||||
.bool_or => try irLogicalOp(sema, builder, inst, true),
|
.bool_br_or => try irLogicalOp(sema, builder, inst, true),
|
||||||
.cmp_eq => try irBinaryOp(sema, builder, inst, .cmp_eq),
|
.cmp_eq => try irBinaryOp(sema, builder, inst, .cmp_eq),
|
||||||
.cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq),
|
.cmp_neq => try irBinaryOp(sema, builder, inst, .cmp_neq),
|
||||||
.cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt),
|
.cmp_lt => try irBinaryOp(sema, builder, inst, .cmp_lt),
|
||||||
|
|
@ -1008,7 +1038,10 @@ fn analyzeBodyInner(
|
||||||
try irBreak(sema, inst);
|
try irBreak(sema, inst);
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
.break_inline => try irBreakInline(sema, inst),
|
.break_inline => {
|
||||||
|
sema.comptime_break_inst = inst;
|
||||||
|
return error.ComptimeBreak;
|
||||||
|
},
|
||||||
.block => {
|
.block => {
|
||||||
try irBlock(sema, builder, inst);
|
try irBlock(sema, builder, inst);
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1233,7 +1266,7 @@ fn resolveGlobalDecl(
|
||||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
|
||||||
entry.resolution = .in_progress;
|
entry.resolution = .in_progress;
|
||||||
const val = try sema.analyzeBodyInner(builder, body, true);
|
const val = try sema.analyzeInlineBody(builder, body);
|
||||||
entry.resolution = .{ .resolved = val };
|
entry.resolution = .{ .resolved = val };
|
||||||
return val;
|
return val;
|
||||||
},
|
},
|
||||||
|
|
@ -1260,6 +1293,7 @@ pub fn scanTopLevelDecls(
|
||||||
.sema = sema,
|
.sema = sema,
|
||||||
.code = undefined,
|
.code = undefined,
|
||||||
.namespace = namespace,
|
.namespace = namespace,
|
||||||
|
.knot_tag = .knot,
|
||||||
};
|
};
|
||||||
defer builder.deinit(gpa);
|
defer builder.deinit(gpa);
|
||||||
|
|
||||||
|
|
|
||||||
165
src/Story.zig
165
src/Story.zig
|
|
@ -100,10 +100,13 @@ pub const Opcode = enum(u8) {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CallFrame = struct {
|
pub const CallFrame = struct {
|
||||||
|
/// Pointer to the knot that initiated the call.
|
||||||
callee: *Object.Knot,
|
callee: *Object.Knot,
|
||||||
caller_top: usize,
|
/// Instruction pointer.
|
||||||
ip: usize,
|
ip: usize,
|
||||||
sp: usize,
|
bp: usize,
|
||||||
|
/// Output stream base.
|
||||||
|
output_base: usize,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Value = union(enum) {
|
pub const Value = union(enum) {
|
||||||
|
|
@ -377,11 +380,11 @@ fn getConstant(story: *Story, frame: *CallFrame, offset: u8) !Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getLocal(vm: *Story, frame: *CallFrame, offset: u8) ?Value {
|
fn getLocal(vm: *Story, frame: *CallFrame, offset: u8) ?Value {
|
||||||
return vm.stack[frame.sp + offset];
|
return vm.stack[frame.bp + offset + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setLocal(vm: *Story, frame: *CallFrame, offset: u8, value: Value) void {
|
fn setLocal(vm: *Story, frame: *CallFrame, offset: u8, value: Value) void {
|
||||||
vm.stack[frame.sp + offset] = value;
|
vm.stack[frame.bp + offset + 1] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This should probably check the constants table first.
|
// TODO: This should probably check the constants table first.
|
||||||
|
|
@ -426,68 +429,58 @@ pub fn getKnot(vm: *Story, name: []const u8) ?*Object.Knot {
|
||||||
return knot;
|
return knot;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn call(vm: *Story, knot: *Object.Knot) !void {
|
fn callValue(vm: *Story, value: Value, args_count: u8) !void {
|
||||||
if (vm.call_stack_top >= vm.call_stack.len)
|
switch (value) {
|
||||||
return error.CallStackOverflow;
|
.object => |object| switch (object.tag) {
|
||||||
|
.knot => return call(vm, @ptrCast(object), args_count),
|
||||||
const locals_count = knot.code.locals_count;
|
else => return error.InvalidCallTarget,
|
||||||
const args_count = knot.code.args_count;
|
},
|
||||||
const sp = vm.stack_top - args_count;
|
else => return error.InvalidCallTarget,
|
||||||
const caller_top = if (vm.call_stack_top == 0)
|
}
|
||||||
sp
|
|
||||||
else
|
|
||||||
sp - 1;
|
|
||||||
|
|
||||||
const frame_top = sp + args_count + locals_count;
|
|
||||||
if (frame_top > vm.stack.len) return error.StackOverflow;
|
|
||||||
for (vm.stack[sp + args_count .. frame_top]) |*slot| slot.* = .nil;
|
|
||||||
|
|
||||||
vm.stack_top = frame_top;
|
|
||||||
vm.call_stack[vm.call_stack_top] = .{
|
|
||||||
.callee = knot,
|
|
||||||
.ip = 0,
|
|
||||||
.sp = sp,
|
|
||||||
.caller_top = caller_top,
|
|
||||||
};
|
|
||||||
vm.call_stack_top += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diverts are essentially tail calls.
|
fn call(vm: *Story, knot: *Object.Knot, args_count: u8) !void {
|
||||||
fn divert(vm: *Story, knot: *Object.Knot) !void {
|
assert(knot.code.args_count == args_count);
|
||||||
const args_count = knot.code.args_count;
|
if (vm.call_stack_top >= vm.call_stack.len)
|
||||||
const locals_count = knot.code.locals_count;
|
return error.CallStackOverflow;
|
||||||
|
|
||||||
if (!vm.can_advance)
|
if (!vm.can_advance)
|
||||||
vm.can_advance = true;
|
vm.can_advance = true;
|
||||||
|
|
||||||
if (vm.call_stack_top == 0)
|
vm.call_stack[vm.call_stack_top] = .{
|
||||||
return vm.call(knot);
|
|
||||||
|
|
||||||
const args_start = vm.stack_top - args_count;
|
|
||||||
const current_frame = &vm.call_stack[vm.call_stack_top - 1];
|
|
||||||
const sp = current_frame.sp;
|
|
||||||
const caller_top = current_frame.caller_top;
|
|
||||||
|
|
||||||
if (args_count > 0) {
|
|
||||||
std.mem.copyForwards(
|
|
||||||
Value,
|
|
||||||
vm.stack[sp .. sp + args_count],
|
|
||||||
vm.stack[args_start .. args_start + args_count],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const frame_top = sp + args_count + locals_count;
|
|
||||||
if (frame_top > vm.stack.len) return error.StackOverflow;
|
|
||||||
|
|
||||||
for (vm.stack[sp + args_count .. frame_top]) |*slot| slot.* = .nil;
|
|
||||||
vm.stack_top = frame_top;
|
|
||||||
|
|
||||||
current_frame.* = .{
|
|
||||||
.callee = knot,
|
.callee = knot,
|
||||||
.ip = 0,
|
.ip = 0,
|
||||||
.sp = sp,
|
.bp = vm.stack_top - args_count - 1,
|
||||||
.caller_top = caller_top,
|
.output_base = vm.output_buffer.items.len,
|
||||||
};
|
};
|
||||||
|
vm.call_stack_top += 1;
|
||||||
|
vm.stack_top += knot.code.locals_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn divertValue(vm: *Story, value: Value, args_count: u8) !void {
|
||||||
|
switch (value) {
|
||||||
|
.object => |object| switch (object.tag) {
|
||||||
|
.knot => return divert(vm, @ptrCast(object), args_count),
|
||||||
|
else => return error.InvalidCallTarget,
|
||||||
|
},
|
||||||
|
else => return error.InvalidCallTarget,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diverts are essentially tail calls.
|
||||||
|
fn divert(vm: *Story, knot: *Object.Knot, args_count: u8) !void {
|
||||||
|
assert(knot.code.args_count == args_count);
|
||||||
|
if (vm.call_stack_top == 0)
|
||||||
|
return vm.call(knot, args_count);
|
||||||
|
|
||||||
|
const frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
|
frame.* = .{
|
||||||
|
.callee = knot,
|
||||||
|
.ip = 0,
|
||||||
|
.bp = vm.stack_top - args_count - 1,
|
||||||
|
.output_base = vm.output_buffer.items.len,
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.stack_top += knot.code.locals_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn readAddress(code: []const Story.Opcode, offset: usize) u16 {
|
fn readAddress(code: []const Story.Opcode, offset: usize) u16 {
|
||||||
|
|
@ -518,16 +511,32 @@ fn step(vm: *Story) !StepSignal {
|
||||||
.exit => return .exit,
|
.exit => return .exit,
|
||||||
.done => return .done,
|
.done => return .done,
|
||||||
.ret => {
|
.ret => {
|
||||||
const return_value = vm.stack[vm.stack_top - 1];
|
if (vm.call_stack_top == 0) return error.UnexpectedReturn;
|
||||||
|
|
||||||
vm.call_stack_top -= 1;
|
vm.call_stack_top -= 1;
|
||||||
const completed_frame = vm.call_stack[vm.call_stack_top];
|
|
||||||
|
|
||||||
vm.stack_top = completed_frame.caller_top;
|
const resolved_stream: ?Value = if (vm.output_buffer.items.len > frame.output_base) blk: {
|
||||||
|
const frame_output = vm.output_buffer.items[frame.output_base..];
|
||||||
|
defer vm.output_buffer.shrinkRetainingCapacity(frame.output_base);
|
||||||
|
|
||||||
|
const str_bytes = try resolveOutputStream(vm, arena, frame_output);
|
||||||
|
const str_object = try Object.String.create(vm, .{ .bytes = str_bytes });
|
||||||
|
break :blk .{ .object = &str_object.base };
|
||||||
|
} else blk: {
|
||||||
|
break :blk null;
|
||||||
|
};
|
||||||
|
const return_value = if (resolved_stream) |stream| blk: {
|
||||||
|
if (frame.bp + frame.callee.code.stack_size + 1 < vm.stack_top) {
|
||||||
|
try vm.output_buffer.append(gpa, .{ .value = stream });
|
||||||
|
break :blk popStack(vm).?;
|
||||||
|
}
|
||||||
|
break :blk stream;
|
||||||
|
} else if (frame.bp + frame.callee.code.stack_size + 1 < vm.stack_top) blk: {
|
||||||
|
break :blk popStack(vm).?;
|
||||||
|
} else .nil;
|
||||||
|
|
||||||
|
vm.stack_top = frame.bp;
|
||||||
vm.stack[vm.stack_top] = return_value;
|
vm.stack[vm.stack_top] = return_value;
|
||||||
vm.stack_top += 1;
|
vm.stack_top += 1;
|
||||||
|
|
||||||
if (vm.call_stack_top == 0) return error.UnexpectedReturn;
|
|
||||||
frame = &vm.call_stack[vm.call_stack_top - 1];
|
frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
},
|
},
|
||||||
.pop => {
|
.pop => {
|
||||||
|
|
@ -629,17 +638,11 @@ fn step(vm: *Story) !StepSignal {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.call => {
|
.call => {
|
||||||
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
const args_count: u8 = @intFromEnum(code[frame.ip + 1]);
|
||||||
frame.ip += 2;
|
frame.ip += 2;
|
||||||
|
|
||||||
if (peekStack(vm, arg_offset)) |value| {
|
if (peekStack(vm, args_count)) |value| {
|
||||||
switch (value) {
|
try callValue(vm, value, args_count);
|
||||||
.object => |object| switch (object.tag) {
|
|
||||||
.knot => try call(vm, @ptrCast(object)),
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
@ -647,17 +650,11 @@ fn step(vm: *Story) !StepSignal {
|
||||||
frame = &vm.call_stack[vm.call_stack_top - 1];
|
frame = &vm.call_stack[vm.call_stack_top - 1];
|
||||||
},
|
},
|
||||||
.divert => {
|
.divert => {
|
||||||
const arg_offset: u8 = @intFromEnum(code[frame.ip + 1]);
|
const args_count: u8 = @intFromEnum(code[frame.ip + 1]);
|
||||||
frame.ip += 2;
|
frame.ip += 2;
|
||||||
|
|
||||||
if (peekStack(vm, arg_offset)) |value| {
|
if (peekStack(vm, args_count)) |value| {
|
||||||
switch (value) {
|
try divertValue(vm, value, args_count);
|
||||||
.object => |object| switch (object.tag) {
|
|
||||||
.knot => try divert(vm, @ptrCast(object)),
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return error.InvalidArgument;
|
return error.InvalidArgument;
|
||||||
}
|
}
|
||||||
|
|
@ -831,6 +828,7 @@ pub fn advance(story: *Story) !?[]const u8 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
story.can_advance = false;
|
story.can_advance = false;
|
||||||
|
if (output_buffer.items.len == 0) return null;
|
||||||
return try resolveOutputStream(story, arena, output_buffer.items[0..]);
|
return try resolveOutputStream(story, arena, output_buffer.items[0..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -906,7 +904,8 @@ pub fn fromSourceBytes(
|
||||||
|
|
||||||
try comp.setupStoryRuntime(gpa, &story);
|
try comp.setupStoryRuntime(gpa, &story);
|
||||||
if (story.getKnot(Story.default_knot_name)) |knot| {
|
if (story.getKnot(Story.default_knot_name)) |knot| {
|
||||||
try story.divert(knot);
|
try story.pushStack(.{ .object = &knot.base });
|
||||||
|
try story.divert(knot, 0);
|
||||||
}
|
}
|
||||||
return story;
|
return story;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -350,15 +350,26 @@ pub fn dump(story: *Story, writer: *std.Io.Writer) !void {
|
||||||
|
|
||||||
pub fn trace(vm: *Story, writer: *std.Io.Writer, frame: *Story.CallFrame) !void {
|
pub fn trace(vm: *Story, writer: *std.Io.Writer, frame: *Story.CallFrame) !void {
|
||||||
var dumper: Dumper = .{ .story = vm };
|
var dumper: Dumper = .{ .story = vm };
|
||||||
try writer.print("\tStack => stack_pointer={d}, objects=[", .{frame.sp});
|
try writer.print("\tStack => stack_base={d}, values=[", .{frame.bp});
|
||||||
|
|
||||||
const window = vm.stack[frame.sp..vm.stack_top];
|
const stack_window = vm.stack[frame.bp..vm.stack_top];
|
||||||
for (window, 0..) |*slot, i| {
|
for (stack_window, 0..) |*slot, i| {
|
||||||
if (i > 0) try writer.writeAll(", ");
|
if (i > 0) try writer.writeAll(", ");
|
||||||
try dumper.dumpValue(writer, slot);
|
try dumper.dumpValue(writer, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
try writer.writeAll("]\n");
|
try writer.writeAll("]\n");
|
||||||
|
|
||||||
|
try writer.print("\tOutput => offset_base={d}, values=[", .{frame.output_base});
|
||||||
|
|
||||||
|
const output_window = vm.output_buffer.items[frame.output_base..];
|
||||||
|
for (output_window, 0..) |slot, i| {
|
||||||
|
if (i > 0) try writer.writeAll(", ");
|
||||||
|
try writer.print("{any}", .{slot});
|
||||||
|
}
|
||||||
|
|
||||||
|
try writer.writeAll("]\n");
|
||||||
|
|
||||||
_ = try dumper.dumpInst(writer, frame.callee, frame.ip, true);
|
_ = try dumper.dumpInst(writer, frame.callee, frame.ip, true);
|
||||||
return writer.flush();
|
return writer.flush();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,10 @@ pub const String = struct {
|
||||||
},
|
},
|
||||||
.object => |object| switch (object.tag) {
|
.object => |object| switch (object.tag) {
|
||||||
.string => return @ptrCast(object),
|
.string => return @ptrCast(object),
|
||||||
else => return error.TypeError,
|
else => {
|
||||||
|
std.debug.print("Bad object type: {s}\n", .{@tagName(object.tag)});
|
||||||
|
return error.TypeError;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -292,11 +292,13 @@ pub const Module = struct {
|
||||||
.sema = &sema,
|
.sema = &sema,
|
||||||
.code = code_chunk,
|
.code = code_chunk,
|
||||||
.namespace = work_unit.namespace,
|
.namespace = work_unit.namespace,
|
||||||
|
.knot_tag = undefined,
|
||||||
};
|
};
|
||||||
defer builder.deinit(gpa);
|
defer builder.deinit(gpa);
|
||||||
|
|
||||||
switch (work_unit.tag) {
|
switch (work_unit.tag) {
|
||||||
.knot => {
|
.knot => {
|
||||||
|
builder.knot_tag = .knot;
|
||||||
try sema.analyzeKnot(&builder, work_unit.inst_index);
|
try sema.analyzeKnot(&builder, work_unit.inst_index);
|
||||||
try builder.finalize();
|
try builder.finalize();
|
||||||
|
|
||||||
|
|
@ -307,6 +309,7 @@ pub const Module = struct {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
.stitch => {
|
.stitch => {
|
||||||
|
builder.knot_tag = .knot;
|
||||||
try sema.analyzeStitch(&builder, work_unit.inst_index);
|
try sema.analyzeStitch(&builder, work_unit.inst_index);
|
||||||
try builder.finalize();
|
try builder.finalize();
|
||||||
|
|
||||||
|
|
@ -317,6 +320,7 @@ pub const Module = struct {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
.function => {
|
.function => {
|
||||||
|
builder.knot_tag = .function;
|
||||||
try sema.analyzeFunction(&builder, work_unit.inst_index);
|
try sema.analyzeFunction(&builder, work_unit.inst_index);
|
||||||
try builder.finalize();
|
try builder.finalize();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,15 @@ pub const Writer = struct {
|
||||||
try self.writeInstRef(w, data.rhs);
|
try self.writeInstRef(w, data.rhs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn writeBoolBinaryInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
|
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
||||||
|
const extra = self.code.extraData(Ir.Inst.BoolBr, data.extra_index);
|
||||||
|
const body = self.code.bodySlice(extra.end, extra.data.body_len);
|
||||||
|
try self.writeInstRef(w, extra.data.lhs);
|
||||||
|
try w.writeAll(", ");
|
||||||
|
try self.writeBodyInner(w, body);
|
||||||
|
}
|
||||||
|
|
||||||
fn writeFieldPtrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
fn writeFieldPtrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
||||||
const extra = self.code.extraData(Ir.Inst.Field, data.extra_index).data;
|
const extra = self.code.extraData(Ir.Inst.Field, data.extra_index).data;
|
||||||
|
|
@ -114,13 +123,16 @@ pub const Writer = struct {
|
||||||
try self.writeStringRef(w, extra.data.field_name_start);
|
try self.writeStringRef(w, extra.data.field_name_start);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
try w.writeAll(", ");
|
|
||||||
try w.writeAll("[\n");
|
try w.writeAll(", [");
|
||||||
|
if (args_len > 0) {
|
||||||
|
try w.writeAll("\n");
|
||||||
|
|
||||||
var arg_start: u32 = args_len;
|
var arg_start: u32 = args_len;
|
||||||
var i: u32 = 0;
|
var i: u32 = 0;
|
||||||
while (i < args_len) : (i += 1) {
|
while (i < args_len) : (i += 1) {
|
||||||
self.pushIndent();
|
self.pushIndent();
|
||||||
|
try self.writeIndent(w);
|
||||||
const arg_end = self.code.extra[extra.end + i];
|
const arg_end = self.code.extra[extra.end + i];
|
||||||
defer arg_start = arg_end;
|
defer arg_start = arg_end;
|
||||||
const arg_body = body[arg_start..arg_end];
|
const arg_body = body[arg_start..arg_end];
|
||||||
|
|
@ -130,13 +142,17 @@ pub const Writer = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
try self.writeIndent(w);
|
try self.writeIndent(w);
|
||||||
|
}
|
||||||
try w.writeAll("]");
|
try w.writeAll("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeBreakInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
fn writeBreakInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
const data = self.code.instructions[@intFromEnum(inst)].data.payload;
|
||||||
const extra = self.code.extraData(Ir.Inst.Break, data.extra_index);
|
const extra = self.code.extraData(Ir.Inst.Break, data.extra_index);
|
||||||
|
|
||||||
try self.writeInstIndex(w, extra.data.block_inst);
|
try self.writeInstIndex(w, extra.data.block_inst);
|
||||||
|
try w.writeAll(", ");
|
||||||
|
try self.writeInstRef(w, extra.data.operand);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeCondbrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
fn writeCondbrInst(self: *Writer, w: *std.Io.Writer, inst: Ir.Inst.Index) Error!void {
|
||||||
|
|
@ -318,7 +334,7 @@ pub const Writer = struct {
|
||||||
.decl_ref => try self.writeStrTokInst(w, inst),
|
.decl_ref => try self.writeStrTokInst(w, inst),
|
||||||
.condbr => try self.writeCondbrInst(w, inst),
|
.condbr => try self.writeCondbrInst(w, inst),
|
||||||
.@"break" => try self.writeBreakInst(w, inst),
|
.@"break" => try self.writeBreakInst(w, inst),
|
||||||
.break_inline => try self.writeBinaryInst(w, inst),
|
.break_inline => try self.writeBreakInst(w, inst),
|
||||||
.switch_br => try self.writeSwitchBrInst(w, inst),
|
.switch_br => try self.writeSwitchBrInst(w, inst),
|
||||||
.alloc => {},
|
.alloc => {},
|
||||||
.load => try self.writeUnaryInst(w, inst),
|
.load => try self.writeUnaryInst(w, inst),
|
||||||
|
|
@ -331,8 +347,8 @@ pub const Writer = struct {
|
||||||
.mod => try self.writeBinaryInst(w, inst),
|
.mod => try self.writeBinaryInst(w, inst),
|
||||||
.neg => try self.writeUnaryInst(w, inst),
|
.neg => try self.writeUnaryInst(w, inst),
|
||||||
.not => try self.writeUnaryInst(w, inst),
|
.not => try self.writeUnaryInst(w, inst),
|
||||||
.bool_and => try self.writeBinaryInst(w, inst),
|
.bool_br_and => try self.writeBoolBinaryInst(w, inst),
|
||||||
.bool_or => try self.writeBinaryInst(w, inst),
|
.bool_br_or => try self.writeBoolBinaryInst(w, inst),
|
||||||
.cmp_eq => try self.writeBinaryInst(w, inst),
|
.cmp_eq => try self.writeBinaryInst(w, inst),
|
||||||
.cmp_neq => try self.writeBinaryInst(w, inst),
|
.cmp_neq => try self.writeBinaryInst(w, inst),
|
||||||
.cmp_gt => try self.writeBinaryInst(w, inst),
|
.cmp_gt => try self.writeBinaryInst(w, inst),
|
||||||
|
|
@ -359,5 +375,6 @@ pub const Writer = struct {
|
||||||
}
|
}
|
||||||
try w.writeAll(")");
|
try w.writeAll(")");
|
||||||
try w.writeAll("\n");
|
try w.writeAll("\n");
|
||||||
|
try w.flush();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue