authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-28 20:32:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-28 20:32:53-07:00
log3b5dd48f99269cf8e944adf40657f2866adecc37
tree287b4417d847bcfc27d068ef75e45c6a2fce1991
parent2df2f0020f4ddc41b3b914cd17efcb403cf0f6ad
parent813d3308ccd13bdc96a40b583ffd8722651b7b83

Merge branch 'hello-c-backend' into master

This branch introduces a new kind of test into the stage2 test harness: Zig code that compiles into C code with the C backend, and then the resulting C code gets run and output compared against the expected result. This branch also implements extern functions in the frontend so that we can have a "hello world" C backend test that passes.

12 files changed, 716 insertions(+), 352 deletions(-)

lib/std/special/test_runner.zig+8
...@@ -11,7 +11,15 @@ pub const io_mode: io.Mode = builtin.test_io_mode;...@@ -11,7 +11,15 @@ pub const io_mode: io.Mode = builtin.test_io_mode;
1111
12var log_err_count: usize = 0;12var log_err_count: usize = 0;
1313
14var args_buffer: [std.fs.MAX_PATH_BYTES + std.mem.page_size]u8 = undefined;
15var args_allocator = std.heap.FixedBufferAllocator.init(&args_buffer);
16
14pub fn main() anyerror!void {17pub fn main() anyerror!void {
18 const args = std.process.argsAlloc(&args_allocator.allocator) catch {
19 @panic("Too many bytes passed over the CLI to the test runner");
20 };
21 std.testing.zig_exe_path = args[1];
22
15 const test_fn_list = builtin.test_functions;23 const test_fn_list = builtin.test_functions;
16 var ok_count: usize = 0;24 var ok_count: usize = 0;
17 var skip_count: usize = 0;25 var skip_count: usize = 0;
lib/std/testing.zig+4
...@@ -21,6 +21,10 @@ pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");...@@ -21,6 +21,10 @@ pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
21/// TODO https://github.com/ziglang/zig/issues/573821/// TODO https://github.com/ziglang/zig/issues/5738
22pub var log_level = std.log.Level.warn;22pub var log_level = std.log.Level.warn;
2323
24/// This is available to any test that wants to execute Zig in a child process.
25/// It will be the same executable that is running `zig test`.
26pub var zig_exe_path: []const u8 = undefined;
27
24/// This function is intended to be used only in tests. It prints diagnostics to stderr28/// This function is intended to be used only in tests. It prints diagnostics to stderr
25/// and then aborts when actual_error_union is not expected_error.29/// and then aborts when actual_error_union is not expected_error.
26pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {30pub fn expectError(expected_error: anyerror, actual_error_union: anytype) void {
src/Compilation.zig+1-4
...@@ -1431,9 +1431,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1431,9 +1431,6 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1431 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);1431 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
1432 defer c_comp_progress_node.end();1432 defer c_comp_progress_node.end();
14331433
1434 var arena = std.heap.ArenaAllocator.init(self.gpa);
1435 defer arena.deinit();
1436
1437 self.work_queue_wait_group.reset();1434 self.work_queue_wait_group.reset();
1438 defer self.work_queue_wait_group.wait();1435 defer self.work_queue_wait_group.wait();
14391436
...@@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
1502 };1499 };
15031500
1504 if (self.c_header) |*header| {1501 if (self.c_header) |*header| {
1505 c_codegen.generateHeader(&arena, module, &header.*, decl) catch |err| switch (err) {1502 c_codegen.generateHeader(self, module, header, decl) catch |err| switch (err) {
1506 error.OutOfMemory => return error.OutOfMemory,1503 error.OutOfMemory => return error.OutOfMemory,
1507 error.AnalysisFail => {1504 error.AnalysisFail => {
1508 decl.analysis = .dependency_failure;1505 decl.analysis = .dependency_failure;
src/Module.zig+86-11
...@@ -277,6 +277,8 @@ pub const Decl = struct {...@@ -277,6 +277,8 @@ pub const Decl = struct {
277};277};
278278
279/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.279/// Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
280/// Extern functions do not have this data structure; they are represented by
281/// the `Decl` only, with a `Value` tag of `extern_fn`.
280pub const Fn = struct {282pub const Fn = struct {
281 /// This memory owned by the Decl's TypedValue.Managed arena allocator.283 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
282 analysis: union(enum) {284 analysis: union(enum) {
...@@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1010 defer fn_type_scope.instructions.deinit(self.gpa);1012 defer fn_type_scope.instructions.deinit(self.gpa);
10111013
1012 decl.is_pub = fn_proto.getVisibToken() != null;1014 decl.is_pub = fn_proto.getVisibToken() != null;
1013 const body_node = fn_proto.getBodyNode() orelse
1014 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
10151015
1016 const param_decls = fn_proto.params();1016 const param_decls = fn_proto.params();
1017 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);1017 const param_types = try fn_type_scope.arena.alloc(*zir.Inst, param_decls.len);
...@@ -1083,6 +1083,36 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1083,6 +1083,36 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1083 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{1083 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
1084 .instructions = fn_type_scope.instructions.items,1084 .instructions = fn_type_scope.instructions.items,
1085 });1085 });
1086 const body_node = fn_proto.getBodyNode() orelse {
1087 // Extern function.
1088 var type_changed = true;
1089 if (decl.typedValueManaged()) |tvm| {
1090 type_changed = !tvm.typed_value.ty.eql(fn_type);
1091
1092 tvm.deinit(self.gpa);
1093 }
1094 const value_payload = try decl_arena.allocator.create(Value.Payload.ExternFn);
1095 value_payload.* = .{ .decl = decl };
1096
1097 decl_arena_state.* = decl_arena.state;
1098 decl.typed_value = .{
1099 .most_recent = .{
1100 .typed_value = .{
1101 .ty = fn_type,
1102 .val = Value.initPayload(&value_payload.base),
1103 },
1104 .arena = decl_arena_state,
1105 },
1106 };
1107 decl.analysis = .complete;
1108 decl.generation = self.generation;
1109
1110 try self.comp.bin_file.allocateDeclIndexes(decl);
1111 try self.comp.work_queue.writeItem(.{ .codegen_decl = decl });
1112
1113 return type_changed;
1114 };
1115
1086 const new_func = try decl_arena.allocator.create(Fn);1116 const new_func = try decl_arena.allocator.create(Fn);
1087 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);1117 const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
10881118
...@@ -1899,7 +1929,13 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {...@@ -1899,7 +1929,13 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
1899 return null;1929 return null;
1900}1930}
19011931
1902pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void {1932pub fn analyzeExport(
1933 self: *Module,
1934 scope: *Scope,
1935 src: usize,
1936 borrowed_symbol_name: []const u8,
1937 exported_decl: *Decl,
1938) !void {
1903 try self.ensureDeclAnalyzed(exported_decl);1939 try self.ensureDeclAnalyzed(exported_decl);
1904 const typed_value = exported_decl.typed_value.most_recent.typed_value;1940 const typed_value = exported_decl.typed_value.most_recent.typed_value;
1905 switch (typed_value.ty.zigTypeTag()) {1941 switch (typed_value.ty.zigTypeTag()) {
...@@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
2801 }2837 }
2802 }2838 }
28032839
2804 // *[N]T to []T2840 // Coercions where the source is a single pointer to an array.
2805 if (inst.ty.isSinglePointer() and dest_type.isSlice() and2841 src_array_ptr: {
2806 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))2842 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
2807 {
2808 const array_type = inst.ty.elemType();2843 const array_type = inst.ty.elemType();
2844 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
2845 const array_elem_type = array_type.elemType();
2846 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
2847 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
2848
2809 const dst_elem_type = dest_type.elemType();2849 const dst_elem_type = dest_type.elemType();
2810 if (array_type.zigTypeTag() == .Array and2850 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
2811 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)2851 .ok => {},
2812 {2852 .no_match => break :src_array_ptr,
2813 return self.coerceArrayPtrToSlice(scope, dest_type, inst);2853 }
2854
2855 switch (dest_type.ptrSize()) {
2856 .Slice => {
2857 // *[N]T to []T
2858 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
2859 },
2860 .C => {
2861 // *[N]T to [*c]T
2862 return self.coerceArrayPtrToMany(scope, dest_type, inst);
2863 },
2864 .Many => {
2865 // *[N]T to [*]T
2866 // *[N:s]T to [*:s]T
2867 const src_sentinel = array_type.sentinel();
2868 const dst_sentinel = dest_type.sentinel();
2869 if (src_sentinel == null and dst_sentinel == null)
2870 return self.coerceArrayPtrToMany(scope, dest_type, inst);
2871
2872 if (src_sentinel) |src_s| {
2873 if (dst_sentinel) |dst_s| {
2874 if (src_s.eql(dst_s)) {
2875 return self.coerceArrayPtrToMany(scope, dest_type, inst);
2876 }
2877 }
2878 }
2879 },
2880 .One => {},
2814 }2881 }
2815 }2882 }
28162883
...@@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I...@@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
2918 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});2985 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
2919}2986}
29202987
2988fn coerceArrayPtrToMany(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
2989 if (inst.value()) |val| {
2990 // The comptime Value representation is compatible with both types.
2991 return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val });
2992 }
2993 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
2994}
2995
2921pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {2996pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
2922 @setCold(true);2997 @setCold(true);
2923 const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);2998 const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
src/codegen/c.zig+298-141
...@@ -11,8 +11,9 @@ const Type = @import("../type.zig").Type;...@@ -11,8 +11,9 @@ const Type = @import("../type.zig").Type;
11const C = link.File.C;11const C = link.File.C;
12const Decl = Module.Decl;12const Decl = Module.Decl;
13const mem = std.mem;13const mem = std.mem;
14const log = std.log.scoped(.c);
1415
15const indentation = " ";16const Writer = std.ArrayList(u8).Writer;
1617
17/// Maps a name from Zig source to C. Currently, this will always give the same18/// Maps a name from Zig source to C. Currently, this will always give the same
18/// output for any given input, sometimes resulting in broken identifiers.19/// output for any given input, sometimes resulting in broken identifiers.
...@@ -20,45 +21,162 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {...@@ -20,45 +21,162 @@ fn map(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
20 return allocator.dupe(u8, name);21 return allocator.dupe(u8, name);
21}22}
2223
23fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void {24fn renderType(
24 switch (T.zigTypeTag()) {25 ctx: *Context,
26 writer: Writer,
27 t: Type,
28) error{ OutOfMemory, AnalysisFail }!void {
29 switch (t.zigTypeTag()) {
25 .NoReturn => {30 .NoReturn => {
26 try writer.writeAll("zig_noreturn void");31 try writer.writeAll("zig_noreturn void");
27 },32 },
28 .Void => try writer.writeAll("void"),33 .Void => try writer.writeAll("void"),
29 .Bool => try writer.writeAll("bool"),34 .Bool => try writer.writeAll("bool"),
30 .Int => {35 .Int => {
31 if (T.tag() == .u8) {36 switch (t.tag()) {
32 header.need_stdint = true;37 .u8 => try writer.writeAll("uint8_t"),
33 try writer.writeAll("uint8_t");38 .i8 => try writer.writeAll("int8_t"),
34 } else if (T.tag() == .u32) {39 .u16 => try writer.writeAll("uint16_t"),
35 header.need_stdint = true;40 .i16 => try writer.writeAll("int16_t"),
36 try writer.writeAll("uint32_t");41 .u32 => try writer.writeAll("uint32_t"),
37 } else if (T.tag() == .usize) {42 .i32 => try writer.writeAll("int32_t"),
38 header.need_stddef = true;43 .u64 => try writer.writeAll("uint64_t"),
39 try writer.writeAll("size_t");44 .i64 => try writer.writeAll("int64_t"),
45 .usize => try writer.writeAll("uintptr_t"),
46 .isize => try writer.writeAll("intptr_t"),
47 .c_short => try writer.writeAll("short"),
48 .c_ushort => try writer.writeAll("unsigned short"),
49 .c_int => try writer.writeAll("int"),
50 .c_uint => try writer.writeAll("unsigned int"),
51 .c_long => try writer.writeAll("long"),
52 .c_ulong => try writer.writeAll("unsigned long"),
53 .c_longlong => try writer.writeAll("long long"),
54 .c_ulonglong => try writer.writeAll("unsigned long long"),
55 .int_signed, .int_unsigned => {
56 const info = t.intInfo(ctx.target);
57 const sign_prefix = switch (info.signedness) {
58 .signed => "i",
59 .unsigned => "",
60 };
61 inline for (.{ 8, 16, 32, 64, 128 }) |nbits| {
62 if (info.bits <= nbits) {
63 try writer.print("{s}int{d}_t", .{ sign_prefix, nbits });
64 break;
65 }
66 } else {
67 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement integer types larger than 128 bits", .{});
68 }
69 },
70 else => unreachable,
71 }
72 },
73 .Pointer => {
74 if (t.isSlice()) {
75 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement slices", .{});
40 } else {76 } else {
41 return ctx.fail(ctx.decl.src(), "TODO implement int type {}", .{T});77 if (t.isConstPtr()) {
78 try writer.writeAll("const ");
79 }
80 if (t.isVolatilePtr()) {
81 try writer.writeAll("volatile ");
82 }
83 try renderType(ctx, writer, t.elemType());
84 try writer.writeAll(" *");
42 }85 }
43 },86 },
44 else => |e| return ctx.fail(ctx.decl.src(), "TODO implement type {}", .{e}),87 .Array => {
88 try renderType(ctx, writer, t.elemType());
89 try writer.writeAll(" *");
90 },
91 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement type {s}", .{
92 @tagName(e),
93 }),
45 }94 }
46}95}
4796
48fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {97fn renderValue(
49 switch (T.zigTypeTag()) {98 ctx: *Context,
99 writer: Writer,
100 t: Type,
101 val: Value,
102) error{ OutOfMemory, AnalysisFail }!void {
103 switch (t.zigTypeTag()) {
50 .Int => {104 .Int => {
51 if (T.isSignedInt())105 if (t.isSignedInt())
52 return writer.print("{}", .{val.toSignedInt()});106 return writer.print("{d}", .{val.toSignedInt()});
53 return writer.print("{}", .{val.toUnsignedInt()});107 return writer.print("{d}", .{val.toUnsignedInt()});
108 },
109 .Pointer => switch (val.tag()) {
110 .undef, .zero => try writer.writeAll("0"),
111 .one => try writer.writeAll("1"),
112 .decl_ref => {
113 const decl_ref_payload = val.cast(Value.Payload.DeclRef).?;
114
115 // Determine if we must pointer cast.
116 const decl_tv = decl_ref_payload.decl.typed_value.most_recent.typed_value;
117 if (t.eql(decl_tv.ty)) {
118 try writer.print("&{s}", .{decl_ref_payload.decl.name});
119 } else {
120 try writer.writeAll("(");
121 try renderType(ctx, writer, t);
122 try writer.print(")&{s}", .{decl_ref_payload.decl.name});
123 }
124 },
125 .function => {
126 const payload = val.cast(Value.Payload.Function).?;
127 try writer.print("{s}", .{payload.func.owner_decl.name});
128 },
129 .extern_fn => {
130 const payload = val.cast(Value.Payload.ExternFn).?;
131 try writer.print("{s}", .{payload.decl.name});
132 },
133 else => |e| return ctx.fail(
134 ctx.decl.src(),
135 "TODO: C backend: implement Pointer value {s}",
136 .{@tagName(e)},
137 ),
54 },138 },
55 else => |e| return ctx.fail(ctx.decl.src(), "TODO implement value {}", .{e}),139 .Array => {
140 // First try specific tag representations for more efficiency.
141 switch (val.tag()) {
142 .undef, .empty_struct_value, .empty_array => try writer.writeAll("{}"),
143 .bytes => {
144 const bytes = val.cast(Value.Payload.Bytes).?.data;
145 // TODO: make our own C string escape instead of using {Z}
146 try writer.print("\"{Z}\"", .{bytes});
147 },
148 else => {
149 // Fall back to generic implementation.
150 try writer.writeAll("{");
151 var index: usize = 0;
152 const len = t.arrayLen();
153 const elem_ty = t.elemType();
154 while (index < len) : (index += 1) {
155 if (index != 0) try writer.writeAll(",");
156 const elem_val = try val.elemValue(&ctx.arena.allocator, index);
157 try renderValue(ctx, writer, elem_ty, elem_val);
158 }
159 if (t.sentinel()) |sentinel_val| {
160 if (index != 0) try writer.writeAll(",");
161 try renderValue(ctx, writer, elem_ty, sentinel_val);
162 }
163 try writer.writeAll("}");
164 },
165 }
166 },
167 else => |e| return ctx.fail(ctx.decl.src(), "TODO: C backend: implement value {s}", .{
168 @tagName(e),
169 }),
56 }170 }
57}171}
58172
59fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {173fn renderFunctionSignature(
174 ctx: *Context,
175 writer: Writer,
176 decl: *Decl,
177) !void {
60 const tv = decl.typed_value.most_recent.typed_value;178 const tv = decl.typed_value.most_recent.typed_value;
61 try renderType(ctx, header, writer, tv.ty.fnReturnType());179 try renderType(ctx, writer, tv.ty.fnReturnType());
62 // Use the child allocator directly, as we know the name can be freed before180 // Use the child allocator directly, as we know the name can be freed before
63 // the rest of the arena.181 // the rest of the arena.
64 const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name));182 const name = try map(ctx.arena.child_allocator, mem.spanZ(decl.name));
...@@ -73,38 +191,122 @@ fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayLi...@@ -73,38 +191,122 @@ fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayLi
73 if (index > 0) {191 if (index > 0) {
74 try writer.writeAll(", ");192 try writer.writeAll(", ");
75 }193 }
76 try renderType(ctx, header, writer, tv.ty.fnParamType(index));194 try renderType(ctx, writer, tv.ty.fnParamType(index));
77 try writer.print(" arg{}", .{index});195 try writer.print(" arg{}", .{index});
78 }196 }
79 }197 }
80 try writer.writeByte(')');198 try writer.writeByte(')');
81}199}
82200
201fn indent(file: *C) !void {
202 const indent_size = 4;
203 const indent_level = 1;
204 const indent_amt = indent_size * indent_level;
205 try file.main.writer().writeByteNTimes(' ', indent_amt);
206}
207
83pub fn generate(file: *C, decl: *Decl) !void {208pub fn generate(file: *C, decl: *Decl) !void {
84 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {209 const tv = decl.typed_value.most_recent.typed_value;
85 .Fn => try genFn(file, decl),210
86 .Array => try genArray(file, decl),211 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
87 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),212 defer arena.deinit();
213 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
214 defer inst_map.deinit();
215 var ctx = Context{
216 .decl = decl,
217 .arena = &arena,
218 .inst_map = &inst_map,
219 .target = file.base.options.target,
220 .header = &file.header,
221 };
222 defer {
223 file.error_msg = ctx.error_msg;
224 ctx.deinit();
225 }
226
227 if (tv.val.cast(Value.Payload.Function)) |func_payload| {
228 const writer = file.main.writer();
229 try renderFunctionSignature(&ctx, writer, decl);
230
231 try writer.writeAll(" {");
232
233 const func: *Module.Fn = func_payload.func;
234 const instructions = func.analysis.success.instructions;
235 if (instructions.len > 0) {
236 try writer.writeAll("\n");
237 for (instructions) |inst| {
238 if (switch (inst.tag) {
239 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
240 .call => try genCall(&ctx, file, inst.castTag(.call).?),
241 .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"),
242 .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"),
243 .ret => try genRet(&ctx, file, inst.castTag(.ret).?),
244 .retvoid => try genRetVoid(file),
245 .arg => try genArg(&ctx),
246 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
247 .breakpoint => try genBreakpoint(file, inst.castTag(.breakpoint).?),
248 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
249 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
250 else => |e| return ctx.fail(decl.src(), "TODO: C backend: implement codegen for {}", .{e}),
251 }) |name| {
252 try ctx.inst_map.putNoClobber(inst, name);
253 }
254 }
255 }
256
257 try writer.writeAll("}\n\n");
258 } else if (tv.val.tag() == .extern_fn) {
259 return; // handled when referenced
260 } else {
261 const writer = file.constants.writer();
262 try writer.writeAll("static ");
263
264 // TODO ask the Decl if it is const
265 // https://github.com/ziglang/zig/issues/7582
266
267 var suffix = std.ArrayList(u8).init(file.base.allocator);
268 defer suffix.deinit();
269
270 var render_ty = tv.ty;
271 while (render_ty.zigTypeTag() == .Array) {
272 const sentinel_bit = @boolToInt(render_ty.sentinel() != null);
273 const c_len = render_ty.arrayLen() + sentinel_bit;
274 try suffix.writer().print("[{d}]", .{c_len});
275 render_ty = render_ty.elemType();
276 }
277
278 try renderType(&ctx, writer, render_ty);
279 try writer.print(" {s}{s}", .{ decl.name, suffix.items });
280
281 try writer.writeAll(" = ");
282 try renderValue(&ctx, writer, tv.ty, tv.val);
283 try writer.writeAll(";\n");
88 }284 }
89}285}
90286
91pub fn generateHeader(287pub fn generateHeader(
92 arena: *std.heap.ArenaAllocator,288 comp: *Compilation,
93 module: *Module,289 module: *Module,
94 header: *C.Header,290 header: *C.Header,
95 decl: *Decl,291 decl: *Decl,
96) error{ AnalysisFail, OutOfMemory }!void {292) error{ AnalysisFail, OutOfMemory }!void {
97 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {293 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
98 .Fn => {294 .Fn => {
99 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);295 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);
100 defer inst_map.deinit();296 defer inst_map.deinit();
297
298 var arena = std.heap.ArenaAllocator.init(comp.gpa);
299 defer arena.deinit();
300
101 var ctx = Context{301 var ctx = Context{
102 .decl = decl,302 .decl = decl,
103 .arena = arena,303 .arena = &arena,
104 .inst_map = &inst_map,304 .inst_map = &inst_map,
305 .target = comp.getTarget(),
306 .header = header,
105 };307 };
106 const writer = header.buf.writer();308 const writer = header.buf.writer();
107 renderFunctionSignature(&ctx, header, writer, decl) catch |err| {309 renderFunctionSignature(&ctx, writer, decl) catch |err| {
108 if (err == error.AnalysisFail) {310 if (err == error.AnalysisFail) {
109 try module.failed_decls.put(module.gpa, decl, ctx.error_msg);311 try module.failed_decls.put(module.gpa, decl, ctx.error_msg);
110 }312 }
...@@ -116,24 +318,6 @@ pub fn generateHeader(...@@ -116,24 +318,6 @@ pub fn generateHeader(
116 }318 }
117}319}
118320
119fn genArray(file: *C, decl: *Decl) !void {
120 const tv = decl.typed_value.most_recent.typed_value;
121 // TODO: prevent inline asm constants from being emitted
122 const name = try map(file.base.allocator, mem.span(decl.name));
123 defer file.base.allocator.free(name);
124 if (tv.val.cast(Value.Payload.Bytes)) |payload|
125 if (tv.ty.sentinel()) |sentinel|
126 if (sentinel.toUnsignedInt() == 0)
127 // TODO: static by default
128 try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data })
129 else
130 return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{})
131 else
132 return file.fail(decl.src(), "TODO byte arrays without sentinels", .{})
133 else
134 return file.fail(decl.src(), "TODO non-byte arrays", .{});
135}
136
137const Context = struct {321const Context = struct {
138 decl: *Decl,322 decl: *Decl,
139 inst_map: *std.AutoHashMap(*Inst, []u8),323 inst_map: *std.AutoHashMap(*Inst, []u8),
...@@ -141,6 +325,8 @@ const Context = struct {...@@ -141,6 +325,8 @@ const Context = struct {
141 argdex: usize = 0,325 argdex: usize = 0,
142 unnamed_index: usize = 0,326 unnamed_index: usize = 0,
143 error_msg: *Compilation.ErrorMsg = undefined,327 error_msg: *Compilation.ErrorMsg = undefined,
328 target: std.Target,
329 header: *C.Header,
144330
145 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {331 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
146 if (inst.cast(Inst.Constant)) |const_inst| {332 if (inst.cast(Inst.Constant)) |const_inst| {
...@@ -170,55 +356,6 @@ const Context = struct {...@@ -170,55 +356,6 @@ const Context = struct {
170 }356 }
171};357};
172358
173fn genFn(file: *C, decl: *Decl) !void {
174 const writer = file.main.writer();
175 const tv = decl.typed_value.most_recent.typed_value;
176
177 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
178 defer arena.deinit();
179 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
180 defer inst_map.deinit();
181 var ctx = Context{
182 .decl = decl,
183 .arena = &arena,
184 .inst_map = &inst_map,
185 };
186 defer {
187 file.error_msg = ctx.error_msg;
188 ctx.deinit();
189 }
190
191 try renderFunctionSignature(&ctx, &file.header, writer, decl);
192
193 try writer.writeAll(" {");
194
195 const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
196 const instructions = func.analysis.success.instructions;
197 if (instructions.len > 0) {
198 try writer.writeAll("\n");
199 for (instructions) |inst| {
200 if (switch (inst.tag) {
201 .assembly => try genAsm(&ctx, file, inst.castTag(.assembly).?),
202 .call => try genCall(&ctx, file, inst.castTag(.call).?),
203 .add => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "+"),
204 .sub => try genBinOp(&ctx, file, inst.cast(Inst.BinOp).?, "-"),
205 .ret => try genRet(&ctx, inst.castTag(.ret).?),
206 .retvoid => try genRetVoid(file),
207 .arg => try genArg(&ctx),
208 .dbg_stmt => try genDbgStmt(&ctx, inst.castTag(.dbg_stmt).?),
209 .breakpoint => try genBreak(&ctx, inst.castTag(.breakpoint).?),
210 .unreach => try genUnreach(file, inst.castTag(.unreach).?),
211 .intcast => try genIntCast(&ctx, file, inst.castTag(.intcast).?),
212 else => |e| return ctx.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
213 }) |name| {
214 try ctx.inst_map.putNoClobber(inst, name);
215 }
216 }
217 }
218
219 try writer.writeAll("}\n\n");
220}
221
222fn genArg(ctx: *Context) !?[]u8 {359fn genArg(ctx: *Context) !?[]u8 {
223 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});360 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});
224 ctx.argdex += 1;361 ctx.argdex += 1;
...@@ -226,25 +363,40 @@ fn genArg(ctx: *Context) !?[]u8 {...@@ -226,25 +363,40 @@ fn genArg(ctx: *Context) !?[]u8 {
226}363}
227364
228fn genRetVoid(file: *C) !?[]u8 {365fn genRetVoid(file: *C) !?[]u8 {
229 try file.main.writer().print(indentation ++ "return;\n", .{});366 try indent(file);
367 try file.main.writer().print("return;\n", .{});
230 return null;368 return null;
231}369}
232370
233fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {371fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
234 return ctx.fail(ctx.decl.src(), "TODO return", .{});372 try indent(file);
373 const writer = file.main.writer();
374 try writer.writeAll("return ");
375 try genValue(ctx, writer, inst.operand);
376 try writer.writeAll(";\n");
377 return null;
378}
379
380fn genValue(ctx: *Context, writer: Writer, inst: *Inst) !void {
381 if (inst.value()) |val| {
382 try renderValue(ctx, writer, inst.ty, val);
383 return;
384 }
385 return ctx.fail(ctx.decl.src(), "TODO: C backend: genValue for non-constant value", .{});
235}386}
236387
237fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {388fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
238 if (inst.base.isUnused())389 if (inst.base.isUnused())
239 return null;390 return null;
391 try indent(file);
240 const op = inst.operand;392 const op = inst.operand;
241 const writer = file.main.writer();393 const writer = file.main.writer();
242 const name = try ctx.name();394 const name = try ctx.name();
243 const from = try ctx.resolveInst(inst.operand);395 const from = try ctx.resolveInst(inst.operand);
244 try writer.writeAll(indentation ++ "const ");396 try writer.writeAll("const ");
245 try renderType(ctx, &file.header, writer, inst.base.ty);397 try renderType(ctx, writer, inst.base.ty);
246 try writer.print(" {} = (", .{name});398 try writer.print(" {} = (", .{name});
247 try renderType(ctx, &file.header, writer, inst.base.ty);399 try renderType(ctx, writer, inst.base.ty);
248 try writer.print("){};\n", .{from});400 try writer.print("){};\n", .{from});
249 return name;401 return name;
250}402}
...@@ -252,54 +404,57 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {...@@ -252,54 +404,57 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
252fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {404fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {
253 if (inst.base.isUnused())405 if (inst.base.isUnused())
254 return null;406 return null;
407 try indent(file);
255 const lhs = ctx.resolveInst(inst.lhs);408 const lhs = ctx.resolveInst(inst.lhs);
256 const rhs = ctx.resolveInst(inst.rhs);409 const rhs = ctx.resolveInst(inst.rhs);
257 const writer = file.main.writer();410 const writer = file.main.writer();
258 const name = try ctx.name();411 const name = try ctx.name();
259 try writer.writeAll(indentation ++ "const ");412 try writer.writeAll("const ");
260 try renderType(ctx, &file.header, writer, inst.base.ty);413 try renderType(ctx, writer, inst.base.ty);
261 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });414 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
262 return name;415 return name;
263}416}
264417
265fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {418fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
419 try indent(file);
266 const writer = file.main.writer();420 const writer = file.main.writer();
267 const header = file.header.buf.writer();421 const header = file.header.buf.writer();
268 try writer.writeAll(indentation);
269 if (inst.func.castTag(.constant)) |func_inst| {422 if (inst.func.castTag(.constant)) |func_inst| {
270 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {423 const fn_decl = if (func_inst.val.cast(Value.Payload.ExternFn)) |extern_fn|
271 const target = func_val.func.owner_decl;424 extern_fn.decl
272 const target_ty = target.typed_value.most_recent.typed_value.ty;425 else if (func_inst.val.cast(Value.Payload.Function)) |func_val|
273 const ret_ty = target_ty.fnReturnType().tag();426 func_val.func.owner_decl
274 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {427 else
275 try writer.print("(void)", .{});428 unreachable;
276 }429
277 const tname = mem.spanZ(target.name);430 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
278 if (file.called.get(tname) == null) {431 const ret_ty = fn_ty.fnReturnType().tag();
279 try file.called.put(tname, void{});432 if (fn_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
280 try renderFunctionSignature(ctx, &file.header, header, target);433 try writer.print("(void)", .{});
281 try header.writeAll(";\n");434 }
282 }435 const fn_name = mem.spanZ(fn_decl.name);
283 try writer.print("{}(", .{tname});436 if (file.called.get(fn_name) == null) {
284 if (inst.args.len != 0) {437 try file.called.put(fn_name, void{});
285 for (inst.args) |arg, i| {438 try renderFunctionSignature(ctx, header, fn_decl);
286 if (i > 0) {439 try header.writeAll(";\n");
287 try writer.writeAll(", ");440 }
288 }441 try writer.print("{s}(", .{fn_name});
289 if (arg.cast(Inst.Constant)) |con| {442 if (inst.args.len != 0) {
290 try renderValue(ctx, writer, arg.ty, con.val);443 for (inst.args) |arg, i| {
291 } else {444 if (i > 0) {
292 const val = try ctx.resolveInst(arg);445 try writer.writeAll(", ");
293 try writer.print("{}", .{val});446 }
294 }447 if (arg.cast(Inst.Constant)) |con| {
448 try renderValue(ctx, writer, arg.ty, con.val);
449 } else {
450 const val = try ctx.resolveInst(arg);
451 try writer.print("{}", .{val});
295 }452 }
296 }453 }
297 try writer.writeAll(");\n");
298 } else {
299 return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{});
300 }454 }
455 try writer.writeAll(");\n");
301 } else {456 } else {
302 return ctx.fail(ctx.decl.src(), "TODO non-constant call inst?", .{});457 return ctx.fail(ctx.decl.src(), "TODO: C backend: implement function pointers", .{});
303 }458 }
304 return null;459 return null;
305}460}
...@@ -309,25 +464,27 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {...@@ -309,25 +464,27 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
309 return null;464 return null;
310}465}
311466
312fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {467fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
313 // TODO ??468 try indent(file);
469 try file.main.writer().writeAll("zig_breakpoint();\n");
314 return null;470 return null;
315}471}
316472
317fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {473fn genUnreach(file: *C, inst: *Inst.NoOp) !?[]u8 {
318 try file.main.writer().writeAll(indentation ++ "zig_unreachable();\n");474 try indent(file);
475 try file.main.writer().writeAll("zig_unreachable();\n");
319 return null;476 return null;
320}477}
321478
322fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {479fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
480 try indent(file);
323 const writer = file.main.writer();481 const writer = file.main.writer();
324 try writer.writeAll(indentation);
325 for (as.inputs) |i, index| {482 for (as.inputs) |i, index| {
326 if (i[0] == '{' and i[i.len - 1] == '}') {483 if (i[0] == '{' and i[i.len - 1] == '}') {
327 const reg = i[1 .. i.len - 1];484 const reg = i[1 .. i.len - 1];
328 const arg = as.args[index];485 const arg = as.args[index];
329 try writer.writeAll("register ");486 try writer.writeAll("register ");
330 try renderType(ctx, &file.header, writer, arg.ty);487 try renderType(ctx, writer, arg.ty);
331 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });488 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
332 // TODO merge constant handling into inst_map as well489 // TODO merge constant handling into inst_map as well
333 if (arg.castTag(.constant)) |c| {490 if (arg.castTag(.constant)) |c| {
src/link/C.zig+1-15
...@@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c;...@@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c;
1515
16pub const Header = struct {16pub const Header = struct {
17 buf: std.ArrayList(u8),17 buf: std.ArrayList(u8),
18 need_stddef: bool = false,
19 need_stdint: bool = false,
20 emit_loc: ?Compilation.EmitLoc,18 emit_loc: ?Compilation.EmitLoc,
2119
22 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {20 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
...@@ -31,20 +29,8 @@ pub const Header = struct {...@@ -31,20 +29,8 @@ pub const Header = struct {
31 defer tracy.end();29 defer tracy.end();
3230
33 try writer.writeAll(@embedFile("cbe.h"));31 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) {32 if (self.buf.items.len > 0) {
47 try writer.print("{}", .{self.buf.items});33 try writer.print("{s}", .{self.buf.items});
48 }34 }
49 }35 }
5036
src/link/cbe.h+21-2
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1#if __STDC_VERSION__ >= 199901L1#if __STDC_VERSION__ >= 199901L
2// C99 or newer
3#include <stdbool.h>2#include <stdbool.h>
4#else3#else
5#define bool unsigned char4#define bool unsigned char
...@@ -17,9 +16,29 @@...@@ -17,9 +16,29 @@
17#define zig_noreturn16#define zig_noreturn
18#endif17#endif
1918
20#if __GNUC__19#if defined(__GNUC__)
21#define zig_unreachable() __builtin_unreachable()20#define zig_unreachable() __builtin_unreachable()
22#else21#else
23#define zig_unreachable()22#define zig_unreachable()
24#endif23#endif
2524
25#if defined(_MSC_VER)
26#define zig_breakpoint __debugbreak()
27#else
28#if defined(__MINGW32__) || defined(__MINGW64__)
29#define zig_breakpoint __debugbreak()
30#elif defined(__clang__)
31#define zig_breakpoint __builtin_debugtrap()
32#elif defined(__GNUC__)
33#define zig_breakpoint __builtin_trap()
34#elif defined(__i386__) || defined(__x86_64__)
35#define zig_breakpoint __asm__ volatile("int $0x03");
36#else
37#define zig_breakpoint raise(SIGTRAP)
38#endif
39#endif
40
41#include <stdint.h>
42#define int128_t __int128
43#define uint128_t unsigned __int128
44
src/main.zig+3-1
...@@ -1828,7 +1828,9 @@ fn buildOutputType(...@@ -1828,7 +1828,9 @@ fn buildOutputType(
1828 else => unreachable,1828 else => unreachable,
1829 }1829 }
1830 }1830 }
1831 try argv.append(exe_path);1831 try argv.appendSlice(&[_][]const u8{
1832 exe_path, self_exe_path,
1833 });
1832 } else {1834 } else {
1833 for (test_exec_args.items) |arg| {1835 for (test_exec_args.items) |arg| {
1834 try argv.append(arg orelse exe_path);1836 try argv.append(arg orelse exe_path);
src/test.zig+116-121
...@@ -11,8 +11,9 @@ const enable_wine: bool = build_options.enable_wine;...@@ -11,8 +11,9 @@ const enable_wine: bool = build_options.enable_wine;
11const enable_wasmtime: bool = build_options.enable_wasmtime;11const enable_wasmtime: bool = build_options.enable_wasmtime;
12const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;12const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
13const ThreadPool = @import("ThreadPool.zig");13const ThreadPool = @import("ThreadPool.zig");
14const CrossTarget = std.zig.CrossTarget;
1415
15const cheader = @embedFile("link/cbe.h");16const c_header = @embedFile("link/cbe.h");
1617
17test "self-hosted" {18test "self-hosted" {
18 var ctx = TestContext.init();19 var ctx = TestContext.init();
...@@ -88,6 +89,9 @@ pub const TestContext = struct {...@@ -88,6 +89,9 @@ pub const TestContext = struct {
88 /// A transformation update transforms the input and tests against89 /// A transformation update transforms the input and tests against
89 /// the expected output ZIR.90 /// the expected output ZIR.
90 Transformation: [:0]const u8,91 Transformation: [:0]const u8,
92 /// Check the main binary output file against an expected set of bytes.
93 /// This is most useful with, for example, `-ofmt=c`.
94 CompareObjectFile: []const u8,
91 /// An error update attempts to compile bad code, and ensures that it95 /// An error update attempts to compile bad code, and ensures that it
92 /// fails to compile, and for the expected reasons.96 /// fails to compile, and for the expected reasons.
93 /// A slice containing the expected errors *in sequential order*.97 /// A slice containing the expected errors *in sequential order*.
...@@ -109,12 +113,12 @@ pub const TestContext = struct {...@@ -109,12 +113,12 @@ pub const TestContext = struct {
109 path: []const u8,113 path: []const u8,
110 };114 };
111115
112 pub const TestType = enum {116 pub const Extension = enum {
113 Zig,117 Zig,
114 ZIR,118 ZIR,
115 };119 };
116120
117 /// A Case consists of a set of *updates*. The same Compilation is used for each121 /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each
118 /// update, so each update's source is treated as a single file being122 /// update, so each update's source is treated as a single file being
119 /// updated by the test harness and incrementally compiled.123 /// updated by the test harness and incrementally compiled.
120 pub const Case = struct {124 pub const Case = struct {
...@@ -123,13 +127,14 @@ pub const TestContext = struct {...@@ -123,13 +127,14 @@ pub const TestContext = struct {
123 name: []const u8,127 name: []const u8,
124 /// The platform the test targets. For non-native platforms, an emulator128 /// The platform the test targets. For non-native platforms, an emulator
125 /// such as QEMU is required for tests to complete.129 /// such as QEMU is required for tests to complete.
126 target: std.zig.CrossTarget,130 target: CrossTarget,
127 /// In order to be able to run e.g. Execution updates, this must be set131 /// In order to be able to run e.g. Execution updates, this must be set
128 /// to Executable.132 /// to Executable.
129 output_mode: std.builtin.OutputMode,133 output_mode: std.builtin.OutputMode,
130 updates: std.ArrayList(Update),134 updates: std.ArrayList(Update),
131 extension: TestType,135 extension: Extension,
132 cbe: bool = false,136 object_format: ?std.builtin.ObjectFormat = null,
137 emit_h: bool = false,
133138
134 files: std.ArrayList(File),139 files: std.ArrayList(File),
135140
...@@ -145,6 +150,7 @@ pub const TestContext = struct {...@@ -145,6 +150,7 @@ pub const TestContext = struct {
145 /// Adds a subcase in which the module is updated with `src`, and a C150 /// Adds a subcase in which the module is updated with `src`, and a C
146 /// header is generated.151 /// header is generated.
147 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {152 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
153 self.emit_h = true;
148 self.updates.append(.{154 self.updates.append(.{
149 .src = src,155 .src = src,
150 .case = .{ .Header = result },156 .case = .{ .Header = result },
...@@ -160,6 +166,15 @@ pub const TestContext = struct {...@@ -160,6 +166,15 @@ pub const TestContext = struct {
160 }) catch unreachable;166 }) catch unreachable;
161 }167 }
162168
169 /// Adds a subcase in which the module is updated with `src`, compiled,
170 /// and the object file data is compared against `result`.
171 pub fn addCompareObjectFile(self: *Case, src: [:0]const u8, result: []const u8) void {
172 self.updates.append(.{
173 .src = src,
174 .case = .{ .CompareObjectFile = result },
175 }) catch unreachable;
176 }
177
163 /// Adds a subcase in which the module is updated with `src`, which178 /// Adds a subcase in which the module is updated with `src`, which
164 /// should contain invalid input, and ensures that compilation fails179 /// should contain invalid input, and ensures that compilation fails
165 /// for the expected reasons, given in sequential order in `errors` in180 /// for the expected reasons, given in sequential order in `errors` in
...@@ -214,86 +229,100 @@ pub const TestContext = struct {...@@ -214,86 +229,100 @@ pub const TestContext = struct {
214 pub fn addExe(229 pub fn addExe(
215 ctx: *TestContext,230 ctx: *TestContext,
216 name: []const u8,231 name: []const u8,
217 target: std.zig.CrossTarget,232 target: CrossTarget,
218 T: TestType,233 extension: Extension,
219 ) *Case {234 ) *Case {
220 ctx.cases.append(Case{235 ctx.cases.append(Case{
221 .name = name,236 .name = name,
222 .target = target,237 .target = target,
223 .updates = std.ArrayList(Update).init(ctx.cases.allocator),238 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
224 .output_mode = .Exe,239 .output_mode = .Exe,
225 .extension = T,240 .extension = extension,
226 .files = std.ArrayList(File).init(ctx.cases.allocator),241 .files = std.ArrayList(File).init(ctx.cases.allocator),
227 }) catch unreachable;242 }) catch unreachable;
228 return &ctx.cases.items[ctx.cases.items.len - 1];243 return &ctx.cases.items[ctx.cases.items.len - 1];
229 }244 }
230245
231 /// Adds a test case for Zig input, producing an executable246 /// Adds a test case for Zig input, producing an executable
232 pub fn exe(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {247 pub fn exe(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
233 return ctx.addExe(name, target, .Zig);248 return ctx.addExe(name, target, .Zig);
234 }249 }
235250
236 /// Adds a test case for ZIR input, producing an executable251 /// Adds a test case for ZIR input, producing an executable
237 pub fn exeZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {252 pub fn exeZIR(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
238 return ctx.addExe(name, target, .ZIR);253 return ctx.addExe(name, target, .ZIR);
239 }254 }
240255
256 pub fn exeFromCompiledC(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
257 ctx.cases.append(Case{
258 .name = name,
259 .target = target,
260 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
261 .output_mode = .Exe,
262 .extension = .Zig,
263 .object_format = .c,
264 .files = std.ArrayList(File).init(ctx.cases.allocator),
265 }) catch unreachable;
266 return &ctx.cases.items[ctx.cases.items.len - 1];
267 }
268
241 pub fn addObj(269 pub fn addObj(
242 ctx: *TestContext,270 ctx: *TestContext,
243 name: []const u8,271 name: []const u8,
244 target: std.zig.CrossTarget,272 target: CrossTarget,
245 T: TestType,273 extension: Extension,
246 ) *Case {274 ) *Case {
247 ctx.cases.append(Case{275 ctx.cases.append(Case{
248 .name = name,276 .name = name,
249 .target = target,277 .target = target,
250 .updates = std.ArrayList(Update).init(ctx.cases.allocator),278 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
251 .output_mode = .Obj,279 .output_mode = .Obj,
252 .extension = T,280 .extension = extension,
253 .files = std.ArrayList(File).init(ctx.cases.allocator),281 .files = std.ArrayList(File).init(ctx.cases.allocator),
254 }) catch unreachable;282 }) catch unreachable;
255 return &ctx.cases.items[ctx.cases.items.len - 1];283 return &ctx.cases.items[ctx.cases.items.len - 1];
256 }284 }
257285
258 /// Adds a test case for Zig input, producing an object file286 /// Adds a test case for Zig input, producing an object file.
259 pub fn obj(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {287 pub fn obj(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
260 return ctx.addObj(name, target, .Zig);288 return ctx.addObj(name, target, .Zig);
261 }289 }
262290
263 /// Adds a test case for ZIR input, producing an object file291 /// Adds a test case for ZIR input, producing an object file.
264 pub fn objZIR(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget) *Case {292 pub fn objZIR(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
265 return ctx.addObj(name, target, .ZIR);293 return ctx.addObj(name, target, .ZIR);
266 }294 }
267295
268 pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {296 /// Adds a test case for Zig or ZIR input, producing C code.
297 pub fn addC(ctx: *TestContext, name: []const u8, target: CrossTarget, ext: Extension) *Case {
269 ctx.cases.append(Case{298 ctx.cases.append(Case{
270 .name = name,299 .name = name,
271 .target = target,300 .target = target,
272 .updates = std.ArrayList(Update).init(ctx.cases.allocator),301 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
273 .output_mode = .Obj,302 .output_mode = .Obj,
274 .extension = T,303 .extension = ext,
275 .cbe = true,304 .object_format = .c,
276 .files = std.ArrayList(File).init(ctx.cases.allocator),305 .files = std.ArrayList(File).init(ctx.cases.allocator),
277 }) catch unreachable;306 }) catch unreachable;
278 return &ctx.cases.items[ctx.cases.items.len - 1];307 return &ctx.cases.items[ctx.cases.items.len - 1];
279 }308 }
280309
281 pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {310 pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
282 ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);311 ctx.addC(name, target, .Zig).addCompareObjectFile(src, c_header ++ out);
283 }312 }
284313
285 pub fn h(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {314 pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
286 ctx.addC(name, target, .Zig).addHeader(src, cheader ++ out);315 ctx.addC(name, target, .Zig).addHeader(src, c_header ++ out);
287 }316 }
288317
289 pub fn addCompareOutput(318 pub fn addCompareOutput(
290 ctx: *TestContext,319 ctx: *TestContext,
291 name: []const u8,320 name: []const u8,
292 T: TestType,321 extension: Extension,
293 src: [:0]const u8,322 src: [:0]const u8,
294 expected_stdout: []const u8,323 expected_stdout: []const u8,
295 ) void {324 ) void {
296 ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);325 ctx.addExe(name, .{}, extension).addCompareOutput(src, expected_stdout);
297 }326 }
298327
299 /// Adds a test case that compiles the Zig source given in `src`, executes328 /// Adds a test case that compiles the Zig source given in `src`, executes
...@@ -321,12 +350,12 @@ pub const TestContext = struct {...@@ -321,12 +350,12 @@ pub const TestContext = struct {
321 pub fn addTransform(350 pub fn addTransform(
322 ctx: *TestContext,351 ctx: *TestContext,
323 name: []const u8,352 name: []const u8,
324 target: std.zig.CrossTarget,353 target: CrossTarget,
325 T: TestType,354 extension: Extension,
326 src: [:0]const u8,355 src: [:0]const u8,
327 result: [:0]const u8,356 result: [:0]const u8,
328 ) void {357 ) void {
329 ctx.addObj(name, target, T).addTransform(src, result);358 ctx.addObj(name, target, extension).addTransform(src, result);
330 }359 }
331360
332 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests361 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
...@@ -334,7 +363,7 @@ pub const TestContext = struct {...@@ -334,7 +363,7 @@ pub const TestContext = struct {
334 pub fn transform(363 pub fn transform(
335 ctx: *TestContext,364 ctx: *TestContext,
336 name: []const u8,365 name: []const u8,
337 target: std.zig.CrossTarget,366 target: CrossTarget,
338 src: [:0]const u8,367 src: [:0]const u8,
339 result: [:0]const u8,368 result: [:0]const u8,
340 ) void {369 ) void {
...@@ -346,7 +375,7 @@ pub const TestContext = struct {...@@ -346,7 +375,7 @@ pub const TestContext = struct {
346 pub fn transformZIR(375 pub fn transformZIR(
347 ctx: *TestContext,376 ctx: *TestContext,
348 name: []const u8,377 name: []const u8,
349 target: std.zig.CrossTarget,378 target: CrossTarget,
350 src: [:0]const u8,379 src: [:0]const u8,
351 result: [:0]const u8,380 result: [:0]const u8,
352 ) void {381 ) void {
...@@ -356,12 +385,12 @@ pub const TestContext = struct {...@@ -356,12 +385,12 @@ pub const TestContext = struct {
356 pub fn addError(385 pub fn addError(
357 ctx: *TestContext,386 ctx: *TestContext,
358 name: []const u8,387 name: []const u8,
359 target: std.zig.CrossTarget,388 target: CrossTarget,
360 T: TestType,389 extension: Extension,
361 src: [:0]const u8,390 src: [:0]const u8,
362 expected_errors: []const []const u8,391 expected_errors: []const []const u8,
363 ) void {392 ) void {
364 ctx.addObj(name, target, T).addError(src, expected_errors);393 ctx.addObj(name, target, extension).addError(src, expected_errors);
365 }394 }
366395
367 /// Adds a test case that ensures that the Zig given in `src` fails to396 /// Adds a test case that ensures that the Zig given in `src` fails to
...@@ -370,7 +399,7 @@ pub const TestContext = struct {...@@ -370,7 +399,7 @@ pub const TestContext = struct {
370 pub fn compileError(399 pub fn compileError(
371 ctx: *TestContext,400 ctx: *TestContext,
372 name: []const u8,401 name: []const u8,
373 target: std.zig.CrossTarget,402 target: CrossTarget,
374 src: [:0]const u8,403 src: [:0]const u8,
375 expected_errors: []const []const u8,404 expected_errors: []const []const u8,
376 ) void {405 ) void {
...@@ -383,7 +412,7 @@ pub const TestContext = struct {...@@ -383,7 +412,7 @@ pub const TestContext = struct {
383 pub fn compileErrorZIR(412 pub fn compileErrorZIR(
384 ctx: *TestContext,413 ctx: *TestContext,
385 name: []const u8,414 name: []const u8,
386 target: std.zig.CrossTarget,415 target: CrossTarget,
387 src: [:0]const u8,416 src: [:0]const u8,
388 expected_errors: []const []const u8,417 expected_errors: []const []const u8,
389 ) void {418 ) void {
...@@ -393,11 +422,11 @@ pub const TestContext = struct {...@@ -393,11 +422,11 @@ pub const TestContext = struct {
393 pub fn addCompiles(422 pub fn addCompiles(
394 ctx: *TestContext,423 ctx: *TestContext,
395 name: []const u8,424 name: []const u8,
396 target: std.zig.CrossTarget,425 target: CrossTarget,
397 T: TestType,426 extension: Extension,
398 src: [:0]const u8,427 src: [:0]const u8,
399 ) void {428 ) void {
400 ctx.addObj(name, target, T).compiles(src);429 ctx.addObj(name, target, extension).compiles(src);
401 }430 }
402431
403 /// Adds a test case that asserts that the Zig given in `src` compiles432 /// Adds a test case that asserts that the Zig given in `src` compiles
...@@ -405,7 +434,7 @@ pub const TestContext = struct {...@@ -405,7 +434,7 @@ pub const TestContext = struct {
405 pub fn compiles(434 pub fn compiles(
406 ctx: *TestContext,435 ctx: *TestContext,
407 name: []const u8,436 name: []const u8,
408 target: std.zig.CrossTarget,437 target: CrossTarget,
409 src: [:0]const u8,438 src: [:0]const u8,
410 ) void {439 ) void {
411 ctx.addCompiles(name, target, .Zig, src);440 ctx.addCompiles(name, target, .Zig, src);
...@@ -416,7 +445,7 @@ pub const TestContext = struct {...@@ -416,7 +445,7 @@ pub const TestContext = struct {
416 pub fn compilesZIR(445 pub fn compilesZIR(
417 ctx: *TestContext,446 ctx: *TestContext,
418 name: []const u8,447 name: []const u8,
419 target: std.zig.CrossTarget,448 target: CrossTarget,
420 src: [:0]const u8,449 src: [:0]const u8,
421 ) void {450 ) void {
422 ctx.addCompiles(name, target, .ZIR, src);451 ctx.addCompiles(name, target, .ZIR, src);
...@@ -430,7 +459,7 @@ pub const TestContext = struct {...@@ -430,7 +459,7 @@ pub const TestContext = struct {
430 pub fn incrementalFailure(459 pub fn incrementalFailure(
431 ctx: *TestContext,460 ctx: *TestContext,
432 name: []const u8,461 name: []const u8,
433 target: std.zig.CrossTarget,462 target: CrossTarget,
434 src: [:0]const u8,463 src: [:0]const u8,
435 expected_errors: []const []const u8,464 expected_errors: []const []const u8,
436 fixed_src: [:0]const u8,465 fixed_src: [:0]const u8,
...@@ -448,7 +477,7 @@ pub const TestContext = struct {...@@ -448,7 +477,7 @@ pub const TestContext = struct {
448 pub fn incrementalFailureZIR(477 pub fn incrementalFailureZIR(
449 ctx: *TestContext,478 ctx: *TestContext,
450 name: []const u8,479 name: []const u8,
451 target: std.zig.CrossTarget,480 target: CrossTarget,
452 src: [:0]const u8,481 src: [:0]const u8,
453 expected_errors: []const []const u8,482 expected_errors: []const []const u8,
454 fixed_src: [:0]const u8,483 fixed_src: [:0]const u8,
...@@ -548,12 +577,11 @@ pub const TestContext = struct {...@@ -548,12 +577,11 @@ pub const TestContext = struct {
548 .root_src_path = tmp_src_path,577 .root_src_path = tmp_src_path,
549 };578 };
550579
551 const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
552 const bin_name = try std.zig.binNameAlloc(arena, .{580 const bin_name = try std.zig.binNameAlloc(arena, .{
553 .root_name = "test_case",581 .root_name = "test_case",
554 .target = target,582 .target = target,
555 .output_mode = case.output_mode,583 .output_mode = case.output_mode,
556 .object_format = ofmt,584 .object_format = case.object_format,
557 });585 });
558586
559 const emit_directory: Compilation.Directory = .{587 const emit_directory: Compilation.Directory = .{
...@@ -564,7 +592,7 @@ pub const TestContext = struct {...@@ -564,7 +592,7 @@ pub const TestContext = struct {
564 .directory = emit_directory,592 .directory = emit_directory,
565 .basename = bin_name,593 .basename = bin_name,
566 };594 };
567 const emit_h: ?Compilation.EmitLoc = if (case.cbe)595 const emit_h: ?Compilation.EmitLoc = if (case.emit_h)
568 .{596 .{
569 .directory = emit_directory,597 .directory = emit_directory,
570 .basename = "test_case.h",598 .basename = "test_case.h",
...@@ -588,7 +616,7 @@ pub const TestContext = struct {...@@ -588,7 +616,7 @@ pub const TestContext = struct {
588 .emit_h = emit_h,616 .emit_h = emit_h,
589 .root_pkg = &root_pkg,617 .root_pkg = &root_pkg,
590 .keep_source_files_loaded = true,618 .keep_source_files_loaded = true,
591 .object_format = ofmt,619 .object_format = case.object_format,
592 .is_native_os = case.target.isNativeOs(),620 .is_native_os = case.target.isNativeOs(),
593 .is_native_abi = case.target.isNativeAbi(),621 .is_native_abi = case.target.isNativeAbi(),
594 });622 });
...@@ -631,9 +659,10 @@ pub const TestContext = struct {...@@ -631,9 +659,10 @@ pub const TestContext = struct {
631 },659 },
632 }660 }
633 }661 }
634 if (case.cbe) {662 if (comp.bin_file.cast(link.File.C)) |c_file| {
635 const C = comp.bin_file.cast(link.File.C).?;663 std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{
636 std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});664 c_file.main.items,
665 });
637 }666 }
638 std.debug.print("Test failed.\n", .{});667 std.debug.print("Test failed.\n", .{});
639 std.process.exit(1);668 std.process.exit(1);
...@@ -644,67 +673,37 @@ pub const TestContext = struct {...@@ -644,67 +673,37 @@ pub const TestContext = struct {
644 .Header => |expected_output| {673 .Header => |expected_output| {
645 var file = try tmp.dir.openFile("test_case.h", .{ .read = true });674 var file = try tmp.dir.openFile("test_case.h", .{ .read = true });
646 defer file.close();675 defer file.close();
647 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read headeroutput!");676 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
648677
649 if (expected_output.len != out.len) {678 std.testing.expectEqualStrings(expected_output, out);
650 std.debug.print("\nTransformed header length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });679 },
651 std.process.exit(1);680 .CompareObjectFile => |expected_output| {
652 }681 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
653 for (expected_output) |e, i| {682 defer file.close();
654 if (out[i] != e) {683 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
655 std.debug.print("\nTransformed header differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });684
656 std.process.exit(1);685 std.testing.expectEqualStrings(expected_output, out);
657 }
658 }
659 },686 },
660 .Transformation => |expected_output| {687 .Transformation => |expected_output| {
661 if (case.cbe) {688 update_node.setEstimatedTotalItems(5);
662 // The C file is always closed after an update, because we don't support689 var emit_node = update_node.start("emit", 0);
663 // incremental updates690 emit_node.activate();
664 var file = try tmp.dir.openFile(bin_name, .{ .read = true });691 var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?);
665 defer file.close();692 defer new_zir_module.deinit(allocator);
666 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");693 emit_node.end();
667694
668 if (expected_output.len != out.len) {695 var write_node = update_node.start("write", 0);
669 std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });696 write_node.activate();
670 std.process.exit(1);697 var out_zir = std.ArrayList(u8).init(allocator);
671 }698 defer out_zir.deinit();
672 for (expected_output) |e, i| {699 try new_zir_module.writeToStream(allocator, out_zir.outStream());
673 if (out[i] != e) {700 write_node.end();
674 std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });701
675 std.process.exit(1);702 var test_node = update_node.start("assert", 0);
676 }703 test_node.activate();
677 }704 defer test_node.end();
678 } else {705
679 update_node.setEstimatedTotalItems(5);706 std.testing.expectEqualStrings(expected_output, out_zir.items);
680 var emit_node = update_node.start("emit", 0);
681 emit_node.activate();
682 var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?);
683 defer new_zir_module.deinit(allocator);
684 emit_node.end();
685
686 var write_node = update_node.start("write", 0);
687 write_node.activate();
688 var out_zir = std.ArrayList(u8).init(allocator);
689 defer out_zir.deinit();
690 try new_zir_module.writeToStream(allocator, out_zir.outStream());
691 write_node.end();
692
693 var test_node = update_node.start("assert", 0);
694 test_node.activate();
695 defer test_node.end();
696
697 if (expected_output.len != out_zir.items.len) {
698 std.debug.print("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
699 std.process.exit(1);
700 }
701 for (expected_output) |e, i| {
702 if (out_zir.items[i] != e) {
703 std.debug.print("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
704 std.process.exit(1);
705 }
706 }
707 }
708 },707 },
709 .Error => |e| {708 .Error => |e| {
710 var test_node = update_node.start("assert", 0);709 var test_node = update_node.start("assert", 0);
...@@ -762,8 +761,6 @@ pub const TestContext = struct {...@@ -762,8 +761,6 @@ pub const TestContext = struct {
762 }761 }
763 },762 },
764 .Execution => |expected_stdout| {763 .Execution => |expected_stdout| {
765 std.debug.assert(!case.cbe);
766
767 update_node.setEstimatedTotalItems(4);764 update_node.setEstimatedTotalItems(4);
768 var exec_result = x: {765 var exec_result = x: {
769 var exec_node = update_node.start("execute", 0);766 var exec_node = update_node.start("execute", 0);
...@@ -773,9 +770,12 @@ pub const TestContext = struct {...@@ -773,9 +770,12 @@ pub const TestContext = struct {
773 var argv = std.ArrayList([]const u8).init(allocator);770 var argv = std.ArrayList([]const u8).init(allocator);
774 defer argv.deinit();771 defer argv.deinit();
775772
776 const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});773 const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{s}", .{bin_name});
777774 if (case.object_format != null and case.object_format.? == .c) {
778 switch (case.target.getExternalExecutor()) {775 try argv.appendSlice(&[_][]const u8{
776 std.testing.zig_exe_path, "run", exe_path, "-lc",
777 });
778 } else switch (case.target.getExternalExecutor()) {
779 .native => try argv.append(exe_path),779 .native => try argv.append(exe_path),
780 .unavailable => {780 .unavailable => {
781 try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name);781 try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name);
...@@ -837,18 +837,13 @@ pub const TestContext = struct {...@@ -837,18 +837,13 @@ pub const TestContext = struct {
837 switch (exec_result.term) {837 switch (exec_result.term) {
838 .Exited => |code| {838 .Exited => |code| {
839 if (code != 0) {839 if (code != 0) {
840 std.debug.print("elf file exited with code {}\n", .{code});840 std.debug.print("execution exited with code {}\n", .{code});
841 return error.BinaryBadExitCode;841 return error.BinaryBadExitCode;
842 }842 }
843 },843 },
844 else => return error.BinaryCrashed,844 else => return error.BinaryCrashed,
845 }845 }
846 if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {846 std.testing.expectEqualStrings(expected_stdout, exec_result.stdout);
847 std.debug.panic(
848 "update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
849 .{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
850 );
851 }
852 },847 },
853 }848 }
854 }849 }
src/type.zig+91-1
...@@ -172,7 +172,15 @@ pub const Type = extern union {...@@ -172,7 +172,15 @@ pub const Type = extern union {
172 const is_slice_b = isSlice(b);172 const is_slice_b = isSlice(b);
173 if (is_slice_a != is_slice_b)173 if (is_slice_a != is_slice_b)
174 return false;174 return false;
175 @panic("TODO implement more pointer Type equality comparison");175
176 const ptr_size_a = ptrSize(a);
177 const ptr_size_b = ptrSize(b);
178 if (ptr_size_a != ptr_size_b)
179 return false;
180
181 std.debug.panic("TODO implement more pointer Type equality comparison: {} and {}", .{
182 a, b,
183 });
176 },184 },
177 .Int => {185 .Int => {
178 // Detect that e.g. u64 != usize, even if the bits match on a particular target.186 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
...@@ -1128,6 +1136,88 @@ pub const Type = extern union {...@@ -1128,6 +1136,88 @@ pub const Type = extern union {
1128 };1136 };
1129 }1137 }
11301138
1139 /// Asserts the `Type` is a pointer.
1140 pub fn ptrSize(self: Type) std.builtin.TypeInfo.Pointer.Size {
1141 return switch (self.tag()) {
1142 .u8,
1143 .i8,
1144 .u16,
1145 .i16,
1146 .u32,
1147 .i32,
1148 .u64,
1149 .i64,
1150 .usize,
1151 .isize,
1152 .c_short,
1153 .c_ushort,
1154 .c_int,
1155 .c_uint,
1156 .c_long,
1157 .c_ulong,
1158 .c_longlong,
1159 .c_ulonglong,
1160 .c_longdouble,
1161 .f16,
1162 .f32,
1163 .f64,
1164 .f128,
1165 .c_void,
1166 .bool,
1167 .void,
1168 .type,
1169 .anyerror,
1170 .comptime_int,
1171 .comptime_float,
1172 .noreturn,
1173 .@"null",
1174 .@"undefined",
1175 .array,
1176 .array_sentinel,
1177 .array_u8,
1178 .array_u8_sentinel_0,
1179 .fn_noreturn_no_args,
1180 .fn_void_no_args,
1181 .fn_naked_noreturn_no_args,
1182 .fn_ccc_void_no_args,
1183 .function,
1184 .int_unsigned,
1185 .int_signed,
1186 .optional,
1187 .optional_single_mut_pointer,
1188 .optional_single_const_pointer,
1189 .enum_literal,
1190 .error_union,
1191 .@"anyframe",
1192 .anyframe_T,
1193 .anyerror_void_error_union,
1194 .error_set,
1195 .error_set_single,
1196 .empty_struct,
1197 => unreachable,
1198
1199 .const_slice,
1200 .mut_slice,
1201 .const_slice_u8,
1202 => .Slice,
1203
1204 .many_const_pointer,
1205 .many_mut_pointer,
1206 => .Many,
1207
1208 .c_const_pointer,
1209 .c_mut_pointer,
1210 => .C,
1211
1212 .single_const_pointer,
1213 .single_mut_pointer,
1214 .single_const_pointer_to_comptime_int,
1215 => .One,
1216
1217 .pointer => self.cast(Payload.Pointer).?.size,
1218 };
1219 }
1220
1131 pub fn isSlice(self: Type) bool {1221 pub fn isSlice(self: Type) bool {
1132 return switch (self.tag()) {1222 return switch (self.tag()) {
1133 .u8,1223 .u8,
src/value.zig+24
...@@ -82,6 +82,7 @@ pub const Value = extern union {...@@ -82,6 +82,7 @@ pub const Value = extern union {
82 int_big_positive,82 int_big_positive,
83 int_big_negative,83 int_big_negative,
84 function,84 function,
85 extern_fn,
85 variable,86 variable,
86 ref_val,87 ref_val,
87 decl_ref,88 decl_ref,
...@@ -205,6 +206,7 @@ pub const Value = extern union {...@@ -205,6 +206,7 @@ pub const Value = extern union {
205 @panic("TODO implement copying of big ints");206 @panic("TODO implement copying of big ints");
206 },207 },
207 .function => return self.copyPayloadShallow(allocator, Payload.Function),208 .function => return self.copyPayloadShallow(allocator, Payload.Function),
209 .extern_fn => return self.copyPayloadShallow(allocator, Payload.ExternFn),
208 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),210 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
209 .ref_val => {211 .ref_val => {
210 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);212 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
...@@ -337,6 +339,7 @@ pub const Value = extern union {...@@ -337,6 +339,7 @@ pub const Value = extern union {
337 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),339 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
338 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),340 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
339 .function => return out_stream.writeAll("(function)"),341 .function => return out_stream.writeAll("(function)"),
342 .extern_fn => return out_stream.writeAll("(extern function)"),
340 .variable => return out_stream.writeAll("(variable)"),343 .variable => return out_stream.writeAll("(variable)"),
341 .ref_val => {344 .ref_val => {
342 const ref_val = val.cast(Payload.RefVal).?;345 const ref_val = val.cast(Payload.RefVal).?;
...@@ -468,6 +471,7 @@ pub const Value = extern union {...@@ -468,6 +471,7 @@ pub const Value = extern union {
468 .int_big_positive,471 .int_big_positive,
469 .int_big_negative,472 .int_big_negative,
470 .function,473 .function,
474 .extern_fn,
471 .variable,475 .variable,
472 .ref_val,476 .ref_val,
473 .decl_ref,477 .decl_ref,
...@@ -533,6 +537,7 @@ pub const Value = extern union {...@@ -533,6 +537,7 @@ pub const Value = extern union {
533 .anyframe_type,537 .anyframe_type,
534 .null_value,538 .null_value,
535 .function,539 .function,
540 .extern_fn,
536 .variable,541 .variable,
537 .ref_val,542 .ref_val,
538 .decl_ref,543 .decl_ref,
...@@ -617,6 +622,7 @@ pub const Value = extern union {...@@ -617,6 +622,7 @@ pub const Value = extern union {
617 .anyframe_type,622 .anyframe_type,
618 .null_value,623 .null_value,
619 .function,624 .function,
625 .extern_fn,
620 .variable,626 .variable,
621 .ref_val,627 .ref_val,
622 .decl_ref,628 .decl_ref,
...@@ -701,6 +707,7 @@ pub const Value = extern union {...@@ -701,6 +707,7 @@ pub const Value = extern union {
701 .anyframe_type,707 .anyframe_type,
702 .null_value,708 .null_value,
703 .function,709 .function,
710 .extern_fn,
704 .variable,711 .variable,
705 .ref_val,712 .ref_val,
706 .decl_ref,713 .decl_ref,
...@@ -812,6 +819,7 @@ pub const Value = extern union {...@@ -812,6 +819,7 @@ pub const Value = extern union {
812 .anyframe_type,819 .anyframe_type,
813 .null_value,820 .null_value,
814 .function,821 .function,
822 .extern_fn,
815 .variable,823 .variable,
816 .ref_val,824 .ref_val,
817 .decl_ref,825 .decl_ref,
...@@ -901,6 +909,7 @@ pub const Value = extern union {...@@ -901,6 +909,7 @@ pub const Value = extern union {
901 .anyframe_type,909 .anyframe_type,
902 .null_value,910 .null_value,
903 .function,911 .function,
912 .extern_fn,
904 .variable,913 .variable,
905 .ref_val,914 .ref_val,
906 .decl_ref,915 .decl_ref,
...@@ -1071,6 +1080,7 @@ pub const Value = extern union {...@@ -1071,6 +1080,7 @@ pub const Value = extern union {
1071 .bool_false,1080 .bool_false,
1072 .null_value,1081 .null_value,
1073 .function,1082 .function,
1083 .extern_fn,
1074 .variable,1084 .variable,
1075 .ref_val,1085 .ref_val,
1076 .decl_ref,1086 .decl_ref,
...@@ -1150,6 +1160,7 @@ pub const Value = extern union {...@@ -1150,6 +1160,7 @@ pub const Value = extern union {
1150 .anyframe_type,1160 .anyframe_type,
1151 .null_value,1161 .null_value,
1152 .function,1162 .function,
1163 .extern_fn,
1153 .variable,1164 .variable,
1154 .ref_val,1165 .ref_val,
1155 .decl_ref,1166 .decl_ref,
...@@ -1383,6 +1394,10 @@ pub const Value = extern union {...@@ -1383,6 +1394,10 @@ pub const Value = extern union {
1383 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);1394 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
1384 std.hash.autoHash(&hasher, payload.func);1395 std.hash.autoHash(&hasher, payload.func);
1385 },1396 },
1397 .extern_fn => {
1398 const payload = @fieldParentPtr(Payload.ExternFn, "base", self.ptr_otherwise);
1399 std.hash.autoHash(&hasher, payload.decl);
1400 },
1386 .variable => {1401 .variable => {
1387 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);1402 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);
1388 std.hash.autoHash(&hasher, payload.variable);1403 std.hash.autoHash(&hasher, payload.variable);
...@@ -1449,6 +1464,7 @@ pub const Value = extern union {...@@ -1449,6 +1464,7 @@ pub const Value = extern union {
1449 .bool_false,1464 .bool_false,
1450 .null_value,1465 .null_value,
1451 .function,1466 .function,
1467 .extern_fn,
1452 .variable,1468 .variable,
1453 .int_u64,1469 .int_u64,
1454 .int_i64,1470 .int_i64,
...@@ -1533,6 +1549,7 @@ pub const Value = extern union {...@@ -1533,6 +1549,7 @@ pub const Value = extern union {
1533 .bool_false,1549 .bool_false,
1534 .null_value,1550 .null_value,
1535 .function,1551 .function,
1552 .extern_fn,
1536 .variable,1553 .variable,
1537 .int_u64,1554 .int_u64,
1538 .int_i64,1555 .int_i64,
...@@ -1634,6 +1651,7 @@ pub const Value = extern union {...@@ -1634,6 +1651,7 @@ pub const Value = extern union {
1634 .bool_true,1651 .bool_true,
1635 .bool_false,1652 .bool_false,
1636 .function,1653 .function,
1654 .extern_fn,
1637 .variable,1655 .variable,
1638 .int_u64,1656 .int_u64,
1639 .int_i64,1657 .int_i64,
...@@ -1730,6 +1748,7 @@ pub const Value = extern union {...@@ -1730,6 +1748,7 @@ pub const Value = extern union {
1730 .bool_true,1748 .bool_true,
1731 .bool_false,1749 .bool_false,
1732 .function,1750 .function,
1751 .extern_fn,
1733 .variable,1752 .variable,
1734 .int_u64,1753 .int_u64,
1735 .int_i64,1754 .int_i64,
...@@ -1793,6 +1812,11 @@ pub const Value = extern union {...@@ -1793,6 +1812,11 @@ pub const Value = extern union {
1793 func: *Module.Fn,1812 func: *Module.Fn,
1794 };1813 };
17951814
1815 pub const ExternFn = struct {
1816 base: Payload = Payload{ .tag = .extern_fn },
1817 decl: *Module.Decl,
1818 };
1819
1796 pub const Variable = struct {1820 pub const Variable = struct {
1797 base: Payload = Payload{ .tag = .variable },1821 base: Payload = Payload{ .tag = .variable },
1798 variable: *Module.Var,1822 variable: *Module.Var,
test/stage2/cbe.zig+63-56
...@@ -9,12 +9,37 @@ const linux_x64 = std.zig.CrossTarget{...@@ -9,12 +9,37 @@ const linux_x64 = std.zig.CrossTarget{
9};9};
1010
11pub fn addCases(ctx: *TestContext) !void {11pub fn addCases(ctx: *TestContext) !void {
12 {
13 var case = ctx.exeFromCompiledC("hello world with updates", .{});
14
15 // Regular old hello world
16 case.addCompareOutput(
17 \\extern fn puts(s: [*:0]const u8) c_int;
18 \\export fn main() c_int {
19 \\ _ = puts("hello world!");
20 \\ return 0;
21 \\}
22 , "hello world!" ++ std.cstr.line_sep);
23
24 // Now change the message only
25 // TODO fix C backend not supporting updates
26 // https://github.com/ziglang/zig/issues/7589
27 //case.addCompareOutput(
28 // \\extern fn puts(s: [*:0]const u8) c_int;
29 // \\export fn main() c_int {
30 // \\ _ = puts("yo");
31 // \\ return 0;
32 // \\}
33 //, "yo" ++ std.cstr.line_sep);
34 }
35
12 ctx.c("empty start function", linux_x64,36 ctx.c("empty start function", linux_x64,
13 \\export fn _start() noreturn {37 \\export fn _start() noreturn {
14 \\ unreachable;38 \\ unreachable;
15 \\}39 \\}
16 ,40 ,
17 \\zig_noreturn void _start(void) {41 \\zig_noreturn void _start(void) {
42 \\ zig_breakpoint();
18 \\ zig_unreachable();43 \\ zig_unreachable();
19 \\}44 \\}
20 \\45 \\
...@@ -41,6 +66,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -41,6 +66,7 @@ pub fn addCases(ctx: *TestContext) !void {
41 \\}66 \\}
42 \\67 \\
43 \\zig_noreturn void main(void) {68 \\zig_noreturn void main(void) {
69 \\ zig_breakpoint();
44 \\ zig_unreachable();70 \\ zig_unreachable();
45 \\}71 \\}
46 \\72 \\
...@@ -61,22 +87,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -61,22 +87,21 @@ pub fn addCases(ctx: *TestContext) !void {
61 \\ exitGood();87 \\ exitGood();
62 \\}88 \\}
63 ,89 ,
64 \\#include <stddef.h>
65 \\
66 \\zig_noreturn void exitGood(void);90 \\zig_noreturn void exitGood(void);
67 \\91 \\
68 \\const char *const exitGood__anon_0 = "{rax}";92 \\static uint8_t exitGood__anon_0[6] = "{rax}";
69 \\const char *const exitGood__anon_1 = "{rdi}";93 \\static uint8_t exitGood__anon_1[6] = "{rdi}";
70 \\const char *const exitGood__anon_2 = "syscall";94 \\static uint8_t exitGood__anon_2[8] = "syscall";
71 \\95 \\
72 \\zig_noreturn void _start(void) {96 \\zig_noreturn void _start(void) {
73 \\ exitGood();97 \\ exitGood();
74 \\}98 \\}
75 \\99 \\
76 \\zig_noreturn void exitGood(void) {100 \\zig_noreturn void exitGood(void) {
77 \\ register size_t rax_constant __asm__("rax") = 231;101 \\ register uintptr_t rax_constant __asm__("rax") = 231;
78 \\ register size_t rdi_constant __asm__("rdi") = 0;102 \\ register uintptr_t rdi_constant __asm__("rdi") = 0;
79 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));103 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
104 \\ zig_breakpoint();
80 \\ zig_unreachable();105 \\ zig_unreachable();
81 \\}106 \\}
82 \\107 \\
...@@ -96,22 +121,21 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -96,22 +121,21 @@ pub fn addCases(ctx: *TestContext) !void {
96 \\}121 \\}
97 \\122 \\
98 ,123 ,
99 \\#include <stddef.h>124 \\zig_noreturn void exit(uintptr_t arg0);
100 \\125 \\
101 \\zig_noreturn void exit(size_t arg0);126 \\static uint8_t exit__anon_0[6] = "{rax}";
102 \\127 \\static uint8_t exit__anon_1[6] = "{rdi}";
103 \\const char *const exit__anon_0 = "{rax}";128 \\static uint8_t exit__anon_2[8] = "syscall";
104 \\const char *const exit__anon_1 = "{rdi}";
105 \\const char *const exit__anon_2 = "syscall";
106 \\129 \\
107 \\zig_noreturn void _start(void) {130 \\zig_noreturn void _start(void) {
108 \\ exit(0);131 \\ exit(0);
109 \\}132 \\}
110 \\133 \\
111 \\zig_noreturn void exit(size_t arg0) {134 \\zig_noreturn void exit(uintptr_t arg0) {
112 \\ register size_t rax_constant __asm__("rax") = 231;135 \\ register uintptr_t rax_constant __asm__("rax") = 231;
113 \\ register size_t rdi_constant __asm__("rdi") = arg0;136 \\ register uintptr_t rdi_constant __asm__("rdi") = arg0;
114 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));137 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
138 \\ zig_breakpoint();
115 \\ zig_unreachable();139 \\ zig_unreachable();
116 \\}140 \\}
117 \\141 \\
...@@ -131,24 +155,22 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -131,24 +155,22 @@ pub fn addCases(ctx: *TestContext) !void {
131 \\}155 \\}
132 \\156 \\
133 ,157 ,
134 \\#include <stddef.h>
135 \\#include <stdint.h>
136 \\
137 \\zig_noreturn void exit(uint8_t arg0);158 \\zig_noreturn void exit(uint8_t arg0);
138 \\159 \\
139 \\const char *const exit__anon_0 = "{rax}";160 \\static uint8_t exit__anon_0[6] = "{rax}";
140 \\const char *const exit__anon_1 = "{rdi}";161 \\static uint8_t exit__anon_1[6] = "{rdi}";
141 \\const char *const exit__anon_2 = "syscall";162 \\static uint8_t exit__anon_2[8] = "syscall";
142 \\163 \\
143 \\zig_noreturn void _start(void) {164 \\zig_noreturn void _start(void) {
144 \\ exit(0);165 \\ exit(0);
145 \\}166 \\}
146 \\167 \\
147 \\zig_noreturn void exit(uint8_t arg0) {168 \\zig_noreturn void exit(uint8_t arg0) {
148 \\ const size_t __temp_0 = (size_t)arg0;169 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;
149 \\ register size_t rax_constant __asm__("rax") = 231;170 \\ register uintptr_t rax_constant __asm__("rax") = 231;
150 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;171 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
151 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));172 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
173 \\ zig_breakpoint();
152 \\ zig_unreachable();174 \\ zig_unreachable();
153 \\}175 \\}
154 \\176 \\
...@@ -172,15 +194,12 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -172,15 +194,12 @@ pub fn addCases(ctx: *TestContext) !void {
172 \\}194 \\}
173 \\195 \\
174 ,196 ,
175 \\#include <stddef.h>
176 \\#include <stdint.h>
177 \\
178 \\zig_noreturn void exitMath(uint8_t arg0);197 \\zig_noreturn void exitMath(uint8_t arg0);
179 \\zig_noreturn void exit(uint8_t arg0);198 \\zig_noreturn void exit(uint8_t arg0);
180 \\199 \\
181 \\const char *const exit__anon_0 = "{rax}";200 \\static uint8_t exit__anon_0[6] = "{rax}";
182 \\const char *const exit__anon_1 = "{rdi}";201 \\static uint8_t exit__anon_1[6] = "{rdi}";
183 \\const char *const exit__anon_2 = "syscall";202 \\static uint8_t exit__anon_2[8] = "syscall";
184 \\203 \\
185 \\zig_noreturn void _start(void) {204 \\zig_noreturn void _start(void) {
186 \\ exitMath(1);205 \\ exitMath(1);
...@@ -193,10 +212,11 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -193,10 +212,11 @@ pub fn addCases(ctx: *TestContext) !void {
193 \\}212 \\}
194 \\213 \\
195 \\zig_noreturn void exit(uint8_t arg0) {214 \\zig_noreturn void exit(uint8_t arg0) {
196 \\ const size_t __temp_0 = (size_t)arg0;215 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;
197 \\ register size_t rax_constant __asm__("rax") = 231;216 \\ register uintptr_t rax_constant __asm__("rax") = 231;
198 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;217 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
199 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));218 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
219 \\ zig_breakpoint();
200 \\ zig_unreachable();220 \\ zig_unreachable();
201 \\}221 \\}
202 \\222 \\
...@@ -220,15 +240,12 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -220,15 +240,12 @@ pub fn addCases(ctx: *TestContext) !void {
220 \\}240 \\}
221 \\241 \\
222 ,242 ,
223 \\#include <stddef.h>
224 \\#include <stdint.h>
225 \\
226 \\zig_noreturn void exitMath(uint8_t arg0);243 \\zig_noreturn void exitMath(uint8_t arg0);
227 \\zig_noreturn void exit(uint8_t arg0);244 \\zig_noreturn void exit(uint8_t arg0);
228 \\245 \\
229 \\const char *const exit__anon_0 = "{rax}";246 \\static uint8_t exit__anon_0[6] = "{rax}";
230 \\const char *const exit__anon_1 = "{rdi}";247 \\static uint8_t exit__anon_1[6] = "{rdi}";
231 \\const char *const exit__anon_2 = "syscall";248 \\static uint8_t exit__anon_2[8] = "syscall";
232 \\249 \\
233 \\zig_noreturn void _start(void) {250 \\zig_noreturn void _start(void) {
234 \\ exitMath(1);251 \\ exitMath(1);
...@@ -241,10 +258,11 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -241,10 +258,11 @@ pub fn addCases(ctx: *TestContext) !void {
241 \\}258 \\}
242 \\259 \\
243 \\zig_noreturn void exit(uint8_t arg0) {260 \\zig_noreturn void exit(uint8_t arg0) {
244 \\ const size_t __temp_0 = (size_t)arg0;261 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;
245 \\ register size_t rax_constant __asm__("rax") = 231;262 \\ register uintptr_t rax_constant __asm__("rax") = 231;
246 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;263 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
247 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));264 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
265 \\ zig_breakpoint();
248 \\ zig_unreachable();266 \\ zig_unreachable();
249 \\}267 \\}
250 \\268 \\
...@@ -252,33 +270,25 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -252,33 +270,25 @@ pub fn addCases(ctx: *TestContext) !void {
252 ctx.h("header with single param function", linux_x64,270 ctx.h("header with single param function", linux_x64,
253 \\export fn start(a: u8) void{}271 \\export fn start(a: u8) void{}
254 ,272 ,
255 \\#include <stdint.h>
256 \\
257 \\void start(uint8_t arg0);273 \\void start(uint8_t arg0);
258 \\274 \\
259 );275 );
260 ctx.h("header with multiple param function", linux_x64,276 ctx.h("header with multiple param function", linux_x64,
261 \\export fn start(a: u8, b: u8, c: u8) void{}277 \\export fn start(a: u8, b: u8, c: u8) void{}
262 ,278 ,
263 \\#include <stdint.h>
264 \\
265 \\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2);279 \\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2);
266 \\280 \\
267 );281 );
268 ctx.h("header with u32 param function", linux_x64,282 ctx.h("header with u32 param function", linux_x64,
269 \\export fn start(a: u32) void{}283 \\export fn start(a: u32) void{}
270 ,284 ,
271 \\#include <stdint.h>
272 \\
273 \\void start(uint32_t arg0);285 \\void start(uint32_t arg0);
274 \\286 \\
275 );287 );
276 ctx.h("header with usize param function", linux_x64,288 ctx.h("header with usize param function", linux_x64,
277 \\export fn start(a: usize) void{}289 \\export fn start(a: usize) void{}
278 ,290 ,
279 \\#include <stddef.h>291 \\void start(uintptr_t arg0);
280 \\
281 \\void start(size_t arg0);
282 \\292 \\
283 );293 );
284 ctx.h("header with bool param function", linux_x64,294 ctx.h("header with bool param function", linux_x64,
...@@ -308,10 +318,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -308,10 +318,7 @@ pub fn addCases(ctx: *TestContext) !void {
308 ctx.h("header with multiple includes", linux_x64,318 ctx.h("header with multiple includes", linux_x64,
309 \\export fn start(a: u32, b: usize) void{}319 \\export fn start(a: u32, b: usize) void{}
310 ,320 ,
311 \\#include <stddef.h>321 \\void start(uint32_t arg0, uintptr_t arg1);
312 \\#include <stdint.h>
313 \\
314 \\void start(uint32_t arg0, size_t arg1);
315 \\322 \\
316 );323 );
317}324}