Commit cf09b335d8

Noam Preil <pleasantatk@gmail.com>
2020-07-08 01:35:33
CBE: Working function call w/ no args or return value
1 parent cf86aa8
Changed files (3)
src-self-hosted
test
stage2
src-self-hosted/cgen.zig
@@ -1,7 +1,9 @@
 const link = @import("link.zig");
 const Module = @import("Module.zig");
-const std = @import("std");
+const ir = @import("ir.zig");
 const Value = @import("value.zig").Value;
+const Type = @import("type.zig").Type;
+const std = @import("std");
 
 const C = link.File.C;
 const Decl = Module.Decl;
@@ -14,36 +16,69 @@ fn map(name: []const u8) ![]const u8 {
     return name;
 }
 
+fn renderFunctionSignature(writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
+    const tv = decl.typed_value.most_recent.typed_value;
+    switch (tv.ty.fnReturnType().zigTypeTag()) {
+        .NoReturn => {
+            try writer.writeAll("_Noreturn void ");
+        },
+        else => return error.Unimplemented,
+    }
+    const name = try map(mem.spanZ(decl.name));
+    try writer.print("{}(", .{name});
+    if (tv.ty.fnParamLen() == 0) {
+        try writer.writeAll("void)");
+    } else {
+        return error.Unimplemented;
+    }
+}
+
 pub fn generate(file: *C, decl: *Decl, standard: CStandard) !void {
-    const writer = file.file.?.writer();
+    const writer = file.main.writer();
+    const header = file.header.writer();
     const tv = decl.typed_value.most_recent.typed_value;
     switch (tv.ty.zigTypeTag()) {
         .Fn => {
-            const return_type = tv.ty.fnReturnType();
-            switch (return_type.zigTypeTag()) {
-                .NoReturn => try writer.writeAll("_Noreturn void "),
-                else => return error.Unimplemented,
-            }
-
-            const name = try map(mem.spanZ(decl.name));
-            try writer.print("{} (", .{name});
-            if (tv.ty.fnParamLen() == 0) {
-                try writer.writeAll("void){");
-            } else {
-                return error.Unimplemented;
-            }
+            try renderFunctionSignature(writer, decl);
+            try writer.writeAll(" {");
 
             const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
             const instructions = func.analysis.success.instructions;
             if (instructions.len > 0) {
-                try writer.writeAll("\n\t");
                 for (instructions) |inst| {
-                    std.debug.warn("\nTranslating {}\n", .{inst.*});
+                    try writer.writeAll("\n\t");
+                    switch (inst.tag) {
+                        .call => {
+                            const call = inst.cast(ir.Inst.Call).?.args;
+                            if (call.func.cast(ir.Inst.Constant)) |func_inst| {
+                                if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
+                                    const target = func_val.func.owner_decl;
+                                    const tname = mem.spanZ(target.name);
+                                    if (file.called.get(tname) == null) {
+                                        try file.called.put(tname, void{});
+                                        try renderFunctionSignature(header, target);
+                                        try header.writeAll(";\n");
+                                    }
+                                    try writer.print("{}();", .{tname});
+                                } else {
+                                    return error.Unimplemented;
+                                }
+                                if (call.args.len != 0) {
+                                    return error.Unimplemented;
+                                }
+                            } else {
+                                return error.Unimplemented;
+                            }
+                        },
+                        else => {
+                            std.debug.warn("\nTranslating {}\n", .{inst.*});
+                        },
+                    }
                 }
                 try writer.writeAll("\n");
             }
 
-            try writer.writeAll("}\n");
+            try writer.writeAll("}\n\n");
         },
         else => return error.Unimplemented,
     }
src-self-hosted/link.zig
@@ -93,6 +93,9 @@ pub fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C
         .file = file,
         .options = options,
         .owns_file_handle = false,
+        .main = std.ArrayList(u8).init(allocator),
+        .header = std.ArrayList(u8).init(allocator),
+        .called = std.StringHashMap(void).init(allocator),
     };
     errdefer self.deinit();
     return self;
@@ -165,9 +168,7 @@ pub const File = struct {
     pub fn flush(base: *File) !void {
         try switch (base.tag) {
             .Elf => @fieldParentPtr(Elf, "base", base).flush(),
-            .C => {
-                //TODO
-            },
+            .C => @fieldParentPtr(C, "base", base).flush(),
             else => unreachable,
         };
     }
@@ -208,9 +209,12 @@ pub const File = struct {
         base: File = File{ .tag = base_tag },
 
         allocator: *Allocator,
+        header: std.ArrayList(u8),
+        main: std.ArrayList(u8),
         file: ?fs.File,
         owns_file_handle: bool,
         options: Options,
+        called: std.StringHashMap(void),
 
         pub fn makeWritable(self: *File.C, dir: fs.Dir, sub_path: []const u8) !void {
             assert(self.owns_file_handle);
@@ -223,6 +227,9 @@ pub const File = struct {
         }
 
         pub fn deinit(self: *File.C) void {
+            self.main.deinit();
+            self.header.deinit();
+            self.called.deinit();
             if (self.owns_file_handle) {
                 if (self.file) |f|
                     f.close();
@@ -232,6 +239,21 @@ pub const File = struct {
         pub fn updateDecl(self: *File.C, module: *Module, decl: *Module.Decl) !void {
             try cgen.generate(self, decl, self.options.c_standard.?);
         }
+
+        pub fn flush(self: *File.C) !void {
+            const writer = self.file.?.writer();
+            if (self.header.items.len > 0) {
+                try self.header.append('\n');
+            }
+            try writer.writeAll(self.header.items);
+            if (self.main.items.len > 1) {
+                const last_two = self.main.items[self.main.items.len - 2 ..];
+                if (std.mem.eql(u8, last_two, "\n\n")) {
+                    self.main.items.len -= 1;
+                }
+            }
+            try writer.writeAll(self.main.items);
+        }
     };
 
     pub const Elf = struct {
test/stage2/cbe.zig
@@ -9,13 +9,26 @@ const linux_x64 = std.zig.CrossTarget{
 };
 
 pub fn addCases(ctx: *TestContext) !void {
-    // These tests should work on every platform
     ctx.c11("empty start function", linux_x64,
         \\export fn _start() noreturn {}
     ,
-    // A newline is always generated after every function; this ensures, among
-    // other things, that there is always a newline at the end of the file
-    \\_Noreturn void _start(void) {}
+        \\_Noreturn void _start(void) {}
+        \\
+    );
+    ctx.c11("less empty start function", linux_x64,
+        \\fn main() noreturn {}
+        \\
+        \\export fn _start() noreturn {
+        \\	main();
+        \\}
+    ,
+        \\_Noreturn void main(void);
+        \\
+        \\_Noreturn void _start(void) {
+        \\	main();
+        \\}
+        \\
+        \\_Noreturn void main(void) {}
         \\
     );
 }