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;
1111
1212var 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
1417pub 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
1523 const test_fn_list = builtin.test_functions;
1624 var ok_count: usize = 0;
1725 var skip_count: usize = 0;
lib/std/testing.zig+4
......@@ -21,6 +21,10 @@ pub var base_allocator_instance = std.heap.FixedBufferAllocator.init("");
2121/// TODO https://github.com/ziglang/zig/issues/5738
2222pub 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
2428/// This function is intended to be used only in tests. It prints diagnostics to stderr
2529/// and then aborts when actual_error_union is not expected_error.
2630pub 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
14311431 var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
14321432 defer c_comp_progress_node.end();
14331433
1434 var arena = std.heap.ArenaAllocator.init(self.gpa);
1435 defer arena.deinit();
1436
14371434 self.work_queue_wait_group.reset();
14381435 defer self.work_queue_wait_group.wait();
14391436
......@@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
15021499 };
15031500
15041501 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) {
15061503 error.OutOfMemory => return error.OutOfMemory,
15071504 error.AnalysisFail => {
15081505 decl.analysis = .dependency_failure;
src/Module.zig+86-11
......@@ -277,6 +277,8 @@ pub const Decl = struct {
277277};
278278
279279/// 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`.
280282pub const Fn = struct {
281283 /// This memory owned by the Decl's TypedValue.Managed arena allocator.
282284 analysis: union(enum) {
......@@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
10101012 defer fn_type_scope.instructions.deinit(self.gpa);
10111013
10121014 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
10161016 const param_decls = fn_proto.params();
10171017 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 {
10831083 const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{
10841084 .instructions = fn_type_scope.instructions.items,
10851085 });
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
10861116 const new_func = try decl_arena.allocator.create(Fn);
10871117 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 {
18991929 return null;
19001930}
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 {
19031939 try self.ensureDeclAnalyzed(exported_decl);
19041940 const typed_value = exported_decl.typed_value.most_recent.typed_value;
19051941 switch (typed_value.ty.zigTypeTag()) {
......@@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
28012837 }
28022838 }
28032839
2804 // *[N]T to []T
2805 if (inst.ty.isSinglePointer() and dest_type.isSlice() and
2806 (!inst.ty.isConstPtr() or dest_type.isConstPtr()))
2807 {
2840 // Coercions where the source is a single pointer to an array.
2841 src_array_ptr: {
2842 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
28082843 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
28092849 const dst_elem_type = dest_type.elemType();
2810 if (array_type.zigTypeTag() == .Array and
2811 coerceInMemoryAllowed(dst_elem_type, array_type.elemType()) == .ok)
2812 {
2813 return self.coerceArrayPtrToSlice(scope, dest_type, inst);
2850 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
2851 .ok => {},
2852 .no_match => break :src_array_ptr,
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 => {},
28142881 }
28152882 }
28162883
......@@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I
29182985 return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{});
29192986}
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
29212996pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
29222997 @setCold(true);
29232998 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;
1111const C = link.File.C;
1212const Decl = Module.Decl;
1313const mem = std.mem;
14const log = std.log.scoped(.c);
1415
15const indentation = " ";
16const Writer = std.ArrayList(u8).Writer;
1617
1718/// Maps a name from Zig source to C. Currently, this will always give the same
1819/// 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 {
2021 return allocator.dupe(u8, name);
2122}
2223
23fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void {
24 switch (T.zigTypeTag()) {
24fn renderType(
25 ctx: *Context,
26 writer: Writer,
27 t: Type,
28) error{ OutOfMemory, AnalysisFail }!void {
29 switch (t.zigTypeTag()) {
2530 .NoReturn => {
2631 try writer.writeAll("zig_noreturn void");
2732 },
2833 .Void => try writer.writeAll("void"),
2934 .Bool => try writer.writeAll("bool"),
3035 .Int => {
31 if (T.tag() == .u8) {
32 header.need_stdint = true;
33 try writer.writeAll("uint8_t");
34 } else if (T.tag() == .u32) {
35 header.need_stdint = true;
36 try writer.writeAll("uint32_t");
37 } else if (T.tag() == .usize) {
38 header.need_stddef = true;
39 try writer.writeAll("size_t");
36 switch (t.tag()) {
37 .u8 => try writer.writeAll("uint8_t"),
38 .i8 => try writer.writeAll("int8_t"),
39 .u16 => try writer.writeAll("uint16_t"),
40 .i16 => try writer.writeAll("int16_t"),
41 .u32 => try writer.writeAll("uint32_t"),
42 .i32 => try writer.writeAll("int32_t"),
43 .u64 => try writer.writeAll("uint64_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", .{});
4076 } 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(" *");
4285 }
4386 },
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 }),
4594 }
4695}
4796
48fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void {
49 switch (T.zigTypeTag()) {
97fn renderValue(
98 ctx: *Context,
99 writer: Writer,
100 t: Type,
101 val: Value,
102) error{ OutOfMemory, AnalysisFail }!void {
103 switch (t.zigTypeTag()) {
50104 .Int => {
51 if (T.isSignedInt())
52 return writer.print("{}", .{val.toSignedInt()});
53 return writer.print("{}", .{val.toUnsignedInt()});
105 if (t.isSignedInt())
106 return writer.print("{d}", .{val.toSignedInt()});
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 ),
54138 },
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 }),
56170 }
57171}
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 {
60178 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());
62180 // Use the child allocator directly, as we know the name can be freed before
63181 // the rest of the arena.
64182 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
73191 if (index > 0) {
74192 try writer.writeAll(", ");
75193 }
76 try renderType(ctx, header, writer, tv.ty.fnParamType(index));
194 try renderType(ctx, writer, tv.ty.fnParamType(index));
77195 try writer.print(" arg{}", .{index});
78196 }
79197 }
80198 try writer.writeByte(')');
81199}
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
83208pub fn generate(file: *C, decl: *Decl) !void {
84 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
85 .Fn => try genFn(file, decl),
86 .Array => try genArray(file, decl),
87 else => |e| return file.fail(decl.src(), "TODO {}", .{e}),
209 const tv = decl.typed_value.most_recent.typed_value;
210
211 var arena = std.heap.ArenaAllocator.init(file.base.allocator);
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");
88284 }
89285}
90286
91287pub fn generateHeader(
92 arena: *std.heap.ArenaAllocator,
288 comp: *Compilation,
93289 module: *Module,
94290 header: *C.Header,
95291 decl: *Decl,
96292) error{ AnalysisFail, OutOfMemory }!void {
97293 switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) {
98294 .Fn => {
99 var inst_map = std.AutoHashMap(*Inst, []u8).init(&arena.allocator);
295 var inst_map = std.AutoHashMap(*Inst, []u8).init(comp.gpa);
100296 defer inst_map.deinit();
297
298 var arena = std.heap.ArenaAllocator.init(comp.gpa);
299 defer arena.deinit();
300
101301 var ctx = Context{
102302 .decl = decl,
103 .arena = arena,
303 .arena = &arena,
104304 .inst_map = &inst_map,
305 .target = comp.getTarget(),
306 .header = header,
105307 };
106308 const writer = header.buf.writer();
107 renderFunctionSignature(&ctx, header, writer, decl) catch |err| {
309 renderFunctionSignature(&ctx, writer, decl) catch |err| {
108310 if (err == error.AnalysisFail) {
109311 try module.failed_decls.put(module.gpa, decl, ctx.error_msg);
110312 }
......@@ -116,24 +318,6 @@ pub fn generateHeader(
116318 }
117319}
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
137321const Context = struct {
138322 decl: *Decl,
139323 inst_map: *std.AutoHashMap(*Inst, []u8),
......@@ -141,6 +325,8 @@ const Context = struct {
141325 argdex: usize = 0,
142326 unnamed_index: usize = 0,
143327 error_msg: *Compilation.ErrorMsg = undefined,
328 target: std.Target,
329 header: *C.Header,
144330
145331 fn resolveInst(self: *Context, inst: *Inst) ![]u8 {
146332 if (inst.cast(Inst.Constant)) |const_inst| {
......@@ -170,55 +356,6 @@ const Context = struct {
170356 }
171357};
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
222359fn genArg(ctx: *Context) !?[]u8 {
223360 const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex});
224361 ctx.argdex += 1;
......@@ -226,25 +363,40 @@ fn genArg(ctx: *Context) !?[]u8 {
226363}
227364
228365fn 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", .{});
230368 return null;
231369}
232370
233fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 {
234 return ctx.fail(ctx.decl.src(), "TODO return", .{});
371fn genRet(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
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", .{});
235386}
236387
237388fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
238389 if (inst.base.isUnused())
239390 return null;
391 try indent(file);
240392 const op = inst.operand;
241393 const writer = file.main.writer();
242394 const name = try ctx.name();
243395 const from = try ctx.resolveInst(inst.operand);
244 try writer.writeAll(indentation ++ "const ");
245 try renderType(ctx, &file.header, writer, inst.base.ty);
396 try writer.writeAll("const ");
397 try renderType(ctx, writer, inst.base.ty);
246398 try writer.print(" {} = (", .{name});
247 try renderType(ctx, &file.header, writer, inst.base.ty);
399 try renderType(ctx, writer, inst.base.ty);
248400 try writer.print("){};\n", .{from});
249401 return name;
250402}
......@@ -252,54 +404,57 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 {
252404fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 {
253405 if (inst.base.isUnused())
254406 return null;
407 try indent(file);
255408 const lhs = ctx.resolveInst(inst.lhs);
256409 const rhs = ctx.resolveInst(inst.rhs);
257410 const writer = file.main.writer();
258411 const name = try ctx.name();
259 try writer.writeAll(indentation ++ "const ");
260 try renderType(ctx, &file.header, writer, inst.base.ty);
412 try writer.writeAll("const ");
413 try renderType(ctx, writer, inst.base.ty);
261414 try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs });
262415 return name;
263416}
264417
265418fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 {
419 try indent(file);
266420 const writer = file.main.writer();
267421 const header = file.header.buf.writer();
268 try writer.writeAll(indentation);
269422 if (inst.func.castTag(.constant)) |func_inst| {
270 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
271 const target = func_val.func.owner_decl;
272 const target_ty = target.typed_value.most_recent.typed_value.ty;
273 const ret_ty = target_ty.fnReturnType().tag();
274 if (target_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
275 try writer.print("(void)", .{});
276 }
277 const tname = mem.spanZ(target.name);
278 if (file.called.get(tname) == null) {
279 try file.called.put(tname, void{});
280 try renderFunctionSignature(ctx, &file.header, header, target);
281 try header.writeAll(";\n");
282 }
283 try writer.print("{}(", .{tname});
284 if (inst.args.len != 0) {
285 for (inst.args) |arg, i| {
286 if (i > 0) {
287 try writer.writeAll(", ");
288 }
289 if (arg.cast(Inst.Constant)) |con| {
290 try renderValue(ctx, writer, arg.ty, con.val);
291 } else {
292 const val = try ctx.resolveInst(arg);
293 try writer.print("{}", .{val});
294 }
423 const fn_decl = if (func_inst.val.cast(Value.Payload.ExternFn)) |extern_fn|
424 extern_fn.decl
425 else if (func_inst.val.cast(Value.Payload.Function)) |func_val|
426 func_val.func.owner_decl
427 else
428 unreachable;
429
430 const fn_ty = fn_decl.typed_value.most_recent.typed_value.ty;
431 const ret_ty = fn_ty.fnReturnType().tag();
432 if (fn_ty.fnReturnType().hasCodeGenBits() and inst.base.isUnused()) {
433 try writer.print("(void)", .{});
434 }
435 const fn_name = mem.spanZ(fn_decl.name);
436 if (file.called.get(fn_name) == null) {
437 try file.called.put(fn_name, void{});
438 try renderFunctionSignature(ctx, header, fn_decl);
439 try header.writeAll(";\n");
440 }
441 try writer.print("{s}(", .{fn_name});
442 if (inst.args.len != 0) {
443 for (inst.args) |arg, i| {
444 if (i > 0) {
445 try writer.writeAll(", ");
446 }
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});
295452 }
296453 }
297 try writer.writeAll(");\n");
298 } else {
299 return ctx.fail(ctx.decl.src(), "TODO non-function call target?", .{});
300454 }
455 try writer.writeAll(");\n");
301456 } 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", .{});
303458 }
304459 return null;
305460}
......@@ -309,25 +464,27 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
309464 return null;
310465}
311466
312fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 {
313 // TODO ??
467fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 {
468 try indent(file);
469 try file.main.writer().writeAll("zig_breakpoint();\n");
314470 return null;
315471}
316472
317473fn 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");
319476 return null;
320477}
321478
322479fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 {
480 try indent(file);
323481 const writer = file.main.writer();
324 try writer.writeAll(indentation);
325482 for (as.inputs) |i, index| {
326483 if (i[0] == '{' and i[i.len - 1] == '}') {
327484 const reg = i[1 .. i.len - 1];
328485 const arg = as.args[index];
329486 try writer.writeAll("register ");
330 try renderType(ctx, &file.header, writer, arg.ty);
487 try renderType(ctx, writer, arg.ty);
331488 try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg });
332489 // TODO merge constant handling into inst_map as well
333490 if (arg.castTag(.constant)) |c| {
src/link/C.zig+1-15
......@@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c;
1515
1616pub const Header = struct {
1717 buf: std.ArrayList(u8),
18 need_stddef: bool = false,
19 need_stdint: bool = false,
2018 emit_loc: ?Compilation.EmitLoc,
2119
2220 pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header {
......@@ -31,20 +29,8 @@ pub const Header = struct {
3129 defer tracy.end();
3230
3331 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 }
4632 if (self.buf.items.len > 0) {
47 try writer.print("{}", .{self.buf.items});
33 try writer.print("{s}", .{self.buf.items});
4834 }
4935 }
5036
src/link/cbe.h+21-2
......@@ -1,5 +1,4 @@
11#if __STDC_VERSION__ >= 199901L
2// C99 or newer
32#include <stdbool.h>
43#else
54#define bool unsigned char
......@@ -17,9 +16,29 @@
1716#define zig_noreturn
1817#endif
1918
20#if __GNUC__
19#if defined(__GNUC__)
2120#define zig_unreachable() __builtin_unreachable()
2221#else
2322#define zig_unreachable()
2423#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(
18281828 else => unreachable,
18291829 }
18301830 }
1831 try argv.append(exe_path);
1831 try argv.appendSlice(&[_][]const u8{
1832 exe_path, self_exe_path,
1833 });
18321834 } else {
18331835 for (test_exec_args.items) |arg| {
18341836 try argv.append(arg orelse exe_path);
src/test.zig+116-121
......@@ -11,8 +11,9 @@ const enable_wine: bool = build_options.enable_wine;
1111const enable_wasmtime: bool = build_options.enable_wasmtime;
1212const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
1313const ThreadPool = @import("ThreadPool.zig");
14const CrossTarget = std.zig.CrossTarget;
1415
15const cheader = @embedFile("link/cbe.h");
16const c_header = @embedFile("link/cbe.h");
1617
1718test "self-hosted" {
1819 var ctx = TestContext.init();
......@@ -88,6 +89,9 @@ pub const TestContext = struct {
8889 /// A transformation update transforms the input and tests against
8990 /// the expected output ZIR.
9091 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,
9195 /// An error update attempts to compile bad code, and ensures that it
9296 /// fails to compile, and for the expected reasons.
9397 /// A slice containing the expected errors *in sequential order*.
......@@ -109,12 +113,12 @@ pub const TestContext = struct {
109113 path: []const u8,
110114 };
111115
112 pub const TestType = enum {
116 pub const Extension = enum {
113117 Zig,
114118 ZIR,
115119 };
116120
117 /// A Case consists of a set of *updates*. The same Compilation is used for each
121 /// A `Case` consists of a list of `Update`. The same `Compilation` is used for each
118122 /// update, so each update's source is treated as a single file being
119123 /// updated by the test harness and incrementally compiled.
120124 pub const Case = struct {
......@@ -123,13 +127,14 @@ pub const TestContext = struct {
123127 name: []const u8,
124128 /// The platform the test targets. For non-native platforms, an emulator
125129 /// such as QEMU is required for tests to complete.
126 target: std.zig.CrossTarget,
130 target: CrossTarget,
127131 /// In order to be able to run e.g. Execution updates, this must be set
128132 /// to Executable.
129133 output_mode: std.builtin.OutputMode,
130134 updates: std.ArrayList(Update),
131 extension: TestType,
132 cbe: bool = false,
135 extension: Extension,
136 object_format: ?std.builtin.ObjectFormat = null,
137 emit_h: bool = false,
133138
134139 files: std.ArrayList(File),
135140
......@@ -145,6 +150,7 @@ pub const TestContext = struct {
145150 /// Adds a subcase in which the module is updated with `src`, and a C
146151 /// header is generated.
147152 pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
153 self.emit_h = true;
148154 self.updates.append(.{
149155 .src = src,
150156 .case = .{ .Header = result },
......@@ -160,6 +166,15 @@ pub const TestContext = struct {
160166 }) catch unreachable;
161167 }
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
163178 /// Adds a subcase in which the module is updated with `src`, which
164179 /// should contain invalid input, and ensures that compilation fails
165180 /// for the expected reasons, given in sequential order in `errors` in
......@@ -214,86 +229,100 @@ pub const TestContext = struct {
214229 pub fn addExe(
215230 ctx: *TestContext,
216231 name: []const u8,
217 target: std.zig.CrossTarget,
218 T: TestType,
232 target: CrossTarget,
233 extension: Extension,
219234 ) *Case {
220235 ctx.cases.append(Case{
221236 .name = name,
222237 .target = target,
223238 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
224239 .output_mode = .Exe,
225 .extension = T,
240 .extension = extension,
226241 .files = std.ArrayList(File).init(ctx.cases.allocator),
227242 }) catch unreachable;
228243 return &ctx.cases.items[ctx.cases.items.len - 1];
229244 }
230245
231246 /// 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 {
233248 return ctx.addExe(name, target, .Zig);
234249 }
235250
236251 /// 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 {
238253 return ctx.addExe(name, target, .ZIR);
239254 }
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
241269 pub fn addObj(
242270 ctx: *TestContext,
243271 name: []const u8,
244 target: std.zig.CrossTarget,
245 T: TestType,
272 target: CrossTarget,
273 extension: Extension,
246274 ) *Case {
247275 ctx.cases.append(Case{
248276 .name = name,
249277 .target = target,
250278 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
251279 .output_mode = .Obj,
252 .extension = T,
280 .extension = extension,
253281 .files = std.ArrayList(File).init(ctx.cases.allocator),
254282 }) catch unreachable;
255283 return &ctx.cases.items[ctx.cases.items.len - 1];
256284 }
257285
258 /// 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 {
286 /// Adds a test case for Zig input, producing an object file.
287 pub fn obj(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
260288 return ctx.addObj(name, target, .Zig);
261289 }
262290
263 /// 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 {
291 /// Adds a test case for ZIR input, producing an object file.
292 pub fn objZIR(ctx: *TestContext, name: []const u8, target: CrossTarget) *Case {
265293 return ctx.addObj(name, target, .ZIR);
266294 }
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 {
269298 ctx.cases.append(Case{
270299 .name = name,
271300 .target = target,
272301 .updates = std.ArrayList(Update).init(ctx.cases.allocator),
273302 .output_mode = .Obj,
274 .extension = T,
275 .cbe = true,
303 .extension = ext,
304 .object_format = .c,
276305 .files = std.ArrayList(File).init(ctx.cases.allocator),
277306 }) catch unreachable;
278307 return &ctx.cases.items[ctx.cases.items.len - 1];
279308 }
280309
281 pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
282 ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
310 pub fn c(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
311 ctx.addC(name, target, .Zig).addCompareObjectFile(src, c_header ++ out);
283312 }
284313
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);
314 pub fn h(ctx: *TestContext, name: []const u8, target: CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
315 ctx.addC(name, target, .Zig).addHeader(src, c_header ++ out);
287316 }
288317
289318 pub fn addCompareOutput(
290319 ctx: *TestContext,
291320 name: []const u8,
292 T: TestType,
321 extension: Extension,
293322 src: [:0]const u8,
294323 expected_stdout: []const u8,
295324 ) void {
296 ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout);
325 ctx.addExe(name, .{}, extension).addCompareOutput(src, expected_stdout);
297326 }
298327
299328 /// Adds a test case that compiles the Zig source given in `src`, executes
......@@ -321,12 +350,12 @@ pub const TestContext = struct {
321350 pub fn addTransform(
322351 ctx: *TestContext,
323352 name: []const u8,
324 target: std.zig.CrossTarget,
325 T: TestType,
353 target: CrossTarget,
354 extension: Extension,
326355 src: [:0]const u8,
327356 result: [:0]const u8,
328357 ) void {
329 ctx.addObj(name, target, T).addTransform(src, result);
358 ctx.addObj(name, target, extension).addTransform(src, result);
330359 }
331360
332361 /// Adds a test case that compiles the Zig given in `src` to ZIR and tests
......@@ -334,7 +363,7 @@ pub const TestContext = struct {
334363 pub fn transform(
335364 ctx: *TestContext,
336365 name: []const u8,
337 target: std.zig.CrossTarget,
366 target: CrossTarget,
338367 src: [:0]const u8,
339368 result: [:0]const u8,
340369 ) void {
......@@ -346,7 +375,7 @@ pub const TestContext = struct {
346375 pub fn transformZIR(
347376 ctx: *TestContext,
348377 name: []const u8,
349 target: std.zig.CrossTarget,
378 target: CrossTarget,
350379 src: [:0]const u8,
351380 result: [:0]const u8,
352381 ) void {
......@@ -356,12 +385,12 @@ pub const TestContext = struct {
356385 pub fn addError(
357386 ctx: *TestContext,
358387 name: []const u8,
359 target: std.zig.CrossTarget,
360 T: TestType,
388 target: CrossTarget,
389 extension: Extension,
361390 src: [:0]const u8,
362391 expected_errors: []const []const u8,
363392 ) void {
364 ctx.addObj(name, target, T).addError(src, expected_errors);
393 ctx.addObj(name, target, extension).addError(src, expected_errors);
365394 }
366395
367396 /// Adds a test case that ensures that the Zig given in `src` fails to
......@@ -370,7 +399,7 @@ pub const TestContext = struct {
370399 pub fn compileError(
371400 ctx: *TestContext,
372401 name: []const u8,
373 target: std.zig.CrossTarget,
402 target: CrossTarget,
374403 src: [:0]const u8,
375404 expected_errors: []const []const u8,
376405 ) void {
......@@ -383,7 +412,7 @@ pub const TestContext = struct {
383412 pub fn compileErrorZIR(
384413 ctx: *TestContext,
385414 name: []const u8,
386 target: std.zig.CrossTarget,
415 target: CrossTarget,
387416 src: [:0]const u8,
388417 expected_errors: []const []const u8,
389418 ) void {
......@@ -393,11 +422,11 @@ pub const TestContext = struct {
393422 pub fn addCompiles(
394423 ctx: *TestContext,
395424 name: []const u8,
396 target: std.zig.CrossTarget,
397 T: TestType,
425 target: CrossTarget,
426 extension: Extension,
398427 src: [:0]const u8,
399428 ) void {
400 ctx.addObj(name, target, T).compiles(src);
429 ctx.addObj(name, target, extension).compiles(src);
401430 }
402431
403432 /// Adds a test case that asserts that the Zig given in `src` compiles
......@@ -405,7 +434,7 @@ pub const TestContext = struct {
405434 pub fn compiles(
406435 ctx: *TestContext,
407436 name: []const u8,
408 target: std.zig.CrossTarget,
437 target: CrossTarget,
409438 src: [:0]const u8,
410439 ) void {
411440 ctx.addCompiles(name, target, .Zig, src);
......@@ -416,7 +445,7 @@ pub const TestContext = struct {
416445 pub fn compilesZIR(
417446 ctx: *TestContext,
418447 name: []const u8,
419 target: std.zig.CrossTarget,
448 target: CrossTarget,
420449 src: [:0]const u8,
421450 ) void {
422451 ctx.addCompiles(name, target, .ZIR, src);
......@@ -430,7 +459,7 @@ pub const TestContext = struct {
430459 pub fn incrementalFailure(
431460 ctx: *TestContext,
432461 name: []const u8,
433 target: std.zig.CrossTarget,
462 target: CrossTarget,
434463 src: [:0]const u8,
435464 expected_errors: []const []const u8,
436465 fixed_src: [:0]const u8,
......@@ -448,7 +477,7 @@ pub const TestContext = struct {
448477 pub fn incrementalFailureZIR(
449478 ctx: *TestContext,
450479 name: []const u8,
451 target: std.zig.CrossTarget,
480 target: CrossTarget,
452481 src: [:0]const u8,
453482 expected_errors: []const []const u8,
454483 fixed_src: [:0]const u8,
......@@ -548,12 +577,11 @@ pub const TestContext = struct {
548577 .root_src_path = tmp_src_path,
549578 };
550579
551 const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
552580 const bin_name = try std.zig.binNameAlloc(arena, .{
553581 .root_name = "test_case",
554582 .target = target,
555583 .output_mode = case.output_mode,
556 .object_format = ofmt,
584 .object_format = case.object_format,
557585 });
558586
559587 const emit_directory: Compilation.Directory = .{
......@@ -564,7 +592,7 @@ pub const TestContext = struct {
564592 .directory = emit_directory,
565593 .basename = bin_name,
566594 };
567 const emit_h: ?Compilation.EmitLoc = if (case.cbe)
595 const emit_h: ?Compilation.EmitLoc = if (case.emit_h)
568596 .{
569597 .directory = emit_directory,
570598 .basename = "test_case.h",
......@@ -588,7 +616,7 @@ pub const TestContext = struct {
588616 .emit_h = emit_h,
589617 .root_pkg = &root_pkg,
590618 .keep_source_files_loaded = true,
591 .object_format = ofmt,
619 .object_format = case.object_format,
592620 .is_native_os = case.target.isNativeOs(),
593621 .is_native_abi = case.target.isNativeAbi(),
594622 });
......@@ -631,9 +659,10 @@ pub const TestContext = struct {
631659 },
632660 }
633661 }
634 if (case.cbe) {
635 const C = comp.bin_file.cast(link.File.C).?;
636 std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
662 if (comp.bin_file.cast(link.File.C)) |c_file| {
663 std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{
664 c_file.main.items,
665 });
637666 }
638667 std.debug.print("Test failed.\n", .{});
639668 std.process.exit(1);
......@@ -644,67 +673,37 @@ pub const TestContext = struct {
644673 .Header => |expected_output| {
645674 var file = try tmp.dir.openFile("test_case.h", .{ .read = true });
646675 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) {
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 }
678 std.testing.expectEqualStrings(expected_output, out);
679 },
680 .CompareObjectFile => |expected_output| {
681 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
682 defer file.close();
683 const out = try file.reader().readAllAlloc(arena, 5 * 1024 * 1024);
684
685 std.testing.expectEqualStrings(expected_output, out);
659686 },
660687 .Transformation => |expected_output| {
661 if (case.cbe) {
662 // The C file is always closed after an update, because we don't support
663 // incremental updates
664 var file = try tmp.dir.openFile(bin_name, .{ .read = true });
665 defer file.close();
666 var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
667
668 if (expected_output.len != out.len) {
669 std.debug.print("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
670 std.process.exit(1);
671 }
672 for (expected_output) |e, i| {
673 if (out[i] != e) {
674 std.debug.print("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
675 std.process.exit(1);
676 }
677 }
678 } else {
679 update_node.setEstimatedTotalItems(5);
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 }
688 update_node.setEstimatedTotalItems(5);
689 var emit_node = update_node.start("emit", 0);
690 emit_node.activate();
691 var new_zir_module = try zir.emit(allocator, comp.bin_file.options.module.?);
692 defer new_zir_module.deinit(allocator);
693 emit_node.end();
694
695 var write_node = update_node.start("write", 0);
696 write_node.activate();
697 var out_zir = std.ArrayList(u8).init(allocator);
698 defer out_zir.deinit();
699 try new_zir_module.writeToStream(allocator, out_zir.outStream());
700 write_node.end();
701
702 var test_node = update_node.start("assert", 0);
703 test_node.activate();
704 defer test_node.end();
705
706 std.testing.expectEqualStrings(expected_output, out_zir.items);
708707 },
709708 .Error => |e| {
710709 var test_node = update_node.start("assert", 0);
......@@ -762,8 +761,6 @@ pub const TestContext = struct {
762761 }
763762 },
764763 .Execution => |expected_stdout| {
765 std.debug.assert(!case.cbe);
766
767764 update_node.setEstimatedTotalItems(4);
768765 var exec_result = x: {
769766 var exec_node = update_node.start("execute", 0);
......@@ -773,9 +770,12 @@ pub const TestContext = struct {
773770 var argv = std.ArrayList([]const u8).init(allocator);
774771 defer argv.deinit();
775772
776 const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
777
778 switch (case.target.getExternalExecutor()) {
773 const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{s}", .{bin_name});
774 if (case.object_format != null and case.object_format.? == .c) {
775 try argv.appendSlice(&[_][]const u8{
776 std.testing.zig_exe_path, "run", exe_path, "-lc",
777 });
778 } else switch (case.target.getExternalExecutor()) {
779779 .native => try argv.append(exe_path),
780780 .unavailable => {
781781 try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name);
......@@ -837,18 +837,13 @@ pub const TestContext = struct {
837837 switch (exec_result.term) {
838838 .Exited => |code| {
839839 if (code != 0) {
840 std.debug.print("elf file exited with code {}\n", .{code});
840 std.debug.print("execution exited with code {}\n", .{code});
841841 return error.BinaryBadExitCode;
842842 }
843843 },
844844 else => return error.BinaryCrashed,
845845 }
846 if (!std.mem.eql(u8, 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 }
846 std.testing.expectEqualStrings(expected_stdout, exec_result.stdout);
852847 },
853848 }
854849 }
src/type.zig+91-1
......@@ -172,7 +172,15 @@ pub const Type = extern union {
172172 const is_slice_b = isSlice(b);
173173 if (is_slice_a != is_slice_b)
174174 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 });
176184 },
177185 .Int => {
178186 // Detect that e.g. u64 != usize, even if the bits match on a particular target.
......@@ -1128,6 +1136,88 @@ pub const Type = extern union {
11281136 };
11291137 }
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
11311221 pub fn isSlice(self: Type) bool {
11321222 return switch (self.tag()) {
11331223 .u8,
src/value.zig+24
......@@ -82,6 +82,7 @@ pub const Value = extern union {
8282 int_big_positive,
8383 int_big_negative,
8484 function,
85 extern_fn,
8586 variable,
8687 ref_val,
8788 decl_ref,
......@@ -205,6 +206,7 @@ pub const Value = extern union {
205206 @panic("TODO implement copying of big ints");
206207 },
207208 .function => return self.copyPayloadShallow(allocator, Payload.Function),
209 .extern_fn => return self.copyPayloadShallow(allocator, Payload.ExternFn),
208210 .variable => return self.copyPayloadShallow(allocator, Payload.Variable),
209211 .ref_val => {
210212 const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
......@@ -337,6 +339,7 @@ pub const Value = extern union {
337339 .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}),
338340 .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}),
339341 .function => return out_stream.writeAll("(function)"),
342 .extern_fn => return out_stream.writeAll("(extern function)"),
340343 .variable => return out_stream.writeAll("(variable)"),
341344 .ref_val => {
342345 const ref_val = val.cast(Payload.RefVal).?;
......@@ -468,6 +471,7 @@ pub const Value = extern union {
468471 .int_big_positive,
469472 .int_big_negative,
470473 .function,
474 .extern_fn,
471475 .variable,
472476 .ref_val,
473477 .decl_ref,
......@@ -533,6 +537,7 @@ pub const Value = extern union {
533537 .anyframe_type,
534538 .null_value,
535539 .function,
540 .extern_fn,
536541 .variable,
537542 .ref_val,
538543 .decl_ref,
......@@ -617,6 +622,7 @@ pub const Value = extern union {
617622 .anyframe_type,
618623 .null_value,
619624 .function,
625 .extern_fn,
620626 .variable,
621627 .ref_val,
622628 .decl_ref,
......@@ -701,6 +707,7 @@ pub const Value = extern union {
701707 .anyframe_type,
702708 .null_value,
703709 .function,
710 .extern_fn,
704711 .variable,
705712 .ref_val,
706713 .decl_ref,
......@@ -812,6 +819,7 @@ pub const Value = extern union {
812819 .anyframe_type,
813820 .null_value,
814821 .function,
822 .extern_fn,
815823 .variable,
816824 .ref_val,
817825 .decl_ref,
......@@ -901,6 +909,7 @@ pub const Value = extern union {
901909 .anyframe_type,
902910 .null_value,
903911 .function,
912 .extern_fn,
904913 .variable,
905914 .ref_val,
906915 .decl_ref,
......@@ -1071,6 +1080,7 @@ pub const Value = extern union {
10711080 .bool_false,
10721081 .null_value,
10731082 .function,
1083 .extern_fn,
10741084 .variable,
10751085 .ref_val,
10761086 .decl_ref,
......@@ -1150,6 +1160,7 @@ pub const Value = extern union {
11501160 .anyframe_type,
11511161 .null_value,
11521162 .function,
1163 .extern_fn,
11531164 .variable,
11541165 .ref_val,
11551166 .decl_ref,
......@@ -1383,6 +1394,10 @@ pub const Value = extern union {
13831394 const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise);
13841395 std.hash.autoHash(&hasher, payload.func);
13851396 },
1397 .extern_fn => {
1398 const payload = @fieldParentPtr(Payload.ExternFn, "base", self.ptr_otherwise);
1399 std.hash.autoHash(&hasher, payload.decl);
1400 },
13861401 .variable => {
13871402 const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise);
13881403 std.hash.autoHash(&hasher, payload.variable);
......@@ -1449,6 +1464,7 @@ pub const Value = extern union {
14491464 .bool_false,
14501465 .null_value,
14511466 .function,
1467 .extern_fn,
14521468 .variable,
14531469 .int_u64,
14541470 .int_i64,
......@@ -1533,6 +1549,7 @@ pub const Value = extern union {
15331549 .bool_false,
15341550 .null_value,
15351551 .function,
1552 .extern_fn,
15361553 .variable,
15371554 .int_u64,
15381555 .int_i64,
......@@ -1634,6 +1651,7 @@ pub const Value = extern union {
16341651 .bool_true,
16351652 .bool_false,
16361653 .function,
1654 .extern_fn,
16371655 .variable,
16381656 .int_u64,
16391657 .int_i64,
......@@ -1730,6 +1748,7 @@ pub const Value = extern union {
17301748 .bool_true,
17311749 .bool_false,
17321750 .function,
1751 .extern_fn,
17331752 .variable,
17341753 .int_u64,
17351754 .int_i64,
......@@ -1793,6 +1812,11 @@ pub const Value = extern union {
17931812 func: *Module.Fn,
17941813 };
17951814
1815 pub const ExternFn = struct {
1816 base: Payload = Payload{ .tag = .extern_fn },
1817 decl: *Module.Decl,
1818 };
1819
17961820 pub const Variable = struct {
17971821 base: Payload = Payload{ .tag = .variable },
17981822 variable: *Module.Var,
test/stage2/cbe.zig+63-56
......@@ -9,12 +9,37 @@ const linux_x64 = std.zig.CrossTarget{
99};
1010
1111pub 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
1236 ctx.c("empty start function", linux_x64,
1337 \\export fn _start() noreturn {
1438 \\ unreachable;
1539 \\}
1640 ,
1741 \\zig_noreturn void _start(void) {
42 \\ zig_breakpoint();
1843 \\ zig_unreachable();
1944 \\}
2045 \\
......@@ -41,6 +66,7 @@ pub fn addCases(ctx: *TestContext) !void {
4166 \\}
4267 \\
4368 \\zig_noreturn void main(void) {
69 \\ zig_breakpoint();
4470 \\ zig_unreachable();
4571 \\}
4672 \\
......@@ -61,22 +87,21 @@ pub fn addCases(ctx: *TestContext) !void {
6187 \\ exitGood();
6288 \\}
6389 ,
64 \\#include <stddef.h>
65 \\
6690 \\zig_noreturn void exitGood(void);
6791 \\
68 \\const char *const exitGood__anon_0 = "{rax}";
69 \\const char *const exitGood__anon_1 = "{rdi}";
70 \\const char *const exitGood__anon_2 = "syscall";
92 \\static uint8_t exitGood__anon_0[6] = "{rax}";
93 \\static uint8_t exitGood__anon_1[6] = "{rdi}";
94 \\static uint8_t exitGood__anon_2[8] = "syscall";
7195 \\
7296 \\zig_noreturn void _start(void) {
7397 \\ exitGood();
7498 \\}
7599 \\
76100 \\zig_noreturn void exitGood(void) {
77 \\ register size_t rax_constant __asm__("rax") = 231;
78 \\ register size_t rdi_constant __asm__("rdi") = 0;
101 \\ register uintptr_t rax_constant __asm__("rax") = 231;
102 \\ register uintptr_t rdi_constant __asm__("rdi") = 0;
79103 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
104 \\ zig_breakpoint();
80105 \\ zig_unreachable();
81106 \\}
82107 \\
......@@ -96,22 +121,21 @@ pub fn addCases(ctx: *TestContext) !void {
96121 \\}
97122 \\
98123 ,
99 \\#include <stddef.h>
124 \\zig_noreturn void exit(uintptr_t arg0);
100125 \\
101 \\zig_noreturn void exit(size_t arg0);
102 \\
103 \\const char *const exit__anon_0 = "{rax}";
104 \\const char *const exit__anon_1 = "{rdi}";
105 \\const char *const exit__anon_2 = "syscall";
126 \\static uint8_t exit__anon_0[6] = "{rax}";
127 \\static uint8_t exit__anon_1[6] = "{rdi}";
128 \\static uint8_t exit__anon_2[8] = "syscall";
106129 \\
107130 \\zig_noreturn void _start(void) {
108131 \\ exit(0);
109132 \\}
110133 \\
111 \\zig_noreturn void exit(size_t arg0) {
112 \\ register size_t rax_constant __asm__("rax") = 231;
113 \\ register size_t rdi_constant __asm__("rdi") = arg0;
134 \\zig_noreturn void exit(uintptr_t arg0) {
135 \\ register uintptr_t rax_constant __asm__("rax") = 231;
136 \\ register uintptr_t rdi_constant __asm__("rdi") = arg0;
114137 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
138 \\ zig_breakpoint();
115139 \\ zig_unreachable();
116140 \\}
117141 \\
......@@ -131,24 +155,22 @@ pub fn addCases(ctx: *TestContext) !void {
131155 \\}
132156 \\
133157 ,
134 \\#include <stddef.h>
135 \\#include <stdint.h>
136 \\
137158 \\zig_noreturn void exit(uint8_t arg0);
138159 \\
139 \\const char *const exit__anon_0 = "{rax}";
140 \\const char *const exit__anon_1 = "{rdi}";
141 \\const char *const exit__anon_2 = "syscall";
160 \\static uint8_t exit__anon_0[6] = "{rax}";
161 \\static uint8_t exit__anon_1[6] = "{rdi}";
162 \\static uint8_t exit__anon_2[8] = "syscall";
142163 \\
143164 \\zig_noreturn void _start(void) {
144165 \\ exit(0);
145166 \\}
146167 \\
147168 \\zig_noreturn void exit(uint8_t arg0) {
148 \\ const size_t __temp_0 = (size_t)arg0;
149 \\ register size_t rax_constant __asm__("rax") = 231;
150 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;
169 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;
170 \\ register uintptr_t rax_constant __asm__("rax") = 231;
171 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
151172 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
173 \\ zig_breakpoint();
152174 \\ zig_unreachable();
153175 \\}
154176 \\
......@@ -172,15 +194,12 @@ pub fn addCases(ctx: *TestContext) !void {
172194 \\}
173195 \\
174196 ,
175 \\#include <stddef.h>
176 \\#include <stdint.h>
177 \\
178197 \\zig_noreturn void exitMath(uint8_t arg0);
179198 \\zig_noreturn void exit(uint8_t arg0);
180199 \\
181 \\const char *const exit__anon_0 = "{rax}";
182 \\const char *const exit__anon_1 = "{rdi}";
183 \\const char *const exit__anon_2 = "syscall";
200 \\static uint8_t exit__anon_0[6] = "{rax}";
201 \\static uint8_t exit__anon_1[6] = "{rdi}";
202 \\static uint8_t exit__anon_2[8] = "syscall";
184203 \\
185204 \\zig_noreturn void _start(void) {
186205 \\ exitMath(1);
......@@ -193,10 +212,11 @@ pub fn addCases(ctx: *TestContext) !void {
193212 \\}
194213 \\
195214 \\zig_noreturn void exit(uint8_t arg0) {
196 \\ const size_t __temp_0 = (size_t)arg0;
197 \\ register size_t rax_constant __asm__("rax") = 231;
198 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;
215 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;
216 \\ register uintptr_t rax_constant __asm__("rax") = 231;
217 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
199218 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
219 \\ zig_breakpoint();
200220 \\ zig_unreachable();
201221 \\}
202222 \\
......@@ -220,15 +240,12 @@ pub fn addCases(ctx: *TestContext) !void {
220240 \\}
221241 \\
222242 ,
223 \\#include <stddef.h>
224 \\#include <stdint.h>
225 \\
226243 \\zig_noreturn void exitMath(uint8_t arg0);
227244 \\zig_noreturn void exit(uint8_t arg0);
228245 \\
229 \\const char *const exit__anon_0 = "{rax}";
230 \\const char *const exit__anon_1 = "{rdi}";
231 \\const char *const exit__anon_2 = "syscall";
246 \\static uint8_t exit__anon_0[6] = "{rax}";
247 \\static uint8_t exit__anon_1[6] = "{rdi}";
248 \\static uint8_t exit__anon_2[8] = "syscall";
232249 \\
233250 \\zig_noreturn void _start(void) {
234251 \\ exitMath(1);
......@@ -241,10 +258,11 @@ pub fn addCases(ctx: *TestContext) !void {
241258 \\}
242259 \\
243260 \\zig_noreturn void exit(uint8_t arg0) {
244 \\ const size_t __temp_0 = (size_t)arg0;
245 \\ register size_t rax_constant __asm__("rax") = 231;
246 \\ register size_t rdi_constant __asm__("rdi") = __temp_0;
261 \\ const uintptr_t __temp_0 = (uintptr_t)arg0;
262 \\ register uintptr_t rax_constant __asm__("rax") = 231;
263 \\ register uintptr_t rdi_constant __asm__("rdi") = __temp_0;
247264 \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
265 \\ zig_breakpoint();
248266 \\ zig_unreachable();
249267 \\}
250268 \\
......@@ -252,33 +270,25 @@ pub fn addCases(ctx: *TestContext) !void {
252270 ctx.h("header with single param function", linux_x64,
253271 \\export fn start(a: u8) void{}
254272 ,
255 \\#include <stdint.h>
256 \\
257273 \\void start(uint8_t arg0);
258274 \\
259275 );
260276 ctx.h("header with multiple param function", linux_x64,
261277 \\export fn start(a: u8, b: u8, c: u8) void{}
262278 ,
263 \\#include <stdint.h>
264 \\
265279 \\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2);
266280 \\
267281 );
268282 ctx.h("header with u32 param function", linux_x64,
269283 \\export fn start(a: u32) void{}
270284 ,
271 \\#include <stdint.h>
272 \\
273285 \\void start(uint32_t arg0);
274286 \\
275287 );
276288 ctx.h("header with usize param function", linux_x64,
277289 \\export fn start(a: usize) void{}
278290 ,
279 \\#include <stddef.h>
280 \\
281 \\void start(size_t arg0);
291 \\void start(uintptr_t arg0);
282292 \\
283293 );
284294 ctx.h("header with bool param function", linux_x64,
......@@ -308,10 +318,7 @@ pub fn addCases(ctx: *TestContext) !void {
308318 ctx.h("header with multiple includes", linux_x64,
309319 \\export fn start(a: u32, b: usize) void{}
310320 ,
311 \\#include <stddef.h>
312 \\#include <stdint.h>
313 \\
314 \\void start(uint32_t arg0, size_t arg1);
321 \\void start(uint32_t arg0, uintptr_t arg1);
315322 \\
316323 );
317324}