zig/src-self-hosted/ir.zig

167 lines
5.1 KiB
Zig
Raw Normal View History

const std = @import("std");
2020-04-17 21:09:43 -07:00
const mem = std.mem;
const Allocator = std.mem.Allocator;
const Value = @import("value.zig").Value;
2020-04-18 16:41:45 -07:00
const Type = @import("type.zig").Type;
const assert = std.debug.assert;
const text = @import("ir/text.zig");
/// These are in-memory, analyzed instructions. See `text.Inst` for the representation
/// of instructions that correspond to the ZIR text format.
pub const Inst = struct {
pub fn ty(base: *Inst) ?Type {
switch (base.tag) {
.constant => return base.cast(Constant).?.ty,
.@"asm" => return base.cast(Assembly).?.ty,
.@"fn" => return base.cast(Fn).?.ty,
2020-04-17 21:09:43 -07:00
.ptrtoint => return Type.initTag(.@"usize"),
.@"unreachable" => return Type.initTag(.@"noreturn"),
.@"export" => return Type.initTag(.@"void"),
.fntype, .primitive => return Type.initTag(.@"type"),
.fieldptr,
.deref,
=> return null,
}
2020-04-19 17:04:11 -07:00
}
2020-04-17 21:09:43 -07:00
/// This struct owns the `Value` memory. When the struct is deallocated,
/// so is the `Value`. The value of a constant must be copied into
/// a memory location for the value to survive after a const instruction.
pub const Constant = struct {
base: Inst = Inst{ .tag = .constant },
2020-04-18 16:41:45 -07:00
ty: Type,
2020-04-17 23:55:28 -07:00
2020-04-18 16:41:45 -07:00
positionals: struct {
value: Value,
},
2020-04-17 23:55:28 -07:00
kw_args: struct {},
};
2020-04-17 21:09:43 -07:00
};
const Analyze = struct {
2020-04-17 21:09:43 -07:00
allocator: *Allocator,
old_tree: *const Module,
2020-04-18 17:04:37 -07:00
errors: std.ArrayList(ErrorMsg),
decls: std.ArrayList(*Inst),
const NewInst = struct {
ptr: *Inst,
};
};
2020-04-18 17:04:37 -07:00
pub fn analyze(allocator: *Allocator, old_tree: Module) !Module {
var ctx = Analyze{
2020-04-17 21:09:43 -07:00
.allocator = allocator,
.old_tree = &old_tree,
2020-04-18 17:04:37 -07:00
.decls = std.ArrayList(*Inst).init(allocator),
.errors = std.ArrayList(ErrorMsg).init(allocator),
.inst_table = std.HashMap(*Inst, Analyze.InstData).init(allocator),
};
defer ctx.decls.deinit();
defer ctx.errors.deinit();
defer inst_table.deinit();
analyzeRoot(&ctx) catch |err| switch (err) {
error.AnalyzeFailure => {
2020-04-18 17:04:37 -07:00
assert(ctx.errors.items.len != 0);
2020-04-17 21:09:43 -07:00
},
else => |e| return e,
};
return Module{
2020-04-18 17:04:37 -07:00
.decls = ctx.decls.toOwnedSlice(),
.errors = ctx.errors.toOwnedSlice(),
};
2020-04-17 21:09:43 -07:00
}
fn analyzeRoot(ctx: *Analyze) !void {
for (old_tree.decls) |decl| {
if (decl.cast(Inst.Export)) |export_inst| {
try analyzeExport(ctx, export_inst);
2018-07-13 18:56:38 -07:00
}
2020-04-17 21:09:43 -07:00
}
}
2018-07-13 18:56:38 -07:00
fn analyzeExport(ctx: *Analyze, export_inst: *Inst.Export) !void {
const old_decl = export_inst.positionals.value;
const new_info = ctx.inst_table.get(old_exp_target) orelse blk: {
const new_decl = try analyzeDecl(ctx, old_decl);
const new_info: Analyze.NewInst = .{ .ptr = new_decl };
try ctx.inst_table.put(old_decl, new_info);
break :blk new_info;
};
2020-04-17 23:55:28 -07:00
//const exp_type = new_info.ptr.ty();
//switch (exp_type.zigTypeTag()) {
// .Fn => {
// if () |kv| {
// kv.value
// }
// return analyzeExportFn(ctx, exp_target.cast(Inst.,
// },
// else => return ctx.fail("unable to export type '{}'", .{exp_type}),
//}
2020-04-17 21:09:43 -07:00
}
2020-04-17 21:09:43 -07:00
pub fn main() anyerror!void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const allocator = &arena.allocator;
2020-04-17 21:09:43 -07:00
const args = try std.process.argsAlloc(allocator);
2020-04-17 21:09:43 -07:00
const src_path = args[1];
const debug_error_trace = true;
const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0);
var tree = try text.parse(allocator, source);
2020-04-19 17:04:11 -07:00
defer tree.deinit();
2020-04-18 17:04:37 -07:00
if (tree.errors.len != 0) {
for (tree.errors) |err_msg| {
2020-04-17 21:09:43 -07:00
const loc = findLineColumn(source, err_msg.byte_offset);
std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
}
2020-04-17 21:09:43 -07:00
if (debug_error_trace) return error.ParseFailure;
std.process.exit(1);
}
2020-04-19 17:04:11 -07:00
tree.dump();
//const new_tree = try analyze(allocator, tree);
2020-04-19 17:04:11 -07:00
//defer new_tree.deinit();
//if (new_tree.errors.len != 0) {
// for (new_tree.errors) |err_msg| {
// const loc = findLineColumn(source, err_msg.byte_offset);
// std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg });
// }
// if (debug_error_trace) return error.ParseFailure;
// std.process.exit(1);
//}
//new_tree.dump();
2020-04-17 21:09:43 -07:00
}
2020-04-17 21:09:43 -07:00
fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
var line: usize = 0;
var column: usize = 0;
for (source[0..byte_offset]) |byte| {
switch (byte) {
'\n' => {
line += 1;
column = 0;
},
else => {
column += 1;
},
}
}
2020-04-17 21:09:43 -07:00
return .{ .line = line, .column = column };
2018-07-13 18:56:38 -07:00
}
2020-04-18 21:38:56 -07:00
// Performance optimization ideas:
// * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions