From 10e6cde083cf9ddd1ae72850d45c25b1258d0d7e Mon Sep 17 00:00:00 2001 From: MCRusher Date: Sat, 23 Nov 2019 22:54:33 -0500 Subject: [PATCH 01/38] Added initCapacity and relevant test Added ArrayList.initCapcity() as a way to preallocate a block of memory to reduce future allocations. Added a test "std.ArrayList.initCapacity" that ensures initCapacity adds no elements and increases capacity by at least the requested amount --- lib/std/array_list.zig | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index 59fd2a10e5..d7da5f7f8a 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -40,6 +40,14 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type { .allocator = allocator, }; } + + /// Initialize with capacity to hold at least num elements. + /// Deinitialize with `deinit` or use `toOwnedSlice`. + pub fn initCapacity(allocator: *Allocator, num: usize) !Self { + var self = Self.init(allocator); + try self.ensureCapacity(num); + return self; + } /// Release all allocated memory. pub fn deinit(self: Self) void { @@ -271,6 +279,15 @@ test "std.ArrayList.init" { testing.expect(list.capacity() == 0); } +test "std.ArrayList.initCapacity" { + var bytes: [1024]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + var list = try ArrayList(i8).initCapacity(allocator, 200); + defer list.deinit(); + testing.expect(list.count() == 0); + testing.expect(list.capacity() >= 200); +} + test "std.ArrayList.basic" { var bytes: [1024]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; From d49e0a7b90dabbf486902fa9d4a4306aede55087 Mon Sep 17 00:00:00 2001 From: MCRusher Date: Sat, 23 Nov 2019 23:08:33 -0500 Subject: [PATCH 02/38] Added initCapacity, capacity, and 2 tests. Added Buffer.initCapcity() to buffer to allow preallocation of a block of memory to reduce future allocations. Uses the added ArrayList.initCapacity() function to achieve this. Added Buffer.capacity() to track current usable allocation size, not counting null byte, and returning 0 if empty or created with Buffer.initNull() Added a test for initCapacity() that shows that no further allocation is performed for an append of size smaller than or equal to capacity when initCapacity is used. Added a test for initSize(), since it did not exist already. Also added a comment to better explain the difference between initSize() and initCapacity() note: forgot in the first commit but thanks to mikdusan for helping me brainstorm, through the process, and for drawing up a draft diff which I tweaked. --- lib/std/buffer.zig | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/lib/std/buffer.zig b/lib/std/buffer.zig index 24bd23fa74..7876f2197f 100644 --- a/lib/std/buffer.zig +++ b/lib/std/buffer.zig @@ -16,13 +16,22 @@ pub const Buffer = struct { mem.copy(u8, self.list.items, m); return self; } - + + /// Initialize memory to size bytes of undefined values. /// Must deinitialize with deinit. pub fn initSize(allocator: *Allocator, size: usize) !Buffer { var self = initNull(allocator); try self.resize(size); return self; } + + /// Initialize with capacity to hold at least num bytes. + /// Must deinitialize with deinit. + pub fn initCapacity(allocator: *Allocator, num: usize) !Buffer { + var self = Buffer{ .list = try ArrayList(u8).initCapacity(allocator, num + 1) }; + self.list.appendAssumeCapacity(0); + return self; + } /// Must deinitialize with deinit. /// None of the other operations are valid until you do one of these: @@ -98,6 +107,13 @@ pub const Buffer = struct { pub fn len(self: Buffer) usize { return self.list.len - 1; } + + pub fn capacity(self: Buffer) usize { + return if (self.list.items.len > 0) + self.list.items.len - 1 + else + 0; + } pub fn append(self: *Buffer, m: []const u8) !void { const old_len = self.len(); @@ -156,3 +172,21 @@ test "simple Buffer" { try buf2.resize(4); testing.expect(buf.startsWith(buf2.toSlice())); } + +test "Buffer.initSize" { + var buf = try Buffer.initSize(debug.global_allocator, 3); + testing.expect(buf.len() == 3); + try buf.append("hello"); + testing.expect(mem.eql(u8, buf.toSliceConst()[3..], "hello")); +} + +test "Buffer.initCapacity" { + var buf = try Buffer.initCapacity(debug.global_allocator, 10); + testing.expect(buf.len() == 0); + testing.expect(buf.capacity() >= 10); + const old_cap = buf.capacity(); + try buf.append("hello"); + testing.expect(buf.len() == 5); + testing.expect(buf.capacity() == old_cap); + testing.expect(mem.eql(u8, buf.toSliceConst(), "hello")); +} From 17abb7ef7f75705c1195022076e95cdde6ab2243 Mon Sep 17 00:00:00 2001 From: MCRusher Date: Sat, 23 Nov 2019 23:49:55 -0500 Subject: [PATCH 03/38] Adds initCapacity() to buffer and arraylist array_list.zig: - adds ArrayList.initCapacity(*Allocator,usize) to allow preallocation of a block at initialization to reduce future allocations. - adds a test for ArrayList.initCapacity() that ensures ArrayList.len() is unchanged and that at least the requested amount is allocated for. buffer.zig: - adds Buffer.initCapacity(*Allocator,usize), based off of ArrayList.initCapacity(), to preallocate a buffer before use. note: contrary to Buffer.initSize(0) and then Buffer.list.ensureCapacity(200) (the presumed current method), this only allocates once instead of twice. - adds Buffer.capacity to check usable allocated space, not including the null byte. note: returns 0 when Buffer has only a null byte or when initNull() was used before without resize()/replaceContents(). - adds a test "Buffer.initCapacity" which ensures that Buffer.append()'s with [added size <= Buffer.capacity()-Buffer.len()] do not cause a reallocation to occur. - adds a test "Buffer.initSize" which ensures that Buffer.initSize() behaves as expected, also combined with Buffer.append(). - adds a doc comment to Buffer.initSize() that makes its function and distinction from Buffer.initCapacity() clearer. From 104b6f16297812b1578262a5b5b4a091a6fc480d Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 21 Sep 2019 17:38:45 +0200 Subject: [PATCH 04/38] refactored gen_h_file to improve maintainability and output - extracted functions - factorised extern "C" into a block containing all function prototypes instead of writing macros all over the place - using intermediate buffers instead of writing directly to the output file --- src/codegen.cpp | 267 +++++++++++++++++++++++++----------------------- test/gen_h.zig | 30 +++--- 2 files changed, 158 insertions(+), 139 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a288f397fd..c5e29ff4f7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9732,113 +9732,7 @@ static Buf *preprocessor_mangle(Buf *src) { return result; } -static void gen_h_file(CodeGen *g) { - GenH gen_h_data = {0}; - GenH *gen_h = &gen_h_data; - - assert(!g->is_test_build); - assert(!g->disable_gen_h); - - Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name)); - - FILE *out_h = fopen(buf_ptr(out_h_path), "wb"); - if (!out_h) - zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno)); - - Buf *export_macro = nullptr; - if (g->is_dynamic) { - export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); - buf_upcase(export_macro); - } - - Buf *extern_c_macro = preprocessor_mangle(buf_sprintf("%s_EXTERN_C", buf_ptr(g->root_out_name))); - buf_upcase(extern_c_macro); - - Buf h_buf = BUF_INIT; - buf_resize(&h_buf, 0); - for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { - ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i); - - if (fn_table_entry->export_list.length == 0) - continue; - - FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; - - Buf return_type_c = BUF_INIT; - get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); - - Buf *symbol_name; - if (fn_table_entry->export_list.length == 0) { - symbol_name = &fn_table_entry->symbol_name; - } else { - GlobalExport *fn_export = &fn_table_entry->export_list.items[0]; - symbol_name = &fn_export->name; - } - - buf_appendf(&h_buf, "%s %s %s(", - buf_ptr(g->is_dynamic ? export_macro : extern_c_macro), - buf_ptr(&return_type_c), - buf_ptr(symbol_name)); - - Buf param_type_c = BUF_INIT; - if (fn_type_id->param_count > 0) { - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; - AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); - Buf *param_name = param_decl_node->data.param_decl.name; - - const char *comma_str = (param_i == 0) ? "" : ", "; - const char *restrict_str = param_info->is_noalias ? "restrict" : ""; - get_c_type(g, gen_h, param_info->type, ¶m_type_c); - - if (param_info->type->id == ZigTypeIdArray) { - // Arrays decay to pointers - buf_appendf(&h_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } else { - buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } - } - buf_appendf(&h_buf, ")"); - } else { - buf_appendf(&h_buf, "void)"); - } - - buf_appendf(&h_buf, ";\n"); - - } - - Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); - buf_upcase(ifdef_dance_name); - - fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); - fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); - - if (g->c_want_stdbool) - fprintf(out_h, "#include \n"); - if (g->c_want_stdint) - fprintf(out_h, "#include \n"); - - fprintf(out_h, "\n"); - - fprintf(out_h, "#ifdef __cplusplus\n"); - fprintf(out_h, "#define %s extern \"C\"\n", buf_ptr(extern_c_macro)); - fprintf(out_h, "#else\n"); - fprintf(out_h, "#define %s\n", buf_ptr(extern_c_macro)); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - - if (g->is_dynamic) { - fprintf(out_h, "#if defined(_WIN32)\n"); - fprintf(out_h, "#define %s %s __declspec(dllimport)\n", buf_ptr(export_macro), buf_ptr(extern_c_macro)); - fprintf(out_h, "#else\n"); - fprintf(out_h, "#define %s %s __attribute__((visibility (\"default\")))\n", - buf_ptr(export_macro), buf_ptr(extern_c_macro)); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - } - +static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) { for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) { ZigType *type_entry = gen_h->types_to_declare.at(type_i); switch (type_entry->id) { @@ -9869,25 +9763,25 @@ static void gen_h_file(CodeGen *g) { case ZigTypeIdEnum: if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { - fprintf(out_h, "enum %s {\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry))); for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; Buf *value_buf = buf_alloc(); bigint_append_buf(value_buf, &enum_field->value, 10); - fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); + buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); if (field_i != type_entry->data.enumeration.src_field_count - 1) { - fprintf(out_h, ","); + buf_appendf(out_buf, ","); } - fprintf(out_h, "\n"); + buf_appendf(out_buf, "\n"); } - fprintf(out_h, "};\n\n"); + buf_appendf(out_buf, "};\n\n"); } else { - fprintf(out_h, "enum %s;\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry))); } break; case ZigTypeIdStruct: if (type_entry->data.structure.layout == ContainerLayoutExtern) { - fprintf(out_h, "struct %s {\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry))); for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { TypeStructField *struct_field = type_entry->data.structure.fields[field_i]; @@ -9895,43 +9789,166 @@ static void gen_h_file(CodeGen *g) { get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); if (struct_field->type_entry->id == ZigTypeIdArray) { - fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), + buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name), struct_field->type_entry->data.array.len); } else { - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); + buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); } } - fprintf(out_h, "};\n\n"); + buf_appendf(out_buf, "};\n\n"); } else { - fprintf(out_h, "struct %s;\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); } break; case ZigTypeIdUnion: if (type_entry->data.unionation.layout == ContainerLayoutExtern) { - fprintf(out_h, "union %s {\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry))); for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; Buf *type_name_buf = buf_alloc(); get_c_type(g, gen_h, union_field->type_entry, type_name_buf); - fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); + buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); } - fprintf(out_h, "};\n\n"); + buf_appendf(out_buf, "};\n\n"); } else { - fprintf(out_h, "union %s;\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry))); } break; case ZigTypeIdOpaque: - fprintf(out_h, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); + buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); break; } } +} - fprintf(out_h, "%s", buf_ptr(&h_buf)); +static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) { + for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { + ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i); - fprintf(out_h, "\n#endif\n"); + if (fn_table_entry->export_list.length == 0) + continue; + + FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; + + Buf return_type_c = BUF_INIT; + get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); + + Buf *symbol_name; + if (fn_table_entry->export_list.length == 0) { + symbol_name = &fn_table_entry->symbol_name; + } else { + GlobalExport *fn_export = &fn_table_entry->export_list.items[0]; + symbol_name = &fn_export->name; + } + + if (export_macro != nullptr) { + buf_appendf(out_buf, "%s %s %s(", + buf_ptr(export_macro), + buf_ptr(&return_type_c), + buf_ptr(symbol_name)); + } else { + buf_appendf(out_buf, "%s %s(", + buf_ptr(&return_type_c), + buf_ptr(symbol_name)); + } + + Buf param_type_c = BUF_INIT; + if (fn_type_id->param_count > 0) { + for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { + FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; + AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); + Buf *param_name = param_decl_node->data.param_decl.name; + + const char *comma_str = (param_i == 0) ? "" : ", "; + const char *restrict_str = param_info->is_noalias ? "restrict" : ""; + get_c_type(g, gen_h, param_info->type, ¶m_type_c); + + if (param_info->type->id == ZigTypeIdArray) { + // Arrays decay to pointers + buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c), + restrict_str, buf_ptr(param_name)); + } else { + buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c), + restrict_str, buf_ptr(param_name)); + } + } + buf_appendf(out_buf, ")"); + } else { + buf_appendf(out_buf, "void)"); + } + + buf_appendf(out_buf, ";\n"); + } +} + +static void gen_h_file(CodeGen *g) { + GenH gen_h_data = {0}; + GenH *gen_h = &gen_h_data; + + assert(!g->is_test_build); + assert(!g->disable_gen_h); + + Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name)); + + FILE *out_h = fopen(buf_ptr(out_h_path), "wb"); + if (!out_h) + zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno)); + + Buf *export_macro = nullptr; + if (g->is_dynamic) { + export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); + buf_upcase(export_macro); + } + + Buf fns_buf = BUF_INIT; + buf_resize(&fns_buf, 0); + gen_h_file_functions(g, gen_h, &fns_buf, export_macro); + + Buf types_buf = BUF_INIT; + buf_resize(&types_buf, 0); + gen_h_file_types(g, gen_h, &types_buf); + Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); + buf_upcase(ifdef_dance_name); + + fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); + fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); + + if (g->c_want_stdbool) + fprintf(out_h, "#include \n"); + if (g->c_want_stdint) + fprintf(out_h, "#include \n"); + + fprintf(out_h, "\n"); + + if (g->is_dynamic) { + fprintf(out_h, "#if defined(_WIN32)\n"); + fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro)); + fprintf(out_h, "#else\n"); + fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n", + buf_ptr(export_macro)); + fprintf(out_h, "#endif\n"); + fprintf(out_h, "\n"); + } + + fprintf(out_h, "%s", buf_ptr(&types_buf)); + + fprintf(out_h, "#ifdef __cplusplus\n"); + fprintf(out_h, "extern \"C\" {\n"); + fprintf(out_h, "#endif\n"); + fprintf(out_h, "\n"); + + fprintf(out_h, "%s\n", buf_ptr(&fns_buf)); + + fprintf(out_h, "#ifdef __cplusplus\n"); + fprintf(out_h, "} // extern \"C\"\n"); + fprintf(out_h, "#endif\n\n"); + + fprintf(out_h, "%s\n", buf_ptr(&vars_buf)); + + fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name)); if (fclose(out_h)) zig_panic("unable to close h file: %s", strerror(errno)); diff --git a/test/gen_h.zig b/test/gen_h.zig index 5979afd66a..2fc63fbb18 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -10,9 +10,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ B = 1, \\ C = 2 \\}; - \\ - \\TEST_EXTERN_C void entry(enum Foo foo); - \\ + , + \\void entry(enum Foo foo); ); cases.add("declare struct", @@ -34,8 +33,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ uint64_t E; \\ uint64_t F; \\}; - \\ - \\TEST_EXTERN_C void entry(struct Foo foo); + , + \\void entry(struct Foo foo); \\ ); @@ -69,8 +68,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ bool C; \\ struct Big D; \\}; - \\ - \\TEST_EXTERN_C void entry(union Foo foo); + , + \\void entry(union Foo foo); \\ ); @@ -80,8 +79,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn entry(foo: ?*Foo) void { } , \\struct Foo; - \\ - \\TEST_EXTERN_C void entry(struct Foo * foo); + , + \\void entry(struct Foo * foo); ); cases.add("array field-type", @@ -95,8 +94,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ int32_t A[2]; \\ uint32_t * B[4]; \\}; - \\ - \\TEST_EXTERN_C void entry(struct Foo foo, uint8_t bar[]); + , + \\void entry(struct Foo foo, uint8_t bar[]); \\ ); @@ -110,7 +109,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\} , \\struct S; - \\TEST_EXTERN_C uint8_t a(struct S * s); + , + \\uint8_t a(struct S * s); \\ ); @@ -125,7 +125,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\} , \\union U; - \\TEST_EXTERN_C uint8_t a(union U * s); + , + \\uint8_t a(union U * s); \\ ); @@ -140,7 +141,8 @@ pub fn addCases(cases: *tests.GenHContext) void { \\} , \\enum E; - \\TEST_EXTERN_C uint8_t a(enum E * s); + , + \\uint8_t a(enum E * s); \\ ); } From 49156e398053a47875448d3da123a87645ff26b6 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 21 Sep 2019 17:39:20 +0200 Subject: [PATCH 05/38] gen-h: add a pass for exported variables --- src/codegen.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index c5e29ff4f7..d7a0a52360 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9884,6 +9884,28 @@ static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* exp } } +static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) { + for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) { + ZigVar* var = g->global_vars.at(exp_var_i)->var; + if (var->export_list.length == 0) + continue; + + Buf var_type_c = BUF_INIT; + get_c_type(g, gen_h, var->var_type, &var_type_c); + + if (export_macro != nullptr) { + buf_appendf(h_buf, "extern %s %s %s;\n", + buf_ptr(export_macro), + buf_ptr(&var_type_c), + var->name); + } else { + buf_appendf(h_buf, "extern %s %s;\n", + buf_ptr(&var_type_c), + var->name); + } + } +} + static void gen_h_file(CodeGen *g) { GenH gen_h_data = {0}; GenH *gen_h = &gen_h_data; @@ -9907,9 +9929,15 @@ static void gen_h_file(CodeGen *g) { buf_resize(&fns_buf, 0); gen_h_file_functions(g, gen_h, &fns_buf, export_macro); + Buf vars_buf = BUF_INIT; + buf_resize(&vars_buf, 0); + gen_h_file_variables(g, gen_h, &vars_buf, export_macro); + + // Types will be populated by exported functions and variables so it has to run last. Buf types_buf = BUF_INIT; buf_resize(&types_buf, 0); gen_h_file_types(g, gen_h, &types_buf); + Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); buf_upcase(ifdef_dance_name); From 1bde3b68218ecc25bfcc5e2304399294f83a74d8 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 21 Sep 2019 17:41:25 +0200 Subject: [PATCH 06/38] gen-h: register the need for stdbool.h and stdint.h when exploring types recursively otherwise they wouldn't be included when only a struct contains them --- src/codegen.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index d7a0a52360..8049c3e065 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -9515,7 +9515,11 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e case ZigTypeIdVoid: case ZigTypeIdUnreachable: case ZigTypeIdBool: + g->c_want_stdbool = true; + return; case ZigTypeIdInt: + g->c_want_stdint = true; + return; case ZigTypeIdFloat: return; case ZigTypeIdOpaque: @@ -9596,7 +9600,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu break; case ZigTypeIdBool: buf_init_from_str(out_buf, "bool"); - g->c_want_stdbool = true; break; case ZigTypeIdUnreachable: buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void"); @@ -9620,7 +9623,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu } break; case ZigTypeIdInt: - g->c_want_stdint = true; buf_resize(out_buf, 0); buf_appendf(out_buf, "%sint%" PRIu32 "_t", type_entry->data.integral.is_signed ? "" : "u", From 912c1c24c32799c63949fdf466fccd31f84a1aa1 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 21 Sep 2019 17:42:00 +0200 Subject: [PATCH 07/38] simple test for exporting variables in static libs --- test/standalone/static_c_lib/foo.c | 2 ++ test/standalone/static_c_lib/foo.h | 1 + test/standalone/static_c_lib/foo.zig | 4 ++++ 3 files changed, 7 insertions(+) diff --git a/test/standalone/static_c_lib/foo.c b/test/standalone/static_c_lib/foo.c index 2366282d47..78a332a6ee 100644 --- a/test/standalone/static_c_lib/foo.c +++ b/test/standalone/static_c_lib/foo.c @@ -2,3 +2,5 @@ uint32_t add(uint32_t a, uint32_t b) { return a + b; } + +uint32_t foo = 12345; diff --git a/test/standalone/static_c_lib/foo.h b/test/standalone/static_c_lib/foo.h index e8ebb9842e..6700499fe8 100644 --- a/test/standalone/static_c_lib/foo.h +++ b/test/standalone/static_c_lib/foo.h @@ -1,2 +1,3 @@ #include uint32_t add(uint32_t a, uint32_t b); +extern uint32_t foo; diff --git a/test/standalone/static_c_lib/foo.zig b/test/standalone/static_c_lib/foo.zig index edf94539c8..a5ba90c95d 100644 --- a/test/standalone/static_c_lib/foo.zig +++ b/test/standalone/static_c_lib/foo.zig @@ -6,3 +6,7 @@ test "C add" { const result = c.add(1, 2); expect(result == 3); } + +test "C extern variable" { + expect(c.foo == 12345); +} From 696567d9d7870e5bf640186193fe0b7fbccdb6b6 Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 21 Sep 2019 23:29:55 +0200 Subject: [PATCH 08/38] export: check variable type also fixed existing occurrences --- src-self-hosted/stage1.zig | 6 +++--- src/analyze.cpp | 11 +++++++++++ test/gen_h.zig | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index 37945bf029..2c5503380b 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -428,11 +428,11 @@ export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextRes }; } -export const stage2_DepTokenizer = extern struct { +const stage2_DepTokenizer = extern struct { handle: *DepTokenizer, }; -export const stage2_DepNextResult = extern struct { +const stage2_DepNextResult = extern struct { type_id: TypeId, // when type_id == error --> error text @@ -441,7 +441,7 @@ export const stage2_DepNextResult = extern struct { // when type_id == prereq --> prereq pathname textz: [*]const u8, - export const TypeId = extern enum { + const TypeId = extern enum { error_, null_, target, diff --git a/src/analyze.cpp b/src/analyze.cpp index 0a8b32dca7..97f1fd5227 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3773,6 +3773,16 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf return variable_entry; } +static void validate_export_var_type(CodeGen *g, ZigType* type, AstNode *source_node) { + switch (type->id) { + case ZigTypeIdMetaType: + add_node_error(g, source_node, buf_sprintf("cannot export variable of type 'type'")); + break; + default: + break; + } +} + static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { AstNode *source_node = tld_var->base.source_node; AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration; @@ -3862,6 +3872,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) { } if (is_export) { + validate_export_var_type(g, type, source_node); add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong); } diff --git a/test/gen_h.zig b/test/gen_h.zig index 2fc63fbb18..93ccb21a64 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -74,7 +74,7 @@ pub fn addCases(cases: *tests.GenHContext) void { ); cases.add("declare opaque type", - \\export const Foo = @OpaqueType(); + \\const Foo = @OpaqueType(); \\ \\export fn entry(foo: ?*Foo) void { } , From 6ebd26f3dbaf54230f24118043fe91d1f09f9e8e Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 23 Nov 2019 16:44:33 +0100 Subject: [PATCH 09/38] removed unnecessary backslashes in windows' libc paths --- src/libc_installation.cpp | 2 +- src/os.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp index 6ae6bb9075..2adc1cb69d 100644 --- a/src/libc_installation.cpp +++ b/src/libc_installation.cpp @@ -389,7 +389,7 @@ static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, Zi } Buf search_path = BUF_INIT; buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); - buf_append_str(&search_path, "\\..\\..\\include"); + buf_append_str(&search_path, "..\\..\\include"); Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path)); bool exists; diff --git a/src/os.cpp b/src/os.cpp index da1f4ecfb0..f6a0b4fbd8 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1554,7 +1554,7 @@ void os_stderr_set_color(TermColor color) { Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); + buf_appendf(output_buf, "%sLib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1586,7 +1586,7 @@ Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Ar Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { #if defined(ZIG_OS_WINDOWS) buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); + buf_appendf(output_buf, "%sInclude\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { return ErrorNone; } @@ -1603,7 +1603,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch #if defined(ZIG_OS_WINDOWS) { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); + buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); @@ -1626,7 +1626,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch } { buf_resize(output_buf, 0); - buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); + buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); switch (platform_type) { case ZigLLVM_x86: buf_append_str(output_buf, "x86\\"); From 36849d8a7b5b0ed62d966ea9c402f192ade0cadf Mon Sep 17 00:00:00 2001 From: Vexu <15308111+Vexu@users.noreply.github.com> Date: Tue, 26 Nov 2019 10:27:51 +0200 Subject: [PATCH 10/38] fixes and cleanup in self hosted --- src-self-hosted/codegen.zig | 30 ++-- src-self-hosted/compilation.zig | 221 +++++++++++--------------- src-self-hosted/decl.zig | 9 +- src-self-hosted/ir.zig | 43 ++--- src-self-hosted/libc_installation.zig | 4 +- src-self-hosted/link.zig | 55 +++---- src-self-hosted/llvm.zig | 2 +- src-self-hosted/main.zig | 112 ++++++------- src-self-hosted/test.zig | 11 +- src-self-hosted/type.zig | 22 +-- src-self-hosted/util.zig | 23 +-- src-self-hosted/value.zig | 12 +- 12 files changed, 243 insertions(+), 301 deletions(-) diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 7d4fb5fa72..030112985a 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -25,10 +25,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const context = llvm_handle.node.data; - const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; + const module = llvm.ModuleCreateWithNameInContext(comp.name.toSliceConst(), context) orelse return error.OutOfMemory; defer llvm.DisposeModule(module); - llvm.SetTarget(module, comp.llvm_triple.ptr()); + llvm.SetTarget(module, comp.llvm_triple.toSliceConst()); llvm.SetDataLayout(module, comp.target_layout_str); if (util.getObjectFormat(comp.target) == .coff) { @@ -48,23 +48,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const producer = try std.Buffer.allocPrint( &code.arena.allocator, "zig {}.{}.{}", - u32(c.ZIG_VERSION_MAJOR), - u32(c.ZIG_VERSION_MINOR), - u32(c.ZIG_VERSION_PATCH), + @as(u32, c.ZIG_VERSION_MAJOR), + @as(u32, c.ZIG_VERSION_MINOR), + @as(u32, c.ZIG_VERSION_PATCH), ); const flags = ""; const runtime_version = 0; const compile_unit_file = llvm.CreateFile( dibuilder, - comp.name.ptr(), - comp.root_package.root_src_dir.ptr(), + comp.name.toSliceConst(), + comp.root_package.root_src_dir.toSliceConst(), ) orelse return error.OutOfMemory; const is_optimized = comp.build_mode != .Debug; const compile_unit = llvm.CreateCompileUnit( dibuilder, DW.LANG_C99, compile_unit_file, - producer.ptr(), + producer.toSliceConst(), is_optimized, flags, runtime_version, @@ -99,7 +99,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) // verify the llvm module when safety is on if (std.debug.runtime_safety) { - var error_ptr: ?[*]u8 = null; + var error_ptr: ?[*:0]u8 = null; _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); } @@ -108,12 +108,12 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const is_small = comp.build_mode == .ReleaseSmall; const is_debug = comp.build_mode == .Debug; - var err_msg: [*]u8 = undefined; + var err_msg: [*:0]u8 = undefined; // TODO integrate this with evented I/O if (llvm.TargetMachineEmitToFile( comp.target_machine, module, - output_path.ptr(), + output_path.toSliceConst(), llvm.EmitBinary, &err_msg, is_debug, @@ -154,7 +154,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, - fn_val.symbol_name.ptr(), + fn_val.symbol_name.toSliceConst(), llvm_fn_type, ) orelse return error.OutOfMemory; @@ -379,7 +379,7 @@ fn renderLoadUntyped( ptr: *llvm.Value, alignment: Type.Pointer.Align, vol: Type.Pointer.Vol, - name: [*]const u8, + name: [*:0]const u8, ) !*llvm.Value { const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory; switch (vol) { @@ -390,7 +390,7 @@ fn renderLoadUntyped( return result; } -fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value { +fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value { return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name); } @@ -438,7 +438,7 @@ pub fn renderAlloca( ) !*llvm.Value { const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context); const name_with_null = try std.cstr.addNullByte(ofile.arena, name); - const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory; + const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory; llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type)); return result; } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 2f0de0c521..c8da540325 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -93,7 +93,7 @@ pub const ZigCompiler = struct { return LlvmHandle{ .node = node }; } - pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { + pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { if (self.native_libc.start()) |ptr| return ptr; try self.native_libc.data.findNative(self.allocator); self.native_libc.resolve(); @@ -135,26 +135,26 @@ pub const Compilation = struct { /// lazily created when we need it tmp_dir: event.Future(BuildError![]u8), - version_major: u32, - version_minor: u32, - version_patch: u32, + version_major: u32 = 0, + version_minor: u32 = 0, + version_patch: u32 = 0, - linker_script: ?[]const u8, - out_h_path: ?[]const u8, + linker_script: ?[]const u8 = null, + out_h_path: ?[]const u8 = null, - is_test: bool, - each_lib_rpath: bool, - strip: bool, + is_test: bool = false, + each_lib_rpath: bool = false, + strip: bool = false, is_static: bool, - linker_rdynamic: bool, + linker_rdynamic: bool = false, - clang_argv: []const []const u8, - lib_dirs: []const []const u8, - rpath_list: []const []const u8, - assembly_files: []const []const u8, + clang_argv: []const []const u8 = [_][]const u8{}, + lib_dirs: []const []const u8 = [_][]const u8{}, + rpath_list: []const []const u8 = [_][]const u8{}, + assembly_files: []const []const u8 = [_][]const u8{}, /// paths that are explicitly provided by the user to link against - link_objects: []const []const u8, + link_objects: []const []const u8 = [_][]const u8{}, /// functions that have their own objects that we need to link /// it uses an optional pointer so that tombstone removals are possible @@ -162,33 +162,33 @@ pub const Compilation = struct { pub const FnLinkSet = std.TailQueue(?*Value.Fn); - windows_subsystem_windows: bool, - windows_subsystem_console: bool, + windows_subsystem_windows: bool = false, + windows_subsystem_console: bool = false, link_libs_list: ArrayList(*LinkLib), - libc_link_lib: ?*LinkLib, + libc_link_lib: ?*LinkLib = null, - err_color: errmsg.Color, + err_color: errmsg.Color = .Auto, - verbose_tokenize: bool, - verbose_ast_tree: bool, - verbose_ast_fmt: bool, - verbose_cimport: bool, - verbose_ir: bool, - verbose_llvm_ir: bool, - verbose_link: bool, + verbose_tokenize: bool = false, + verbose_ast_tree: bool = false, + verbose_ast_fmt: bool = false, + verbose_cimport: bool = false, + verbose_ir: bool = false, + verbose_llvm_ir: bool = false, + verbose_link: bool = false, - darwin_frameworks: []const []const u8, - darwin_version_min: DarwinVersionMin, + darwin_frameworks: []const []const u8 = [_][]const u8{}, + darwin_version_min: DarwinVersionMin = .None, - test_filters: []const []const u8, - test_name_prefix: ?[]const u8, + test_filters: []const []const u8 = [_][]const u8{}, + test_name_prefix: ?[]const u8 = null, - emit_file_type: Emit, + emit_file_type: Emit = .Binary, kind: Kind, - link_out_file: ?[]const u8, + link_out_file: ?[]const u8 = null, events: *event.Channel(Event), exported_symbol_names: event.Locked(Decl.Table), @@ -213,7 +213,7 @@ pub const Compilation = struct { target_machine: *llvm.TargetMachine, target_data_ref: *llvm.TargetData, - target_layout_str: [*]u8, + target_layout_str: [*:0]u8, target_ptr_bits: u32, /// for allocating things which have the same lifetime as this Compilation @@ -222,7 +222,7 @@ pub const Compilation = struct { root_package: *Package, std_package: *Package, - override_libc: ?*LibCInstallation, + override_libc: ?*LibCInstallation = null, /// need to wait on this group before deinitializing deinit_group: event.Group(void), @@ -231,7 +231,7 @@ pub const Compilation = struct { // main_loop_frame: @Frame(Compilation.mainLoop), main_loop_future: event.Future(void), - have_err_ret_tracing: bool, + have_err_ret_tracing: bool = false, /// not locked because it is read-only primitive_type_table: TypeTable, @@ -243,7 +243,7 @@ pub const Compilation = struct { c_int_types: [CInt.list.len]*Type.Int, - // fs_watch: *fs.Watch(*Scope.Root), + fs_watch: *fs.Watch(*Scope.Root), const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); @@ -392,43 +392,10 @@ pub const Compilation = struct { .name = undefined, .llvm_triple = undefined, - - .version_major = 0, - .version_minor = 0, - .version_patch = 0, - - .verbose_tokenize = false, - .verbose_ast_tree = false, - .verbose_ast_fmt = false, - .verbose_cimport = false, - .verbose_ir = false, - .verbose_llvm_ir = false, - .verbose_link = false, - - .linker_script = null, - .out_h_path = null, - .is_test = false, - .each_lib_rpath = false, - .strip = false, .is_static = is_static, - .linker_rdynamic = false, - .clang_argv = [_][]const u8{}, - .lib_dirs = [_][]const u8{}, - .rpath_list = [_][]const u8{}, - .assembly_files = [_][]const u8{}, - .link_objects = [_][]const u8{}, .fn_link_set = event.Locked(FnLinkSet).init(FnLinkSet.init()), - .windows_subsystem_windows = false, - .windows_subsystem_console = false, .link_libs_list = undefined, - .libc_link_lib = null, - .err_color = errmsg.Color.Auto, - .darwin_frameworks = [_][]const u8{}, - .darwin_version_min = DarwinVersionMin.None, - .test_filters = [_][]const u8{}, - .test_name_prefix = null, - .emit_file_type = Emit.Binary, - .link_out_file = null, + .exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)), .prelink_group = event.Group(BuildError!void).init(allocator), .deinit_group = event.Group(void).init(allocator), @@ -458,11 +425,9 @@ pub const Compilation = struct { .root_package = undefined, .std_package = undefined, - .override_libc = null, - .have_err_ret_tracing = false, .primitive_type_table = undefined, - // .fs_watch = undefined, + .fs_watch = undefined, }; comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); comp.primitive_type_table = TypeTable.init(comp.arena()); @@ -534,8 +499,8 @@ pub const Compilation = struct { comp.root_package = try Package.create(comp.arena(), ".", ""); } - // comp.fs_watch = try fs.Watch(*Scope.Root).create(16); - // defer comp.fs_watch.destroy(); + comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16); + defer comp.fs_watch.deinit(); try comp.initTypes(); defer comp.primitive_type_table.deinit(); @@ -559,7 +524,7 @@ pub const Compilation = struct { } /// it does ref the result because it could be an arbitrary integer size - pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { + pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type { if (name.len >= 2) { switch (name[0]) { 'i', 'u' => blk: { @@ -795,47 +760,47 @@ pub const Compilation = struct { self.events.put(Event{ .Error = err }); } - // // First, get an item from the watch channel, waiting on the channel. - // var group = event.Group(BuildError!void).init(self.gpa()); - // { - // const ev = (self.fs_watch.channel.get()) catch |err| { - // build_result = err; - // continue; - // }; - // const root_scope = ev.data; - // group.call(rebuildFile, self, root_scope) catch |err| { - // build_result = err; - // continue; - // }; - // } - // // Next, get all the items from the channel that are buffered up. - // while (self.fs_watch.channel.getOrNull()) |ev_or_err| { - // if (ev_or_err) |ev| { - // const root_scope = ev.data; - // group.call(rebuildFile, self, root_scope) catch |err| { - // build_result = err; - // continue; - // }; - // } else |err| { - // build_result = err; - // continue; - // } - // } - // build_result = group.wait(); + // First, get an item from the watch channel, waiting on the channel. + var group = event.Group(BuildError!void).init(self.gpa()); + { + const ev = (self.fs_watch.channel.get()) catch |err| { + build_result = err; + continue; + }; + const root_scope = ev.data; + group.call(rebuildFile, self, root_scope) catch |err| { + build_result = err; + continue; + }; + } + // Next, get all the items from the channel that are buffered up. + while (self.fs_watch.channel.getOrNull()) |ev_or_err| { + if (ev_or_err) |ev| { + const root_scope = ev.data; + group.call(rebuildFile, self, root_scope) catch |err| { + build_result = err; + continue; + }; + } else |err| { + build_result = err; + continue; + } + } + build_result = group.wait(); } } - async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void { + async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void { const tree_scope = blk: { - const source_code = ""; - // const source_code = fs.readFile( - // root_scope.realpath, - // max_src_size, - // ) catch |err| { - // try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err)); - // return; - // }; - // errdefer self.gpa().free(source_code); + const source_code = fs.readFile( + self.gpa(), + root_scope.realpath, + max_src_size, + ) catch |err| { + try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err)); + return; + }; + errdefer self.gpa().free(source_code); const tree = try std.zig.parse(self.gpa(), source_code); errdefer { @@ -873,7 +838,7 @@ pub const Compilation = struct { try decl_group.wait(); } - async fn rebuildChangedDecls( + fn rebuildChangedDecls( self: *Compilation, group: *event.Group(BuildError!void), locked_table: *Decl.Table, @@ -962,7 +927,7 @@ pub const Compilation = struct { } } - async fn initialCompile(self: *Compilation) !void { + fn initialCompile(self: *Compilation) !void { if (self.root_src_path) |root_src_path| { const root_scope = blk: { // TODO async/await std.fs.realpath @@ -981,7 +946,7 @@ pub const Compilation = struct { } } - async fn maybeLink(self: *Compilation) !void { + fn maybeLink(self: *Compilation) !void { (self.prelink_group.wait()) catch |err| switch (err) { error.SemanticAnalysisFailed => {}, else => return err, @@ -1184,7 +1149,7 @@ pub const Compilation = struct { /// If the temporary directory for this compilation has not been created, it creates it. /// Then it creates a random file name in that dir and returns it. - pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { + pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { const tmp_dir = try self.getTmpDir(); const file_prefix = self.getRandomFileName(); @@ -1200,14 +1165,14 @@ pub const Compilation = struct { /// If the temporary directory for this Compilation has not been created, creates it. /// Then returns it. The directory is unique to this Compilation and cleaned up when /// the Compilation deinitializes. - async fn getTmpDir(self: *Compilation) ![]const u8 { + fn getTmpDir(self: *Compilation) ![]const u8 { if (self.tmp_dir.start()) |ptr| return ptr.*; self.tmp_dir.data = self.getTmpDirImpl(); self.tmp_dir.resolve(); return self.tmp_dir.data; } - async fn getTmpDirImpl(self: *Compilation) ![]u8 { + fn getTmpDirImpl(self: *Compilation) ![]u8 { const comp_dir_name = self.getRandomFileName(); const zig_dir_path = try getZigDir(self.gpa()); defer self.gpa().free(zig_dir_path); @@ -1217,7 +1182,7 @@ pub const Compilation = struct { return tmp_dir; } - async fn getRandomFileName(self: *Compilation) [12]u8 { + fn getRandomFileName(self: *Compilation) [12]u8 { // here we replace the standard +/ with -_ so that it can be used in a file name const b64_fs_encoder = std.base64.Base64Encoder.init( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", @@ -1243,7 +1208,7 @@ pub const Compilation = struct { } /// Returns a value which has been ref()'d once - async fn analyzeConstValue( + fn analyzeConstValue( comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, @@ -1256,7 +1221,7 @@ pub const Compilation = struct { return analyzed_code.getCompTimeResult(comp); } - async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type { + fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type { const meta_type = &Type.MetaType.get(comp).base; defer meta_type.base.deref(comp); @@ -1287,7 +1252,7 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib } /// The function that actually does the generation. -async fn generateDecl(comp: *Compilation, decl: *Decl) !void { +fn generateDecl(comp: *Compilation, decl: *Decl) !void { switch (decl.id) { .Var => @panic("TODO"), .Fn => { @@ -1298,7 +1263,7 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void { } } -async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { +fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const tree_scope = fn_decl.base.tree_scope; const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl); @@ -1315,7 +1280,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { // The Decl.Fn owns the initial 1 reference count const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); - fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; + fn_decl.value = .{ .Fn = fn_val }; symbol_name_consumed = true; // Define local parameter variables @@ -1382,7 +1347,7 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 { return std.fs.getAppDataDir(allocator, "zig"); } -async fn analyzeFnType( +fn analyzeFnType( comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, @@ -1444,7 +1409,7 @@ async fn analyzeFnType( return fn_type; } -async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { +fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_type = try analyzeFnType( comp, fn_decl.base.tree_scope, @@ -1459,6 +1424,6 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { // The Decl.Fn owns the initial 1 reference count const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name); - fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val }; + fn_decl.value = .{ .FnProto = fn_proto_val }; symbol_name_consumed = true; } diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index 21a99729f5..e68a1458d6 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -69,15 +69,12 @@ pub const Decl = struct { pub const Fn = struct { base: Decl, - value: Val, - fn_proto: *ast.Node.FnProto, - - // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous - pub const Val = union(enum) { + value: union(enum) { Unresolved, Fn: *Value.Fn, FnProto: *Value.FnProto, - }; + }, + fn_proto: *ast.Node.FnProto, pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 { return if (self.fn_proto.extern_export_inline_token) |tok_index| x: { diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 905d218114..4cab9a4ce4 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -110,7 +110,7 @@ pub const Inst = struct { unreachable; } - pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { + pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst { switch (base.id) { .Return => return @fieldParentPtr(Return, "base", base).analyze(ira), .Const => return @fieldParentPtr(Const, "base", base).analyze(ira), @@ -422,7 +422,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst { + pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); if (ira.getCompTimeValOrNullUndefOk(target)) |val| { @@ -472,7 +472,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst { + pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst { (ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => return error.SemanticAnalysisFailed, @@ -516,7 +516,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { + pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { switch (self.params.var_scope.data) { .Const => @panic("TODO"), .Param => |param| { @@ -563,7 +563,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst { + pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst { const target = try self.params.target.getAsParam(); const target_type = target.getKnownType(); if (target_type.id != .Pointer) { @@ -645,7 +645,7 @@ pub const Inst = struct { return false; } - pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { + pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst { const child_type = try self.params.child_type.getAsConstType(ira); // if (child_type->id == TypeTableEntryIdUnreachable) { // ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed")); @@ -927,7 +927,7 @@ pub const Variable = struct { pub const BasicBlock = struct { ref_count: usize, - name_hint: [*]const u8, // must be a C string literal + name_hint: [*:0]const u8, debug_id: usize, scope: *Scope, instruction_list: std.ArrayList(*Inst), @@ -1051,7 +1051,7 @@ pub const Builder = struct { } /// No need to clean up resources thanks to the arena allocator. - pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock { + pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock { const basic_block = try self.arena().create(BasicBlock); basic_block.* = BasicBlock{ .ref_count = 0, @@ -1186,6 +1186,7 @@ pub const Builder = struct { } } + fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { const fn_ref = try irb.genNode(suffix_op.lhs, scope, .None); @@ -1214,7 +1215,7 @@ pub const Builder = struct { //return ir_lval_wrap(irb, scope, fn_call, lval); } - async fn genPtrType( + fn genPtrType( irb: *Builder, prefix_op: *ast.Node.PrefixOp, ptr_info: ast.Node.PrefixOp.PtrInfo, @@ -1307,9 +1308,9 @@ pub const Builder = struct { var rest: []const u8 = undefined; if (int_token.len >= 3 and int_token[0] == '0') { base = switch (int_token[1]) { - 'b' => u8(2), - 'o' => u8(8), - 'x' => u8(16), + 'b' => 2, + 'o' => 8, + 'x' => 16, else => unreachable, }; rest = int_token[2..]; @@ -1339,7 +1340,7 @@ pub const Builder = struct { return inst; } - pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst { + pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst { const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token); const src_span = Span.token(str_lit.token); @@ -1389,7 +1390,7 @@ pub const Builder = struct { } } - pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { + pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst { const block_scope = try Scope.Block.create(irb.comp, parent_scope); const outer_block_scope = &block_scope.base; @@ -1499,7 +1500,7 @@ pub const Builder = struct { return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true); } - pub async fn genControlFlowExpr( + pub fn genControlFlowExpr( irb: *Builder, control_flow_expr: *ast.Node.ControlFlowExpression, scope: *Scope, @@ -1596,7 +1597,7 @@ pub const Builder = struct { } } - pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { + pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst { const src_span = Span.token(identifier.token); const name = irb.code.tree_scope.tree.tokenSlice(identifier.token); @@ -1694,7 +1695,7 @@ pub const Builder = struct { return result; } - async fn genDefersForBlock( + fn genDefersForBlock( irb: *Builder, inner_scope: *Scope, outer_scope: *Scope, @@ -1797,7 +1798,7 @@ pub const Builder = struct { // Look at the params and ref() other instructions comptime var i = 0; inline while (i < @memberCount(I.Params)) : (i += 1) { - const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i))); + const FieldType = comptime @typeOf(@field(@as(I.Params, undefined), @memberName(I.Params, i))); switch (FieldType) { *Inst => @field(inst.params, @memberName(I.Params, i)).ref(self), *BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self), @@ -1909,7 +1910,7 @@ pub const Builder = struct { VarScope: *Scope.Var, }; - async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident { + fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident { var s = scope; while (true) { switch (s.id) { @@ -2519,7 +2520,7 @@ const Analyze = struct { } }; -pub async fn gen( +pub fn gen( comp: *Compilation, body_node: *ast.Node, tree_scope: *Scope.AstTree, @@ -2541,7 +2542,7 @@ pub async fn gen( return irb.finish(); } -pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { +pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code { const old_entry_bb = old_code.basic_block_list.at(0); var ira = try Analyze.init(comp, old_code.tree_scope, expected_type); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 42e7de5d08..3d121ecf69 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -143,7 +143,7 @@ pub const LibCInstallation = struct { } /// Finds the default, native libc. - pub async fn findNative(self: *LibCInstallation, allocator: *Allocator) !void { + pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void { self.initEmpty(); var group = event.Group(FindError!void).init(allocator); errdefer group.wait() catch {}; @@ -393,7 +393,7 @@ pub const LibCInstallation = struct { }; /// caller owns returned memory -async fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 { +fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 { const cc_exe = std.os.getenv("CC") orelse "cc"; const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", o_file); defer allocator.free(arg1); diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index aafe05eb8b..f5ccb5a0dc 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -11,7 +11,7 @@ const util = @import("util.zig"); const Context = struct { comp: *Compilation, arena: std.heap.ArenaAllocator, - args: std.ArrayList([*]const u8), + args: std.ArrayList([*:0]const u8), link_in_crt: bool, link_err: error{OutOfMemory}!void, @@ -21,7 +21,7 @@ const Context = struct { out_file_path: std.Buffer, }; -pub async fn link(comp: *Compilation) !void { +pub fn link(comp: *Compilation) !void { var ctx = Context{ .comp = comp, .arena = std.heap.ArenaAllocator.init(comp.gpa()), @@ -33,7 +33,7 @@ pub async fn link(comp: *Compilation) !void { .out_file_path = undefined, }; defer ctx.arena.deinit(); - ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator); + ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator); ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator); if (comp.link_out_file) |out_file| { @@ -171,7 +171,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { //} try ctx.args.append("-o"); - try ctx.args.append(ctx.out_file_path.ptr()); + try ctx.args.append(ctx.out_file_path.toSliceConst()); if (ctx.link_in_crt) { const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o"; @@ -214,10 +214,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void { if (ctx.comp.haveLibC()) { try ctx.args.append("-L"); - try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr); + // TODO addNullByte should probably return [:0]u8 + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr)); try ctx.args.append("-L"); - try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr)); if (!ctx.comp.is_static) { const dl = blk: { @@ -226,7 +227,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { return error.LibCMissingDynamicLinker; }; try ctx.args.append("-dynamic-linker"); - try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr)); } } @@ -238,7 +239,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { // .o files for (ctx.comp.link_objects) |link_object| { const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); - try ctx.args.append(link_obj_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr)); } try addFnObjects(ctx); @@ -313,7 +314,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void { fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void { const full_path = try std.fs.path.join(&ctx.arena.allocator, [_][]const u8{ dirname, basename }); const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path); - try ctx.args.append(full_path_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr)); } fn constructLinkerArgsCoff(ctx: *Context) !void { @@ -339,12 +340,12 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { const is_library = ctx.comp.kind == .Lib; const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst()); - try ctx.args.append(out_arg.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr)); if (ctx.comp.haveLibC()) { - try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr); - try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr); - try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr)); + try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr)); } if (ctx.link_in_crt) { @@ -353,17 +354,17 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { if (ctx.comp.is_static) { const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str); - try ctx.args.append(cmt_lib_name.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr)); } else { const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str); - try ctx.args.append(msvcrt_lib_name.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr)); } const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str); - try ctx.args.append(vcruntime_lib_name.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr)); const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str); - try ctx.args.append(crt_lib_name.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr)); // Visual C++ 2015 Conformance Changes // https://msdn.microsoft.com/en-us/library/bb531344.aspx @@ -395,7 +396,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void { for (ctx.comp.link_objects) |link_object| { const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); - try ctx.args.append(link_obj_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr)); } try addFnObjects(ctx); @@ -504,11 +505,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { //} try ctx.args.append("-arch"); - const darwin_arch_str = try std.cstr.addNullByte( - &ctx.arena.allocator, - ctx.comp.target.getDarwinArchString(), - ); - try ctx.args.append(darwin_arch_str.ptr); + try ctx.args.append(util.getDarwinArchString(ctx.comp.target)); const platform = try DarwinPlatform.get(ctx.comp); switch (platform.kind) { @@ -517,7 +514,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { .IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"), } const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro); - try ctx.args.append(ver_str.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr)); if (ctx.comp.kind == .Exe) { if (ctx.comp.is_static) { @@ -528,7 +525,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { } try ctx.args.append("-o"); - try ctx.args.append(ctx.out_file_path.ptr()); + try ctx.args.append(ctx.out_file_path.toSliceConst()); //for (size_t i = 0; i < g->rpath_list.length; i += 1) { // Buf *rpath = g->rpath_list.at(i); @@ -572,7 +569,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { for (ctx.comp.link_objects) |link_object| { const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object); - try ctx.args.append(link_obj_with_null.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr)); } try addFnObjects(ctx); @@ -593,10 +590,10 @@ fn constructLinkerArgsMachO(ctx: *Context) !void { } else { if (mem.indexOfScalar(u8, lib.name, '/') == null) { const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name); - try ctx.args.append(arg.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); } else { const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name); - try ctx.args.append(arg.ptr); + try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr)); } } } @@ -639,7 +636,7 @@ fn addFnObjects(ctx: *Context) !void { ctx.comp.gpa().destroy(node); continue; }; - try ctx.args.append(fn_val.containing_object.ptr()); + try ctx.args.append(fn_val.containing_object.toSliceConst()); it = node.next; } } diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index af42adb97d..476637b2c2 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -86,7 +86,7 @@ pub const AddGlobal = LLVMAddGlobal; extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value; pub const ConstStringInContext = LLVMConstStringInContext; -extern fn LLVMConstStringInContext(C: *Context, Str: [*:0]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value; +extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value; pub const ConstInt = LLVMConstInt; extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index bf3fb4dea5..934c7ab772 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -126,7 +126,8 @@ pub fn main() !void { try stderr.print("unknown command: {}\n\n", args[1]); try stderr.write(usage); - process.exit(1); + process.argsFree(allocator, args); + defer process.exit(1); } const usage_build_generic = @@ -461,13 +462,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.link_objects = link_objects; comp.start(); - const frame = async processBuildEvents(comp, color); + processBuildEvents(comp, color); } -async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { +fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { var count: usize = 0; - while (true) { - // TODO directly awaiting async should guarantee memory allocation elision + while (true) { // TODO(Vexu) const build_event = comp.events.get(); count += 1; @@ -567,10 +567,6 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { var zig_compiler = try ZigCompiler.init(allocator); defer zig_compiler.deinit(); - const frame = async findLibCAsync(&zig_compiler); -} - -async fn findLibCAsync(zig_compiler: *ZigCompiler) void { const libc = zig_compiler.getNativeLibC() catch |err| { stderr.print("unable to find libc: {}\n", @errorName(err)) catch process.exit(1); process.exit(1); @@ -644,11 +640,23 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void { process.exit(1); } - return asyncFmtMain( - allocator, - &flags, - color, - ); + var fmt = Fmt{ + .allocator = allocator, + .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)), + .any_error = false, + .color = color, + }; + + const check_mode = flags.present("check"); + + var group = event.Group(FmtError!void).init(allocator); + for (flags.positionals.toSliceConst()) |file_path| { + try group.call(fmtPath, &fmt, file_path, check_mode); + } + try group.wait(); + if (fmt.any_error) { + process.exit(1); + } } const FmtError = error{ @@ -673,30 +681,6 @@ const FmtError = error{ CurrentWorkingDirectoryUnlinked, } || fs.File.OpenError; -async fn asyncFmtMain( - allocator: *Allocator, - flags: *const Args, - color: errmsg.Color, -) FmtError!void { - var fmt = Fmt{ - .allocator = allocator, - .seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)), - .any_error = false, - .color = color, - }; - - const check_mode = flags.present("check"); - - var group = event.Group(FmtError!void).init(allocator); - for (flags.positionals.toSliceConst()) |file_path| { - try group.call(fmtPath, &fmt, file_path, check_mode); - } - try group.wait(); - if (fmt.any_error) { - process.exit(1); - } -} - async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void { const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref); defer fmt.allocator.free(file_path); @@ -708,33 +692,33 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro if (try held.value.put(file_path, {})) |_| return; } - const source_code = ""; - // const source_code = event.fs.readFile( - // file_path, - // max_src_size, - // ) catch |err| switch (err) { - // error.IsDir, error.AccessDenied => { - // // TODO make event based (and dir.next()) - // var dir = try fs.Dir.cwd().openDirList(file_path); - // defer dir.close(); + const source_code = event.fs.readFile( + fmt.allocator, + file_path, + max_src_size, + ) catch |err| switch (err) { + error.IsDir, error.AccessDenied => { + var dir = try fs.Dir.cwd().openDirList(file_path); + defer dir.close(); - // var group = event.Group(FmtError!void).init(fmt.allocator); - // while (try dir.next()) |entry| { - // if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) { - // const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name }); - // try group.call(fmtPath, fmt, full_path, check_mode); - // } - // } - // return group.wait(); - // }, - // else => { - // // TODO lock stderr printing - // try stderr.print("unable to open '{}': {}\n", file_path, err); - // fmt.any_error = true; - // return; - // }, - // }; - // defer fmt.allocator.free(source_code); + var group = event.Group(FmtError!void).init(fmt.allocator); + var it = dir.iterate(); + while (try it.next()) |entry| { + if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name }); + try group.call(fmtPath, fmt, full_path, check_mode); + } + } + return group.wait(); + }, + else => { + // TODO lock stderr printing + try stderr.print("unable to open '{}': {}\n", file_path, err); + fmt.any_error = true; + return; + }, + }; + defer fmt.allocator.free(source_code); const tree = std.zig.parse(fmt.allocator, source_code) catch |err| { try stderr.print("error parsing file '{}': {}\n", file_path, err); diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 85b9d6b912..4c1ed6ad45 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -116,7 +116,7 @@ pub const TestContext = struct { const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr()); const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); - const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{.Native = {}}).exeFileExt()); + const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{ .Native = {} }).exeFileExt()); if (std.fs.path.dirname(file1_path)) |dirname| { try std.fs.makePath(allocator, dirname); } @@ -148,15 +148,12 @@ pub const TestContext = struct { exe_file: []const u8, expected_output: []const u8, ) anyerror!void { - // TODO this should not be necessary - const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file); - defer comp.destroy(); const build_event = comp.events.get(); switch (build_event) { .Ok => { - const argv = [_][]const u8{exe_file_2}; + const argv = [_][]const u8{exe_file}; // TODO use event loop const child = try std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024); switch (child.term) { @@ -173,8 +170,8 @@ pub const TestContext = struct { return error.OutputMismatch; } }, - Compilation.Event.Error => |err| return err, - Compilation.Event.Fail => |msgs| { + .Error => |err| return err, + .Fail => |msgs| { const stderr = std.io.getStdErr(); try stderr.write("build incorrectly failed:\n"); for (msgs) |msg| { diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 3bf39d9085..2fce36aec2 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -53,7 +53,7 @@ pub const Type = struct { base: *Type, allocator: *Allocator, llvm_context: *llvm.Context, - ) (error{OutOfMemory}!*llvm.Type) { + ) error{OutOfMemory}!*llvm.Type { switch (base.id) { .Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context), .Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context), @@ -184,7 +184,7 @@ pub const Type = struct { /// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead. /// Otherwise, this one will grab one from the pool and then release it. - pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 { + pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 { if (base.abi_alignment.start()) |ptr| return ptr.*; { @@ -200,7 +200,7 @@ pub const Type = struct { } /// If you have an llvm conext handy, you can use it here. - pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { + pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { if (base.abi_alignment.start()) |ptr| return ptr.*; base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context); @@ -209,7 +209,7 @@ pub const Type = struct { } /// Lower level function that does the work. See getAbiAlignment. - async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { + fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 { const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context); return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type)); } @@ -367,7 +367,7 @@ pub const Type = struct { } /// takes ownership of key.Normal.params on success - pub async fn get(comp: *Compilation, key: Key) !*Fn { + pub fn get(comp: *Compilation, key: Key) !*Fn { { const held = comp.fn_type_table.acquire(); defer held.release(); @@ -564,7 +564,7 @@ pub const Type = struct { return comp.u8_type; } - pub async fn get(comp: *Compilation, key: Key) !*Int { + pub fn get(comp: *Compilation, key: Key) !*Int { { const held = comp.int_type_table.acquire(); defer held.release(); @@ -606,7 +606,7 @@ pub const Type = struct { comp.registerGarbage(Int, &self.garbage_node); } - pub async fn gcDestroy(self: *Int, comp: *Compilation) void { + pub fn gcDestroy(self: *Int, comp: *Compilation) void { { const held = comp.int_type_table.acquire(); defer held.release(); @@ -700,7 +700,7 @@ pub const Type = struct { comp.registerGarbage(Pointer, &self.garbage_node); } - pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void { + pub fn gcDestroy(self: *Pointer, comp: *Compilation) void { { const held = comp.ptr_type_table.acquire(); defer held.release(); @@ -711,14 +711,14 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 { + pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 { switch (self.key.alignment) { .Abi => return self.key.child_type.getAbiAlignment(comp), .Override => |alignment| return alignment, } } - pub async fn get( + pub fn get( comp: *Compilation, key: Key, ) !*Pointer { @@ -828,7 +828,7 @@ pub const Type = struct { comp.gpa().destroy(self); } - pub async fn get(comp: *Compilation, key: Key) !*Array { + pub fn get(comp: *Compilation, key: Key) !*Array { key.elem_type.base.ref(); errdefer key.elem_type.base.deref(comp); diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig index f1d892411f..4cd54cdc29 100644 --- a/src-self-hosted/util.zig +++ b/src-self-hosted/util.zig @@ -32,21 +32,21 @@ pub fn getFloatAbi(self: Target) FloatAbi { }; } -pub fn getObjectFormat(self: Target) Target.ObjectFormat { - return switch (self) { - .Native => @import("builtin").object_format, - .Cross => { +pub fn getObjectFormat(target: Target) Target.ObjectFormat { + switch (target) { + .Native => return @import("builtin").object_format, + .Cross => blk: { if (target.isWindows() or target.isUefi()) { - break .coff; + return .coff; } else if (target.isDarwin()) { - break .macho; + return .macho; } if (target.isWasm()) { - break .wasm; + return .wasm; } - break .elf; + return .elf; }, - }; + } } pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { @@ -156,7 +156,7 @@ pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { } } -pub fn getDarwinArchString(self: Target) []const u8 { +pub fn getDarwinArchString(self: Target) [:0]const u8 { const arch = self.getArch(); switch (arch) { .aarch64 => return "arm64", @@ -166,7 +166,8 @@ pub fn getDarwinArchString(self: Target) []const u8 { .powerpc => return "ppc", .powerpc64 => return "ppc64", .powerpc64le => return "ppc64le", - else => return @tagName(arch), + // @tagName should be able to return sentinel terminated slice + else => @panic("TODO"), //return @tagName(arch), } } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 0458bac12a..b4dc757af6 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -156,7 +156,7 @@ pub const Value = struct { const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, - self.symbol_name.ptr(), + self.symbol_name.toSliceConst(), llvm_fn_type, ) orelse return error.OutOfMemory; @@ -241,7 +241,7 @@ pub const Value = struct { const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); const llvm_fn = llvm.AddFunction( ofile.module, - self.symbol_name.ptr(), + self.symbol_name.toSliceConst(), llvm_fn_type, ) orelse return error.OutOfMemory; @@ -334,7 +334,7 @@ pub const Value = struct { field_index: usize, }; - pub async fn createArrayElemPtr( + pub fn createArrayElemPtr( comp: *Compilation, array_val: *Array, mut: Type.Pointer.Mut, @@ -390,13 +390,13 @@ pub const Value = struct { const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?; const ptr_bit_count = ofile.comp.target_ptr_bits; const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory; - const indices = [_]*llvm.Value{ + var indices = [_]*llvm.Value{ llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory, llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory, }; return llvm.ConstInBoundsGEP( array_llvm_value, - &indices, + @ptrCast([*]*llvm.Value, &indices), @intCast(c_uint, indices.len), ) orelse return error.OutOfMemory; }, @@ -423,7 +423,7 @@ pub const Value = struct { }; /// Takes ownership of buffer - pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array { + pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array { const u8_type = Type.Int.get_u8(comp); defer u8_type.base.base.deref(comp); From 128034481ab6eb02da4114b4d49ec3cfd4f8451c Mon Sep 17 00:00:00 2001 From: Vexu <15308111+Vexu@users.noreply.github.com> Date: Tue, 26 Nov 2019 10:34:38 +0200 Subject: [PATCH 11/38] solve recursion in self hosted --- lib/std/event/fs.zig | 26 +++++++------- src-self-hosted/compilation.zig | 52 ++++++++++++++++----------- src-self-hosted/ir.zig | 25 ++++++++----- src-self-hosted/libc_installation.zig | 2 +- src-self-hosted/link.zig | 9 +++-- src-self-hosted/main.zig | 11 +++--- src-self-hosted/util.zig | 2 +- src-self-hosted/value.zig | 2 +- 8 files changed, 75 insertions(+), 54 deletions(-) diff --git a/lib/std/event/fs.zig b/lib/std/event/fs.zig index 0bbc710dfc..5d55805e35 100644 --- a/lib/std/event/fs.zig +++ b/lib/std/event/fs.zig @@ -735,24 +735,26 @@ pub fn Watch(comptime V: type) type { allocator: *Allocator, const OsData = switch (builtin.os) { - .macosx, .freebsd, .netbsd, .dragonfly => struct { - file_table: FileTable, - table_lock: event.Lock, - - const FileTable = std.StringHashMap(*Put); - const Put = struct { - putter_frame: @Frame(kqPutEvents), - cancelled: bool = false, - value: V, - }; - }, - + // TODO https://github.com/ziglang/zig/issues/3778 + .macosx, .freebsd, .netbsd, .dragonfly => KqOsData, .linux => LinuxOsData, .windows => WindowsOsData, else => @compileError("Unsupported OS"), }; + const KqOsData = struct { + file_table: FileTable, + table_lock: event.Lock, + + const FileTable = std.StringHashMap(*Put); + const Put = struct { + putter_frame: @Frame(kqPutEvents), + cancelled: bool = false, + value: V, + }; + }; + const WindowsOsData = struct { table_lock: event.Lock, dir_table: DirTable, diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index c8da540325..259cfd602f 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -133,7 +133,7 @@ pub const Compilation = struct { zig_std_dir: []const u8, /// lazily created when we need it - tmp_dir: event.Future(BuildError![]u8), + tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(), version_major: u32 = 0, version_minor: u32 = 0, @@ -158,7 +158,7 @@ pub const Compilation = struct { /// functions that have their own objects that we need to link /// it uses an optional pointer so that tombstone removals are possible - fn_link_set: event.Locked(FnLinkSet), + fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()), pub const FnLinkSet = std.TailQueue(?*Value.Fn); @@ -227,9 +227,9 @@ pub const Compilation = struct { /// need to wait on this group before deinitializing deinit_group: event.Group(void), - // destroy_frame: @Frame(createAsync), - // main_loop_frame: @Frame(Compilation.mainLoop), - main_loop_future: event.Future(void), + destroy_frame: *@Frame(createAsync), + main_loop_frame: *@Frame(Compilation.mainLoop), + main_loop_future: event.Future(void) = event.Future(void).init(), have_err_ret_tracing: bool = false, @@ -245,6 +245,8 @@ pub const Compilation = struct { fs_watch: *fs.Watch(*Scope.Root), + cancelled: bool = false, + const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); @@ -348,7 +350,9 @@ pub const Compilation = struct { zig_lib_dir: []const u8, ) !*Compilation { var optional_comp: ?*Compilation = null; - var frame = async createAsync( + var frame = try zig_compiler.allocator.create(@Frame(createAsync)); + errdefer zig_compiler.allocator.destroy(frame); + frame.* = async createAsync( &optional_comp, zig_compiler, name, @@ -385,15 +389,12 @@ pub const Compilation = struct { .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, .zig_std_dir = undefined, - .tmp_dir = event.Future(BuildError![]u8).init(), - // .destroy_frame = @frame(), - // .main_loop_frame = undefined, - .main_loop_future = event.Future(void).init(), + .destroy_frame = @frame(), + .main_loop_frame = undefined, .name = undefined, .llvm_triple = undefined, .is_static = is_static, - .fn_link_set = event.Locked(FnLinkSet).init(FnLinkSet.init()), .link_libs_list = undefined, .exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)), @@ -505,7 +506,10 @@ pub const Compilation = struct { try comp.initTypes(); defer comp.primitive_type_table.deinit(); - // comp.main_loop_frame = async comp.mainLoop(); + comp.main_loop_frame = try allocator.create(@Frame(mainLoop)); + defer allocator.destroy(comp.main_loop_frame); + + comp.main_loop_frame.* = async comp.mainLoop(); // Set this to indicate that initialization completed successfully. // from here on out we must not return an error. // This must occur before the first suspend/await. @@ -718,8 +722,11 @@ pub const Compilation = struct { } pub fn destroy(self: *Compilation) void { - // await self.main_loop_frame; - // resume self.destroy_frame; + const allocator = self.gpa(); + self.cancelled = true; + await self.main_loop_frame; + resume self.destroy_frame; + allocator.destroy(self.destroy_frame); } fn start(self: *Compilation) void { @@ -732,7 +739,7 @@ pub const Compilation = struct { var build_result = self.initialCompile(); - while (true) { + while (!self.cancelled) { const link_result = if (build_result) blk: { break :blk self.maybeLink(); } else |err| err; @@ -1130,11 +1137,10 @@ pub const Compilation = struct { return link_lib; } - /// cancels itself so no need to await or cancel the promise. async fn startFindingNativeLibC(self: *Compilation) void { - std.event.Loop.instance.?.yield(); + event.Loop.startCpuBoundOperation(); // we don't care if it fails, we're just trying to kick off the future resolution - _ = (self.zig_compiler.getNativeLibC()) catch return; + _ = self.zig_compiler.getNativeLibC() catch return; } /// General Purpose Allocator. Must free when done. @@ -1215,7 +1221,10 @@ pub const Compilation = struct { node: *ast.Node, expected_type: *Type, ) !*Value { - const analyzed_code = try comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type); + var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode)); + defer comp.gpa().destroy(frame); + frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type); + const analyzed_code = try await frame; defer analyzed_code.destroy(comp.gpa()); return analyzed_code.getCompTimeResult(comp); @@ -1315,12 +1324,15 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { try fn_type.non_key.Normal.variable_list.append(var_scope); } - const analyzed_code = try comp.genAndAnalyzeCode( + var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode)); + defer comp.gpa().destroy(frame); + frame.* = async comp.genAndAnalyzeCode( tree_scope, fn_val.child_scope, body_node, fn_type.key.data.Normal.return_type, ); + const analyzed_code = try await frame; errdefer analyzed_code.destroy(comp.gpa()); assert(fn_val.block_scope != null); diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 4cab9a4ce4..8d1c32cefd 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -658,7 +658,7 @@ pub const Inst = struct { const amt = try align_inst.getAsConstAlign(ira); break :blk Type.Pointer.Align{ .Override = amt }; } else blk: { - break :blk Type.Pointer.Align{ .Abi = {} }; + break :blk .Abi; }; const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ .child_type = child_type, @@ -1078,6 +1078,14 @@ pub const Builder = struct { self.current_basic_block = basic_block; } + pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { + const alloc = irb.comp.gpa(); + var frame = try alloc.create(@Frame(genNode)); + defer alloc.destroy(frame); + frame.* = async irb.genNode(node, scope, lval); + return await frame; + } + pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst { switch (node.id) { .Root => unreachable, @@ -1157,7 +1165,7 @@ pub const Builder = struct { }, .GroupedExpression => { const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node); - return irb.genNode(grouped_expr.expr, scope, lval); + return irb.genNodeRecursive(grouped_expr.expr, scope, lval); }, .BuiltinCall => return error.Unimplemented, .ErrorSetDecl => return error.Unimplemented, @@ -1187,14 +1195,13 @@ pub const Builder = struct { } fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { - async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst { - const fn_ref = try irb.genNode(suffix_op.lhs, scope, .None); + const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None); const args = try irb.arena().alloc(*Inst, call.params.len); var it = call.params.iterator(0); var i: usize = 0; while (it.next()) |arg_node_ptr| : (i += 1) { - args[i] = try irb.genNode(arg_node_ptr.*, scope, .None); + args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None); } //bool is_async = node->data.fn_call_expr.is_async; @@ -1239,7 +1246,7 @@ pub const Builder = struct { //} else { // align_value = nullptr; //} - const child_type = try irb.genNode(prefix_op.rhs, scope, .None); + const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None); //uint32_t bit_offset_start = 0; //if (node->data.pointer_type.bit_offset_start != nullptr) { @@ -1438,7 +1445,7 @@ pub const Builder = struct { child_scope = &defer_child_scope.base; continue; } - const statement_value = try irb.genNode(statement_node, child_scope, .None); + const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None); is_continuation_unreachable = statement_value.isNoReturn(); if (is_continuation_unreachable) { @@ -1534,7 +1541,7 @@ pub const Builder = struct { const outer_scope = irb.begin_scope.?; const return_value = if (control_flow_expr.rhs) |rhs| blk: { - break :blk try irb.genNode(rhs, scope, .None); + break :blk try irb.genNodeRecursive(rhs, scope, .None); } else blk: { break :blk try irb.buildConstVoid(scope, src_span, true); }; @@ -1713,7 +1720,7 @@ pub const Builder = struct { }; if (generate) { const defer_expr_scope = defer_scope.defer_expr_scope; - const instruction = try irb.genNode( + const instruction = try irb.genNodeRecursive( defer_expr_scope.expr_node, &defer_expr_scope.base, .None, diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 3d121ecf69..71cec00eba 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -400,7 +400,7 @@ fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool const argv = [_][]const u8{ cc_exe, arg1 }; // TODO This simulates evented I/O for the child process exec - std.event.Loop.instance.?.yield(); + event.Loop.startCpuBoundOperation(); const errorable_result = std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024); const exec_result = if (std.debug.runtime_safety) blk: { break :blk errorable_result catch unreachable; diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index f5ccb5a0dc..54835f9a02 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -623,16 +623,15 @@ fn constructLinkerArgsWasm(ctx: *Context) void { } fn addFnObjects(ctx: *Context) !void { - // at this point it's guaranteed nobody else has this lock, so we circumvent it - // and avoid having to be an async function - const fn_link_set = &ctx.comp.fn_link_set.private_data; + const held = ctx.comp.fn_link_set.acquire(); + defer held.release(); - var it = fn_link_set.first; + var it = held.value.first; while (it) |node| { const fn_val = node.data orelse { // handle the tombstone. See Value.Fn.destroy. it = node.next; - fn_link_set.remove(node); + held.value.remove(node); ctx.comp.gpa().destroy(node); continue; }; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 934c7ab772..81b3055a54 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -127,7 +127,7 @@ pub fn main() !void { try stderr.print("unknown command: {}\n\n", args[1]); try stderr.write(usage); process.argsFree(allocator, args); - defer process.exit(1); + process.exit(1); } const usage_build_generic = @@ -467,7 +467,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void { var count: usize = 0; - while (true) { // TODO(Vexu) + while (!comp.cancelled) { const build_event = comp.events.get(); count += 1; @@ -545,7 +545,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil "Try running `zig libc` to see an example for the native target.\n", libc_paths_file, @errorName(err), - ) catch process.exit(1); + ) catch {}; process.exit(1); }; } @@ -568,7 +568,7 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void { defer zig_compiler.deinit(); const libc = zig_compiler.getNativeLibC() catch |err| { - stderr.print("unable to find libc: {}\n", @errorName(err)) catch process.exit(1); + stderr.print("unable to find libc: {}\n", @errorName(err)) catch {}; process.exit(1); }; libc.render(stdout) catch process.exit(1); @@ -706,7 +706,8 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro while (try it.next()) |entry| { if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name }); - try group.call(fmtPath, fmt, full_path, check_mode); + @panic("TODO https://github.com/ziglang/zig/issues/3777"); + // try group.call(fmtPath, fmt, full_path, check_mode); } } return group.wait(); diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig index 4cd54cdc29..070ba9ba87 100644 --- a/src-self-hosted/util.zig +++ b/src-self-hosted/util.zig @@ -167,7 +167,7 @@ pub fn getDarwinArchString(self: Target) [:0]const u8 { .powerpc64 => return "ppc64", .powerpc64le => return "ppc64le", // @tagName should be able to return sentinel terminated slice - else => @panic("TODO"), //return @tagName(arch), + else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch), } } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index b4dc757af6..accd70d9cc 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -350,7 +350,7 @@ pub const Value = struct { .mut = mut, .vol = Type.Pointer.Vol.Non, .size = size, - .alignment = Type.Pointer.Align.Abi, + .alignment = .Abi, }); var ptr_type_consumed = false; errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp); From 798d05dd02a1c350c1ae48b25b84c0b88db9f449 Mon Sep 17 00:00:00 2001 From: Vexu <15308111+Vexu@users.noreply.github.com> Date: Tue, 26 Nov 2019 21:36:57 +0200 Subject: [PATCH 12/38] add workaround for #3190 --- src-self-hosted/link.zig | 4 +++- src-self-hosted/type.zig | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src-self-hosted/link.zig b/src-self-hosted/link.zig index 54835f9a02..67490b4e1f 100644 --- a/src-self-hosted/link.zig +++ b/src-self-hosted/link.zig @@ -58,7 +58,8 @@ pub fn link(comp: *Compilation) !void { try ctx.args.append("lld"); if (comp.haveLibC()) { - ctx.libc = ctx.comp.override_libc orelse blk: { + // TODO https://github.com/ziglang/zig/issues/3190 + var libc = ctx.comp.override_libc orelse blk: { switch (comp.target) { Target.Native => { break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound; @@ -66,6 +67,7 @@ pub fn link(comp: *Compilation) !void { else => return error.LibCRequiredButNotProvidedOrFound, } }; + ctx.libc = libc; } try constructLinkerArgs(&ctx); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 2fce36aec2..8b068150b6 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -726,8 +726,10 @@ pub const Type = struct { switch (key.alignment) { .Abi => {}, .Override => |alignment| { + // TODO https://github.com/ziglang/zig/issues/3190 + var align_spill = alignment; const abi_align = try key.child_type.getAbiAlignment(comp); - if (abi_align == alignment) { + if (abi_align == align_spill) { normal_key.alignment = .Abi; } }, From 0d55075de44ed037799b088a8e1ecf0783491da6 Mon Sep 17 00:00:00 2001 From: Vexu <15308111+Vexu@users.noreply.github.com> Date: Tue, 26 Nov 2019 22:38:05 +0200 Subject: [PATCH 13/38] fix command functions not being async pointers --- src-self-hosted/main.zig | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 81b3055a54..7906c9daa7 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -49,7 +49,7 @@ const usage = const Command = struct { name: []const u8, - exec: fn (*Allocator, []const []const u8) anyerror!void, + exec: async fn (*Allocator, []const []const u8) anyerror!void, }; pub fn main() !void { @@ -118,9 +118,12 @@ pub fn main() !void { }, }; - for (commands) |command| { + inline for (commands) |command| { if (mem.eql(u8, command.name, args[1])) { - return command.exec(allocator, args[2..]); + var frame = try allocator.create(@Frame(command.exec)); + defer allocator.destroy(frame); + frame.* = async command.exec(allocator, args[2..]); + return await frame; } } @@ -852,10 +855,12 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void { .exec = cmdInternalBuildInfo, }}; - for (sub_commands) |sub_command| { + inline for (sub_commands) |sub_command| { if (mem.eql(u8, sub_command.name, args[0])) { - try sub_command.exec(allocator, args[1..]); - return; + var frame = try allocator.create(@Frame(sub_command.exec)); + defer allocator.destroy(frame); + frame.* = async sub_command.exec(allocator, args[1..]); + return await frame; } } From a6ef83cccfd1c586346c374d0f011e29d465da0e Mon Sep 17 00:00:00 2001 From: syscall0 Date: Tue, 26 Nov 2019 23:37:38 +0300 Subject: [PATCH 14/38] Fix build system crash when compiling windows dll --- lib/std/build.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 9bac20df4b..ea6ea929cf 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2175,7 +2175,7 @@ const InstallArtifactStep = struct { const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); try builder.updateFile(self.artifact.getOutputPath(), full_dest_path); - if (self.artifact.isDynamicLibrary()) { + if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) { try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename); } if (self.pdb_dir) |pdb_dir| { From 8ecd6c4d8c021f7778b4959bdf75204dfd2d1946 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 26 Nov 2019 19:50:52 -0500 Subject: [PATCH 15/38] add compiler note for bad int coercion closes #3724 --- src/ir.cpp | 27 +++++++++++++++++++++++++++ test/compile_errors.zig | 1 + 2 files changed, 28 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index db135092e0..18d7698c7a 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -71,6 +71,7 @@ enum ConstCastResultId { ConstCastResultIdPtrLens, ConstCastResultIdCV, ConstCastResultIdPtrSentinel, + ConstCastResultIdIntShorten, }; struct ConstCastOnly; @@ -97,6 +98,7 @@ struct ConstCastBadAllowsZero; struct ConstCastBadNullTermArrays; struct ConstCastBadCV; struct ConstCastPtrSentinel; +struct ConstCastIntShorten; struct ConstCastOnly { ConstCastResultId id; @@ -117,6 +119,7 @@ struct ConstCastOnly { ConstCastBadNullTermArrays *sentinel_arrays; ConstCastBadCV *bad_cv; ConstCastPtrSentinel *bad_ptr_sentinel; + ConstCastIntShorten *int_shorten; } data; }; @@ -186,6 +189,11 @@ struct ConstCastPtrSentinel { ZigType *actual_type; }; +struct ConstCastIntShorten { + ZigType *wanted_type; + ZigType *actual_type; +}; + static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope); static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval, ResultLoc *result_loc); @@ -10213,6 +10221,14 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted return result; } + if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) { + result.id = ConstCastResultIdIntShorten; + result.data.int_shorten = allocate_nonzero(1); + result.data.int_shorten->wanted_type = wanted_type; + result.data.int_shorten->actual_type = actual_type; + return result; + } + result.id = ConstCastResultIdType; result.data.type_mismatch = allocate_nonzero(1); result.data.type_mismatch->wanted_type = wanted_type; @@ -12733,6 +12749,17 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("calling convention mismatch")); break; + case ConstCastResultIdIntShorten: { + ZigType *wanted_type = cast_result->data.int_shorten->wanted_type; + ZigType *actual_type = cast_result->data.int_shorten->actual_type; + const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; + const char *actual_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values", + wanted_signed, wanted_type->data.integral.bit_count, + actual_signed, actual_type->data.integral.bit_count)); + break; + } case ConstCastResultIdFnAlign: // TODO case ConstCastResultIdFnVarArgs: // TODO case ConstCastResultIdFnReturnType: // TODO diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c1e2d579a4..f2600d2d7a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1648,6 +1648,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'", "tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'", "tmp.zig:11:20: error: expected type 'u8', found 'u16'", + "tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values", ); cases.add( From 9bce97a479e70f2d8e09047c9a0c93690cd8fd99 Mon Sep 17 00:00:00 2001 From: kprotty Date: Sat, 23 Nov 2019 10:22:16 -0600 Subject: [PATCH 16/38] Start on ResetEvent --- lib/std/c.zig | 1 + lib/std/reset_event.zig | 340 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+) create mode 100644 lib/std/reset_event.zig diff --git a/lib/std/c.zig b/lib/std/c.zig index 19c3c8feb8..9e43359852 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -220,6 +220,7 @@ pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int; pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{}; pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int; +pub extern "c" fn pthread_cond_timedwait(noalias: cond: *pthread_cond_t, noalias: mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int; pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int; pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int; diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig new file mode 100644 index 0000000000..35eab6ac13 --- /dev/null +++ b/lib/std/reset_event.zig @@ -0,0 +1,340 @@ +const std = @import("std.zig"); +const builtin = @import("builtin"); +const testing = std.testing; +const assert = std.debug.assert; +const Backoff = std.SpinLock.Backoff; +const c = std.c; +const time = std.time; +const linux = std.os.linux; +const windows = std.os.windows; + +/// A resource object which supports blocking until signaled. +/// Once finished, the `deinit()` method should be called for correctness. +pub const ResetEvent = struct { + os_event: OsEvent, + + pub fn init() ResetEvent { + return ResetEvent{ .os_event = OsEvent.init() }; + } + + pub fn deinit(self: *ResetEvent) void { + self.os_event.deinit(); + self.* = undefined; + } + + /// Returns whether or not the event is currenetly set + pub fn isSet(self: *const ResetEvent) bool { + return self.os_event.isSet(); + } + + /// Sets the event if not already set and + /// wakes up AT LEAST one thread waiting the event. + /// Returns whether or not a thread was woken up. + pub fn set(self: *ResetEvent) bool { + return self.os_event.set(); + } + + /// Resets the event to its original, unset state. + /// Returns whether or not the event was currently set before un-setting. + pub fn reset(self: *ResetEvent) bool { + return self.os_event.reset(); + } + + const WaitError = error{ + /// The thread blocked longer than the maximum time specified. + TimedOut, + }; + + /// Wait for the event to be set by blocking the current thread. + /// Optionally provided timeout in nanoseconds which throws an + /// `error.TimedOut` if the thread blocked AT LEAST longer than specified. + /// Returns whether or not the thread blocked from the event being unset at the time of calling. + pub fn wait(self: *ResetEvent, timeout_ns: ?u64) WaitError!bool { + return self.os_event.wait(timeout_ns); + } +}; + +const OsEvent = if (builtin.single_threaded) DebugEvent else switch (builtin.os) { + .windows => WindowsEvent, + .linux => if (builtin.link_libc) PosixEvent else LinuxEvent, + else => if (builtin.link_libc) PosixEvent else SpinEvent, +}; + +const DebugEvent = struct { + is_set: @typeOf(set_init), + + const set_init = if (std.debug.runtime_safety) false else {}; + + pub fn init() DebugEvent { + return DebugEvent{ .is_set = set_init }; + } + + pub fn deinit(self: *DebugEvent) void { + self.* = undefined; + } + + pub fn isSet(self: *const DebugEvent) bool { + if (!std.debug.runtime_safety) + return true; + return self.is_set; + } + + pub fn set(self: *DebugEvent) bool { + if (std.debug.runtime_safety) + self.is_set = true; + return false; + } + + pub fn reset(self: *DebugEvent) bool { + if (!std.debug.runtime_safety) + return false; + const was_set = self.is_set; + self.is_set = false; + return was_set; + } + + pub fn wait(self: *DebugEvent, timeout: ?u64) ResetEvent.WaitError!bool { + if (std.debug.runtime_safety and !self.is_set) + @panic("deadlock detected"); + return ResetEvent.WaitError.TimedOut; + } +}; + +fn EventState(comptime TagType: type) type { + return enum(TagType) { + Empty, + Waiting, + Signaled, + }; +} + +const SpinEvent = struct { + state: State, + + const State = EventState(u8); + + pub fn init() SpinEvent { + return SpinEvent{ .state = .Empty }; + } + + pub fn deinit(self: *SpinEvent) void { + self.* = undefined; + } + + pub fn isSet(self: *const SpinEvent) bool { + return @atomicLoad(State, &self.state, .Acquire) == .Signaled; + } + + pub fn set(self: *SpinEvent) bool { + return @atomicRmw(State, &self.state, .Xchg, .Signaled, .Release) == .Waiting; + } + + pub fn reset(self: *SpinEvent) bool { + return @atomicRmw(State, &self.state, .Xchg, .Empty, .Monotonic) == .Signaled; + } + + pub fn wait(self: *SpinEvent, timeout: ?u64) ResetEvent.WaitError!bool { + var state = @atomicLoad(State, &self.state, .Monotonic); + while (true) { + switch (state) { + .Empty => state = @cmpxchgWeak(State, &self.state, state, .Waiting, .Acquire, .Monotonic) orelse break, + .Waiting => break, + .Signaled => return false, + } + } + + // TODO: handle case for time.Timer.start() fails + var spin = Backoff.init(); + var timer = if (timeout == null) null else time.Timer.start() catch unreachable; + while (@atomicLoad(State, &self.state, .Monotonic) == .Waiting) { + spin.yield(); + if (timeout) |timeout_ns| { + if (timer.?.read() > timeout_ns) + return ResetEvent.WaitError.TimedOut; + } + } + return true; + } +}; + +const LinuxEvent = struct { + state: State, + + const State = EventState(i32); + + pub fn init() LinuxEvent { + return LinuxEvent{ .state = .Empty }; + } + + pub fn deinit(self: *LinuxEvent) void { + self.* = undefined; + } + + pub fn isSet(self: *const LinuxEvent) bool { + return @atomicLoad(State, &self.state, .Acquire) == .Signaled; + } + + pub fn set(self: *LinuxEvent) bool { + if (@atomicRmw(State, &self.state, .Xchg, .Signaled, .Release) != .Waiting) + return false; + const rc = linux.futex_wake(@ptrCast(*const i32, &self.state), linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); + assert(linux.getErrno(rc) == 0); + return true; + } + + pub fn reset(self: *LinuxEvent) bool { + return @atomicRmw(State, &self.state, .Xchg, .Empty, .Monotonic) == .Signaled; + } + + pub fn wait(self: *LinuxEvent, timeout: ?u64) ResetEvent.WaitError!bool { + var state = @atomicLoad(State, &self.state, .Monotonic); + while (true) { + switch (state) { + .Empty => state = @cmpxchgWeak(State, &self.state, .Empty, .Waiting, .Acquire, .Monotonic) orelse break, + .Waiting => break, + .Signaled => return false, + } + } + + var ts: linux.timespec = undefined; + var ts_ptr: ?*linux.timespec = null; + if (timeout) |timeout_ns| { + ts_ptr = &ts; + ts.tv_sec = @intCast(isize, timeout_ns / time.ns_per_s); + ts.tv_nsec = @intCast(isize, timeout_ns % time.ns_per_s); + } + + while (@atomicLoad(State, &self.state, .Monotonic) == .Waiting) { + const rc = linux.futex_wait(@ptrCast(*const i32, &self.state), linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, @enumToInt(State.Waiting), ts_ptr); + switch (linux.getErrno(rc)) { + 0, linux.EINTR => continue, + linux.EAGAIN => break, + linux.ETIMEDOUT => return ResetEvent.WaitError.TimedOut, + else => unreachable, + } + } + } +}; + +const PosixEvent = struct { + state: State, + cond: c.pthread_cond_t, + mutex: c.pthread_mutex_t, + + const State = EventState(u8); + + pub fn init() PosixEvent { + return PosixEvent{ + .state = .Empty, + .cond = c.PTHREAD_COND_INITIALIZER, + .mutex = c.PTHREAD_MUTEX_INITIALIZER, + }; + } + + pub fn deinit(self: *PosixEvent) void { + // On dragonfly, the destroy functions return EINVAL if they were initialized statically. + const retm = c.pthread_mutex_destroy(&self.mutex); + assert(retm == 0 or retm == (if (builtin.os == .dragonfly) std.os.EINVAL else 0)); + const retc = c.pthread_cond_destroy(&self.cond); + assert(retc == 0 or retc == (if (builtin.os == .dragonfly) std.os.EINVAL else 0)); + self.* = undefined; + } + + pub fn isSet(self: *const PosixEvent) bool { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + return self.state == .Signaled; + } + + pub fn set(self: *PosixEvent) bool { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + const woken = self.state == .Waiting; + self.state = .Signaled; + return woken; + } + + pub fn reset(self: *PosixEvent) bool { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + const was_set = self.state == .Signaled; + self.state = .Empty; + return was_set; + } + + pub fn wait(self: *PosixEvent, timeout: ?u64) ResetEvent.WaitError!bool { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + if (self.state == .Signaled) + return false; + + var ts: std.os.timespec = undefined; + var ts_ptr = &ts; + if (timeout) |timeout_ns| { + var tv: std.os.timeval = undefined; + assert(c.gettimeofday(&tv, null) == 0); + ts.tv_sec = @intCast(isize, tv.tv_sec + (timeout_ns / time.ns_per_s)); + ts.tv_nsec = @intCast(isize, (tv.tv_usec * time.microsecond) + (timeout_ns % time.ns_per_s)); + } + + self.state = .Waiting; + while (self.state == .Waiting) { + const rc = switch (timeout == null) { + true => c.pthread_cond_wait(&self.cond, &self.mutex), + else => c.pthread_cond_timedwait(&self.cond, &self.mutex, ts_ptr), + }; + assert(rc == 0); + } + } +}; + +const WindowsEvent = struct { + state: State, + + const State = EventState(u32); + + pub fn init() WindowsEvent { + return WindowsEvent{ .state = .Empty }; + } + + pub fn deinit(self: *WindowsEvent) void { + self.* = undefined; + } + + pub fn isSet(self: *const WindowsEvent) bool { + return @atomicLoad(State, &self.state, .Acquire) == .Signaled; + } + + pub fn set(self: *WindowsEvent) bool { + if (@atomicRmw(State, &self.state, .Xchg, .Signaled, .Release) != .Waiting) + return false; + + if (getEventHandle()) |handle| { + const key = @ptrCast(*const c_void, &self.state); + const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); + assert(rc == 0); + } + return true; + } + + pub fn reset(self: *WindowsEvent) bool { + return @atomicRmw(State, &self.state, .Xchg, .Empty, .Monotonic) == .Signaled; + } + + pub fn wait(self: *WindowsEvent, timeout: ?u64) ResetEvent.WaitError!bool { + var state = @atomicLoad(State, &self.state, .Monotonic); + while (true) { + switch (state) { + .Empty => state = @cmpxchgWeak(State, &self.state, .Empty, .Waiting, .Acquire, .Monotonic) orelse break, + .Waiting => break, + .Signaled => return false, + } + } + + const timeout_ms = if (timeout @intCast(windows.LARGE_INTEGER, ) + } +}; From ef208fee3cf7eb25ae08e1a896eba58b91e56d50 Mon Sep 17 00:00:00 2001 From: kprotty Date: Sat, 23 Nov 2019 14:04:31 -0600 Subject: [PATCH 17/38] Definition fixups & ResetEvent test cases --- lib/std/c.zig | 2 +- lib/std/reset_event.zig | 410 ++++++++++++++++++++++++---------------- 2 files changed, 249 insertions(+), 163 deletions(-) diff --git a/lib/std/c.zig b/lib/std/c.zig index 9e43359852..9e70ff988d 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -220,7 +220,7 @@ pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int; pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{}; pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int; -pub extern "c" fn pthread_cond_timedwait(noalias: cond: *pthread_cond_t, noalias: mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int; +pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int; pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int; pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int; diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index 35eab6ac13..59399d5e78 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -4,9 +4,10 @@ const testing = std.testing; const assert = std.debug.assert; const Backoff = std.SpinLock.Backoff; const c = std.c; +const os = std.os; const time = std.time; -const linux = std.os.linux; -const windows = std.os.windows; +const linux = os.linux; +const windows = os.windows; /// A resource object which supports blocking until signaled. /// Once finished, the `deinit()` method should be called for correctness. @@ -23,15 +24,15 @@ pub const ResetEvent = struct { } /// Returns whether or not the event is currenetly set - pub fn isSet(self: *const ResetEvent) bool { + pub fn isSet(self: *ResetEvent) bool { return self.os_event.isSet(); } /// Sets the event if not already set and /// wakes up AT LEAST one thread waiting the event. /// Returns whether or not a thread was woken up. - pub fn set(self: *ResetEvent) bool { - return self.os_event.set(); + pub fn set(self: *ResetEvent, auto_reset: bool) bool { + return self.os_event.set(auto_reset); } /// Resets the event to its original, unset state. @@ -73,15 +74,15 @@ const DebugEvent = struct { self.* = undefined; } - pub fn isSet(self: *const DebugEvent) bool { + pub fn isSet(self: *DebugEvent) bool { if (!std.debug.runtime_safety) return true; return self.is_set; } - pub fn set(self: *DebugEvent) bool { + pub fn set(self: *DebugEvent, auto_reset: bool) bool { if (std.debug.runtime_safety) - self.is_set = true; + self.is_set = !auto_reset; return false; } @@ -100,102 +101,87 @@ const DebugEvent = struct { } }; -fn EventState(comptime TagType: type) type { - return enum(TagType) { - Empty, - Waiting, - Signaled, +fn AtomicEvent(comptime FutexImpl: type) type { + return struct { + state: u32, + + const IS_SET: u32 = 1 << 0; + const WAIT_MASK = ~IS_SET; + + pub const Self = @This(); + pub const Futex = FutexImpl; + + pub fn init() Self { + return Self{ .state = 0 }; + } + + pub fn deinit(self: *Self) void { + self.* = undefined; + } + + pub fn isSet(self: *const Self) bool { + const state = @atomicLoad(u32, &self.state, .Acquire); + return (state & IS_SET) != 0; + } + + pub fn reset(self: *Self) bool { + const old_state = @atomicRmw(u32, &self.state, .Xchg, 0, .Monotonic); + return (old_state & IS_SET) != 0; + } + + pub fn set(self: *Self, auto_reset: bool) bool { + const new_state = if (auto_reset) 0 else IS_SET; + const old_state = @atomicRmw(u32, &self.state, .Xchg, new_state, .Release); + if ((old_state & WAIT_MASK) == 0) { + return false; + } + + Futex.wake(&self.state); + return true; + } + + pub fn wait(self: *Self, timeout: ?u64) ResetEvent.WaitError!bool { + var dummy_value: u32 = undefined; + const wait_token = @truncate(u32, @ptrToInt(&dummy_value)); + + var state = @atomicLoad(u32, &self.state, .Monotonic); + while (true) { + if ((state & IS_SET) != 0) + return false; + state = @cmpxchgWeak(u32, &self.state, state, wait_token, .Acquire, .Monotonic) orelse break; + } + + try Futex.wait(&self.state, wait_token, timeout); + return true; + } }; } -const SpinEvent = struct { - state: State, +const SpinEvent = AtomicEvent(struct { + fn wake(ptr: *const u32) void {} - const State = EventState(u8); - - pub fn init() SpinEvent { - return SpinEvent{ .state = .Empty }; - } - - pub fn deinit(self: *SpinEvent) void { - self.* = undefined; - } - - pub fn isSet(self: *const SpinEvent) bool { - return @atomicLoad(State, &self.state, .Acquire) == .Signaled; - } - - pub fn set(self: *SpinEvent) bool { - return @atomicRmw(State, &self.state, .Xchg, .Signaled, .Release) == .Waiting; - } - - pub fn reset(self: *SpinEvent) bool { - return @atomicRmw(State, &self.state, .Xchg, .Empty, .Monotonic) == .Signaled; - } - - pub fn wait(self: *SpinEvent, timeout: ?u64) ResetEvent.WaitError!bool { - var state = @atomicLoad(State, &self.state, .Monotonic); - while (true) { - switch (state) { - .Empty => state = @cmpxchgWeak(State, &self.state, state, .Waiting, .Acquire, .Monotonic) orelse break, - .Waiting => break, - .Signaled => return false, - } - } - - // TODO: handle case for time.Timer.start() fails + fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void { + // TODO: handle platforms where time.Timer.start() fails var spin = Backoff.init(); var timer = if (timeout == null) null else time.Timer.start() catch unreachable; - while (@atomicLoad(State, &self.state, .Monotonic) == .Waiting) { + while (@atomicLoad(u32, ptr, .Acquire) == expected) { spin.yield(); if (timeout) |timeout_ns| { if (timer.?.read() > timeout_ns) return ResetEvent.WaitError.TimedOut; } } - return true; } -}; +}); -const LinuxEvent = struct { - state: State, - - const State = EventState(i32); - - pub fn init() LinuxEvent { - return LinuxEvent{ .state = .Empty }; - } - - pub fn deinit(self: *LinuxEvent) void { - self.* = undefined; - } - - pub fn isSet(self: *const LinuxEvent) bool { - return @atomicLoad(State, &self.state, .Acquire) == .Signaled; - } - - pub fn set(self: *LinuxEvent) bool { - if (@atomicRmw(State, &self.state, .Xchg, .Signaled, .Release) != .Waiting) - return false; - const rc = linux.futex_wake(@ptrCast(*const i32, &self.state), linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); +const LinuxEvent = AtomicEvent(struct { + fn wake(ptr: *const u32) void { + const key = @ptrCast(*const i32, ptr); + const rc = linux.futex_wake(key, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); assert(linux.getErrno(rc) == 0); - return true; } - pub fn reset(self: *LinuxEvent) bool { - return @atomicRmw(State, &self.state, .Xchg, .Empty, .Monotonic) == .Signaled; - } - - pub fn wait(self: *LinuxEvent, timeout: ?u64) ResetEvent.WaitError!bool { - var state = @atomicLoad(State, &self.state, .Monotonic); - while (true) { - switch (state) { - .Empty => state = @cmpxchgWeak(State, &self.state, .Empty, .Waiting, .Acquire, .Monotonic) orelse break, - .Waiting => break, - .Signaled => return false, - } - } - + fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void { var ts: linux.timespec = undefined; var ts_ptr: ?*linux.timespec = null; if (timeout) |timeout_ns| { @@ -204,28 +190,94 @@ const LinuxEvent = struct { ts.tv_nsec = @intCast(isize, timeout_ns % time.ns_per_s); } - while (@atomicLoad(State, &self.state, .Monotonic) == .Waiting) { - const rc = linux.futex_wait(@ptrCast(*const i32, &self.state), linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, @enumToInt(State.Waiting), ts_ptr); + const key = @ptrCast(*const i32, ptr); + const key_expect = @bitCast(i32, expected); + while (@atomicLoad(i32, key, .Acquire) == key_expect) { + const rc = linux.futex_wait(key, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, key_expect, ts_ptr); switch (linux.getErrno(rc)) { - 0, linux.EINTR => continue, - linux.EAGAIN => break, + 0, linux.EAGAIN => break, + linux.EINTR => continue, linux.ETIMEDOUT => return ResetEvent.WaitError.TimedOut, else => unreachable, } } } -}; +}); + +const WindowsEvent = AtomicEvent(struct { + fn wake(ptr: *const u32) void { + if (getEventHandle()) |handle| { + const key = @ptrCast(*const c_void, ptr); + const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); + assert(rc == 0); + } + } + + fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void { + // fallback to spinlock if NT Keyed Events arent available + const handle = getEventHandle() orelse { + return SpinEvent.Futex.wait(ptr, expected, timeout); + }; + + var timeout_ptr: ?*windows.LARGE_INTEGER = null; + var timeout_value: windows.LARGE_INTEGER = undefined; + if (timeout) |timeout_ns| { + timeout_ptr = &timeout_value; + timeout_value = @intCast(windows.LARGE_INTEGER, @divFloor(timeout_ns, time.millisecond)); + } + + const key = @ptrCast(*const c_void, ptr); + while (@atomicLoad(u32, ptr, .Acquire) == expected) { + const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, timeout_ptr); + assert(rc == 0); + } + } + + var keyed_state = State.Uninitialized; + var keyed_handle: ?windows.HANDLE = null; + + const State = enum(u8) { + Uninitialized, + Intializing, + Initialized, + }; + + fn getEventHandle() ?windows.HANDLE { + var spin = Backoff.init(); + var state = @atomicLoad(State, &keyed_state, .Monotonic); + + while (true) { + switch (state) { + .Initialized => { + return keyed_handle; + }, + .Intializing => { + spin.yield(); + state = @atomicLoad(State, &keyed_state, .Acquire); + }, + .Uninitialized => state = @cmpxchgWeak(State, &keyed_state, state, .Intializing, .Acquire, .Monotonic) orelse { + var handle: windows.HANDLE = undefined; + const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE; + if (windows.ntdll.NtCreateKeyedEvent(&handle, access_mask, null, 0) == 0) + keyed_handle = handle; + @atomicStore(State, &keyed_state, .Initialized, .Release); + return keyed_handle; + }, + } + } + } +}); const PosixEvent = struct { - state: State, + state: u32, cond: c.pthread_cond_t, mutex: c.pthread_mutex_t, - const State = EventState(u8); + const IS_SET: u32 = 1; pub fn init() PosixEvent { return PosixEvent{ - .state = .Empty, + .state = .0, .cond = c.PTHREAD_COND_INITIALIZER, .mutex = c.PTHREAD_MUTEX_INITIALIZER, }; @@ -234,107 +286,141 @@ const PosixEvent = struct { pub fn deinit(self: *PosixEvent) void { // On dragonfly, the destroy functions return EINVAL if they were initialized statically. const retm = c.pthread_mutex_destroy(&self.mutex); - assert(retm == 0 or retm == (if (builtin.os == .dragonfly) std.os.EINVAL else 0)); + assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0)); const retc = c.pthread_cond_destroy(&self.cond); - assert(retc == 0 or retc == (if (builtin.os == .dragonfly) std.os.EINVAL else 0)); - self.* = undefined; + assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0)); } - pub fn isSet(self: *const PosixEvent) bool { + pub fn isSet(self: *PosixEvent) bool { assert(c.pthread_mutex_lock(&self.mutex) == 0); defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); - return self.state == .Signaled; - } - - pub fn set(self: *PosixEvent) bool { - assert(c.pthread_mutex_lock(&self.mutex) == 0); - defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); - - const woken = self.state == .Waiting; - self.state = .Signaled; - return woken; + return self.state == IS_SET; } pub fn reset(self: *PosixEvent) bool { assert(c.pthread_mutex_lock(&self.mutex) == 0); defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); - const was_set = self.state == .Signaled; - self.state = .Empty; + const was_set = self.state == IS_SET; + self.state = 0; return was_set; } + pub fn set(self: *PosixEvent, auto_reset: bool) bool { + assert(c.pthread_mutex_lock(&self.mutex) == 0); + defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); + + const had_waiter = self.state > IS_SET; + self.state = if (auto_reset) 0 else IS_SET; + if (had_waiter) { + assert(c.pthread_cond_signal(&self.cond) == 0); + } + return had_waiter; + } + pub fn wait(self: *PosixEvent, timeout: ?u64) ResetEvent.WaitError!bool { assert(c.pthread_mutex_lock(&self.mutex) == 0); defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); - if (self.state == .Signaled) + if (self.state == IS_SET) return false; - var ts: std.os.timespec = undefined; + var ts: os.timespec = undefined; var ts_ptr = &ts; if (timeout) |timeout_ns| { - var tv: std.os.timeval = undefined; + var tv: os.timeval = undefined; assert(c.gettimeofday(&tv, null) == 0); - ts.tv_sec = @intCast(isize, tv.tv_sec + (timeout_ns / time.ns_per_s)); - ts.tv_nsec = @intCast(isize, (tv.tv_usec * time.microsecond) + (timeout_ns % time.ns_per_s)); + ts.tv_sec = tv.tv_sec + @intCast(isize, timeout_ns / time.ns_per_s); + ts.tv_nsec = (tv.tv_usec * time.microsecond) + @intCast(isize, timeout_ns % time.ns_per_s); } - self.state = .Waiting; - while (self.state == .Waiting) { + var dummy_value: u32 = undefined; + var wait_token = @truncate(u32, @ptrToInt(&dummy_value)); + self.state = wait_token; + + while (self.state == wait_token) { const rc = switch (timeout == null) { true => c.pthread_cond_wait(&self.cond, &self.mutex), else => c.pthread_cond_timedwait(&self.cond, &self.mutex, ts_ptr), }; - assert(rc == 0); - } - } -}; - -const WindowsEvent = struct { - state: State, - - const State = EventState(u32); - - pub fn init() WindowsEvent { - return WindowsEvent{ .state = .Empty }; - } - - pub fn deinit(self: *WindowsEvent) void { - self.* = undefined; - } - - pub fn isSet(self: *const WindowsEvent) bool { - return @atomicLoad(State, &self.state, .Acquire) == .Signaled; - } - - pub fn set(self: *WindowsEvent) bool { - if (@atomicRmw(State, &self.state, .Xchg, .Signaled, .Release) != .Waiting) - return false; - - if (getEventHandle()) |handle| { - const key = @ptrCast(*const c_void, &self.state); - const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); - assert(rc == 0); + // TODO: rc appears to be the positive error code making os.errno() always return 0 on linux + switch (std.math.max(@as(c_int, os.errno(rc)), rc)) { + 0 => {}, + os.ETIMEDOUT => return ResetEvent.WaitError.TimedOut, + os.EINVAL => unreachable, + os.EPERM => unreachable, + else => unreachable, + } } return true; } +}; - pub fn reset(self: *WindowsEvent) bool { - return @atomicRmw(State, &self.state, .Xchg, .Empty, .Monotonic) == .Signaled; - } +test "std.ResetEvent" { + // TODO + if (builtin.single_threaded) + return error.SkipZigTest; - pub fn wait(self: *WindowsEvent, timeout: ?u64) ResetEvent.WaitError!bool { - var state = @atomicLoad(State, &self.state, .Monotonic); - while (true) { - switch (state) { - .Empty => state = @cmpxchgWeak(State, &self.state, .Empty, .Waiting, .Acquire, .Monotonic) orelse break, - .Waiting => break, - .Signaled => return false, - } + var event = ResetEvent.init(); + defer event.deinit(); + + // test event setting + testing.expect(event.isSet() == false); + testing.expect(event.set(false) == false); + testing.expect(event.isSet() == true); + + // test event resetting + testing.expect(event.reset() == true); + testing.expect(event.isSet() == false); + testing.expect(event.reset() == false); + + // test waiting timeout + const delay = 100 * time.millisecond; + var timer = time.Timer.start() catch unreachable; + testing.expectError(ResetEvent.WaitError.TimedOut, event.wait(delay)); + const elapsed = timer.read(); + testing.expect(elapsed >= delay and elapsed < delay * 2); + + // test cross thread signaling + const Context = struct { + event: ResetEvent, + value: u128, + + fn receiver(self: *@This()) void { + // wait for the sender to notify us with updated value + assert(self.value == 0); + assert((self.event.wait(1 * time.second) catch unreachable) == true); + assert(self.value == 1); + + // wait for sender to sleep, then notify it of new value + time.sleep(50 * time.millisecond); + self.value = 2; + assert(self.event.set(false) == true); } - const timeout_ms = if (timeout @intCast(windows.LARGE_INTEGER, ) - } -}; + fn sender(self: *@This()) !void { + // wait for the receiver() to start wait()'ing + time.sleep(50 * time.millisecond); + + // update value to 1 and notify the receiver() + assert(self.value == 0); + self.value = 1; + assert(self.event.set(true) == true); + + // wait for the receiver to update the value & notify us + assert((try self.event.wait(1 * time.second)) == true); + assert(self.value == 2); + } + }; + + _ = event.reset(); + var context = Context{ + .event = event, + .value = 0, + }; + + var receiver = try std.Thread.spawn(&context, Context.receiver); + defer receiver.wait(); + try context.sender(); +} \ No newline at end of file From a0955990dc2c8df42879e33b308ca177ba2c771a Mon Sep 17 00:00:00 2001 From: kprotty Date: Sat, 23 Nov 2019 15:50:08 -0600 Subject: [PATCH 18/38] fix ResetEvent windows bugs --- lib/std/reset_event.zig | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index 59399d5e78..f3194b0a7d 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -219,17 +219,23 @@ const WindowsEvent = AtomicEvent(struct { return SpinEvent.Futex.wait(ptr, expected, timeout); }; + // NT uses timeouts in units of 100ns with negative value being relative var timeout_ptr: ?*windows.LARGE_INTEGER = null; var timeout_value: windows.LARGE_INTEGER = undefined; if (timeout) |timeout_ns| { timeout_ptr = &timeout_value; - timeout_value = @intCast(windows.LARGE_INTEGER, @divFloor(timeout_ns, time.millisecond)); + timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100); } - const key = @ptrCast(*const c_void, ptr); - while (@atomicLoad(u32, ptr, .Acquire) == expected) { + // NtWaitForKeyedEvent doesnt have spurious wake-ups + if (@atomicLoad(u32, ptr, .Acquire) == expected) { + const key = @ptrCast(*const c_void, ptr); const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, timeout_ptr); - assert(rc == 0); + switch (rc) { + 0 => {}, + windows.WAIT_TIMEOUT => return ResetEvent.WaitError.TimedOut, + else => unreachable, + } } } @@ -377,10 +383,13 @@ test "std.ResetEvent" { // test waiting timeout const delay = 100 * time.millisecond; + const error_margin = 50 * time.millisecond; + var timer = time.Timer.start() catch unreachable; testing.expectError(ResetEvent.WaitError.TimedOut, event.wait(delay)); const elapsed = timer.read(); - testing.expect(elapsed >= delay and elapsed < delay * 2); + testing.expect(elapsed >= delay - error_margin); + testing.expect(elapsed <= delay + error_margin); // test cross thread signaling const Context = struct { From ca2d566ec85bee81396f64325844cc760b3cf870 Mon Sep 17 00:00:00 2001 From: kprotty Date: Sat, 23 Nov 2019 16:24:01 -0600 Subject: [PATCH 19/38] replace ThreadParker with ResetEvent + WordLock mutex --- lib/std/mutex.zig | 136 ++++++++++++++++++++++------------ lib/std/parker.zig | 180 --------------------------------------------- lib/std/std.zig | 2 +- 3 files changed, 88 insertions(+), 230 deletions(-) delete mode 100644 lib/std/parker.zig diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index e8f83a4a17..39cfab19ce 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -1,13 +1,12 @@ const std = @import("std.zig"); const builtin = @import("builtin"); const testing = std.testing; -const SpinLock = std.SpinLock; -const ThreadParker = std.ThreadParker; +const ResetEvent = std.ResetEvent; /// Lock may be held only once. If the same thread /// tries to acquire the same mutex twice, it deadlocks. -/// This type supports static initialization and is based off of Golang 1.13 runtime.lock_futex: -/// https://github.com/golang/go/blob/master/src/runtime/lock_futex.go +/// This type supports static initialization and is based off of Webkit's WTF Lock (via rust parking_lot) +/// https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs /// When an application is built in single threaded release mode, all the functions are /// no-ops. In single threaded debug mode, there is deadlock detection. pub const Mutex = if (builtin.single_threaded) @@ -39,80 +38,119 @@ pub const Mutex = if (builtin.single_threaded) } else struct { - state: State, // TODO: make this an enum - parker: ThreadParker, + state: usize, - const State = enum(u32) { - Unlocked, - Sleeping, - Locked, - }; + const MUTEX_LOCK: usize = 1 << 0; + const QUEUE_LOCK: usize = 1 << 1; + const QUEUE_MASK: usize = ~(MUTEX_LOCK | QUEUE_LOCK); + const QueueNode = std.atomic.Stack(ResetEvent).Node; /// number of iterations to spin yielding the cpu const SPIN_CPU = 4; - /// number of iterations to perform in the cpu yield loop + /// number of iterations to spin in the cpu yield loop const SPIN_CPU_COUNT = 30; /// number of iterations to spin yielding the thread const SPIN_THREAD = 1; pub fn init() Mutex { - return Mutex{ - .state = .Unlocked, - .parker = ThreadParker.init(), - }; + return Mutex{ .state = 0 }; } pub fn deinit(self: *Mutex) void { - self.parker.deinit(); + self.* = undefined; } pub const Held = struct { mutex: *Mutex, pub fn release(self: Held) void { - switch (@atomicRmw(State, &self.mutex.state, .Xchg, .Unlocked, .Release)) { - .Locked => {}, - .Sleeping => self.mutex.parker.unpark(@ptrCast(*const u32, &self.mutex.state)), - .Unlocked => unreachable, // unlocking an unlocked mutex - else => unreachable, // should never be anything else + // since MUTEX_LOCK is the first bit, we can use (.Sub) instead of (.And, ~MUTEX_LOCK). + // this is because .Sub may be implemented more efficiently than the latter + // (e.g. `lock xadd` vs `cmpxchg` loop on x86) + const state = @atomicRmw(usize, &self.mutex.state, .Sub, MUTEX_LOCK, .Release); + if ((state & QUEUE_MASK) != 0 and (state & QUEUE_LOCK) == 0) { + self.mutex.releaseSlow(state); } } }; pub fn acquire(self: *Mutex) Held { - // Try and speculatively grab the lock. - // If it fails, the state is either Locked or Sleeping - // depending on if theres a thread stuck sleeping below. - var state = @atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire); - if (state == .Unlocked) - return Held{ .mutex = self }; + // fast path close to SpinLock fast path + if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic)) |current_state| { + self.acquireSlow(current_state); + } + return Held{ .mutex = self }; + } + + fn acquireSlow(self: *Mutex, current_state: usize) void { + var spin: usize = 0; + var state = current_state; + while (true) { + + // try and acquire the lock if unlocked + if ((state & MUTEX_LOCK) == 0) { + state = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return; + continue; + } + + // spin only if the waiting queue isn't empty and when it hasn't spun too much already + if ((state & QUEUE_MASK) == 0 and spin < SPIN_CPU + SPIN_THREAD) { + if (spin < SPIN_CPU) { + std.SpinLock.yield(SPIN_CPU_COUNT); + } else { + std.os.sched_yield() catch std.time.sleep(0); + } + state = @atomicLoad(usize, &self.state, .Monotonic); + continue; + } + + // thread should block, try and add this event to the waiting queue + var node = QueueNode{ + .next = @intToPtr(?*QueueNode, state & QUEUE_MASK), + .data = ResetEvent.init(), + }; + defer node.data.deinit(); + const new_state = @ptrToInt(&node) | (state & ~QUEUE_MASK); + state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse { + // node is in the queue, wait until a `held.release()` wakes us up. + _ = node.data.wait(null) catch unreachable; + spin = 0; + state = @atomicLoad(usize, &self.state, .Monotonic); + continue; + }; + } + } + + fn releaseSlow(self: *Mutex, current_state: usize) void { + // grab the QUEUE_LOCK in order to signal a waiting queue node's event. + var state = current_state; + while (true) { + if ((state & QUEUE_LOCK) != 0 or (state & QUEUE_MASK) == 0) + return; + state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break; + } while (true) { - // try and acquire the lock using cpu spinning on failure - var spin: usize = 0; - while (spin < SPIN_CPU) : (spin += 1) { - var value = @atomicLoad(State, &self.state, .Monotonic); - while (value == .Unlocked) - value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; - SpinLock.yield(SPIN_CPU_COUNT); + // barrier needed to observe incoming state changes + defer @fence(.Acquire); + + // the mutex is currently locked. try to unset the QUEUE_LOCK and let the locker wake up the next node. + // avoids waking up multiple sleeping threads which try to acquire the lock again which increases contention. + if ((state & MUTEX_LOCK) != 0) { + state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Monotonic) orelse return; + continue; } - // try and acquire the lock using thread rescheduling on failure - spin = 0; - while (spin < SPIN_THREAD) : (spin += 1) { - var value = @atomicLoad(State, &self.state, .Monotonic); - while (value == .Unlocked) - value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self }; - std.os.sched_yield() catch std.time.sleep(1); - } - - // failed to acquire the lock, go to sleep until woken up by `Held.release()` - if (@atomicRmw(State, &self.state, .Xchg, .Sleeping, .Acquire) == .Unlocked) - return Held{ .mutex = self }; - state = .Sleeping; - self.parker.park(@ptrCast(*const u32, &self.state), @enumToInt(State.Sleeping)); + // try to pop the top node on the waiting queue stack to wake it up + // while at the same time unsetting the QUEUE_LOCK. + const node = @intToPtr(*QueueNode, state & QUEUE_MASK); + const new_state = @ptrToInt(node.next) | (state & MUTEX_LOCK); + state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse { + _ = node.data.set(false); + return; + }; } } }; diff --git a/lib/std/parker.zig b/lib/std/parker.zig deleted file mode 100644 index 4ba0100b9e..0000000000 --- a/lib/std/parker.zig +++ /dev/null @@ -1,180 +0,0 @@ -const std = @import("std.zig"); -const builtin = @import("builtin"); -const time = std.time; -const testing = std.testing; -const assert = std.debug.assert; -const SpinLock = std.SpinLock; -const linux = std.os.linux; -const windows = std.os.windows; - -pub const ThreadParker = switch (builtin.os) { - .linux => if (builtin.link_libc) PosixParker else LinuxParker, - .windows => WindowsParker, - else => if (builtin.link_libc) PosixParker else SpinParker, -}; - -const SpinParker = struct { - pub fn init() SpinParker { - return SpinParker{}; - } - pub fn deinit(self: *SpinParker) void {} - - pub fn unpark(self: *SpinParker, ptr: *const u32) void {} - - pub fn park(self: *SpinParker, ptr: *const u32, expected: u32) void { - var backoff = SpinLock.Backoff.init(); - while (@atomicLoad(u32, ptr, .Acquire) == expected) - backoff.yield(); - } -}; - -const LinuxParker = struct { - pub fn init() LinuxParker { - return LinuxParker{}; - } - pub fn deinit(self: *LinuxParker) void {} - - pub fn unpark(self: *LinuxParker, ptr: *const u32) void { - const rc = linux.futex_wake(@ptrCast(*const i32, ptr), linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1); - assert(linux.getErrno(rc) == 0); - } - - pub fn park(self: *LinuxParker, ptr: *const u32, expected: u32) void { - const value = @intCast(i32, expected); - while (@atomicLoad(u32, ptr, .Acquire) == expected) { - const rc = linux.futex_wait(@ptrCast(*const i32, ptr), linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, value, null); - switch (linux.getErrno(rc)) { - 0, linux.EAGAIN => return, - linux.EINTR => continue, - linux.EINVAL => unreachable, - else => continue, - } - } - } -}; - -const WindowsParker = struct { - waiters: u32, - - pub fn init() WindowsParker { - return WindowsParker{ .waiters = 0 }; - } - pub fn deinit(self: *WindowsParker) void {} - - pub fn unpark(self: *WindowsParker, ptr: *const u32) void { - const key = @ptrCast(*const c_void, ptr); - const handle = getEventHandle() orelse return; - - var waiting = @atomicLoad(u32, &self.waiters, .Monotonic); - while (waiting != 0) { - waiting = @cmpxchgWeak(u32, &self.waiters, waiting, waiting - 1, .Acquire, .Monotonic) orelse { - const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null); - assert(rc == 0); - return; - }; - } - } - - pub fn park(self: *WindowsParker, ptr: *const u32, expected: u32) void { - var spin = SpinLock.Backoff.init(); - const ev_handle = getEventHandle(); - const key = @ptrCast(*const c_void, ptr); - - while (@atomicLoad(u32, ptr, .Monotonic) == expected) { - if (ev_handle) |handle| { - _ = @atomicRmw(u32, &self.waiters, .Add, 1, .Release); - const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null); - assert(rc == 0); - } else { - spin.yield(); - } - } - } - - var event_handle = std.lazyInit(windows.HANDLE); - - fn getEventHandle() ?windows.HANDLE { - if (event_handle.get()) |handle_ptr| - return handle_ptr.*; - defer event_handle.resolve(); - - const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE; - if (windows.ntdll.NtCreateKeyedEvent(&event_handle.data, access_mask, null, 0) != 0) - return null; - return event_handle.data; - } -}; - -const PosixParker = struct { - cond: c.pthread_cond_t, - mutex: c.pthread_mutex_t, - - const c = std.c; - - pub fn init() PosixParker { - return PosixParker{ - .cond = c.PTHREAD_COND_INITIALIZER, - .mutex = c.PTHREAD_MUTEX_INITIALIZER, - }; - } - - pub fn deinit(self: *PosixParker) void { - // On dragonfly, the destroy functions return EINVAL if they were initialized statically. - const retm = c.pthread_mutex_destroy(&self.mutex); - assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0)); - const retc = c.pthread_cond_destroy(&self.cond); - assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0)); - } - - pub fn unpark(self: *PosixParker, ptr: *const u32) void { - assert(c.pthread_mutex_lock(&self.mutex) == 0); - defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); - assert(c.pthread_cond_signal(&self.cond) == 0); - } - - pub fn park(self: *PosixParker, ptr: *const u32, expected: u32) void { - assert(c.pthread_mutex_lock(&self.mutex) == 0); - defer assert(c.pthread_mutex_unlock(&self.mutex) == 0); - while (@atomicLoad(u32, ptr, .Acquire) == expected) - assert(c.pthread_cond_wait(&self.cond, &self.mutex) == 0); - } -}; - -test "std.ThreadParker" { - if (builtin.single_threaded) - return error.SkipZigTest; - - const Context = struct { - parker: ThreadParker, - data: u32, - - fn receiver(self: *@This()) void { - self.parker.park(&self.data, 0); // receives 1 - assert(@atomicRmw(u32, &self.data, .Xchg, 2, .SeqCst) == 1); // sends 2 - self.parker.unpark(&self.data); // wakes up waiters on 2 - self.parker.park(&self.data, 2); // receives 3 - assert(@atomicRmw(u32, &self.data, .Xchg, 4, .SeqCst) == 3); // sends 4 - self.parker.unpark(&self.data); // wakes up waiters on 4 - } - - fn sender(self: *@This()) void { - assert(@atomicRmw(u32, &self.data, .Xchg, 1, .SeqCst) == 0); // sends 1 - self.parker.unpark(&self.data); // wakes up waiters on 1 - self.parker.park(&self.data, 1); // receives 2 - assert(@atomicRmw(u32, &self.data, .Xchg, 3, .SeqCst) == 2); // sends 3 - self.parker.unpark(&self.data); // wakes up waiters on 3 - self.parker.park(&self.data, 3); // receives 4 - } - }; - - var context = Context{ - .parker = ThreadParker.init(), - .data = 0, - }; - defer context.parker.deinit(); - - var receiver = try std.Thread.spawn(&context, Context.receiver); - defer receiver.wait(); - - context.sender(); -} diff --git a/lib/std/std.zig b/lib/std/std.zig index 83b7ed6e94..09db489604 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -16,6 +16,7 @@ pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice; pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian; pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; pub const Progress = @import("progress.zig").Progress; +pub const ResetEvent = @import("reset_event.zig").ResetEvent; pub const SegmentedList = @import("segmented_list.zig").SegmentedList; pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList; pub const SpinLock = @import("spinlock.zig").SpinLock; @@ -23,7 +24,6 @@ pub const StringHashMap = @import("hash_map.zig").StringHashMap; pub const TailQueue = @import("linked_list.zig").TailQueue; pub const Target = @import("target.zig").Target; pub const Thread = @import("thread.zig").Thread; -pub const ThreadParker = @import("parker.zig").ThreadParker; pub const atomic = @import("atomic.zig"); pub const base64 = @import("base64.zig"); From 056b5a26c960923d7afe9807255a32b85d99cacc Mon Sep 17 00:00:00 2001 From: kprotty Date: Sun, 24 Nov 2019 20:28:29 -0600 Subject: [PATCH 20/38] ResetEvent: get abstime based on std.time --- lib/std/reset_event.zig | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index f3194b0a7d..ffa73941c7 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -333,12 +333,20 @@ const PosixEvent = struct { return false; var ts: os.timespec = undefined; - var ts_ptr = &ts; if (timeout) |timeout_ns| { - var tv: os.timeval = undefined; - assert(c.gettimeofday(&tv, null) == 0); - ts.tv_sec = tv.tv_sec + @intCast(isize, timeout_ns / time.ns_per_s); - ts.tv_nsec = (tv.tv_usec * time.microsecond) + @intCast(isize, timeout_ns % time.ns_per_s); + var timeout_abs = timeout_ns; + if (comptime std.Target.current.isDarwin()) { + var tv: os.darwin.timeval = undefined; + assert(os.darwin.gettimeofday(&tv, null) == 0); + timeout_abs += @intCast(u64, tv.tv_sec) * time.second; + timeout_abs += @intCast(u64, tv.tv_usec) * time.microsecond; + } else { + os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable; + timeout_abs += @intCast(u64, ts.tv_sec) * time.second; + timeout_abs += @intCast(u64, ts.tv_nsec); + } + ts.tv_sec = @intCast(@typeOf(ts.tv_sec), @divFloor(timeout_abs, time.second)); + ts.tv_nsec = @intCast(@typeOf(ts.tv_nsec), @mod(timeout_abs, time.second)); } var dummy_value: u32 = undefined; @@ -348,7 +356,7 @@ const PosixEvent = struct { while (self.state == wait_token) { const rc = switch (timeout == null) { true => c.pthread_cond_wait(&self.cond, &self.mutex), - else => c.pthread_cond_timedwait(&self.cond, &self.mutex, ts_ptr), + else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts), }; // TODO: rc appears to be the positive error code making os.errno() always return 0 on linux switch (std.math.max(@as(c_int, os.errno(rc)), rc)) { From ff445814cbf909db79193ce5815279eb074246fe Mon Sep 17 00:00:00 2001 From: kprotty Date: Tue, 26 Nov 2019 13:04:18 -0600 Subject: [PATCH 21/38] remove wait timeout test cases --- lib/std/reset_event.zig | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/std/reset_event.zig b/lib/std/reset_event.zig index ffa73941c7..e408c0d0ac 100644 --- a/lib/std/reset_event.zig +++ b/lib/std/reset_event.zig @@ -389,16 +389,6 @@ test "std.ResetEvent" { testing.expect(event.isSet() == false); testing.expect(event.reset() == false); - // test waiting timeout - const delay = 100 * time.millisecond; - const error_margin = 50 * time.millisecond; - - var timer = time.Timer.start() catch unreachable; - testing.expectError(ResetEvent.WaitError.TimedOut, event.wait(delay)); - const elapsed = timer.read(); - testing.expect(elapsed >= delay - error_margin); - testing.expect(elapsed <= delay + error_margin); - // test cross thread signaling const Context = struct { event: ResetEvent, From 4d8a8e65df79ddd5edf52f961552036ccfca6e8e Mon Sep 17 00:00:00 2001 From: Vexu <15308111+Vexu@users.noreply.github.com> Date: Wed, 27 Nov 2019 10:17:37 +0200 Subject: [PATCH 22/38] add more workarounds --- lib/std/event/fs.zig | 2 +- src-self-hosted/compilation.zig | 6 +++++- src-self-hosted/main.zig | 3 ++- src-self-hosted/test.zig | 15 ++++++++------- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/std/event/fs.zig b/lib/std/event/fs.zig index 5d55805e35..346d0f294a 100644 --- a/lib/std/event/fs.zig +++ b/lib/std/event/fs.zig @@ -1293,7 +1293,7 @@ pub fn Watch(comptime V: type) type { os.linux.EINVAL => unreachable, os.linux.EFAULT => unreachable, os.linux.EAGAIN => { - global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN); + global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN | os.EPOLLONESHOT); }, else => unreachable, } diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 259cfd602f..a43d4ebc83 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -363,7 +363,11 @@ pub const Compilation = struct { is_static, zig_lib_dir, ); - return optional_comp orelse if (await frame) |_| unreachable else |err| err; + // TODO causes segfault + // return optional_comp orelse if (await frame) |_| unreachable else |err| err; + if (optional_comp) |comp| { + return comp; + } else if (await frame) |_| unreachable else |err| return err; } async fn createAsync( diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 7906c9daa7..4288c4ea9a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -56,7 +56,8 @@ pub fn main() !void { // This allocator needs to be thread-safe because we use it for the event.Loop // which multiplexes async functions onto kernel threads. // libc allocator is guaranteed to have this property. - const allocator = std.heap.c_allocator; + // TODO https://github.com/ziglang/zig/issues/3783 + const allocator = std.heap.page_allocator; stdout = &std.io.getStdOut().outStream().stream; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 4c1ed6ad45..62b7914dbc 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -26,7 +26,8 @@ test "stage2" { } const file1 = "1.zig"; -const allocator = std.heap.c_allocator; +// TODO https://github.com/ziglang/zig/issues/3783 +const allocator = std.heap.page_allocator; pub const TestContext = struct { zig_compiler: ZigCompiler, @@ -94,8 +95,8 @@ pub const TestContext = struct { &self.zig_compiler, "test", file1_path, - Target.Native, - Compilation.Kind.Obj, + .Native, + .Obj, .Debug, true, // is_static self.zig_lib_dir, @@ -128,8 +129,8 @@ pub const TestContext = struct { &self.zig_compiler, "test", file1_path, - Target.Native, - Compilation.Kind.Exe, + .Native, + .Exe, .Debug, false, self.zig_lib_dir, @@ -170,13 +171,13 @@ pub const TestContext = struct { return error.OutputMismatch; } }, - .Error => |err| return err, + .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err, .Fail => |msgs| { const stderr = std.io.getStdErr(); try stderr.write("build incorrectly failed:\n"); for (msgs) |msg| { defer msg.destroy(); - try msg.printToFile(stderr, errmsg.Color.Auto); + try msg.printToFile(stderr, .Auto); } }, } From f0d6447569e89a7b862da806a78da52d15160ef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20Quei=C3=9Fner?= Date: Wed, 27 Nov 2019 22:35:32 +0100 Subject: [PATCH 23/38] Implements std.testing.expectEqual for tagged unions. (#3773) --- lib/std/testing.zig | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 09ab6e406d..2ed6c66fe6 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -89,7 +89,26 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { if (union_info.tag_type == null) { @compileError("Unable to compare untagged union values"); } - @compileError("TODO implement testing.expectEqual for tagged unions"); + + const TagType = @TagType(@typeOf(expected)); + + const expectedTag = @as(TagType, expected); + const actualTag = @as(TagType, actual); + + expectEqual(expectedTag, actualTag); + + // we only reach this loop if the tags are equal + inline for (std.meta.fields(@typeOf(actual))) |fld| { + if (std.mem.eql(u8, fld.name, @tagName(actualTag))) { + expectEqual(@field(expected, fld.name), @field(actual, fld.name)); + return; + } + } + + // we iterate over *all* union fields + // => we should never get here as the loop above is + // including all possible values. + unreachable; }, .Optional => { @@ -124,6 +143,19 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { } } +test "expectEqual.union(enum)" +{ + const T = union(enum) { + a: i32, + b: f32, + }; + + const a10 = T { .a = 10 }; + const a20 = T { .a = 20 }; + + expectEqual(a10, a10); +} + /// This function is intended to be used only in tests. When the two slices are not /// equal, prints diagnostics to stderr to show exactly how they are not equal, /// then aborts. From ca61a5f0b74fb420a21cdbb9e58f9d38ebe9dc0f Mon Sep 17 00:00:00 2001 From: Michael Dusan Date: Wed, 27 Nov 2019 17:47:44 -0500 Subject: [PATCH 24/38] Windows: fix test/standalone/shared_library - on Windows use first found env var { "Path", "PATH" } Bug Description: `build test` results in the following error on in a msys64 shell with "PATH" env var instead of "Path": error while loading shared libraries: mathtest.dll: cannot open shared object file: No such file or directory --- lib/std/build.zig | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index ea6ea929cf..38025b83bc 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2062,14 +2062,28 @@ pub const RunStep = struct { } pub fn addPathDir(self: *RunStep, search_path: []const u8) void { - const PATH = if (builtin.os == .windows) "Path" else "PATH"; const env_map = self.getEnvMap(); - const prev_path = env_map.get(PATH) orelse { - env_map.set(PATH, search_path) catch unreachable; - return; - }; - const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", prev_path, search_path); - env_map.set(PATH, new_path) catch unreachable; + + var key: []const u8 = undefined; + var prev_path: ?[]const u8 = undefined; + if (builtin.os == .windows) { + key = "Path"; + prev_path = env_map.get(key); + if (prev_path == null) { + key = "PATH"; + prev_path = env_map.get(key); + } + } else { + key = "PATH"; + prev_path = env_map.get(key); + } + + if (prev_path) |pp| { + const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", pp, search_path); + env_map.set(key, new_path) catch unreachable; + } else { + env_map.set(key, search_path) catch unreachable; + } } pub fn getEnvMap(self: *RunStep) *BufMap { From a6c9c5f767217ba3591e51047736f0fa6a975a57 Mon Sep 17 00:00:00 2001 From: Quetzal Bradley Date: Thu, 28 Nov 2019 02:19:08 +0000 Subject: [PATCH 25/38] implement correct buffer wrapping logic in std.event.Channel --- lib/std/event/channel.zig | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/std/event/channel.zig b/lib/std/event/channel.zig index ac5a65e1b0..96cfd706be 100644 --- a/lib/std/event/channel.zig +++ b/lib/std/event/channel.zig @@ -54,6 +54,10 @@ pub fn Channel(comptime T: type) type { /// For a zero length buffer, use `[0]T{}`. /// TODO https://github.com/ziglang/zig/issues/2765 pub fn init(self: *SelfChannel, buffer: []T) void { + // The ring buffer implementation only works with power of 2 buffer sizes + // because of relying on subtracting across zero. For example (0 -% 1) % 10 == 5 + assert(buffer.len == 0 or @popCount(usize, buffer.len) == 1); + self.* = SelfChannel{ .buffer_len = 0, .buffer_nodes = buffer, @@ -184,11 +188,11 @@ pub fn Channel(comptime T: type) type { const get_node = &self.getters.get().?.data; switch (get_node.data) { GetNode.Data.Normal => |info| { - info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; + info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len]; }, GetNode.Data.OrNull => |info| { _ = self.or_null_queue.remove(info.or_null); - info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len]; + info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len]; }, } global_event_loop.onNextTick(get_node.tick_node); @@ -222,7 +226,7 @@ pub fn Channel(comptime T: type) type { while (self.buffer_len != self.buffer_nodes.len and put_count != 0) { const put_node = &self.putters.get().?.data; - self.buffer_nodes[self.buffer_index] = put_node.data; + self.buffer_nodes[self.buffer_index % self.buffer_nodes.len] = put_node.data; global_event_loop.onNextTick(put_node.tick_node); self.buffer_index +%= 1; self.buffer_len += 1; @@ -283,6 +287,29 @@ test "std.event.Channel" { await putter; } +test "std.event.Channel wraparound" { + + // TODO provide a way to run tests in evented I/O mode + if (!std.io.is_async) return error.SkipZigTest; + + const channel_size = 2; + + var buf : [channel_size]i32 = undefined; + var channel: Channel(i32) = undefined; + channel.init(&buf); + defer channel.deinit(); + + // add items to channel and pull them out until + // the buffer wraps around, make sure it doesn't crash. + var result : i32 = undefined; + channel.put(5); + testing.expectEqual(@as(i32, 5), channel.get()); + channel.put(6); + testing.expectEqual(@as(i32, 6), channel.get()); + channel.put(7); + testing.expectEqual(@as(i32, 7), channel.get()); +} + async fn testChannelGetter(channel: *Channel(i32)) void { const value1 = channel.get(); testing.expect(value1 == 1234); From 2ab7f31e99defc62353c3dfed0f6f427c9ac8e80 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Nov 2019 10:44:55 -0500 Subject: [PATCH 26/38] std.math: remove constants that should be expressions All four of these can be represented in fewer characters with expressions, which will be guaranteed to happen at compile-time, and have the same or better precision. The other math constants here which depend on function calls could be similarly removed if and when #425 is solved. However I left them for now since Zig does not eagerly evaluate functions with comptime parameters. --- lib/std/math.zig | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/std/math.zig b/lib/std/math.zig index 714521357c..f87996f174 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -25,18 +25,6 @@ pub const ln2 = 0.693147180559945309417232121458176568; /// ln(10) pub const ln10 = 2.302585092994045684017991454684364208; -/// Ï€/2 -pub const pi_2 = 1.570796326794896619231321691639751442; - -/// Ï€/4 -pub const pi_4 = 0.785398163397448309615660845819875721; - -/// 1/Ï€ -pub const one_pi = 0.318309886183790671537767526745028724; - -/// 2/Ï€ -pub const two_pi = 0.636619772367581343075535053490057448; - /// 2/sqrt(Ï€) pub const two_sqrtpi = 1.128379167095512573896158903121545172; From a438a615e6e2ef9e49bf10405b3ba6c8814ccf4b Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Nov 2019 12:41:07 -0500 Subject: [PATCH 27/38] docs: this statement was in the wrong section --- doc/langref.html.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 8c118cf3b2..b742119af7 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -7455,6 +7455,10 @@ fn add(a: i32, b: i32) i32 { return a + b; } Attempting to convert a number which is out of range of the destination type results in safety-protected {#link|Undefined Behavior#}.

