diff options
| author | Alex Cameron <ascottcameron@gmail.com> | 2020-11-13 00:50:02 +1100 |
|---|---|---|
| committer | Alex Cameron <ascottcameron@gmail.com> | 2020-12-23 01:14:35 +1100 |
| commit | 40f0275e7cb7f3f4b2c382e5b457e714080c4cf2 (patch) | |
| tree | 87d919099376fe351c830fdb305d796e52d49b0b /src | |
| parent | 43dbe86226fe89c6364fa0261297f1a9d8eb2a58 (diff) | |
| download | zig-40f0275e7cb7f3f4b2c382e5b457e714080c4cf2.tar.gz zig-40f0275e7cb7f3f4b2c382e5b457e714080c4cf2.zip | |
Implement emit-h
Diffstat (limited to 'src')
| -rw-r--r-- | src/Compilation.zig | 77 | ||||
| -rw-r--r-- | src/codegen/c.zig | 130 | ||||
| -rw-r--r-- | src/link/C.zig | 62 |
3 files changed, 183 insertions, 86 deletions
diff --git a/src/Compilation.zig b/src/Compilation.zig index 23f67f5b37..5b06bd6370 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -26,6 +26,8 @@ const Module = @import("Module.zig"); const Cache = @import("Cache.zig"); const stage1 = @import("stage1.zig"); const translate_c = @import("translate_c.zig"); +const c_codegen = @import("codegen/c.zig"); +const c_link = @import("link/C.zig"); const ThreadPool = @import("ThreadPool.zig"); const WaitGroup = @import("WaitGroup.zig"); @@ -131,6 +133,7 @@ emit_asm: ?EmitLoc, emit_llvm_ir: ?EmitLoc, emit_analysis: ?EmitLoc, emit_docs: ?EmitLoc, +c_header: ?c_link.Header, pub const InnerError = Module.InnerError; @@ -895,10 +898,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { }; }; - if (!use_llvm and options.emit_h != null) { - fatal("TODO implement support for -femit-h in the self-hosted backend", .{}); - } - var system_libs: std.StringArrayHashMapUnmanaged(void) = .{}; errdefer system_libs.deinit(gpa); try system_libs.ensureCapacity(gpa, options.system_libs.len); @@ -975,6 +974,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { .global_cache_directory = options.global_cache_directory, .bin_file = bin_file, .emit_h = options.emit_h, + .c_header = if (!use_llvm and options.emit_h != null) c_link.Header.init(gpa) else null, .emit_asm = options.emit_asm, .emit_llvm_ir = options.emit_llvm_ir, .emit_analysis = options.emit_analysis, @@ -1286,6 +1286,20 @@ pub fn update(self: *Compilation) !void { module.root_scope.unload(self.gpa); } } + + // If we've chosen to emit a C header, flush the header to the disk. + if (self.c_header) |header| { + const header_path = self.emit_h.?; + // If a directory has been provided, write the header there. Otherwise, just write it to the + // cache directory. + const header_dir = if (header_path.directory) |dir| + dir.handle + else + self.local_cache_directory.handle; + const header_file = try header_dir.createFile(header_path.basename, .{}); + defer header_file.close(); + try header.flush(header_file.writer()); + } } /// Having the file open for writing is problematic as far as executing the @@ -1385,6 +1399,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len); defer c_comp_progress_node.end(); + var arena = std.heap.ArenaAllocator.init(self.gpa); + defer arena.deinit(); + var wg = WaitGroup{}; defer wg.wait(); @@ -1432,22 +1449,44 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); - self.bin_file.updateDecl(module, decl) catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - error.AnalysisFail => { - decl.analysis = .dependency_failure; - }, - else => { - try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); - module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( - module.gpa, - decl.src(), - "unable to codegen: {}", - .{@errorName(err)}, - )); - decl.analysis = .codegen_failure_retryable; - }, + self.bin_file.updateDecl(module, decl) catch |err| { + switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .dependency_failure; + }, + else => { + try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); + module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.gpa, + decl.src(), + "unable to codegen: {}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + }, + } + return; }; + + if (self.c_header) |*header| { + c_codegen.generateHeader(&arena, module, &header.*, decl) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.AnalysisFail => { + decl.analysis = .dependency_failure; + }, + else => { + try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); + module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( + module.gpa, + decl.src(), + "unable to generate C header: {}", + .{@errorName(err)}, + )); + decl.analysis = .codegen_failure_retryable; + }, + }; + } }, }, .analyze_decl => |decl| { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index e562ce320e..425663ad9c 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -2,6 +2,7 @@ const std = @import("std"); const link = @import("../link.zig"); const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); const Inst = @import("../ir.zig").Inst; const Value = @import("../value.zig").Value; @@ -19,7 +20,7 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { return allocator.dupe(u8, name); } -fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { +fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void { switch (T.zigTypeTag()) { .NoReturn => { try writer.writeAll("zig_noreturn void"); @@ -27,16 +28,16 @@ fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { .Void => try writer.writeAll("void"), .Int => { if (T.tag() == .u8) { - ctx.file.need_stdint = true; + header.need_stdint = true; try writer.writeAll("uint8_t"); } else if (T.tag() == .usize) { - ctx.file.need_stddef = true; + header.need_stddef = true; try writer.writeAll("size_t"); } else { - return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{}); + return ctx.fail(ctx.decl.src(), "TODO implement int types", .{}); } }, - else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}), + else => |e| return ctx.fail(ctx.decl.src(), "TODO implement type {}", .{e}), } } @@ -47,13 +48,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va return writer.print("{}", .{val.toSignedInt()}); return writer.print("{}", .{val.toUnsignedInt()}); }, - else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}), + else => |e| return ctx.fail(ctx.decl.src(), "TODO implement value {}", .{e}), } } -fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { +fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { const tv = decl.typed_value.most_recent.typed_value; - try renderType(ctx, writer, tv.ty.fnReturnType()); + try renderType(ctx, header, writer, tv.ty.fnReturnType()); // Use the child allocator directly, as we know the name can be freed before // the rest of the arena. const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); @@ -68,7 +69,7 @@ fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl if (index > 0) { try writer.writeAll(", "); } - try renderType(ctx, writer, tv.ty.fnParamType(index)); + try renderType(ctx, header, writer, tv.ty.fnParamType(index)); try writer.print(" arg{}", .{index}); } } @@ -83,6 +84,34 @@ pub fn generate(file: *C, decl: *Decl) !void { } } +pub fn generateHeader( + arena: *std.heap.ArenaAllocator, + module: *Module, + header: *C.Header, + decl: *Decl, +) error{ AnalysisFail, OutOfMemory }!void { + switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { + .Fn => { + var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); + defer inst_map.deinit(); + var ctx = Context{ + .decl = decl, + .arena = arena, + .inst_map = &inst_map, + }; + const writer = header.buf.writer(); + renderFunctionSignature(&ctx, header, writer, decl) catch |err| { + if (err == error.AnalysisFail) { + try module.failed_decls.put(module.gpa, decl, ctx.error_msg); + } + return err; + }; + try writer.writeAll(";\n"); + }, + else => {}, + } +} + fn genArray(file: *C, decl: *Decl) !void { const tv = decl.typed_value.most_recent.typed_value; // TODO: prevent inline asm constants from being emitted @@ -102,12 +131,12 @@ fn genArray(file: *C, decl: *Decl) !void { } const Context = struct { - file: *C, decl: *Decl, inst_map: *std.AutoHashMap(*Inst, []u8), arena: *std.heap.ArenaAllocator, argdex: usize = 0, unnamed_index: usize = 0, + error_msg: *Compilation.ErrorMsg = undefined, fn resolveInst(self: *Context, inst: *Inst) ![]u8 { if (inst.cast(Inst.Constant)) |const_inst| { @@ -127,6 +156,11 @@ const Context = struct { return val; } + fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { + self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args); + return error.AnalysisFail; + } + fn deinit(self: *Context) void { self.* = undefined; } @@ -141,14 +175,16 @@ fn genFn(file: *C, decl: *Decl) !void { var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); defer inst_map.deinit(); var ctx = Context{ - .file = file, .decl = decl, .arena = &arena, .inst_map = &inst_map, }; - defer ctx.deinit(); + defer { + file.error_msg = ctx.error_msg; + ctx.deinit(); + } - try renderFunctionSignature(&ctx, writer, decl); + try renderFunctionSignature(&ctx, &file.header, writer, decl); try writer.writeAll(" {"); @@ -158,18 +194,18 @@ fn genFn(file: *C, decl: *Decl) !void { try writer.writeAll("\n"); for (instructions) |inst| { if (switch (inst.tag) { - .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), - .call => try genCall(&ctx, inst.castTag(.call).?), - .add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"), - .sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"), + .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?), + .call => try genCall(&ctx, file, inst.castTag(.call).?), + .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"), + .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"), .ret => try genRet(&ctx, inst.castTag(.ret).?), - .retvoid => try genRetVoid(&ctx), + .retvoid => try genRetVoid(file), .arg => try genArg(&ctx), .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?), - .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?), - .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?), - else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}), + .unreach => try genUnreach(file, inst.castTag(.unreach).?), + .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?), + else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}), }) |name| { try ctx.inst_map.putNoClobber(inst, name); } @@ -185,46 +221,46 @@ fn genArg(ctx: *Context) !?[]u8 { return name; } -fn genRetVoid(ctx: *Context) !?[]u8 { - try ctx.file.main.writer().print(indentation ++ "return;\n", .{}); +fn genRetVoid(file: *C) !?[]u8 { + try file.main.writer().print(indentation ++ "return;\n", .{}); return null; } fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { - return ctx.file.fail(ctx.decl.src(), "TODO return", .{}); + return ctx.fail(ctx.decl.src(), "TODO return", .{}); } -fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { +fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { if (inst.base.isUnused()) return null; const op = inst.operand; - const writer = ctx.file.main.writer(); + const writer = file.main.writer(); const name = try ctx.name(); const from = try ctx.resolveInst(inst.operand); try writer.writeAll(indentation ++ "const "); - try renderType(ctx, writer, inst.base.ty); + try renderType(ctx, &file.header, writer, inst.base.ty); try writer.print(" {} = (", .{name}); - try renderType(ctx, writer, inst.base.ty); + try renderType(ctx, &file.header, writer, inst.base.ty); try writer.print("){};\n", .{from}); return name; } -fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { +fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { if (inst.base.isUnused()) return null; const lhs = ctx.resolveInst(inst.lhs); const rhs = ctx.resolveInst(inst.rhs); - const writer = ctx.file.main.writer(); + const writer = file.main.writer(); const name = try ctx.name(); try writer.writeAll(indentation ++ "const "); - try renderType(ctx, writer, inst.base.ty); + try renderType(ctx, &file.header, writer, inst.base.ty); try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); return name; } -fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { - const writer = ctx.file.main.writer(); - const header = ctx.file.header.writer(); +fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { + const writer = file.main.writer(); + const header = file.header.buf.writer(); try writer.writeAll(indentation); if (inst.func.castTag(.constant)) |func_inst| { if (func_inst.val.cast(Value.Payload.Function)) |func_val| { @@ -235,9 +271,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { try writer.print("(void)", .{}); } const tname = mem.spanZ(target.name); - if (ctx.file.called.get(tname) == null) { - try ctx.file.called.put(tname, void{}); - try renderFunctionSignature(ctx, header, target); + if (file.called.get(tname) == null) { + try file.called.put(tname, void{}); + try renderFunctionSignature(ctx, &file.header, header, target); try header.writeAll(";\n"); } try writer.print("{}(", .{tname}); @@ -256,10 +292,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { } try writer.writeAll(");\n"); } else { - return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{}); + return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{}); } } else { - return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); + return ctx.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); } return null; } @@ -274,20 +310,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { return null; } -fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { - try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); +fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 { + try file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); return null; } -fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { - const writer = ctx.file.main.writer(); +fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { + const writer = file.main.writer(); try writer.writeAll(indentation); for (as.inputs) |i, index| { if (i[0] == '{' and i[i.len - 1] == '}') { const reg = i[1 .. i.len - 1]; const arg = as.args[index]; try writer.writeAll("register "); - try renderType(ctx, writer, arg.ty); + try renderType(ctx, &file.header, writer, arg.ty); try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); // TODO merge constant handling into inst_map as well if (arg.castTag(.constant)) |c| { @@ -296,17 +332,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { } else { const gop = try ctx.inst_map.getOrPut(arg); if (!gop.found_existing) { - return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); + return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); } try writer.print("{};\n ", .{gop.entry.value}); } } else { - return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); + return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); } } try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); if (as.output) |o| { - return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{}); + return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{}); } if (as.inputs.len > 0) { if (as.output == null) { diff --git a/src/link/C.zig b/src/link/C.zig index 467e10998e..0906482dd1 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -13,15 +13,52 @@ const C = @This(); pub const base_tag: File.Tag = .c; +pub const Header = struct { + buf: std.ArrayList(u8), + need_stddef: bool = false, + need_stdint: bool = false, + + pub fn init(allocator: *Allocator) Header { + return .{ + .buf = std.ArrayList(u8).init(allocator), + }; + } + + pub fn flush(self: *const Header, writer: anytype) !void { + const tracy = trace(@src()); + defer tracy.end(); + + try writer.writeAll(@embedFile("cbe.h")); + var includes = false; + if (self.need_stddef) { + try writer.writeAll("#include <stddef.h>\n"); + includes = true; + } + if (self.need_stdint) { + try writer.writeAll("#include <stdint.h>\n"); + includes = true; + } + if (includes) { + try writer.writeByte('\n'); + } + if (self.buf.items.len > 0) { + try writer.print("{}\n", .{self.buf.items}); + } + } + + pub fn deinit(self: *Header) void { + self.buf.deinit(); + self.* = undefined; + } +}; + base: File, -header: std.ArrayList(u8), +header: Header, constants: std.ArrayList(u8), main: std.ArrayList(u8), called: std.StringHashMap(void), -need_stddef: bool = false, -need_stdint: bool = false, error_msg: *Compilation.ErrorMsg = undefined, pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { @@ -44,7 +81,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio .allocator = allocator, }, .main = std.ArrayList(u8).init(allocator), - .header = std.ArrayList(u8).init(allocator), + .header = Header.init(allocator), .constants = std.ArrayList(u8).init(allocator), .called = std.StringHashMap(void).init(allocator), }; @@ -82,22 +119,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { defer tracy.end(); const writer = self.base.file.?.writer(); - try writer.writeAll(@embedFile("cbe.h")); - var includes = false; - if (self.need_stddef) { - try writer.writeAll("#include <stddef.h>\n"); - includes = true; - } - if (self.need_stdint) { - try writer.writeAll("#include <stdint.h>\n"); - includes = true; - } - if (includes) { - try writer.writeByte('\n'); - } - if (self.header.items.len > 0) { - try writer.print("{}\n", .{self.header.items}); - } + try self.header.flush(writer); if (self.constants.items.len > 0) { try writer.print("{}\n", .{self.constants.items}); } |
