feat: infrastructure to support qualified lookups
This commit is contained in:
parent
e8ad1cd5b5
commit
440ec68481
6 changed files with 1118 additions and 575 deletions
531
src/Sema.zig
531
src/Sema.zig
|
|
@ -3,19 +3,16 @@ const Ast = @import("Ast.zig");
|
|||
const Ir = @import("Ir.zig");
|
||||
const Story = @import("Story.zig");
|
||||
const compile = @import("compile.zig");
|
||||
const Compilation = compile.Compilation;
|
||||
const InternPool = compile.InternPool;
|
||||
const Module = compile.Module;
|
||||
const assert = std.debug.assert;
|
||||
const Sema = @This();
|
||||
|
||||
gpa: std.mem.Allocator,
|
||||
arena: std.mem.Allocator,
|
||||
tree: Ast,
|
||||
module: *compile.Module,
|
||||
ir: Ir,
|
||||
constants: std.ArrayListUnmanaged(Compilation.Constant) = .empty,
|
||||
constants_map: std.AutoHashMapUnmanaged(Compilation.Constant, u32) = .empty,
|
||||
globals_map: std.AutoHashMapUnmanaged(u32, u32) = .empty,
|
||||
knots: std.ArrayListUnmanaged(Compilation.Knot) = .empty,
|
||||
errors: *std.ArrayListUnmanaged(Compilation.Error),
|
||||
errors: *std.ArrayListUnmanaged(Module.Error),
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
|
|
@ -24,14 +21,19 @@ const InnerError = error{
|
|||
InvalidJump,
|
||||
};
|
||||
|
||||
const Ref = union(enum) {
|
||||
pub const Ref = union(enum) {
|
||||
none,
|
||||
bool_true,
|
||||
bool_false,
|
||||
index: u32,
|
||||
constant: u32,
|
||||
global: u32,
|
||||
local: u32,
|
||||
constant: InternPool.Constant.Index,
|
||||
variable: InternPool.Constant.Index,
|
||||
temporary: u32,
|
||||
// FIXME: This is horrible.
|
||||
knot: struct {
|
||||
const_index: InternPool.Constant.Index,
|
||||
namespace: *Module.Namespace,
|
||||
},
|
||||
};
|
||||
|
||||
pub const SrcLoc = struct {
|
||||
|
|
@ -45,8 +47,9 @@ fn fail(
|
|||
args: anytype,
|
||||
) error{ OutOfMemory, AnalysisFail } {
|
||||
// TODO: Revisit this
|
||||
const source_bytes = sema.module.tree.source;
|
||||
const message = try std.fmt.allocPrint(sema.arena, format, args);
|
||||
const loc = compile.findLineColumn(sema.tree.source, src.src_offset);
|
||||
const loc = compile.findLineColumn(source_bytes, src.src_offset);
|
||||
try sema.errors.append(sema.gpa, .{
|
||||
.line = loc.line,
|
||||
.column = loc.column,
|
||||
|
|
@ -56,47 +59,57 @@ fn fail(
|
|||
return error.AnalysisFail;
|
||||
}
|
||||
|
||||
/// Intern a constant.
|
||||
fn getOrPutConstant(sema: *Sema, data: Compilation.Constant) error{OutOfMemory}!Ref {
|
||||
if (sema.constants_map.get(data)) |index| {
|
||||
return .{ .constant = index };
|
||||
} else {
|
||||
const gpa = sema.gpa;
|
||||
const index = sema.constants.items.len;
|
||||
try sema.constants.append(gpa, data);
|
||||
try sema.constants_map.put(gpa, data, @intCast(index));
|
||||
return .{ .constant = @intCast(index) };
|
||||
/// Retrieve an index into the global constant pool for an integer value.
|
||||
fn getOrPutInt(sema: *Sema, value: u64) !Ref {
|
||||
const const_index = try sema.module.intern_pool.getOrPutInt(sema.gpa, value);
|
||||
return .{ .constant = const_index };
|
||||
}
|
||||
|
||||
/// Retrieve an index into the global constant pool for a string value.
|
||||
fn getOrPutStr(sema: *Sema, value: Ir.NullTerminatedString) !Ref {
|
||||
const const_index = try sema.module.intern_pool.getOrPutStr(sema.gpa, value);
|
||||
return .{ .constant = const_index };
|
||||
}
|
||||
|
||||
pub fn lookupIdentifier(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
ident: InternPool.Constant.Index,
|
||||
) !Ref {
|
||||
return sema.lookupInNamespace(chunk.namespace, ident);
|
||||
}
|
||||
|
||||
pub fn lookupInNamespace(
|
||||
sema: *Sema,
|
||||
namespace: *Module.Namespace,
|
||||
ident: InternPool.Constant.Index,
|
||||
) !Ref {
|
||||
var scope: ?*Module.Namespace = namespace;
|
||||
while (scope) |s| : (scope = s.parent) {
|
||||
if (s.decls.get(ident)) |decl| switch (decl.tag) {
|
||||
.knot => return .{ .knot = .{
|
||||
.namespace = decl.namespace.?,
|
||||
.const_index = ident,
|
||||
} },
|
||||
.variable => return .{ .variable = ident },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Intern an integer as a story constant.
|
||||
fn getOrPutInt(sema: *Sema, value: u64) error{OutOfMemory}!Ref {
|
||||
return sema.getOrPutConstant(.{ .integer = value });
|
||||
}
|
||||
|
||||
/// Intern a string as a story constant.
|
||||
fn getOrPutStr(sema: *Sema, value: Ir.NullTerminatedString) error{OutOfMemory}!Ref {
|
||||
return sema.getOrPutConstant(.{ .string = value });
|
||||
// FIXME: This is temporary
|
||||
return sema.fail(.{ .src_offset = 0 }, "unknown identifier", .{});
|
||||
}
|
||||
|
||||
pub fn deinit(sema: *Sema) void {
|
||||
const gpa = sema.gpa;
|
||||
sema.constants.deinit(gpa);
|
||||
sema.constants_map.deinit(gpa);
|
||||
sema.globals_map.deinit(gpa);
|
||||
sema.knots.deinit(gpa);
|
||||
sema.* = undefined;
|
||||
}
|
||||
|
||||
const Chunk = struct {
|
||||
pub const Chunk = struct {
|
||||
sema: *Sema,
|
||||
knot: *Compilation.Knot,
|
||||
namespace: *Module.Namespace,
|
||||
code: *Module.CodeChunk,
|
||||
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, Ref) = .empty,
|
||||
constants_map: std.AutoHashMapUnmanaged(InternPool.Constant.Index, u8) = .empty,
|
||||
labels: std.ArrayListUnmanaged(Label) = .empty,
|
||||
fixups: std.ArrayListUnmanaged(Fixup) = .empty,
|
||||
inst_map: std.AutoHashMapUnmanaged(Ir.Inst.Index, Ref) = .empty,
|
||||
constant_map: std.AutoHashMapUnmanaged(u32, u32) = .empty,
|
||||
|
||||
const dummy_address = 0xffffffff;
|
||||
|
||||
const Label = struct {
|
||||
code_offset: usize,
|
||||
|
|
@ -111,16 +124,31 @@ const Chunk = struct {
|
|||
code_offset: u32,
|
||||
};
|
||||
|
||||
fn deinit(chunk: *Chunk, gpa: std.mem.Allocator) void {
|
||||
chunk.fixups.deinit(gpa);
|
||||
chunk.labels.deinit(gpa);
|
||||
const dummy_address = 0xffffffff;
|
||||
|
||||
pub fn deinit(chunk: *Chunk, gpa: std.mem.Allocator) void {
|
||||
chunk.inst_map.deinit(gpa);
|
||||
chunk.constant_map.deinit(gpa);
|
||||
chunk.constants_map.deinit(gpa);
|
||||
chunk.labels.deinit(gpa);
|
||||
chunk.fixups.deinit(gpa);
|
||||
}
|
||||
|
||||
/// Reserve a stack slot for temporary variables.
|
||||
fn addStackSlot(chunk: *Chunk) u8 {
|
||||
const new_slot = chunk.code.stack_size;
|
||||
chunk.code.stack_size += 1;
|
||||
return @intCast(new_slot);
|
||||
}
|
||||
|
||||
/// Reserve a stack slot for a parameter.
|
||||
fn addParameter(chunk: *Chunk) u8 {
|
||||
chunk.code.args_count += 1;
|
||||
return chunk.addStackSlot();
|
||||
}
|
||||
|
||||
fn addByteOp(chunk: *Chunk, op: Story.Opcode) error{OutOfMemory}!Ref {
|
||||
const gpa = chunk.sema.gpa;
|
||||
const bytecode = &chunk.knot.bytecode;
|
||||
const bytecode = &chunk.code.bytecode;
|
||||
const byte_index = bytecode.items.len;
|
||||
try bytecode.append(gpa, @intFromEnum(op));
|
||||
return .{ .index = @intCast(byte_index) };
|
||||
|
|
@ -128,7 +156,7 @@ const Chunk = struct {
|
|||
|
||||
fn addConstOp(chunk: *Chunk, op: Story.Opcode, arg: u8) error{OutOfMemory}!Ref {
|
||||
const gpa = chunk.sema.gpa;
|
||||
const bytecode = &chunk.knot.bytecode;
|
||||
const bytecode = &chunk.code.bytecode;
|
||||
const byte_index = bytecode.items.len;
|
||||
try bytecode.ensureUnusedCapacity(gpa, 2);
|
||||
bytecode.appendAssumeCapacity(@intFromEnum(op));
|
||||
|
|
@ -138,7 +166,7 @@ const Chunk = struct {
|
|||
|
||||
fn addJumpOp(chunk: *Chunk, op: Story.Opcode) error{OutOfMemory}!Ref {
|
||||
const gpa = chunk.sema.gpa;
|
||||
const bytecode = &chunk.knot.bytecode;
|
||||
const bytecode = &chunk.code.bytecode;
|
||||
try bytecode.ensureUnusedCapacity(gpa, 3);
|
||||
bytecode.appendAssumeCapacity(@intFromEnum(op));
|
||||
bytecode.appendAssumeCapacity(0xff);
|
||||
|
|
@ -173,13 +201,26 @@ const Chunk = struct {
|
|||
}
|
||||
|
||||
fn setLabel(chunk: *Chunk, label_index: usize) void {
|
||||
const code_offset = chunk.knot.bytecode.items.len;
|
||||
const bytecode = &chunk.code.bytecode;
|
||||
const code_offset = bytecode.items.len;
|
||||
assert(label_index <= chunk.labels.items.len);
|
||||
|
||||
const label_data = &chunk.labels.items[label_index];
|
||||
label_data.code_offset = code_offset;
|
||||
}
|
||||
|
||||
/// Intern a reference to a global constant within this chunk.
|
||||
fn getOrPutConstantIndex(chunk: *Chunk, index: InternPool.Constant.Index) !u8 {
|
||||
const gpa = chunk.sema.gpa;
|
||||
const constants = &chunk.code.constants;
|
||||
if (chunk.constants_map.get(index)) |local_index| return local_index;
|
||||
|
||||
const local_index: u8 = @intCast(constants.items.len);
|
||||
try constants.append(gpa, @intCast(@intFromEnum(index)));
|
||||
try chunk.constants_map.put(gpa, index, local_index);
|
||||
return local_index;
|
||||
}
|
||||
|
||||
fn resolveInst(chunk: *Chunk, ref: Ir.Inst.Ref) Ref {
|
||||
if (ref.toIndex()) |index| {
|
||||
return chunk.inst_map.get(index).?;
|
||||
|
|
@ -187,16 +228,14 @@ const Chunk = struct {
|
|||
switch (ref) {
|
||||
.bool_true => return .bool_true,
|
||||
.bool_false => return .bool_false,
|
||||
else => return .{ .constant = @intFromEnum(ref) },
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolveLabels(chunk: *Chunk) !void {
|
||||
const start_index = 0;
|
||||
const end_index = chunk.fixups.items.len;
|
||||
const bytecode = &chunk.knot.bytecode;
|
||||
pub fn finalize(chunk: *Chunk) !void {
|
||||
const bytecode = &chunk.code.bytecode;
|
||||
|
||||
for (chunk.fixups.items[start_index..end_index]) |fixup| {
|
||||
for (chunk.fixups.items) |fixup| {
|
||||
const label = chunk.labels.items[fixup.label_index];
|
||||
assert(label.code_offset != dummy_address);
|
||||
const target_offset: usize = switch (fixup.mode) {
|
||||
|
|
@ -213,82 +252,29 @@ const Chunk = struct {
|
|||
}
|
||||
}
|
||||
|
||||
/// Intern a reference to a global constant within this chunk.
|
||||
fn getOrPutConstantIndex(chunk: *Chunk, global_index: u32) !u32 {
|
||||
const gpa = chunk.sema.gpa;
|
||||
if (chunk.constant_map.get(global_index)) |local_index| return local_index;
|
||||
|
||||
const local_index: u32 = @intCast(chunk.knot.constants.items.len);
|
||||
try chunk.knot.constants.append(gpa, global_index);
|
||||
try chunk.constant_map.put(gpa, global_index, local_index);
|
||||
return local_index;
|
||||
}
|
||||
|
||||
fn doLoad(chunk: *Chunk, ref: Ref) InnerError!Ref {
|
||||
switch (ref) {
|
||||
.none => return ref,
|
||||
.bool_true => return chunk.addByteOp(.true),
|
||||
.bool_false => return chunk.addByteOp(.false),
|
||||
.none => return ref,
|
||||
.constant => |global_index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(global_index);
|
||||
.constant => |index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(index);
|
||||
return chunk.addConstOp(.load_const, @intCast(local_index));
|
||||
},
|
||||
.global => |global_index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(global_index);
|
||||
.variable => |index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(index);
|
||||
return chunk.addConstOp(.load_global, @intCast(local_index));
|
||||
},
|
||||
.local => |id| return chunk.addConstOp(.load, @intCast(id)),
|
||||
.temporary => |id| return chunk.addConstOp(.load, @intCast(id)),
|
||||
.index => return ref,
|
||||
.knot => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn analyzeArithmeticArg(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
arg: Ref,
|
||||
arg_src: SrcLoc,
|
||||
) !void {
|
||||
switch (arg) {
|
||||
.global => |index| {
|
||||
const g = sema.ir.globals[index];
|
||||
switch (g.tag) {
|
||||
.variable => {
|
||||
const name = try sema.getOrPutStr(g.name);
|
||||
const local_index = try chunk.getOrPutConstantIndex(name.constant);
|
||||
_ = try chunk.addConstOp(.load_global, @intCast(local_index));
|
||||
},
|
||||
.knot => return fail(sema, arg_src, "invalid operand to arithmetic expression", .{}),
|
||||
}
|
||||
},
|
||||
.constant => |index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(index);
|
||||
_ = try chunk.addConstOp(.load_const, @intCast(local_index));
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeDivertTarget(sema: *Sema, chunk: *Chunk, src: SrcLoc, callee: Ref) !Ref {
|
||||
switch (callee) {
|
||||
.global => |global_index| {
|
||||
const g = sema.ir.globals[global_index];
|
||||
switch (g.tag) {
|
||||
.knot => {
|
||||
const name = try sema.getOrPutStr(g.name);
|
||||
const local_index = try chunk.getOrPutConstantIndex(name.constant);
|
||||
return chunk.addConstOp(.load_global, @intCast(local_index));
|
||||
},
|
||||
.variable => return fail(sema, src, "invalid divert target", .{}),
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn irInteger(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
|
||||
const value = sema.ir.instructions[@intFromEnum(inst)].data.int;
|
||||
return sema.getOrPutConstant(.{ .integer = value });
|
||||
return sema.getOrPutInt(value);
|
||||
}
|
||||
|
||||
fn irString(sema: *Sema, inst: Ir.Inst.Index) InnerError!Ref {
|
||||
|
|
@ -324,11 +310,9 @@ fn irBinaryOp(
|
|||
}
|
||||
|
||||
fn irAlloc(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
|
||||
const local_index = chunk.knot.stack_size;
|
||||
// TODO: Add constraints on how many temporaries we can have.
|
||||
// max(u8) or max(u16) are most likey appropriate.
|
||||
chunk.knot.stack_size += 1;
|
||||
return .{ .local = local_index };
|
||||
return .{ .temporary = chunk.addStackSlot() };
|
||||
}
|
||||
|
||||
fn irStore(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
||||
|
|
@ -340,14 +324,18 @@ fn irStore(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
|||
.bool_true, .bool_false => unreachable, // TODO: "Cannot assign to boolean"
|
||||
.none => unreachable,
|
||||
.constant => |_| unreachable, // TODO: "Cannot assign to constant"
|
||||
.global => |id| _ = try chunk.addConstOp(.store_global, @intCast(id)),
|
||||
.local => |id| _ = try chunk.addConstOp(.store, @intCast(id)),
|
||||
.knot => |_| unreachable, // TODO: "Cannot assign to knot"
|
||||
.variable => |index| {
|
||||
_ = try chunk.addConstOp(.store_global, @intCast(@intFromEnum(index)));
|
||||
},
|
||||
.temporary => |index| {
|
||||
_ = try chunk.addConstOp(.store, @intCast(index));
|
||||
},
|
||||
.index => unreachable,
|
||||
}
|
||||
_ = try chunk.addByteOp(.pop);
|
||||
}
|
||||
|
||||
// TODO: Check what the target is!
|
||||
fn irLoad(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.un;
|
||||
const lhs = chunk.resolveInst(data.lhs);
|
||||
|
|
@ -366,13 +354,13 @@ fn irCondBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
|||
_ = try chunk.doLoad(condition);
|
||||
try chunk.addFixup(.jmp_f, else_label);
|
||||
_ = try chunk.addByteOp(.pop);
|
||||
try blockBodyInner(sema, chunk, then_body);
|
||||
try analyzeBodyInner(sema, chunk, then_body);
|
||||
|
||||
try chunk.addFixup(.jmp, end_label);
|
||||
chunk.setLabel(else_label);
|
||||
_ = try chunk.addByteOp(.pop);
|
||||
|
||||
try blockBodyInner(sema, chunk, else_body);
|
||||
try analyzeBodyInner(sema, chunk, else_body);
|
||||
chunk.setLabel(end_label);
|
||||
return .none;
|
||||
}
|
||||
|
|
@ -386,7 +374,7 @@ fn irBlock(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
|||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
return blockBodyInner(sema, chunk, body);
|
||||
return analyzeBodyInner(sema, chunk, body);
|
||||
}
|
||||
|
||||
fn irSwitchBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
||||
|
|
@ -401,10 +389,8 @@ fn irSwitchBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
|||
// TODO: Do something with this value?
|
||||
//const condition = chunk.resolveInst(extra.data.operand);
|
||||
const exit_label = try chunk.addLabel();
|
||||
|
||||
const cmp_var = chunk.knot.stack_size;
|
||||
chunk.knot.stack_size += 1;
|
||||
_ = try chunk.addConstOp(.store, @intCast(cmp_var));
|
||||
const cmp_var = chunk.addStackSlot();
|
||||
_ = try chunk.addConstOp(.store, cmp_var);
|
||||
|
||||
for (cases_slice) |case_index| {
|
||||
const case_extra = sema.ir.extraData(Ir.Inst.SwitchBr.Case, @intFromEnum(case_index));
|
||||
|
|
@ -428,7 +414,7 @@ fn irSwitchBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
|||
|
||||
chunk.setLabel(label_index);
|
||||
_ = try chunk.addByteOp(.pop);
|
||||
try blockBodyInner(sema, chunk, case_body);
|
||||
try analyzeBodyInner(sema, chunk, case_body);
|
||||
try chunk.addFixup(.jmp, exit_label);
|
||||
}
|
||||
|
||||
|
|
@ -438,7 +424,7 @@ fn irSwitchBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
|||
);
|
||||
|
||||
chunk.setLabel(else_label);
|
||||
try blockBodyInner(sema, chunk, else_body);
|
||||
try analyzeBodyInner(sema, chunk, else_body);
|
||||
chunk.setLabel(exit_label);
|
||||
}
|
||||
|
||||
|
|
@ -515,7 +501,7 @@ fn irChoiceBr(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!void {
|
|||
}
|
||||
_ = try chunk.addByteOp(.stream_flush);
|
||||
|
||||
try blockBodyInner(sema, chunk, body_slice);
|
||||
try analyzeBodyInner(sema, chunk, body_slice);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -523,72 +509,10 @@ fn irImplicitRet(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) InnerError!Ref {
|
|||
return chunk.addByteOp(.exit);
|
||||
}
|
||||
|
||||
fn irDeclRef(sema: *Sema, _: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
||||
fn irDeclRef(sema: *Sema, chunk: *Chunk, inst: Ir.Inst.Index) InnerError!Ref {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.str_tok;
|
||||
const str = try sema.getOrPutStr(data.start);
|
||||
if (sema.globals_map.get(str.constant)) |global_index| {
|
||||
return .{ .global = global_index };
|
||||
}
|
||||
return fail(sema, .{ .src_offset = data.src_offset }, "unknown global variable", .{});
|
||||
}
|
||||
|
||||
fn irDeclVar(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
name: Ir.NullTerminatedString,
|
||||
inst: Ir.Inst.Index,
|
||||
) InnerError!void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
try blockBodyInner(sema, chunk, body);
|
||||
// FIXME: hack
|
||||
{
|
||||
const last_inst = body[body.len - 1].toRef();
|
||||
const val = chunk.resolveInst(last_inst);
|
||||
_ = try chunk.doLoad(val);
|
||||
}
|
||||
const interned_str = try sema.getOrPutStr(name);
|
||||
_ = try chunk.addConstOp(.store_global, @intCast(interned_str.constant));
|
||||
_ = try chunk.addByteOp(.pop);
|
||||
}
|
||||
|
||||
fn irDeclKnot(
|
||||
sema: *Sema,
|
||||
name_ref: Ir.NullTerminatedString,
|
||||
inst: Ir.Inst.Index,
|
||||
) InnerError!void {
|
||||
const gpa = sema.gpa;
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Knot, data.extra_index);
|
||||
|
||||
var knot: Compilation.Knot = .{
|
||||
.name = name_ref,
|
||||
.arity = 0,
|
||||
.stack_size = 0,
|
||||
};
|
||||
var chunk: Chunk = .{
|
||||
.sema = sema,
|
||||
.knot = &knot,
|
||||
};
|
||||
defer chunk.deinit(gpa);
|
||||
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
try blockBodyInner(sema, &chunk, body);
|
||||
_ = try chunk.addByteOp(.exit);
|
||||
try chunk.resolveLabels();
|
||||
try sema.knots.append(gpa, knot);
|
||||
}
|
||||
|
||||
fn irDeclaration(sema: *Sema, parent_chunk: ?*Chunk, inst: Ir.Inst.Index) !void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Declaration, data.extra_index).data;
|
||||
const value_data = sema.ir.instructions[@intFromEnum(extra.value)];
|
||||
switch (value_data.tag) {
|
||||
.decl_var => try irDeclVar(sema, parent_chunk.?, extra.name, extra.value),
|
||||
.decl_knot => try irDeclKnot(sema, extra.name, extra.value),
|
||||
else => unreachable,
|
||||
}
|
||||
const decl_name = try sema.getOrPutStr(data.start);
|
||||
return sema.lookupIdentifier(chunk, decl_name.constant);
|
||||
}
|
||||
|
||||
fn irCall(_: *Sema, _: *Chunk, _: Ir.Inst.Index) !Ref {
|
||||
|
|
@ -608,12 +532,28 @@ fn irDivert(
|
|||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(ExtraType, data.extra_index);
|
||||
const body = sema.ir.extra[extra.end..];
|
||||
const callee = switch (kind) {
|
||||
.direct => chunk.resolveInst(extra.data.callee),
|
||||
.field => chunk.resolveInst(extra.data.obj_ptr),
|
||||
};
|
||||
const callee_src: SrcLoc = .{ .src_offset = data.src_offset };
|
||||
_ = try analyzeDivertTarget(sema, chunk, callee_src, callee);
|
||||
switch (kind) {
|
||||
.direct => {
|
||||
const callee = chunk.resolveInst(extra.data.callee);
|
||||
const callee_src: SrcLoc = .{ .src_offset = data.src_offset };
|
||||
_ = try analyzeDivertTarget(sema, chunk, callee_src, callee);
|
||||
},
|
||||
.field => {
|
||||
const callee = chunk.resolveInst(extra.data.obj_ptr);
|
||||
const callee_src: SrcLoc = .{ .src_offset = data.src_offset };
|
||||
const field_name = try sema.getOrPutStr(extra.data.field_name_start);
|
||||
|
||||
_ = try analyzeDivertTarget(sema, chunk, callee_src, callee);
|
||||
const e = try sema.lookupInNamespace(callee.knot.namespace, field_name.constant);
|
||||
switch (e) {
|
||||
.knot => |knot| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(knot.const_index);
|
||||
_ = try chunk.addConstOp(.load_attr, @intCast(local_index));
|
||||
},
|
||||
else => return sema.fail(callee_src, "invalid divert target", .{}),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const args_len = extra.data.args_len;
|
||||
var arg_start: u32 = args_len;
|
||||
|
|
@ -622,7 +562,7 @@ fn irDivert(
|
|||
const arg_end = sema.ir.extra[extra.end + i];
|
||||
defer arg_start = arg_end;
|
||||
const arg_body = body[arg_start..arg_end];
|
||||
try blockBodyInner(sema, chunk, @ptrCast(arg_body));
|
||||
try analyzeBodyInner(sema, chunk, @ptrCast(arg_body));
|
||||
// FIXME: hack
|
||||
{
|
||||
const last_inst: Ir.Inst.Index = @enumFromInt(arg_body[arg_body.len - 1]);
|
||||
|
|
@ -639,28 +579,50 @@ fn irFieldPtr(_: *Sema, _: *Chunk, _: Ir.Inst.Index) !Ref {
|
|||
|
||||
// TODO: Check for duplicate parameters.
|
||||
fn irParam(_: *Sema, chunk: *Chunk, _: Ir.Inst.Index) !Ref {
|
||||
//const data = sema.ir.instructions[@intFromEnum(inst)].data.string;
|
||||
const local_index = chunk.knot.stack_size;
|
||||
// TODO: Add constraints on how many temporaries we can have.
|
||||
// max(u8) or max(u16) are most likey appropriate.
|
||||
chunk.knot.arity += 1;
|
||||
chunk.knot.stack_size += 1;
|
||||
return .{ .local = local_index };
|
||||
return .{ .temporary = chunk.addParameter() };
|
||||
}
|
||||
|
||||
fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) InnerError!void {
|
||||
const gpa = sema.gpa;
|
||||
fn analyzeArithmeticArg(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
arg: Ref,
|
||||
arg_src: SrcLoc,
|
||||
) !void {
|
||||
switch (arg) {
|
||||
.variable => |index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(index);
|
||||
_ = try chunk.addConstOp(.load_global, @intCast(local_index));
|
||||
},
|
||||
.constant => |index| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(index);
|
||||
_ = try chunk.addConstOp(.load_const, @intCast(local_index));
|
||||
},
|
||||
.knot => return fail(sema, arg_src, "invalid operand to arithmetic expression", .{}),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeDivertTarget(sema: *Sema, chunk: *Chunk, src: SrcLoc, callee: Ref) !Ref {
|
||||
switch (callee) {
|
||||
.knot => |knot| {
|
||||
const local_index = try chunk.getOrPutConstantIndex(knot.const_index);
|
||||
return chunk.addConstOp(.load_global, @intCast(local_index));
|
||||
},
|
||||
else => return sema.fail(src, "invalid divert target", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
fn analyzeBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) InnerError!void {
|
||||
for (body) |inst| {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)];
|
||||
const ref: Ref = switch (data.tag) {
|
||||
.file => unreachable,
|
||||
.declaration => {
|
||||
try irDeclaration(sema, chunk, inst);
|
||||
continue;
|
||||
},
|
||||
.decl_var => unreachable, // handled in declaration()
|
||||
.decl_knot => unreachable, // handled in declaration()
|
||||
.file => unreachable, // never present inside block bodies
|
||||
.declaration => unreachable, // never present inside block bodies
|
||||
.decl_var => unreachable, // never present inside block bodies
|
||||
.decl_knot => unreachable, // never present inside block bodies
|
||||
.decl_stitch => unreachable, // never present inside block bodies
|
||||
.switch_br => {
|
||||
try irSwitchBr(sema, chunk, inst);
|
||||
continue;
|
||||
|
|
@ -720,29 +682,118 @@ fn blockBodyInner(sema: *Sema, chunk: *Chunk, body: []const Ir.Inst.Index) Inner
|
|||
.field_ptr => try irFieldPtr(sema, chunk, inst),
|
||||
.param => try irParam(sema, chunk, inst),
|
||||
};
|
||||
try chunk.inst_map.put(gpa, inst, ref);
|
||||
try chunk.inst_map.put(sema.gpa, inst, ref);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyzeFile(sema: *Sema, inst: Ir.Inst.Index) InnerError!void {
|
||||
pub fn analyzeStitch(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
inst: Ir.Inst.Index,
|
||||
) !void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Block, data.extra_index);
|
||||
const extra = sema.ir.extraData(Ir.Inst.Stitch, data.extra_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
|
||||
// FIXME: We are going to get burned by this if we don't formalize it.
|
||||
// Adding common constants to the constant pool.
|
||||
const static_constants = &[_]Compilation.Constant{
|
||||
.{ .integer = 0 },
|
||||
.{ .integer = 1 },
|
||||
};
|
||||
for (static_constants) |sc| {
|
||||
_ = try sema.getOrPutConstant(sc);
|
||||
}
|
||||
|
||||
try sema.globals_map.ensureUnusedCapacity(sema.gpa, @intCast(sema.ir.globals.len));
|
||||
for (sema.ir.globals, 0..) |global, global_index| {
|
||||
const interned = try sema.getOrPutStr(global.name);
|
||||
sema.globals_map.putAssumeCapacity(interned.constant, @intCast(global_index));
|
||||
}
|
||||
for (body) |body_index| try irDeclaration(sema, null, body_index);
|
||||
try analyzeBodyInner(sema, chunk, body);
|
||||
}
|
||||
|
||||
pub fn analyzeKnot(
|
||||
sema: *Sema,
|
||||
chunk: *Chunk,
|
||||
inst: Ir.Inst.Index,
|
||||
) !void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Knot, data.extra_index);
|
||||
const body = sema.ir.bodySlice(extra.end, extra.data.body_len);
|
||||
try analyzeBodyInner(sema, chunk, body);
|
||||
}
|
||||
|
||||
fn analyzeNestedDecl(
|
||||
sema: *Sema,
|
||||
namespace: *Module.Namespace,
|
||||
inst: Ir.Inst.Index,
|
||||
) !void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Declaration, data.extra_index).data;
|
||||
const decl = sema.ir.instructions[@intFromEnum(extra.value)];
|
||||
const decl_name = try sema.module.intern_pool.getOrPutStr(sema.gpa, extra.name);
|
||||
|
||||
switch (decl.tag) {
|
||||
.decl_stitch => {
|
||||
const child_namespace = try sema.module.createNamespace(namespace);
|
||||
try namespace.decls.put(sema.arena, decl_name, .{
|
||||
.tag = .knot,
|
||||
.decl_inst = extra.value,
|
||||
.args_count = 0,
|
||||
.namespace = child_namespace,
|
||||
});
|
||||
try sema.module.queueWorkItem(.{
|
||||
.tag = .stitch,
|
||||
.decl_name = decl_name,
|
||||
.inst_index = extra.value,
|
||||
.namespace = child_namespace,
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyzeTopLevelDecl(
|
||||
sema: *Sema,
|
||||
namespace: *Module.Namespace,
|
||||
inst: Ir.Inst.Index,
|
||||
) !void {
|
||||
const data = sema.ir.instructions[@intFromEnum(inst)].data.payload;
|
||||
const extra = sema.ir.extraData(Ir.Inst.Declaration, data.extra_index).data;
|
||||
const decl_inst = sema.ir.instructions[@intFromEnum(extra.value)];
|
||||
const decl_name = try sema.module.intern_pool.getOrPutStr(sema.gpa, extra.name);
|
||||
switch (decl_inst.tag) {
|
||||
.decl_var => {
|
||||
const decl_extra = sema.ir.extraData(Ir.Inst.Var, decl_inst.data.payload.extra_index);
|
||||
const body = sema.ir.bodySlice(decl_extra.end, decl_extra.data.body_len);
|
||||
|
||||
try namespace.decls.put(sema.gpa, decl_name, .{
|
||||
.tag = .variable,
|
||||
.namespace = null,
|
||||
.decl_inst = extra.value,
|
||||
.args_count = 0,
|
||||
});
|
||||
|
||||
// FIXME: Broken
|
||||
var chunk: *Chunk = undefined;
|
||||
try analyzeBodyInner(sema, chunk, body);
|
||||
// FIXME: hack
|
||||
{
|
||||
const last_inst = body[body.len - 1].toRef();
|
||||
const val = chunk.resolveInst(last_inst);
|
||||
_ = try chunk.doLoad(val);
|
||||
}
|
||||
_ = try chunk.addConstOp(.store_global, @intCast(@intFromEnum(decl_name)));
|
||||
_ = try chunk.addByteOp(.pop);
|
||||
},
|
||||
.decl_knot => {
|
||||
const _data = sema.ir.instructions[@intFromEnum(extra.value)].data.payload;
|
||||
const _extra = sema.ir.extraData(Ir.Inst.Knot, _data.extra_index);
|
||||
const _body = sema.ir.bodySlice(_extra.end, _extra.data.body_len);
|
||||
const _stitches = sema.ir.bodySlice(_extra.end + _body.len, _extra.data.stitches_len);
|
||||
const child_namespace = try sema.module.createNamespace(namespace);
|
||||
try namespace.decls.put(sema.arena, decl_name, .{
|
||||
.tag = .knot,
|
||||
.decl_inst = extra.value,
|
||||
.args_count = 0,
|
||||
.namespace = child_namespace,
|
||||
});
|
||||
try sema.module.queueWorkItem(.{
|
||||
.tag = .knot,
|
||||
.decl_name = decl_name,
|
||||
.inst_index = extra.value,
|
||||
.namespace = child_namespace,
|
||||
});
|
||||
|
||||
for (_stitches) |st| {
|
||||
try analyzeNestedDecl(sema, child_namespace, st);
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue