diff options
Diffstat (limited to 'src/main.zig')
-rw-r--r-- | src/main.zig | 253 |
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.?, "-"); +} |