| author | |
| committer | |
| log | 03113d92467a99658c2606a21821b28b19dfdded |
| tree | 9897504c15a08c1ef9165bee3b3299274eb171c9 |
| parent | ba2f2e139306618b8beaa8e833b1f9845309df73 |
| parent | 9849e894d52dd7b9aedc149011cb19d2bccaab70 |
| signature |
Implement emit-h7 files changed, 328 insertions(+), 104 deletions(-)
src/Compilation.zig+69-24| ... | @@ -26,6 +26,8 @@ const Module = @import("Module.zig"); | ... | @@ -26,6 +26,8 @@ const Module = @import("Module.zig"); |
| 26 | const Cache = @import("Cache.zig"); | 26 | const Cache = @import("Cache.zig"); |
| 27 | const stage1 = @import("stage1.zig"); | 27 | const stage1 = @import("stage1.zig"); |
| 28 | const translate_c = @import("translate_c.zig"); | 28 | const translate_c = @import("translate_c.zig"); |
| 29 | const c_codegen = @import("codegen/c.zig"); | ||
| 30 | const c_link = @import("link/C.zig"); | ||
| 29 | const ThreadPool = @import("ThreadPool.zig"); | 31 | const ThreadPool = @import("ThreadPool.zig"); |
| 30 | const WaitGroup = @import("WaitGroup.zig"); | 32 | const WaitGroup = @import("WaitGroup.zig"); |
| 31 | 33 | ||
| ... | @@ -126,12 +128,13 @@ test_filter: ?[]const u8, | ... | @@ -126,12 +128,13 @@ test_filter: ?[]const u8, |
| 126 | test_name_prefix: ?[]const u8, | 128 | test_name_prefix: ?[]const u8, |
| 127 | test_evented_io: bool, | 129 | test_evented_io: bool, |
| 128 | 130 | ||
| 129 | emit_h: ?EmitLoc, | ||
| 130 | emit_asm: ?EmitLoc, | 131 | emit_asm: ?EmitLoc, |
| 131 | emit_llvm_ir: ?EmitLoc, | 132 | emit_llvm_ir: ?EmitLoc, |
| 132 | emit_analysis: ?EmitLoc, | 133 | emit_analysis: ?EmitLoc, |
| 133 | emit_docs: ?EmitLoc, | 134 | emit_docs: ?EmitLoc, |
| 134 | 135 | ||
| 136 | c_header: ?c_link.Header, | ||
| 137 | |||
| 135 | pub const InnerError = Module.InnerError; | 138 | pub const InnerError = Module.InnerError; |
| 136 | 139 | ||
| 137 | pub const CRTFile = struct { | 140 | pub const CRTFile = struct { |
| ... | @@ -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 | }; |
| 897 | 900 | ||
| 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); |
| ... | @@ -974,7 +973,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | ... | @@ -974,7 +973,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 974 | .local_cache_directory = options.local_cache_directory, | 973 | .local_cache_directory = options.local_cache_directory, |
| 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 | .c_header = if (!use_llvm and options.emit_h != null) c_link.Header.init(gpa, options.emit_h) else null, |
| 978 | .emit_asm = options.emit_asm, | 977 | .emit_asm = options.emit_asm, |
| 979 | .emit_llvm_ir = options.emit_llvm_ir, | 978 | .emit_llvm_ir = options.emit_llvm_ir, |
| 980 | .emit_analysis = options.emit_analysis, | 979 | .emit_analysis = options.emit_analysis, |
| ... | @@ -1185,6 +1184,10 @@ pub fn destroy(self: *Compilation) void { | ... | @@ -1185,6 +1184,10 @@ pub fn destroy(self: *Compilation) void { |
| 1185 | } | 1184 | } |
| 1186 | self.failed_c_objects.deinit(gpa); | 1185 | self.failed_c_objects.deinit(gpa); |
| 1187 | 1186 | ||
| 1187 | if (self.c_header) |*header| { | ||
| 1188 | header.deinit(); | ||
| 1189 | } | ||
| 1190 | |||
| 1188 | self.cache_parent.manifest_dir.close(); | 1191 | self.cache_parent.manifest_dir.close(); |
| 1189 | if (self.owned_link_dir) |*dir| dir.close(); | 1192 | if (self.owned_link_dir) |*dir| dir.close(); |
| 1190 | 1193 | ||
| ... | @@ -1286,6 +1289,20 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1286,6 +1289,20 @@ pub fn update(self: *Compilation) !void { |
| 1286 | module.root_scope.unload(self.gpa); | 1289 | module.root_scope.unload(self.gpa); |
| 1287 | } | 1290 | } |
| 1288 | } | 1291 | } |
| 1292 | |||
| 1293 | // If we've chosen to emit a C header, flush the header to the disk. | ||
| 1294 | if (self.c_header) |header| { | ||
| 1295 | const header_path = header.emit_loc.?; | ||
| 1296 | // If a directory has been provided, write the header there. Otherwise, just write it to the | ||
| 1297 | // cache directory. | ||
| 1298 | const header_dir = if (header_path.directory) |dir| | ||
| 1299 | dir.handle | ||
| 1300 | else | ||
| 1301 | self.local_cache_directory.handle; | ||
| 1302 | const header_file = try header_dir.createFile(header_path.basename, .{}); | ||
| 1303 | defer header_file.close(); | ||
| 1304 | try header.flush(header_file.writer()); | ||
| 1305 | } | ||
| 1289 | } | 1306 | } |
| 1290 | 1307 | ||
| 1291 | /// Having the file open for writing is problematic as far as executing the | 1308 | /// Having the file open for writing is problematic as far as executing the |
| ... | @@ -1385,6 +1402,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor | ... | @@ -1385,6 +1402,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); | 1402 | var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len); |
| 1386 | defer c_comp_progress_node.end(); | 1403 | defer c_comp_progress_node.end(); |
| 1387 | 1404 | ||
| 1405 | var arena = std.heap.ArenaAllocator.init(self.gpa); | ||
| 1406 | defer arena.deinit(); | ||
| 1407 | |||
| 1388 | var wg = WaitGroup{}; | 1408 | var wg = WaitGroup{}; |
| 1389 | defer wg.wait(); | 1409 | defer wg.wait(); |
| 1390 | 1410 | ||
| ... | @@ -1432,22 +1452,44 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor | ... | @@ -1432,22 +1452,44 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1432 | 1452 | ||
| 1433 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | 1453 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); |
| 1434 | 1454 | ||
| 1435 | self.bin_file.updateDecl(module, decl) catch |err| switch (err) { | 1455 | self.bin_file.updateDecl(module, decl) catch |err| { |
| 1436 | error.OutOfMemory => return error.OutOfMemory, | 1456 | switch (err) { |
| 1437 | error.AnalysisFail => { | 1457 | error.OutOfMemory => return error.OutOfMemory, |
| 1438 | decl.analysis = .dependency_failure; | 1458 | error.AnalysisFail => { |
| 1439 | }, | 1459 | decl.analysis = .dependency_failure; |
| 1440 | else => { | 1460 | }, |
| 1441 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); | 1461 | else => { |
| 1442 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 1462 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); |
| 1443 | module.gpa, | 1463 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1444 | decl.src(), | 1464 | module.gpa, |
| 1445 | "unable to codegen: {}", | 1465 | decl.src(), |
| 1446 | .{@errorName(err)}, | 1466 | "unable to codegen: {}", |
| 1447 | )); | 1467 | .{@errorName(err)}, |
| 1448 | decl.analysis = .codegen_failure_retryable; | 1468 | )); |
| 1449 | }, | 1469 | decl.analysis = .codegen_failure_retryable; |
| 1470 | }, | ||
| 1471 | } | ||
| 1472 | return; | ||
| 1450 | }; | 1473 | }; |
| 1474 | |||
| 1475 | if (self.c_header) |*header| { | ||
| 1476 | c_codegen.generateHeader(&arena, module, &header.*, decl) catch |err| switch (err) { | ||
| 1477 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1478 | error.AnalysisFail => { | ||
| 1479 | decl.analysis = .dependency_failure; | ||
| 1480 | }, | ||
| 1481 | else => { | ||
| 1482 | try module.failed_decls.ensureCapacity(module.gpa, module.failed_decls.items().len + 1); | ||
| 1483 | module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | ||
| 1484 | module.gpa, | ||
| 1485 | decl.src(), | ||
| 1486 | "unable to generate C header: {}", | ||
| 1487 | .{@errorName(err)}, | ||
| 1488 | )); | ||
| 1489 | decl.analysis = .codegen_failure_retryable; | ||
| 1490 | }, | ||
| 1491 | }; | ||
| 1492 | } | ||
| 1451 | }, | 1493 | }, |
| 1452 | }, | 1494 | }, |
| 1453 | .analyze_decl => |decl| { | 1495 | .analyze_decl => |decl| { |
| ... | @@ -2913,7 +2955,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -2913,7 +2955,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 2913 | man.hash.add(comp.bin_file.options.function_sections); | 2955 | man.hash.add(comp.bin_file.options.function_sections); |
| 2914 | man.hash.add(comp.bin_file.options.is_test); | 2956 | man.hash.add(comp.bin_file.options.is_test); |
| 2915 | man.hash.add(comp.bin_file.options.emit != null); | 2957 | man.hash.add(comp.bin_file.options.emit != null); |
| 2916 | man.hash.addOptionalEmitLoc(comp.emit_h); | 2958 | man.hash.add(comp.c_header != null); |
| 2959 | if (comp.c_header) |header| { | ||
| 2960 | man.hash.addEmitLoc(header.emit_loc.?); | ||
| 2961 | } | ||
| 2917 | man.hash.addOptionalEmitLoc(comp.emit_asm); | 2962 | man.hash.addOptionalEmitLoc(comp.emit_asm); |
| 2918 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); | 2963 | man.hash.addOptionalEmitLoc(comp.emit_llvm_ir); |
| 2919 | man.hash.addOptionalEmitLoc(comp.emit_analysis); | 2964 | man.hash.addOptionalEmitLoc(comp.emit_analysis); |
| ... | @@ -3012,10 +3057,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node | ... | @@ -3012,10 +3057,10 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node |
| 3012 | }); | 3057 | }); |
| 3013 | break :blk try directory.join(arena, &[_][]const u8{bin_basename}); | 3058 | break :blk try directory.join(arena, &[_][]const u8{bin_basename}); |
| 3014 | } else ""; | 3059 | } else ""; |
| 3015 | if (comp.emit_h != null) { | 3060 | if (comp.c_header != null) { |
| 3016 | log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{}); | 3061 | log.warn("-femit-h is not available in the stage1 backend; no .h file will be produced", .{}); |
| 3017 | } | 3062 | } |
| 3018 | const emit_h_path = try stage1LocPath(arena, comp.emit_h, directory); | 3063 | const emit_h_path = try stage1LocPath(arena, if (comp.c_header) |header| header.emit_loc else null, directory); |
| 3019 | const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory); | 3064 | const emit_asm_path = try stage1LocPath(arena, comp.emit_asm, directory); |
| 3020 | const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory); | 3065 | const emit_llvm_ir_path = try stage1LocPath(arena, comp.emit_llvm_ir, directory); |
| 3021 | const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory); | 3066 | const emit_analysis_path = try stage1LocPath(arena, comp.emit_analysis, directory); |
src/codegen/c.zig+87-47| ... | @@ -2,6 +2,7 @@ const std = @import("std"); | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | 2 | ||
| 3 | const link = @import("../link.zig"); | 3 | const link = @import("../link.zig"); |
| 4 | const Module = @import("../Module.zig"); | 4 | const Module = @import("../Module.zig"); |
| 5 | const Compilation = @import("../Compilation.zig"); | ||
| 5 | 6 | ||
| 6 | const Inst = @import("../ir.zig").Inst; | 7 | const Inst = @import("../ir.zig").Inst; |
| 7 | const Value = @import("../value.zig").Value; | 8 | const Value = @import("../value.zig").Value; |
| ... | @@ -19,24 +20,28 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 { | ... | @@ -19,24 +20,28 @@ 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 | } |
| 21 | 22 | ||
| 22 | fn renderType(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type) !void { | 23 | fn 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"); |
| 26 | }, | 27 | }, |
| 27 | .Void => try writer.writeAll("void"), | 28 | .Void => try writer.writeAll("void"), |
| 29 | .Bool => try writer.writeAll("bool"), | ||
| 28 | .Int => { | 30 | .Int => { |
| 29 | if (T.tag() == .u8) { | 31 | if (T.tag() == .u8) { |
| 30 | ctx.file.need_stdint = true; | 32 | header.need_stdint = true; |
| 31 | try writer.writeAll("uint8_t"); | 33 | try writer.writeAll("uint8_t"); |
| 34 | } else if (T.tag() == .u32) { | ||
| 35 | header.need_stdint = true; | ||
| 36 | try writer.writeAll("uint32_t"); | ||
| 32 | } else if (T.tag() == .usize) { | 37 | } else if (T.tag() == .usize) { |
| 33 | ctx.file.need_stddef = true; | 38 | header.need_stddef = true; |
| 34 | try writer.writeAll("size_t"); | 39 | try writer.writeAll("size_t"); |
| 35 | } else { | 40 | } else { |
| 36 | return ctx.file.fail(ctx.decl.src(), "TODO implement int types", .{}); | 41 | return ctx.fail(ctx.decl.src(), "TODO implement int type {}", .{T}); |
| 37 | } | 42 | } |
| 38 | }, | 43 | }, |
| 39 | else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement type {}", .{e}), | 44 | else => |e| return ctx.fail(ctx.decl.src(), "TODO implement type {}", .{e}), |
| 40 | } | 45 | } |
| 41 | } | 46 | } |
| 42 | 47 | ||
| ... | @@ -47,13 +52,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va | ... | @@ -47,13 +52,13 @@ fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Va |
| 47 | return writer.print("{}", .{val.toSignedInt()}); | 52 | return writer.print("{}", .{val.toSignedInt()}); |
| 48 | return writer.print("{}", .{val.toUnsignedInt()}); | 53 | return writer.print("{}", .{val.toUnsignedInt()}); |
| 49 | }, | 54 | }, |
| 50 | else => |e| return ctx.file.fail(ctx.decl.src(), "TODO implement value {}", .{e}), | 55 | else => |e| return ctx.fail(ctx.decl.src(), "TODO implement value {}", .{e}), |
| 51 | } | 56 | } |
| 52 | } | 57 | } |
| 53 | 58 | ||
| 54 | fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { | 59 | fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { |
| 55 | const tv = decl.typed_value.most_recent.typed_value; | 60 | const tv = decl.typed_value.most_recent.typed_value; |
| 56 | try renderType(ctx, writer, tv.ty.fnReturnType()); | 61 | try renderType(ctx, header, writer, tv.ty.fnReturnType()); |
| 57 | // Use the child allocator directly, as we know the name can be freed before | 62 | // Use the child allocator directly, as we know the name can be freed before |
| 58 | // the rest of the arena. | 63 | // the rest of the arena. |
| 59 | const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); | 64 | const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name)); |
| ... | @@ -68,7 +73,7 @@ fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl | ... | @@ -68,7 +73,7 @@ fn renderFunctionSignature(ctx: *Context, writer: std.ArrayList(u8).Writer, decl |
| 68 | if (index > 0) { | 73 | if (index > 0) { |
| 69 | try writer.writeAll(", "); | 74 | try writer.writeAll(", "); |
| 70 | } | 75 | } |
| 71 | try renderType(ctx, writer, tv.ty.fnParamType(index)); | 76 | try renderType(ctx, header, writer, tv.ty.fnParamType(index)); |
| 72 | try writer.print(" arg{}", .{index}); | 77 | try writer.print(" arg{}", .{index}); |
| 73 | } | 78 | } |
| 74 | } | 79 | } |
| ... | @@ -83,6 +88,34 @@ pub fn generate(file: *C, decl: *Decl) !void { | ... | @@ -83,6 +88,34 @@ pub fn generate(file: *C, decl: *Decl) !void { |
| 83 | } | 88 | } |
| 84 | } | 89 | } |
| 85 | 90 | ||
| 91 | pub fn generateHeader( | ||
| 92 | arena: *std.heap.ArenaAllocator, | ||
| 93 | module: *Module, | ||
| 94 | header: *C.Header, | ||
| 95 | decl: *Decl, | ||
| 96 | ) error{ AnalysisFail, OutOfMemory }!void { | ||
| 97 | switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { | ||
| 98 | .Fn => { | ||
| 99 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); | ||
| 100 | defer inst_map.deinit(); | ||
| 101 | var ctx = Context{ | ||
| 102 | .decl = decl, | ||
| 103 | .arena = arena, | ||
| 104 | .inst_map = &inst_map, | ||
| 105 | }; | ||
| 106 | const writer = header.buf.writer(); | ||
| 107 | renderFunctionSignature(&ctx, header, writer, decl) catch |err| { | ||
| 108 | if (err == error.AnalysisFail) { | ||
| 109 | try module.failed_decls.put(module.gpa, decl, ctx.error_msg); | ||
| 110 | } | ||
| 111 | return err; | ||
| 112 | }; | ||
| 113 | try writer.writeAll(";\n"); | ||
| 114 | }, | ||
| 115 | else => {}, | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 86 | fn genArray(file: *C, decl: *Decl) !void { | 119 | fn genArray(file: *C, decl: *Decl) !void { |
| 87 | const tv = decl.typed_value.most_recent.typed_value; | 120 | const tv = decl.typed_value.most_recent.typed_value; |
| 88 | // TODO: prevent inline asm constants from being emitted | 121 | // TODO: prevent inline asm constants from being emitted |
| ... | @@ -102,12 +135,12 @@ fn genArray(file: *C, decl: *Decl) !void { | ... | @@ -102,12 +135,12 @@ fn genArray(file: *C, decl: *Decl) !void { |
| 102 | } | 135 | } |
| 103 | 136 | ||
| 104 | const Context = struct { | 137 | const Context = struct { |
| 105 | file: *C, | ||
| 106 | decl: *Decl, | 138 | decl: *Decl, |
| 107 | inst_map: *std.AutoHashMap(*Inst, []u8), | 139 | inst_map: *std.AutoHashMap(*Inst, []u8), |
| 108 | arena: *std.heap.ArenaAllocator, | 140 | arena: *std.heap.ArenaAllocator, |
| 109 | argdex: usize = 0, | 141 | argdex: usize = 0, |
| 110 | unnamed_index: usize = 0, | 142 | unnamed_index: usize = 0, |
| 143 | error_msg: *Compilation.ErrorMsg = undefined, | ||
| 111 | 144 | ||
| 112 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { | 145 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { |
| 113 | if (inst.cast(Inst.Constant)) |const_inst| { | 146 | if (inst.cast(Inst.Constant)) |const_inst| { |
| ... | @@ -127,6 +160,11 @@ const Context = struct { | ... | @@ -127,6 +160,11 @@ const Context = struct { |
| 127 | return val; | 160 | return val; |
| 128 | } | 161 | } |
| 129 | 162 | ||
| 163 | fn fail(self: *Context, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { | ||
| 164 | self.error_msg = try Compilation.ErrorMsg.create(self.arena.child_allocator, src, format, args); | ||
| 165 | return error.AnalysisFail; | ||
| 166 | } | ||
| 167 | |||
| 130 | fn deinit(self: *Context) void { | 168 | fn deinit(self: *Context) void { |
| 131 | self.* = undefined; | 169 | self.* = undefined; |
| 132 | } | 170 | } |
| ... | @@ -141,14 +179,16 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -141,14 +179,16 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 141 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); | 179 | var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator); |
| 142 | defer inst_map.deinit(); | 180 | defer inst_map.deinit(); |
| 143 | var ctx = Context{ | 181 | var ctx = Context{ |
| 144 | .file = file, | ||
| 145 | .decl = decl, | 182 | .decl = decl, |
| 146 | .arena = &arena, | 183 | .arena = &arena, |
| 147 | .inst_map = &inst_map, | 184 | .inst_map = &inst_map, |
| 148 | }; | 185 | }; |
| 149 | defer ctx.deinit(); | 186 | defer { |
| 187 | file.error_msg = ctx.error_msg; | ||
| 188 | ctx.deinit(); | ||
| 189 | } | ||
| 150 | 190 | ||
| 151 | try renderFunctionSignature(&ctx, writer, decl); | 191 | try renderFunctionSignature(&ctx, &file.header, writer, decl); |
| 152 | 192 | ||
| 153 | try writer.writeAll(" {"); | 193 | try writer.writeAll(" {"); |
| 154 | 194 | ||
| ... | @@ -158,18 +198,18 @@ fn genFn(file: *C, decl: *Decl) !void { | ... | @@ -158,18 +198,18 @@ fn genFn(file: *C, decl: *Decl) !void { |
| 158 | try writer.writeAll("\n"); | 198 | try writer.writeAll("\n"); |
| 159 | for (instructions) |inst| { | 199 | for (instructions) |inst| { |
| 160 | if (switch (inst.tag) { | 200 | if (switch (inst.tag) { |
| 161 | .assembly => try genAsm(&ctx, inst.castTag(.assembly).?), | 201 | .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?), |
| 162 | .call => try genCall(&ctx, inst.castTag(.call).?), | 202 | .call => try genCall(&ctx, file, inst.castTag(.call).?), |
| 163 | .add => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "+"), | 203 | .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"), |
| 164 | .sub => try genBinOp(&ctx, inst.cast(Inst.BinOp).?, "-"), | 204 | .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"), |
| 165 | .ret => try genRet(&ctx, inst.castTag(.ret).?), | 205 | .ret => try genRet(&ctx, inst.castTag(.ret).?), |
| 166 | .retvoid => try genRetVoid(&ctx), | 206 | .retvoid => try genRetVoid(file), |
| 167 | .arg => try genArg(&ctx), | 207 | .arg => try genArg(&ctx), |
| 168 | .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), | 208 | .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?), |
| 169 | .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?), | 209 | .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?), |
| 170 | .unreach => try genUnreach(&ctx, inst.castTag(.unreach).?), | 210 | .unreach => try genUnreach(file, inst.castTag(.unreach).?), |
| 171 | .intcast => try genIntCast(&ctx, inst.castTag(.intcast).?), | 211 | .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?), |
| 172 | else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}), | 212 | else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}), |
| 173 | }) |name| { | 213 | }) |name| { |
| 174 | try ctx.inst_map.putNoClobber(inst, name); | 214 | try ctx.inst_map.putNoClobber(inst, name); |
| 175 | } | 215 | } |
| ... | @@ -185,46 +225,46 @@ fn genArg(ctx: *Context) !?[]u8 { | ... | @@ -185,46 +225,46 @@ fn genArg(ctx: *Context) !?[]u8 { |
| 185 | return name; | 225 | return name; |
| 186 | } | 226 | } |
| 187 | 227 | ||
| 188 | fn genRetVoid(ctx: *Context) !?[]u8 { | 228 | fn genRetVoid(file: *C) !?[]u8 { |
| 189 | try ctx.file.main.writer().print(indentation ++ "return;\n", .{}); | 229 | try file.main.writer().print(indentation ++ "return;\n", .{}); |
| 190 | return null; | 230 | return null; |
| 191 | } | 231 | } |
| 192 | 232 | ||
| 193 | fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | 233 | fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { |
| 194 | return ctx.file.fail(ctx.decl.src(), "TODO return", .{}); | 234 | return ctx.fail(ctx.decl.src(), "TODO return", .{}); |
| 195 | } | 235 | } |
| 196 | 236 | ||
| 197 | fn genIntCast(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | 237 | fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 198 | if (inst.base.isUnused()) | 238 | if (inst.base.isUnused()) |
| 199 | return null; | 239 | return null; |
| 200 | const op = inst.operand; | 240 | const op = inst.operand; |
| 201 | const writer = ctx.file.main.writer(); | 241 | const writer = file.main.writer(); |
| 202 | const name = try ctx.name(); | 242 | const name = try ctx.name(); |
| 203 | const from = try ctx.resolveInst(inst.operand); | 243 | const from = try ctx.resolveInst(inst.operand); |
| 204 | try writer.writeAll(indentation ++ "const "); | 244 | try writer.writeAll(indentation ++ "const "); |
| 205 | try renderType(ctx, writer, inst.base.ty); | 245 | try renderType(ctx, &file.header, writer, inst.base.ty); |
| 206 | try writer.print(" {} = (", .{name}); | 246 | try writer.print(" {} = (", .{name}); |
| 207 | try renderType(ctx, writer, inst.base.ty); | 247 | try renderType(ctx, &file.header, writer, inst.base.ty); |
| 208 | try writer.print("){};\n", .{from}); | 248 | try writer.print("){};\n", .{from}); |
| 209 | return name; | 249 | return name; |
| 210 | } | 250 | } |
| 211 | 251 | ||
| 212 | fn genBinOp(ctx: *Context, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { | 252 | fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { |
| 213 | if (inst.base.isUnused()) | 253 | if (inst.base.isUnused()) |
| 214 | return null; | 254 | return null; |
| 215 | const lhs = ctx.resolveInst(inst.lhs); | 255 | const lhs = ctx.resolveInst(inst.lhs); |
| 216 | const rhs = ctx.resolveInst(inst.rhs); | 256 | const rhs = ctx.resolveInst(inst.rhs); |
| 217 | const writer = ctx.file.main.writer(); | 257 | const writer = file.main.writer(); |
| 218 | const name = try ctx.name(); | 258 | const name = try ctx.name(); |
| 219 | try writer.writeAll(indentation ++ "const "); | 259 | try writer.writeAll(indentation ++ "const "); |
| 220 | try renderType(ctx, writer, inst.base.ty); | 260 | try renderType(ctx, &file.header, writer, inst.base.ty); |
| 221 | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); | 261 | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); |
| 222 | return name; | 262 | return name; |
| 223 | } | 263 | } |
| 224 | 264 | ||
| 225 | fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | 265 | fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 226 | const writer = ctx.file.main.writer(); | 266 | const writer = file.main.writer(); |
| 227 | const header = ctx.file.header.writer(); | 267 | const header = file.header.buf.writer(); |
| 228 | try writer.writeAll(indentation); | 268 | try writer.writeAll(indentation); |
| 229 | if (inst.func.castTag(.constant)) |func_inst| { | 269 | if (inst.func.castTag(.constant)) |func_inst| { |
| 230 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { | 270 | if (func_inst.val.cast(Value.Payload.Function)) |func_val| { |
| ... | @@ -235,9 +275,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | ... | @@ -235,9 +275,9 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 235 | try writer.print("(void)", .{}); | 275 | try writer.print("(void)", .{}); |
| 236 | } | 276 | } |
| 237 | const tname = mem.spanZ(target.name); | 277 | const tname = mem.spanZ(target.name); |
| 238 | if (ctx.file.called.get(tname) == null) { | 278 | if (file.called.get(tname) == null) { |
| 239 | try ctx.file.called.put(tname, void{}); | 279 | try file.called.put(tname, void{}); |
| 240 | try renderFunctionSignature(ctx, header, target); | 280 | try renderFunctionSignature(ctx, &file.header, header, target); |
| 241 | try header.writeAll(";\n"); | 281 | try header.writeAll(";\n"); |
| 242 | } | 282 | } |
| 243 | try writer.print("{}(", .{tname}); | 283 | try writer.print("{}(", .{tname}); |
| ... | @@ -256,10 +296,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { | ... | @@ -256,10 +296,10 @@ fn genCall(ctx: *Context, inst: *Inst.Call) !?[]u8 { |
| 256 | } | 296 | } |
| 257 | try writer.writeAll(");\n"); | 297 | try writer.writeAll(");\n"); |
| 258 | } else { | 298 | } else { |
| 259 | return ctx.file.fail(ctx.decl.src(), "TODO non-function call target?", .{}); | 299 | return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{}); |
| 260 | } | 300 | } |
| 261 | } else { | 301 | } else { |
| 262 | return ctx.file.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); | 302 | return ctx.fail(ctx.decl.src(), "TODO non-constant call inst?", .{}); |
| 263 | } | 303 | } |
| 264 | return null; | 304 | return null; |
| 265 | } | 305 | } |
| ... | @@ -274,20 +314,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | ... | @@ -274,20 +314,20 @@ fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 274 | return null; | 314 | return null; |
| 275 | } | 315 | } |
| 276 | 316 | ||
| 277 | fn genUnreach(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | 317 | fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 { |
| 278 | try ctx.file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); | 318 | try file.main.writer().writeAll(indentation ++ "zig_unreachable();\n"); |
| 279 | return null; | 319 | return null; |
| 280 | } | 320 | } |
| 281 | 321 | ||
| 282 | fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { | 322 | fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { |
| 283 | const writer = ctx.file.main.writer(); | 323 | const writer = file.main.writer(); |
| 284 | try writer.writeAll(indentation); | 324 | try writer.writeAll(indentation); |
| 285 | for (as.inputs) |i, index| { | 325 | for (as.inputs) |i, index| { |
| 286 | if (i[0] == '{' and i[i.len - 1] == '}') { | 326 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 287 | const reg = i[1 .. i.len - 1]; | 327 | const reg = i[1 .. i.len - 1]; |
| 288 | const arg = as.args[index]; | 328 | const arg = as.args[index]; |
| 289 | try writer.writeAll("register "); | 329 | try writer.writeAll("register "); |
| 290 | try renderType(ctx, writer, arg.ty); | 330 | try renderType(ctx, &file.header, writer, arg.ty); |
| 291 | try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); | 331 | try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); |
| 292 | // TODO merge constant handling into inst_map as well | 332 | // TODO merge constant handling into inst_map as well |
| 293 | if (arg.castTag(.constant)) |c| { | 333 | if (arg.castTag(.constant)) |c| { |
| ... | @@ -296,17 +336,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { | ... | @@ -296,17 +336,17 @@ fn genAsm(ctx: *Context, as: *Inst.Assembly) !?[]u8 { |
| 296 | } else { | 336 | } else { |
| 297 | const gop = try ctx.inst_map.getOrPut(arg); | 337 | const gop = try ctx.inst_map.getOrPut(arg); |
| 298 | if (!gop.found_existing) { | 338 | if (!gop.found_existing) { |
| 299 | return ctx.file.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); | 339 | return ctx.fail(ctx.decl.src(), "Internal error in C backend: asm argument not found in inst_map", .{}); |
| 300 | } | 340 | } |
| 301 | try writer.print("{};\n ", .{gop.entry.value}); | 341 | try writer.print("{};\n ", .{gop.entry.value}); |
| 302 | } | 342 | } |
| 303 | } else { | 343 | } else { |
| 304 | return ctx.file.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); | 344 | return ctx.fail(ctx.decl.src(), "TODO non-explicit inline asm regs", .{}); |
| 305 | } | 345 | } |
| 306 | } | 346 | } |
| 307 | try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); | 347 | try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source }); |
| 308 | if (as.output) |o| { | 348 | if (as.output) |o| { |
| 309 | return ctx.file.fail(ctx.decl.src(), "TODO inline asm output", .{}); | 349 | return ctx.fail(ctx.decl.src(), "TODO inline asm output", .{}); |
| 310 | } | 350 | } |
| 311 | if (as.inputs.len > 0) { | 351 | if (as.inputs.len > 0) { |
| 312 | if (as.output == null) { | 352 | if (as.output == null) { |
src/link/C.zig+45-18| ... | @@ -13,15 +13,54 @@ const C = @This(); | ... | @@ -13,15 +13,54 @@ const C = @This(); |
| 13 | 13 | ||
| 14 | pub const base_tag: File.Tag = .c; | 14 | pub const base_tag: File.Tag = .c; |
| 15 | 15 | ||
| 16 | pub const Header = struct { | ||
| 17 | buf: std.ArrayList(u8), | ||
| 18 | need_stddef: bool = false, | ||
| 19 | need_stdint: bool = false, | ||
| 20 | emit_loc: ?Compilation.EmitLoc, | ||
| 21 | |||
| 22 | pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header { | ||
| 23 | return .{ | ||
| 24 | .buf = std.ArrayList(u8).init(allocator), | ||
| 25 | .emit_loc = emit_loc, | ||
| 26 | }; | ||
| 27 | } | ||
| 28 | |||
| 29 | pub fn flush(self: *const Header, writer: anytype) !void { | ||
| 30 | const tracy = trace(@src()); | ||
| 31 | defer tracy.end(); | ||
| 32 | |||
| 33 | try writer.writeAll(@embedFile("cbe.h")); | ||
| 34 | var includes = false; | ||
| 35 | if (self.need_stddef) { | ||
| 36 | try writer.writeAll("#include <stddef.h>\n"); | ||
| 37 | includes = true; | ||
| 38 | } | ||
| 39 | if (self.need_stdint) { | ||
| 40 | try writer.writeAll("#include <stdint.h>\n"); | ||
| 41 | includes = true; | ||
| 42 | } | ||
| 43 | if (includes) { | ||
| 44 | try writer.writeByte('\n'); | ||
| 45 | } | ||
| 46 | if (self.buf.items.len > 0) { | ||
| 47 | try writer.print("{}", .{self.buf.items}); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | pub fn deinit(self: *Header) void { | ||
| 52 | self.buf.deinit(); | ||
| 53 | self.* = undefined; | ||
| 54 | } | ||
| 55 | }; | ||
| 56 | |||
| 16 | base: File, | 57 | base: File, |
| 17 | 58 | ||
| 18 | header: std.ArrayList(u8), | 59 | header: Header, |
| 19 | constants: std.ArrayList(u8), | 60 | constants: std.ArrayList(u8), |
| 20 | main: std.ArrayList(u8), | 61 | main: std.ArrayList(u8), |
| 21 | 62 | ||
| 22 | called: std.StringHashMap(void), | 63 | called: std.StringHashMap(void), |
| 23 | need_stddef: bool = false, | ||
| 24 | need_stdint: bool = false, | ||
| 25 | error_msg: *Compilation.ErrorMsg = undefined, | 64 | error_msg: *Compilation.ErrorMsg = undefined, |
| 26 | 65 | ||
| 27 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { | 66 | pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { |
| ... | @@ -44,7 +83,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio | ... | @@ -44,7 +83,7 @@ pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Optio |
| 44 | .allocator = allocator, | 83 | .allocator = allocator, |
| 45 | }, | 84 | }, |
| 46 | .main = std.ArrayList(u8).init(allocator), | 85 | .main = std.ArrayList(u8).init(allocator), |
| 47 | .header = std.ArrayList(u8).init(allocator), | 86 | .header = Header.init(allocator, null), |
| 48 | .constants = std.ArrayList(u8).init(allocator), | 87 | .constants = std.ArrayList(u8).init(allocator), |
| 49 | .called = std.StringHashMap(void).init(allocator), | 88 | .called = std.StringHashMap(void).init(allocator), |
| 50 | }; | 89 | }; |
| ... | @@ -82,22 +121,10 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -82,22 +121,10 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 82 | defer tracy.end(); | 121 | defer tracy.end(); |
| 83 | 122 | ||
| 84 | const writer = self.base.file.?.writer(); | 123 | const writer = self.base.file.?.writer(); |
| 85 | try writer.writeAll(@embedFile("cbe.h")); | 124 | try self.header.flush(writer); |
| 86 | var includes = false; | 125 | if (self.header.buf.items.len > 0) { |
| 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'); | 126 | try writer.writeByte('\n'); |
| 97 | } | 127 | } |
| 98 | if (self.header.items.len > 0) { | ||
| 99 | try writer.print("{}\n", .{self.header.items}); | ||
| 100 | } | ||
| 101 | if (self.constants.items.len > 0) { | 128 | if (self.constants.items.len > 0) { |
| 102 | try writer.print("{}\n", .{self.constants.items}); | 129 | try writer.print("{}\n", .{self.constants.items}); |
| 103 | } | 130 | } |
src/link/cbe.h+10| ... | @@ -1,3 +1,12 @@ | ... | @@ -1,3 +1,12 @@ |
| 1 | #if __STDC_VERSION__ >= 199901L | ||
| 2 | // C99 or newer | ||
| 3 | #include <stdbool.h> | ||
| 4 | #else | ||
| 5 | #define bool unsigned char | ||
| 6 | #define true 1 | ||
| 7 | #define false 0 | ||
| 8 | #endif | ||
| 9 | |||
| 1 | #if __STDC_VERSION__ >= 201112L | 10 | #if __STDC_VERSION__ >= 201112L |
| 2 | #define zig_noreturn _Noreturn | 11 | #define zig_noreturn _Noreturn |
| 3 | #elif __GNUC__ | 12 | #elif __GNUC__ |
| ... | @@ -13,3 +22,4 @@ | ... | @@ -13,3 +22,4 @@ |
| 13 | #else | 22 | #else |
| 14 | #define zig_unreachable() | 23 | #define zig_unreachable() |
| 15 | #endif | 24 | #endif |
| 25 |
src/main.zig+1-11| ... | @@ -490,7 +490,7 @@ fn buildOutputType( | ... | @@ -490,7 +490,7 @@ fn buildOutputType( |
| 490 | var target_dynamic_linker: ?[]const u8 = null; | 490 | var target_dynamic_linker: ?[]const u8 = null; |
| 491 | var target_ofmt: ?[]const u8 = null; | 491 | var target_ofmt: ?[]const u8 = null; |
| 492 | var output_mode: std.builtin.OutputMode = undefined; | 492 | var output_mode: std.builtin.OutputMode = undefined; |
| 493 | var emit_h: Emit = undefined; | 493 | var emit_h: Emit = .no; |
| 494 | var soname: SOName = undefined; | 494 | var soname: SOName = undefined; |
| 495 | var ensure_libc_on_non_freestanding = false; | 495 | var ensure_libc_on_non_freestanding = false; |
| 496 | var ensure_libcpp_on_non_freestanding = false; | 496 | var ensure_libcpp_on_non_freestanding = false; |
| ... | @@ -594,16 +594,6 @@ fn buildOutputType( | ... | @@ -594,16 +594,6 @@ fn buildOutputType( |
| 594 | }, | 594 | }, |
| 595 | else => unreachable, | 595 | else => unreachable, |
| 596 | } | 596 | } |
| 597 | // TODO finish self-hosted and add support for emitting C header files | ||
| 598 | emit_h = .no; | ||
| 599 | //switch (arg_mode) { | ||
| 600 | // .build => switch (output_mode) { | ||
| 601 | // .Exe => emit_h = .no, | ||
| 602 | // .Obj, .Lib => emit_h = .yes_default_path, | ||
| 603 | // }, | ||
| 604 | // .translate_c, .zig_test, .run => emit_h = .no, | ||
| 605 | // else => unreachable, | ||
| 606 | //} | ||
| 607 | 597 | ||
| 608 | soname = .yes_default_value; | 598 | soname = .yes_default_value; |
| 609 | const args = all_args[2..]; | 599 | const args = all_args[2..]; |
src/test.zig+45-4| ... | @@ -96,6 +96,10 @@ pub const TestContext = struct { | ... | @@ -96,6 +96,10 @@ pub const TestContext = struct { |
| 96 | /// stdout against the expected results | 96 | /// stdout against the expected results |
| 97 | /// This is a slice containing the expected message. | 97 | /// This is a slice containing the expected message. |
| 98 | Execution: []const u8, | 98 | Execution: []const u8, |
| 99 | /// A header update compiles the input with the equivalent of | ||
| 100 | /// `-femit-h` and tests the produced header against the | ||
| 101 | /// expected result | ||
| 102 | Header: []const u8, | ||
| 99 | }, | 103 | }, |
| 100 | }; | 104 | }; |
| 101 | 105 | ||
| ... | @@ -138,6 +142,15 @@ pub const TestContext = struct { | ... | @@ -138,6 +142,15 @@ pub const TestContext = struct { |
| 138 | }) catch unreachable; | 142 | }) catch unreachable; |
| 139 | } | 143 | } |
| 140 | 144 | ||
| 145 | /// Adds a subcase in which the module is updated with `src`, and a C | ||
| 146 | /// header is generated. | ||
| 147 | pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { | ||
| 148 | self.updates.append(.{ | ||
| 149 | .src = src, | ||
| 150 | .case = .{ .Header = result }, | ||
| 151 | }) catch unreachable; | ||
| 152 | } | ||
| 153 | |||
| 141 | /// Adds a subcase in which the module is updated with `src`, compiled, | 154 | /// Adds a subcase in which the module is updated with `src`, compiled, |
| 142 | /// run, and the output is tested against `result`. | 155 | /// run, and the output is tested against `result`. |
| 143 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { | 156 | pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void { |
| ... | @@ -269,6 +282,10 @@ pub const TestContext = struct { | ... | @@ -269,6 +282,10 @@ pub const TestContext = struct { |
| 269 | ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out); | 282 | ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out); |
| 270 | } | 283 | } |
| 271 | 284 | ||
| 285 | pub fn h(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void { | ||
| 286 | ctx.addC(name, target, .Zig).addHeader(src, cheader ++ out); | ||
| 287 | } | ||
| 288 | |||
| 272 | pub fn addCompareOutput( | 289 | pub fn addCompareOutput( |
| 273 | ctx: *TestContext, | 290 | ctx: *TestContext, |
| 274 | name: []const u8, | 291 | name: []const u8, |
| ... | @@ -547,6 +564,13 @@ pub const TestContext = struct { | ... | @@ -547,6 +564,13 @@ pub const TestContext = struct { |
| 547 | .directory = emit_directory, | 564 | .directory = emit_directory, |
| 548 | .basename = bin_name, | 565 | .basename = bin_name, |
| 549 | }; | 566 | }; |
| 567 | const emit_h: ?Compilation.EmitLoc = if (case.cbe) | ||
| 568 | .{ | ||
| 569 | .directory = emit_directory, | ||
| 570 | .basename = "test_case.h", | ||
| 571 | } | ||
| 572 | else | ||
| 573 | null; | ||
| 550 | const comp = try Compilation.create(allocator, .{ | 574 | const comp = try Compilation.create(allocator, .{ |
| 551 | .local_cache_directory = zig_cache_directory, | 575 | .local_cache_directory = zig_cache_directory, |
| 552 | .global_cache_directory = zig_cache_directory, | 576 | .global_cache_directory = zig_cache_directory, |
| ... | @@ -561,6 +585,7 @@ pub const TestContext = struct { | ... | @@ -561,6 +585,7 @@ pub const TestContext = struct { |
| 561 | // TODO: support testing optimizations | 585 | // TODO: support testing optimizations |
| 562 | .optimize_mode = .Debug, | 586 | .optimize_mode = .Debug, |
| 563 | .emit_bin = emit_bin, | 587 | .emit_bin = emit_bin, |
| 588 | .emit_h = emit_h, | ||
| 564 | .root_pkg = &root_pkg, | 589 | .root_pkg = &root_pkg, |
| 565 | .keep_source_files_loaded = true, | 590 | .keep_source_files_loaded = true, |
| 566 | .object_format = ofmt, | 591 | .object_format = ofmt, |
| ... | @@ -616,6 +641,22 @@ pub const TestContext = struct { | ... | @@ -616,6 +641,22 @@ pub const TestContext = struct { |
| 616 | } | 641 | } |
| 617 | 642 | ||
| 618 | switch (update.case) { | 643 | switch (update.case) { |
| 644 | .Header => |expected_output| { | ||
| 645 | var file = try tmp.dir.openFile("test_case.h", .{ .read = true }); | ||
| 646 | defer file.close(); | ||
| 647 | var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read headeroutput!"); | ||
| 648 | |||
| 649 | if (expected_output.len != out.len) { | ||
| 650 | std.debug.print("\nTransformed header length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); | ||
| 651 | std.process.exit(1); | ||
| 652 | } | ||
| 653 | for (expected_output) |e, i| { | ||
| 654 | if (out[i] != e) { | ||
| 655 | std.debug.print("\nTransformed header differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out }); | ||
| 656 | std.process.exit(1); | ||
| 657 | } | ||
| 658 | } | ||
| 659 | }, | ||
| 619 | .Transformation => |expected_output| { | 660 | .Transformation => |expected_output| { |
| 620 | if (case.cbe) { | 661 | if (case.cbe) { |
| 621 | // The C file is always closed after an update, because we don't support | 662 | // The C file is always closed after an update, because we don't support |
| ... | @@ -670,8 +711,8 @@ pub const TestContext = struct { | ... | @@ -670,8 +711,8 @@ pub const TestContext = struct { |
| 670 | test_node.activate(); | 711 | test_node.activate(); |
| 671 | defer test_node.end(); | 712 | defer test_node.end(); |
| 672 | var handled_errors = try arena.alloc(bool, e.len); | 713 | var handled_errors = try arena.alloc(bool, e.len); |
| 673 | for (handled_errors) |*h| { | 714 | for (handled_errors) |*handled| { |
| 674 | h.* = false; | 715 | handled.* = false; |
| 675 | } | 716 | } |
| 676 | var all_errors = try comp.getAllErrorsAlloc(); | 717 | var all_errors = try comp.getAllErrorsAlloc(); |
| 677 | defer all_errors.deinit(allocator); | 718 | defer all_errors.deinit(allocator); |
| ... | @@ -709,8 +750,8 @@ pub const TestContext = struct { | ... | @@ -709,8 +750,8 @@ pub const TestContext = struct { |
| 709 | } | 750 | } |
| 710 | } | 751 | } |
| 711 | 752 | ||
| 712 | for (handled_errors) |h, i| { | 753 | for (handled_errors) |handled, i| { |
| 713 | if (!h) { | 754 | if (!handled) { |
| 714 | const er = e[i]; | 755 | const er = e[i]; |
| 715 | std.debug.print( | 756 | std.debug.print( |
| 716 | "{s}\nDid not receive error:\n================\n{}\n================\nTest failed.\n", | 757 | "{s}\nDid not receive error:\n================\n{}\n================\nTest failed.\n", |
test/stage2/cbe.zig+71| ... | @@ -19,6 +19,12 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -19,6 +19,12 @@ pub fn addCases(ctx: *TestContext) !void { |
| 19 | \\} | 19 | \\} |
| 20 | \\ | 20 | \\ |
| 21 | ); | 21 | ); |
| 22 | ctx.h("simple header", linux_x64, | ||
| 23 | \\export fn start() void{} | ||
| 24 | , | ||
| 25 | \\void start(void); | ||
| 26 | \\ | ||
| 27 | ); | ||
| 22 | ctx.c("less empty start function", linux_x64, | 28 | ctx.c("less empty start function", linux_x64, |
| 23 | \\fn main() noreturn { | 29 | \\fn main() noreturn { |
| 24 | \\ unreachable; | 30 | \\ unreachable; |
| ... | @@ -243,4 +249,69 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -243,4 +249,69 @@ pub fn addCases(ctx: *TestContext) !void { |
| 243 | \\} | 249 | \\} |
| 244 | \\ | 250 | \\ |
| 245 | ); | 251 | ); |
| 252 | ctx.h("header with single param function", linux_x64, | ||
| 253 | \\export fn start(a: u8) void{} | ||
| 254 | , | ||
| 255 | \\#include <stdint.h> | ||
| 256 | \\ | ||
| 257 | \\void start(uint8_t arg0); | ||
| 258 | \\ | ||
| 259 | ); | ||
| 260 | ctx.h("header with multiple param function", linux_x64, | ||
| 261 | \\export fn start(a: u8, b: u8, c: u8) void{} | ||
| 262 | , | ||
| 263 | \\#include <stdint.h> | ||
| 264 | \\ | ||
| 265 | \\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2); | ||
| 266 | \\ | ||
| 267 | ); | ||
| 268 | ctx.h("header with u32 param function", linux_x64, | ||
| 269 | \\export fn start(a: u32) void{} | ||
| 270 | , | ||
| 271 | \\#include <stdint.h> | ||
| 272 | \\ | ||
| 273 | \\void start(uint32_t arg0); | ||
| 274 | \\ | ||
| 275 | ); | ||
| 276 | ctx.h("header with usize param function", linux_x64, | ||
| 277 | \\export fn start(a: usize) void{} | ||
| 278 | , | ||
| 279 | \\#include <stddef.h> | ||
| 280 | \\ | ||
| 281 | \\void start(size_t arg0); | ||
| 282 | \\ | ||
| 283 | ); | ||
| 284 | ctx.h("header with bool param function", linux_x64, | ||
| 285 | \\export fn start(a: bool) void{} | ||
| 286 | , | ||
| 287 | \\void start(bool arg0); | ||
| 288 | \\ | ||
| 289 | ); | ||
| 290 | ctx.h("header with noreturn function", linux_x64, | ||
| 291 | \\export fn start() noreturn { | ||
| 292 | \\ unreachable; | ||
| 293 | \\} | ||
| 294 | , | ||
| 295 | \\zig_noreturn void start(void); | ||
| 296 | \\ | ||
| 297 | ); | ||
| 298 | ctx.h("header with multiple functions", linux_x64, | ||
| 299 | \\export fn a() void{} | ||
| 300 | \\export fn b() void{} | ||
| 301 | \\export fn c() void{} | ||
| 302 | , | ||
| 303 | \\void a(void); | ||
| 304 | \\void b(void); | ||
| 305 | \\void c(void); | ||
| 306 | \\ | ||
| 307 | ); | ||
| 308 | ctx.h("header with multiple includes", linux_x64, | ||
| 309 | \\export fn start(a: u32, b: usize) void{} | ||
| 310 | , | ||
| 311 | \\#include <stddef.h> | ||
| 312 | \\#include <stdint.h> | ||
| 313 | \\ | ||
| 314 | \\void start(uint32_t arg0, size_t arg1); | ||
| 315 | \\ | ||
| 316 | ); | ||
| 246 | } | 317 | } |