summary refs log tree commit diff
path: root/src/main.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.zig')
-rw-r--r--src/main.zig253
1 files changed, 253 insertions, 0 deletions
diff --git a/src/main.zig b/src/main.zig
new file mode 100644
index 0000000..fbbb8b1
--- /dev/null
+++ b/src/main.zig
@@ -0,0 +1,253 @@
+//! Parse command-line arguments and call the compiler.
+const Allocator = std.mem.Allocator;
+const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator;
+const arch = builtin.target.cpu.arch;
+const argsWithAllocator = std.process.argsWithAllocator;
+const assert = std.debug.assert;
+const builtin = @import("builtin");
+const c = @import("cimport.zig");
+const cWriter = std.io.cWriter;
+const eql = std.mem.eql;
+const exit = std.os.exit;
+const getStdErr = std.io.getStdErr;
+const getStdOut = std.io.getStdOut;
+const isAlphabetic = std.ascii.isAlphabetic;
+const span = std.mem.span;
+const std = @import("std");
+const toUpper = std.ascii.toUpper;
+
+/// Native target.
+const default_target = switch (builtin.target.os.tag) {
+    .macos => switch (arch) {
+        .aarch64 => &c.T_arm64_apple,
+        .x86_64 => &c.T_amd64_apple,
+        else => unreachable,
+    },
+    else => switch (arch) {
+        .aarch64 => &c.T_arm64,
+        .riscv64 => &c.T_rv64,
+        .x86_64 => &c.T_amd64_sysv,
+        else => unreachable,
+    },
+};
+
+/// Print the help message and exit with the given status.
+/// The usage is printed to stderr on an error status, to stdout otherwise.
+fn printUsage(prog: [:0]const u8, status: u8) !noreturn {
+    const stream = (if (status == 0) getStdOut() else getStdErr()).writer();
+    try stream.print(
+        \\{s} [OPTIONS] {{file.ssa, -}}
+        \\	-h          prints this help
+        \\	-o file     output to file
+        \\	-t <target> generate for a target among:
+        \\	            
+    , .{ prog });
+    for (c.targets, 0..) |target, i| {
+        if (i > 0)
+            try stream.writeAll(", ");
+        const name: [*:0]const u8 = @ptrCast(&target.name);
+        try stream.print("{s}", .{ span(name) });
+        if (target == default_target)
+            try stream.writeAll(" (default)");
+    }
+    try stream.writeAll(
+        \\
+        \\	-d <flags>  dump debug information:
+        \\	            P (parsing),
+        \\	            M (memory optimization),
+        \\	            N (SSA construction),
+        \\	            C (copy elimination),
+        \\	            F (constant folding),
+        \\	            A (ABI lowering),
+        \\	            I (instruction selection),
+        \\	            L (liveness),
+        \\	            S (spilling),
+        \\	            R (register allocation)
+        \\
+    );
+    exit(status);
+}
+
+/// Print the given message and exit.  The message is printed
+/// to stderr on an error status, to stdout otherwise.
+fn die(comptime format: []const u8, args: anytype, status: u8) !noreturn {
+    const stream = if (status == 0) getStdOut() else getStdErr();
+    try stream.writer().print(format, args);
+    exit(status);
+}
+
+/// Function emitter.
+const Fun = struct {
+    /// Postprocess the parsed function.
+    fn process(fun: *c.Fn) void {
+        c.T.abi0(fun);
+        c.fillrpo(fun);
+        c.fillpreds(fun);
+        c.filluse(fun);
+        c.promote(fun);
+        c.filluse(fun);
+        c.ssa(fun);
+        c.filluse(fun);
+        c.ssacheck(fun);
+        c.fillalias(fun);
+        c.loadopt(fun);
+        c.filluse(fun);
+        c.fillalias(fun);
+        c.coalesce(fun);
+        c.filluse(fun);
+        c.ssacheck(fun);
+        c.copy(fun);
+        c.filluse(fun);
+        c.fold(fun);
+        c.T.abi1(fun);
+        c.simpl(fun);
+        c.fillpreds(fun);
+        c.filluse(fun);
+        c.T.isel(fun);
+        c.fillrpo(fun);
+        c.filllive(fun);
+        c.fillloop(fun);
+        c.fillcost(fun);
+        c.spill(fun);
+        c.rega(fun);
+        c.fillrpo(fun);
+        c.simpljmp(fun);
+        c.fillpreds(fun);
+        c.fillrpo(fun);
+        assert(fun.rpo[0] == fun.start);
+        var n: c_uint = 0;
+        while (n + 1 < fun.nblk) : (n += 1)
+            fun.rpo[n].?.link = fun.rpo[n + 1]
+        else
+            fun.rpo[n].?.link = null;
+    }
+
+    /// Log debugging information about the given function.
+    fn debug(fun: *c.Fn, out: *c.FILE) callconv(.C) void {
+        const writer = cWriter(@ptrCast(out));
+        const name: [*:0]const u8 = @ptrCast(&fun.name);
+        writer.print("**** Function {s} ****", .{ span(name) })
+            catch unreachable;
+        if (c.debug['P']) {
+            writer.writeAll("\n> After parsing:\n") catch unreachable;
+            c.printfn(fun, out);
+        }
+        process(fun);
+        writer.writeAll("\n") catch unreachable;
+        c.freeall();
+    }
+
+    /// Emit the given function.
+    fn emit(fun: *c.Fn, out: *c.FILE) callconv(.C) void {
+        process(fun);
+        c.T.emitfn(fun, out);
+        const writer = cWriter(@ptrCast(out));
+        const name: [*:0]const u8 = @ptrCast(&fun.name);
+        writer.print("/* end function {s} */\n\n", .{ span(name) })
+            catch unreachable;
+        c.freeall();
+    }
+};
+
+/// Parser of a compilation unit.
+const Parser = struct {
+    dbg: bool,
+    out: *c.FILE,
+
+    /// Initialize parser from command-line options.
+    pub fn init(allocator: Allocator) !Parser {
+        c.T = default_target.*;
+        var args = try argsWithAllocator(allocator);
+        var dbg = false;
+        var out: *c.FILE = c.stdio.stdout.?;
+        errdefer _ = c.fclose(out);
+
+        const prog = args.next().?;
+        while (args.next()) |arg| {
+            if (arg.len < 2 or arg[0] != '-')
+                continue;
+            const value = if (arg[1] == 'h')
+                try printUsage(prog, 0)
+            else if (arg.len == 2)
+                args.next() orelse try printUsage(prog, 1)
+            else
+                arg[2..];
+
+            switch (arg[1]) {
+                'd' => for (value) |flag| {
+                    if (isAlphabetic(flag)) {
+                        c.debug[toUpper(flag)] = true;
+                        dbg = true;
+                    }
+                },
+                'o' => {
+                    _ = c.fclose(out);
+                    out = c.fopen(value, "w") // TODO: explicit OpenError
+                        orelse try die("cannot open '{s}'\n", .{ value }, 1);
+                },
+                't' => {
+                    if (eql(u8, value, "?")) {
+                        const name: [*:0]const u8 = @ptrCast(&c.T.name);
+                        try die("{s}\n", .{ span(name) }, 0);
+                    }
+                    c.T = for (c.targets) |target| {
+                        const name: [*:0]const u8 = @ptrCast(&target.name);
+                        if (eql(u8, value, span(name)))
+                            break target.*;
+                    } else try die("unknown target '{s}'\n", .{ value }, 1);
+                },
+                else => try printUsage(prog, 1),
+            }
+        }
+
+        if (dbg)
+            _ = c.fclose(out);
+        return .{ .dbg = dbg, .out = if (dbg) c.stdio.stderr.? else out };
+    }
+
+    /// Finish writing to output file.
+    pub fn deinit(self: Parser) void {
+        if (!self.dbg)
+            c.T.emitfin(self.out);
+        _ = c.fclose(self.out);
+    }
+
+    /// Skip emitting data in debug mode.
+    fn noop_dat(_: *c.Dat, _: *c.FILE) callconv(.C) void {}
+
+    /// Parse the given file.
+    pub fn parse(self: Parser, file: *c.FILE, path: [*:0]const u8) void {
+        c.parse(file, path, self.out,
+            if (self.dbg) noop_dat else c.emitdat,
+            if (self.dbg) Fun.debug else Fun.emit);
+    }
+};
+
+pub fn main() !void {
+    var gpa = GeneralPurposeAllocator(.{}){};
+    const allocator = gpa.allocator();
+    defer _ = gpa.detectLeaks();
+
+    const parser = try Parser.init(allocator);
+    defer parser.deinit();
+    var read_stdin = true;
+    var args = try argsWithAllocator(allocator);
+    _ = args.next().?;
+    while (args.next()) |arg| {
+        if (arg[0] == '-' and arg.len > 1) {
+            // must be -d, -o or -t
+            if (arg.len == 2)
+                _ = args.next().?;
+            continue;
+        }
+        read_stdin = false;
+        const in_file = if (eql(u8, arg, "-"))
+            c.stdio.stdin.?
+        else c.fopen(arg, "r") // TODO: explicit OpenError
+            orelse try die("cannot open '{s}'\n", .{ arg }, 1);
+        defer _ = c.fclose(in_file);
+        parser.parse(in_file, arg.ptr);
+    }
+    if (read_stdin)
+        parser.parse(c.stdio.stdin.?, "-");
+}