//! 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 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 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.?, "-"); }