| author | |
| committer | |
| log | 3b5dd48f99269cf8e944adf40657f2866adecc37 |
| tree | 287b4417d847bcfc27d068ef75e45c6a2fce1991 |
| parent | 2df2f0020f4ddc41b3b914cd17efcb403cf0f6ad |
| parent | 813d3308ccd13bdc96a40b583ffd8722651b7b83 |
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 | 11 | |
| 12 | 12 | var log_err_count: usize = 0; |
| 13 | 13 | |
| 14 | var args_buffer: [std.fs.MAX_PATH_BYTES + std.mem.page_size]u8 = undefined; | |
| 15 | var args_allocator = std.heap.FixedBufferAllocator.init(&args_buffer); | |
| 16 | ||
| 14 | 17 | pub 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 | 23 | const test_fn_list = builtin.test_functions; |
| 16 | 24 | var ok_count: usize = 0; |
| 17 | 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 | 21 | /// TODO https://github.com/ziglang/zig/issues/5738 |
| 22 | 22 | pub var log_level = std.log.Level.warn; |
| 23 | 23 | |
| 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`. | |
| 26 | pub var zig_exe_path: []const u8 = undefined; | |
| 27 | ||
| 24 | 28 | /// This function is intended to be used only in tests. It prints diagnostics to stderr |
| 25 | 29 | /// and then aborts when actual_error_union is not expected_error. |
| 26 | 30 | pub 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 | 1431 | var c_comp_progress_node = main_progress_node.start("Compile C Objects", self.c_source_files.len); |
| 1432 | 1432 | defer c_comp_progress_node.end(); |
| 1433 | 1433 | |
| 1434 | var arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 1435 | defer arena.deinit(); | |
| 1436 | ||
| 1437 | 1434 | self.work_queue_wait_group.reset(); |
| 1438 | 1435 | defer self.work_queue_wait_group.wait(); |
| 1439 | 1436 | |
| ... | ... | @@ -1502,7 +1499,7 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 1502 | 1499 | }; |
| 1503 | 1500 | |
| 1504 | 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 | 1503 | error.OutOfMemory => return error.OutOfMemory, |
| 1507 | 1504 | error.AnalysisFail => { |
| 1508 | 1505 | decl.analysis = .dependency_failure; |
src/Module.zig+86-11| ... | ... | @@ -277,6 +277,8 @@ pub const Decl = struct { |
| 277 | 277 | }; |
| 278 | 278 | |
| 279 | 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`. | |
| 280 | 282 | pub const Fn = struct { |
| 281 | 283 | /// This memory owned by the Decl's TypedValue.Managed arena allocator. |
| 282 | 284 | analysis: union(enum) { |
| ... | ... | @@ -1010,8 +1012,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1010 | 1012 | defer fn_type_scope.instructions.deinit(self.gpa); |
| 1011 | 1013 | |
| 1012 | 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", .{}); | |
| 1015 | 1015 | |
| 1016 | 1016 | const param_decls = fn_proto.params(); |
| 1017 | 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 | 1083 | const fn_type = try zir_sema.analyzeBodyValueAsType(self, &block_scope, fn_type_inst, .{ |
| 1084 | 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 | 1116 | const new_func = try decl_arena.allocator.create(Fn); |
| 1087 | 1117 | const fn_payload = try decl_arena.allocator.create(Value.Payload.Function); |
| 1088 | 1118 | |
| ... | ... | @@ -1899,7 +1929,13 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value { |
| 1899 | 1929 | return null; |
| 1900 | 1930 | } |
| 1901 | 1931 | |
| 1902 | pub fn analyzeExport(self: *Module, scope: *Scope, src: usize, borrowed_symbol_name: []const u8, exported_decl: *Decl) !void { | |
| 1932 | pub fn analyzeExport( | |
| 1933 | self: *Module, | |
| 1934 | scope: *Scope, | |
| 1935 | src: usize, | |
| 1936 | borrowed_symbol_name: []const u8, | |
| 1937 | exported_decl: *Decl, | |
| 1938 | ) !void { | |
| 1903 | 1939 | try self.ensureDeclAnalyzed(exported_decl); |
| 1904 | 1940 | const typed_value = exported_decl.typed_value.most_recent.typed_value; |
| 1905 | 1941 | switch (typed_value.ty.zigTypeTag()) { |
| ... | ... | @@ -2801,16 +2837,47 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 2801 | 2837 | } |
| 2802 | 2838 | } |
| 2803 | 2839 | |
| 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; | |
| 2808 | 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 | 2849 | 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 => {}, | |
| 2814 | 2881 | } |
| 2815 | 2882 | } |
| 2816 | 2883 | |
| ... | ... | @@ -2918,6 +2985,14 @@ fn coerceArrayPtrToSlice(self: *Module, scope: *Scope, dest_type: Type, inst: *I |
| 2918 | 2985 | return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); |
| 2919 | 2986 | } |
| 2920 | 2987 | |
| 2988 | fn 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 | ||
| 2921 | 2996 | pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError { |
| 2922 | 2997 | @setCold(true); |
| 2923 | 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 | 11 | const C = link.File.C; |
| 12 | 12 | const Decl = Module.Decl; |
| 13 | 13 | const mem = std.mem; |
| 14 | const log = std.log.scoped(.c); | |
| 14 | 15 | |
| 15 | const indentation = " "; | |
| 16 | const Writer = std.ArrayList(u8).Writer; | |
| 16 | 17 | |
| 17 | 18 | /// Maps a name from Zig source to C. Currently, this will always give the same |
| 18 | 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 | 21 | return allocator.dupe(u8, name); |
| 21 | 22 | } |
| 22 | 23 | |
| 23 | fn renderType(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, T: Type) !void { | |
| 24 | switch (T.zigTypeTag()) { | |
| 24 | fn renderType( | |
| 25 | ctx: *Context, | |
| 26 | writer: Writer, | |
| 27 | t: Type, | |
| 28 | ) error{ OutOfMemory, AnalysisFail }!void { | |
| 29 | switch (t.zigTypeTag()) { | |
| 25 | 30 | .NoReturn => { |
| 26 | 31 | try writer.writeAll("zig_noreturn void"); |
| 27 | 32 | }, |
| 28 | 33 | .Void => try writer.writeAll("void"), |
| 29 | 34 | .Bool => try writer.writeAll("bool"), |
| 30 | 35 | .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", .{}); | |
| 40 | 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 | } |
| 47 | 96 | |
| 48 | fn renderValue(ctx: *Context, writer: std.ArrayList(u8).Writer, T: Type, val: Value) !void { | |
| 49 | switch (T.zigTypeTag()) { | |
| 97 | fn renderValue( | |
| 98 | ctx: *Context, | |
| 99 | writer: Writer, | |
| 100 | t: Type, | |
| 101 | val: Value, | |
| 102 | ) error{ OutOfMemory, AnalysisFail }!void { | |
| 103 | switch (t.zigTypeTag()) { | |
| 50 | 104 | .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 | ), | |
| 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 | } |
| 58 | 172 | |
| 59 | fn renderFunctionSignature(ctx: *Context, header: *C.Header, writer: std.ArrayList(u8).Writer, decl: *Decl) !void { | |
| 173 | fn renderFunctionSignature( | |
| 174 | ctx: *Context, | |
| 175 | writer: Writer, | |
| 176 | decl: *Decl, | |
| 177 | ) !void { | |
| 60 | 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 | 180 | // Use the child allocator directly, as we know the name can be freed before |
| 63 | 181 | // the rest of the arena. |
| 64 | 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 | 191 | if (index > 0) { |
| 74 | 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 | 195 | try writer.print(" arg{}", .{index}); |
| 78 | 196 | } |
| 79 | 197 | } |
| 80 | 198 | try writer.writeByte(')'); |
| 81 | 199 | } |
| 82 | 200 | |
| 201 | fn 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 | ||
| 83 | 208 | pub 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"); | |
| 88 | 284 | } |
| 89 | 285 | } |
| 90 | 286 | |
| 91 | 287 | pub fn generateHeader( |
| 92 | arena: *std.heap.ArenaAllocator, | |
| 288 | comp: *Compilation, | |
| 93 | 289 | module: *Module, |
| 94 | 290 | header: *C.Header, |
| 95 | 291 | decl: *Decl, |
| 96 | 292 | ) error{ AnalysisFail, OutOfMemory }!void { |
| 97 | 293 | switch (decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { |
| 98 | 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 | 296 | defer inst_map.deinit(); |
| 297 | ||
| 298 | var arena = std.heap.ArenaAllocator.init(comp.gpa); | |
| 299 | defer arena.deinit(); | |
| 300 | ||
| 101 | 301 | var ctx = Context{ |
| 102 | 302 | .decl = decl, |
| 103 | .arena = arena, | |
| 303 | .arena = &arena, | |
| 104 | 304 | .inst_map = &inst_map, |
| 305 | .target = comp.getTarget(), | |
| 306 | .header = header, | |
| 105 | 307 | }; |
| 106 | 308 | const writer = header.buf.writer(); |
| 107 | renderFunctionSignature(&ctx, header, writer, decl) catch |err| { | |
| 309 | renderFunctionSignature(&ctx, writer, decl) catch |err| { | |
| 108 | 310 | if (err == error.AnalysisFail) { |
| 109 | 311 | try module.failed_decls.put(module.gpa, decl, ctx.error_msg); |
| 110 | 312 | } |
| ... | ... | @@ -116,24 +318,6 @@ pub fn generateHeader( |
| 116 | 318 | } |
| 117 | 319 | } |
| 118 | 320 | |
| 119 | fn 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 | ||
| 137 | 321 | const Context = struct { |
| 138 | 322 | decl: *Decl, |
| 139 | 323 | inst_map: *std.AutoHashMap(*Inst, []u8), |
| ... | ... | @@ -141,6 +325,8 @@ const Context = struct { |
| 141 | 325 | argdex: usize = 0, |
| 142 | 326 | unnamed_index: usize = 0, |
| 143 | 327 | error_msg: *Compilation.ErrorMsg = undefined, |
| 328 | target: std.Target, | |
| 329 | header: *C.Header, | |
| 144 | 330 | |
| 145 | 331 | fn resolveInst(self: *Context, inst: *Inst) ![]u8 { |
| 146 | 332 | if (inst.cast(Inst.Constant)) |const_inst| { |
| ... | ... | @@ -170,55 +356,6 @@ const Context = struct { |
| 170 | 356 | } |
| 171 | 357 | }; |
| 172 | 358 | |
| 173 | fn 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 | ||
| 222 | 359 | fn genArg(ctx: *Context) !?[]u8 { |
| 223 | 360 | const name = try std.fmt.allocPrint(&ctx.arena.allocator, "arg{}", .{ctx.argdex}); |
| 224 | 361 | ctx.argdex += 1; |
| ... | ... | @@ -226,25 +363,40 @@ fn genArg(ctx: *Context) !?[]u8 { |
| 226 | 363 | } |
| 227 | 364 | |
| 228 | 365 | fn 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 | 368 | return null; |
| 231 | 369 | } |
| 232 | 370 | |
| 233 | fn genRet(ctx: *Context, inst: *Inst.UnOp) !?[]u8 { | |
| 234 | return ctx.fail(ctx.decl.src(), "TODO return", .{}); | |
| 371 | fn 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 | ||
| 380 | fn 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 | } |
| 236 | 387 | |
| 237 | 388 | fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 238 | 389 | if (inst.base.isUnused()) |
| 239 | 390 | return null; |
| 391 | try indent(file); | |
| 240 | 392 | const op = inst.operand; |
| 241 | 393 | const writer = file.main.writer(); |
| 242 | 394 | const name = try ctx.name(); |
| 243 | 395 | 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); | |
| 246 | 398 | try writer.print(" {} = (", .{name}); |
| 247 | try renderType(ctx, &file.header, writer, inst.base.ty); | |
| 399 | try renderType(ctx, writer, inst.base.ty); | |
| 248 | 400 | try writer.print("){};\n", .{from}); |
| 249 | 401 | return name; |
| 250 | 402 | } |
| ... | ... | @@ -252,54 +404,57 @@ fn genIntCast(ctx: *Context, file: *C, inst: *Inst.UnOp) !?[]u8 { |
| 252 | 404 | fn genBinOp(ctx: *Context, file: *C, inst: *Inst.BinOp, comptime operator: []const u8) !?[]u8 { |
| 253 | 405 | if (inst.base.isUnused()) |
| 254 | 406 | return null; |
| 407 | try indent(file); | |
| 255 | 408 | const lhs = ctx.resolveInst(inst.lhs); |
| 256 | 409 | const rhs = ctx.resolveInst(inst.rhs); |
| 257 | 410 | const writer = file.main.writer(); |
| 258 | 411 | 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); | |
| 261 | 414 | try writer.print(" {} = {} " ++ operator ++ " {};\n", .{ name, lhs, rhs }); |
| 262 | 415 | return name; |
| 263 | 416 | } |
| 264 | 417 | |
| 265 | 418 | fn genCall(ctx: *Context, file: *C, inst: *Inst.Call) !?[]u8 { |
| 419 | try indent(file); | |
| 266 | 420 | const writer = file.main.writer(); |
| 267 | 421 | const header = file.header.buf.writer(); |
| 268 | try writer.writeAll(indentation); | |
| 269 | 422 | 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}); | |
| 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 | 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 | 459 | return null; |
| 305 | 460 | } |
| ... | ... | @@ -309,25 +464,27 @@ fn genDbgStmt(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { |
| 309 | 464 | return null; |
| 310 | 465 | } |
| 311 | 466 | |
| 312 | fn genBreak(ctx: *Context, inst: *Inst.NoOp) !?[]u8 { | |
| 313 | // TODO ?? | |
| 467 | fn genBreakpoint(file: *C, inst: *Inst.NoOp) !?[]u8 { | |
| 468 | try indent(file); | |
| 469 | try file.main.writer().writeAll("zig_breakpoint();\n"); | |
| 314 | 470 | return null; |
| 315 | 471 | } |
| 316 | 472 | |
| 317 | 473 | fn 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 | 476 | return null; |
| 320 | 477 | } |
| 321 | 478 | |
| 322 | 479 | fn genAsm(ctx: *Context, file: *C, as: *Inst.Assembly) !?[]u8 { |
| 480 | try indent(file); | |
| 323 | 481 | const writer = file.main.writer(); |
| 324 | try writer.writeAll(indentation); | |
| 325 | 482 | for (as.inputs) |i, index| { |
| 326 | 483 | if (i[0] == '{' and i[i.len - 1] == '}') { |
| 327 | 484 | const reg = i[1 .. i.len - 1]; |
| 328 | 485 | const arg = as.args[index]; |
| 329 | 486 | try writer.writeAll("register "); |
| 330 | try renderType(ctx, &file.header, writer, arg.ty); | |
| 487 | try renderType(ctx, writer, arg.ty); | |
| 331 | 488 | try writer.print(" {}_constant __asm__(\"{}\") = ", .{ reg, reg }); |
| 332 | 489 | // TODO merge constant handling into inst_map as well |
| 333 | 490 | if (arg.castTag(.constant)) |c| { |
src/link/C.zig+1-15| ... | ... | @@ -15,8 +15,6 @@ pub const base_tag: File.Tag = .c; |
| 15 | 15 | |
| 16 | 16 | pub const Header = struct { |
| 17 | 17 | buf: std.ArrayList(u8), |
| 18 | need_stddef: bool = false, | |
| 19 | need_stdint: bool = false, | |
| 20 | 18 | emit_loc: ?Compilation.EmitLoc, |
| 21 | 19 | |
| 22 | 20 | pub fn init(allocator: *Allocator, emit_loc: ?Compilation.EmitLoc) Header { |
| ... | ... | @@ -31,20 +29,8 @@ pub const Header = struct { |
| 31 | 29 | defer tracy.end(); |
| 32 | 30 | |
| 33 | 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 | 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 | } |
| 50 | 36 |
src/link/cbe.h+21-2| ... | ... | @@ -1,5 +1,4 @@ |
| 1 | 1 | #if __STDC_VERSION__ >= 199901L |
| 2 | // C99 or newer | |
| 3 | 2 | #include <stdbool.h> |
| 4 | 3 | #else |
| 5 | 4 | #define bool unsigned char |
| ... | ... | @@ -17,9 +16,29 @@ |
| 17 | 16 | #define zig_noreturn |
| 18 | 17 | #endif |
| 19 | 18 | |
| 20 | #if __GNUC__ | |
| 19 | #if defined(__GNUC__) | |
| 21 | 20 | #define zig_unreachable() __builtin_unreachable() |
| 22 | 21 | #else |
| 23 | 22 | #define zig_unreachable() |
| 24 | 23 | #endif |
| 25 | 24 | |
| 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 | 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 | 1834 | } else { |
| 1833 | 1835 | for (test_exec_args.items) |arg| { |
| 1834 | 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 | 11 | const enable_wasmtime: bool = build_options.enable_wasmtime; |
| 12 | 12 | const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir; |
| 13 | 13 | const ThreadPool = @import("ThreadPool.zig"); |
| 14 | const CrossTarget = std.zig.CrossTarget; | |
| 14 | 15 | |
| 15 | const cheader = @embedFile("link/cbe.h"); | |
| 16 | const c_header = @embedFile("link/cbe.h"); | |
| 16 | 17 | |
| 17 | 18 | test "self-hosted" { |
| 18 | 19 | var ctx = TestContext.init(); |
| ... | ... | @@ -88,6 +89,9 @@ pub const TestContext = struct { |
| 88 | 89 | /// A transformation update transforms the input and tests against |
| 89 | 90 | /// the expected output ZIR. |
| 90 | 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 | 95 | /// An error update attempts to compile bad code, and ensures that it |
| 92 | 96 | /// fails to compile, and for the expected reasons. |
| 93 | 97 | /// A slice containing the expected errors *in sequential order*. |
| ... | ... | @@ -109,12 +113,12 @@ pub const TestContext = struct { |
| 109 | 113 | path: []const u8, |
| 110 | 114 | }; |
| 111 | 115 | |
| 112 | pub const TestType = enum { | |
| 116 | pub const Extension = enum { | |
| 113 | 117 | Zig, |
| 114 | 118 | ZIR, |
| 115 | 119 | }; |
| 116 | 120 | |
| 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 | |
| 118 | 122 | /// update, so each update's source is treated as a single file being |
| 119 | 123 | /// updated by the test harness and incrementally compiled. |
| 120 | 124 | pub const Case = struct { |
| ... | ... | @@ -123,13 +127,14 @@ pub const TestContext = struct { |
| 123 | 127 | name: []const u8, |
| 124 | 128 | /// The platform the test targets. For non-native platforms, an emulator |
| 125 | 129 | /// such as QEMU is required for tests to complete. |
| 126 | target: std.zig.CrossTarget, | |
| 130 | target: CrossTarget, | |
| 127 | 131 | /// In order to be able to run e.g. Execution updates, this must be set |
| 128 | 132 | /// to Executable. |
| 129 | 133 | output_mode: std.builtin.OutputMode, |
| 130 | 134 | 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, | |
| 133 | 138 | |
| 134 | 139 | files: std.ArrayList(File), |
| 135 | 140 | |
| ... | ... | @@ -145,6 +150,7 @@ pub const TestContext = struct { |
| 145 | 150 | /// Adds a subcase in which the module is updated with `src`, and a C |
| 146 | 151 | /// header is generated. |
| 147 | 152 | pub fn addHeader(self: *Case, src: [:0]const u8, result: [:0]const u8) void { |
| 153 | self.emit_h = true; | |
| 148 | 154 | self.updates.append(.{ |
| 149 | 155 | .src = src, |
| 150 | 156 | .case = .{ .Header = result }, |
| ... | ... | @@ -160,6 +166,15 @@ pub const TestContext = struct { |
| 160 | 166 | }) catch unreachable; |
| 161 | 167 | } |
| 162 | 168 | |
| 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 | 178 | /// Adds a subcase in which the module is updated with `src`, which |
| 164 | 179 | /// should contain invalid input, and ensures that compilation fails |
| 165 | 180 | /// for the expected reasons, given in sequential order in `errors` in |
| ... | ... | @@ -214,86 +229,100 @@ pub const TestContext = struct { |
| 214 | 229 | pub fn addExe( |
| 215 | 230 | ctx: *TestContext, |
| 216 | 231 | name: []const u8, |
| 217 | target: std.zig.CrossTarget, | |
| 218 | T: TestType, | |
| 232 | target: CrossTarget, | |
| 233 | extension: Extension, | |
| 219 | 234 | ) *Case { |
| 220 | 235 | ctx.cases.append(Case{ |
| 221 | 236 | .name = name, |
| 222 | 237 | .target = target, |
| 223 | 238 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 224 | 239 | .output_mode = .Exe, |
| 225 | .extension = T, | |
| 240 | .extension = extension, | |
| 226 | 241 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 227 | 242 | }) catch unreachable; |
| 228 | 243 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 229 | 244 | } |
| 230 | 245 | |
| 231 | 246 | /// 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 | 248 | return ctx.addExe(name, target, .Zig); |
| 234 | 249 | } |
| 235 | 250 | |
| 236 | 251 | /// 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 | 253 | return ctx.addExe(name, target, .ZIR); |
| 239 | 254 | } |
| 240 | 255 | |
| 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 | 269 | pub fn addObj( |
| 242 | 270 | ctx: *TestContext, |
| 243 | 271 | name: []const u8, |
| 244 | target: std.zig.CrossTarget, | |
| 245 | T: TestType, | |
| 272 | target: CrossTarget, | |
| 273 | extension: Extension, | |
| 246 | 274 | ) *Case { |
| 247 | 275 | ctx.cases.append(Case{ |
| 248 | 276 | .name = name, |
| 249 | 277 | .target = target, |
| 250 | 278 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 251 | 279 | .output_mode = .Obj, |
| 252 | .extension = T, | |
| 280 | .extension = extension, | |
| 253 | 281 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 254 | 282 | }) catch unreachable; |
| 255 | 283 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 256 | 284 | } |
| 257 | 285 | |
| 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 { | |
| 260 | 288 | return ctx.addObj(name, target, .Zig); |
| 261 | 289 | } |
| 262 | 290 | |
| 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 { | |
| 265 | 293 | return ctx.addObj(name, target, .ZIR); |
| 266 | 294 | } |
| 267 | 295 | |
| 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 | 298 | ctx.cases.append(Case{ |
| 270 | 299 | .name = name, |
| 271 | 300 | .target = target, |
| 272 | 301 | .updates = std.ArrayList(Update).init(ctx.cases.allocator), |
| 273 | 302 | .output_mode = .Obj, |
| 274 | .extension = T, | |
| 275 | .cbe = true, | |
| 303 | .extension = ext, | |
| 304 | .object_format = .c, | |
| 276 | 305 | .files = std.ArrayList(File).init(ctx.cases.allocator), |
| 277 | 306 | }) catch unreachable; |
| 278 | 307 | return &ctx.cases.items[ctx.cases.items.len - 1]; |
| 279 | 308 | } |
| 280 | 309 | |
| 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); | |
| 283 | 312 | } |
| 284 | 313 | |
| 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); | |
| 287 | 316 | } |
| 288 | 317 | |
| 289 | 318 | pub fn addCompareOutput( |
| 290 | 319 | ctx: *TestContext, |
| 291 | 320 | name: []const u8, |
| 292 | T: TestType, | |
| 321 | extension: Extension, | |
| 293 | 322 | src: [:0]const u8, |
| 294 | 323 | expected_stdout: []const u8, |
| 295 | 324 | ) void { |
| 296 | ctx.addExe(name, .{}, T).addCompareOutput(src, expected_stdout); | |
| 325 | ctx.addExe(name, .{}, extension).addCompareOutput(src, expected_stdout); | |
| 297 | 326 | } |
| 298 | 327 | |
| 299 | 328 | /// Adds a test case that compiles the Zig source given in `src`, executes |
| ... | ... | @@ -321,12 +350,12 @@ pub const TestContext = struct { |
| 321 | 350 | pub fn addTransform( |
| 322 | 351 | ctx: *TestContext, |
| 323 | 352 | name: []const u8, |
| 324 | target: std.zig.CrossTarget, | |
| 325 | T: TestType, | |
| 353 | target: CrossTarget, | |
| 354 | extension: Extension, | |
| 326 | 355 | src: [:0]const u8, |
| 327 | 356 | result: [:0]const u8, |
| 328 | 357 | ) void { |
| 329 | ctx.addObj(name, target, T).addTransform(src, result); | |
| 358 | ctx.addObj(name, target, extension).addTransform(src, result); | |
| 330 | 359 | } |
| 331 | 360 | |
| 332 | 361 | /// Adds a test case that compiles the Zig given in `src` to ZIR and tests |
| ... | ... | @@ -334,7 +363,7 @@ pub const TestContext = struct { |
| 334 | 363 | pub fn transform( |
| 335 | 364 | ctx: *TestContext, |
| 336 | 365 | name: []const u8, |
| 337 | target: std.zig.CrossTarget, | |
| 366 | target: CrossTarget, | |
| 338 | 367 | src: [:0]const u8, |
| 339 | 368 | result: [:0]const u8, |
| 340 | 369 | ) void { |
| ... | ... | @@ -346,7 +375,7 @@ pub const TestContext = struct { |
| 346 | 375 | pub fn transformZIR( |
| 347 | 376 | ctx: *TestContext, |
| 348 | 377 | name: []const u8, |
| 349 | target: std.zig.CrossTarget, | |
| 378 | target: CrossTarget, | |
| 350 | 379 | src: [:0]const u8, |
| 351 | 380 | result: [:0]const u8, |
| 352 | 381 | ) void { |
| ... | ... | @@ -356,12 +385,12 @@ pub const TestContext = struct { |
| 356 | 385 | pub fn addError( |
| 357 | 386 | ctx: *TestContext, |
| 358 | 387 | name: []const u8, |
| 359 | target: std.zig.CrossTarget, | |
| 360 | T: TestType, | |
| 388 | target: CrossTarget, | |
| 389 | extension: Extension, | |
| 361 | 390 | src: [:0]const u8, |
| 362 | 391 | expected_errors: []const []const u8, |
| 363 | 392 | ) void { |
| 364 | ctx.addObj(name, target, T).addError(src, expected_errors); | |
| 393 | ctx.addObj(name, target, extension).addError(src, expected_errors); | |
| 365 | 394 | } |
| 366 | 395 | |
| 367 | 396 | /// Adds a test case that ensures that the Zig given in `src` fails to |
| ... | ... | @@ -370,7 +399,7 @@ pub const TestContext = struct { |
| 370 | 399 | pub fn compileError( |
| 371 | 400 | ctx: *TestContext, |
| 372 | 401 | name: []const u8, |
| 373 | target: std.zig.CrossTarget, | |
| 402 | target: CrossTarget, | |
| 374 | 403 | src: [:0]const u8, |
| 375 | 404 | expected_errors: []const []const u8, |
| 376 | 405 | ) void { |
| ... | ... | @@ -383,7 +412,7 @@ pub const TestContext = struct { |
| 383 | 412 | pub fn compileErrorZIR( |
| 384 | 413 | ctx: *TestContext, |
| 385 | 414 | name: []const u8, |
| 386 | target: std.zig.CrossTarget, | |
| 415 | target: CrossTarget, | |
| 387 | 416 | src: [:0]const u8, |
| 388 | 417 | expected_errors: []const []const u8, |
| 389 | 418 | ) void { |
| ... | ... | @@ -393,11 +422,11 @@ pub const TestContext = struct { |
| 393 | 422 | pub fn addCompiles( |
| 394 | 423 | ctx: *TestContext, |
| 395 | 424 | name: []const u8, |
| 396 | target: std.zig.CrossTarget, | |
| 397 | T: TestType, | |
| 425 | target: CrossTarget, | |
| 426 | extension: Extension, | |
| 398 | 427 | src: [:0]const u8, |
| 399 | 428 | ) void { |
| 400 | ctx.addObj(name, target, T).compiles(src); | |
| 429 | ctx.addObj(name, target, extension).compiles(src); | |
| 401 | 430 | } |
| 402 | 431 | |
| 403 | 432 | /// Adds a test case that asserts that the Zig given in `src` compiles |
| ... | ... | @@ -405,7 +434,7 @@ pub const TestContext = struct { |
| 405 | 434 | pub fn compiles( |
| 406 | 435 | ctx: *TestContext, |
| 407 | 436 | name: []const u8, |
| 408 | target: std.zig.CrossTarget, | |
| 437 | target: CrossTarget, | |
| 409 | 438 | src: [:0]const u8, |
| 410 | 439 | ) void { |
| 411 | 440 | ctx.addCompiles(name, target, .Zig, src); |
| ... | ... | @@ -416,7 +445,7 @@ pub const TestContext = struct { |
| 416 | 445 | pub fn compilesZIR( |
| 417 | 446 | ctx: *TestContext, |
| 418 | 447 | name: []const u8, |
| 419 | target: std.zig.CrossTarget, | |
| 448 | target: CrossTarget, | |
| 420 | 449 | src: [:0]const u8, |
| 421 | 450 | ) void { |
| 422 | 451 | ctx.addCompiles(name, target, .ZIR, src); |
| ... | ... | @@ -430,7 +459,7 @@ pub const TestContext = struct { |
| 430 | 459 | pub fn incrementalFailure( |
| 431 | 460 | ctx: *TestContext, |
| 432 | 461 | name: []const u8, |
| 433 | target: std.zig.CrossTarget, | |
| 462 | target: CrossTarget, | |
| 434 | 463 | src: [:0]const u8, |
| 435 | 464 | expected_errors: []const []const u8, |
| 436 | 465 | fixed_src: [:0]const u8, |
| ... | ... | @@ -448,7 +477,7 @@ pub const TestContext = struct { |
| 448 | 477 | pub fn incrementalFailureZIR( |
| 449 | 478 | ctx: *TestContext, |
| 450 | 479 | name: []const u8, |
| 451 | target: std.zig.CrossTarget, | |
| 480 | target: CrossTarget, | |
| 452 | 481 | src: [:0]const u8, |
| 453 | 482 | expected_errors: []const []const u8, |
| 454 | 483 | fixed_src: [:0]const u8, |
| ... | ... | @@ -548,12 +577,11 @@ pub const TestContext = struct { |
| 548 | 577 | .root_src_path = tmp_src_path, |
| 549 | 578 | }; |
| 550 | 579 | |
| 551 | const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null; | |
| 552 | 580 | const bin_name = try std.zig.binNameAlloc(arena, .{ |
| 553 | 581 | .root_name = "test_case", |
| 554 | 582 | .target = target, |
| 555 | 583 | .output_mode = case.output_mode, |
| 556 | .object_format = ofmt, | |
| 584 | .object_format = case.object_format, | |
| 557 | 585 | }); |
| 558 | 586 | |
| 559 | 587 | const emit_directory: Compilation.Directory = .{ |
| ... | ... | @@ -564,7 +592,7 @@ pub const TestContext = struct { |
| 564 | 592 | .directory = emit_directory, |
| 565 | 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 | 597 | .directory = emit_directory, |
| 570 | 598 | .basename = "test_case.h", |
| ... | ... | @@ -588,7 +616,7 @@ pub const TestContext = struct { |
| 588 | 616 | .emit_h = emit_h, |
| 589 | 617 | .root_pkg = &root_pkg, |
| 590 | 618 | .keep_source_files_loaded = true, |
| 591 | .object_format = ofmt, | |
| 619 | .object_format = case.object_format, | |
| 592 | 620 | .is_native_os = case.target.isNativeOs(), |
| 593 | 621 | .is_native_abi = case.target.isNativeAbi(), |
| 594 | 622 | }); |
| ... | ... | @@ -631,9 +659,10 @@ pub const TestContext = struct { |
| 631 | 659 | }, |
| 632 | 660 | } |
| 633 | 661 | } |
| 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 | }); | |
| 637 | 666 | } |
| 638 | 667 | std.debug.print("Test failed.\n", .{}); |
| 639 | 668 | std.process.exit(1); |
| ... | ... | @@ -644,67 +673,37 @@ pub const TestContext = struct { |
| 644 | 673 | .Header => |expected_output| { |
| 645 | 674 | var file = try tmp.dir.openFile("test_case.h", .{ .read = true }); |
| 646 | 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); | |
| 648 | 677 | |
| 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); | |
| 659 | 686 | }, |
| 660 | 687 | .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); | |
| 708 | 707 | }, |
| 709 | 708 | .Error => |e| { |
| 710 | 709 | var test_node = update_node.start("assert", 0); |
| ... | ... | @@ -762,8 +761,6 @@ pub const TestContext = struct { |
| 762 | 761 | } |
| 763 | 762 | }, |
| 764 | 763 | .Execution => |expected_stdout| { |
| 765 | std.debug.assert(!case.cbe); | |
| 766 | ||
| 767 | 764 | update_node.setEstimatedTotalItems(4); |
| 768 | 765 | var exec_result = x: { |
| 769 | 766 | var exec_node = update_node.start("execute", 0); |
| ... | ... | @@ -773,9 +770,12 @@ pub const TestContext = struct { |
| 773 | 770 | var argv = std.ArrayList([]const u8).init(allocator); |
| 774 | 771 | defer argv.deinit(); |
| 775 | 772 | |
| 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()) { | |
| 779 | 779 | .native => try argv.append(exe_path), |
| 780 | 780 | .unavailable => { |
| 781 | 781 | try self.runInterpreterIfAvailable(allocator, &exec_node, case, tmp.dir, bin_name); |
| ... | ... | @@ -837,18 +837,13 @@ pub const TestContext = struct { |
| 837 | 837 | switch (exec_result.term) { |
| 838 | 838 | .Exited => |code| { |
| 839 | 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 | 841 | return error.BinaryBadExitCode; |
| 842 | 842 | } |
| 843 | 843 | }, |
| 844 | 844 | else => return error.BinaryCrashed, |
| 845 | 845 | } |
| 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); | |
| 852 | 847 | }, |
| 853 | 848 | } |
| 854 | 849 | } |
src/type.zig+91-1| ... | ... | @@ -172,7 +172,15 @@ pub const Type = extern union { |
| 172 | 172 | const is_slice_b = isSlice(b); |
| 173 | 173 | if (is_slice_a != is_slice_b) |
| 174 | 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 | 185 | .Int => { |
| 178 | 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 | 1136 | }; |
| 1129 | 1137 | } |
| 1130 | 1138 | |
| 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 | 1221 | pub fn isSlice(self: Type) bool { |
| 1132 | 1222 | return switch (self.tag()) { |
| 1133 | 1223 | .u8, |
src/value.zig+24| ... | ... | @@ -82,6 +82,7 @@ pub const Value = extern union { |
| 82 | 82 | int_big_positive, |
| 83 | 83 | int_big_negative, |
| 84 | 84 | function, |
| 85 | extern_fn, | |
| 85 | 86 | variable, |
| 86 | 87 | ref_val, |
| 87 | 88 | decl_ref, |
| ... | ... | @@ -205,6 +206,7 @@ pub const Value = extern union { |
| 205 | 206 | @panic("TODO implement copying of big ints"); |
| 206 | 207 | }, |
| 207 | 208 | .function => return self.copyPayloadShallow(allocator, Payload.Function), |
| 209 | .extern_fn => return self.copyPayloadShallow(allocator, Payload.ExternFn), | |
| 208 | 210 | .variable => return self.copyPayloadShallow(allocator, Payload.Variable), |
| 209 | 211 | .ref_val => { |
| 210 | 212 | const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise); |
| ... | ... | @@ -337,6 +339,7 @@ pub const Value = extern union { |
| 337 | 339 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), |
| 338 | 340 | .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}), |
| 339 | 341 | .function => return out_stream.writeAll("(function)"), |
| 342 | .extern_fn => return out_stream.writeAll("(extern function)"), | |
| 340 | 343 | .variable => return out_stream.writeAll("(variable)"), |
| 341 | 344 | .ref_val => { |
| 342 | 345 | const ref_val = val.cast(Payload.RefVal).?; |
| ... | ... | @@ -468,6 +471,7 @@ pub const Value = extern union { |
| 468 | 471 | .int_big_positive, |
| 469 | 472 | .int_big_negative, |
| 470 | 473 | .function, |
| 474 | .extern_fn, | |
| 471 | 475 | .variable, |
| 472 | 476 | .ref_val, |
| 473 | 477 | .decl_ref, |
| ... | ... | @@ -533,6 +537,7 @@ pub const Value = extern union { |
| 533 | 537 | .anyframe_type, |
| 534 | 538 | .null_value, |
| 535 | 539 | .function, |
| 540 | .extern_fn, | |
| 536 | 541 | .variable, |
| 537 | 542 | .ref_val, |
| 538 | 543 | .decl_ref, |
| ... | ... | @@ -617,6 +622,7 @@ pub const Value = extern union { |
| 617 | 622 | .anyframe_type, |
| 618 | 623 | .null_value, |
| 619 | 624 | .function, |
| 625 | .extern_fn, | |
| 620 | 626 | .variable, |
| 621 | 627 | .ref_val, |
| 622 | 628 | .decl_ref, |
| ... | ... | @@ -701,6 +707,7 @@ pub const Value = extern union { |
| 701 | 707 | .anyframe_type, |
| 702 | 708 | .null_value, |
| 703 | 709 | .function, |
| 710 | .extern_fn, | |
| 704 | 711 | .variable, |
| 705 | 712 | .ref_val, |
| 706 | 713 | .decl_ref, |
| ... | ... | @@ -812,6 +819,7 @@ pub const Value = extern union { |
| 812 | 819 | .anyframe_type, |
| 813 | 820 | .null_value, |
| 814 | 821 | .function, |
| 822 | .extern_fn, | |
| 815 | 823 | .variable, |
| 816 | 824 | .ref_val, |
| 817 | 825 | .decl_ref, |
| ... | ... | @@ -901,6 +909,7 @@ pub const Value = extern union { |
| 901 | 909 | .anyframe_type, |
| 902 | 910 | .null_value, |
| 903 | 911 | .function, |
| 912 | .extern_fn, | |
| 904 | 913 | .variable, |
| 905 | 914 | .ref_val, |
| 906 | 915 | .decl_ref, |
| ... | ... | @@ -1071,6 +1080,7 @@ pub const Value = extern union { |
| 1071 | 1080 | .bool_false, |
| 1072 | 1081 | .null_value, |
| 1073 | 1082 | .function, |
| 1083 | .extern_fn, | |
| 1074 | 1084 | .variable, |
| 1075 | 1085 | .ref_val, |
| 1076 | 1086 | .decl_ref, |
| ... | ... | @@ -1150,6 +1160,7 @@ pub const Value = extern union { |
| 1150 | 1160 | .anyframe_type, |
| 1151 | 1161 | .null_value, |
| 1152 | 1162 | .function, |
| 1163 | .extern_fn, | |
| 1153 | 1164 | .variable, |
| 1154 | 1165 | .ref_val, |
| 1155 | 1166 | .decl_ref, |
| ... | ... | @@ -1383,6 +1394,10 @@ pub const Value = extern union { |
| 1383 | 1394 | const payload = @fieldParentPtr(Payload.Function, "base", self.ptr_otherwise); |
| 1384 | 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 | 1401 | .variable => { |
| 1387 | 1402 | const payload = @fieldParentPtr(Payload.Variable, "base", self.ptr_otherwise); |
| 1388 | 1403 | std.hash.autoHash(&hasher, payload.variable); |
| ... | ... | @@ -1449,6 +1464,7 @@ pub const Value = extern union { |
| 1449 | 1464 | .bool_false, |
| 1450 | 1465 | .null_value, |
| 1451 | 1466 | .function, |
| 1467 | .extern_fn, | |
| 1452 | 1468 | .variable, |
| 1453 | 1469 | .int_u64, |
| 1454 | 1470 | .int_i64, |
| ... | ... | @@ -1533,6 +1549,7 @@ pub const Value = extern union { |
| 1533 | 1549 | .bool_false, |
| 1534 | 1550 | .null_value, |
| 1535 | 1551 | .function, |
| 1552 | .extern_fn, | |
| 1536 | 1553 | .variable, |
| 1537 | 1554 | .int_u64, |
| 1538 | 1555 | .int_i64, |
| ... | ... | @@ -1634,6 +1651,7 @@ pub const Value = extern union { |
| 1634 | 1651 | .bool_true, |
| 1635 | 1652 | .bool_false, |
| 1636 | 1653 | .function, |
| 1654 | .extern_fn, | |
| 1637 | 1655 | .variable, |
| 1638 | 1656 | .int_u64, |
| 1639 | 1657 | .int_i64, |
| ... | ... | @@ -1730,6 +1748,7 @@ pub const Value = extern union { |
| 1730 | 1748 | .bool_true, |
| 1731 | 1749 | .bool_false, |
| 1732 | 1750 | .function, |
| 1751 | .extern_fn, | |
| 1733 | 1752 | .variable, |
| 1734 | 1753 | .int_u64, |
| 1735 | 1754 | .int_i64, |
| ... | ... | @@ -1793,6 +1812,11 @@ pub const Value = extern union { |
| 1793 | 1812 | func: *Module.Fn, |
| 1794 | 1813 | }; |
| 1795 | 1814 | |
| 1815 | pub const ExternFn = struct { | |
| 1816 | base: Payload = Payload{ .tag = .extern_fn }, | |
| 1817 | decl: *Module.Decl, | |
| 1818 | }; | |
| 1819 | ||
| 1796 | 1820 | pub const Variable = struct { |
| 1797 | 1821 | base: Payload = Payload{ .tag = .variable }, |
| 1798 | 1822 | variable: *Module.Var, |
test/stage2/cbe.zig+63-56| ... | ... | @@ -9,12 +9,37 @@ const linux_x64 = std.zig.CrossTarget{ |
| 9 | 9 | }; |
| 10 | 10 | |
| 11 | 11 | pub 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 | 36 | ctx.c("empty start function", linux_x64, |
| 13 | 37 | \\export fn _start() noreturn { |
| 14 | 38 | \\ unreachable; |
| 15 | 39 | \\} |
| 16 | 40 | , |
| 17 | 41 | \\zig_noreturn void _start(void) { |
| 42 | \\ zig_breakpoint(); | |
| 18 | 43 | \\ zig_unreachable(); |
| 19 | 44 | \\} |
| 20 | 45 | \\ |
| ... | ... | @@ -41,6 +66,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 41 | 66 | \\} |
| 42 | 67 | \\ |
| 43 | 68 | \\zig_noreturn void main(void) { |
| 69 | \\ zig_breakpoint(); | |
| 44 | 70 | \\ zig_unreachable(); |
| 45 | 71 | \\} |
| 46 | 72 | \\ |
| ... | ... | @@ -61,22 +87,21 @@ pub fn addCases(ctx: *TestContext) !void { |
| 61 | 87 | \\ exitGood(); |
| 62 | 88 | \\} |
| 63 | 89 | , |
| 64 | \\#include <stddef.h> | |
| 65 | \\ | |
| 66 | 90 | \\zig_noreturn void exitGood(void); |
| 67 | 91 | \\ |
| 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"; | |
| 71 | 95 | \\ |
| 72 | 96 | \\zig_noreturn void _start(void) { |
| 73 | 97 | \\ exitGood(); |
| 74 | 98 | \\} |
| 75 | 99 | \\ |
| 76 | 100 | \\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; | |
| 79 | 103 | \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); |
| 104 | \\ zig_breakpoint(); | |
| 80 | 105 | \\ zig_unreachable(); |
| 81 | 106 | \\} |
| 82 | 107 | \\ |
| ... | ... | @@ -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); | |
| 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"; | |
| 106 | 129 | \\ |
| 107 | 130 | \\zig_noreturn void _start(void) { |
| 108 | 131 | \\ exit(0); |
| 109 | 132 | \\} |
| 110 | 133 | \\ |
| 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; | |
| 114 | 137 | \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); |
| 138 | \\ zig_breakpoint(); | |
| 115 | 139 | \\ zig_unreachable(); |
| 116 | 140 | \\} |
| 117 | 141 | \\ |
| ... | ... | @@ -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 | 158 | \\zig_noreturn void exit(uint8_t arg0); |
| 138 | 159 | \\ |
| 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"; | |
| 142 | 163 | \\ |
| 143 | 164 | \\zig_noreturn void _start(void) { |
| 144 | 165 | \\ exit(0); |
| 145 | 166 | \\} |
| 146 | 167 | \\ |
| 147 | 168 | \\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; | |
| 151 | 172 | \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); |
| 173 | \\ zig_breakpoint(); | |
| 152 | 174 | \\ zig_unreachable(); |
| 153 | 175 | \\} |
| 154 | 176 | \\ |
| ... | ... | @@ -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 | 197 | \\zig_noreturn void exitMath(uint8_t arg0); |
| 179 | 198 | \\zig_noreturn void exit(uint8_t arg0); |
| 180 | 199 | \\ |
| 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"; | |
| 184 | 203 | \\ |
| 185 | 204 | \\zig_noreturn void _start(void) { |
| 186 | 205 | \\ exitMath(1); |
| ... | ... | @@ -193,10 +212,11 @@ pub fn addCases(ctx: *TestContext) !void { |
| 193 | 212 | \\} |
| 194 | 213 | \\ |
| 195 | 214 | \\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; | |
| 199 | 218 | \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); |
| 219 | \\ zig_breakpoint(); | |
| 200 | 220 | \\ zig_unreachable(); |
| 201 | 221 | \\} |
| 202 | 222 | \\ |
| ... | ... | @@ -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 | 243 | \\zig_noreturn void exitMath(uint8_t arg0); |
| 227 | 244 | \\zig_noreturn void exit(uint8_t arg0); |
| 228 | 245 | \\ |
| 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"; | |
| 232 | 249 | \\ |
| 233 | 250 | \\zig_noreturn void _start(void) { |
| 234 | 251 | \\ exitMath(1); |
| ... | ... | @@ -241,10 +258,11 @@ pub fn addCases(ctx: *TestContext) !void { |
| 241 | 258 | \\} |
| 242 | 259 | \\ |
| 243 | 260 | \\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; | |
| 247 | 264 | \\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant)); |
| 265 | \\ zig_breakpoint(); | |
| 248 | 266 | \\ zig_unreachable(); |
| 249 | 267 | \\} |
| 250 | 268 | \\ |
| ... | ... | @@ -252,33 +270,25 @@ pub fn addCases(ctx: *TestContext) !void { |
| 252 | 270 | ctx.h("header with single param function", linux_x64, |
| 253 | 271 | \\export fn start(a: u8) void{} |
| 254 | 272 | , |
| 255 | \\#include <stdint.h> | |
| 256 | \\ | |
| 257 | 273 | \\void start(uint8_t arg0); |
| 258 | 274 | \\ |
| 259 | 275 | ); |
| 260 | 276 | ctx.h("header with multiple param function", linux_x64, |
| 261 | 277 | \\export fn start(a: u8, b: u8, c: u8) void{} |
| 262 | 278 | , |
| 263 | \\#include <stdint.h> | |
| 264 | \\ | |
| 265 | 279 | \\void start(uint8_t arg0, uint8_t arg1, uint8_t arg2); |
| 266 | 280 | \\ |
| 267 | 281 | ); |
| 268 | 282 | ctx.h("header with u32 param function", linux_x64, |
| 269 | 283 | \\export fn start(a: u32) void{} |
| 270 | 284 | , |
| 271 | \\#include <stdint.h> | |
| 272 | \\ | |
| 273 | 285 | \\void start(uint32_t arg0); |
| 274 | 286 | \\ |
| 275 | 287 | ); |
| 276 | 288 | ctx.h("header with usize param function", linux_x64, |
| 277 | 289 | \\export fn start(a: usize) void{} |
| 278 | 290 | , |
| 279 | \\#include <stddef.h> | |
| 280 | \\ | |
| 281 | \\void start(size_t arg0); | |
| 291 | \\void start(uintptr_t arg0); | |
| 282 | 292 | \\ |
| 283 | 293 | ); |
| 284 | 294 | ctx.h("header with bool param function", linux_x64, |
| ... | ... | @@ -308,10 +318,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 308 | 318 | ctx.h("header with multiple includes", linux_x64, |
| 309 | 319 | \\export fn start(a: u32, b: usize) void{} |
| 310 | 320 | , |
| 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); | |
| 315 | 322 | \\ |
| 316 | 323 | ); |
| 317 | 324 | } |