+

+ If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#}, + then this is semantically equivalent to {#link|Type Coercion#}. +

{#header_close#} {#header_open|@intToEnum#} @@ -8206,10 +8210,6 @@ test "integer truncation" { This function always truncates the significant bits of the integer, regardless of endianness on the target platform.

-

- If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#}, - then this is semantically equivalent to {#link|Type Coercion#}. -

{#header_close#} {#header_open|@Type#} From f980c29306ac9435662bde6fb5557ca0c6d98310 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 29 Nov 2019 22:33:55 -0500 Subject: [PATCH 28/38] fix typo in error note for integer casting --- src/ir.cpp | 2 +- test/compile_errors.zig | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 18d7698c7a..272442a563 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12753,7 +12753,7 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa ZigType *wanted_type = cast_result->data.int_shorten->wanted_type; ZigType *actual_type = cast_result->data.int_shorten->actual_type; const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; - const char *actual_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; + const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned"; add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values", wanted_signed, wanted_type->data.integral.bit_count, diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f2600d2d7a..ef265cff2b 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1644,11 +1644,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var spartan_count: u16 = 300; \\ var byte: u8 = spartan_count; \\} + \\export fn entry4() void { + \\ var signed: i8 = -1; + \\ var unsigned: u64 = signed; + \\} , "tmp.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'", "tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'", "tmp.zig:11:20: error: expected type 'u8', found 'u16'", "tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values", + "tmp.zig:15:25: error: expected type 'u64', found 'i8'", + "tmp.zig:15:25: note: unsigned 64-bit int cannot represent all possible signed 8-bit values", ); cases.add( From e1e06e676666015faf733f70c6e1970999b5af9e Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 30 Nov 2019 16:13:33 +0100 Subject: [PATCH 29/38] linux-i386 support --- lib/std/debug.zig | 6 + lib/std/os/bits/linux.zig | 1 + lib/std/os/bits/linux/arm-eabi.zig | 1 - lib/std/os/bits/linux/arm64.zig | 1 - lib/std/os/bits/linux/i386.zig | 642 +++++++++++++++++++++++++++++ lib/std/os/bits/linux/mipsel.zig | 1 - lib/std/os/bits/linux/x86_64.zig | 1 - lib/std/os/linux.zig | 49 +++ lib/std/os/linux/i386.zig | 119 ++++++ lib/std/os/linux/tls.zig | 27 ++ lib/std/os/wasi.zig | 4 +- lib/std/special/c.zig | 43 ++ lib/std/thread.zig | 31 +- test/tests.zig | 44 +- 14 files changed, 960 insertions(+), 10 deletions(-) create mode 100644 lib/std/os/bits/linux/i386.zig create mode 100644 lib/std/os/linux/i386.zig diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6324c88d86..377e400d7c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -2417,6 +2417,12 @@ extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *con std.debug.warn("Segmentation fault at address 0x{x}\n", addr); switch (builtin.arch) { + .i386 => { + const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); + const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_EIP]); + const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_EBP]); + dumpStackTraceFromBase(bp, ip); + }, .x86_64 => { const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]); diff --git a/lib/std/os/bits/linux.zig b/lib/std/os/bits/linux.zig index 118223d03c..52cc5a710d 100644 --- a/lib/std/os/bits/linux.zig +++ b/lib/std/os/bits/linux.zig @@ -9,6 +9,7 @@ pub usingnamespace switch (builtin.arch) { }; pub usingnamespace switch (builtin.arch) { + .i386 => @import("linux/i386.zig"), .x86_64 => @import("linux/x86_64.zig"), .aarch64 => @import("linux/arm64.zig"), .arm => @import("linux/arm-eabi.zig"), diff --git a/lib/std/os/bits/linux/arm-eabi.zig b/lib/std/os/bits/linux/arm-eabi.zig index a5b6a31c60..de71607c30 100644 --- a/lib/std/os/bits/linux/arm-eabi.zig +++ b/lib/std/os/bits/linux/arm-eabi.zig @@ -466,7 +466,6 @@ pub const MAP_LOCKED = 0x2000; /// don't check for reservations pub const MAP_NORESERVE = 0x4000; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6"; diff --git a/lib/std/os/bits/linux/arm64.zig b/lib/std/os/bits/linux/arm64.zig index 84f621b07a..8dcebc5ddf 100644 --- a/lib/std/os/bits/linux/arm64.zig +++ b/lib/std/os/bits/linux/arm64.zig @@ -358,7 +358,6 @@ pub const MAP_LOCKED = 0x2000; /// don't check for reservations pub const MAP_NORESERVE = 0x4000; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6.39"; diff --git a/lib/std/os/bits/linux/i386.zig b/lib/std/os/bits/linux/i386.zig new file mode 100644 index 0000000000..2585785b13 --- /dev/null +++ b/lib/std/os/bits/linux/i386.zig @@ -0,0 +1,642 @@ +// i386-specific declarations that are intended to be imported into the POSIX namespace. +// This does include Linux-only APIs. + +const std = @import("../../../std.zig"); +const linux = std.os.linux; +const socklen_t = linux.socklen_t; +const iovec = linux.iovec; +const iovec_const = linux.iovec_const; +const uid_t = linux.uid_t; +const gid_t = linux.gid_t; +const stack_t = linux.stack_t; +const sigset_t = linux.sigset_t; + +pub const SYS_restart_syscall = 0; +pub const SYS_exit = 1; +pub const SYS_fork = 2; +pub const SYS_read = 3; +pub const SYS_write = 4; +pub const SYS_open = 5; +pub const SYS_close = 6; +pub const SYS_waitpid = 7; +pub const SYS_creat = 8; +pub const SYS_link = 9; +pub const SYS_unlink = 10; +pub const SYS_execve = 11; +pub const SYS_chdir = 12; +pub const SYS_time = 13; +pub const SYS_mknod = 14; +pub const SYS_chmod = 15; +pub const SYS_lchown = 16; +pub const SYS_break = 17; +pub const SYS_oldstat = 18; +pub const SYS_lseek = 19; +pub const SYS_getpid = 20; +pub const SYS_mount = 21; +pub const SYS_umount = 22; +pub const SYS_setuid = 23; +pub const SYS_getuid = 24; +pub const SYS_stime = 25; +pub const SYS_ptrace = 26; +pub const SYS_alarm = 27; +pub const SYS_oldfstat = 28; +pub const SYS_pause = 29; +pub const SYS_utime = 30; +pub const SYS_stty = 31; +pub const SYS_gtty = 32; +pub const SYS_access = 33; +pub const SYS_nice = 34; +pub const SYS_ftime = 35; +pub const SYS_sync = 36; +pub const SYS_kill = 37; +pub const SYS_rename = 38; +pub const SYS_mkdir = 39; +pub const SYS_rmdir = 40; +pub const SYS_dup = 41; +pub const SYS_pipe = 42; +pub const SYS_times = 43; +pub const SYS_prof = 44; +pub const SYS_brk = 45; +pub const SYS_setgid = 46; +pub const SYS_getgid = 47; +pub const SYS_signal = 48; +pub const SYS_geteuid = 49; +pub const SYS_getegid = 50; +pub const SYS_acct = 51; +pub const SYS_umount2 = 52; +pub const SYS_lock = 53; +pub const SYS_ioctl = 54; +pub const SYS_fcntl = 55; +pub const SYS_mpx = 56; +pub const SYS_setpgid = 57; +pub const SYS_ulimit = 58; +pub const SYS_oldolduname = 59; +pub const SYS_umask = 60; +pub const SYS_chroot = 61; +pub const SYS_ustat = 62; +pub const SYS_dup2 = 63; +pub const SYS_getppid = 64; +pub const SYS_getpgrp = 65; +pub const SYS_setsid = 66; +pub const SYS_sigaction = 67; +pub const SYS_sgetmask = 68; +pub const SYS_ssetmask = 69; +pub const SYS_setreuid = 70; +pub const SYS_setregid = 71; +pub const SYS_sigsuspend = 72; +pub const SYS_sigpending = 73; +pub const SYS_sethostname = 74; +pub const SYS_setrlimit = 75; +pub const SYS_getrlimit = 76; +pub const SYS_getrusage = 77; +pub const SYS_gettimeofday = 78; +pub const SYS_settimeofday = 79; +pub const SYS_getgroups = 80; +pub const SYS_setgroups = 81; +pub const SYS_select = 82; +pub const SYS_symlink = 83; +pub const SYS_oldlstat = 84; +pub const SYS_readlink = 85; +pub const SYS_uselib = 86; +pub const SYS_swapon = 87; +pub const SYS_reboot = 88; +pub const SYS_readdir = 89; +pub const SYS_mmap = 90; +pub const SYS_munmap = 91; +pub const SYS_truncate = 92; +pub const SYS_ftruncate = 93; +pub const SYS_fchmod = 94; +pub const SYS_fchown = 95; +pub const SYS_getpriority = 96; +pub const SYS_setpriority = 97; +pub const SYS_profil = 98; +pub const SYS_statfs = 99; +pub const SYS_fstatfs = 100; +pub const SYS_ioperm = 101; +pub const SYS_socketcall = 102; +pub const SYS_syslog = 103; +pub const SYS_setitimer = 104; +pub const SYS_getitimer = 105; +pub const SYS_stat = 106; +pub const SYS_lstat = 107; +pub const SYS_fstat = 108; +pub const SYS_olduname = 109; +pub const SYS_iopl = 110; +pub const SYS_vhangup = 111; +pub const SYS_idle = 112; +pub const SYS_vm86old = 113; +pub const SYS_wait4 = 114; +pub const SYS_swapoff = 115; +pub const SYS_sysinfo = 116; +pub const SYS_ipc = 117; +pub const SYS_fsync = 118; +pub const SYS_sigreturn = 119; +pub const SYS_clone = 120; +pub const SYS_setdomainname = 121; +pub const SYS_uname = 122; +pub const SYS_modify_ldt = 123; +pub const SYS_adjtimex = 124; +pub const SYS_mprotect = 125; +pub const SYS_sigprocmask = 126; +pub const SYS_create_module = 127; +pub const SYS_init_module = 128; +pub const SYS_delete_module = 129; +pub const SYS_get_kernel_syms = 130; +pub const SYS_quotactl = 131; +pub const SYS_getpgid = 132; +pub const SYS_fchdir = 133; +pub const SYS_bdflush = 134; +pub const SYS_sysfs = 135; +pub const SYS_personality = 136; +pub const SYS_afs_syscall = 137; +pub const SYS_setfsuid = 138; +pub const SYS_setfsgid = 139; +pub const SYS__llseek = 140; +pub const SYS_getdents = 141; +pub const SYS__newselect = 142; +pub const SYS_flock = 143; +pub const SYS_msync = 144; +pub const SYS_readv = 145; +pub const SYS_writev = 146; +pub const SYS_getsid = 147; +pub const SYS_fdatasync = 148; +pub const SYS__sysctl = 149; +pub const SYS_mlock = 150; +pub const SYS_munlock = 151; +pub const SYS_mlockall = 152; +pub const SYS_munlockall = 153; +pub const SYS_sched_setparam = 154; +pub const SYS_sched_getparam = 155; +pub const SYS_sched_setscheduler = 156; +pub const SYS_sched_getscheduler = 157; +pub const SYS_sched_yield = 158; +pub const SYS_sched_get_priority_max = 159; +pub const SYS_sched_get_priority_min = 160; +pub const SYS_sched_rr_get_interval = 161; +pub const SYS_nanosleep = 162; +pub const SYS_mremap = 163; +pub const SYS_setresuid = 164; +pub const SYS_getresuid = 165; +pub const SYS_vm86 = 166; +pub const SYS_query_module = 167; +pub const SYS_poll = 168; +pub const SYS_nfsservctl = 169; +pub const SYS_setresgid = 170; +pub const SYS_getresgid = 171; +pub const SYS_prctl = 172; +pub const SYS_rt_sigreturn = 173; +pub const SYS_rt_sigaction = 174; +pub const SYS_rt_sigprocmask = 175; +pub const SYS_rt_sigpending = 176; +pub const SYS_rt_sigtimedwait = 177; +pub const SYS_rt_sigqueueinfo = 178; +pub const SYS_rt_sigsuspend = 179; +pub const SYS_pread64 = 180; +pub const SYS_pwrite64 = 181; +pub const SYS_chown = 182; +pub const SYS_getcwd = 183; +pub const SYS_capget = 184; +pub const SYS_capset = 185; +pub const SYS_sigaltstack = 186; +pub const SYS_sendfile = 187; +pub const SYS_getpmsg = 188; +pub const SYS_putpmsg = 189; +pub const SYS_vfork = 190; +pub const SYS_ugetrlimit = 191; +pub const SYS_mmap2 = 192; +pub const SYS_truncate64 = 193; +pub const SYS_ftruncate64 = 194; +pub const SYS_stat64 = 195; +pub const SYS_lstat64 = 196; +pub const SYS_fstat64 = 197; +pub const SYS_lchown32 = 198; +pub const SYS_getuid32 = 199; +pub const SYS_getgid32 = 200; +pub const SYS_geteuid32 = 201; +pub const SYS_getegid32 = 202; +pub const SYS_setreuid32 = 203; +pub const SYS_setregid32 = 204; +pub const SYS_getgroups32 = 205; +pub const SYS_setgroups32 = 206; +pub const SYS_fchown32 = 207; +pub const SYS_setresuid32 = 208; +pub const SYS_getresuid32 = 209; +pub const SYS_setresgid32 = 210; +pub const SYS_getresgid32 = 211; +pub const SYS_chown32 = 212; +pub const SYS_setuid32 = 213; +pub const SYS_setgid32 = 214; +pub const SYS_setfsuid32 = 215; +pub const SYS_setfsgid32 = 216; +pub const SYS_pivot_root = 217; +pub const SYS_mincore = 218; +pub const SYS_madvise = 219; +pub const SYS_getdents64 = 220; +pub const SYS_fcntl64 = 221; +pub const SYS_gettid = 224; +pub const SYS_readahead = 225; +pub const SYS_setxattr = 226; +pub const SYS_lsetxattr = 227; +pub const SYS_fsetxattr = 228; +pub const SYS_getxattr = 229; +pub const SYS_lgetxattr = 230; +pub const SYS_fgetxattr = 231; +pub const SYS_listxattr = 232; +pub const SYS_llistxattr = 233; +pub const SYS_flistxattr = 234; +pub const SYS_removexattr = 235; +pub const SYS_lremovexattr = 236; +pub const SYS_fremovexattr = 237; +pub const SYS_tkill = 238; +pub const SYS_sendfile64 = 239; +pub const SYS_futex = 240; +pub const SYS_sched_setaffinity = 241; +pub const SYS_sched_getaffinity = 242; +pub const SYS_set_thread_area = 243; +pub const SYS_get_thread_area = 244; +pub const SYS_io_setup = 245; +pub const SYS_io_destroy = 246; +pub const SYS_io_getevents = 247; +pub const SYS_io_submit = 248; +pub const SYS_io_cancel = 249; +pub const SYS_fadvise64 = 250; +pub const SYS_exit_group = 252; +pub const SYS_lookup_dcookie = 253; +pub const SYS_epoll_create = 254; +pub const SYS_epoll_ctl = 255; +pub const SYS_epoll_wait = 256; +pub const SYS_remap_file_pages = 257; +pub const SYS_set_tid_address = 258; +pub const SYS_timer_create = 259; +pub const SYS_timer_settime = SYS_timer_create + 1; +pub const SYS_timer_gettime = SYS_timer_create + 2; +pub const SYS_timer_getoverrun = SYS_timer_create + 3; +pub const SYS_timer_delete = SYS_timer_create + 4; +pub const SYS_clock_settime = SYS_timer_create + 5; +pub const SYS_clock_gettime = SYS_timer_create + 6; +pub const SYS_clock_getres = SYS_timer_create + 7; +pub const SYS_clock_nanosleep = SYS_timer_create + 8; +pub const SYS_statfs64 = 268; +pub const SYS_fstatfs64 = 269; +pub const SYS_tgkill = 270; +pub const SYS_utimes = 271; +pub const SYS_fadvise64_64 = 272; +pub const SYS_vserver = 273; +pub const SYS_mbind = 274; +pub const SYS_get_mempolicy = 275; +pub const SYS_set_mempolicy = 276; +pub const SYS_mq_open = 277; +pub const SYS_mq_unlink = SYS_mq_open + 1; +pub const SYS_mq_timedsend = SYS_mq_open + 2; +pub const SYS_mq_timedreceive = SYS_mq_open + 3; +pub const SYS_mq_notify = SYS_mq_open + 4; +pub const SYS_mq_getsetattr = SYS_mq_open + 5; +pub const SYS_kexec_load = 283; +pub const SYS_waitid = 284; +pub const SYS_add_key = 286; +pub const SYS_request_key = 287; +pub const SYS_keyctl = 288; +pub const SYS_ioprio_set = 289; +pub const SYS_ioprio_get = 290; +pub const SYS_inotify_init = 291; +pub const SYS_inotify_add_watch = 292; +pub const SYS_inotify_rm_watch = 293; +pub const SYS_migrate_pages = 294; +pub const SYS_openat = 295; +pub const SYS_mkdirat = 296; +pub const SYS_mknodat = 297; +pub const SYS_fchownat = 298; +pub const SYS_futimesat = 299; +pub const SYS_fstatat64 = 300; +pub const SYS_unlinkat = 301; +pub const SYS_renameat = 302; +pub const SYS_linkat = 303; +pub const SYS_symlinkat = 304; +pub const SYS_readlinkat = 305; +pub const SYS_fchmodat = 306; +pub const SYS_faccessat = 307; +pub const SYS_pselect6 = 308; +pub const SYS_ppoll = 309; +pub const SYS_unshare = 310; +pub const SYS_set_robust_list = 311; +pub const SYS_get_robust_list = 312; +pub const SYS_splice = 313; +pub const SYS_sync_file_range = 314; +pub const SYS_tee = 315; +pub const SYS_vmsplice = 316; +pub const SYS_move_pages = 317; +pub const SYS_getcpu = 318; +pub const SYS_epoll_pwait = 319; +pub const SYS_utimensat = 320; +pub const SYS_signalfd = 321; +pub const SYS_timerfd_create = 322; +pub const SYS_eventfd = 323; +pub const SYS_fallocate = 324; +pub const SYS_timerfd_settime = 325; +pub const SYS_timerfd_gettime = 326; +pub const SYS_signalfd4 = 327; +pub const SYS_eventfd2 = 328; +pub const SYS_epoll_create1 = 329; +pub const SYS_dup3 = 330; +pub const SYS_pipe2 = 331; +pub const SYS_inotify_init1 = 332; +pub const SYS_preadv = 333; +pub const SYS_pwritev = 334; +pub const SYS_rt_tgsigqueueinfo = 335; +pub const SYS_perf_event_open = 336; +pub const SYS_recvmmsg = 337; +pub const SYS_fanotify_init = 338; +pub const SYS_fanotify_mark = 339; +pub const SYS_prlimit64 = 340; +pub const SYS_name_to_handle_at = 341; +pub const SYS_open_by_handle_at = 342; +pub const SYS_clock_adjtime = 343; +pub const SYS_syncfs = 344; +pub const SYS_sendmmsg = 345; +pub const SYS_setns = 346; +pub const SYS_process_vm_readv = 347; +pub const SYS_process_vm_writev = 348; +pub const SYS_kcmp = 349; +pub const SYS_finit_module = 350; +pub const SYS_sched_setattr = 351; +pub const SYS_sched_getattr = 352; +pub const SYS_renameat2 = 353; +pub const SYS_seccomp = 354; +pub const SYS_getrandom = 355; +pub const SYS_memfd_create = 356; +pub const SYS_bpf = 357; +pub const SYS_execveat = 358; +pub const SYS_socket = 359; +pub const SYS_socketpair = 360; +pub const SYS_bind = 361; +pub const SYS_connect = 362; +pub const SYS_listen = 363; +pub const SYS_accept4 = 364; +pub const SYS_getsockopt = 365; +pub const SYS_setsockopt = 366; +pub const SYS_getsockname = 367; +pub const SYS_getpeername = 368; +pub const SYS_sendto = 369; +pub const SYS_sendmsg = 370; +pub const SYS_recvfrom = 371; +pub const SYS_recvmsg = 372; +pub const SYS_shutdown = 373; +pub const SYS_userfaultfd = 374; +pub const SYS_membarrier = 375; +pub const SYS_mlock2 = 376; +pub const SYS_copy_file_range = 377; +pub const SYS_preadv2 = 378; +pub const SYS_pwritev2 = 379; +pub const SYS_pkey_mprotect = 380; +pub const SYS_pkey_alloc = 381; +pub const SYS_pkey_free = 382; +pub const SYS_statx = 383; +pub const SYS_arch_prctl = 384; +pub const SYS_io_pgetevents = 385; +pub const SYS_rseq = 386; +pub const SYS_semget = 393; +pub const SYS_semctl = 394; +pub const SYS_shmget = 395; +pub const SYS_shmctl = 396; +pub const SYS_shmat = 397; +pub const SYS_shmdt = 398; +pub const SYS_msgget = 399; +pub const SYS_msgsnd = 400; +pub const SYS_msgrcv = 401; +pub const SYS_msgctl = 402; +pub const SYS_clock_gettime64 = 403; +pub const SYS_clock_settime64 = 404; +pub const SYS_clock_adjtime64 = 405; +pub const SYS_clock_getres_time64 = 406; +pub const SYS_clock_nanosleep_time64 = 407; +pub const SYS_timer_gettime64 = 408; +pub const SYS_timer_settime64 = 409; +pub const SYS_timerfd_gettime64 = 410; +pub const SYS_timerfd_settime64 = 411; +pub const SYS_utimensat_time64 = 412; +pub const SYS_pselect6_time64 = 413; +pub const SYS_ppoll_time64 = 414; +pub const SYS_io_pgetevents_time64 = 416; +pub const SYS_recvmmsg_time64 = 417; +pub const SYS_mq_timedsend_time64 = 418; +pub const SYS_mq_timedreceive_time64 = 419; +pub const SYS_semtimedop_time64 = 420; +pub const SYS_rt_sigtimedwait_time64 = 421; +pub const SYS_futex_time64 = 422; +pub const SYS_sched_rr_get_interval_time64 = 423; +pub const SYS_pidfd_send_signal = 424; +pub const SYS_io_uring_setup = 425; +pub const SYS_io_uring_enter = 426; +pub const SYS_io_uring_register = 427; +pub const SYS_open_tree = 428; +pub const SYS_move_mount = 429; +pub const SYS_fsopen = 430; +pub const SYS_fsconfig = 431; +pub const SYS_fsmount = 432; +pub const SYS_fspick = 433; + +pub const O_CREAT = 0o100; +pub const O_EXCL = 0o200; +pub const O_NOCTTY = 0o400; +pub const O_TRUNC = 0o1000; +pub const O_APPEND = 0o2000; +pub const O_NONBLOCK = 0o4000; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0o4010000; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o200000; +pub const O_NOFOLLOW = 0o400000; +pub const O_CLOEXEC = 0o2000000; + +pub const O_ASYNC = 0o20000; +pub const O_DIRECT = 0o40000; +pub const O_LARGEFILE = 0o100000; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 12; +pub const F_SETLK = 13; +pub const F_SETLKW = 14; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +pub const MAP_NORESERVE = 0x4000; +pub const MAP_GROWSDOWN = 0x0100; +pub const MAP_DENYWRITE = 0x0800; +pub const MAP_EXECUTABLE = 0x1000; +pub const MAP_LOCKED = 0x2000; +pub const MAP_32BIT = 0x40; + +pub const MMAP2_UNIT = 4096; + +pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; +pub const VDSO_CGT_VER = "LINUX_2.6"; + +pub const msghdr = extern struct { + msg_name: ?*sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const msghdr_const = extern struct { + msg_name: ?*const sockaddr, + msg_namelen: socklen_t, + msg_iov: [*]iovec_const, + msg_iovlen: i32, + msg_control: ?*c_void, + msg_controllen: socklen_t, + msg_flags: i32, +}; + +pub const blksize_t = i32; +pub const nlink_t = u32; +pub const time_t = isize; +pub const mode_t = u32; +pub const off_t = i64; +pub const ino_t = u64; +pub const dev_t = u64; +pub const blkcnt_t = i64; + +/// Renamed to Stat to not conflict with the stat function. +/// atime, mtime, and ctime have functions to return `timespec`, +/// because although this is a POSIX API, the layout and names of +/// the structs are inconsistent across operating systems, and +/// in C, macros are used to hide the differences. Here we use +/// methods to accomplish this. +pub const Stat = extern struct { + dev: dev_t, + __dev_padding: u32, + __ino_truncated: u32, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + rdev: dev_t, + __rdev_padding: u32, + size: off_t, + blksize: blksize_t, + blocks: blkcnt_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + ino: ino_t, + + pub fn atime(self: Stat) timespec { + return self.atim; + } + + pub fn mtime(self: Stat) timespec { + return self.mtim; + } + + pub fn ctime(self: Stat) timespec { + return self.ctim; + } +}; + +pub const timespec = extern struct { + tv_sec: i32, + tv_nsec: i32, +}; + +pub const timeval = extern struct { + tv_sec: i32, + tv_usec: i32, +}; + +pub const timezone = extern struct { + tz_minuteswest: i32, + tz_dsttime: i32, +}; + +pub const mcontext_t = extern struct { + gregs: [19]usize, + fpregs: [*]u8, + oldmask: usize, + cr2: usize, +}; + +pub const REG_GS = 0; +pub const REG_FS = 1; +pub const REG_ES = 2; +pub const REG_DS = 3; +pub const REG_EDI = 4; +pub const REG_ESI = 5; +pub const REG_EBP = 6; +pub const REG_ESP = 7; +pub const REG_EBX = 8; +pub const REG_EDX = 9; +pub const REG_ECX = 10; +pub const REG_EAX = 11; +pub const REG_TRAPNO = 12; +pub const REG_ERR = 13; +pub const REG_EIP = 14; +pub const REG_CS = 15; +pub const REG_EFL = 16; +pub const REG_UESP = 17; +pub const REG_SS = 18; + +pub const ucontext_t = extern struct { + flags: usize, + link: *ucontext_t, + stack: stack_t, + mcontext: mcontext_t, + sigmask: sigset_t, + regspace: [64]u64, +}; + +pub const Elf_Symndx = u32; + +pub const user_desc = packed struct { + entry_number: u32, + base_addr: u32, + limit: u32, + seg_32bit: u1, + contents: u2, + read_exec_only: u1, + limit_in_pages: u1, + seg_not_present: u1, + useable: u1, +}; + +// socketcall() call numbers +pub const SC_socket = 1; +pub const SC_bind = 2; +pub const SC_connect = 3; +pub const SC_listen = 4; +pub const SC_accept = 5; +pub const SC_getsockname = 6; +pub const SC_getpeername = 7; +pub const SC_socketpair = 8; +pub const SC_send = 9; +pub const SC_recv = 10; +pub const SC_sendto = 11; +pub const SC_recvfrom = 12; +pub const SC_shutdown = 13; +pub const SC_setsockopt = 14; +pub const SC_getsockopt = 15; +pub const SC_sendmsg = 16; +pub const SC_recvmsg = 17; +pub const SC_accept4 = 18; +pub const SC_recvmmsg = 19; +pub const SC_sendmmsg = 20; diff --git a/lib/std/os/bits/linux/mipsel.zig b/lib/std/os/bits/linux/mipsel.zig index c3821791ce..638a4b1de7 100644 --- a/lib/std/os/bits/linux/mipsel.zig +++ b/lib/std/os/bits/linux/mipsel.zig @@ -454,7 +454,6 @@ pub const SO_PEERSEC = 30; pub const SO_SNDBUFFORCE = 31; pub const SO_RCVBUFFORCE = 33; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__kernel_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6.39"; diff --git a/lib/std/os/bits/linux/x86_64.zig b/lib/std/os/bits/linux/x86_64.zig index 7b1ad4dd5c..da3caf2c88 100644 --- a/lib/std/os/bits/linux/x86_64.zig +++ b/lib/std/os/bits/linux/x86_64.zig @@ -420,7 +420,6 @@ pub const MAP_LOCKED = 0x2000; /// don't check for reservations pub const MAP_NORESERVE = 0x4000; -pub const VDSO_USEFUL = true; pub const VDSO_CGT_SYM = "__vdso_clock_gettime"; pub const VDSO_CGT_VER = "LINUX_2.6"; pub const VDSO_GETCPU_SYM = "__vdso_getcpu"; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 1de66b8d2e..e3d84e1e63 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -14,6 +14,7 @@ const vdso = @import("linux/vdso.zig"); const dl = @import("../dynamic_library.zig"); pub usingnamespace switch (builtin.arch) { + .i386 => @import("linux/i386.zig"), .x86_64 => @import("linux/x86_64.zig"), .aarch64 => @import("linux/arm64.zig"), .arm => @import("linux/arm-eabi.zig"), @@ -743,26 +744,44 @@ pub fn sigismember(set: *const sigset_t, sig: u6) bool { } pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_getsockname, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) }); + } return syscall3(SYS_getsockname, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len)); } pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_getpeername, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) }); + } return syscall3(SYS_getpeername, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len)); } pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_socket, &[3]usize{ domain, socket_type, protocol }); + } return syscall3(SYS_socket, domain, socket_type, protocol); } pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_setsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen) }); + } return syscall5(SYS_setsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); } pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_getsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen) }); + } return syscall5(SYS_getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); } pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); + } return syscall3(SYS_sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags); } @@ -807,42 +826,72 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize } pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_connect, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len }); + } return syscall3(SYS_connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len); } pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags }); + } return syscall3(SYS_recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags); } pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_recvfrom, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen) }); + } return syscall6(SYS_recvfrom, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); } pub fn shutdown(fd: i32, how: i32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_shutdown, &[2]usize{ @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how)) }); + } return syscall2(SYS_shutdown, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how))); } pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_bind, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len) }); + } return syscall3(SYS_bind, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len)); } pub fn listen(fd: i32, backlog: u32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_listen, &[2]usize{ @bitCast(usize, @as(isize, fd)), backlog }); + } return syscall2(SYS_listen, @bitCast(usize, @as(isize, fd)), backlog); } pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_sendto, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen) }); + } return syscall6(SYS_sendto, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); } pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_socketpair, &[4]usize{ @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]) }); + } return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); } pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + if (builtin.arch == .i386) { + return socketcall(SC_accept, &[4]usize{ fd, addr, len, 0 }); + } return accept4(fd, addr, len, 0); } pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { + if (builtin.arch == .i386) { + return socketcall(SC_accept4, &[4]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags }); + } return syscall4(SYS_accept4, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags); } diff --git a/lib/std/os/linux/i386.zig b/lib/std/os/linux/i386.zig new file mode 100644 index 0000000000..3345f9904d --- /dev/null +++ b/lib/std/os/linux/i386.zig @@ -0,0 +1,119 @@ +usingnamespace @import("../bits.zig"); + +pub fn syscall0(number: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number) + : "memory" + ); +} + +pub fn syscall1(number: usize, arg1: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1) + : "memory" + ); +} + +pub fn syscall2(number: usize, arg1: usize, arg2: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2) + : "memory" + ); +} + +pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3) + : "memory" + ); +} + +pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3), + [arg4] "{esi}" (arg4) + : "memory" + ); +} + +pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3), + [arg4] "{esi}" (arg4), + [arg5] "{edi}" (arg5) + : "memory" + ); +} + +pub fn syscall6( + number: usize, + arg1: usize, + arg2: usize, + arg3: usize, + arg4: usize, + arg5: usize, + arg6: usize, +) usize { + return asm volatile ( + \\ push %%ebp + \\ mov %[arg6], %%ebp + \\ int $0x80 + \\ pop %%ebp + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (number), + [arg1] "{ebx}" (arg1), + [arg2] "{ecx}" (arg2), + [arg3] "{edx}" (arg3), + [arg4] "{esi}" (arg4), + [arg5] "{edi}" (arg5), + [arg6] "rm" (arg6) + : "memory" + ); +} + +pub fn socketcall(call: usize, args: [*]usize) usize { + return asm volatile ("int $0x80" + : [ret] "={eax}" (-> usize) + : [number] "{eax}" (@as(usize, SYS_socketcall)), + [arg1] "{ebx}" (call), + [arg2] "{ecx}" (@ptrToInt(args)) + : "memory" + ); +} + +/// This matches the libc clone function. +pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize; + +pub nakedcc fn restore() void { + return asm volatile ("int $0x80" + : + : [number] "{eax}" (@as(usize, SYS_sigreturn)) + : "memory" + ); +} + +pub nakedcc fn restore_rt() void { + return asm volatile ("int $0x80" + : + : [number] "{eax}" (@as(usize, SYS_rt_sigreturn)) + : "memory" + ); +} diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 849a9ef405..c340a977e0 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -109,12 +109,38 @@ const TLSImage = struct { tcb_offset: usize, dtv_offset: usize, data_offset: usize, + // Only used on the i386 architecture + gdt_entry_number: usize, }; pub var tls_image: ?TLSImage = null; pub fn setThreadPointer(addr: usize) void { switch (builtin.arch) { + .i386 => { + var user_desc = std.os.linux.user_desc{ + .entry_number = tls_image.?.gdt_entry_number, + .base_addr = addr, + .limit = 0xfffff, + .seg_32bit = 1, + .contents = 0, // Data + .read_exec_only = 0, + .limit_in_pages = 1, + .seg_not_present = 0, + .useable = 1, + }; + const rc = std.os.linux.syscall1(std.os.linux.SYS_set_thread_area, @ptrToInt(&user_desc)); + assert(rc == 0); + + const gdt_entry_number = user_desc.entry_number; + // We have to keep track of our slot as it's also needed for clone() + tls_image.?.gdt_entry_number = gdt_entry_number; + // Update the %gs selector + asm volatile ("movl %[gs_val], %%gs" + : + : [gs_val] "r" (gdt_entry_number << 3 | 3) + ); + }, .x86_64 => { const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, std.os.linux.ARCH_SET_FS, addr); assert(rc == 0); @@ -238,6 +264,7 @@ pub fn initTLS() ?*elf.Phdr { .tcb_offset = tcb_offset, .dtv_offset = dtv_offset, .data_offset = data_offset, + .gdt_entry_number = @bitCast(usize, @as(isize, -1)), }; } diff --git a/lib/std/os/wasi.zig b/lib/std/os/wasi.zig index c417097c7b..6c981dfc72 100644 --- a/lib/std/os/wasi.zig +++ b/lib/std/os/wasi.zig @@ -12,8 +12,8 @@ comptime { assert(@alignOf(u16) == 2); assert(@alignOf(i32) == 4); assert(@alignOf(u32) == 4); - assert(@alignOf(i64) == 8); - assert(@alignOf(u64) == 8); + // assert(@alignOf(i64) == 8); + // assert(@alignOf(u64) == 8); } pub const iovec_t = iovec; diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index 8ac00c4e04..c0eaf74a19 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -197,6 +197,49 @@ extern fn __stack_chk_fail() noreturn { // across .o file boundaries. fix comptime @ptrCast of nakedcc functions. nakedcc fn clone() void { switch (builtin.arch) { + .i386 => { + // __clone(func, stack, flags, arg, ptid, tls, ctid) + // +8, +12, +16, +20, +24, +28, +32 + // syscall(SYS_clone, flags, stack, ptid, tls, ctid) + // eax, ebx, ecx, edx, esi, edi + asm volatile ( + \\ push %%ebp + \\ mov %%esp,%%ebp + \\ push %%ebx + \\ push %%esi + \\ push %%edi + \\ // Setup the arguments + \\ mov 16(%%ebp),%%ebx + \\ mov 12(%%ebp),%%ecx + \\ and $-16,%%ecx + \\ sub $20,%%ecx + \\ mov 20(%%ebp),%%eax + \\ mov %%eax,4(%%ecx) + \\ mov 8(%%ebp),%%eax + \\ mov %%eax,0(%%ecx) + \\ mov 24(%%ebp),%%edx + \\ mov 28(%%ebp),%%esi + \\ mov 32(%%ebp),%%edi + \\ mov $120,%%eax + \\ int $128 + \\ test %%eax,%%eax + \\ jnz 1f + \\ pop %%eax + \\ xor %%ebp,%%ebp + \\ call *%%eax + \\ mov %%eax,%%ebx + \\ xor %%eax,%%eax + \\ inc %%eax + \\ int $128 + \\ hlt + \\1: + \\ pop %%edi + \\ pop %%esi + \\ pop %%ebx + \\ pop %%ebp + \\ ret + ); + }, .x86_64 => { asm volatile ( \\ xor %%eax,%%eax diff --git a/lib/std/thread.zig b/lib/std/thread.zig index fe976a6839..571773dcae 100644 --- a/lib/std/thread.zig +++ b/lib/std/thread.zig @@ -314,11 +314,38 @@ pub const Thread = struct { os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID | os.CLONE_DETACHED; var newtls: usize = undefined; + // This structure is only needed when targeting i386 + var user_desc: if (builtin.arch == .i386) os.linux.user_desc else void = undefined; + if (os.linux.tls.tls_image) |tls_img| { - newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset); + if (builtin.arch == .i386) { + user_desc = os.linux.user_desc{ + .entry_number = tls_img.gdt_entry_number, + .base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset), + .limit = 0xfffff, + .seg_32bit = 1, + .contents = 0, // Data + .read_exec_only = 0, + .limit_in_pages = 1, + .seg_not_present = 0, + .useable = 1, + }; + newtls = @ptrToInt(&user_desc); + } else { + newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset); + } flags |= os.CLONE_SETTLS; } - const rc = os.linux.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); + + const rc = os.linux.clone( + MainFuncs.linuxThreadMain, + mmap_addr + stack_end_offset, + flags, + arg, + &thread_ptr.data.handle, + newtls, + &thread_ptr.data.handle, + ); switch (os.errno(rc)) { 0 => return thread_ptr, os.EAGAIN => return error.ThreadQuotaExceeded, diff --git a/test/tests.zig b/test/tests.zig index 2bb0f33487..929af9abf9 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -70,6 +70,36 @@ const test_targets = [_]TestTarget{ .link_libc = true, }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .linux, + .arch = .i386, + .abi = .none, + }, + }, + }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .linux, + .arch = .i386, + .abi = .gnu, + }, + }, + .link_libc = true, + }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .linux, + .arch = .i386, + .abi = .musl, + }, + }, + .link_libc = true, + }, + TestTarget{ .target = Target{ .Cross = CrossTarget{ @@ -140,6 +170,16 @@ const test_targets = [_]TestTarget{ }, }, }, + TestTarget{ + .target = Target{ + .Cross = CrossTarget{ + .os = .linux, + .arch = .mipsel, + .abi = .gnu, + }, + }, + .link_libc = true, + }, TestTarget{ .target = Target{ .Cross = CrossTarget{ @@ -411,7 +451,7 @@ pub fn addPkgTests( const ArchTag = @TagType(builtin.Arch); if (test_target.disable_native and test_target.target.getOs() == builtin.os and - @as(ArchTag,test_target.target.getArch()) == @as(ArchTag,builtin.arch)) + @as(ArchTag, test_target.target.getArch()) == @as(ArchTag, builtin.arch)) { continue; } @@ -429,7 +469,7 @@ pub fn addPkgTests( "bare"; const triple_prefix = if (test_target.target == .Native) - @as([]const u8,"native") + @as([]const u8, "native") else test_target.target.zigTripleNoSubArch(b.allocator) catch unreachable; From d0c6728d59c6061d18337dde77670551721cde95 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 30 Nov 2019 18:55:22 +0100 Subject: [PATCH 30/38] Eat shit and die glibc --- test/tests.zig | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/tests.zig b/test/tests.zig index 929af9abf9..b14706bea9 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -79,16 +79,6 @@ const test_targets = [_]TestTarget{ }, }, }, - TestTarget{ - .target = Target{ - .Cross = CrossTarget{ - .os = .linux, - .arch = .i386, - .abi = .gnu, - }, - }, - .link_libc = true, - }, TestTarget{ .target = Target{ .Cross = CrossTarget{ From d039fed831cfc219821b58f1d819d79ad49dc652 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 30 Nov 2019 13:32:11 -0500 Subject: [PATCH 31/38] introduce std.fs.Dir.openFile and std.fs.Dir.createFile These functions have flags parameters which cover all the use cases. The other functions are now deprecated. --- lib/std/fs.zig | 103 ++++++++++++++++++++++++++++++----- lib/std/fs/file.zig | 128 +++++++++++++++++++------------------------- 2 files changed, 144 insertions(+), 87 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index f580cf2045..ec143056f1 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -698,29 +698,104 @@ pub const Dir = struct { self.* = undefined; } - /// Call `File.close` on the result when done. - pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File { + /// Opens a file for reading or writing, without attempting to create a new file. + /// Call `File.close` to release the resource. + pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { if (builtin.os == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); - return self.openReadW(&path_w); + return self.openFileW(&path_w, flags); } const path_c = try os.toPosixPath(sub_path); - return self.openReadC(&path_c); + return self.openFileC(&path_c, flags); } - /// Call `File.close` on the result when done. - pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File { + /// Same as `openFile` but the path parameter is null-terminated. + pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { if (builtin.os == .windows) { const path_w = try os.windows.cStrToPrefixedFileW(sub_path); - return self.openReadW(&path_w); + return self.openFileW(&path_w, flags); } const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; - const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC; - const fd = try os.openatC(self.fd, sub_path, flags, 0); - return File.openHandle(fd); + const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) + @as(u32, os.O_RDWR) + else if (flags.write) + @as(u32, os.O_WRONLY) + else + @as(u32, os.O_RDONLY); + const fd = try os.openatC(self.fd, sub_path, os_flags, 0); + return File{ .handle = fd }; } - pub fn openReadW(self: Dir, sub_path_w: [*:0]const u16) File.OpenError!File { + /// Same as `openFile` but the path parameter is WTF-16 encoded. + pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { + const w = os.windows; + const access_mask = w.SYNCHRONIZE | + (if (flags.read) @as(u32, w.GENERIC_READ) else 0) | + (if (flags.write) @as(u32, w.GENERIC_WRITE) else 0); + return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN); + } + + /// Creates, opens, or overwrites a file with write access. + /// Call `File.close` on the result when done. + pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { + if (builtin.os == .windows) { + const path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.createFileW(&path_w, flags); + } + const path_c = try os.toPosixPath(sub_path); + return self.createFileC(&path_c, flags); + } + + /// Same as `createFile` but the path parameter is null-terminated. + pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { + if (builtin.os == .windows) { + const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); + return self.createFileW(&path_w, flags); + } + const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; + const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | + (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | + (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | + (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); + const fd = try os.openatC(self.fd, sub_path_c, os_flags, flags.mode); + return File{ .handle = fd }; + } + + /// Same as `createFile` but the path parameter is WTF-16 encoded. + pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { + const w = os.windows; + const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | + (if (flags.read) @as(u32, w.GENERIC_READ) else 0); + const creation = if (flags.exclusive) + @as(u32, w.FILE_CREATE) + else if (flags.truncate) + @as(u32, w.FILE_OVERWRITE_IF) + else + @as(u32, w.FILE_OPEN_IF); + return self.openFileWindows(sub_path_w, access_mask, creation); + } + + /// Deprecated; call `openFile` directly. + pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File { + return self.openFile(sub_path, .{}); + } + + /// Deprecated; call `openFileC` directly. + pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File { + return self.openFileC(sub_path, .{}); + } + + /// Deprecated; call `openFileW` directly. + pub fn openReadW(self: Dir, sub_path: [*:0]const u16) File.OpenError!File { + return self.openFileW(sub_path, .{}); + } + + pub fn openFileWindows( + self: Dir, + sub_path_w: [*:0]const u16, + access_mask: os.windows.ACCESS_MASK, + creation: os.windows.ULONG, + ) File.OpenError!File { const w = os.windows; var result = File{ .handle = undefined }; @@ -750,13 +825,13 @@ pub const Dir = struct { var io: w.IO_STATUS_BLOCK = undefined; const rc = w.ntdll.NtCreateFile( &result.handle, - w.GENERIC_READ | w.SYNCHRONIZE, + access_mask, &attr, &io, null, w.FILE_ATTRIBUTE_NORMAL, - w.FILE_SHARE_READ, - w.FILE_OPEN, + w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE, + creation, w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT, null, 0, diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 9cfdad3885..52e5f3e49e 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -25,105 +25,87 @@ pub const File = struct { pub const OpenError = windows.CreateFileError || os.OpenError; - /// Deprecated; call `std.fs.Dir.openRead` directly. + /// TODO https://github.com/ziglang/zig/issues/3802 + pub const OpenFlags = struct { + read: bool = true, + write: bool = false, + }; + + /// TODO https://github.com/ziglang/zig/issues/3802 + pub const CreateFlags = struct { + /// Whether the file will be created with read access. + read: bool = false, + + /// If the file already exists, and is a regular file, and the access + /// mode allows writing, it will be truncated to length 0. + truncate: bool = true, + + /// Ensures that this open call creates the file, otherwise causes + /// `error.FileAlreadyExists` to be returned. + exclusive: bool = false, + + /// For POSIX systems this is the file system mode the file will + /// be created with. + mode: Mode = default_mode, + }; + + /// Deprecated; call `std.fs.Dir.openFile` directly. pub fn openRead(path: []const u8) OpenError!File { - return std.fs.Dir.cwd().openRead(path); + return std.fs.Dir.cwd().openFile(path, .{}); } - /// Deprecated; call `std.fs.Dir.openReadC` directly. + /// Deprecated; call `std.fs.Dir.openFileC` directly. pub fn openReadC(path_c: [*:0]const u8) OpenError!File { - return std.fs.Dir.cwd().openReadC(path_c); + return std.fs.Dir.cwd().openFileC(path_c, .{}); } - /// Deprecated; call `std.fs.Dir.openReadW` directly. + /// Deprecated; call `std.fs.Dir.openFileW` directly. pub fn openReadW(path_w: [*]const u16) OpenError!File { - return std.fs.Dir.cwd().openReadW(path_w); + return std.fs.Dir.cwd().openFileW(path_w, .{}); } - /// Calls `openWriteMode` with `default_mode` for the mode. - /// TODO: deprecate this and move it to `std.fs.Dir`. + /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWrite(path: []const u8) OpenError!File { - return openWriteMode(path, default_mode); + return std.fs.Dir.cwd().createFile(path, .{}); } - /// If the path does not exist it will be created. - /// If a file already exists in the destination it will be truncated. - /// Call close to clean up. - /// TODO: deprecate this and move it to `std.fs.Dir`. + /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File { - if (builtin.os == .windows) { - const path_w = try windows.sliceToPrefixedFileW(path); - return openWriteModeW(&path_w, file_mode); - } - const path_c = try os.toPosixPath(path); - return openWriteModeC(&path_c, file_mode); + return std.fs.Dir.cwd().createFile(path, .{ .mode = file_mode }); } - /// Same as `openWriteMode` except `path` is null-terminated. - /// TODO: deprecate this and move it to `std.fs.Dir`. - pub fn openWriteModeC(path: [*:0]const u8, file_mode: Mode) OpenError!File { - if (builtin.os == .windows) { - const path_w = try windows.cStrToPrefixedFileW(path); - return openWriteModeW(&path_w, file_mode); - } - const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; - const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC; - const fd = try os.openC(path, flags, file_mode); - return openHandle(fd); + /// Deprecated; call `std.fs.Dir.createFileC` directly. + pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { + return std.fs.Dir.cwd().createFileC(path_c, .{ .mode = file_mode }); } - /// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded - /// TODO: deprecate this and move it to `std.fs.Dir`. + /// Deprecated; call `std.fs.Dir.createFileW` directly. pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { - const handle = try windows.CreateFileW( - path_w, - windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, - null, - windows.CREATE_ALWAYS, - windows.FILE_ATTRIBUTE_NORMAL, - null, - ); - return openHandle(handle); + return std.fs.Dir.cwd().createFileW(path_w, .{ .mode = file_mode }); } - /// If the path does not exist it will be created. - /// If a file already exists in the destination this returns OpenError.PathAlreadyExists - /// Call close to clean up. - /// TODO: deprecate this and move it to `std.fs.Dir`. + /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { - if (builtin.os == .windows) { - const path_w = try windows.sliceToPrefixedFileW(path); - return openWriteNoClobberW(&path_w, file_mode); - } - const path_c = try os.toPosixPath(path); - return openWriteNoClobberC(&path_c, file_mode); + return std.fs.Dir.cwd().createFile(path, .{ + .mode = file_mode, + .exclusive = true, + }); } - /// TODO: deprecate this and move it to `std.fs.Dir`. - pub fn openWriteNoClobberC(path: [*:0]const u8, file_mode: Mode) OpenError!File { - if (builtin.os == .windows) { - const path_w = try windows.cStrToPrefixedFileW(path); - return openWriteNoClobberW(&path_w, file_mode); - } - const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; - const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL; - const fd = try os.openC(path, flags, file_mode); - return openHandle(fd); + /// Deprecated; call `std.fs.Dir.createFileC` directly. + pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { + return std.fs.Dir.cwd().createFileC(path_c, .{ + .mode = file_mode, + .exclusive = true, + }); } - /// TODO: deprecate this and move it to `std.fs.Dir`. + /// Deprecated; call `std.fs.Dir.createFileW` directly. pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { - const handle = try windows.CreateFileW( - path_w, - windows.GENERIC_WRITE, - windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE, - null, - windows.CREATE_NEW, - windows.FILE_ATTRIBUTE_NORMAL, - null, - ); - return openHandle(handle); + return std.fs.Dir.cwd().createFileW(path_w, .{ + .mode = file_mode, + .exclusive = true, + }); } pub fn openHandle(handle: os.fd_t) File { From 6f105b5ae7b66ca7e46347b97e5630b866087b87 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 30 Nov 2019 21:14:32 +0100 Subject: [PATCH 32/38] Implement target_dynamic_linker for mips --- src/target.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/target.cpp b/src/target.cpp index 538f836f24..3a6d8d19f4 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1306,6 +1306,8 @@ const char *target_dynamic_linker(const ZigTarget *target) { case ZigLLVM_mips: case ZigLLVM_mipsel: + return "/lib/ld.so.1"; + case ZigLLVM_mips64: case ZigLLVM_mips64el: zig_panic("TODO implement target_dynamic_linker for mips"); From 413f9a5cfc9e867e3bc69b47b38c62b52a52d5e9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 30 Nov 2019 15:14:04 -0500 Subject: [PATCH 33/38] move `std.fs.Dir.cwd` to `std.fs.cwd` update to non-deprecated std.fs APIs throughout the codebase Related: #3811 --- lib/std/build.zig | 2 +- lib/std/debug.zig | 4 +- lib/std/fs.zig | 172 ++++++++++++++++++++++++++++------- lib/std/fs/file.zig | 20 ++-- lib/std/fs/path.zig | 33 ++++++- lib/std/io.zig | 11 +-- lib/std/io/test.zig | 28 +++--- lib/std/net.zig | 4 +- lib/std/os.zig | 12 ++- lib/std/os/linux/test.zig | 7 +- lib/std/os/test.zig | 4 +- lib/std/pdb.zig | 3 +- src-self-hosted/main.zig | 2 +- src-self-hosted/stage1.zig | 2 +- test/standalone/cat/main.zig | 8 +- 15 files changed, 224 insertions(+), 88 deletions(-) diff --git a/lib/std/build.zig b/lib/std/build.zig index 38025b83bc..026e889b7b 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -2416,7 +2416,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 { const path_file = try fs.path.join(allocator, [_][]const u8{ appdata_path, "vcpkg.path.txt" }); defer allocator.free(path_file); - const file = fs.File.openRead(path_file) catch return null; + const file = fs.cwd().openFile(path_file, .{}) catch return null; defer file.close(); const size = @intCast(usize, try file.getEndPos()); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 6324c88d86..3c842f3ac1 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { } fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { - var f = try File.openRead(line_info.file_name); + var f = try fs.cwd().openFile(line_info.file_name, .{}); defer f.close(); // TODO fstat and make sure that the file has the correct size @@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx)); gop.kv.value = MachOFile{ - .bytes = try std.fs.Dir.cwd().readFileAllocAligned( + .bytes = try std.fs.cwd().readFileAllocAligned( di.ofiles.allocator, ofile_path, maxInt(usize), diff --git a/lib/std/fs.zig b/lib/std/fs.zig index ec143056f1..0f8da873a6 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File; pub const symLink = os.symlink; pub const symLinkC = os.symlinkC; -pub const deleteFile = os.unlink; -pub const deleteFileC = os.unlinkC; pub const rename = os.rename; pub const renameC = os.renameC; pub const renameW = os.renameW; @@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { /// If any of the directories do not exist for dest_path, they are created. /// TODO https://github.com/ziglang/zig/issues/2885 pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { - var src_file = try File.openRead(source_path); + const my_cwd = cwd(); + + var src_file = try my_cwd.openFile(source_path, .{}); defer src_file.close(); const src_stat = try src_file.stat(); check_dest_stat: { const dest_stat = blk: { - var dest_file = File.openRead(dest_path) catch |err| switch (err) { + var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) { error.FileNotFound => break :check_dest_stat, else => |e| return e, }; @@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil /// in the same directory as dest_path. /// Destination file will have the same mode as the source file. pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { - var in_file = try File.openRead(source_path); + var in_file = try cwd().openFile(source_path, .{}); defer in_file.close(); const mode = try in_file.mode(); @@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { /// merged and readily available, /// there is a possibility of power loss or application termination leaving temporary files present pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { - var in_file = try File.openRead(source_path); + var in_file = try cwd().openFile(source_path, .{}); defer in_file.close(); var atomic_file = try AtomicFile.init(dest_path, mode); @@ -206,8 +206,6 @@ pub const AtomicFile = struct { /// dest_path must remain valid for the lifetime of AtomicFile /// call finish to atomically replace dest_path with contents - /// TODO once we have null terminated pointers, use the - /// openWriteNoClobberN function pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { const dirname = path.dirname(dest_path); var rand_buf: [12]u8 = undefined; @@ -224,15 +222,19 @@ pub const AtomicFile = struct { tmp_path_buf[tmp_path_len] = 0; + const my_cwd = cwd(); + while (true) { try crypto.randomBytes(rand_buf[0..]); b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); - const file = File.openWriteNoClobberC(@ptrCast([*:0]u8, &tmp_path_buf), mode) catch |err| switch (err) { + // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast + const file = my_cwd.createFileC( + @ptrCast([*:0]u8, &tmp_path_buf), + .{ .mode = mode, .exclusive = true }, + ) catch |err| switch (err) { error.PathAlreadyExists => continue, - // TODO zig should figure out that this error set does not include PathAlreadyExists since - // it is handled in the above switch - else => return err, + else => |e| return e, }; return AtomicFile{ @@ -248,7 +250,7 @@ pub const AtomicFile = struct { pub fn deinit(self: *AtomicFile) void { if (!self.finished) { self.file.close(); - deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {}; + cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {}; self.finished = true; } } @@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void { CannotDeleteRootDirectory, }.CannotDeleteRootDirectory; - var dir = try Dir.cwd().openDirList(dirname); + var dir = try cwd().openDirList(dirname); defer dir.close(); return dir.deleteTree(path.basename(full_path)); } else { - return Dir.cwd().deleteTree(full_path); + return cwd().deleteTree(full_path); } } @@ -657,17 +659,6 @@ pub const Dir = struct { } } - /// Returns an handle to the current working directory that is open for traversal. - /// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. - /// On POSIX targets, this function is comptime-callable. - pub fn cwd() Dir { - if (builtin.os == .windows) { - return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; - } else { - return Dir{ .fd = os.AT_FDCWD }; - } - } - pub const OpenError = error{ FileNotFound, NotDir, @@ -683,12 +674,12 @@ pub const Dir = struct { DeviceBusy, } || os.UnexpectedError; - /// Deprecated; call `Dir.cwd().openDirList` directly. + /// Deprecated; call `cwd().openDirList` directly. pub fn open(dir_path: []const u8) OpenError!Dir { return cwd().openDirList(dir_path); } - /// Deprecated; call `Dir.cwd().openDirListC` directly. + /// Deprecated; call `cwd().openDirListC` directly. pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir { return cwd().openDirListC(dir_path_c); } @@ -700,7 +691,9 @@ pub const Dir = struct { /// Opens a file for reading or writing, without attempting to create a new file. /// Call `File.close` to release the resource. + /// Asserts that the path parameter has no null bytes. pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (builtin.os == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openFileW(&path_w, flags); @@ -737,7 +730,9 @@ pub const Dir = struct { /// Creates, opens, or overwrites a file with write access. /// Call `File.close` on the result when done. + /// Asserts that the path parameter has no null bytes. pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (builtin.os == .windows) { const path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.createFileW(&path_w, flags); @@ -865,7 +860,10 @@ pub const Dir = struct { /// list the contents of a directory, open it with `openDirList`. /// /// Call `close` on the result when done. + /// + /// Asserts that the path parameter has no null bytes. pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (builtin.os == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openDirTraverseW(&sub_path_w); @@ -880,7 +878,10 @@ pub const Dir = struct { /// same and may be more efficient. /// /// Call `close` on the result when done. + /// + /// Asserts that the path parameter has no null bytes. pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (builtin.os == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.openDirListW(&sub_path_w); @@ -995,9 +996,12 @@ pub const Dir = struct { pub const DeleteFileError = os.UnlinkError; /// Delete a file name and possibly the file it refers to, based on an open directory handle. + /// Asserts that the path parameter has no null bytes. pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void { - const sub_path_c = try os.toPosixPath(sub_path); - return self.deleteFileC(&sub_path_c); + os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) { + error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + else => |e| return e, + }; } /// Same as `deleteFile` except the parameter is null-terminated. @@ -1008,6 +1012,14 @@ pub const Dir = struct { }; } + /// Same as `deleteFile` except the parameter is WTF-16 encoded. + pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void { + os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) { + error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR + else => |e| return e, + }; + } + pub const DeleteDirError = error{ DirNotEmpty, FileNotFound, @@ -1026,7 +1038,9 @@ pub const Dir = struct { /// Returns `error.DirNotEmpty` if the directory is not empty. /// To delete a directory recursively, see `deleteTree`. + /// Asserts that the path parameter has no null bytes. pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); if (builtin.os == .windows) { const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); return self.deleteDirW(&sub_path_w); @@ -1054,7 +1068,9 @@ pub const Dir = struct { /// Read value of a symbolic link. /// The return value is a slice of `buffer`, from index `0`. + /// Asserts that the path parameter has no null bytes. pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { + if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0); const sub_path_c = try os.toPosixPath(sub_path); return self.readLinkC(&sub_path_c, buffer); } @@ -1265,8 +1281,94 @@ pub const Dir = struct { } } } + + /// Writes content to the file system, creating a new file if it does not exist, truncating + /// if it already exists. + pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void { + var file = try self.createFile(sub_path, .{}); + defer file.close(); + try file.write(data); + } }; +/// Returns an handle to the current working directory that is open for traversal. +/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior. +/// On POSIX targets, this function is comptime-callable. +pub fn cwd() Dir { + if (builtin.os == .windows) { + return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle }; + } else { + return Dir{ .fd = os.AT_FDCWD }; + } +} + +/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path. +/// Call `File.close` to release the resource. +/// Asserts that the path is absolute. See `Dir.openFile` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function +/// that accepts a null-terminated path. +pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsolute(absolute_path)); + return cwd().openFile(absolute_path, flags); +} + +/// Same as `openFileAbsolute` but the path parameter is null-terminated. +pub fn openFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsoluteC(absolute_path_c)); + return cwd().openFileC(absolute_path_c, flags); +} + +/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded. +pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { + assert(path.isAbsoluteW(absolute_path_w)); + return cwd().openFileW(absolute_path_w, flags); +} + +/// Creates, opens, or overwrites a file with write access, based on an absolute path. +/// Call `File.close` to release the resource. +/// Asserts that the path is absolute. See `Dir.createFile` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function +/// that accepts a null-terminated path. +pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File { + assert(path.isAbsolute(absolute_path)); + return cwd().createFile(absolute_path, flags); +} + +/// Same as `createFileAbsolute` but the path parameter is null-terminated. +pub fn createFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File { + assert(path.isAbsoluteC(absolute_path_c)); + return cwd().createFileC(absolute_path_c, flags); +} + +/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded. +pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { + assert(path.isAbsoluteW(absolute_path_w)); + return cwd().createFileW(absolute_path_w, flags); +} + +/// Delete a file name and possibly the file it refers to, based on an absolute path. +/// Asserts that the path is absolute. See `Dir.deleteFile` for a function that +/// operates on both absolute and relative paths. +/// Asserts that the path parameter has no null bytes. +pub fn deleteFileAbsolute(absolute_path: []const u8) DeleteFileError!void { + assert(path.isAbsolute(absolute_path)); + return cwd().deleteFile(absolute_path); +} + +/// Same as `deleteFileAbsolute` except the parameter is null-terminated. +pub fn deleteFileAbsoluteC(absolute_path_c: [*:0]const u8) DeleteFileError!void { + assert(path.isAbsoluteC(absolute_path_c)); + return cwd().deleteFileC(absolute_path_c); +} + +/// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded. +pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void { + assert(path.isAbsoluteW(absolute_path_w)); + return cwd().deleteFileW(absolute_path_w); +} + pub const Walker = struct { stack: std.ArrayList(StackItem), name_buffer: std.Buffer, @@ -1339,7 +1441,7 @@ pub const Walker = struct { pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { assert(!mem.endsWith(u8, dir_path, path.sep_str)); - var dir = try Dir.cwd().openDirList(dir_path); + var dir = try cwd().openDirList(dir_path); errdefer dir.close(); var name_buffer = try std.Buffer.init(allocator, dir_path); @@ -1373,18 +1475,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE pub fn openSelfExe() OpenSelfExeError!File { if (builtin.os == .linux) { - return File.openReadC("/proc/self/exe"); + return openFileAbsoluteC("/proc/self/exe", .{}); } if (builtin.os == .windows) { const wide_slice = selfExePathW(); const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice); - return Dir.cwd().openReadW(&prefixed_path_w); + return cwd().openReadW(&prefixed_path_w); } var buf: [MAX_PATH_BYTES]u8 = undefined; const self_exe_path = try selfExePath(&buf); buf[self_exe_path.len] = 0; - // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 - return File.openReadC(@ptrCast([*:0]u8, self_exe_path.ptr)); + // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 + return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{}); } test "openSelfExe" { diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 52e5f3e49e..7a12c7b937 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -51,42 +51,42 @@ pub const File = struct { /// Deprecated; call `std.fs.Dir.openFile` directly. pub fn openRead(path: []const u8) OpenError!File { - return std.fs.Dir.cwd().openFile(path, .{}); + return std.fs.cwd().openFile(path, .{}); } /// Deprecated; call `std.fs.Dir.openFileC` directly. pub fn openReadC(path_c: [*:0]const u8) OpenError!File { - return std.fs.Dir.cwd().openFileC(path_c, .{}); + return std.fs.cwd().openFileC(path_c, .{}); } /// Deprecated; call `std.fs.Dir.openFileW` directly. pub fn openReadW(path_w: [*]const u16) OpenError!File { - return std.fs.Dir.cwd().openFileW(path_w, .{}); + return std.fs.cwd().openFileW(path_w, .{}); } /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWrite(path: []const u8) OpenError!File { - return std.fs.Dir.cwd().createFile(path, .{}); + return std.fs.cwd().createFile(path, .{}); } /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File { - return std.fs.Dir.cwd().createFile(path, .{ .mode = file_mode }); + return std.fs.cwd().createFile(path, .{ .mode = file_mode }); } /// Deprecated; call `std.fs.Dir.createFileC` directly. pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { - return std.fs.Dir.cwd().createFileC(path_c, .{ .mode = file_mode }); + return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode }); } /// Deprecated; call `std.fs.Dir.createFileW` directly. pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { - return std.fs.Dir.cwd().createFileW(path_w, .{ .mode = file_mode }); + return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode }); } /// Deprecated; call `std.fs.Dir.createFile` directly. pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File { - return std.fs.Dir.cwd().createFile(path, .{ + return std.fs.cwd().createFile(path, .{ .mode = file_mode, .exclusive = true, }); @@ -94,7 +94,7 @@ pub const File = struct { /// Deprecated; call `std.fs.Dir.createFileC` directly. pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File { - return std.fs.Dir.cwd().createFileC(path_c, .{ + return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode, .exclusive = true, }); @@ -102,7 +102,7 @@ pub const File = struct { /// Deprecated; call `std.fs.Dir.createFileW` directly. pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File { - return std.fs.Dir.cwd().createFileW(path_w, .{ + return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode, .exclusive = true, }); diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index e2f1b5ac65..b1b3ef6af3 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -128,6 +128,14 @@ test "join" { testJoinPosix([_][]const u8{ "a/", "/c" }, "a/c"); } +pub fn isAbsoluteC(path_c: [*:0]const u8) bool { + if (builtin.os == .windows) { + return isAbsoluteWindowsC(path_c); + } else { + return isAbsolutePosixC(path_c); + } +} + pub fn isAbsolute(path: []const u8) bool { if (builtin.os == .windows) { return isAbsoluteWindows(path); @@ -136,7 +144,7 @@ pub fn isAbsolute(path: []const u8) bool { } } -pub fn isAbsoluteW(path_w: [*]const u16) bool { +pub fn isAbsoluteW(path_w: [*:0]const u16) bool { if (path_w[0] == '/') return true; @@ -174,10 +182,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool { return false; } +pub fn isAbsoluteWindowsC(path_c: [*:0]const u8) bool { + if (path_c[0] == '/') + return true; + + if (path_c[0] == '\\') { + return true; + } + if (path_c[0] == 0 or path_c[1] == 0 or path_c[2] == 0) { + return false; + } + if (path_c[1] == ':') { + if (path_c[2] == '/') + return true; + if (path_c[2] == '\\') + return true; + } + return false; +} + pub fn isAbsolutePosix(path: []const u8) bool { return path[0] == sep_posix; } +pub fn isAbsolutePosixC(path_c: [*:0]const u8) bool { + return path_c[0] == sep_posix; +} + test "isAbsoluteWindows" { testIsAbsoluteWindows("/", true); testIsAbsoluteWindows("//", true); diff --git a/lib/std/io.zig b/lib/std/io.zig index 124d3cf253..c36fd195ba 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream; pub const InStream = @import("io/in_stream.zig").InStream; pub const OutStream = @import("io/out_stream.zig").OutStream; -/// TODO move this to `std.fs` and add a version to `std.fs.Dir`. +/// Deprecated; use `std.fs.Dir.writeFile`. pub fn writeFile(path: []const u8, data: []const u8) !void { - var file = try File.openWrite(path); - defer file.close(); - try file.write(data); + return fs.cwd().writeFile(path, data); } -/// On success, caller owns returned buffer. -/// This function is deprecated; use `std.fs.Dir.readFileAlloc`. +/// Deprecated; use `std.fs.Dir.readFileAlloc`. pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 { - return fs.Dir.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); + return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize)); } pub fn BufferedInStream(comptime Error: type) type { diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index d2374f0a3f..2857252bb5 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -14,12 +14,14 @@ test "write a file, read it, then delete it" { var raw_bytes: [200 * 1024]u8 = undefined; var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator; + const cwd = fs.cwd(); + var data: [1024]u8 = undefined; var prng = DefaultPrng.init(1234); prng.random.bytes(data[0..]); const tmp_file_name = "temp_test_file.txt"; { - var file = try File.openWrite(tmp_file_name); + var file = try cwd.createFile(tmp_file_name, .{}); defer file.close(); var file_out_stream = file.outStream(); @@ -32,8 +34,8 @@ test "write a file, read it, then delete it" { } { - // make sure openWriteNoClobber doesn't harm the file - if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| { + // Make sure the exclusive flag is honored. + if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| { unreachable; } else |err| { std.debug.assert(err == File.OpenError.PathAlreadyExists); @@ -41,7 +43,7 @@ test "write a file, read it, then delete it" { } { - var file = try File.openRead(tmp_file_name); + var file = try cwd.openFile(tmp_file_name, .{}); defer file.close(); const file_size = try file.getEndPos(); @@ -58,7 +60,7 @@ test "write a file, read it, then delete it" { expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], data)); expect(mem.eql(u8, contents[contents.len - "end".len ..], "end")); } - try fs.deleteFile(tmp_file_name); + try cwd.deleteFile(tmp_file_name); } test "BufferOutStream" { @@ -274,7 +276,7 @@ test "BitOutStream" { test "BitStreams with File Stream" { const tmp_file_name = "temp_test_file.txt"; { - var file = try File.openWrite(tmp_file_name); + var file = try fs.cwd().createFile(tmp_file_name, .{}); defer file.close(); var file_out = file.outStream(); @@ -291,7 +293,7 @@ test "BitStreams with File Stream" { try bit_stream.flushBits(); } { - var file = try File.openRead(tmp_file_name); + var file = try fs.cwd().openFile(tmp_file_name, .{}); defer file.close(); var file_in = file.inStream(); @@ -316,7 +318,7 @@ test "BitStreams with File Stream" { expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); } - try fs.deleteFile(tmp_file_name); + try fs.cwd().deleteFile(tmp_file_name); } fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void { @@ -599,7 +601,7 @@ test "c out stream" { const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile; defer { _ = std.c.fclose(out_file); - fs.deleteFileC(filename) catch {}; + fs.cwd().deleteFileC(filename) catch {}; } const out_stream = &io.COutStream.init(out_file).stream; @@ -608,10 +610,10 @@ test "c out stream" { test "File seek ops" { const tmp_file_name = "temp_test_file.txt"; - var file = try File.openWrite(tmp_file_name); + var file = try fs.cwd().createFile(tmp_file_name, .{}); defer { file.close(); - fs.deleteFile(tmp_file_name) catch {}; + fs.cwd().deleteFile(tmp_file_name) catch {}; } try file.write([_]u8{0x55} ** 8192); @@ -632,10 +634,10 @@ test "File seek ops" { test "updateTimes" { const tmp_file_name = "just_a_temporary_file.txt"; - var file = try File.openWrite(tmp_file_name); + var file = try fs.cwd().createFile(tmp_file_name, .{}); defer { file.close(); - std.fs.deleteFile(tmp_file_name) catch {}; + std.fs.cwd().deleteFile(tmp_file_name) catch {}; } var stat_old = try file.stat(); // Set atime and mtime to 5s before diff --git a/lib/std/net.zig b/lib/std/net.zig index 7a7b2de026..e18e5d378b 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts( family: os.sa_family_t, port: u16, ) !void { - const file = fs.File.openReadC("/etc/hosts") catch |err| switch (err) { + const file = fs.openFileAbsoluteC("/etc/hosts", .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.AccessDenied, @@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void { }; errdefer rc.deinit(); - const file = fs.File.openReadC("/etc/resolv.conf") catch |err| switch (err) { + const file = fs.openFileAbsoluteC("/etc/resolv.conf", .{}) catch |err| switch (err) { error.FileNotFound, error.NotDir, error.AccessDenied, diff --git a/lib/std/os.zig b/lib/std/os.zig index 22d52b24a6..6572d37305 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e path_buf[search_path.len] = '/'; mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); path_buf[search_path.len + file_slice.len + 1] = 0; - // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 + // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp); switch (err) { error.AccessDenied => seen_eacces = true, @@ -834,7 +834,7 @@ pub fn execvpe( @memcpy(arg_buf.ptr, arg.ptr, arg.len); arg_buf[arg.len] = 0; - // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3731 + // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770 argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr); } argv_buf[argv_slice.len] = null; @@ -842,7 +842,7 @@ pub fn execvpe( const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); defer freeNullDelimitedEnvMap(allocator, envp_buf); - // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 + // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr); return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr); @@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std. @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); env_buf[env_buf.len - 1] = 0; - // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 + // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr); } assert(i == envp_count); } - // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731 + // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 assert(envp_buf[envp_count] == null); return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count]; } @@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{ }; /// Delete a file name and possibly the file it refers to, based on an open directory handle. +/// Asserts that the path parameter has no null bytes. pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { + if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0); if (builtin.os == .windows) { const file_path_w = try windows.sliceToPrefixedFileW(file_path); return unlinkatW(dirfd, &file_path_w, flags); diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 8281851d6b..aa6655b7d8 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -4,6 +4,7 @@ const linux = std.os.linux; const mem = std.mem; const elf = std.elf; const expect = std.testing.expect; +const fs = std.fs; test "getpid" { expect(linux.getpid() != 0); @@ -45,14 +46,12 @@ test "timer" { err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1); } -const File = std.fs.File; - test "statx" { const tmp_file_name = "just_a_temporary_file.txt"; - var file = try File.openWrite(tmp_file_name); + var file = try fs.cwd().createFile(tmp_file_name, .{}); defer { file.close(); - std.fs.deleteFile(tmp_file_name) catch {}; + fs.cwd().deleteFile(tmp_file_name) catch {}; } var statx_buf: linux.Statx = undefined; diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 778a39eb3c..c49de1680b 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" { try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense"); try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah"); try fs.deleteTree("os_test_tmp"); - if (fs.Dir.cwd().openDirTraverse("os_test_tmp")) |dir| { + if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| { @panic("expected error"); } else |err| { expect(err == error.FileNotFound); @@ -111,7 +111,7 @@ test "AtomicFile" { const content = try io.readFileAlloc(allocator, test_out_file); expect(mem.eql(u8, content, test_content)); - try fs.deleteFile(test_out_file); + try fs.cwd().deleteFile(test_out_file); } test "thread local storage" { diff --git a/lib/std/pdb.zig b/lib/std/pdb.zig index 8e4a9b5d6a..2a21f6c43c 100644 --- a/lib/std/pdb.zig +++ b/lib/std/pdb.zig @@ -6,6 +6,7 @@ const mem = std.mem; const os = std.os; const warn = std.debug.warn; const coff = std.coff; +const fs = std.fs; const File = std.fs.File; const ArrayList = std.ArrayList; @@ -469,7 +470,7 @@ pub const Pdb = struct { msf: Msf, pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void { - self.in_file = try File.openRead(file_name); + self.in_file = try fs.cwd().openFile(file_name, .{}); self.allocator = coff_ptr.allocator; self.coff = coff_ptr; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 4288c4ea9a..eab08337a8 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -702,7 +702,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro max_src_size, ) catch |err| switch (err) { error.IsDir, error.AccessDenied => { - var dir = try fs.Dir.cwd().openDirList(file_path); + var dir = try fs.cwd().openDirList(file_path); defer dir.close(); var group = event.Group(FmtError!void).init(fmt.allocator); diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index 0708abaaf0..7d48717598 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { error.IsDir, error.AccessDenied => { // TODO make event based (and dir.next()) - var dir = try fs.Dir.cwd().openDirList(file_path); + var dir = try fs.cwd().openDirList(file_path); defer dir.close(); var dir_it = dir.iterate(); diff --git a/test/standalone/cat/main.zig b/test/standalone/cat/main.zig index 7c15c15e53..f22329050c 100644 --- a/test/standalone/cat/main.zig +++ b/test/standalone/cat/main.zig @@ -1,7 +1,7 @@ const std = @import("std"); const io = std.io; const process = std.process; -const File = std.fs.File; +const fs = std.fs; const mem = std.mem; const warn = std.debug.warn; const allocator = std.debug.global_allocator; @@ -12,6 +12,8 @@ pub fn main() !void { var catted_anything = false; const stdout_file = io.getStdOut(); + const cwd = fs.cwd(); + while (args_it.next(allocator)) |arg_or_err| { const arg = try unwrapArg(arg_or_err); if (mem.eql(u8, arg, "-")) { @@ -20,7 +22,7 @@ pub fn main() !void { } else if (arg[0] == '-') { return usage(exe); } else { - const file = File.openRead(arg) catch |err| { + const file = cwd.openFile(arg, .{}) catch |err| { warn("Unable to open file: {}\n", @errorName(err)); return err; }; @@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void { return error.Invalid; } -fn cat_file(stdout: File, file: File) !void { +fn cat_file(stdout: fs.File, file: fs.File) !void { var buf: [1024 * 4]u8 = undefined; while (true) { From cdeafe777a2fe707674cb44e4aa7b5a1b8319ec5 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Sat, 30 Nov 2019 21:36:16 +0100 Subject: [PATCH 34/38] Eat shit and die mips --- src/target.cpp | 2 -- test/tests.zig | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/src/target.cpp b/src/target.cpp index 3a6d8d19f4..538f836f24 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1306,8 +1306,6 @@ const char *target_dynamic_linker(const ZigTarget *target) { case ZigLLVM_mips: case ZigLLVM_mipsel: - return "/lib/ld.so.1"; - case ZigLLVM_mips64: case ZigLLVM_mips64el: zig_panic("TODO implement target_dynamic_linker for mips"); diff --git a/test/tests.zig b/test/tests.zig index b14706bea9..5d7e4e11e1 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -160,16 +160,6 @@ const test_targets = [_]TestTarget{ }, }, }, - TestTarget{ - .target = Target{ - .Cross = CrossTarget{ - .os = .linux, - .arch = .mipsel, - .abi = .gnu, - }, - }, - .link_libc = true, - }, TestTarget{ .target = Target{ .Cross = CrossTarget{ From 034ccb4e4e64dda2626535d46a4bc0367bcc1ad2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 30 Nov 2019 16:58:32 -0500 Subject: [PATCH 35/38] add missing error code handling on Windows --- lib/std/fs.zig | 1 + lib/std/fs/file.zig | 1 + lib/std/io/test.zig | 2 +- lib/std/os.zig | 7 ++++++- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 0f8da873a6..89a045a652 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -841,6 +841,7 @@ pub const Dir = struct { w.STATUS.ACCESS_DENIED => return error.AccessDenied, w.STATUS.PIPE_BUSY => return error.PipeBusy, w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable, + w.STATUS.OBJECT_NAME_COLLISION => return error.PathAlreadyExists, else => return w.unexpectedStatus(rc), } } diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 7a12c7b937..540f7d395e 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -228,6 +228,7 @@ pub const File = struct { windows.STATUS.SUCCESS => {}, windows.STATUS.BUFFER_OVERFLOW => {}, windows.STATUS.INVALID_PARAMETER => unreachable, + windows.STATUS.ACCESS_DENIED => return error.AccessDenied, else => return windows.unexpectedStatus(rc), } return Stat{ diff --git a/lib/std/io/test.zig b/lib/std/io/test.zig index 2857252bb5..912a871863 100644 --- a/lib/std/io/test.zig +++ b/lib/std/io/test.zig @@ -634,7 +634,7 @@ test "File seek ops" { test "updateTimes" { const tmp_file_name = "just_a_temporary_file.txt"; - var file = try fs.cwd().createFile(tmp_file_name, .{}); + var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true }); defer { file.close(); std.fs.cwd().deleteFile(tmp_file_name) catch {}; diff --git a/lib/std/os.zig b/lib/std/os.zig index 6572d37305..622aaaf3bd 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -2028,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 { } } -pub const FStatError = error{SystemResources} || UnexpectedError; +pub const FStatError = error{ + SystemResources, + AccessDenied, +} || UnexpectedError; pub fn fstat(fd: fd_t) FStatError!Stat { var stat: Stat = undefined; @@ -2038,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { EINVAL => unreachable, EBADF => unreachable, // Always a race condition. ENOMEM => return error.SystemResources, + EACCES => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } @@ -2047,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat { EINVAL => unreachable, EBADF => unreachable, // Always a race condition. ENOMEM => return error.SystemResources, + EACCES => return error.AccessDenied, else => |err| return unexpectedErrno(err), } } From 2e7350140d4fbf9335e48693ccc4cba77d229c84 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 30 Nov 2019 18:36:17 -0500 Subject: [PATCH 36/38] further clarify std.fs function docs --- lib/std/fs.zig | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 89a045a652..95060f566f 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -719,7 +719,8 @@ pub const Dir = struct { return File{ .handle = fd }; } - /// Same as `openFile` but the path parameter is WTF-16 encoded. + /// Same as `openFile` but Windows-only and the path parameter is + /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File { const w = os.windows; const access_mask = w.SYNCHRONIZE | @@ -756,7 +757,8 @@ pub const Dir = struct { return File{ .handle = fd }; } - /// Same as `createFile` but the path parameter is WTF-16 encoded. + /// Same as `createFile` but Windows-only and the path parameter is + /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File { const w = os.windows; const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | From 5026b1aad550bd85d12480e0a356302e858f8eef Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 30 Nov 2019 22:05:28 -0500 Subject: [PATCH 37/38] free IrAnalyze sometimes Total bytes used in stage1 std lib tests: 3.443 GiB -> 3.418 GiB --- src/ir.cpp | 73 +++++++++++++++++++++++++++++----------- src/memory_profiling.cpp | 7 ++-- src/util.hpp | 2 +- 3 files changed, 60 insertions(+), 22 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 272442a563..e0ebe84687 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -41,6 +41,7 @@ struct IrAnalyze { ZigList src_implicit_return_type_list; ZigList resume_stack; IrBasicBlock *const_predecessor_bb; + size_t ref_count; }; enum ConstCastResultId { @@ -252,6 +253,18 @@ static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_n IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); static ResultLoc *no_result_loc(void); +static void ira_ref(IrAnalyze *ira) { + ira->ref_count += 1; +} +static void ira_deref(IrAnalyze *ira) { + if (ira->ref_count > 1) { + ira->ref_count -= 1; + return; + } + assert(ira->ref_count != 0); + destroy(ira, "IrAnalyze"); +} + static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) { assert(get_src_ptr_type(const_val->type) != nullptr); assert(const_val->special == ConstValSpecialStatic); @@ -16011,8 +16024,8 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; - LazyValueErrUnionType *lazy_err_union_type = allocate(1); - lazy_err_union_type->ira = ira; + LazyValueErrUnionType *lazy_err_union_type = allocate(1, "LazyValueErrUnionType"); + lazy_err_union_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_err_union_type->base; lazy_err_union_type->base.id = LazyValueIdErrUnionType; @@ -17863,8 +17876,8 @@ static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; - LazyValueOptType *lazy_opt_type = allocate(1); - lazy_opt_type->ira = ira; + LazyValueOptType *lazy_opt_type = allocate(1, "LazyValueOptType"); + lazy_opt_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_opt_type->base; lazy_opt_type->base.id = LazyValueIdOptType; @@ -19809,8 +19822,8 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstruction *result = ir_const(ira, &slice_type_instruction->base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; - LazyValueSliceType *lazy_slice_type = allocate(1); - lazy_slice_type->ira = ira; + LazyValueSliceType *lazy_slice_type = allocate(1, "LazyValueSliceType"); + lazy_slice_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_slice_type->base; lazy_slice_type->base.id = LazyValueIdSliceType; @@ -19969,8 +19982,8 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructi IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int); result->value->special = ConstValSpecialLazy; - LazyValueSizeOf *lazy_size_of = allocate(1); - lazy_size_of->ira = ira; + LazyValueSizeOf *lazy_size_of = allocate(1, "LazyValueSizeOf"); + lazy_size_of->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_size_of->base; lazy_size_of->base.id = LazyValueIdSizeOf; @@ -24647,8 +24660,8 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int); result->value->special = ConstValSpecialLazy; - LazyValueAlignOf *lazy_align_of = allocate(1); - lazy_align_of->ira = ira; + LazyValueAlignOf *lazy_align_of = allocate(1, "LazyValueAlignOf"); + lazy_align_of->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_align_of->base; lazy_align_of->base.id = LazyValueIdAlignOf; @@ -25131,8 +25144,8 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; - LazyValueFnType *lazy_fn_type = allocate(1); - lazy_fn_type->ira = ira; + LazyValueFnType *lazy_fn_type = allocate(1, "LazyValueFnType"); + lazy_fn_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_fn_type->base; lazy_fn_type->base.id = LazyValueIdFnType; @@ -26172,8 +26185,8 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type); result->value->special = ConstValSpecialLazy; - LazyValuePtrType *lazy_ptr_type = allocate(1); - lazy_ptr_type->ira = ira; + LazyValuePtrType *lazy_ptr_type = allocate(1, "LazyValuePtrType"); + lazy_ptr_type->ira = ira; ira_ref(ira); result->value->data.x_lazy = &lazy_ptr_type->base; lazy_ptr_type->base.id = LazyValueIdPtrType; @@ -27646,7 +27659,8 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ assert(old_exec->first_err_trace_msg == nullptr); assert(expected_type == nullptr || !type_is_invalid(expected_type)); - IrAnalyze *ira = allocate(1); + IrAnalyze *ira = allocate(1, "IrAnalyze"); + ira->ref_count = 1; old_exec->analysis = ira; ira->codegen = codegen; @@ -27713,6 +27727,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ ira->instruction_index += 1; } + ZigType *res_type; if (new_exec->first_err_trace_msg != nullptr) { codegen->trace_err = new_exec->first_err_trace_msg; if (codegen->trace_err != nullptr && new_exec->source_node != nullptr && @@ -27722,13 +27737,18 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_ codegen->trace_err = add_error_note(codegen, codegen->trace_err, new_exec->source_node, buf_create_from_str("referenced here")); } - return ira->codegen->builtin_types.entry_invalid; + res_type = ira->codegen->builtin_types.entry_invalid; } else if (ira->src_implicit_return_type_list.length == 0) { - return codegen->builtin_types.entry_unreachable; + res_type = codegen->builtin_types.entry_unreachable; } else { - return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, + res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, ira->src_implicit_return_type_list.length); } + + // It is now safe to free Pass 1 IR instructions. + ira_deref(ira); + + return res_type; } bool ir_has_side_effects(IrInstruction *instruction) { @@ -28064,6 +28084,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, align_in_bytes); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdSizeOf: { @@ -28119,6 +28141,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, abi_size); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdSliceType: { @@ -28197,6 +28221,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdPtrType: { @@ -28268,6 +28294,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes, allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val); val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdOptType: { @@ -28290,16 +28318,21 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_optional_type(ira->codegen, payload_type); val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdFnType: { LazyValueFnType *lazy_fn_type = reinterpret_cast(val->data.x_lazy); - ZigType *fn_type = ir_resolve_lazy_fn_type(lazy_fn_type->ira, source_node, lazy_fn_type); + IrAnalyze *ira = lazy_fn_type->ira; + ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type); if (fn_type == nullptr) return ErrorSemanticAnalyzeFail; val->special = ConstValSpecialStatic; assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = fn_type; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } case LazyValueIdErrUnionType: { @@ -28328,6 +28361,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { assert(val->type->id == ZigTypeIdMetaType); val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type); val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. return ErrorNone; } } diff --git a/src/memory_profiling.cpp b/src/memory_profiling.cpp index a44dfc4450..4bd4cea7ba 100644 --- a/src/memory_profiling.cpp +++ b/src/memory_profiling.cpp @@ -35,7 +35,9 @@ static const char *get_default_name(const char *name_or_null, size_t type_size) if (name_or_null != nullptr) return name_or_null; if (type_size >= unknown_names.length) { table_active = false; - unknown_names.resize(type_size + 1); + while (type_size >= unknown_names.length) { + unknown_names.append(nullptr); + } table_active = true; } if (unknown_names.at(type_size) == nullptr) { @@ -66,7 +68,8 @@ void memprof_dealloc(const char *name, size_t count, size_t type_size) { name = get_default_name(name, type_size); auto existing_entry = usage_table.maybe_get(name); if (existing_entry == nullptr) { - zig_panic("deallocated more than allocated; compromised memory usage stats"); + zig_panic("deallocated name '%s' (size %zu) not found in allocated table; compromised memory usage stats", + name, type_size); } if (existing_entry->value.type_size != type_size) { zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size); diff --git a/src/util.hpp b/src/util.hpp index 79bebd3355..91535cce18 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -165,7 +165,7 @@ static inline void deallocate(T *old, size_t count, const char *name = nullptr) template static inline void destroy(T *old, const char *name = nullptr) { - return deallocate(old, 1); + return deallocate(old, 1, name); } template From 4b6740e19d57454f3c4eac0c2e9a92ce08e7ec04 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 30 Nov 2019 23:54:15 -0500 Subject: [PATCH 38/38] sometimes free stuff from Zig IR pass 1 Total bytes used in stage1 std lib tests: 3.418 -> 3.198 GiB (saving 225 MiB) There's still this from pass 1 not getting freed: Const: 6909049 items, 72 bytes each, total 474.407 MiB This is due to 2 things hanging on to references to IrAnalyze pointers: * ZigVar->owner_exec->analysis * LazyValue->ira The LazyValue one could be solved by memoizing the results after the lazy value is resolved, and then it could unref the IrAnalyze. ZigVars that are determined to be comptime const, could have their const_value set to that value, instead of using the mem_slot_index mechanism. This would prevent an IrAnalyze ref in some cases. --- src/all_types.hpp | 4 +- src/analyze.cpp | 30 ++-- src/ir.cpp | 386 ++++++++++++++++++++++++++++++++++++++++++++-- src/list.hpp | 2 +- 4 files changed, 400 insertions(+), 22 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5b062efc9a..227018f5e9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1565,7 +1565,7 @@ struct ZigFn { // in the case of async functions this is the implicit return type according to the // zig source code, not according to zig ir ZigType *src_implicit_return_type; - IrExecutable ir_executable; + IrExecutable *ir_executable; IrExecutable analyzed_executable; size_t prealloc_bbc; size_t prealloc_backward_branch_quota; @@ -2204,6 +2204,8 @@ struct ZigVar { bool src_is_const; bool gen_is_const; bool is_thread_local; + bool is_comptime_memoized; + bool is_comptime_memoized_value; }; struct ErrorTableEntry { diff --git a/src/analyze.cpp b/src/analyze.cpp index c0d2d636ef..0f2df5835c 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3275,14 +3275,15 @@ static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool i } ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) { - ZigFn *fn_entry = allocate(1); + ZigFn *fn_entry = allocate(1, "ZigFn"); + fn_entry->ir_executable = allocate(1, "IrExecutablePass1"); fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota; fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc; fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota; fn_entry->analyzed_executable.fn_entry = fn_entry; - fn_entry->ir_executable.fn_entry = fn_entry; + fn_entry->ir_executable->fn_entry = fn_entry; fn_entry->fn_inline = inline_value; return fn_entry; @@ -4610,7 +4611,7 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { assert(!fn_type->data.fn.is_generic); FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - ZigType *block_return_type = ir_analyze(g, &fn->ir_executable, + ZigType *block_return_type = ir_analyze(g, fn->ir_executable, &fn->analyzed_executable, fn_type_id->return_type, return_type_node); fn->src_implicit_return_type = block_return_type; @@ -4706,7 +4707,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { assert(!fn_type->data.fn.is_generic); ir_gen_fn(g, fn_table_entry); - if (fn_table_entry->ir_executable.first_err_trace_msg != nullptr) { + if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) { fn_table_entry->anal_state = FnAnalStateInvalid; return; } @@ -4714,7 +4715,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { fprintf(stderr, "\n"); ast_render(stderr, fn_table_entry->body_node, 4); fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name)); - ir_print(g, stderr, &fn_table_entry->ir_executable, 4, IrPassSrc); + ir_print(g, stderr, fn_table_entry->ir_executable, 4, IrPassSrc); fprintf(stderr, "}\n"); } @@ -6453,20 +6454,31 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) { } bool ir_get_var_is_comptime(ZigVar *var) { + if (var->is_comptime_memoized) + return var->is_comptime_memoized_value; + + var->is_comptime_memoized = true; + // The is_comptime field can be left null, which means not comptime. - if (var->is_comptime == nullptr) - return false; + if (var->is_comptime == nullptr) { + var->is_comptime_memoized_value = false; + return var->is_comptime_memoized_value; + } // When the is_comptime field references an instruction that has to get analyzed, this // is the value. if (var->is_comptime->child != nullptr) { assert(var->is_comptime->child->value->type->id == ZigTypeIdBool); - return var->is_comptime->child->value->data.x_bool; + var->is_comptime_memoized_value = var->is_comptime->child->value->data.x_bool; + var->is_comptime = nullptr; + return var->is_comptime_memoized_value; } // As an optimization, is_comptime values which are constant are allowed // to be omitted from analysis. In this case, there is no child instruction // and we simply look at the unanalyzed const parent instruction. assert(var->is_comptime->value->type->id == ZigTypeIdBool); - return var->is_comptime->value->data.x_bool; + var->is_comptime_memoized_value = var->is_comptime->value->data.x_bool; + var->is_comptime = nullptr; + return var->is_comptime_memoized_value; } bool const_values_equal_ptr(ZigValue *a, ZigValue *b) { diff --git a/src/ir.cpp b/src/ir.cpp index e0ebe84687..93b16dc8d3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -253,6 +253,353 @@ static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_n IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type); static ResultLoc *no_result_loc(void); +static void destroy_instruction(IrInstruction *inst) { +#ifdef ZIG_ENABLE_MEM_PROFILE + const char *name = ir_instruction_type_str(inst->id); +#else + const char *name = nullptr; +#endif + switch (inst->id) { + case IrInstructionIdInvalid: + zig_unreachable(); + case IrInstructionIdReturn: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdConst: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBinOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMergeErrSets: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdDeclVarSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCallSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCallGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCondBr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPhi: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdContainerInitList: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdContainerInitFields: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnreachable: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdElemPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVarPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdReturnPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdLoadPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdLoadPtrGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdStorePtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorStoreElem: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFieldPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdStructFieldPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnionFieldPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetCold: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetRuntimeSafety: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetFloatMode: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdArrayType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSliceType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAnyFrameType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdGlobalAsm: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAsm: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSizeOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestNonNull: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOptionalUnwrapPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPopCount: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdClz: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCtz: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBswap: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitReverse: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchBr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchVar: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchElseVar: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSwitchTarget: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnionTag: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdImport: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdRef: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdRefGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCompileErr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCompileLog: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCImport: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCInclude: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCDefine: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCUndef: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdEmbedFile: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCmpxchgSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCmpxchgGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFence: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTruncate: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFloatCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrSetCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFromBytes: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdToBytes: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToFloat: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFloatToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBoolToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdShuffleVector: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSplatSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSplatGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBoolNot: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemset: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemcpy: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSliceSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSliceGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemberCount: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemberType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMemberName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBreakpoint: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdReturnAddress: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameAddress: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameHandle: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameSizeSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFrameSizeGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAlignOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOverflowOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestErrSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestErrGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnwrapErrCode: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnwrapErrPayload: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOptionalWrap: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrWrapCode: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrWrapPayload: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFnProto: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTestComptime: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrCastSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrCastGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitCastSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitCastGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdWidenOrShorten: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToEnum: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdIntToErr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCheckSwitchProngs: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCheckStatementIsVoid: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTagName: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdDeclRef: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPanic: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFieldParentPtr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdByteOffsetOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdBitOffsetOf: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeInfo: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdHasField: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTypeId: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetEvalBranchQuota: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAlignCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdImplicitCast: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResolveResult: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResetResult: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdOpaqueType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSetAlignStack: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdArgType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdTagType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdExport: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrorReturnTrace: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdErrorUnion: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAtomicRmw: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSaveErrRetAddr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAddImplicitReturnType: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdFloatOp: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdMulAdd: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAtomicLoad: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAtomicStore: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdEnumToInt: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdCheckRuntimeScope: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdDeclVarGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdArrayToVector: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorToArray: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdPtrOfArrayToSlice: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAssertZero: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAssertNonNull: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResizeSlice: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdHasDecl: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUndeclaredIdent: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAllocaSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAllocaGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdEndExpr: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdUnionInitNamedField: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSuspendBegin: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSuspendFinish: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdResume: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAwaitSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdAwaitGen: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSpillBegin: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdSpillEnd: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdVectorExtractElem: + return destroy(reinterpret_cast(inst), name); + } + zig_unreachable(); +} + static void ira_ref(IrAnalyze *ira) { ira->ref_count += 1; } @@ -262,6 +609,22 @@ static void ira_deref(IrAnalyze *ira) { return; } assert(ira->ref_count != 0); + + for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) { + IrBasicBlock *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i]; + for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) { + IrInstruction *pass1_inst = pass1_bb->instruction_list.items[inst_i]; + destroy_instruction(pass1_inst); + } + destroy(pass1_bb, "IrBasicBlock"); + } + ira->old_irb.exec->basic_block_list.deinit(); + ira->old_irb.exec->tld_list.deinit(); + // cannot destroy here because of var->owner_exec + //destroy(ira->old_irb.exec, "IrExecutablePass1"); + ira->src_implicit_return_type_list.deinit(); + ira->resume_stack.deinit(); + ira->exec_context.mem_slot_list.deinit(); destroy(ira, "IrAnalyze"); } @@ -4202,7 +4565,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod IrInstruction **incoming_values = allocate(2); incoming_values[0] = val1; incoming_values[1] = val2; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = post_val1_block; incoming_blocks[1] = post_val2_block; @@ -4292,7 +4655,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode IrInstruction **incoming_values = allocate(2); incoming_values[0] = null_result; incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_null_block; incoming_blocks[1] = after_ok_block; IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); @@ -6057,7 +6420,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; @@ -7409,7 +7772,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; @@ -7506,7 +7869,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode * IrInstruction **incoming_values = allocate(2); incoming_values[0] = then_expr_result; incoming_values[1] = else_expr_result; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_then_block; incoming_blocks[1] = after_else_block; @@ -8102,7 +8465,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode IrInstruction **incoming_values = allocate(2); incoming_values[0] = err_result; incoming_values[1] = unwrapped_payload; - IrBasicBlock **incoming_blocks = allocate(2); + IrBasicBlock **incoming_blocks = allocate(2, "IrBasicBlock *"); incoming_blocks[0] = after_err_block; incoming_blocks[1] = after_ok_block; IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); @@ -8690,7 +9053,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) { assert(fn_entry); - IrExecutable *ir_executable = &fn_entry->ir_executable; + IrExecutable *ir_executable = fn_entry->ir_executable; AstNode *body_node = fn_entry->body_node; assert(fn_entry->child_scope); @@ -11565,7 +11928,7 @@ ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, if (expected_type != nullptr && type_is_invalid(expected_type)) return codegen->invalid_instruction->value; - IrExecutable *ir_executable = allocate(1); + IrExecutable *ir_executable = allocate(1, "IrExecutablePass1"); ir_executable->source_node = source_node; ir_executable->parent_exec = parent_exec; ir_executable->name = exec_name; @@ -11587,7 +11950,7 @@ ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, ir_print(codegen, stderr, ir_executable, 2, IrPassSrc); fprintf(stderr, "}\n"); } - IrExecutable *analyzed_executable = allocate(1); + IrExecutable *analyzed_executable = allocate(1, "IrExecutablePass2"); analyzed_executable->source_node = source_node; analyzed_executable->parent_exec = parent_exec; analyzed_executable->source_exec = ir_executable; @@ -15752,6 +16115,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira, assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length); ZigValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index); copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const); + ira_ref(var->owner_exec->analysis); if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) { return ir_const_void(ira, &decl_var_instruction->base); @@ -17522,8 +17886,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c if (type_is_invalid(impl_fn->type_entry)) return ira->codegen->invalid_instruction; - impl_fn->ir_executable.source_node = call_instruction->base.source_node; - impl_fn->ir_executable.parent_exec = ira->new_irb.exec; + impl_fn->ir_executable->source_node = call_instruction->base.source_node; + impl_fn->ir_executable->parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.source_node = call_instruction->base.source_node; impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; diff --git a/src/list.hpp b/src/list.hpp index 59782b46a8..4b2833843b 100644 --- a/src/list.hpp +++ b/src/list.hpp @@ -13,7 +13,7 @@ template struct ZigList { void deinit() { - free(items); + deallocate(items, capacity); } void append(const T& item) { ensure_capacity(length + 1);