authorgravatar for ascottcameron@gmail.comAlex Cameron <ascottcameron@gmail.com> 2020-11-13 00:50:02+11:00
committergravatar for ascottcameron@gmail.comAlex Cameron <ascottcameron@gmail.com> 2020-12-23 01:14:35+11:00
log40f0275e7cb7f3f4b2c382e5b457e714080c4cf2
tree87d919099376fe351c830fdb305d796e52d49b0b
parent43dbe86226fe89c6364fa0261297f1a9d8eb2a58

Implement emit-h


3 files changed, 183 insertions(+), 86 deletions(-)

src/Compilation.zig+58-19
...@@ -26,6 +26,8 @@ const Module = @import("Module.zig");...@@ -26,6 +26,8 @@ const Module = @import("Module.zig");
26const Cache = @import("Cache.zig");26const Cache = @import("Cache.zig");
27const stage1 = @import("stage1.zig");27const stage1 = @import("stage1.zig");
28const translate_c = @import("translate_c.zig");28const translate_c = @import("translate_c.zig");
29const c_codegen = @import("codegen/c.zig");
30const c_link = @import("link/C.zig");
29const ThreadPool = @import("ThreadPool.zig");31const ThreadPool = @import("ThreadPool.zig");
30const WaitGroup = @import("WaitGroup.zig");32const WaitGroup = @import("WaitGroup.zig");
3133
...@@ -131,6 +133,7 @@ emit_asm: ?EmitLoc,...@@ -131,6 +133,7 @@ emit_asm: ?EmitLoc,
131emit_llvm_ir: ?EmitLoc,133emit_llvm_ir: ?EmitLoc,
132emit_analysis: ?EmitLoc,134emit_analysis: ?EmitLoc,
133emit_docs: ?EmitLoc,135emit_docs: ?EmitLoc,
136c_header: ?c_link.Header,
134137
135pub const InnerError = Module.InnerError;138pub const InnerError = Module.InnerError;
136139
...@@ -895,10 +898,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -895,10 +898,6 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
895 };898 };
896 };899 };
897900
898 if (!use_llvm and options.emit_h != null) {
899 fatal("TODO implement support for -femit-h in the self-hosted backend", .{});
900 }
901
902 var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};901 var system_libs: std.StringArrayHashMapUnmanaged(void) = .{};
903 errdefer system_libs.deinit(gpa);902 errdefer system_libs.deinit(gpa);
904 try system_libs.ensureCapacity(gpa, options.system_libs.len);903 try system_libs.ensureCapacity(gpa, options.system_libs.len);
...@@ -975,6 +974,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -975,6 +974,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
975 .global_cache_directory = options.global_cache_directory,974 .global_cache_directory = options.global_cache_directory,
976 .bin_file = bin_file,975 .bin_file = bin_file,
977 .emit_h = options.emit_h,976 .emit_h = options.emit_h,
977 .c_header = if (!use_llvm and options.emit_h != null) c_link.Header.init(gpa) else null,
978 .emit_asm = options.emit_asm,978 .emit_asm = options.emit_asm,
979 .emit_llvm_ir = options.emit_llvm_ir,979 .emit_llvm_ir = options.emit_llvm_ir,
980 .emit_analysis = options.emit_analysis,980 .emit_analysis = options.emit_analysis,
...@@ -1286,6 +1286,20 @@ pub fn update(self: *Compilation) !void {...@@ -1286,6 +1286,20 @@ pub fn update(self: *Compilation) !void {
1286 module.root_scope.unload(self.gpa);1286 module.root_scope.unload(self.gpa);
1287 }1287 }
1288 }1288 }
1289
1290 // If we've chosen to emit a C header, flush the header to the disk.
1291 if (self.c_header) |header| {
1292 const header_path = self.emit_h.?;
1293 // If a directory has been provided, write the header there. Otherwise, just write it to the
1294 // cache directory.
1295 const header_dir = if (header_path.directory) |dir|
1296 dir.handle
1297 else
1298 self.local_cache_directory.handle;
1299 const header_file = try header_dir.createFile(header_path.basename, .{});
1300 defer header_file.close();
1301 try header.flush(header_file.writer());
1302 }
1289}1303}
12901304
1291/// Having the file open for writing is problematic as far as executing the1305/// 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...@@ -1385,6 +1399,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1385 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);1399 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
1386 defer c_comp_progress_node.end();1400 defer c_comp_progress_node.end();
13871401
1402 var arena = std.heap.ArenaAllocator.init(self.gpa);
1403 defer arena.deinit();
1404
1388 var wg = WaitGroup{};1405 var wg = WaitGroup{};
1389 defer wg.wait();1406 defer wg.wait();
13901407
...@@ -1432,22 +1449,44 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1432,22 +1449,44 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
14321449
1433 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());1450 assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits());
14341451
1435 self.bin_file.updateDecl(module, decl) catch |err| switch (err) {1452 self.bin_file.updateDecl(module, decl) catch |err| {
1436 error.OutOfMemory => return error.OutOfMemory,1453 switch (err) {
1437 error.AnalysisFail => {1454 error.OutOfMemory => return error.OutOfMemory,
1438 decl.analysis = .dependency_failure;1455 error.AnalysisFail => {
1439 },1456 decl.analysis = .dependency_failure;
1440 else => {1457 },
1441 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);1458 else => {
1442 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(1459 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
1443 module.gpa,1460 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1444 decl.src(),1461 module.gpa,
1445 "unable to codegen: {}",1462 decl.src(),
1446 .{@errorName(err)},1463 "unable to codegen: {}",
1447 ));1464 .{@errorName(err)},
1448 decl.analysis = .codegen_failure_retryable;1465 ));
1449 },1466 decl.analysis = .codegen_failure_retryable;
1467 },
1468 }
1469 return;
1450 };1470 };
1471
1472 if (self.c_header) |*header| {
1473 c_codegen.generateHeader(&arena, module, &header.*, decl) catch |err| switch (err) {
1474 error.OutOfMemory => return error.OutOfMemory,
1475 error.AnalysisFail => {
1476 decl.analysis = .dependency_failure;
1477 },
1478 else => {
1479 try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1);
1480 module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
1481 module.gpa,
1482 decl.src(),
1483 "unable to generate C header: {}",
1484 .{@errorName(err)},
1485 ));
1486 decl.analysis = .codegen_failure_retryable;
1487 },
1488 };
1489 }
1451 },1490 },
1452 },1491 },
1453 .analyze_decl => |decl| {1492 .analyze_decl => |decl| {
src/codegen/c.zig+83-47
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
22
3const link = @import("../link.zig");3const link = @import("../link.zig");
4const Module = @import("../Module.zig");4const Module = @import("../Module.zig");
5const Compilation = @import("../Compilation.zig");
56
6const Inst = @import("../ir.zig").Inst;7const Inst = @import("../ir.zig").Inst;
7const Value = @import("../value.zig").Value;8const Value = @import("../value.zig").Value;
...@@ -19,7 +20,7 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {...@@ -19,7 +20,7 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
19 return allocator.dupe(u8, name);20 return allocator.dupe(u8, name);
20}21}
2122
22fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {23fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void {
23 switch (T.zigTypeTag()) {24 switch (T.zigTypeTag()) {
24 .NoReturn => {25 .NoReturn => {
25 try writer.writeAll("zig_noreturn void");26 try writer.writeAll("zig_noreturn void");
...@@ -27,16 +28,16 @@ fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {...@@ -27,16 +28,16 @@ fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void {
27 .Void => try writer.writeAll("void"),28 .Void => try writer.writeAll("void"),
28 .Int => {29 .Int => {
29 if (T.tag() == .u8) {30 if (T.tag() == .u8) {
30 ctx.file.need_stdint = true;31 header.need_stdint = true;
31 try writer.writeAll("uint8_t");32 try writer.writeAll("uint8_t");
32 } else if (T.tag() == .usize) {33 } else if (T.tag() == .usize) {
33 ctx.file.need_stddef = true;34 header.need_stddef = true;
34 try writer.writeAll("size_t");35 try writer.writeAll("size_t");
35 } else {36 } else {
36 return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{});37 return ctx.fail(ctx.decl.src(), "TODO implement int types", .{});
37 }38 }
38 },39 },
39 else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}),40 else => |e| return ctx.fail(ctx.decl.src(), "TODO implement type {}", .{e}),
40 }41 }
41}42}
4243
...@@ -47,13 +48,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va...@@ -47,13 +48,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va
47 return writer.print("{}", .{val.toSignedInt()});48 return writer.print("{}", .{val.toSignedInt()});
48 return writer.print("{}", .{val.toUnsignedInt()});49 return writer.print("{}", .{val.toUnsignedInt()});
49 },50 },
50 else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}),51 else => |e| return ctx.fail(ctx.decl.src(), "TODO implement value {}", .{e}),
51 }52 }
52}53}
5354
54fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {55fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
55 const tv = decl.typed_value.most_recent.typed_value;56 const tv = decl.typed_value.most_recent.typed_value;
56 try renderType(ctx, writer, tv.ty.fnReturnType());57 try renderType(ctx, header, writer, tv.ty.fnReturnType());
57 // Use the child allocator directly, as we know the name can be freed before58 // Use the child allocator directly, as we know the name can be freed before
58 // the rest of the arena.59 // the rest of the arena.
59 const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name));60 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...@@ -68,7 +69,7 @@ fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl
68 if (index > 0) {69 if (index > 0) {
69 try writer.writeAll(", ");70 try writer.writeAll(", ");
70 }71 }
71 try renderType(ctx, writer, tv.ty.fnParamType(index));72 try renderType(ctx, header, writer, tv.ty.fnParamType(index));
72 try writer.print(" arg{}", .{index});73 try writer.print(" arg{}", .{index});
73 }74 }
74 }75 }
...@@ -83,6 +84,34 @@ pub fn generate(file: *C, decl: *Decl) !void {...@@ -83,6 +84,34 @@ pub fn generate(file: *C, decl: *Decl) !void {
83 }84 }
84}85}
8586
87pub fn generateHeader(
88 arena: *std.heap.ArenaAllocator,
89 module: *Module,
90 header: *C.Header,
91 decl: *Decl,
92) error{ AnalysisFail, OutOfMemory }!void {
93 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
94 .Fn => {
95 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
96 defer inst_map.deinit();
97 var ctx = Context{
98 .decl = decl,
99 .arena = arena,
100 .inst_map = &inst_map,
101 };
102 const writer = header.buf.writer();
103 renderFunctionSignature(&ctx, header, writer, decl) catch |err| {
104 if (err == error.AnalysisFail) {
105 try module.failed_decls.put(module.gpa, decl, ctx.error_msg);
106 }
107 return err;
108 };
109 try writer.writeAll(";\n");
110 },
111 else => {},
112 }
113}
114
86fn genArray(file: *C, decl: *Decl) !void {115fn genArray(file: *C, decl: *Decl) !void {
87 const tv = decl.typed_value.most_recent.typed_value;116 const tv = decl.typed_value.most_recent.typed_value;
88 // TODO: prevent inline asm constants from being emitted117 // TODO: prevent inline asm constants from being emitted
...@@ -102,12 +131,12 @@ fn genArray(file: *C, decl: *Decl) !void {...@@ -102,12 +131,12 @@ fn genArray(file: *C, decl: *Decl) !void {
102}131}
103132
104const Context = struct {133const Context = struct {
105 file: *C,
106 decl: *Decl,134 decl: *Decl,
107 inst_map: *std.AutoHashMap(*Inst, []u8),135 inst_map: *std.AutoHashMap(*Inst, []u8),
108 arena: *std.heap.ArenaAllocator,136 arena: *std.heap.ArenaAllocator,
109 argdex: usize = 0,137 argdex: usize = 0,
110 unnamed_index: usize = 0,138 unnamed_index: usize = 0,
139 error_msg: *Compilation.ErrorMsg = undefined,
111140
112 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {141 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
113 if (inst.cast(Inst.Constant)) |const_inst| {142 if (inst.cast(Inst.Constant)) |const_inst| {
...@@ -127,6 +156,11 @@ const Context = struct {...@@ -127,6 +156,11 @@ const Context = struct {
127 return val;156 return val;
128 }157 }
129158
159 fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
160 self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args);
161 return error.AnalysisFail;
162 }
163
130 fn deinit(self: *Context) void {164 fn deinit(self: *Context) void {
131 self.* = undefined;165 self.* = undefined;
132 }166 }
...@@ -141,14 +175,16 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -141,14 +175,16 @@ fn genFn(file: *C, decl: *Decl) !void {
141 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);175 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
142 defer inst_map.deinit();176 defer inst_map.deinit();
143 var ctx = Context{177 var ctx = Context{
144 .file = file,
145 .decl = decl,178 .decl = decl,
146 .arena = &arena,179 .arena = &arena,
147 .inst_map = &inst_map,180 .inst_map = &inst_map,
148 };181 };
149 defer ctx.deinit();182 defer {
183 file.error_msg = ctx.error_msg;
184 ctx.deinit();
185 }
150186
151 try renderFunctionSignature(&ctx, writer, decl);187 try renderFunctionSignature(&ctx, &file.header, writer, decl);
152188
153 try writer.writeAll(" {");189 try writer.writeAll(" {");
154190
...@@ -158,18 +194,18 @@ fn genFn(file: *C, decl: *Decl) !void {...@@ -158,18 +194,18 @@ fn genFn(file: *C, decl: *Decl) !void {
158 try writer.writeAll("\n");194 try writer.writeAll("\n");
159 for (instructions) |inst| {195 for (instructions) |inst| {
160 if (switch (inst.tag) {196 if (switch (inst.tag) {
161 .assembly => try genAsm(&ctx, inst.castTag(.assembly).?),197 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
162 .call => try genCall(&ctx, inst.castTag(.call).?),198 .call => try genCall(&ctx, file, inst.castTag(.call).?),
163 .add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"),199 .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"),
164 .sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"),200 .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"),
165 .ret => try genRet(&ctx, inst.castTag(.ret).?),201 .ret => try genRet(&ctx, inst.castTag(.ret).?),
166 .retvoid => try genRetVoid(&ctx),202 .retvoid => try genRetVoid(file),
167 .arg => try genArg(&ctx),203 .arg => try genArg(&ctx),
168 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),204 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
169 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),205 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
170 .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?),206 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
171 .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?),207 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
172 else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),208 else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
173 }) |name| {209 }) |name| {
174 try ctx.inst_map.putNoClobber(inst, name);210 try ctx.inst_map.putNoClobber(inst, name);
175 }211 }
...@@ -185,46 +221,46 @@ fn genArg(ctx: *Context) !?[]u8 {...@@ -185,46 +221,46 @@ fn genArg(ctx: *Context) !?[]u8 {
185 return name;221 return name;
186}222}
187223
188fn genRetVoid(ctx: *Context) !?[]u8 {224fn genRetVoid(file: *C) !?[]u8 {
189 try ctx.file.main.writer().print(indentation ++ "return;\n", .{});225 try file.main.writer().print(indentation ++ "return;\n", .{});
190 return null;226 return null;
191}227}
192228
193fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {229fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
194 return ctx.file.fail(ctx.decl.src(), "TODO return", .{});230 return ctx.fail(ctx.decl.src(), "TODO return", .{});
195}231}
196232
197fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {233fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
198 if (inst.base.isUnused())234 if (inst.base.isUnused())
199 return null;235 return null;
200 const op = inst.operand;236 const op = inst.operand;
201 const writer = ctx.file.main.writer();237 const writer = file.main.writer();
202 const name = try ctx.name();238 const name = try ctx.name();
203 const from = try ctx.resolveInst(inst.operand);239 const from = try ctx.resolveInst(inst.operand);
204 try writer.writeAll(indentation ++ "const ");240 try writer.writeAll(indentation ++ "const ");
205 try renderType(ctx, writer, inst.base.ty);241 try renderType(ctx, &file.header, writer, inst.base.ty);
206 try writer.print(" {} = (", .{name});242 try writer.print(" {} = (", .{name});
207 try renderType(ctx, writer, inst.base.ty);243 try renderType(ctx, &file.header, writer, inst.base.ty);
208 try writer.print("){};\n", .{from});244 try writer.print("){};\n", .{from});
209 return name;245 return name;
210}246}
211247
212fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {248fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {
213 if (inst.base.isUnused())249 if (inst.base.isUnused())
214 return null;250 return null;
215 const lhs = ctx.resolveInst(inst.lhs);251 const lhs = ctx.resolveInst(inst.lhs);
216 const rhs = ctx.resolveInst(inst.rhs);252 const rhs = ctx.resolveInst(inst.rhs);
217 const writer = ctx.file.main.writer();253 const writer = file.main.writer();
218 const name = try ctx.name();254 const name = try ctx.name();
219 try writer.writeAll(indentation ++ "const ");255 try writer.writeAll(indentation ++ "const ");
220 try renderType(ctx, writer, inst.base.ty);256 try renderType(ctx, &file.header, writer, inst.base.ty);
221 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });257 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
222 return name;258 return name;
223}259}
224260
225fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {261fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
226 const writer = ctx.file.main.writer();262 const writer = file.main.writer();
227 const header = ctx.file.header.writer();263 const header = file.header.buf.writer();
228 try writer.writeAll(indentation);264 try writer.writeAll(indentation);
229 if (inst.func.castTag(.constant)) |func_inst| {265 if (inst.func.castTag(.constant)) |func_inst| {
230 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {266 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
...@@ -235,9 +271,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {...@@ -235,9 +271,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
235 try writer.print("(void)", .{});271 try writer.print("(void)", .{});
236 }272 }
237 const tname = mem.spanZ(target.name);273 const tname = mem.spanZ(target.name);
238 if (ctx.file.called.get(tname) == null) {274 if (file.called.get(tname) == null) {
239 try ctx.file.called.put(tname, void{});275 try file.called.put(tname, void{});
240 try renderFunctionSignature(ctx, header, target);276 try renderFunctionSignature(ctx, &file.header, header, target);
241 try header.writeAll(";\n");277 try header.writeAll(";\n");
242 }278 }
243 try writer.print("{}(", .{tname});279 try writer.print("{}(", .{tname});
...@@ -256,10 +292,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {...@@ -256,10 +292,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 {
256 }292 }
257 try writer.writeAll(");\n");293 try writer.writeAll(");\n");
258 } else {294 } else {
259 return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{});295 return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{});
260 }296 }
261 } else {297 } else {
262 return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});298 return ctx.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});
263 }299 }
264 return null;300 return null;
265}301}
...@@ -274,20 +310,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {...@@ -274,20 +310,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
274 return null;310 return null;
275}311}
276312
277fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {313fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {
278 try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n");314 try file.main.writer().writeAll(indentation ++ "zig_unreachable();\n");
279 return null;315 return null;
280}316}
281317
282fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {318fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
283 const writer = ctx.file.main.writer();319 const writer = file.main.writer();
284 try writer.writeAll(indentation);320 try writer.writeAll(indentation);
285 for (as.inputs) |i, index| {321 for (as.inputs) |i, index| {
286 if (i[0] == '{' and i[i.len - 1] == '}') {322 if (i[0] == '{' and i[i.len - 1] == '}') {
287 const reg = i[1 .. i.len - 1];323 const reg = i[1 .. i.len - 1];
288 const arg = as.args[index];324 const arg = as.args[index];
289 try writer.writeAll("register ");325 try writer.writeAll("register ");
290 try renderType(ctx, writer, arg.ty);326 try renderType(ctx, &file.header, writer, arg.ty);
291 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });327 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
292 // TODO merge constant handling into inst_map as well328 // TODO merge constant handling into inst_map as well
293 if (arg.castTag(.constant)) |c| {329 if (arg.castTag(.constant)) |c| {
...@@ -296,17 +332,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {...@@ -296,17 +332,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 {
296 } else {332 } else {
297 const gop = try ctx.inst_map.getOrPut(arg);333 const gop = try ctx.inst_map.getOrPut(arg);
298 if (!gop.found_existing) {334 if (!gop.found_existing) {
299 return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});335 return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{});
300 }336 }
301 try writer.print("{};\n ", .{gop.entry.value});337 try writer.print("{};\n ", .{gop.entry.value});
302 }338 }
303 } else {339 } else {
304 return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});340 return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{});
305 }341 }
306 }342 }
307 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });343 try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
308 if (as.output) |o| {344 if (as.output) |o| {
309 return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{});345 return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{});
310 }346 }
311 if (as.inputs.len > 0) {347 if (as.inputs.len > 0) {
312 if (as.output == null) {348 if (as.output == null) {
src/link/C.zig+42-20
...@@ -13,15 +13,52 @@ const C = @This();...@@ -13,15 +13,52 @@ const C = @This();
1313
14pub const base_tag: File.Tag = .c;14pub const base_tag: File.Tag = .c;
1515
16pub const Header = struct {
17 buf: std.ArrayList(u8),
18 need_stddef: bool = false,
19 need_stdint: bool = false,
20
21 pub fn init(allocator: *Allocator) Header {
22 return .{
23 .buf = std.ArrayList(u8).init(allocator),
24 };
25 }
26
27 pub fn flush(self: *const Header, writer: anytype) !void {
28 const tracy = trace(@src());
29 defer tracy.end();
30
31 try writer.writeAll(@embedFile("cbe.h"));
32 var includes = false;
33 if (self.need_stddef) {
34 try writer.writeAll("#include <stddef.h>\n");
35 includes = true;
36 }
37 if (self.need_stdint) {
38 try writer.writeAll("#include <stdint.h>\n");
39 includes = true;
40 }
41 if (includes) {
42 try writer.writeByte('\n');
43 }
44 if (self.buf.items.len > 0) {
45 try writer.print("{}\n", .{self.buf.items});
46 }
47 }
48
49 pub fn deinit(self: *Header) void {
50 self.buf.deinit();
51 self.* = undefined;
52 }
53};
54
16base: File,55base: File,
1756
18header: std.ArrayList(u8),57header: Header,
19constants: std.ArrayList(u8),58constants: std.ArrayList(u8),
20main: std.ArrayList(u8),59main: std.ArrayList(u8),
2160
22called: std.StringHashMap(void),61called: std.StringHashMap(void),
23need_stddef: bool = false,
24need_stdint: bool = false,
25error_msg: *Compilation.ErrorMsg = undefined,62error_msg: *Compilation.ErrorMsg = undefined,
2663
27pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C {64pub 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...@@ -44,7 +81,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio
44 .allocator = allocator,81 .allocator = allocator,
45 },82 },
46 .main = std.ArrayList(u8).init(allocator),83 .main = std.ArrayList(u8).init(allocator),
47 .header = std.ArrayList(u8).init(allocator),84 .header = Header.init(allocator),
48 .constants = std.ArrayList(u8).init(allocator),85 .constants = std.ArrayList(u8).init(allocator),
49 .called = std.StringHashMap(void).init(allocator),86 .called = std.StringHashMap(void).init(allocator),
50 };87 };
...@@ -82,22 +119,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {...@@ -82,22 +119,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
82 defer tracy.end();119 defer tracy.end();
83120
84 const writer = self.base.file.?.writer();121 const writer = self.base.file.?.writer();
85 try writer.writeAll(@embedFile("cbe.h"));122 try self.header.flush(writer);
86 var includes = false;
87 if (self.need_stddef) {
88 try writer.writeAll("#include <stddef.h>\n");
89 includes = true;
90 }
91 if (self.need_stdint) {
92 try writer.writeAll("#include <stdint.h>\n");
93 includes = true;
94 }
95 if (includes) {
96 try writer.writeByte('\n');
97 }
98 if (self.header.items.len > 0) {
99 try writer.print("{}\n", .{self.header.items});
100 }
101 if (self.constants.items.len > 0) {123 if (self.constants.items.len > 0) {
102 try writer.print("{}\n", .{self.constants.items});124 try writer.print("{}\n", .{self.constants.items});
103 }125 }