authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-10 14:03:03-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-10 14:03:03-04:00
logcfaebb20d8906d31cedc59e39e6a9286967a931a
tree33d0c3994bfb658937e2e692a946bc8a2eca4fe5
parentb5d07297dec61a3993dfe91ceee2c87672db1e8e
parent0ce6934e2631eb3beca817d3bce12ecb13aafa13

Merge remote-tracking branch 'origin/master' into llvm7


42 files changed, 3056 insertions(+), 790 deletions(-)

CMakeLists.txt+5
...@@ -458,6 +458,11 @@ set(ZIG_STD_FILES...@@ -458,6 +458,11 @@ set(ZIG_STD_FILES
458 "elf.zig"458 "elf.zig"
459 "empty.zig"459 "empty.zig"
460 "event.zig"460 "event.zig"
461 "event/channel.zig"
462 "event/lock.zig"
463 "event/locked.zig"
464 "event/loop.zig"
465 "event/tcp.zig"
461 "fmt/errol/enum3.zig"466 "fmt/errol/enum3.zig"
462 "fmt/errol/index.zig"467 "fmt/errol/index.zig"
463 "fmt/errol/lookup.zig"468 "fmt/errol/lookup.zig"
doc/docgen.zig+16-13
...@@ -689,7 +689,10 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {...@@ -689,7 +689,10 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
689fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {689fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {
690 var code_progress_index: usize = 0;690 var code_progress_index: usize = 0;
691691
692 const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe));692 var env_map = try os.getEnvMap(allocator);
693 try env_map.set("ZIG_DEBUG_COLOR", "1");
694
695 const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, &env_map, zig_exe));
693696
694 for (toc.nodes) |node| {697 for (toc.nodes) |node| {
695 switch (node) {698 switch (node) {
...@@ -778,12 +781,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -778,12 +781,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
778 try build_args.append("c");781 try build_args.append("c");
779 try out.print(" --library c");782 try out.print(" --library c");
780 }783 }
781 _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");784 _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
782785
783 const run_args = [][]const u8{tmp_bin_file_name};786 const run_args = [][]const u8{tmp_bin_file_name};
784787
785 const result = if (expected_outcome == ExpectedOutcome.Fail) blk: {788 const result = if (expected_outcome == ExpectedOutcome.Fail) blk: {
786 const result = try os.ChildProcess.exec(allocator, run_args, null, null, max_doc_file_size);789 const result = try os.ChildProcess.exec(allocator, run_args, null, &env_map, max_doc_file_size);
787 switch (result.term) {790 switch (result.term) {
788 os.ChildProcess.Term.Exited => |exit_code| {791 os.ChildProcess.Term.Exited => |exit_code| {
789 if (exit_code == 0) {792 if (exit_code == 0) {
...@@ -799,7 +802,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -799,7 +802,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
799 }802 }
800 break :blk result;803 break :blk result;
801 } else blk: {804 } else blk: {
802 break :blk exec(allocator, run_args) catch return parseError(tokenizer, code.source_token, "example crashed");805 break :blk exec(allocator, &env_map, run_args) catch return parseError(tokenizer, code.source_token, "example crashed");
803 };806 };
804807
805 const escaped_stderr = try escapeHtml(allocator, result.stderr);808 const escaped_stderr = try escapeHtml(allocator, result.stderr);
...@@ -845,7 +848,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -845,7 +848,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
845 "msvc",848 "msvc",
846 });849 });
847 }850 }
848 const result = exec(allocator, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");851 const result = exec(allocator, &env_map, test_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "test failed");
849 const escaped_stderr = try escapeHtml(allocator, result.stderr);852 const escaped_stderr = try escapeHtml(allocator, result.stderr);
850 const escaped_stdout = try escapeHtml(allocator, result.stdout);853 const escaped_stdout = try escapeHtml(allocator, result.stdout);
851 try out.print("\n{}{}</code></pre>\n", escaped_stderr, escaped_stdout);854 try out.print("\n{}{}</code></pre>\n", escaped_stderr, escaped_stdout);
...@@ -877,7 +880,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -877,7 +880,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
877 try out.print(" --release-small");880 try out.print(" --release-small");
878 },881 },
879 }882 }
880 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);883 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size);
881 switch (result.term) {884 switch (result.term) {
882 os.ChildProcess.Term.Exited => |exit_code| {885 os.ChildProcess.Term.Exited => |exit_code| {
883 if (exit_code == 0) {886 if (exit_code == 0) {
...@@ -923,7 +926,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -923,7 +926,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
923 builtin.Mode.ReleaseSmall => try test_args.append("--release-small"),926 builtin.Mode.ReleaseSmall => try test_args.append("--release-small"),
924 }927 }
925928
926 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);929 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, &env_map, max_doc_file_size);
927 switch (result.term) {930 switch (result.term) {
928 os.ChildProcess.Term.Exited => |exit_code| {931 os.ChildProcess.Term.Exited => |exit_code| {
929 if (exit_code == 0) {932 if (exit_code == 0) {
...@@ -1000,7 +1003,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1000,7 +1003,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1000 }1003 }
10011004
1002 if (maybe_error_match) |error_match| {1005 if (maybe_error_match) |error_match| {
1003 const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, null, max_doc_file_size);1006 const result = try os.ChildProcess.exec(allocator, build_args.toSliceConst(), null, &env_map, max_doc_file_size);
1004 switch (result.term) {1007 switch (result.term) {
1005 os.ChildProcess.Term.Exited => |exit_code| {1008 os.ChildProcess.Term.Exited => |exit_code| {
1006 if (exit_code == 0) {1009 if (exit_code == 0) {
...@@ -1032,7 +1035,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1032,7 +1035,7 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1032 try out.print("</code></pre>\n");1035 try out.print("</code></pre>\n");
1033 }1036 }
1034 } else {1037 } else {
1035 _ = exec(allocator, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");1038 _ = exec(allocator, &env_map, build_args.toSliceConst()) catch return parseError(tokenizer, code.source_token, "example failed to compile");
1036 }1039 }
1037 if (!code.is_inline) {1040 if (!code.is_inline) {
1038 try out.print("</code></pre>\n");1041 try out.print("</code></pre>\n");
...@@ -1045,8 +1048,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1045,8 +1048,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1045 }1048 }
1046}1049}
10471050
1048fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.ExecResult {1051fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !os.ChildProcess.ExecResult {
1049 const result = try os.ChildProcess.exec(allocator, args, null, null, max_doc_file_size);1052 const result = try os.ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size);
1050 switch (result.term) {1053 switch (result.term) {
1051 os.ChildProcess.Term.Exited => |exit_code| {1054 os.ChildProcess.Term.Exited => |exit_code| {
1052 if (exit_code != 0) {1055 if (exit_code != 0) {
...@@ -1070,8 +1073,8 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex...@@ -1070,8 +1073,8 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex
1070 return result;1073 return result;
1071}1074}
10721075
1073fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 {1076fn getBuiltinCode(allocator: *mem.Allocator, env_map: *std.BufMap, zig_exe: []const u8) ![]const u8 {
1074 const result = try exec(allocator, []const []const u8{1077 const result = try exec(allocator, env_map, []const []const u8{
1075 zig_exe,1078 zig_exe,
1076 "builtin",1079 "builtin",
1077 });1080 });
doc/langref.html.in+149-10
...@@ -679,7 +679,7 @@ fn divide(a: i32, b: i32) i32 {...@@ -679,7 +679,7 @@ fn divide(a: i32, b: i32) i32 {
679 {#header_open|Float Literals#}679 {#header_open|Float Literals#}
680 <p>680 <p>
681 Float literals have type <code>comptime_float</code> which is guaranteed to hold at least all possible values681 Float literals have type <code>comptime_float</code> which is guaranteed to hold at least all possible values
682 that the largest other floating point type can hold. Float literals implicitly cast to any other type.682 that the largest other floating point type can hold. Float literals {#link|implicitly cast|Implicit Casts#} to any other type.
683 </p>683 </p>
684 {#code_begin|syntax#}684 {#code_begin|syntax#}
685const floating_point = 123.0E+77;685const floating_point = 123.0E+77;
...@@ -1604,7 +1604,7 @@ test "variable alignment" {...@@ -1604,7 +1604,7 @@ test "variable alignment" {
1604 }1604 }
1605}1605}
1606 {#code_end#}1606 {#code_end#}
1607 <p>In the same way that a <code>*i32</code> can be implicitly cast to a1607 <p>In the same way that a <code>*i32</code> can be {#link|implicitly cast|Implicit Casts#} to a
1608 <code>*const i32</code>, a pointer with a larger alignment can be implicitly1608 <code>*const i32</code>, a pointer with a larger alignment can be implicitly
1609 cast to a pointer with a smaller alignment, but not vice versa.1609 cast to a pointer with a smaller alignment, but not vice versa.
1610 </p>1610 </p>
...@@ -2968,7 +2968,7 @@ test "fn reflection" {...@@ -2968,7 +2968,7 @@ test "fn reflection" {
2968 However right now it is hard coded to be a <code>u16</code>. See <a href="https://github.com/ziglang/zig/issues/786">#768</a>.2968 However right now it is hard coded to be a <code>u16</code>. See <a href="https://github.com/ziglang/zig/issues/786">#768</a>.
2969 </p>2969 </p>
2970 <p>2970 <p>
2971 You can implicitly cast an error from a subset to its superset:2971 You can {#link|implicitly cast|Implicit Casts#} an error from a subset to its superset:
2972 </p>2972 </p>
2973 {#code_begin|test#}2973 {#code_begin|test#}
2974const std = @import("std");2974const std = @import("std");
...@@ -3101,7 +3101,7 @@ test "parse u64" {...@@ -3101,7 +3101,7 @@ test "parse u64" {
3101 <p>3101 <p>
3102 Within the function definition, you can see some return statements that return3102 Within the function definition, you can see some return statements that return
3103 an error, and at the bottom a return statement that returns a <code>u64</code>.3103 an error, and at the bottom a return statement that returns a <code>u64</code>.
3104 Both types implicitly cast to <code>error!u64</code>.3104 Both types {#link|implicitly cast|Implicit Casts#} to <code>error!u64</code>.
3105 </p>3105 </p>
3106 <p>3106 <p>
3107 What it looks like to use this function varies depending on what you're3107 What it looks like to use this function varies depending on what you're
...@@ -5013,7 +5013,7 @@ comptime {...@@ -5013,7 +5013,7 @@ comptime {
5013 <p>5013 <p>
5014 If <code>x</code> is zero, <code>@clz</code> returns <code>T.bit_count</code>.5014 If <code>x</code> is zero, <code>@clz</code> returns <code>T.bit_count</code>.
5015 </p>5015 </p>
50165016 {#see_also|@ctz|@popCount#}
5017 {#header_close#}5017 {#header_close#}
5018 {#header_open|@cmpxchgStrong#}5018 {#header_open|@cmpxchgStrong#}
5019 <pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T</code></pre>5019 <pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: *T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) ?T</code></pre>
...@@ -5149,6 +5149,7 @@ test "main" {...@@ -5149,6 +5149,7 @@ test "main" {
5149 <p>5149 <p>
5150 If <code>x</code> is zero, <code>@ctz</code> returns <code>T.bit_count</code>.5150 If <code>x</code> is zero, <code>@ctz</code> returns <code>T.bit_count</code>.
5151 </p>5151 </p>
5152 {#see_also|@clz|@popCount#}
5152 {#header_close#}5153 {#header_close#}
5153 {#header_open|@divExact#}5154 {#header_open|@divExact#}
5154 <pre><code class="zig">@divExact(numerator: T, denominator: T) T</code></pre>5155 <pre><code class="zig">@divExact(numerator: T, denominator: T) T</code></pre>
...@@ -5631,6 +5632,16 @@ test "call foo" {...@@ -5631,6 +5632,16 @@ test "call foo" {
5631 </ul>5632 </ul>
5632 {#see_also|Root Source File#}5633 {#see_also|Root Source File#}
5633 {#header_close#}5634 {#header_close#}
5635 {#header_open|@popCount#}
5636 <pre><code class="zig">@popCount(integer: var) var</code></pre>
5637 <p>Counts the number of bits set in an integer.</p>
5638 <p>
5639 If <code>integer</code> is known at {#link|comptime#}, the return type is <code>comptime_int</code>.
5640 Otherwise, the return type is an unsigned integer with the minimum number
5641 of bits that can represent the bit count of the integer type.
5642 </p>
5643 {#see_also|@ctz|@clz#}
5644 {#header_close#}
5634 {#header_open|@ptrCast#}5645 {#header_open|@ptrCast#}
5635 <pre><code class="zig">@ptrCast(comptime DestType: type, value: var) DestType</code></pre>5646 <pre><code class="zig">@ptrCast(comptime DestType: type, value: var) DestType</code></pre>
5636 <p>5647 <p>
...@@ -6638,15 +6649,143 @@ pub fn main() void {...@@ -6638,15 +6649,143 @@ pub fn main() void {
6638 {#header_close#}6649 {#header_close#}
66396650
6640 {#header_open|Invalid Error Set Cast#}6651 {#header_open|Invalid Error Set Cast#}
6641 <p>TODO</p>6652 <p>At compile-time:</p>
6653 {#code_begin|test_err|error.B not a member of error set 'Set2'#}
6654const Set1 = error{
6655 A,
6656 B,
6657};
6658const Set2 = error{
6659 A,
6660 C,
6661};
6662comptime {
6663 _ = @errSetCast(Set2, Set1.B);
6664}
6665 {#code_end#}
6666 <p>At runtime:</p>
6667 {#code_begin|exe_err#}
6668const std = @import("std");
6669
6670const Set1 = error{
6671 A,
6672 B,
6673};
6674const Set2 = error{
6675 A,
6676 C,
6677};
6678pub fn main() void {
6679 foo(Set1.B);
6680}
6681fn foo(set1: Set1) void {
6682 const x = @errSetCast(Set2, set1);
6683 std.debug.warn("value: {}\n", x);
6684}
6685 {#code_end#}
6642 {#header_close#}6686 {#header_close#}
66436687
6644 {#header_open|Incorrect Pointer Alignment#}6688 {#header_open|Incorrect Pointer Alignment#}
6645 <p>TODO</p>6689 <p>At compile-time:</p>
66466690 {#code_begin|test_err|pointer address 0x1 is not aligned to 4 bytes#}
6691comptime {
6692 const ptr = @intToPtr(*i32, 0x1);
6693 const aligned = @alignCast(4, ptr);
6694}
6695 {#code_end#}
6696 <p>At runtime:</p>
6697 {#code_begin|exe_err#}
6698pub fn main() !void {
6699 var array align(4) = []u32{ 0x11111111, 0x11111111 };
6700 const bytes = @sliceToBytes(array[0..]);
6701 if (foo(bytes) != 0x11111111) return error.Wrong;
6702}
6703fn foo(bytes: []u8) u32 {
6704 const slice4 = bytes[1..5];
6705 const int_slice = @bytesToSlice(u32, @alignCast(4, slice4));
6706 return int_slice[0];
6707}
6708 {#code_end#}
6647 {#header_close#}6709 {#header_close#}
6648 {#header_open|Wrong Union Field Access#}6710 {#header_open|Wrong Union Field Access#}
6649 <p>TODO</p>6711 <p>At compile-time:</p>
6712 {#code_begin|test_err|accessing union field 'float' while field 'int' is set#}
6713comptime {
6714 var f = Foo{ .int = 42 };
6715 f.float = 12.34;
6716}
6717
6718const Foo = union {
6719 float: f32,
6720 int: u32,
6721};
6722 {#code_end#}
6723 <p>At runtime:</p>
6724 {#code_begin|exe_err#}
6725const std = @import("std");
6726
6727const Foo = union {
6728 float: f32,
6729 int: u32,
6730};
6731
6732pub fn main() void {
6733 var f = Foo{ .int = 42 };
6734 bar(&f);
6735}
6736
6737fn bar(f: *Foo) void {
6738 f.float = 12.34;
6739 std.debug.warn("value: {}\n", f.float);
6740}
6741 {#code_end#}
6742 <p>
6743 This safety is not available for <code>extern</code> or <code>packed</code> unions.
6744 </p>
6745 <p>
6746 To change the active field of a union, assign the entire union, like this:
6747 </p>
6748 {#code_begin|exe#}
6749const std = @import("std");
6750
6751const Foo = union {
6752 float: f32,
6753 int: u32,
6754};
6755
6756pub fn main() void {
6757 var f = Foo{ .int = 42 };
6758 bar(&f);
6759}
6760
6761fn bar(f: *Foo) void {
6762 f.* = Foo{ .float = 12.34 };
6763 std.debug.warn("value: {}\n", f.float);
6764}
6765 {#code_end#}
6766 <p>
6767 To change the active field of a union when a meaningful value for the field is not known,
6768 use {#link|undefined#}, like this:
6769 </p>
6770 {#code_begin|exe#}
6771const std = @import("std");
6772
6773const Foo = union {
6774 float: f32,
6775 int: u32,
6776};
6777
6778pub fn main() void {
6779 var f = Foo{ .int = 42 };
6780 f = Foo{ .float = undefined };
6781 bar(&f);
6782 std.debug.warn("value: {}\n", f.float);
6783}
6784
6785fn bar(f: *Foo) void {
6786 f.float = 12.34;
6787}
6788 {#code_end#}
6650 {#header_close#}6789 {#header_close#}
66516790
6652 {#header_open|Out of Bounds Float To Integer Cast#}6791 {#header_open|Out of Bounds Float To Integer Cast#}
...@@ -7337,7 +7476,7 @@ hljs.registerLanguage("zig", function(t) {...@@ -7337,7 +7476,7 @@ hljs.registerLanguage("zig", function(t) {
7337 a = t.IR + "\\s*\\(",7476 a = t.IR + "\\s*\\(",
7338 c = {7477 c = {
7339 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse",7478 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong resume cancel await async orelse",
7340 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum",7479 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage divTrunc divFloor enumTagName intToPtr ptrToInt panic ptrCast intCast floatCast intToFloat floatToInt boolToInt bytesToSlice sliceToBytes errSetCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz popCount import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo typeName newStackCall errorToInt intToError enumToInt intToEnum",
7341 literal: "true false null undefined"7480 literal: "true false null undefined"
7342 },7481 },
7343 n = [e, t.CLCM, t.CBCM, s, r];7482 n = [e, t.CLCM, t.CBCM, s, r];
doc/semantic_analysis.md deleted-74
...@@ -1,74 +0,0 @@
1# How Semantic Analysis Works
2
3We start with a set of files. Typically the user only has one entry point file,
4which imports the other files they want to use. However, the compiler may
5choose to add more files to the compilation, for example bootstrap.zig which
6contains the code that calls main.
7
8Our goal now is to treat everything that is marked with the `export` keyword
9as a root node, and then parse and semantically analyze as little as possible
10in order to fulfill these exports.
11
12So, some parts of the code very well may have uncaught semantic errors, but as
13long as the code is not referenced in any way, the compiler will not complain
14because the code may as well not exist. This is similar to the fact that code
15excluded from compilation with an `#ifdef` in C is not analyzed. Avoiding
16analyzing unused code will save compilation time - one of Zig's goals.
17
18So, for each file, we iterate over the top level declarations. The set of top
19level declarations are:
20
21 * Function Definition
22 * Global Variable Declaration
23 * Container Declaration (struct or enum)
24 * Error Value Declaration
25 * Use Declaration
26
27Each of these can have `export` attached to them except for error value
28declarations and use declarations.
29
30When we see a top level declaration during this iteration, we determine its
31unique name identifier within the file. For example, for a function definition,
32the unique name identifier is simply its name. Using this name we add the top
33level declaration to a map.
34
35If the top level declaration is exported, we add it to a set of exported top
36level identifiers.
37
38If the top level declaration is a use declaration, we add it to a set of use
39declarations.
40
41If the top level declaration is an error value declaration, we assign it a value
42and increment the count of error values.
43
44After this preliminary iteration over the top level declarations, we iterate
45over the use declarations and resolve them. To resolve a use declaration, we
46analyze the associated expression, verify that its type is the namespace type,
47and then add all the items from the namespace into the top level declaration
48map for the current file.
49
50To analyze an expression, we recurse the abstract syntax tree of the
51expression. Whenever we must look up a symbol, if the symbol exists already,
52we can use it. Otherwise, we look it up in the top level declaration map.
53If it exists, we can use it. Otherwise, we interrupt resolving this use
54declaration to resolve the next one. If a dependency loop is detected, emit
55an error. If all use declarations are resolved yet the symbol we need still
56does not exist, emit an error.
57
58To analyze an `@import` expression, find the referenced file, parse it, and
59add it to the set of files to perform semantic analysis on.
60
61Proceed through the rest of the use declarations the same way.
62
63If we make it through the use declarations without an error, then we have a
64complete map of all globals that exist in the current file.
65
66Next we iterate over the set of exported top level declarations.
67
68If it's a function definition, add it to the set of exported function
69definitions and resolve the function prototype only. Otherwise, resolve the
70top level declaration completely. This may involve recursively resolving other
71top level declarations that expressions depend on.
72
73Finally, iterate over the set of exported function definitions and analyze the
74bodies.
src-self-hosted/c.zig+2
...@@ -1,4 +1,6 @@...@@ -1,4 +1,6 @@
1pub use @cImport({1pub use @cImport({
2 @cDefine("__STDC_CONSTANT_MACROS", "");
3 @cDefine("__STDC_LIMIT_MACROS", "");
2 @cInclude("inttypes.h");4 @cInclude("inttypes.h");
3 @cInclude("config.h");5 @cInclude("config.h");
4 @cInclude("zig_llvm.h");6 @cInclude("zig_llvm.h");
src-self-hosted/main.zig+2-3
...@@ -384,7 +384,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo...@@ -384,7 +384,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
384 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);384 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
385 defer allocator.free(zig_lib_dir);385 defer allocator.free(zig_lib_dir);
386386
387 var loop = try event.Loop.init(allocator);387 var loop: event.Loop = undefined;
388 try loop.initMultiThreaded(allocator);
388389
389 var module = try Module.create(390 var module = try Module.create(
390 &loop,391 &loop,
...@@ -493,8 +494,6 @@ async fn processBuildEvents(module: *Module, watch: bool) void {...@@ -493,8 +494,6 @@ async fn processBuildEvents(module: *Module, watch: bool) void {
493 switch (build_event) {494 switch (build_event) {
494 Module.Event.Ok => {495 Module.Event.Ok => {
495 std.debug.warn("Build succeeded\n");496 std.debug.warn("Build succeeded\n");
496 // for now we stop after 1
497 module.loop.stop();
498 return;497 return;
499 },498 },
500 Module.Event.Error => |err| {499 Module.Event.Error => |err| {
src-self-hosted/module.zig+242-15
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const os = std.os;2const os = std.os;
3const io = std.io;3const io = std.io;
4const mem = std.mem;4const mem = std.mem;
5const Allocator = mem.Allocator;
5const Buffer = std.Buffer;6const Buffer = std.Buffer;
6const llvm = @import("llvm.zig");7const llvm = @import("llvm.zig");
7const c = @import("c.zig");8const c = @import("c.zig");
...@@ -13,6 +14,7 @@ const ArrayList = std.ArrayList;...@@ -13,6 +14,7 @@ const ArrayList = std.ArrayList;
13const errmsg = @import("errmsg.zig");14const errmsg = @import("errmsg.zig");
14const ast = std.zig.ast;15const ast = std.zig.ast;
15const event = std.event;16const event = std.event;
17const assert = std.debug.assert;
1618
17pub const Module = struct {19pub const Module = struct {
18 loop: *event.Loop,20 loop: *event.Loop,
...@@ -81,6 +83,8 @@ pub const Module = struct {...@@ -81,6 +83,8 @@ pub const Module = struct {
81 link_out_file: ?[]const u8,83 link_out_file: ?[]const u8,
82 events: *event.Channel(Event),84 events: *event.Channel(Event),
8385
86 exported_symbol_names: event.Locked(Decl.Table),
87
84 // TODO handle some of these earlier and report them in a way other than error codes88 // TODO handle some of these earlier and report them in a way other than error codes
85 pub const BuildError = error{89 pub const BuildError = error{
86 OutOfMemory,90 OutOfMemory,
...@@ -232,6 +236,7 @@ pub const Module = struct {...@@ -232,6 +236,7 @@ pub const Module = struct {
232 .test_name_prefix = null,236 .test_name_prefix = null,
233 .emit_file_type = Emit.Binary,237 .emit_file_type = Emit.Binary,
234 .link_out_file = null,238 .link_out_file = null,
239 .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
235 });240 });
236 }241 }
237242
...@@ -272,38 +277,91 @@ pub const Module = struct {...@@ -272,38 +277,91 @@ pub const Module = struct {
272 return;277 return;
273 };278 };
274 await (async self.events.put(Event.Ok) catch unreachable);279 await (async self.events.put(Event.Ok) catch unreachable);
280 // for now we stop after 1
281 return;
275 }282 }
276 }283 }
277284
278 async fn addRootSrc(self: *Module) !void {285 async fn addRootSrc(self: *Module) !void {
279 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");286 const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
287 // TODO async/await os.path.real
280 const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {288 const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| {
281 try printError("unable to get real path '{}': {}", root_src_path, err);289 try printError("unable to get real path '{}': {}", root_src_path, err);
282 return err;290 return err;
283 };291 };
284 errdefer self.a().free(root_src_real_path);292 errdefer self.a().free(root_src_real_path);
285293
294 // TODO async/await readFileAlloc()
286 const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {295 const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| {
287 try printError("unable to open '{}': {}", root_src_real_path, err);296 try printError("unable to open '{}': {}", root_src_real_path, err);
288 return err;297 return err;
289 };298 };
290 errdefer self.a().free(source_code);299 errdefer self.a().free(source_code);
291300
292 var tree = try std.zig.parse(self.a(), source_code);301 var parsed_file = ParsedFile{
293 defer tree.deinit();302 .tree = try std.zig.parse(self.a(), source_code),
294303 .realpath = root_src_real_path,
295 //var it = tree.root_node.decls.iterator();304 };
296 //while (it.next()) |decl_ptr| {305 errdefer parsed_file.tree.deinit();
297 // const decl = decl_ptr.*;306
298 // switch (decl.id) {307 const tree = &parsed_file.tree;
299 // ast.Node.Comptime => @panic("TODO"),308
300 // ast.Node.VarDecl => @panic("TODO"),309 // create empty struct for it
301 // ast.Node.UseDecl => @panic("TODO"),310 const decls = try Scope.Decls.create(self.a(), null);
302 // ast.Node.FnDef => @panic("TODO"),311 errdefer decls.destroy();
303 // ast.Node.TestDecl => @panic("TODO"),312
304 // else => unreachable,313 var it = tree.root_node.decls.iterator(0);
305 // }314 while (it.next()) |decl_ptr| {
306 //}315 const decl = decl_ptr.*;
316 switch (decl.id) {
317 ast.Node.Id.Comptime => @panic("TODO"),
318 ast.Node.Id.VarDecl => @panic("TODO"),
319 ast.Node.Id.FnProto => {
320 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
321
322 const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
323 @panic("TODO add compile error");
324 //try self.addCompileError(
325 // &parsed_file,
326 // fn_proto.fn_token,
327 // fn_proto.fn_token + 1,
328 // "missing function name",
329 //);
330 continue;
331 };
332
333 const fn_decl = try self.a().create(Decl.Fn{
334 .base = Decl{
335 .id = Decl.Id.Fn,
336 .name = name,
337 .visib = parseVisibToken(tree, fn_proto.visib_token),
338 .resolution = Decl.Resolution.Unresolved,
339 },
340 .value = Decl.Fn.Val{ .Unresolved = {} },
341 .fn_proto = fn_proto,
342 });
343 errdefer self.a().destroy(fn_decl);
344
345 // TODO make this parallel
346 try await try async self.addTopLevelDecl(tree, &fn_decl.base);
347 },
348 ast.Node.Id.TestDecl => @panic("TODO"),
349 else => unreachable,
350 }
351 }
352 }
353
354 async fn addTopLevelDecl(self: *Module, tree: *ast.Tree, decl: *Decl) !void {
355 const is_export = decl.isExported(tree);
356
357 {
358 const exported_symbol_names = await try async self.exported_symbol_names.acquire();
359 defer exported_symbol_names.release();
360
361 if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
362 @panic("TODO report compile error");
363 }
364 }
307 }365 }
308366
309 pub fn link(self: *Module, out_file: ?[]const u8) !void {367 pub fn link(self: *Module, out_file: ?[]const u8) !void {
...@@ -350,3 +408,172 @@ fn printError(comptime format: []const u8, args: ...) !void {...@@ -350,3 +408,172 @@ fn printError(comptime format: []const u8, args: ...) !void {
350 const out_stream = &stderr_file_out_stream.stream;408 const out_stream = &stderr_file_out_stream.stream;
351 try out_stream.print(format, args);409 try out_stream.print(format, args);
352}410}
411
412fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib {
413 if (optional_token_index) |token_index| {
414 const token = tree.tokens.at(token_index);
415 assert(token.id == Token.Id.Keyword_pub);
416 return Visib.Pub;
417 } else {
418 return Visib.Private;
419 }
420}
421
422pub const Scope = struct {
423 id: Id,
424 parent: ?*Scope,
425
426 pub const Id = enum {
427 Decls,
428 Block,
429 };
430
431 pub const Decls = struct {
432 base: Scope,
433 table: Decl.Table,
434
435 pub fn create(a: *Allocator, parent: ?*Scope) !*Decls {
436 const self = try a.create(Decls{
437 .base = Scope{
438 .id = Id.Decls,
439 .parent = parent,
440 },
441 .table = undefined,
442 });
443 errdefer a.destroy(self);
444
445 self.table = Decl.Table.init(a);
446 errdefer self.table.deinit();
447
448 return self;
449 }
450
451 pub fn destroy(self: *Decls) void {
452 self.table.deinit();
453 self.table.allocator.destroy(self);
454 self.* = undefined;
455 }
456 };
457
458 pub const Block = struct {
459 base: Scope,
460 };
461};
462
463pub const Visib = enum {
464 Private,
465 Pub,
466};
467
468pub const Decl = struct {
469 id: Id,
470 name: []const u8,
471 visib: Visib,
472 resolution: Resolution,
473
474 pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
475
476 pub fn isExported(base: *const Decl, tree: *ast.Tree) bool {
477 switch (base.id) {
478 Id.Fn => {
479 const fn_decl = @fieldParentPtr(Fn, "base", base);
480 return fn_decl.isExported(tree);
481 },
482 else => return false,
483 }
484 }
485
486 pub const Resolution = enum {
487 Unresolved,
488 InProgress,
489 Invalid,
490 Ok,
491 };
492
493 pub const Id = enum {
494 Var,
495 Fn,
496 CompTime,
497 };
498
499 pub const Var = struct {
500 base: Decl,
501 };
502
503 pub const Fn = struct {
504 base: Decl,
505 value: Val,
506 fn_proto: *const ast.Node.FnProto,
507
508 // TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
509 pub const Val = union {
510 Unresolved: void,
511 Ok: *Value.Fn,
512 };
513
514 pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
515 return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
516 const token = tree.tokens.at(tok_index);
517 break :x switch (token.id) {
518 Token.Id.Extern => tree.tokenSlicePtr(token),
519 else => null,
520 };
521 } else null;
522 }
523
524 pub fn isExported(self: Fn, tree: *ast.Tree) bool {
525 if (self.fn_proto.extern_export_inline_token) |tok_index| {
526 const token = tree.tokens.at(tok_index);
527 return token.id == Token.Id.Keyword_export;
528 } else {
529 return false;
530 }
531 }
532 };
533
534 pub const CompTime = struct {
535 base: Decl,
536 };
537};
538
539pub const Value = struct {
540 pub const Fn = struct {};
541};
542
543pub const Type = struct {
544 id: Id,
545
546 pub const Id = enum {
547 Type,
548 Void,
549 Bool,
550 NoReturn,
551 Int,
552 Float,
553 Pointer,
554 Array,
555 Struct,
556 ComptimeFloat,
557 ComptimeInt,
558 Undefined,
559 Null,
560 Optional,
561 ErrorUnion,
562 ErrorSet,
563 Enum,
564 Union,
565 Fn,
566 Opaque,
567 Promise,
568 };
569
570 pub const Struct = struct {
571 base: Type,
572 decls: *Scope.Decls,
573 };
574};
575
576pub const ParsedFile = struct {
577 tree: ast.Tree,
578 realpath: []const u8,
579};
src/all_types.hpp+13
...@@ -1352,6 +1352,7 @@ enum BuiltinFnId {...@@ -1352,6 +1352,7 @@ enum BuiltinFnId {
1352 BuiltinFnIdCompileLog,1352 BuiltinFnIdCompileLog,
1353 BuiltinFnIdCtz,1353 BuiltinFnIdCtz,
1354 BuiltinFnIdClz,1354 BuiltinFnIdClz,
1355 BuiltinFnIdPopCount,
1355 BuiltinFnIdImport,1356 BuiltinFnIdImport,
1356 BuiltinFnIdCImport,1357 BuiltinFnIdCImport,
1357 BuiltinFnIdErrName,1358 BuiltinFnIdErrName,
...@@ -1477,6 +1478,7 @@ bool type_id_eql(TypeId a, TypeId b);...@@ -1477,6 +1478,7 @@ bool type_id_eql(TypeId a, TypeId b);
1477enum ZigLLVMFnId {1478enum ZigLLVMFnId {
1478 ZigLLVMFnIdCtz,1479 ZigLLVMFnIdCtz,
1479 ZigLLVMFnIdClz,1480 ZigLLVMFnIdClz,
1481 ZigLLVMFnIdPopCount,
1480 ZigLLVMFnIdOverflowArithmetic,1482 ZigLLVMFnIdOverflowArithmetic,
1481 ZigLLVMFnIdFloor,1483 ZigLLVMFnIdFloor,
1482 ZigLLVMFnIdCeil,1484 ZigLLVMFnIdCeil,
...@@ -1499,6 +1501,9 @@ struct ZigLLVMFnKey {...@@ -1499,6 +1501,9 @@ struct ZigLLVMFnKey {
1499 struct {1501 struct {
1500 uint32_t bit_count;1502 uint32_t bit_count;
1501 } clz;1503 } clz;
1504 struct {
1505 uint32_t bit_count;
1506 } pop_count;
1502 struct {1507 struct {
1503 uint32_t bit_count;1508 uint32_t bit_count;
1504 } floating;1509 } floating;
...@@ -2048,6 +2053,7 @@ enum IrInstructionId {...@@ -2048,6 +2053,7 @@ enum IrInstructionId {
2048 IrInstructionIdUnionTag,2053 IrInstructionIdUnionTag,
2049 IrInstructionIdClz,2054 IrInstructionIdClz,
2050 IrInstructionIdCtz,2055 IrInstructionIdCtz,
2056 IrInstructionIdPopCount,
2051 IrInstructionIdImport,2057 IrInstructionIdImport,
2052 IrInstructionIdCImport,2058 IrInstructionIdCImport,
2053 IrInstructionIdCInclude,2059 IrInstructionIdCInclude,
...@@ -2191,6 +2197,7 @@ struct IrInstructionSwitchBr {...@@ -2191,6 +2197,7 @@ struct IrInstructionSwitchBr {
2191 size_t case_count;2197 size_t case_count;
2192 IrInstructionSwitchBrCase *cases;2198 IrInstructionSwitchBrCase *cases;
2193 IrInstruction *is_comptime;2199 IrInstruction *is_comptime;
2200 IrInstruction *switch_prongs_void;
2194};2201};
21952202
2196struct IrInstructionSwitchVar {2203struct IrInstructionSwitchVar {
...@@ -2542,6 +2549,12 @@ struct IrInstructionClz {...@@ -2542,6 +2549,12 @@ struct IrInstructionClz {
2542 IrInstruction *value;2549 IrInstruction *value;
2543};2550};
25442551
2552struct IrInstructionPopCount {
2553 IrInstruction base;
2554
2555 IrInstruction *value;
2556};
2557
2545struct IrInstructionUnionTag {2558struct IrInstructionUnionTag {
2546 IrInstruction base;2559 IrInstruction base;
25472560
src/analyze.cpp+41
...@@ -212,6 +212,43 @@ static uint8_t bits_needed_for_unsigned(uint64_t x) {...@@ -212,6 +212,43 @@ static uint8_t bits_needed_for_unsigned(uint64_t x) {
212 return (upper >= x) ? base : (base + 1);212 return (upper >= x) ? base : (base + 1);
213}213}
214214
215AstNode *type_decl_node(TypeTableEntry *type_entry) {
216 switch (type_entry->id) {
217 case TypeTableEntryIdInvalid:
218 zig_unreachable();
219 case TypeTableEntryIdStruct:
220 return type_entry->data.structure.decl_node;
221 case TypeTableEntryIdEnum:
222 return type_entry->data.enumeration.decl_node;
223 case TypeTableEntryIdUnion:
224 return type_entry->data.unionation.decl_node;
225 case TypeTableEntryIdOpaque:
226 case TypeTableEntryIdMetaType:
227 case TypeTableEntryIdVoid:
228 case TypeTableEntryIdBool:
229 case TypeTableEntryIdUnreachable:
230 case TypeTableEntryIdInt:
231 case TypeTableEntryIdFloat:
232 case TypeTableEntryIdPointer:
233 case TypeTableEntryIdArray:
234 case TypeTableEntryIdComptimeFloat:
235 case TypeTableEntryIdComptimeInt:
236 case TypeTableEntryIdUndefined:
237 case TypeTableEntryIdNull:
238 case TypeTableEntryIdOptional:
239 case TypeTableEntryIdErrorUnion:
240 case TypeTableEntryIdErrorSet:
241 case TypeTableEntryIdFn:
242 case TypeTableEntryIdNamespace:
243 case TypeTableEntryIdBlock:
244 case TypeTableEntryIdBoundFn:
245 case TypeTableEntryIdArgTuple:
246 case TypeTableEntryIdPromise:
247 return nullptr;
248 }
249 zig_unreachable();
250}
251
215bool type_is_complete(TypeTableEntry *type_entry) {252bool type_is_complete(TypeTableEntry *type_entry) {
216 switch (type_entry->id) {253 switch (type_entry->id) {
217 case TypeTableEntryIdInvalid:254 case TypeTableEntryIdInvalid:
...@@ -5939,6 +5976,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {...@@ -5939,6 +5976,8 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
5939 return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;5976 return (uint32_t)(x.data.ctz.bit_count) * (uint32_t)810453934;
5940 case ZigLLVMFnIdClz:5977 case ZigLLVMFnIdClz:
5941 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;5978 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
5979 case ZigLLVMFnIdPopCount:
5980 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)101195049;
5942 case ZigLLVMFnIdFloor:5981 case ZigLLVMFnIdFloor:
5943 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;5982 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
5944 case ZigLLVMFnIdCeil:5983 case ZigLLVMFnIdCeil:
...@@ -5961,6 +6000,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {...@@ -5961,6 +6000,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
5961 return a.data.ctz.bit_count == b.data.ctz.bit_count;6000 return a.data.ctz.bit_count == b.data.ctz.bit_count;
5962 case ZigLLVMFnIdClz:6001 case ZigLLVMFnIdClz:
5963 return a.data.clz.bit_count == b.data.clz.bit_count;6002 return a.data.clz.bit_count == b.data.clz.bit_count;
6003 case ZigLLVMFnIdPopCount:
6004 return a.data.pop_count.bit_count == b.data.pop_count.bit_count;
5964 case ZigLLVMFnIdFloor:6005 case ZigLLVMFnIdFloor:
5965 case ZigLLVMFnIdCeil:6006 case ZigLLVMFnIdCeil:
5966 case ZigLLVMFnIdSqrt:6007 case ZigLLVMFnIdSqrt:
src/analyze.hpp+1
...@@ -202,5 +202,6 @@ uint32_t get_coro_frame_align_bytes(CodeGen *g);...@@ -202,5 +202,6 @@ uint32_t get_coro_frame_align_bytes(CodeGen *g);
202bool fn_type_can_fail(FnTypeId *fn_type_id);202bool fn_type_can_fail(FnTypeId *fn_type_id);
203bool type_can_fail(TypeTableEntry *type_entry);203bool type_can_fail(TypeTableEntry *type_entry);
204bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type);204bool fn_eval_cacheable(Scope *scope, TypeTableEntry *return_type);
205AstNode *type_decl_node(TypeTableEntry *type_entry);
205206
206#endif207#endif
src/bigint.cpp+31
...@@ -1593,6 +1593,37 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) {...@@ -1593,6 +1593,37 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) {
1593 }1593 }
1594}1594}
15951595
1596size_t bigint_popcount_unsigned(const BigInt *bi) {
1597 assert(!bi->is_negative);
1598 if (bi->digit_count == 0)
1599 return 0;
1600
1601 size_t count = 0;
1602 size_t bit_count = bi->digit_count * 64;
1603 for (size_t i = 0; i < bit_count; i += 1) {
1604 if (bit_at_index(bi, i))
1605 count += 1;
1606 }
1607 return count;
1608}
1609
1610size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) {
1611 if (bit_count == 0)
1612 return 0;
1613 if (bi->digit_count == 0)
1614 return 0;
1615
1616 BigInt twos_comp = {0};
1617 to_twos_complement(&twos_comp, bi, bit_count);
1618
1619 size_t count = 0;
1620 for (size_t i = 0; i < bit_count; i += 1) {
1621 if (bit_at_index(&twos_comp, i))
1622 count += 1;
1623 }
1624 return count;
1625}
1626
1596size_t bigint_ctz(const BigInt *bi, size_t bit_count) {1627size_t bigint_ctz(const BigInt *bi, size_t bit_count) {
1597 if (bit_count == 0)1628 if (bit_count == 0)
1598 return 0;1629 return 0;
src/bigint.hpp+2
...@@ -81,6 +81,8 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base);...@@ -81,6 +81,8 @@ void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base);
8181
82size_t bigint_ctz(const BigInt *bi, size_t bit_count);82size_t bigint_ctz(const BigInt *bi, size_t bit_count);
83size_t bigint_clz(const BigInt *bi, size_t bit_count);83size_t bigint_clz(const BigInt *bi, size_t bit_count);
84size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count);
85size_t bigint_popcount_unsigned(const BigInt *bi);
8486
85size_t bigint_bits_needed(const BigInt *op);87size_t bigint_bits_needed(const BigInt *op);
8688
src/codegen.cpp+38-7
...@@ -2927,18 +2927,26 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI...@@ -2927,18 +2927,26 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI
2927 return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");2927 return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, "");
2928 } else if (array_type->id == TypeTableEntryIdStruct) {2928 } else if (array_type->id == TypeTableEntryIdStruct) {
2929 assert(array_type->data.structure.is_slice);2929 assert(array_type->data.structure.is_slice);
2930 if (!type_has_bits(instruction->base.value.type)) {
2931 if (safety_check_on) {
2932 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind);
2933 add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, array_ptr);
2934 }
2935 return nullptr;
2936 }
2937
2930 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);2938 assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
2931 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);2939 assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
29322940
2933 if (safety_check_on) {2941 if (safety_check_on) {
2934 size_t len_index = array_type->data.structure.fields[1].gen_index;2942 size_t len_index = array_type->data.structure.fields[slice_len_index].gen_index;
2935 assert(len_index != SIZE_MAX);2943 assert(len_index != SIZE_MAX);
2936 LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, "");2944 LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, "");
2937 LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, "");2945 LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, "");
2938 add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);2946 add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len);
2939 }2947 }
29402948
2941 size_t ptr_index = array_type->data.structure.fields[0].gen_index;2949 size_t ptr_index = array_type->data.structure.fields[slice_ptr_index].gen_index;
2942 assert(ptr_index != SIZE_MAX);2950 assert(ptr_index != SIZE_MAX);
2943 LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, "");2951 LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, "");
2944 LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, "");2952 LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, "");
...@@ -3353,14 +3361,22 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,...@@ -3353,14 +3361,22 @@ static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
3353static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {3361static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, BuiltinFnId fn_id) {
3354 ZigLLVMFnKey key = {};3362 ZigLLVMFnKey key = {};
3355 const char *fn_name;3363 const char *fn_name;
3364 uint32_t n_args;
3356 if (fn_id == BuiltinFnIdCtz) {3365 if (fn_id == BuiltinFnIdCtz) {
3357 fn_name = "cttz";3366 fn_name = "cttz";
3367 n_args = 2;
3358 key.id = ZigLLVMFnIdCtz;3368 key.id = ZigLLVMFnIdCtz;
3359 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;3369 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3360 } else if (fn_id == BuiltinFnIdClz) {3370 } else if (fn_id == BuiltinFnIdClz) {
3361 fn_name = "ctlz";3371 fn_name = "ctlz";
3372 n_args = 2;
3362 key.id = ZigLLVMFnIdClz;3373 key.id = ZigLLVMFnIdClz;
3363 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;3374 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3375 } else if (fn_id == BuiltinFnIdPopCount) {
3376 fn_name = "ctpop";
3377 n_args = 1;
3378 key.id = ZigLLVMFnIdPopCount;
3379 key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count;
3364 } else {3380 } else {
3365 zig_unreachable();3381 zig_unreachable();
3366 }3382 }
...@@ -3375,7 +3391,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui...@@ -3375,7 +3391,7 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
3375 int_type->type_ref,3391 int_type->type_ref,
3376 LLVMInt1Type(),3392 LLVMInt1Type(),
3377 };3393 };
3378 LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, 2, false);3394 LLVMTypeRef fn_type = LLVMFunctionType(int_type->type_ref, param_types, n_args, false);
3379 LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type);3395 LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type);
3380 assert(LLVMGetIntrinsicID(fn_val));3396 assert(LLVMGetIntrinsicID(fn_val));
33813397
...@@ -3408,6 +3424,14 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru...@@ -3408,6 +3424,14 @@ static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutable *executable, IrInstru
3408 return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int);3424 return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int);
3409}3425}
34103426
3427static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutable *executable, IrInstructionPopCount *instruction) {
3428 TypeTableEntry *int_type = instruction->value->value.type;
3429 LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount);
3430 LLVMValueRef operand = ir_llvm_value(g, instruction->value);
3431 LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, &operand, 1, "");
3432 return gen_widen_or_shorten(g, false, int_type, instruction->base.value.type, wrong_size_int);
3433}
3434
3411static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) {3435static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutable *executable, IrInstructionSwitchBr *instruction) {
3412 LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value);3436 LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value);
3413 LLVMBasicBlockRef else_block = instruction->else_block->llvm_block;3437 LLVMBasicBlockRef else_block = instruction->else_block->llvm_block;
...@@ -3894,11 +3918,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst...@@ -3894,11 +3918,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
3894 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);3918 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
3895 }3919 }
38963920
3897 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");3921 if (type_has_bits(array_type)) {
3898 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");3922 size_t gen_ptr_index = instruction->base.value.type->data.structure.fields[slice_ptr_index].gen_index;
3899 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);3923 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
3924 LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
3925 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
3926 }
39003927
3901 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");3928 size_t gen_len_index = instruction->base.value.type->data.structure.fields[slice_len_index].gen_index;
3929 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
3902 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");3930 LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
3903 gen_store_untyped(g, len_value, len_field_ptr, 0, false);3931 gen_store_untyped(g, len_value, len_field_ptr, 0, false);
39043932
...@@ -4730,6 +4758,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4730,6 +4758,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4730 return ir_render_clz(g, executable, (IrInstructionClz *)instruction);4758 return ir_render_clz(g, executable, (IrInstructionClz *)instruction);
4731 case IrInstructionIdCtz:4759 case IrInstructionIdCtz:
4732 return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction);4760 return ir_render_ctz(g, executable, (IrInstructionCtz *)instruction);
4761 case IrInstructionIdPopCount:
4762 return ir_render_pop_count(g, executable, (IrInstructionPopCount *)instruction);
4733 case IrInstructionIdSwitchBr:4763 case IrInstructionIdSwitchBr:
4734 return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction);4764 return ir_render_switch_br(g, executable, (IrInstructionSwitchBr *)instruction);
4735 case IrInstructionIdPhi:4765 case IrInstructionIdPhi:
...@@ -6241,6 +6271,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6241,6 +6271,7 @@ static void define_builtin_fns(CodeGen *g) {
6241 create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1);6271 create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1);
6242 create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 1);6272 create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 1);
6243 create_builtin_fn(g, BuiltinFnIdClz, "clz", 1);6273 create_builtin_fn(g, BuiltinFnIdClz, "clz", 1);
6274 create_builtin_fn(g, BuiltinFnIdPopCount, "popCount", 1);
6244 create_builtin_fn(g, BuiltinFnIdImport, "import", 1);6275 create_builtin_fn(g, BuiltinFnIdImport, "import", 1);
6245 create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);6276 create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);
6246 create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);6277 create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);
src/ir.cpp+182-43
...@@ -82,6 +82,7 @@ struct ConstCastSliceMismatch;...@@ -82,6 +82,7 @@ struct ConstCastSliceMismatch;
82struct ConstCastErrUnionErrSetMismatch;82struct ConstCastErrUnionErrSetMismatch;
83struct ConstCastErrUnionPayloadMismatch;83struct ConstCastErrUnionPayloadMismatch;
84struct ConstCastErrSetMismatch;84struct ConstCastErrSetMismatch;
85struct ConstCastTypeMismatch;
8586
86struct ConstCastOnly {87struct ConstCastOnly {
87 ConstCastResultId id;88 ConstCastResultId id;
...@@ -92,6 +93,7 @@ struct ConstCastOnly {...@@ -92,6 +93,7 @@ struct ConstCastOnly {
92 ConstCastOptionalMismatch *optional;93 ConstCastOptionalMismatch *optional;
93 ConstCastErrUnionPayloadMismatch *error_union_payload;94 ConstCastErrUnionPayloadMismatch *error_union_payload;
94 ConstCastErrUnionErrSetMismatch *error_union_error_set;95 ConstCastErrUnionErrSetMismatch *error_union_error_set;
96 ConstCastTypeMismatch *type_mismatch;
95 ConstCastOnly *return_type;97 ConstCastOnly *return_type;
96 ConstCastOnly *async_allocator_type;98 ConstCastOnly *async_allocator_type;
97 ConstCastOnly *null_wrap_ptr_child;99 ConstCastOnly *null_wrap_ptr_child;
...@@ -100,6 +102,11 @@ struct ConstCastOnly {...@@ -100,6 +102,11 @@ struct ConstCastOnly {
100 } data;102 } data;
101};103};
102104
105struct ConstCastTypeMismatch {
106 TypeTableEntry *wanted_type;
107 TypeTableEntry *actual_type;
108};
109
103struct ConstCastOptionalMismatch {110struct ConstCastOptionalMismatch {
104 ConstCastOnly child;111 ConstCastOnly child;
105 TypeTableEntry *wanted_child;112 TypeTableEntry *wanted_child;
...@@ -420,6 +427,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) {...@@ -420,6 +427,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCtz *) {
420 return IrInstructionIdCtz;427 return IrInstructionIdCtz;
421}428}
422429
430static constexpr IrInstructionId ir_instruction_id(IrInstructionPopCount *) {
431 return IrInstructionIdPopCount;
432}
433
423static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) {434static constexpr IrInstructionId ir_instruction_id(IrInstructionUnionTag *) {
424 return IrInstructionIdUnionTag;435 return IrInstructionIdUnionTag;
425}436}
...@@ -1718,8 +1729,18 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr...@@ -1718,8 +1729,18 @@ static IrInstruction *ir_build_ctz_from(IrBuilder *irb, IrInstruction *old_instr
1718 return new_instruction;1729 return new_instruction;
1719}1730}
17201731
1732static IrInstruction *ir_build_pop_count(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *value) {
1733 IrInstructionPopCount *instruction = ir_build_instruction<IrInstructionPopCount>(irb, scope, source_node);
1734 instruction->value = value;
1735
1736 ir_ref_instruction(value, irb->current_basic_block);
1737
1738 return &instruction->base;
1739}
1740
1721static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value,1741static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *target_value,
1722 IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime)1742 IrBasicBlock *else_block, size_t case_count, IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime,
1743 IrInstruction *switch_prongs_void)
1723{1744{
1724 IrInstructionSwitchBr *instruction = ir_build_instruction<IrInstructionSwitchBr>(irb, scope, source_node);1745 IrInstructionSwitchBr *instruction = ir_build_instruction<IrInstructionSwitchBr>(irb, scope, source_node);
1725 instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;1746 instruction->base.value.type = irb->codegen->builtin_types.entry_unreachable;
...@@ -1729,10 +1750,12 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *...@@ -1729,10 +1750,12 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *
1729 instruction->case_count = case_count;1750 instruction->case_count = case_count;
1730 instruction->cases = cases;1751 instruction->cases = cases;
1731 instruction->is_comptime = is_comptime;1752 instruction->is_comptime = is_comptime;
1753 instruction->switch_prongs_void = switch_prongs_void;
17321754
1733 ir_ref_instruction(target_value, irb->current_basic_block);1755 ir_ref_instruction(target_value, irb->current_basic_block);
1734 if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);1756 if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block);
1735 ir_ref_bb(else_block);1757 ir_ref_bb(else_block);
1758 if (switch_prongs_void) ir_ref_instruction(switch_prongs_void, irb->current_basic_block);
17361759
1737 for (size_t i = 0; i < case_count; i += 1) {1760 for (size_t i = 0; i < case_count; i += 1) {
1738 ir_ref_instruction(cases[i].value, irb->current_basic_block);1761 ir_ref_instruction(cases[i].value, irb->current_basic_block);
...@@ -1744,10 +1767,10 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *...@@ -1744,10 +1767,10 @@ static IrInstruction *ir_build_switch_br(IrBuilder *irb, Scope *scope, AstNode *
17441767
1745static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction,1768static IrInstruction *ir_build_switch_br_from(IrBuilder *irb, IrInstruction *old_instruction,
1746 IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count,1769 IrInstruction *target_value, IrBasicBlock *else_block, size_t case_count,
1747 IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime)1770 IrInstructionSwitchBrCase *cases, IrInstruction *is_comptime, IrInstruction *switch_prongs_void)
1748{1771{
1749 IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node,1772 IrInstruction *new_instruction = ir_build_switch_br(irb, old_instruction->scope, old_instruction->source_node,
1750 target_value, else_block, case_count, cases, is_comptime);1773 target_value, else_block, case_count, cases, is_comptime, switch_prongs_void);
1751 ir_link_new_instruction(new_instruction, old_instruction);1774 ir_link_new_instruction(new_instruction, old_instruction);
1752 return new_instruction;1775 return new_instruction;
1753}1776}
...@@ -3831,6 +3854,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3831,6 +3854,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3831 IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value);3854 IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value);
3832 return ir_lval_wrap(irb, scope, ctz, lval);3855 return ir_lval_wrap(irb, scope, ctz, lval);
3833 }3856 }
3857 case BuiltinFnIdPopCount:
3858 {
3859 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3860 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
3861 if (arg0_value == irb->codegen->invalid_instruction)
3862 return arg0_value;
3863
3864 IrInstruction *instr = ir_build_pop_count(irb, scope, node, arg0_value);
3865 return ir_lval_wrap(irb, scope, instr, lval);
3866 }
3834 case BuiltinFnIdClz:3867 case BuiltinFnIdClz:
3835 {3868 {
3836 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);3869 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -6035,13 +6068,13 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *...@@ -6035,13 +6068,13 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
60356068
6036 }6069 }
60376070
6038 ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length,6071 IrInstruction *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, check_ranges.items, check_ranges.length,
6039 else_prong != nullptr);6072 else_prong != nullptr);
60406073
6041 if (cases.length == 0) {6074 if (cases.length == 0) {
6042 ir_build_br(irb, scope, node, else_block, is_comptime);6075 ir_build_br(irb, scope, node, else_block, is_comptime);
6043 } else {6076 } else {
6044 ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime);6077 ir_build_switch_br(irb, scope, node, target_value, else_block, cases.length, cases.items, is_comptime, switch_prongs_void);
6045 }6078 }
60466079
6047 if (!else_prong) {6080 if (!else_prong) {
...@@ -6692,7 +6725,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6692,7 +6725,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
6692 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);6725 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);
6693 cases[1].block = cleanup_block;6726 cases[1].block = cleanup_block;
6694 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6727 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6695 2, cases, const_bool_false);6728 2, cases, const_bool_false, nullptr);
66966729
6697 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6730 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6698 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6731 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
...@@ -6773,7 +6806,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6773,7 +6806,7 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6773 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));6806 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6774 cases[1].block = cleanup_block;6807 cases[1].block = cleanup_block;
6775 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6808 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6776 2, cases, const_bool_false));6809 2, cases, const_bool_false, nullptr));
67776810
6778 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6811 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6779 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6812 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
...@@ -7078,7 +7111,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7078,7 +7111,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7078 cases[0].block = invalid_resume_block;7111 cases[0].block = invalid_resume_block;
7079 cases[1].value = ir_build_const_u8(irb, scope, node, 1);7112 cases[1].value = ir_build_const_u8(irb, scope, node, 1);
7080 cases[1].block = irb->exec->coro_final_cleanup_block;7113 cases[1].block = irb->exec->coro_final_cleanup_block;
7081 ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false);7114 ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block, 2, cases, const_bool_false, nullptr);
70827115
7083 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block);7116 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_suspend_block);
7084 ir_build_coro_end(irb, scope, node);7117 ir_build_coro_end(irb, scope, node);
...@@ -8125,15 +8158,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -8125,15 +8158,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
8125 }8158 }
81268159
8127 // pointer const8160 // pointer const
8128 if (wanted_type->id == TypeTableEntryIdPointer &&8161 if (wanted_type->id == TypeTableEntryIdPointer && actual_type->id == TypeTableEntryIdPointer) {
8129 actual_type->id == TypeTableEntryIdPointer &&
8130 (actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) &&
8131 (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
8132 (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) &&
8133 actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset &&
8134 actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count &&
8135 actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment)
8136 {
8137 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,8162 ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.pointer.child_type,
8138 actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const);8163 actual_type->data.pointer.child_type, source_node, !wanted_type->data.pointer.is_const);
8139 if (child.id != ConstCastResultIdOk) {8164 if (child.id != ConstCastResultIdOk) {
...@@ -8142,8 +8167,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -8142,8 +8167,17 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
8142 result.data.pointer_mismatch->child = child;8167 result.data.pointer_mismatch->child = child;
8143 result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type;8168 result.data.pointer_mismatch->wanted_child = wanted_type->data.pointer.child_type;
8144 result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type;8169 result.data.pointer_mismatch->actual_child = actual_type->data.pointer.child_type;
8170 return result;
8171 }
8172 if ((actual_type->data.pointer.ptr_len == wanted_type->data.pointer.ptr_len) &&
8173 (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) &&
8174 (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile) &&
8175 actual_type->data.pointer.bit_offset == wanted_type->data.pointer.bit_offset &&
8176 actual_type->data.pointer.unaligned_bit_count == wanted_type->data.pointer.unaligned_bit_count &&
8177 actual_type->data.pointer.alignment >= wanted_type->data.pointer.alignment)
8178 {
8179 return result;
8145 }8180 }
8146 return result;
8147 }8181 }
81488182
8149 // slice const8183 // slice const
...@@ -8338,6 +8372,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry...@@ -8338,6 +8372,9 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, TypeTableEntry
8338 }8372 }
83398373
8340 result.id = ConstCastResultIdType;8374 result.id = ConstCastResultIdType;
8375 result.data.type_mismatch = allocate_nonzero<ConstCastTypeMismatch>(1);
8376 result.data.type_mismatch->wanted_type = wanted_type;
8377 result.data.type_mismatch->actual_type = actual_type;
8341 return result;8378 return result;
8342}8379}
83438380
...@@ -10151,6 +10188,21 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa...@@ -10151,6 +10188,21 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
10151 report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg);10188 report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg);
10152 break;10189 break;
10153 }10190 }
10191 case ConstCastResultIdType: {
10192 AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type);
10193 AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type);
10194 if (wanted_decl_node != nullptr) {
10195 add_error_note(ira->codegen, parent_msg, wanted_decl_node,
10196 buf_sprintf("%s declared here",
10197 buf_ptr(&cast_result->data.type_mismatch->wanted_type->name)));
10198 }
10199 if (actual_decl_node != nullptr) {
10200 add_error_note(ira->codegen, parent_msg, actual_decl_node,
10201 buf_sprintf("%s declared here",
10202 buf_ptr(&cast_result->data.type_mismatch->actual_type->name)));
10203 }
10204 break;
10205 }
10154 case ConstCastResultIdFnAlign: // TODO10206 case ConstCastResultIdFnAlign: // TODO
10155 case ConstCastResultIdFnCC: // TODO10207 case ConstCastResultIdFnCC: // TODO
10156 case ConstCastResultIdFnVarArgs: // TODO10208 case ConstCastResultIdFnVarArgs: // TODO
...@@ -10160,7 +10212,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa...@@ -10160,7 +10212,6 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
10160 case ConstCastResultIdFnGenericArgCount: // TODO10212 case ConstCastResultIdFnGenericArgCount: // TODO
10161 case ConstCastResultIdFnArg: // TODO10213 case ConstCastResultIdFnArg: // TODO
10162 case ConstCastResultIdFnArgNoAlias: // TODO10214 case ConstCastResultIdFnArgNoAlias: // TODO
10163 case ConstCastResultIdType: // TODO
10164 case ConstCastResultIdUnresolvedInferredErrSet: // TODO10215 case ConstCastResultIdUnresolvedInferredErrSet: // TODO
10165 case ConstCastResultIdAsyncAllocatorType: // TODO10216 case ConstCastResultIdAsyncAllocatorType: // TODO
10166 case ConstCastResultIdNullWrapPtr: // TODO10217 case ConstCastResultIdNullWrapPtr: // TODO
...@@ -12670,14 +12721,22 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -12670,14 +12721,22 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
12670 // for extern functions, the var args argument is not counted.12721 // for extern functions, the var args argument is not counted.
12671 // for zig functions, it is.12722 // for zig functions, it is.
12672 size_t var_args_1_or_0;12723 size_t var_args_1_or_0;
12673 if (fn_type_id->cc == CallingConventionUnspecified) {12724 if (fn_type_id->cc == CallingConventionC) {
12674 var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0;
12675 } else {
12676 var_args_1_or_0 = 0;12725 var_args_1_or_0 = 0;
12726 } else {
12727 var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0;
12677 }12728 }
12678 size_t src_param_count = fn_type_id->param_count - var_args_1_or_0;12729 size_t src_param_count = fn_type_id->param_count - var_args_1_or_0;
1267912730
12680 size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0;12731 size_t call_param_count = call_instruction->arg_count + first_arg_1_or_0;
12732 for (size_t i = 0; i < call_instruction->arg_count; i += 1) {
12733 ConstExprValue *arg_tuple_value = &call_instruction->args[i]->other->value;
12734 if (arg_tuple_value->type->id == TypeTableEntryIdArgTuple) {
12735 call_param_count -= 1;
12736 call_param_count += arg_tuple_value->data.x_arg_tuple.end_index -
12737 arg_tuple_value->data.x_arg_tuple.start_index;
12738 }
12739 }
12681 AstNode *source_node = call_instruction->base.source_node;12740 AstNode *source_node = call_instruction->base.source_node;
1268212741
12683 AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;;12742 AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;;
...@@ -12858,11 +12917,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -12858,11 +12917,6 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
12858 buf_sprintf("calling a generic function requires compile-time known function value"));12917 buf_sprintf("calling a generic function requires compile-time known function value"));
12859 return ira->codegen->builtin_types.entry_invalid;12918 return ira->codegen->builtin_types.entry_invalid;
12860 }12919 }
12861 if (call_instruction->is_async && fn_type_id->is_var_args) {
12862 ir_add_error(ira, call_instruction->fn_ref,
12863 buf_sprintf("compiler bug: TODO: implement var args async functions. https://github.com/ziglang/zig/issues/557"));
12864 return ira->codegen->builtin_types.entry_invalid;
12865 }
1286612920
12867 // Count the arguments of the function type id we are creating12921 // Count the arguments of the function type id we are creating
12868 size_t new_fn_arg_count = first_arg_1_or_0;12922 size_t new_fn_arg_count = first_arg_1_or_0;
...@@ -12937,18 +12991,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -12937,18 +12991,18 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
12937 if (type_is_invalid(arg->value.type))12991 if (type_is_invalid(arg->value.type))
12938 return ira->codegen->builtin_types.entry_invalid;12992 return ira->codegen->builtin_types.entry_invalid;
1293912993
12940 AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
12941 assert(param_decl_node->type == NodeTypeParamDecl);
12942 bool is_var_args = param_decl_node->data.param_decl.is_var_args;
12943 if (is_var_args && !found_first_var_arg) {
12944 first_var_arg = inst_fn_type_id.param_count;
12945 found_first_var_arg = true;
12946 }
12947
12948 if (arg->value.type->id == TypeTableEntryIdArgTuple) {12994 if (arg->value.type->id == TypeTableEntryIdArgTuple) {
12949 for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index;12995 for (size_t arg_tuple_i = arg->value.data.x_arg_tuple.start_index;
12950 arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1)12996 arg_tuple_i < arg->value.data.x_arg_tuple.end_index; arg_tuple_i += 1)
12951 {12997 {
12998 AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
12999 assert(param_decl_node->type == NodeTypeParamDecl);
13000 bool is_var_args = param_decl_node->data.param_decl.is_var_args;
13001 if (is_var_args && !found_first_var_arg) {
13002 first_var_arg = inst_fn_type_id.param_count;
13003 found_first_var_arg = true;
13004 }
13005
12952 VariableTableEntry *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i);13006 VariableTableEntry *arg_var = get_fn_var_by_index(parent_fn_entry, arg_tuple_i);
12953 if (arg_var == nullptr) {13007 if (arg_var == nullptr) {
12954 ir_add_error(ira, arg,13008 ir_add_error(ira, arg,
...@@ -12969,10 +13023,20 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -12969,10 +13023,20 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
12969 return ira->codegen->builtin_types.entry_invalid;13023 return ira->codegen->builtin_types.entry_invalid;
12970 }13024 }
12971 }13025 }
12972 } else if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope,13026 } else {
12973 &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))13027 AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i);
12974 {13028 assert(param_decl_node->type == NodeTypeParamDecl);
12975 return ira->codegen->builtin_types.entry_invalid;13029 bool is_var_args = param_decl_node->data.param_decl.is_var_args;
13030 if (is_var_args && !found_first_var_arg) {
13031 first_var_arg = inst_fn_type_id.param_count;
13032 found_first_var_arg = true;
13033 }
13034
13035 if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &impl_fn->child_scope,
13036 &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn))
13037 {
13038 return ira->codegen->builtin_types.entry_invalid;
13039 }
12976 }13040 }
12977 }13041 }
1297813042
...@@ -13220,6 +13284,8 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction...@@ -13220,6 +13284,8 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
13220 return ir_finish_anal(ira, cast_instruction->value.type);13284 return ir_finish_anal(ira, cast_instruction->value.type);
13221 } else if (fn_ref->value.type->id == TypeTableEntryIdFn) {13285 } else if (fn_ref->value.type->id == TypeTableEntryIdFn) {
13222 FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);13286 FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
13287 if (fn_table_entry == nullptr)
13288 return ira->codegen->builtin_types.entry_invalid;
13223 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,13289 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
13224 fn_ref, nullptr, is_comptime, call_instruction->fn_inline);13290 fn_ref, nullptr, is_comptime, call_instruction->fn_inline);
13225 } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {13291 } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {
...@@ -13227,7 +13293,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction...@@ -13227,7 +13293,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
13227 FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;13293 FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
13228 IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;13294 IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
13229 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,13295 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
13230 nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline);13296 fn_ref, first_arg_ptr, is_comptime, call_instruction->fn_inline);
13231 } else {13297 } else {
13232 ir_add_error_node(ira, fn_ref->source_node,13298 ir_add_error_node(ira, fn_ref->source_node,
13233 buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));13299 buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
...@@ -15247,6 +15313,48 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC...@@ -15247,6 +15313,48 @@ static TypeTableEntry *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstructionC
15247 }15313 }
15248}15314}
1524915315
15316static TypeTableEntry *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstructionPopCount *instruction) {
15317 IrInstruction *value = instruction->value->other;
15318 if (type_is_invalid(value->value.type))
15319 return ira->codegen->builtin_types.entry_invalid;
15320
15321 if (value->value.type->id != TypeTableEntryIdInt && value->value.type->id != TypeTableEntryIdComptimeInt) {
15322 ir_add_error(ira, value,
15323 buf_sprintf("expected integer type, found '%s'", buf_ptr(&value->value.type->name)));
15324 return ira->codegen->builtin_types.entry_invalid;
15325 }
15326
15327 if (instr_is_comptime(value)) {
15328 ConstExprValue *val = ir_resolve_const(ira, value, UndefBad);
15329 if (!val)
15330 return ira->codegen->builtin_types.entry_invalid;
15331 if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) {
15332 size_t result = bigint_popcount_unsigned(&val->data.x_bigint);
15333 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
15334 bigint_init_unsigned(&out_val->data.x_bigint, result);
15335 return ira->codegen->builtin_types.entry_num_lit_int;
15336 }
15337 if (value->value.type->id == TypeTableEntryIdComptimeInt) {
15338 Buf *val_buf = buf_alloc();
15339 bigint_append_buf(val_buf, &val->data.x_bigint, 10);
15340 ir_add_error(ira, &instruction->base,
15341 buf_sprintf("@popCount on negative %s value %s",
15342 buf_ptr(&value->value.type->name), buf_ptr(val_buf)));
15343 return ira->codegen->builtin_types.entry_invalid;
15344 }
15345 size_t result = bigint_popcount_signed(&val->data.x_bigint, value->value.type->data.integral.bit_count);
15346 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
15347 bigint_init_unsigned(&out_val->data.x_bigint, result);
15348 return ira->codegen->builtin_types.entry_num_lit_int;
15349 }
15350
15351 IrInstruction *result = ir_build_pop_count(&ira->new_irb, instruction->base.scope,
15352 instruction->base.source_node, value);
15353 result->value.type = get_smallest_unsigned_int_type(ira->codegen, value->value.type->data.integral.bit_count);
15354 ir_link_new_instruction(result, &instruction->base);
15355 return result->value.type;
15356}
15357
15250static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) {15358static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *value) {
15251 if (type_is_invalid(value->value.type))15359 if (type_is_invalid(value->value.type))
15252 return ira->codegen->invalid_instruction;15360 return ira->codegen->invalid_instruction;
...@@ -15297,6 +15405,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -15297,6 +15405,13 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
15297 if (type_is_invalid(target_value->value.type))15405 if (type_is_invalid(target_value->value.type))
15298 return ir_unreach_error(ira);15406 return ir_unreach_error(ira);
1529915407
15408 if (switch_br_instruction->switch_prongs_void != nullptr) {
15409 if (type_is_invalid(switch_br_instruction->switch_prongs_void->other->value.type)) {
15410 return ir_unreach_error(ira);
15411 }
15412 }
15413
15414
15300 size_t case_count = switch_br_instruction->case_count;15415 size_t case_count = switch_br_instruction->case_count;
1530115416
15302 bool is_comptime;15417 bool is_comptime;
...@@ -15387,7 +15502,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,...@@ -15387,7 +15502,7 @@ static TypeTableEntry *ir_analyze_instruction_switch_br(IrAnalyze *ira,
1538715502
15388 IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base);15503 IrBasicBlock *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base);
15389 ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base,15504 ir_build_switch_br_from(&ira->new_irb, &switch_br_instruction->base,
15390 target_value, new_else_block, case_count, cases, nullptr);15505 target_value, new_else_block, case_count, cases, nullptr, nullptr);
15391 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);15506 return ir_finish_anal(ira, ira->codegen->builtin_types.entry_unreachable);
15392}15507}
1539315508
...@@ -19136,16 +19251,22 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira...@@ -19136,16 +19251,22 @@ static TypeTableEntry *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira
19136 IrInstruction *start_value = range->start->other;19251 IrInstruction *start_value = range->start->other;
19137 if (type_is_invalid(start_value->value.type))19252 if (type_is_invalid(start_value->value.type))
19138 return ira->codegen->builtin_types.entry_invalid;19253 return ira->codegen->builtin_types.entry_invalid;
19254 IrInstruction *casted_start_value = ir_implicit_cast(ira, start_value, switch_type);
19255 if (type_is_invalid(casted_start_value->value.type))
19256 return ira->codegen->builtin_types.entry_invalid;
1913919257
19140 IrInstruction *end_value = range->end->other;19258 IrInstruction *end_value = range->end->other;
19141 if (type_is_invalid(end_value->value.type))19259 if (type_is_invalid(end_value->value.type))
19142 return ira->codegen->builtin_types.entry_invalid;19260 return ira->codegen->builtin_types.entry_invalid;
19261 IrInstruction *casted_end_value = ir_implicit_cast(ira, end_value, switch_type);
19262 if (type_is_invalid(casted_end_value->value.type))
19263 return ira->codegen->builtin_types.entry_invalid;
1914319264
19144 ConstExprValue *start_val = ir_resolve_const(ira, start_value, UndefBad);19265 ConstExprValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad);
19145 if (!start_val)19266 if (!start_val)
19146 return ira->codegen->builtin_types.entry_invalid;19267 return ira->codegen->builtin_types.entry_invalid;
1914719268
19148 ConstExprValue *end_val = ir_resolve_const(ira, end_value, UndefBad);19269 ConstExprValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad);
19149 if (!end_val)19270 if (!end_val)
19150 return ira->codegen->builtin_types.entry_invalid;19271 return ira->codegen->builtin_types.entry_invalid;
1915119272
...@@ -19264,6 +19385,15 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3...@@ -19264,6 +19385,15 @@ static IrInstruction *ir_align_cast(IrAnalyze *ira, IrInstruction *target, uint3
19264 if (!val)19385 if (!val)
19265 return ira->codegen->invalid_instruction;19386 return ira->codegen->invalid_instruction;
1926619387
19388 if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr &&
19389 val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0)
19390 {
19391 ir_add_error(ira, target,
19392 buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes",
19393 val->data.x_ptr.data.hard_coded_addr.addr, align_bytes));
19394 return ira->codegen->invalid_instruction;
19395 }
19396
19267 IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type);19397 IrInstruction *result = ir_create_const(&ira->new_irb, target->scope, target->source_node, result_type);
19268 copy_const_val(&result->value, val, false);19398 copy_const_val(&result->value, val, false);
19269 result->value.type = result_type;19399 result->value.type = result_type;
...@@ -19690,6 +19820,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr...@@ -19690,6 +19820,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstr
19690 return ira->codegen->builtin_types.entry_invalid;19820 return ira->codegen->builtin_types.entry_invalid;
19691 }19821 }
1969219822
19823 if (!type_has_bits(target->value.type)) {
19824 ir_add_error(ira, target,
19825 buf_sprintf("pointer to size 0 type has no address"));
19826 return ira->codegen->builtin_types.entry_invalid;
19827 }
19828
19693 if (instr_is_comptime(target)) {19829 if (instr_is_comptime(target)) {
19694 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);19830 ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
19695 if (!val)19831 if (!val)
...@@ -20493,6 +20629,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -20493,6 +20629,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
20493 return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);20629 return ir_analyze_instruction_clz(ira, (IrInstructionClz *)instruction);
20494 case IrInstructionIdCtz:20630 case IrInstructionIdCtz:
20495 return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);20631 return ir_analyze_instruction_ctz(ira, (IrInstructionCtz *)instruction);
20632 case IrInstructionIdPopCount:
20633 return ir_analyze_instruction_pop_count(ira, (IrInstructionPopCount *)instruction);
20496 case IrInstructionIdSwitchBr:20634 case IrInstructionIdSwitchBr:
20497 return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction);20635 return ir_analyze_instruction_switch_br(ira, (IrInstructionSwitchBr *)instruction);
20498 case IrInstructionIdSwitchTarget:20636 case IrInstructionIdSwitchTarget:
...@@ -20851,6 +20989,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -20851,6 +20989,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
20851 case IrInstructionIdUnwrapOptional:20989 case IrInstructionIdUnwrapOptional:
20852 case IrInstructionIdClz:20990 case IrInstructionIdClz:
20853 case IrInstructionIdCtz:20991 case IrInstructionIdCtz:
20992 case IrInstructionIdPopCount:
20854 case IrInstructionIdSwitchVar:20993 case IrInstructionIdSwitchVar:
20855 case IrInstructionIdSwitchTarget:20994 case IrInstructionIdSwitchTarget:
20856 case IrInstructionIdUnionTag:20995 case IrInstructionIdUnionTag:
src/ir_print.cpp+9
...@@ -501,6 +501,12 @@ static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) {...@@ -501,6 +501,12 @@ static void ir_print_ctz(IrPrint *irp, IrInstructionCtz *instruction) {
501 fprintf(irp->f, ")");501 fprintf(irp->f, ")");
502}502}
503503
504static void ir_print_pop_count(IrPrint *irp, IrInstructionPopCount *instruction) {
505 fprintf(irp->f, "@popCount(");
506 ir_print_other_instruction(irp, instruction->value);
507 fprintf(irp->f, ")");
508}
509
504static void ir_print_switch_br(IrPrint *irp, IrInstructionSwitchBr *instruction) {510static void ir_print_switch_br(IrPrint *irp, IrInstructionSwitchBr *instruction) {
505 fprintf(irp->f, "switch (");511 fprintf(irp->f, "switch (");
506 ir_print_other_instruction(irp, instruction->target_value);512 ir_print_other_instruction(irp, instruction->target_value);
...@@ -1425,6 +1431,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1425,6 +1431,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1425 case IrInstructionIdCtz:1431 case IrInstructionIdCtz:
1426 ir_print_ctz(irp, (IrInstructionCtz *)instruction);1432 ir_print_ctz(irp, (IrInstructionCtz *)instruction);
1427 break;1433 break;
1434 case IrInstructionIdPopCount:
1435 ir_print_pop_count(irp, (IrInstructionPopCount *)instruction);
1436 break;
1428 case IrInstructionIdClz:1437 case IrInstructionIdClz:
1429 ir_print_clz(irp, (IrInstructionClz *)instruction);1438 ir_print_clz(irp, (IrInstructionClz *)instruction);
1430 break;1439 break;
std/atomic/queue_mpsc.zig+42
...@@ -15,6 +15,8 @@ pub fn QueueMpsc(comptime T: type) type {...@@ -15,6 +15,8 @@ pub fn QueueMpsc(comptime T: type) type {
1515
16 pub const Node = std.atomic.Stack(T).Node;16 pub const Node = std.atomic.Stack(T).Node;
1717
18 /// Not thread-safe. The call to init() must complete before any other functions are called.
19 /// No deinitialization required.
18 pub fn init() Self {20 pub fn init() Self {
19 return Self{21 return Self{
20 .inboxes = []std.atomic.Stack(T){22 .inboxes = []std.atomic.Stack(T){
...@@ -26,12 +28,15 @@ pub fn QueueMpsc(comptime T: type) type {...@@ -26,12 +28,15 @@ pub fn QueueMpsc(comptime T: type) type {
26 };28 };
27 }29 }
2830
31 /// Fully thread-safe. put() may be called from any thread at any time.
29 pub fn put(self: *Self, node: *Node) void {32 pub fn put(self: *Self, node: *Node) void {
30 const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst);33 const inbox_index = @atomicLoad(usize, &self.inbox_index, AtomicOrder.SeqCst);
31 const inbox = &self.inboxes[inbox_index];34 const inbox = &self.inboxes[inbox_index];
32 inbox.push(node);35 inbox.push(node);
33 }36 }
3437
38 /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before
39 /// the next call to get().
35 pub fn get(self: *Self) ?*Node {40 pub fn get(self: *Self) ?*Node {
36 if (self.outbox.pop()) |node| {41 if (self.outbox.pop()) |node| {
37 return node;42 return node;
...@@ -43,6 +48,43 @@ pub fn QueueMpsc(comptime T: type) type {...@@ -43,6 +48,43 @@ pub fn QueueMpsc(comptime T: type) type {
43 }48 }
44 return self.outbox.pop();49 return self.outbox.pop();
45 }50 }
51
52 /// Must be called by only 1 consumer at a time. Every call to get() and isEmpty() must complete before
53 /// the next call to isEmpty().
54 pub fn isEmpty(self: *Self) bool {
55 if (!self.outbox.isEmpty()) return false;
56 const prev_inbox_index = @atomicRmw(usize, &self.inbox_index, AtomicRmwOp.Xor, 0x1, AtomicOrder.SeqCst);
57 const prev_inbox = &self.inboxes[prev_inbox_index];
58 while (prev_inbox.pop()) |node| {
59 self.outbox.push(node);
60 }
61 return self.outbox.isEmpty();
62 }
63
64 /// For debugging only. No API guarantees about what this does.
65 pub fn dump(self: *Self) void {
66 {
67 var it = self.outbox.root;
68 while (it) |node| {
69 std.debug.warn("0x{x} -> ", @ptrToInt(node));
70 it = node.next;
71 }
72 }
73 const inbox_index = self.inbox_index;
74 const inboxes = []*std.atomic.Stack(T){
75 &self.inboxes[self.inbox_index],
76 &self.inboxes[1 - self.inbox_index],
77 };
78 for (inboxes) |inbox| {
79 var it = inbox.root;
80 while (it) |node| {
81 std.debug.warn("0x{x} -> ", @ptrToInt(node));
82 it = node.next;
83 }
84 }
85
86 std.debug.warn("null\n");
87 }
46 };88 };
47}89}
4890
std/build.zig+18
...@@ -814,6 +814,7 @@ pub const LibExeObjStep = struct {...@@ -814,6 +814,7 @@ pub const LibExeObjStep = struct {
814 out_h_filename: []const u8,814 out_h_filename: []const u8,
815 assembly_files: ArrayList([]const u8),815 assembly_files: ArrayList([]const u8),
816 packages: ArrayList(Pkg),816 packages: ArrayList(Pkg),
817 build_options_contents: std.Buffer,
817818
818 // C only stuff819 // C only stuff
819 source_files: ArrayList([]const u8),820 source_files: ArrayList([]const u8),
...@@ -905,6 +906,7 @@ pub const LibExeObjStep = struct {...@@ -905,6 +906,7 @@ pub const LibExeObjStep = struct {
905 .lib_paths = ArrayList([]const u8).init(builder.allocator),906 .lib_paths = ArrayList([]const u8).init(builder.allocator),
906 .object_src = undefined,907 .object_src = undefined,
907 .disable_libc = true,908 .disable_libc = true,
909 .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
908 };910 };
909 self.computeOutFileNames();911 self.computeOutFileNames();
910 return self;912 return self;
...@@ -945,6 +947,7 @@ pub const LibExeObjStep = struct {...@@ -945,6 +947,7 @@ pub const LibExeObjStep = struct {
945 .out_h_filename = undefined,947 .out_h_filename = undefined,
946 .assembly_files = undefined,948 .assembly_files = undefined,
947 .packages = undefined,949 .packages = undefined,
950 .build_options_contents = undefined,
948 };951 };
949 self.computeOutFileNames();952 self.computeOutFileNames();
950 return self;953 return self;
...@@ -1096,6 +1099,12 @@ pub const LibExeObjStep = struct {...@@ -1096,6 +1099,12 @@ pub const LibExeObjStep = struct {
1096 self.include_dirs.append(self.builder.cache_root) catch unreachable;1099 self.include_dirs.append(self.builder.cache_root) catch unreachable;
1097 }1100 }
10981101
1102 pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
1103 assert(self.is_zig);
1104 const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream;
1105 out.print("pub const {} = {};\n", name, value) catch unreachable;
1106 }
1107
1099 pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void {1108 pub fn addIncludeDir(self: *LibExeObjStep, path: []const u8) void {
1100 self.include_dirs.append(path) catch unreachable;1109 self.include_dirs.append(path) catch unreachable;
1101 }1110 }
...@@ -1155,6 +1164,15 @@ pub const LibExeObjStep = struct {...@@ -1155,6 +1164,15 @@ pub const LibExeObjStep = struct {
1155 zig_args.append(builder.pathFromRoot(root_src)) catch unreachable;1164 zig_args.append(builder.pathFromRoot(root_src)) catch unreachable;
1156 }1165 }
11571166
1167 if (self.build_options_contents.len() > 0) {
1168 const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));
1169 try std.io.writeFile(builder.allocator, build_options_file, self.build_options_contents.toSliceConst());
1170 try zig_args.append("--pkg-begin");
1171 try zig_args.append("build_options");
1172 try zig_args.append(builder.pathFromRoot(build_options_file));
1173 try zig_args.append("--pkg-end");
1174 }
1175
1158 for (self.object_files.toSliceConst()) |object_file| {1176 for (self.object_files.toSliceConst()) |object_file| {
1159 zig_args.append("--object") catch unreachable;1177 zig_args.append("--object") catch unreachable;
1160 zig_args.append(builder.pathFromRoot(object_file)) catch unreachable;1178 zig_args.append(builder.pathFromRoot(object_file)) catch unreachable;
std/c/darwin.zig+72
...@@ -6,6 +6,30 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b...@@ -6,6 +6,30 @@ pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: [*]u8, buf_len: usize, b
6pub extern "c" fn mach_absolute_time() u64;6pub extern "c" fn mach_absolute_time() u64;
7pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;7pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
88
9pub extern "c" fn kqueue() c_int;
10pub extern "c" fn kevent(
11 kq: c_int,
12 changelist: [*]const Kevent,
13 nchanges: c_int,
14 eventlist: [*]Kevent,
15 nevents: c_int,
16 timeout: ?*const timespec,
17) c_int;
18
19pub extern "c" fn kevent64(
20 kq: c_int,
21 changelist: [*]const kevent64_s,
22 nchanges: c_int,
23 eventlist: [*]kevent64_s,
24 nevents: c_int,
25 flags: c_uint,
26 timeout: ?*const timespec,
27) c_int;
28
29pub extern "c" fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
30pub extern "c" fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) c_int;
31pub extern "c" fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) c_int;
32
9pub use @import("../os/darwin_errno.zig");33pub use @import("../os/darwin_errno.zig");
1034
11pub const _errno = __error;35pub const _errno = __error;
...@@ -86,3 +110,51 @@ pub const pthread_attr_t = extern struct {...@@ -86,3 +110,51 @@ pub const pthread_attr_t = extern struct {
86 __sig: c_long,110 __sig: c_long,
87 __opaque: [56]u8,111 __opaque: [56]u8,
88};112};
113
114/// Renamed from `kevent` to `Kevent` to avoid conflict with function name.
115pub const Kevent = extern struct {
116 ident: usize,
117 filter: i16,
118 flags: u16,
119 fflags: u32,
120 data: isize,
121 udata: usize,
122};
123
124// sys/types.h on macos uses #pragma pack(4) so these checks are
125// to make sure the struct is laid out the same. These values were
126// produced from C code using the offsetof macro.
127const std = @import("../index.zig");
128const assert = std.debug.assert;
129
130comptime {
131 assert(@offsetOf(Kevent, "ident") == 0);
132 assert(@offsetOf(Kevent, "filter") == 8);
133 assert(@offsetOf(Kevent, "flags") == 10);
134 assert(@offsetOf(Kevent, "fflags") == 12);
135 assert(@offsetOf(Kevent, "data") == 16);
136 assert(@offsetOf(Kevent, "udata") == 24);
137}
138
139pub const kevent64_s = extern struct {
140 ident: u64,
141 filter: i16,
142 flags: u16,
143 fflags: u32,
144 data: i64,
145 udata: u64,
146 ext: [2]u64,
147};
148
149// sys/types.h on macos uses #pragma pack() so these checks are
150// to make sure the struct is laid out the same. These values were
151// produced from C code using the offsetof macro.
152comptime {
153 assert(@offsetOf(kevent64_s, "ident") == 0);
154 assert(@offsetOf(kevent64_s, "filter") == 8);
155 assert(@offsetOf(kevent64_s, "flags") == 10);
156 assert(@offsetOf(kevent64_s, "fflags") == 12);
157 assert(@offsetOf(kevent64_s, "data") == 16);
158 assert(@offsetOf(kevent64_s, "udata") == 24);
159 assert(@offsetOf(kevent64_s, "ext") == 32);
160}
std/crypto/throughput_test.zig+4-4
...@@ -15,8 +15,8 @@ const BytesToHash = 1024 * MiB;...@@ -15,8 +15,8 @@ const BytesToHash = 1024 * MiB;
1515
16pub fn main() !void {16pub fn main() !void {
17 var stdout_file = try std.io.getStdOut();17 var stdout_file = try std.io.getStdOut();
18 var stdout_out_stream = std.io.FileOutStream.init(*stdout_file);18 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
19 const stdout = *stdout_out_stream.stream;19 const stdout = &stdout_out_stream.stream;
2020
21 var block: [HashFunction.block_size]u8 = undefined;21 var block: [HashFunction.block_size]u8 = undefined;
22 std.mem.set(u8, block[0..], 0);22 std.mem.set(u8, block[0..], 0);
...@@ -31,8 +31,8 @@ pub fn main() !void {...@@ -31,8 +31,8 @@ pub fn main() !void {
31 }31 }
32 const end = timer.read();32 const end = timer.read();
3333
34 const elapsed_s = f64(end - start) / time.ns_per_s;34 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
35 const throughput = u64(BytesToHash / elapsed_s);35 const throughput = @floatToInt(u64, BytesToHash / elapsed_s);
3636
37 try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));37 try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
38}38}
std/debug/index.zig+56-24
...@@ -10,6 +10,12 @@ const ArrayList = std.ArrayList;...@@ -10,6 +10,12 @@ const ArrayList = std.ArrayList;
10const builtin = @import("builtin");10const builtin = @import("builtin");
1111
12pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;12pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator;
13pub const failing_allocator = FailingAllocator.init(global_allocator, 0);
14
15pub const runtime_safety = switch (builtin.mode) {
16 builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true,
17 builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false,
18};
1319
14/// Tries to write to stderr, unbuffered, and ignores any error returned.20/// Tries to write to stderr, unbuffered, and ignores any error returned.
15/// Does not append a newline.21/// Does not append a newline.
...@@ -44,6 +50,12 @@ pub fn getSelfDebugInfo() !*ElfStackTrace {...@@ -44,6 +50,12 @@ pub fn getSelfDebugInfo() !*ElfStackTrace {
44 }50 }
45}51}
4652
53fn wantTtyColor() bool {
54 var bytes: [128]u8 = undefined;
55 const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
56 return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty();
57}
58
47/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.59/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
48pub fn dumpCurrentStackTrace(start_addr: ?usize) void {60pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
49 const stderr = getStderrStream() catch return;61 const stderr = getStderrStream() catch return;
...@@ -51,7 +63,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -51,7 +63,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
51 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;63 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
52 return;64 return;
53 };65 };
54 writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| {66 writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, wantTtyColor(), start_addr) catch |err| {
55 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;67 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
56 return;68 return;
57 };69 };
...@@ -64,7 +76,7 @@ pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {...@@ -64,7 +76,7 @@ pub fn dumpStackTrace(stack_trace: *const builtin.StackTrace) void {
64 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;76 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
65 return;77 return;
66 };78 };
67 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| {79 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| {
68 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;80 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
69 return;81 return;
70 };82 };
...@@ -156,7 +168,7 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,...@@ -156,7 +168,7 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,
156 frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;168 frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
157 }) {169 }) {
158 const return_address = stack_trace.instruction_addresses[frame_index];170 const return_address = stack_trace.instruction_addresses[frame_index];
159 try printSourceAtAddress(debug_info, out_stream, return_address);171 try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
160 }172 }
161}173}
162174
...@@ -189,13 +201,11 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_...@@ -189,13 +201,11 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
189 }201 }
190 },202 },
191 }203 }
192 try printSourceAtAddress(debug_info, out_stream, return_address);204 try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
193 }205 }
194}206}
195207
196fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize) !void {208fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
197 const ptr_hex = "0x{x}";
198
199 switch (builtin.os) {209 switch (builtin.os) {
200 builtin.Os.windows => return error.UnsupportedDebugInfo,210 builtin.Os.windows => return error.UnsupportedDebugInfo,
201 builtin.Os.macosx => {211 builtin.Os.macosx => {
...@@ -209,36 +219,58 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us...@@ -209,36 +219,58 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us
209 .address = address,219 .address = address,
210 };220 };
211 const symbol = debug_info.symbol_table.search(address) orelse &unknown;221 const symbol = debug_info.symbol_table.search(address) orelse &unknown;
212 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);222 try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ "0x{x}" ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);
213 },223 },
214 else => {224 else => {
215 const compile_unit = findCompileUnit(debug_info, address) catch {225 const compile_unit = findCompileUnit(debug_info, address) catch {
216 try out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n", address);226 if (tty_color) {
227 try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n ???\n\n", address);
228 } else {
229 try out_stream.print("???:?:?: 0x{x} in ??? (???)\n ???\n\n", address);
230 }
217 return;231 return;
218 };232 };
219 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);233 const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
220 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {234 if (getLineNumberInfo(debug_info, compile_unit, address - 1)) |line_info| {
221 defer line_info.deinit();235 defer line_info.deinit();
222 try out_stream.print(WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? ({})" ++ RESET ++ "\n", line_info.file_name, line_info.line, line_info.column, address, compile_unit_name);236 if (tty_color) {
223 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {237 try out_stream.print(
224 if (line_info.column == 0) {238 WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n",
225 try out_stream.write("\n");239 line_info.file_name,
226 } else {240 line_info.line,
227 {241 line_info.column,
228 var col_i: usize = 1;242 address,
229 while (col_i < line_info.column) : (col_i += 1) {243 compile_unit_name,
230 try out_stream.writeByte(' ');244 );
245 if (printLineFromFile(debug_info.allocator(), out_stream, line_info)) {
246 if (line_info.column == 0) {
247 try out_stream.write("\n");
248 } else {
249 {
250 var col_i: usize = 1;
251 while (col_i < line_info.column) : (col_i += 1) {
252 try out_stream.writeByte(' ');
253 }
231 }254 }
255 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");
232 }256 }
233 try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n");257 } else |err| switch (err) {
258 error.EndOfFile => {},
259 else => return err,
234 }260 }
235 } else |err| switch (err) {261 } else {
236 error.EndOfFile => {},262 try out_stream.print(
237 else => return err,263 "{}:{}:{}: 0x{x} in ??? ({})\n",
264 line_info.file_name,
265 line_info.line,
266 line_info.column,
267 address,
268 compile_unit_name,
269 );
238 }270 }
239 } else |err| switch (err) {271 } else |err| switch (err) {
240 error.MissingDebugInfo, error.InvalidDebugInfo => {272 error.MissingDebugInfo, error.InvalidDebugInfo => {
241 try out_stream.print(ptr_hex ++ " in ??? ({})\n", address, compile_unit_name);273 try out_stream.print("0x{x} in ??? ({})\n", address, compile_unit_name);
242 },274 },
243 else => return err,275 else => return err,
244 }276 }
...@@ -1098,7 +1130,7 @@ fn readILeb128(in_stream: var) !i64 {...@@ -1098,7 +1130,7 @@ fn readILeb128(in_stream: var) !i64 {
10981130
1099/// This should only be used in temporary test programs.1131/// This should only be used in temporary test programs.
1100pub const global_allocator = &global_fixed_allocator.allocator;1132pub const global_allocator = &global_fixed_allocator.allocator;
1101var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);1133var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
1102var global_allocator_mem: [100 * 1024]u8 = undefined;1134var global_allocator_mem: [100 * 1024]u8 = undefined;
11031135
1104// TODO make thread safe1136// TODO make thread safe
std/event.zig+12-524
...@@ -1,525 +1,13 @@...@@ -1,525 +1,13 @@
1const std = @import("index.zig");1pub const Locked = @import("event/locked.zig").Locked;
2const builtin = @import("builtin");2pub const Loop = @import("event/loop.zig").Loop;
3const assert = std.debug.assert;3pub const Lock = @import("event/lock.zig").Lock;
4const event = this;4pub const tcp = @import("event/tcp.zig");
5const mem = std.mem;5pub const Channel = @import("event/channel.zig").Channel;
6const posix = std.os.posix;6
7const AtomicRmwOp = builtin.AtomicRmwOp;7test "import event tests" {
8const AtomicOrder = builtin.AtomicOrder;8 _ = @import("event/locked.zig");
99 _ = @import("event/loop.zig");
10pub const TcpServer = struct {10 _ = @import("event/lock.zig");
11 handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void,11 _ = @import("event/tcp.zig");
1212 _ = @import("event/channel.zig");
13 loop: *Loop,
14 sockfd: i32,
15 accept_coro: ?promise,
16 listen_address: std.net.Address,
17
18 waiting_for_emfile_node: PromiseNode,
19
20 const PromiseNode = std.LinkedList(promise).Node;
21
22 pub fn init(loop: *Loop) !TcpServer {
23 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp);
24 errdefer std.os.close(sockfd);
25
26 // TODO can't initialize handler coroutine here because we need well defined copy elision
27 return TcpServer{
28 .loop = loop,
29 .sockfd = sockfd,
30 .accept_coro = null,
31 .handleRequestFn = undefined,
32 .waiting_for_emfile_node = undefined,
33 .listen_address = undefined,
34 };
35 }
36
37 pub fn listen(self: *TcpServer, address: *const std.net.Address, handleRequestFn: async<*mem.Allocator> fn (*TcpServer, *const std.net.Address, *const std.os.File) void) !void {
38 self.handleRequestFn = handleRequestFn;
39
40 try std.os.posixBind(self.sockfd, &address.os_addr);
41 try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
42 self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
43
44 self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
45 errdefer cancel self.accept_coro.?;
46
47 try self.loop.addFd(self.sockfd, self.accept_coro.?);
48 errdefer self.loop.removeFd(self.sockfd);
49 }
50
51 pub fn deinit(self: *TcpServer) void {
52 self.loop.removeFd(self.sockfd);
53 if (self.accept_coro) |accept_coro| cancel accept_coro;
54 std.os.close(self.sockfd);
55 }
56
57 pub async fn handler(self: *TcpServer) void {
58 while (true) {
59 var accepted_addr: std.net.Address = undefined;
60 if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| {
61 var socket = std.os.File.openHandle(accepted_fd);
62 _ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
63 error.OutOfMemory => {
64 socket.close();
65 continue;
66 },
67 };
68 } else |err| switch (err) {
69 error.WouldBlock => {
70 suspend; // we will get resumed by epoll_wait in the event loop
71 continue;
72 },
73 error.ProcessFdQuotaExceeded => {
74 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
75 suspend |p| {
76 self.waiting_for_emfile_node = PromiseNode.init(p);
77 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
78 }
79 continue;
80 },
81 error.ConnectionAborted, error.FileDescriptorClosed => continue,
82
83 error.PageFault => unreachable,
84 error.InvalidSyscall => unreachable,
85 error.FileDescriptorNotASocket => unreachable,
86 error.OperationNotSupported => unreachable,
87
88 error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => {
89 @panic("TODO handle this error");
90 },
91 }
92 }
93 }
94};
95
96pub const Loop = struct {
97 allocator: *mem.Allocator,
98 keep_running: bool,
99 next_tick_queue: std.atomic.QueueMpsc(promise),
100 os_data: OsData,
101
102 const OsData = switch (builtin.os) {
103 builtin.Os.linux => struct {
104 epollfd: i32,
105 },
106 else => struct {},
107 };
108
109 pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;
110
111 /// The allocator must be thread-safe because we use it for multiplexing
112 /// coroutines onto kernel threads.
113 pub fn init(allocator: *mem.Allocator) !Loop {
114 var self = Loop{
115 .keep_running = true,
116 .allocator = allocator,
117 .os_data = undefined,
118 .next_tick_queue = std.atomic.QueueMpsc(promise).init(),
119 };
120 try self.initOsData();
121 errdefer self.deinitOsData();
122
123 return self;
124 }
125
126 /// must call stop before deinit
127 pub fn deinit(self: *Loop) void {
128 self.deinitOsData();
129 }
130
131 const InitOsDataError = std.os.LinuxEpollCreateError;
132
133 fn initOsData(self: *Loop) InitOsDataError!void {
134 switch (builtin.os) {
135 builtin.Os.linux => {
136 self.os_data.epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
137 errdefer std.os.close(self.os_data.epollfd);
138 },
139 else => {},
140 }
141 }
142
143 fn deinitOsData(self: *Loop) void {
144 switch (builtin.os) {
145 builtin.Os.linux => std.os.close(self.os_data.epollfd),
146 else => {},
147 }
148 }
149
150 pub fn addFd(self: *Loop, fd: i32, prom: promise) !void {
151 var ev = std.os.linux.epoll_event{
152 .events = std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,
153 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(prom) },
154 };
155 try std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
156 }
157
158 pub fn removeFd(self: *Loop, fd: i32) void {
159 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
160 }
161 async fn waitFd(self: *Loop, fd: i32) !void {
162 defer self.removeFd(fd);
163 suspend |p| {
164 try self.addFd(fd, p);
165 }
166 }
167
168 pub fn stop(self: *Loop) void {
169 // TODO make atomic
170 self.keep_running = false;
171 // TODO activate an fd in the epoll set which should cancel all the promises
172 }
173
174 /// bring your own linked list node. this means it can't fail.
175 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
176 self.next_tick_queue.put(node);
177 }
178
179 pub fn run(self: *Loop) void {
180 while (self.keep_running) {
181 // TODO multiplex the next tick queue and the epoll event results onto a thread pool
182 while (self.next_tick_queue.get()) |node| {
183 resume node.data;
184 }
185 if (!self.keep_running) break;
186
187 self.dispatchOsEvents();
188 }
189 }
190
191 fn dispatchOsEvents(self: *Loop) void {
192 switch (builtin.os) {
193 builtin.Os.linux => {
194 var events: [16]std.os.linux.epoll_event = undefined;
195 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
196 for (events[0..count]) |ev| {
197 const p = @intToPtr(promise, ev.data.ptr);
198 resume p;
199 }
200 },
201 else => {},
202 }
203 }
204};
205
206/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size
207/// when buffer is empty, consumers suspend and are resumed by producers
208/// when buffer is full, producers suspend and are resumed by consumers
209pub fn Channel(comptime T: type) type {
210 return struct {
211 loop: *Loop,
212
213 getters: std.atomic.QueueMpsc(GetNode),
214 putters: std.atomic.QueueMpsc(PutNode),
215 get_count: usize,
216 put_count: usize,
217 dispatch_lock: u8, // TODO make this a bool
218 need_dispatch: u8, // TODO make this a bool
219
220 // simple fixed size ring buffer
221 buffer_nodes: []T,
222 buffer_index: usize,
223 buffer_len: usize,
224
225 const SelfChannel = this;
226 const GetNode = struct {
227 ptr: *T,
228 tick_node: *Loop.NextTickNode,
229 };
230 const PutNode = struct {
231 data: T,
232 tick_node: *Loop.NextTickNode,
233 };
234
235 /// call destroy when done
236 pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {
237 const buffer_nodes = try loop.allocator.alloc(T, capacity);
238 errdefer loop.allocator.free(buffer_nodes);
239
240 const self = try loop.allocator.create(SelfChannel{
241 .loop = loop,
242 .buffer_len = 0,
243 .buffer_nodes = buffer_nodes,
244 .buffer_index = 0,
245 .dispatch_lock = 0,
246 .need_dispatch = 0,
247 .getters = std.atomic.QueueMpsc(GetNode).init(),
248 .putters = std.atomic.QueueMpsc(PutNode).init(),
249 .get_count = 0,
250 .put_count = 0,
251 });
252 errdefer loop.allocator.destroy(self);
253
254 return self;
255 }
256
257 /// must be called when all calls to put and get have suspended and no more calls occur
258 pub fn destroy(self: *SelfChannel) void {
259 while (self.getters.get()) |get_node| {
260 cancel get_node.data.tick_node.data;
261 }
262 while (self.putters.get()) |put_node| {
263 cancel put_node.data.tick_node.data;
264 }
265 self.loop.allocator.free(self.buffer_nodes);
266 self.loop.allocator.destroy(self);
267 }
268
269 /// puts a data item in the channel. The promise completes when the value has been added to the
270 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
271 pub async fn put(self: *SelfChannel, data: T) void {
272 // TODO should be able to group memory allocation failure before first suspend point
273 // so that the async invocation catches it
274 var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined;
275 _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable;
276
277 suspend |handle| {
278 var my_tick_node = Loop.NextTickNode{
279 .next = undefined,
280 .data = handle,
281 };
282 var queue_node = std.atomic.QueueMpsc(PutNode).Node{
283 .data = PutNode{
284 .tick_node = &my_tick_node,
285 .data = data,
286 },
287 .next = undefined,
288 };
289 self.putters.put(&queue_node);
290 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
291
292 self.loop.onNextTick(dispatch_tick_node_ptr);
293 }
294 }
295
296 /// await this function to get an item from the channel. If the buffer is empty, the promise will
297 /// complete when the next item is put in the channel.
298 pub async fn get(self: *SelfChannel) T {
299 // TODO should be able to group memory allocation failure before first suspend point
300 // so that the async invocation catches it
301 var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined;
302 _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable;
303
304 // TODO integrate this function with named return values
305 // so we can get rid of this extra result copy
306 var result: T = undefined;
307 var debug_handle: usize = undefined;
308 suspend |handle| {
309 debug_handle = @ptrToInt(handle);
310 var my_tick_node = Loop.NextTickNode{
311 .next = undefined,
312 .data = handle,
313 };
314 var queue_node = std.atomic.QueueMpsc(GetNode).Node{
315 .data = GetNode{
316 .ptr = &result,
317 .tick_node = &my_tick_node,
318 },
319 .next = undefined,
320 };
321 self.getters.put(&queue_node);
322 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
323
324 self.loop.onNextTick(dispatch_tick_node_ptr);
325 }
326 return result;
327 }
328
329 async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void {
330 // resumed by onNextTick
331 suspend |handle| {
332 var tick_node = Loop.NextTickNode{
333 .data = handle,
334 .next = undefined,
335 };
336 tick_node_ptr.* = &tick_node;
337 }
338
339 // set the "need dispatch" flag
340 _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
341
342 lock: while (true) {
343 // set the lock flag
344 const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
345 if (prev_lock != 0) return;
346
347 // clear the need_dispatch flag since we're about to do it
348 _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
349
350 while (true) {
351 one_dispatch: {
352 // later we correct these extra subtractions
353 var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
354 var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
355
356 // transfer self.buffer to self.getters
357 while (self.buffer_len != 0) {
358 if (get_count == 0) break :one_dispatch;
359
360 const get_node = &self.getters.get().?.data;
361 get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
362 self.loop.onNextTick(get_node.tick_node);
363 self.buffer_len -= 1;
364
365 get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
366 }
367
368 // direct transfer self.putters to self.getters
369 while (get_count != 0 and put_count != 0) {
370 const get_node = &self.getters.get().?.data;
371 const put_node = &self.putters.get().?.data;
372
373 get_node.ptr.* = put_node.data;
374 self.loop.onNextTick(get_node.tick_node);
375 self.loop.onNextTick(put_node.tick_node);
376
377 get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
378 put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
379 }
380
381 // transfer self.putters to self.buffer
382 while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
383 const put_node = &self.putters.get().?.data;
384
385 self.buffer_nodes[self.buffer_index] = put_node.data;
386 self.loop.onNextTick(put_node.tick_node);
387 self.buffer_index +%= 1;
388 self.buffer_len += 1;
389
390 put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
391 }
392 }
393
394 // undo the extra subtractions
395 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
396 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
397
398 // clear need-dispatch flag
399 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
400 if (need_dispatch != 0) continue;
401
402 const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
403 assert(my_lock != 0);
404
405 // we have to check again now that we unlocked
406 if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock;
407
408 return;
409 }
410 }
411 }
412 };
413}
414
415pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File {
416 var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733
417
418 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp);
419 errdefer std.os.close(sockfd);
420
421 try std.os.posixConnectAsync(sockfd, &address.os_addr);
422 try await try async loop.waitFd(sockfd);
423 try std.os.posixGetSockOptConnectError(sockfd);
424
425 return std.os.File.openHandle(sockfd);
426}
427
428test "listen on a port, send bytes, receive bytes" {
429 if (builtin.os != builtin.Os.linux) {
430 // TODO build abstractions for other operating systems
431 return;
432 }
433 const MyServer = struct {
434 tcp_server: TcpServer,
435
436 const Self = this;
437 async<*mem.Allocator> fn handler(tcp_server: *TcpServer, _addr: *const std.net.Address, _socket: *const std.os.File) void {
438 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
439 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
440 defer socket.close();
441 const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
442 error.OutOfMemory => @panic("unable to handle connection: out of memory"),
443 };
444 (await next_handler) catch |err| {
445 std.debug.panic("unable to handle connection: {}\n", err);
446 };
447 suspend |p| {
448 cancel p;
449 }
450 }
451 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
452 const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
453 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
454
455 var adapter = std.io.FileOutStream.init(&socket);
456 var stream = &adapter.stream;
457 try stream.print("hello from server\n");
458 }
459 };
460
461 const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
462 const addr = std.net.Address.initIp4(ip4addr, 0);
463
464 var loop = try Loop.init(std.debug.global_allocator);
465 var server = MyServer{ .tcp_server = try TcpServer.init(&loop) };
466 defer server.tcp_server.deinit();
467 try server.tcp_server.listen(addr, MyServer.handler);
468
469 const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address);
470 defer cancel p;
471 loop.run();
472}
473
474async fn doAsyncTest(loop: *Loop, address: *const std.net.Address) void {
475 errdefer @panic("test failure");
476
477 var socket_file = try await try async event.connect(loop, address);
478 defer socket_file.close();
479
480 var buf: [512]u8 = undefined;
481 const amt_read = try socket_file.read(buf[0..]);
482 const msg = buf[0..amt_read];
483 assert(mem.eql(u8, msg, "hello from server\n"));
484 loop.stop();
485}
486
487test "std.event.Channel" {
488 var da = std.heap.DirectAllocator.init();
489 defer da.deinit();
490
491 const allocator = &da.allocator;
492
493 var loop = try Loop.init(allocator);
494 defer loop.deinit();
495
496 const channel = try Channel(i32).create(&loop, 0);
497 defer channel.destroy();
498
499 const handle = try async<allocator> testChannelGetter(&loop, channel);
500 defer cancel handle;
501
502 const putter = try async<allocator> testChannelPutter(channel);
503 defer cancel putter;
504
505 loop.run();
506}
507
508async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
509 errdefer @panic("test failed");
510
511 const value1_promise = try async channel.get();
512 const value1 = await value1_promise;
513 assert(value1 == 1234);
514
515 const value2_promise = try async channel.get();
516 const value2 = await value2_promise;
517 assert(value2 == 4567);
518
519 loop.stop();
520}
521
522async fn testChannelPutter(channel: *Channel(i32)) void {
523 await (async channel.put(1234) catch @panic("out of memory"));
524 await (async channel.put(4567) catch @panic("out of memory"));
525}13}
std/event/channel.zig created+254
...@@ -0,0 +1,254 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const AtomicOrder = builtin.AtomicOrder;
6const Loop = std.event.Loop;
7
8/// many producer, many consumer, thread-safe, lock-free, runtime configurable buffer size
9/// when buffer is empty, consumers suspend and are resumed by producers
10/// when buffer is full, producers suspend and are resumed by consumers
11pub fn Channel(comptime T: type) type {
12 return struct {
13 loop: *Loop,
14
15 getters: std.atomic.QueueMpsc(GetNode),
16 putters: std.atomic.QueueMpsc(PutNode),
17 get_count: usize,
18 put_count: usize,
19 dispatch_lock: u8, // TODO make this a bool
20 need_dispatch: u8, // TODO make this a bool
21
22 // simple fixed size ring buffer
23 buffer_nodes: []T,
24 buffer_index: usize,
25 buffer_len: usize,
26
27 const SelfChannel = this;
28 const GetNode = struct {
29 ptr: *T,
30 tick_node: *Loop.NextTickNode,
31 };
32 const PutNode = struct {
33 data: T,
34 tick_node: *Loop.NextTickNode,
35 };
36
37 /// call destroy when done
38 pub fn create(loop: *Loop, capacity: usize) !*SelfChannel {
39 const buffer_nodes = try loop.allocator.alloc(T, capacity);
40 errdefer loop.allocator.free(buffer_nodes);
41
42 const self = try loop.allocator.create(SelfChannel{
43 .loop = loop,
44 .buffer_len = 0,
45 .buffer_nodes = buffer_nodes,
46 .buffer_index = 0,
47 .dispatch_lock = 0,
48 .need_dispatch = 0,
49 .getters = std.atomic.QueueMpsc(GetNode).init(),
50 .putters = std.atomic.QueueMpsc(PutNode).init(),
51 .get_count = 0,
52 .put_count = 0,
53 });
54 errdefer loop.allocator.destroy(self);
55
56 return self;
57 }
58
59 /// must be called when all calls to put and get have suspended and no more calls occur
60 pub fn destroy(self: *SelfChannel) void {
61 while (self.getters.get()) |get_node| {
62 cancel get_node.data.tick_node.data;
63 }
64 while (self.putters.get()) |put_node| {
65 cancel put_node.data.tick_node.data;
66 }
67 self.loop.allocator.free(self.buffer_nodes);
68 self.loop.allocator.destroy(self);
69 }
70
71 /// puts a data item in the channel. The promise completes when the value has been added to the
72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
73 pub async fn put(self: *SelfChannel, data: T) void {
74 // TODO should be able to group memory allocation failure before first suspend point
75 // so that the async invocation catches it
76 var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined;
77 _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable;
78
79 suspend |handle| {
80 var my_tick_node = Loop.NextTickNode{
81 .next = undefined,
82 .data = handle,
83 };
84 var queue_node = std.atomic.QueueMpsc(PutNode).Node{
85 .data = PutNode{
86 .tick_node = &my_tick_node,
87 .data = data,
88 },
89 .next = undefined,
90 };
91 self.putters.put(&queue_node);
92 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
93
94 self.loop.onNextTick(dispatch_tick_node_ptr);
95 }
96 }
97
98 /// await this function to get an item from the channel. If the buffer is empty, the promise will
99 /// complete when the next item is put in the channel.
100 pub async fn get(self: *SelfChannel) T {
101 // TODO should be able to group memory allocation failure before first suspend point
102 // so that the async invocation catches it
103 var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined;
104 _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable;
105
106 // TODO integrate this function with named return values
107 // so we can get rid of this extra result copy
108 var result: T = undefined;
109 suspend |handle| {
110 var my_tick_node = Loop.NextTickNode{
111 .next = undefined,
112 .data = handle,
113 };
114 var queue_node = std.atomic.QueueMpsc(GetNode).Node{
115 .data = GetNode{
116 .ptr = &result,
117 .tick_node = &my_tick_node,
118 },
119 .next = undefined,
120 };
121 self.getters.put(&queue_node);
122 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
123
124 self.loop.onNextTick(dispatch_tick_node_ptr);
125 }
126 return result;
127 }
128
129 async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void {
130 // resumed by onNextTick
131 suspend |handle| {
132 var tick_node = Loop.NextTickNode{
133 .data = handle,
134 .next = undefined,
135 };
136 tick_node_ptr.* = &tick_node;
137 }
138
139 // set the "need dispatch" flag
140 _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
141
142 lock: while (true) {
143 // set the lock flag
144 const prev_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
145 if (prev_lock != 0) return;
146
147 // clear the need_dispatch flag since we're about to do it
148 _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
149
150 while (true) {
151 one_dispatch: {
152 // later we correct these extra subtractions
153 var get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
154 var put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
155
156 // transfer self.buffer to self.getters
157 while (self.buffer_len != 0) {
158 if (get_count == 0) break :one_dispatch;
159
160 const get_node = &self.getters.get().?.data;
161 get_node.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
162 self.loop.onNextTick(get_node.tick_node);
163 self.buffer_len -= 1;
164
165 get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
166 }
167
168 // direct transfer self.putters to self.getters
169 while (get_count != 0 and put_count != 0) {
170 const get_node = &self.getters.get().?.data;
171 const put_node = &self.putters.get().?.data;
172
173 get_node.ptr.* = put_node.data;
174 self.loop.onNextTick(get_node.tick_node);
175 self.loop.onNextTick(put_node.tick_node);
176
177 get_count = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
178 put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
179 }
180
181 // transfer self.putters to self.buffer
182 while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
183 const put_node = &self.putters.get().?.data;
184
185 self.buffer_nodes[self.buffer_index] = put_node.data;
186 self.loop.onNextTick(put_node.tick_node);
187 self.buffer_index +%= 1;
188 self.buffer_len += 1;
189
190 put_count = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
191 }
192 }
193
194 // undo the extra subtractions
195 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
196 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
197
198 // clear need-dispatch flag
199 const need_dispatch = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
200 if (need_dispatch != 0) continue;
201
202 const my_lock = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
203 assert(my_lock != 0);
204
205 // we have to check again now that we unlocked
206 if (@atomicLoad(u8, &self.need_dispatch, AtomicOrder.SeqCst) != 0) continue :lock;
207
208 return;
209 }
210 }
211 }
212 };
213}
214
215test "std.event.Channel" {
216 var da = std.heap.DirectAllocator.init();
217 defer da.deinit();
218
219 const allocator = &da.allocator;
220
221 var loop: Loop = undefined;
222 // TODO make a multi threaded test
223 try loop.initSingleThreaded(allocator);
224 defer loop.deinit();
225
226 const channel = try Channel(i32).create(&loop, 0);
227 defer channel.destroy();
228
229 const handle = try async<allocator> testChannelGetter(&loop, channel);
230 defer cancel handle;
231
232 const putter = try async<allocator> testChannelPutter(channel);
233 defer cancel putter;
234
235 loop.run();
236}
237
238async fn testChannelGetter(loop: *Loop, channel: *Channel(i32)) void {
239 errdefer @panic("test failed");
240
241 const value1_promise = try async channel.get();
242 const value1 = await value1_promise;
243 assert(value1 == 1234);
244
245 const value2_promise = try async channel.get();
246 const value2 = await value2_promise;
247 assert(value2 == 4567);
248}
249
250async fn testChannelPutter(channel: *Channel(i32)) void {
251 await (async channel.put(1234) catch @panic("out of memory"));
252 await (async channel.put(4567) catch @panic("out of memory"));
253}
254
std/event/lock.zig created+204
...@@ -0,0 +1,204 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const mem = std.mem;
5const AtomicRmwOp = builtin.AtomicRmwOp;
6const AtomicOrder = builtin.AtomicOrder;
7const Loop = std.event.Loop;
8
9/// Thread-safe async/await lock.
10/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
11/// are resumed when the lock is released, in order.
12pub const Lock = struct {
13 loop: *Loop,
14 shared_bit: u8, // TODO make this a bool
15 queue: Queue,
16 queue_empty_bit: u8, // TODO make this a bool
17
18 const Queue = std.atomic.QueueMpsc(promise);
19
20 pub const Held = struct {
21 lock: *Lock,
22
23 pub fn release(self: Held) void {
24 // Resume the next item from the queue.
25 if (self.lock.queue.get()) |node| {
26 self.lock.loop.onNextTick(node);
27 return;
28 }
29
30 // We need to release the lock.
31 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
32 _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
33
34 // There might be a queue item. If we know the queue is empty, we can be done,
35 // because the other actor will try to obtain the lock.
36 // But if there's a queue item, we are the actor which must loop and attempt
37 // to grab the lock again.
38 if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
39 return;
40 }
41
42 while (true) {
43 const old_bit = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
44 if (old_bit != 0) {
45 // We did not obtain the lock. Great, the queue is someone else's problem.
46 return;
47 }
48
49 // Resume the next item from the queue.
50 if (self.lock.queue.get()) |node| {
51 self.lock.loop.onNextTick(node);
52 return;
53 }
54
55 // Release the lock again.
56 _ = @atomicRmw(u8, &self.lock.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
57 _ = @atomicRmw(u8, &self.lock.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
58
59 // Find out if we can be done.
60 if (@atomicLoad(u8, &self.lock.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
61 return;
62 }
63 }
64 }
65 };
66
67 pub fn init(loop: *Loop) Lock {
68 return Lock{
69 .loop = loop,
70 .shared_bit = 0,
71 .queue = Queue.init(),
72 .queue_empty_bit = 1,
73 };
74 }
75
76 /// Must be called when not locked. Not thread safe.
77 /// All calls to acquire() and release() must complete before calling deinit().
78 pub fn deinit(self: *Lock) void {
79 assert(self.shared_bit == 0);
80 while (self.queue.get()) |node| cancel node.data;
81 }
82
83 pub async fn acquire(self: *Lock) Held {
84 s: suspend |handle| {
85 // TODO explicitly put this memory in the coroutine frame #1194
86 var my_tick_node = Loop.NextTickNode{
87 .data = handle,
88 .next = undefined,
89 };
90
91 self.queue.put(&my_tick_node);
92
93 // At this point, we are in the queue, so we might have already been resumed and this coroutine
94 // frame might be destroyed. For the rest of the suspend block we cannot access the coroutine frame.
95
96 // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor
97 // will attempt to grab the lock.
98 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
99
100 while (true) {
101 const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
102 if (old_bit != 0) {
103 // We did not obtain the lock. Trust that our queue entry will resume us, and allow
104 // suspend to complete.
105 break;
106 }
107 // We got the lock. However we might have already been resumed from the queue.
108 if (self.queue.get()) |node| {
109 // Whether this node is us or someone else, we tail resume it.
110 resume node.data;
111 break;
112 } else {
113 // We already got resumed, and there are none left in the queue, which means that
114 // we aren't even supposed to hold the lock right now.
115 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
116 _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
117
118 // There might be a queue item. If we know the queue is empty, we can be done,
119 // because the other actor will try to obtain the lock.
120 // But if there's a queue item, we are the actor which must loop and attempt
121 // to grab the lock again.
122 if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
123 break;
124 } else {
125 continue;
126 }
127 }
128 unreachable;
129 }
130 }
131
132 return Held{ .lock = self };
133 }
134};
135
136test "std.event.Lock" {
137 var da = std.heap.DirectAllocator.init();
138 defer da.deinit();
139
140 const allocator = &da.allocator;
141
142 var loop: Loop = undefined;
143 try loop.initMultiThreaded(allocator);
144 defer loop.deinit();
145
146 var lock = Lock.init(&loop);
147 defer lock.deinit();
148
149 const handle = try async<allocator> testLock(&loop, &lock);
150 defer cancel handle;
151 loop.run();
152
153 assert(mem.eql(i32, shared_test_data, [1]i32{3 * @intCast(i32, shared_test_data.len)} ** shared_test_data.len));
154}
155
156async fn testLock(loop: *Loop, lock: *Lock) void {
157 // TODO explicitly put next tick node memory in the coroutine frame #1194
158 suspend |p| {
159 resume p;
160 }
161 const handle1 = async lockRunner(lock) catch @panic("out of memory");
162 var tick_node1 = Loop.NextTickNode{
163 .next = undefined,
164 .data = handle1,
165 };
166 loop.onNextTick(&tick_node1);
167
168 const handle2 = async lockRunner(lock) catch @panic("out of memory");
169 var tick_node2 = Loop.NextTickNode{
170 .next = undefined,
171 .data = handle2,
172 };
173 loop.onNextTick(&tick_node2);
174
175 const handle3 = async lockRunner(lock) catch @panic("out of memory");
176 var tick_node3 = Loop.NextTickNode{
177 .next = undefined,
178 .data = handle3,
179 };
180 loop.onNextTick(&tick_node3);
181
182 await handle1;
183 await handle2;
184 await handle3;
185}
186
187var shared_test_data = [1]i32{0} ** 10;
188var shared_test_index: usize = 0;
189
190async fn lockRunner(lock: *Lock) void {
191 suspend; // resumed by onNextTick
192
193 var i: usize = 0;
194 while (i < shared_test_data.len) : (i += 1) {
195 const lock_promise = async lock.acquire() catch @panic("out of memory");
196 const handle = await lock_promise;
197 defer handle.release();
198
199 shared_test_index = 0;
200 while (shared_test_index < shared_test_data.len) : (shared_test_index += 1) {
201 shared_test_data[shared_test_index] = shared_test_data[shared_test_index] + 1;
202 }
203 }
204}
std/event/locked.zig created+43
...@@ -0,0 +1,43 @@
1const std = @import("../index.zig");
2const Lock = std.event.Lock;
3const Loop = std.event.Loop;
4
5/// Thread-safe async/await lock that protects one piece of data.
6/// Does not make any syscalls - coroutines which are waiting for the lock are suspended, and
7/// are resumed when the lock is released, in order.
8pub fn Locked(comptime T: type) type {
9 return struct {
10 lock: Lock,
11 private_data: T,
12
13 const Self = this;
14
15 pub const HeldLock = struct {
16 value: *T,
17 held: Lock.Held,
18
19 pub fn release(self: HeldLock) void {
20 self.held.release();
21 }
22 };
23
24 pub fn init(loop: *Loop, data: T) Self {
25 return Self{
26 .lock = Lock.init(loop),
27 .private_data = data,
28 };
29 }
30
31 pub fn deinit(self: *Self) void {
32 self.lock.deinit();
33 }
34
35 pub async fn acquire(self: *Self) HeldLock {
36 return HeldLock{
37 // TODO guaranteed allocation elision
38 .held = await (async self.lock.acquire() catch unreachable),
39 .value = &self.private_data,
40 };
41 }
42 };
43}
std/event/loop.zig created+629
...@@ -0,0 +1,629 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const mem = std.mem;
5const posix = std.os.posix;
6const windows = std.os.windows;
7const AtomicRmwOp = builtin.AtomicRmwOp;
8const AtomicOrder = builtin.AtomicOrder;
9
10pub const Loop = struct {
11 allocator: *mem.Allocator,
12 next_tick_queue: std.atomic.QueueMpsc(promise),
13 os_data: OsData,
14 final_resume_node: ResumeNode,
15 dispatch_lock: u8, // TODO make this a bool
16 pending_event_count: usize,
17 extra_threads: []*std.os.Thread,
18
19 // pre-allocated eventfds. all permanently active.
20 // this is how we send promises to be resumed on other threads.
21 available_eventfd_resume_nodes: std.atomic.Stack(ResumeNode.EventFd),
22 eventfd_resume_nodes: []std.atomic.Stack(ResumeNode.EventFd).Node,
23
24 pub const NextTickNode = std.atomic.QueueMpsc(promise).Node;
25
26 pub const ResumeNode = struct {
27 id: Id,
28 handle: promise,
29
30 pub const Id = enum {
31 Basic,
32 Stop,
33 EventFd,
34 };
35
36 pub const EventFd = switch (builtin.os) {
37 builtin.Os.macosx => MacOsEventFd,
38 builtin.Os.linux => struct {
39 base: ResumeNode,
40 epoll_op: u32,
41 eventfd: i32,
42 },
43 builtin.Os.windows => struct {
44 base: ResumeNode,
45 completion_key: usize,
46 },
47 else => @compileError("unsupported OS"),
48 };
49
50 const MacOsEventFd = struct {
51 base: ResumeNode,
52 kevent: posix.Kevent,
53 };
54 };
55
56 /// After initialization, call run().
57 /// TODO copy elision / named return values so that the threads referencing *Loop
58 /// have the correct pointer value.
59 fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
60 return self.initInternal(allocator, 1);
61 }
62
63 /// The allocator must be thread-safe because we use it for multiplexing
64 /// coroutines onto kernel threads.
65 /// After initialization, call run().
66 /// TODO copy elision / named return values so that the threads referencing *Loop
67 /// have the correct pointer value.
68 fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
69 const core_count = try std.os.cpuCount(allocator);
70 return self.initInternal(allocator, core_count);
71 }
72
73 /// Thread count is the total thread count. The thread pool size will be
74 /// max(thread_count - 1, 0)
75 fn initInternal(self: *Loop, allocator: *mem.Allocator, thread_count: usize) !void {
76 self.* = Loop{
77 .pending_event_count = 0,
78 .allocator = allocator,
79 .os_data = undefined,
80 .next_tick_queue = std.atomic.QueueMpsc(promise).init(),
81 .dispatch_lock = 1, // start locked so threads go directly into epoll wait
82 .extra_threads = undefined,
83 .available_eventfd_resume_nodes = std.atomic.Stack(ResumeNode.EventFd).init(),
84 .eventfd_resume_nodes = undefined,
85 .final_resume_node = ResumeNode{
86 .id = ResumeNode.Id.Stop,
87 .handle = undefined,
88 },
89 };
90 const extra_thread_count = thread_count - 1;
91 self.eventfd_resume_nodes = try self.allocator.alloc(
92 std.atomic.Stack(ResumeNode.EventFd).Node,
93 extra_thread_count,
94 );
95 errdefer self.allocator.free(self.eventfd_resume_nodes);
96
97 self.extra_threads = try self.allocator.alloc(*std.os.Thread, extra_thread_count);
98 errdefer self.allocator.free(self.extra_threads);
99
100 try self.initOsData(extra_thread_count);
101 errdefer self.deinitOsData();
102 }
103
104 /// must call stop before deinit
105 pub fn deinit(self: *Loop) void {
106 self.deinitOsData();
107 self.allocator.free(self.extra_threads);
108 }
109
110 const InitOsDataError = std.os.LinuxEpollCreateError || mem.Allocator.Error || std.os.LinuxEventFdError ||
111 std.os.SpawnThreadError || std.os.LinuxEpollCtlError || std.os.BsdKEventError ||
112 std.os.WindowsCreateIoCompletionPortError;
113
114 const wakeup_bytes = []u8{0x1} ** 8;
115
116 fn initOsData(self: *Loop, extra_thread_count: usize) InitOsDataError!void {
117 switch (builtin.os) {
118 builtin.Os.linux => {
119 errdefer {
120 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
121 }
122 for (self.eventfd_resume_nodes) |*eventfd_node| {
123 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
124 .data = ResumeNode.EventFd{
125 .base = ResumeNode{
126 .id = ResumeNode.Id.EventFd,
127 .handle = undefined,
128 },
129 .eventfd = try std.os.linuxEventFd(1, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK),
130 .epoll_op = posix.EPOLL_CTL_ADD,
131 },
132 .next = undefined,
133 };
134 self.available_eventfd_resume_nodes.push(eventfd_node);
135 }
136
137 self.os_data.epollfd = try std.os.linuxEpollCreate(posix.EPOLL_CLOEXEC);
138 errdefer std.os.close(self.os_data.epollfd);
139
140 self.os_data.final_eventfd = try std.os.linuxEventFd(0, posix.EFD_CLOEXEC | posix.EFD_NONBLOCK);
141 errdefer std.os.close(self.os_data.final_eventfd);
142
143 self.os_data.final_eventfd_event = posix.epoll_event{
144 .events = posix.EPOLLIN,
145 .data = posix.epoll_data{ .ptr = @ptrToInt(&self.final_resume_node) },
146 };
147 try std.os.linuxEpollCtl(
148 self.os_data.epollfd,
149 posix.EPOLL_CTL_ADD,
150 self.os_data.final_eventfd,
151 &self.os_data.final_eventfd_event,
152 );
153
154 var extra_thread_index: usize = 0;
155 errdefer {
156 // writing 8 bytes to an eventfd cannot fail
157 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
158 while (extra_thread_index != 0) {
159 extra_thread_index -= 1;
160 self.extra_threads[extra_thread_index].wait();
161 }
162 }
163 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
164 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
165 }
166 },
167 builtin.Os.macosx => {
168 self.os_data.kqfd = try std.os.bsdKQueue();
169 errdefer std.os.close(self.os_data.kqfd);
170
171 self.os_data.kevents = try self.allocator.alloc(posix.Kevent, extra_thread_count);
172 errdefer self.allocator.free(self.os_data.kevents);
173
174 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
175
176 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
177 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
178 .data = ResumeNode.EventFd{
179 .base = ResumeNode{
180 .id = ResumeNode.Id.EventFd,
181 .handle = undefined,
182 },
183 // this one is for sending events
184 .kevent = posix.Kevent{
185 .ident = i,
186 .filter = posix.EVFILT_USER,
187 .flags = posix.EV_CLEAR | posix.EV_ADD | posix.EV_DISABLE,
188 .fflags = 0,
189 .data = 0,
190 .udata = @ptrToInt(&eventfd_node.data.base),
191 },
192 },
193 .next = undefined,
194 };
195 self.available_eventfd_resume_nodes.push(eventfd_node);
196 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.data.kevent);
197 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
198 eventfd_node.data.kevent.flags = posix.EV_CLEAR | posix.EV_ENABLE;
199 eventfd_node.data.kevent.fflags = posix.NOTE_TRIGGER;
200 // this one is for waiting for events
201 self.os_data.kevents[i] = posix.Kevent{
202 .ident = i,
203 .filter = posix.EVFILT_USER,
204 .flags = 0,
205 .fflags = 0,
206 .data = 0,
207 .udata = @ptrToInt(&eventfd_node.data.base),
208 };
209 }
210
211 // Pre-add so that we cannot get error.SystemResources
212 // later when we try to activate it.
213 self.os_data.final_kevent = posix.Kevent{
214 .ident = extra_thread_count,
215 .filter = posix.EVFILT_USER,
216 .flags = posix.EV_ADD | posix.EV_DISABLE,
217 .fflags = 0,
218 .data = 0,
219 .udata = @ptrToInt(&self.final_resume_node),
220 };
221 const kevent_array = (*[1]posix.Kevent)(&self.os_data.final_kevent);
222 _ = try std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null);
223 self.os_data.final_kevent.flags = posix.EV_ENABLE;
224 self.os_data.final_kevent.fflags = posix.NOTE_TRIGGER;
225
226 var extra_thread_index: usize = 0;
227 errdefer {
228 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch unreachable;
229 while (extra_thread_index != 0) {
230 extra_thread_index -= 1;
231 self.extra_threads[extra_thread_index].wait();
232 }
233 }
234 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
235 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
236 }
237 },
238 builtin.Os.windows => {
239 self.os_data.extra_thread_count = extra_thread_count;
240
241 self.os_data.io_port = try std.os.windowsCreateIoCompletionPort(
242 windows.INVALID_HANDLE_VALUE,
243 null,
244 undefined,
245 undefined,
246 );
247 errdefer std.os.close(self.os_data.io_port);
248
249 for (self.eventfd_resume_nodes) |*eventfd_node, i| {
250 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
251 .data = ResumeNode.EventFd{
252 .base = ResumeNode{
253 .id = ResumeNode.Id.EventFd,
254 .handle = undefined,
255 },
256 // this one is for sending events
257 .completion_key = @ptrToInt(&eventfd_node.data.base),
258 },
259 .next = undefined,
260 };
261 self.available_eventfd_resume_nodes.push(eventfd_node);
262 }
263
264 var extra_thread_index: usize = 0;
265 errdefer {
266 var i: usize = 0;
267 while (i < extra_thread_index) : (i += 1) {
268 while (true) {
269 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
270 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
271 break;
272 }
273 }
274 while (extra_thread_index != 0) {
275 extra_thread_index -= 1;
276 self.extra_threads[extra_thread_index].wait();
277 }
278 }
279 while (extra_thread_index < extra_thread_count) : (extra_thread_index += 1) {
280 self.extra_threads[extra_thread_index] = try std.os.spawnThread(self, workerRun);
281 }
282 },
283 else => {},
284 }
285 }
286
287 fn deinitOsData(self: *Loop) void {
288 switch (builtin.os) {
289 builtin.Os.linux => {
290 std.os.close(self.os_data.final_eventfd);
291 while (self.available_eventfd_resume_nodes.pop()) |node| std.os.close(node.data.eventfd);
292 std.os.close(self.os_data.epollfd);
293 self.allocator.free(self.eventfd_resume_nodes);
294 },
295 builtin.Os.macosx => {
296 self.allocator.free(self.os_data.kevents);
297 std.os.close(self.os_data.kqfd);
298 },
299 builtin.Os.windows => {
300 std.os.close(self.os_data.io_port);
301 },
302 else => {},
303 }
304 }
305
306 /// resume_node must live longer than the promise that it holds a reference to.
307 pub fn addFd(self: *Loop, fd: i32, resume_node: *ResumeNode) !void {
308 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
309 errdefer {
310 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
311 }
312 try self.modFd(
313 fd,
314 posix.EPOLL_CTL_ADD,
315 std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET,
316 resume_node,
317 );
318 }
319
320 pub fn modFd(self: *Loop, fd: i32, op: u32, events: u32, resume_node: *ResumeNode) !void {
321 var ev = std.os.linux.epoll_event{
322 .events = events,
323 .data = std.os.linux.epoll_data{ .ptr = @ptrToInt(resume_node) },
324 };
325 try std.os.linuxEpollCtl(self.os_data.epollfd, op, fd, &ev);
326 }
327
328 pub fn removeFd(self: *Loop, fd: i32) void {
329 self.removeFdNoCounter(fd);
330 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
331 }
332
333 fn removeFdNoCounter(self: *Loop, fd: i32) void {
334 std.os.linuxEpollCtl(self.os_data.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
335 }
336
337 pub async fn waitFd(self: *Loop, fd: i32) !void {
338 defer self.removeFd(fd);
339 suspend |p| {
340 // TODO explicitly put this memory in the coroutine frame #1194
341 var resume_node = ResumeNode{
342 .id = ResumeNode.Id.Basic,
343 .handle = p,
344 };
345 try self.addFd(fd, &resume_node);
346 }
347 }
348
349 /// Bring your own linked list node. This means it can't fail.
350 pub fn onNextTick(self: *Loop, node: *NextTickNode) void {
351 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
352 self.next_tick_queue.put(node);
353 }
354
355 pub fn run(self: *Loop) void {
356 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
357 self.workerRun();
358 for (self.extra_threads) |extra_thread| {
359 extra_thread.wait();
360 }
361 }
362
363 /// This is equivalent to an async call, except instead of beginning execution of the async function,
364 /// it immediately returns to the caller, and the async function is queued in the event loop. It still
365 /// returns a promise to be awaited.
366 pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {
367 const S = struct {
368 async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {
369 suspend |p| {
370 handle.* = p;
371 var my_tick_node = Loop.NextTickNode{
372 .next = undefined,
373 .data = p,
374 };
375 loop.onNextTick(&my_tick_node);
376 }
377 // TODO guaranteed allocation elision for await in same func as async
378 return await (async func(args2) catch unreachable);
379 }
380 };
381 var handle: promise->@typeOf(func).ReturnType = undefined;
382 return async<self.allocator> S.asyncFunc(self, &handle, args);
383 }
384
385 fn workerRun(self: *Loop) void {
386 start_over: while (true) {
387 if (@atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) {
388 while (self.next_tick_queue.get()) |next_tick_node| {
389 const handle = next_tick_node.data;
390 if (self.next_tick_queue.isEmpty()) {
391 // last node, just resume it
392 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
393 resume handle;
394 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
395 continue :start_over;
396 }
397
398 // non-last node, stick it in the epoll/kqueue set so that
399 // other threads can get to it
400 if (self.available_eventfd_resume_nodes.pop()) |resume_stack_node| {
401 const eventfd_node = &resume_stack_node.data;
402 eventfd_node.base.handle = handle;
403 switch (builtin.os) {
404 builtin.Os.macosx => {
405 const kevent_array = (*[1]posix.Kevent)(&eventfd_node.kevent);
406 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
407 _ = std.os.bsdKEvent(self.os_data.kqfd, kevent_array, eventlist, null) catch {
408 // fine, we didn't need it anyway
409 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
410 self.available_eventfd_resume_nodes.push(resume_stack_node);
411 resume handle;
412 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
413 continue :start_over;
414 };
415 },
416 builtin.Os.linux => {
417 // the pending count is already accounted for
418 const epoll_events = posix.EPOLLONESHOT | std.os.linux.EPOLLIN | std.os.linux.EPOLLOUT | std.os.linux.EPOLLET;
419 self.modFd(eventfd_node.eventfd, eventfd_node.epoll_op, epoll_events, &eventfd_node.base) catch {
420 // fine, we didn't need it anyway
421 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
422 self.available_eventfd_resume_nodes.push(resume_stack_node);
423 resume handle;
424 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
425 continue :start_over;
426 };
427 },
428 builtin.Os.windows => {
429 // this value is never dereferenced but we need it to be non-null so that
430 // the consumer code can decide whether to read the completion key.
431 // it has to do this for normal I/O, so we match that behavior here.
432 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
433 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, eventfd_node.completion_key, overlapped) catch {
434 // fine, we didn't need it anyway
435 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
436 self.available_eventfd_resume_nodes.push(resume_stack_node);
437 resume handle;
438 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
439 continue :start_over;
440 };
441 },
442 else => @compileError("unsupported OS"),
443 }
444 } else {
445 // threads are too busy, can't add another eventfd to wake one up
446 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
447 resume handle;
448 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
449 continue :start_over;
450 }
451 }
452
453 const pending_event_count = @atomicLoad(usize, &self.pending_event_count, AtomicOrder.SeqCst);
454 if (pending_event_count == 0) {
455 // cause all the threads to stop
456 switch (builtin.os) {
457 builtin.Os.linux => {
458 // writing 8 bytes to an eventfd cannot fail
459 std.os.posixWrite(self.os_data.final_eventfd, wakeup_bytes) catch unreachable;
460 return;
461 },
462 builtin.Os.macosx => {
463 const final_kevent = (*[1]posix.Kevent)(&self.os_data.final_kevent);
464 const eventlist = ([*]posix.Kevent)(undefined)[0..0];
465 // cannot fail because we already added it and this just enables it
466 _ = std.os.bsdKEvent(self.os_data.kqfd, final_kevent, eventlist, null) catch unreachable;
467 return;
468 },
469 builtin.Os.windows => {
470 var i: usize = 0;
471 while (i < self.os_data.extra_thread_count) : (i += 1) {
472 while (true) {
473 const overlapped = @intToPtr(?*windows.OVERLAPPED, 0x1);
474 std.os.windowsPostQueuedCompletionStatus(self.os_data.io_port, undefined, @ptrToInt(&self.final_resume_node), overlapped) catch continue;
475 break;
476 }
477 }
478 return;
479 },
480 else => @compileError("unsupported OS"),
481 }
482 }
483
484 _ = @atomicRmw(u8, &self.dispatch_lock, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
485 }
486
487 switch (builtin.os) {
488 builtin.Os.linux => {
489 // only process 1 event so we don't steal from other threads
490 var events: [1]std.os.linux.epoll_event = undefined;
491 const count = std.os.linuxEpollWait(self.os_data.epollfd, events[0..], -1);
492 for (events[0..count]) |ev| {
493 const resume_node = @intToPtr(*ResumeNode, ev.data.ptr);
494 const handle = resume_node.handle;
495 const resume_node_id = resume_node.id;
496 switch (resume_node_id) {
497 ResumeNode.Id.Basic => {},
498 ResumeNode.Id.Stop => return,
499 ResumeNode.Id.EventFd => {
500 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
501 event_fd_node.epoll_op = posix.EPOLL_CTL_MOD;
502 const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node);
503 self.available_eventfd_resume_nodes.push(stack_node);
504 },
505 }
506 resume handle;
507 if (resume_node_id == ResumeNode.Id.EventFd) {
508 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
509 }
510 }
511 },
512 builtin.Os.macosx => {
513 var eventlist: [1]posix.Kevent = undefined;
514 const count = std.os.bsdKEvent(self.os_data.kqfd, self.os_data.kevents, eventlist[0..], null) catch unreachable;
515 for (eventlist[0..count]) |ev| {
516 const resume_node = @intToPtr(*ResumeNode, ev.udata);
517 const handle = resume_node.handle;
518 const resume_node_id = resume_node.id;
519 switch (resume_node_id) {
520 ResumeNode.Id.Basic => {},
521 ResumeNode.Id.Stop => return,
522 ResumeNode.Id.EventFd => {
523 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
524 const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node);
525 self.available_eventfd_resume_nodes.push(stack_node);
526 },
527 }
528 resume handle;
529 if (resume_node_id == ResumeNode.Id.EventFd) {
530 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
531 }
532 }
533 },
534 builtin.Os.windows => {
535 var completion_key: usize = undefined;
536 while (true) {
537 var nbytes: windows.DWORD = undefined;
538 var overlapped: ?*windows.OVERLAPPED = undefined;
539 switch (std.os.windowsGetQueuedCompletionStatus(self.os_data.io_port, &nbytes, &completion_key, &overlapped, windows.INFINITE)) {
540 std.os.WindowsWaitResult.Aborted => return,
541 std.os.WindowsWaitResult.Normal => {},
542 }
543 if (overlapped != null) break;
544 }
545 const resume_node = @intToPtr(*ResumeNode, completion_key);
546 const handle = resume_node.handle;
547 const resume_node_id = resume_node.id;
548 switch (resume_node_id) {
549 ResumeNode.Id.Basic => {},
550 ResumeNode.Id.Stop => return,
551 ResumeNode.Id.EventFd => {
552 const event_fd_node = @fieldParentPtr(ResumeNode.EventFd, "base", resume_node);
553 const stack_node = @fieldParentPtr(std.atomic.Stack(ResumeNode.EventFd).Node, "data", event_fd_node);
554 self.available_eventfd_resume_nodes.push(stack_node);
555 },
556 }
557 resume handle;
558 if (resume_node_id == ResumeNode.Id.EventFd) {
559 _ = @atomicRmw(usize, &self.pending_event_count, AtomicRmwOp.Sub, 1, AtomicOrder.SeqCst);
560 }
561 },
562 else => @compileError("unsupported OS"),
563 }
564 }
565 }
566
567 const OsData = switch (builtin.os) {
568 builtin.Os.linux => struct {
569 epollfd: i32,
570 final_eventfd: i32,
571 final_eventfd_event: std.os.linux.epoll_event,
572 },
573 builtin.Os.macosx => MacOsData,
574 builtin.Os.windows => struct {
575 io_port: windows.HANDLE,
576 extra_thread_count: usize,
577 },
578 else => struct {},
579 };
580
581 const MacOsData = struct {
582 kqfd: i32,
583 final_kevent: posix.Kevent,
584 kevents: []posix.Kevent,
585 };
586};
587
588test "std.event.Loop - basic" {
589 var da = std.heap.DirectAllocator.init();
590 defer da.deinit();
591
592 const allocator = &da.allocator;
593
594 var loop: Loop = undefined;
595 try loop.initMultiThreaded(allocator);
596 defer loop.deinit();
597
598 loop.run();
599}
600
601test "std.event.Loop - call" {
602 var da = std.heap.DirectAllocator.init();
603 defer da.deinit();
604
605 const allocator = &da.allocator;
606
607 var loop: Loop = undefined;
608 try loop.initMultiThreaded(allocator);
609 defer loop.deinit();
610
611 var did_it = false;
612 const handle = try loop.call(testEventLoop);
613 const handle2 = try loop.call(testEventLoop2, handle, &did_it);
614 defer cancel handle2;
615
616 loop.run();
617
618 assert(did_it);
619}
620
621async fn testEventLoop() i32 {
622 return 1234;
623}
624
625async fn testEventLoop2(h: promise->i32, did_it: *bool) void {
626 const value = await h;
627 assert(value == 1234);
628 did_it.* = true;
629}
std/event/tcp.zig created+183
...@@ -0,0 +1,183 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const event = std.event;
5const mem = std.mem;
6const posix = std.os.posix;
7const windows = std.os.windows;
8const Loop = std.event.Loop;
9
10pub const Server = struct {
11 handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, *const std.os.File) void,
12
13 loop: *Loop,
14 sockfd: ?i32,
15 accept_coro: ?promise,
16 listen_address: std.net.Address,
17
18 waiting_for_emfile_node: PromiseNode,
19 listen_resume_node: event.Loop.ResumeNode,
20
21 const PromiseNode = std.LinkedList(promise).Node;
22
23 pub fn init(loop: *Loop) Server {
24 // TODO can't initialize handler coroutine here because we need well defined copy elision
25 return Server{
26 .loop = loop,
27 .sockfd = null,
28 .accept_coro = null,
29 .handleRequestFn = undefined,
30 .waiting_for_emfile_node = undefined,
31 .listen_address = undefined,
32 .listen_resume_node = event.Loop.ResumeNode{
33 .id = event.Loop.ResumeNode.Id.Basic,
34 .handle = undefined,
35 },
36 };
37 }
38
39 pub fn listen(
40 self: *Server,
41 address: *const std.net.Address,
42 handleRequestFn: async<*mem.Allocator> fn (*Server, *const std.net.Address, *const std.os.File) void,
43 ) !void {
44 self.handleRequestFn = handleRequestFn;
45
46 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp);
47 errdefer std.os.close(sockfd);
48 self.sockfd = sockfd;
49
50 try std.os.posixBind(sockfd, &address.os_addr);
51 try std.os.posixListen(sockfd, posix.SOMAXCONN);
52 self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(sockfd));
53
54 self.accept_coro = try async<self.loop.allocator> Server.handler(self);
55 errdefer cancel self.accept_coro.?;
56
57 self.listen_resume_node.handle = self.accept_coro.?;
58 try self.loop.addFd(sockfd, &self.listen_resume_node);
59 errdefer self.loop.removeFd(sockfd);
60 }
61
62 /// Stop listening
63 pub fn close(self: *Server) void {
64 self.loop.removeFd(self.sockfd.?);
65 std.os.close(self.sockfd.?);
66 }
67
68 pub fn deinit(self: *Server) void {
69 if (self.accept_coro) |accept_coro| cancel accept_coro;
70 if (self.sockfd) |sockfd| std.os.close(sockfd);
71 }
72
73 pub async fn handler(self: *Server) void {
74 while (true) {
75 var accepted_addr: std.net.Address = undefined;
76 if (std.os.posixAccept(self.sockfd.?, &accepted_addr.os_addr, posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd| {
77 var socket = std.os.File.openHandle(accepted_fd);
78 _ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
79 error.OutOfMemory => {
80 socket.close();
81 continue;
82 },
83 };
84 } else |err| switch (err) {
85 error.WouldBlock => {
86 suspend; // we will get resumed by epoll_wait in the event loop
87 continue;
88 },
89 error.ProcessFdQuotaExceeded => {
90 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
91 suspend |p| {
92 self.waiting_for_emfile_node = PromiseNode.init(p);
93 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
94 }
95 continue;
96 },
97 error.ConnectionAborted, error.FileDescriptorClosed => continue,
98
99 error.PageFault => unreachable,
100 error.InvalidSyscall => unreachable,
101 error.FileDescriptorNotASocket => unreachable,
102 error.OperationNotSupported => unreachable,
103
104 error.SystemFdQuotaExceeded, error.SystemResources, error.ProtocolFailure, error.BlockedByFirewall, error.Unexpected => {
105 @panic("TODO handle this error");
106 },
107 }
108 }
109 }
110};
111
112pub async fn connect(loop: *Loop, _address: *const std.net.Address) !std.os.File {
113 var address = _address.*; // TODO https://github.com/ziglang/zig/issues/733
114
115 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM | posix.SOCK_CLOEXEC | posix.SOCK_NONBLOCK, posix.PROTO_tcp);
116 errdefer std.os.close(sockfd);
117
118 try std.os.posixConnectAsync(sockfd, &address.os_addr);
119 try await try async loop.waitFd(sockfd);
120 try std.os.posixGetSockOptConnectError(sockfd);
121
122 return std.os.File.openHandle(sockfd);
123}
124
125test "listen on a port, send bytes, receive bytes" {
126 if (builtin.os != builtin.Os.linux) {
127 // TODO build abstractions for other operating systems
128 return;
129 }
130 const MyServer = struct {
131 tcp_server: Server,
132
133 const Self = this;
134 async<*mem.Allocator> fn handler(tcp_server: *Server, _addr: *const std.net.Address, _socket: *const std.os.File) void {
135 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
136 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
137 defer socket.close();
138 // TODO guarantee elision of this allocation
139 const next_handler = async errorableHandler(self, _addr, socket) catch unreachable;
140 (await next_handler) catch |err| {
141 std.debug.panic("unable to handle connection: {}\n", err);
142 };
143 suspend |p| {
144 cancel p;
145 }
146 }
147 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
148 const addr = _addr.*; // TODO https://github.com/ziglang/zig/issues/733
149 var socket = _socket.*; // TODO https://github.com/ziglang/zig/issues/733
150
151 var adapter = std.io.FileOutStream.init(&socket);
152 var stream = &adapter.stream;
153 try stream.print("hello from server\n");
154 }
155 };
156
157 const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
158 const addr = std.net.Address.initIp4(ip4addr, 0);
159
160 var loop: Loop = undefined;
161 try loop.initSingleThreaded(std.debug.global_allocator);
162 var server = MyServer{ .tcp_server = Server.init(&loop) };
163 defer server.tcp_server.deinit();
164 try server.tcp_server.listen(addr, MyServer.handler);
165
166 const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address, &server.tcp_server);
167 defer cancel p;
168 loop.run();
169}
170
171async fn doAsyncTest(loop: *Loop, address: *const std.net.Address, server: *Server) void {
172 errdefer @panic("test failure");
173
174 var socket_file = try await try async connect(loop, address);
175 defer socket_file.close();
176
177 var buf: [512]u8 = undefined;
178 const amt_read = try socket_file.read(buf[0..]);
179 const msg = buf[0..amt_read];
180 assert(mem.eql(u8, msg, "hello from server\n"));
181 server.close();
182}
183
std/hash_map.zig+10-10
...@@ -259,14 +259,14 @@ test "basic hash map usage" {...@@ -259,14 +259,14 @@ test "basic hash map usage" {
259 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);259 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
260 defer map.deinit();260 defer map.deinit();
261261
262 assert((map.put(1, 11) catch unreachable) == null);262 assert((try map.put(1, 11)) == null);
263 assert((map.put(2, 22) catch unreachable) == null);263 assert((try map.put(2, 22)) == null);
264 assert((map.put(3, 33) catch unreachable) == null);264 assert((try map.put(3, 33)) == null);
265 assert((map.put(4, 44) catch unreachable) == null);265 assert((try map.put(4, 44)) == null);
266 assert((map.put(5, 55) catch unreachable) == null);266 assert((try map.put(5, 55)) == null);
267267
268 assert((map.put(5, 66) catch unreachable).? == 55);268 assert((try map.put(5, 66)).? == 55);
269 assert((map.put(5, 55) catch unreachable).? == 66);269 assert((try map.put(5, 55)).? == 66);
270270
271 assert(map.contains(2));271 assert(map.contains(2));
272 assert(map.get(2).?.value == 22);272 assert(map.get(2).?.value == 22);
...@@ -282,9 +282,9 @@ test "iterator hash map" {...@@ -282,9 +282,9 @@ test "iterator hash map" {
282 var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);282 var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
283 defer reset_map.deinit();283 defer reset_map.deinit();
284284
285 assert((reset_map.put(1, 11) catch unreachable) == null);285 assert((try reset_map.put(1, 11)) == null);
286 assert((reset_map.put(2, 22) catch unreachable) == null);286 assert((try reset_map.put(2, 22)) == null);
287 assert((reset_map.put(3, 33) catch unreachable) == null);287 assert((try reset_map.put(3, 33)) == null);
288288
289 var keys = []i32{289 var keys = []i32{
290 1,290 1,
std/heap.zig+83-16
...@@ -38,7 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void {...@@ -38,7 +38,7 @@ fn cFree(self: *Allocator, old_mem: []u8) void {
38}38}
3939
40/// This allocator makes a syscall directly for every allocation and free.40/// This allocator makes a syscall directly for every allocation and free.
41/// TODO make this thread-safe. The windows implementation will need some atomics.41/// Thread-safe and lock-free.
42pub const DirectAllocator = struct {42pub const DirectAllocator = struct {
43 allocator: Allocator,43 allocator: Allocator,
44 heap_handle: ?HeapHandle,44 heap_handle: ?HeapHandle,
...@@ -74,34 +74,34 @@ pub const DirectAllocator = struct {...@@ -74,34 +74,34 @@ pub const DirectAllocator = struct {
74 const alloc_size = if (alignment <= os.page_size) n else n + alignment;74 const alloc_size = if (alignment <= os.page_size) n else n + alignment;
75 const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0);75 const addr = p.mmap(null, alloc_size, p.PROT_READ | p.PROT_WRITE, p.MAP_PRIVATE | p.MAP_ANONYMOUS, -1, 0);
76 if (addr == p.MAP_FAILED) return error.OutOfMemory;76 if (addr == p.MAP_FAILED) return error.OutOfMemory;
77
78 if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n];77 if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n];
7978
80 var aligned_addr = addr & ~usize(alignment - 1);79 const aligned_addr = (addr & ~usize(alignment - 1)) + alignment;
81 aligned_addr += alignment;
8280
83 //We can unmap the unused portions of our mmap, but we must only81 // We can unmap the unused portions of our mmap, but we must only
84 // pass munmap bytes that exist outside our allocated pages or it82 // pass munmap bytes that exist outside our allocated pages or it
85 // will happily eat us too83 // will happily eat us too.
8684
87 //Since alignment > page_size, we are by definition on a page boundry85 // Since alignment > page_size, we are by definition on a page boundary.
88 const unused_start = addr;86 const unused_start = addr;
89 const unused_len = aligned_addr - 1 - unused_start;87 const unused_len = aligned_addr - 1 - unused_start;
9088
91 var err = p.munmap(unused_start, unused_len);89 const err = p.munmap(unused_start, unused_len);
92 debug.assert(p.getErrno(err) == 0);90 assert(p.getErrno(err) == 0);
9391
94 //It is impossible that there is an unoccupied page at the top of our92 // It is impossible that there is an unoccupied page at the top of our
95 // mmap.93 // mmap.
9694
97 return @intToPtr([*]u8, aligned_addr)[0..n];95 return @intToPtr([*]u8, aligned_addr)[0..n];
98 },96 },
99 Os.windows => {97 Os.windows => {
100 const amt = n + alignment + @sizeOf(usize);98 const amt = n + alignment + @sizeOf(usize);
101 const heap_handle = self.heap_handle orelse blk: {99 const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
102 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory;100 const heap_handle = optional_heap_handle orelse blk: {
103 self.heap_handle = hh;101 const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory;
104 break :blk hh;102 const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
103 _ = os.windows.HeapDestroy(hh);
104 break :blk other_hh.?; // can't be null because of the cmpxchg
105 };105 };
106 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;106 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
107 const root_addr = @ptrToInt(ptr);107 const root_addr = @ptrToInt(ptr);
...@@ -361,6 +361,73 @@ pub const ThreadSafeFixedBufferAllocator = struct {...@@ -361,6 +361,73 @@ pub const ThreadSafeFixedBufferAllocator = struct {
361 fn free(allocator: *Allocator, bytes: []u8) void {}361 fn free(allocator: *Allocator, bytes: []u8) void {}
362};362};
363363
364pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) StackFallbackAllocator(size) {
365 return StackFallbackAllocator(size){
366 .buffer = undefined,
367 .fallback_allocator = fallback_allocator,
368 .fixed_buffer_allocator = undefined,
369 .allocator = Allocator{
370 .allocFn = StackFallbackAllocator(size).alloc,
371 .reallocFn = StackFallbackAllocator(size).realloc,
372 .freeFn = StackFallbackAllocator(size).free,
373 },
374 };
375}
376
377pub fn StackFallbackAllocator(comptime size: usize) type {
378 return struct {
379 const Self = this;
380
381 buffer: [size]u8,
382 allocator: Allocator,
383 fallback_allocator: *Allocator,
384 fixed_buffer_allocator: FixedBufferAllocator,
385
386 pub fn get(self: *Self) *Allocator {
387 self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]);
388 return &self.allocator;
389 }
390
391 fn alloc(allocator: *Allocator, n: usize, alignment: u29) ![]u8 {
392 const self = @fieldParentPtr(Self, "allocator", allocator);
393 return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, n, alignment) catch
394 self.fallback_allocator.allocFn(self.fallback_allocator, n, alignment);
395 }
396
397 fn realloc(allocator: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
398 const self = @fieldParentPtr(Self, "allocator", allocator);
399 const in_buffer = @ptrToInt(old_mem.ptr) >= @ptrToInt(&self.buffer) and
400 @ptrToInt(old_mem.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
401 if (in_buffer) {
402 return FixedBufferAllocator.realloc(
403 &self.fixed_buffer_allocator.allocator,
404 old_mem,
405 new_size,
406 alignment,
407 ) catch {
408 const result = try self.fallback_allocator.allocFn(
409 self.fallback_allocator,
410 new_size,
411 alignment,
412 );
413 mem.copy(u8, result, old_mem);
414 return result;
415 };
416 }
417 return self.fallback_allocator.reallocFn(self.fallback_allocator, old_mem, new_size, alignment);
418 }
419
420 fn free(allocator: *Allocator, bytes: []u8) void {
421 const self = @fieldParentPtr(Self, "allocator", allocator);
422 const in_buffer = @ptrToInt(bytes.ptr) >= @ptrToInt(&self.buffer) and
423 @ptrToInt(bytes.ptr) < @ptrToInt(&self.buffer) + self.buffer.len;
424 if (!in_buffer) {
425 return self.fallback_allocator.freeFn(self.fallback_allocator, bytes);
426 }
427 }
428 };
429}
430
364test "c_allocator" {431test "c_allocator" {
365 if (builtin.link_libc) {432 if (builtin.link_libc) {
366 var slice = c_allocator.alloc(u8, 50) catch return;433 var slice = c_allocator.alloc(u8, 50) catch return;
std/mem.zig+1-1
...@@ -6,7 +6,7 @@ const builtin = @import("builtin");...@@ -6,7 +6,7 @@ const builtin = @import("builtin");
6const mem = this;6const mem = this;
77
8pub const Allocator = struct {8pub const Allocator = struct {
9 const Error = error{OutOfMemory};9 pub const Error = error{OutOfMemory};
1010
11 /// Allocate byte_count bytes and return them in a slice, with the11 /// Allocate byte_count bytes and return them in a slice, with the
12 /// slice's pointer aligned at least to alignment bytes.12 /// slice's pointer aligned at least to alignment bytes.
std/os/darwin.zig+259
...@@ -264,6 +264,224 @@ pub const SIGUSR1 = 30;...@@ -264,6 +264,224 @@ pub const SIGUSR1 = 30;
264/// user defined signal 2264/// user defined signal 2
265pub const SIGUSR2 = 31;265pub const SIGUSR2 = 31;
266266
267/// no flag value
268pub const KEVENT_FLAG_NONE = 0x000;
269
270/// immediate timeout
271pub const KEVENT_FLAG_IMMEDIATE = 0x001;
272
273/// output events only include change
274pub const KEVENT_FLAG_ERROR_EVENTS = 0x002;
275
276/// add event to kq (implies enable)
277pub const EV_ADD = 0x0001;
278
279/// delete event from kq
280pub const EV_DELETE = 0x0002;
281
282/// enable event
283pub const EV_ENABLE = 0x0004;
284
285/// disable event (not reported)
286pub const EV_DISABLE = 0x0008;
287
288/// only report one occurrence
289pub const EV_ONESHOT = 0x0010;
290
291/// clear event state after reporting
292pub const EV_CLEAR = 0x0020;
293
294/// force immediate event output
295/// ... with or without EV_ERROR
296/// ... use KEVENT_FLAG_ERROR_EVENTS
297/// on syscalls supporting flags
298pub const EV_RECEIPT = 0x0040;
299
300/// disable event after reporting
301pub const EV_DISPATCH = 0x0080;
302
303/// unique kevent per udata value
304pub const EV_UDATA_SPECIFIC = 0x0100;
305
306/// ... in combination with EV_DELETE
307/// will defer delete until udata-specific
308/// event enabled. EINPROGRESS will be
309/// returned to indicate the deferral
310pub const EV_DISPATCH2 = EV_DISPATCH | EV_UDATA_SPECIFIC;
311
312/// report that source has vanished
313/// ... only valid with EV_DISPATCH2
314pub const EV_VANISHED = 0x0200;
315
316/// reserved by system
317pub const EV_SYSFLAGS = 0xF000;
318
319/// filter-specific flag
320pub const EV_FLAG0 = 0x1000;
321
322/// filter-specific flag
323pub const EV_FLAG1 = 0x2000;
324
325/// EOF detected
326pub const EV_EOF = 0x8000;
327
328/// error, data contains errno
329pub const EV_ERROR = 0x4000;
330
331pub const EV_POLL = EV_FLAG0;
332pub const EV_OOBAND = EV_FLAG1;
333
334pub const EVFILT_READ = -1;
335pub const EVFILT_WRITE = -2;
336
337/// attached to aio requests
338pub const EVFILT_AIO = -3;
339
340/// attached to vnodes
341pub const EVFILT_VNODE = -4;
342
343/// attached to struct proc
344pub const EVFILT_PROC = -5;
345
346/// attached to struct proc
347pub const EVFILT_SIGNAL = -6;
348
349/// timers
350pub const EVFILT_TIMER = -7;
351
352/// Mach portsets
353pub const EVFILT_MACHPORT = -8;
354
355/// Filesystem events
356pub const EVFILT_FS = -9;
357
358/// User events
359pub const EVFILT_USER = -10;
360
361/// Virtual memory events
362pub const EVFILT_VM = -12;
363
364/// Exception events
365pub const EVFILT_EXCEPT = -15;
366
367pub const EVFILT_SYSCOUNT = 17;
368
369/// On input, NOTE_TRIGGER causes the event to be triggered for output.
370pub const NOTE_TRIGGER = 0x01000000;
371
372/// ignore input fflags
373pub const NOTE_FFNOP = 0x00000000;
374
375/// and fflags
376pub const NOTE_FFAND = 0x40000000;
377
378/// or fflags
379pub const NOTE_FFOR = 0x80000000;
380
381/// copy fflags
382pub const NOTE_FFCOPY = 0xc0000000;
383
384/// mask for operations
385pub const NOTE_FFCTRLMASK = 0xc0000000;
386pub const NOTE_FFLAGSMASK = 0x00ffffff;
387
388/// low water mark
389pub const NOTE_LOWAT = 0x00000001;
390
391/// OOB data
392pub const NOTE_OOB = 0x00000002;
393
394/// vnode was removed
395pub const NOTE_DELETE = 0x00000001;
396
397/// data contents changed
398pub const NOTE_WRITE = 0x00000002;
399
400/// size increased
401pub const NOTE_EXTEND = 0x00000004;
402
403/// attributes changed
404pub const NOTE_ATTRIB = 0x00000008;
405
406/// link count changed
407pub const NOTE_LINK = 0x00000010;
408
409/// vnode was renamed
410pub const NOTE_RENAME = 0x00000020;
411
412/// vnode access was revoked
413pub const NOTE_REVOKE = 0x00000040;
414
415/// No specific vnode event: to test for EVFILT_READ activation
416pub const NOTE_NONE = 0x00000080;
417
418/// vnode was unlocked by flock(2)
419pub const NOTE_FUNLOCK = 0x00000100;
420
421/// process exited
422pub const NOTE_EXIT = 0x80000000;
423
424/// process forked
425pub const NOTE_FORK = 0x40000000;
426
427/// process exec'd
428pub const NOTE_EXEC = 0x20000000;
429
430/// shared with EVFILT_SIGNAL
431pub const NOTE_SIGNAL = 0x08000000;
432
433/// exit status to be returned, valid for child process only
434pub const NOTE_EXITSTATUS = 0x04000000;
435
436/// provide details on reasons for exit
437pub const NOTE_EXIT_DETAIL = 0x02000000;
438
439/// mask for signal & exit status
440pub const NOTE_PDATAMASK = 0x000fffff;
441pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK);
442
443pub const NOTE_EXIT_DETAIL_MASK = 0x00070000;
444pub const NOTE_EXIT_DECRYPTFAIL = 0x00010000;
445pub const NOTE_EXIT_MEMORY = 0x00020000;
446pub const NOTE_EXIT_CSERROR = 0x00040000;
447
448/// will react on memory pressure
449pub const NOTE_VM_PRESSURE = 0x80000000;
450
451/// will quit on memory pressure, possibly after cleaning up dirty state
452pub const NOTE_VM_PRESSURE_TERMINATE = 0x40000000;
453
454/// will quit immediately on memory pressure
455pub const NOTE_VM_PRESSURE_SUDDEN_TERMINATE = 0x20000000;
456
457/// there was an error
458pub const NOTE_VM_ERROR = 0x10000000;
459
460/// data is seconds
461pub const NOTE_SECONDS = 0x00000001;
462
463/// data is microseconds
464pub const NOTE_USECONDS = 0x00000002;
465
466/// data is nanoseconds
467pub const NOTE_NSECONDS = 0x00000004;
468
469/// absolute timeout
470pub const NOTE_ABSOLUTE = 0x00000008;
471
472/// ext[1] holds leeway for power aware timers
473pub const NOTE_LEEWAY = 0x00000010;
474
475/// system does minimal timer coalescing
476pub const NOTE_CRITICAL = 0x00000020;
477
478/// system does maximum timer coalescing
479pub const NOTE_BACKGROUND = 0x00000040;
480pub const NOTE_MACH_CONTINUOUS_TIME = 0x00000080;
481
482/// data is mach absolute time units
483pub const NOTE_MACHTIME = 0x00000100;
484
267fn wstatus(x: i32) i32 {485fn wstatus(x: i32) i32 {
268 return x & 0o177;486 return x & 0o177;
269}487}
...@@ -385,6 +603,31 @@ pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usi...@@ -385,6 +603,31 @@ pub fn getdirentries64(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usi
385 return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));603 return errnoWrap(@bitCast(isize, c.__getdirentries64(fd, buf_ptr, buf_len, basep)));
386}604}
387605
606pub fn kqueue() usize {
607 return errnoWrap(c.kqueue());
608}
609
610pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize {
611 return errnoWrap(c.kevent(
612 kq,
613 changelist.ptr,
614 @intCast(c_int, changelist.len),
615 eventlist.ptr,
616 @intCast(c_int, eventlist.len),
617 timeout,
618 ));
619}
620
621pub fn kevent64(
622 kq: i32,
623 changelist: []const kevent64_s,
624 eventlist: []kevent64_s,
625 flags: u32,
626 timeout: ?*const timespec,
627) usize {
628 return errnoWrap(c.kevent64(kq, changelist.ptr, changelist.len, eventlist.ptr, eventlist.len, flags, timeout));
629}
630
388pub fn mkdir(path: [*]const u8, mode: u32) usize {631pub fn mkdir(path: [*]const u8, mode: u32) usize {
389 return errnoWrap(c.mkdir(path, mode));632 return errnoWrap(c.mkdir(path, mode));
390}633}
...@@ -393,6 +636,18 @@ pub fn symlink(existing: [*]const u8, new: [*]const u8) usize {...@@ -393,6 +636,18 @@ pub fn symlink(existing: [*]const u8, new: [*]const u8) usize {
393 return errnoWrap(c.symlink(existing, new));636 return errnoWrap(c.symlink(existing, new));
394}637}
395638
639pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize {
640 return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen));
641}
642
643pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize {
644 return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen));
645}
646
647pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize {
648 return errnoWrap(c.sysctlnametomib(name, wibp, sizep));
649}
650
396pub fn rename(old: [*]const u8, new: [*]const u8) usize {651pub fn rename(old: [*]const u8, new: [*]const u8) usize {
397 return errnoWrap(c.rename(old, new));652 return errnoWrap(c.rename(old, new));
398}653}
...@@ -474,6 +729,10 @@ pub const dirent = c.dirent;...@@ -474,6 +729,10 @@ pub const dirent = c.dirent;
474pub const sa_family_t = c.sa_family_t;729pub const sa_family_t = c.sa_family_t;
475pub const sockaddr = c.sockaddr;730pub const sockaddr = c.sockaddr;
476731
732/// Renamed from `kevent` to `Kevent` to avoid conflict with the syscall.
733pub const Kevent = c.Kevent;
734pub const kevent64_s = c.kevent64_s;
735
477/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.736/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
478pub const Sigaction = struct {737pub const Sigaction = struct {
479 handler: extern fn (i32) void,738 handler: extern fn (i32) void,
std/os/index.zig+191-12
...@@ -61,6 +61,15 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;...@@ -61,6 +61,15 @@ pub const windowsLoadDll = windows_util.windowsLoadDll;
61pub const windowsUnloadDll = windows_util.windowsUnloadDll;61pub const windowsUnloadDll = windows_util.windowsUnloadDll;
62pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;62pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
6363
64pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError;
65pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort;
66
67pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError;
68pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus;
69
70pub const WindowsWaitResult = windows_util.WindowsWaitResult;
71pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus;
72
64pub const WindowsWaitError = windows_util.WaitError;73pub const WindowsWaitError = windows_util.WaitError;
65pub const WindowsOpenError = windows_util.OpenError;74pub const WindowsOpenError = windows_util.OpenError;
66pub const WindowsWriteError = windows_util.WriteError;75pub const WindowsWriteError = windows_util.WriteError;
...@@ -544,8 +553,13 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {...@@ -544,8 +553,13 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
544 return null;553 return null;
545}554}
546555
556pub const GetEnvVarOwnedError = error{
557 OutOfMemory,
558 EnvironmentVariableNotFound,
559};
560
547/// Caller must free returned memory.561/// Caller must free returned memory.
548pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {562pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 {
549 if (is_windows) {563 if (is_windows) {
550 const key_with_null = try cstr.addNullByte(allocator, key);564 const key_with_null = try cstr.addNullByte(allocator, key);
551 defer allocator.free(key_with_null);565 defer allocator.free(key_with_null);
...@@ -554,14 +568,17 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {...@@ -554,14 +568,17 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {
554 errdefer allocator.free(buf);568 errdefer allocator.free(buf);
555569
556 while (true) {570 while (true) {
557 const windows_buf_len = try math.cast(windows.DWORD, buf.len);571 const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory;
558 const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len);572 const result = windows.GetEnvironmentVariableA(key_with_null.ptr, buf.ptr, windows_buf_len);
559573
560 if (result == 0) {574 if (result == 0) {
561 const err = windows.GetLastError();575 const err = windows.GetLastError();
562 return switch (err) {576 return switch (err) {
563 windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,577 windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound,
564 else => unexpectedErrorWindows(err),578 else => {
579 _ = unexpectedErrorWindows(err);
580 return error.EnvironmentVariableNotFound;
581 },
565 };582 };
566 }583 }
567584
...@@ -2309,6 +2326,30 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz...@@ -2309,6 +2326,30 @@ pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usiz
2309 }2326 }
2310}2327}
23112328
2329pub const LinuxEventFdError = error{
2330 InvalidFlagValue,
2331 SystemResources,
2332 ProcessFdQuotaExceeded,
2333 SystemFdQuotaExceeded,
2334
2335 Unexpected,
2336};
2337
2338pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 {
2339 const rc = posix.eventfd(initval, flags);
2340 const err = posix.getErrno(rc);
2341 switch (err) {
2342 0 => return @intCast(i32, rc),
2343 else => return unexpectedErrorPosix(err),
2344
2345 posix.EINVAL => return LinuxEventFdError.InvalidFlagValue,
2346 posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded,
2347 posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded,
2348 posix.ENODEV => return LinuxEventFdError.SystemResources,
2349 posix.ENOMEM => return LinuxEventFdError.SystemResources,
2350 }
2351}
2352
2312pub const PosixGetSockNameError = error{2353pub const PosixGetSockNameError = error{
2313 /// Insufficient resources were available in the system to perform the operation.2354 /// Insufficient resources were available in the system to perform the operation.
2314 SystemResources,2355 SystemResources,
...@@ -2568,11 +2609,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -2568,11 +2609,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
2568 thread: Thread,2609 thread: Thread,
2569 inner: Context,2610 inner: Context,
2570 };2611 };
2571 extern fn threadMain(arg: windows.LPVOID) windows.DWORD {2612 extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD {
2572 if (@sizeOf(Context) == 0) {2613 const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
2573 return startFn({});2614 switch (@typeId(@typeOf(startFn).ReturnType)) {
2574 } else {2615 builtin.TypeId.Int => {
2575 return startFn(@ptrCast(*Context, @alignCast(@alignOf(Context), arg)).*);2616 return startFn(arg);
2617 },
2618 builtin.TypeId.Void => {
2619 startFn(arg);
2620 return 0;
2621 },
2622 else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
2576 }2623 }
2577 }2624 }
2578 };2625 };
...@@ -2605,10 +2652,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -2605,10 +2652,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
26052652
2606 const MainFuncs = struct {2653 const MainFuncs = struct {
2607 extern fn linuxThreadMain(ctx_addr: usize) u8 {2654 extern fn linuxThreadMain(ctx_addr: usize) u8 {
2608 if (@sizeOf(Context) == 0) {2655 const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
2609 return startFn({});2656
2610 } else {2657 switch (@typeId(@typeOf(startFn).ReturnType)) {
2611 return startFn(@intToPtr(*const Context, ctx_addr).*);2658 builtin.TypeId.Int => {
2659 return startFn(arg);
2660 },
2661 builtin.TypeId.Void => {
2662 startFn(arg);
2663 return 0;
2664 },
2665 else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
2612 }2666 }
2613 }2667 }
2614 extern fn posixThreadMain(ctx: ?*c_void) ?*c_void {2668 extern fn posixThreadMain(ctx: ?*c_void) ?*c_void {
...@@ -2717,3 +2771,128 @@ pub fn posixFStat(fd: i32) !posix.Stat {...@@ -2717,3 +2771,128 @@ pub fn posixFStat(fd: i32) !posix.Stat {
27172771
2718 return stat;2772 return stat;
2719}2773}
2774
2775pub const CpuCountError = error{
2776 OutOfMemory,
2777 PermissionDenied,
2778 Unexpected,
2779};
2780
2781pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
2782 switch (builtin.os) {
2783 builtin.Os.macosx => {
2784 var count: c_int = undefined;
2785 var count_len: usize = @sizeOf(c_int);
2786 const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0);
2787 const err = posix.getErrno(rc);
2788 switch (err) {
2789 0 => return @intCast(usize, count),
2790 posix.EFAULT => unreachable,
2791 posix.EINVAL => unreachable,
2792 posix.ENOMEM => return CpuCountError.OutOfMemory,
2793 posix.ENOTDIR => unreachable,
2794 posix.EISDIR => unreachable,
2795 posix.ENOENT => unreachable,
2796 posix.EPERM => unreachable,
2797 else => return os.unexpectedErrorPosix(err),
2798 }
2799 },
2800 builtin.Os.linux => {
2801 const usize_count = 16;
2802 const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get();
2803
2804 var set = try allocator.alloc(usize, usize_count);
2805 defer allocator.free(set);
2806
2807 while (true) {
2808 const rc = posix.sched_getaffinity(0, set);
2809 const err = posix.getErrno(rc);
2810 switch (err) {
2811 0 => {
2812 if (rc < set.len * @sizeOf(usize)) {
2813 const result = set[0 .. rc / @sizeOf(usize)];
2814 var sum: usize = 0;
2815 for (result) |x| {
2816 sum += @popCount(x);
2817 }
2818 return sum;
2819 } else {
2820 set = try allocator.realloc(usize, set, set.len * 2);
2821 continue;
2822 }
2823 },
2824 posix.EFAULT => unreachable,
2825 posix.EINVAL => unreachable,
2826 posix.EPERM => return CpuCountError.PermissionDenied,
2827 posix.ESRCH => unreachable,
2828 else => return os.unexpectedErrorPosix(err),
2829 }
2830 }
2831 },
2832 builtin.Os.windows => {
2833 var system_info: windows.SYSTEM_INFO = undefined;
2834 windows.GetSystemInfo(&system_info);
2835 return @intCast(usize, system_info.dwNumberOfProcessors);
2836 },
2837 else => @compileError("unsupported OS"),
2838 }
2839}
2840
2841pub const BsdKQueueError = error{
2842 /// The per-process limit on the number of open file descriptors has been reached.
2843 ProcessFdQuotaExceeded,
2844
2845 /// The system-wide limit on the total number of open files has been reached.
2846 SystemFdQuotaExceeded,
2847
2848 Unexpected,
2849};
2850
2851pub fn bsdKQueue() BsdKQueueError!i32 {
2852 const rc = posix.kqueue();
2853 const err = posix.getErrno(rc);
2854 switch (err) {
2855 0 => return @intCast(i32, rc),
2856 posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded,
2857 posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded,
2858 else => return unexpectedErrorPosix(err),
2859 }
2860}
2861
2862pub const BsdKEventError = error{
2863 /// The process does not have permission to register a filter.
2864 AccessDenied,
2865
2866 /// The event could not be found to be modified or deleted.
2867 EventNotFound,
2868
2869 /// No memory was available to register the event.
2870 SystemResources,
2871
2872 /// The specified process to attach to does not exist.
2873 ProcessNotFound,
2874};
2875
2876pub fn bsdKEvent(
2877 kq: i32,
2878 changelist: []const posix.Kevent,
2879 eventlist: []posix.Kevent,
2880 timeout: ?*const posix.timespec,
2881) BsdKEventError!usize {
2882 while (true) {
2883 const rc = posix.kevent(kq, changelist, eventlist, timeout);
2884 const err = posix.getErrno(rc);
2885 switch (err) {
2886 0 => return rc,
2887 posix.EACCES => return BsdKEventError.AccessDenied,
2888 posix.EFAULT => unreachable,
2889 posix.EBADF => unreachable,
2890 posix.EINTR => continue,
2891 posix.EINVAL => unreachable,
2892 posix.ENOENT => return BsdKEventError.EventNotFound,
2893 posix.ENOMEM => return BsdKEventError.SystemResources,
2894 posix.ESRCH => return BsdKEventError.ProcessNotFound,
2895 else => unreachable,
2896 }
2897 }
2898}
std/os/linux/index.zig+12
...@@ -523,6 +523,10 @@ pub const CLONE_NEWPID = 0x20000000;...@@ -523,6 +523,10 @@ pub const CLONE_NEWPID = 0x20000000;
523pub const CLONE_NEWNET = 0x40000000;523pub const CLONE_NEWNET = 0x40000000;
524pub const CLONE_IO = 0x80000000;524pub const CLONE_IO = 0x80000000;
525525
526pub const EFD_SEMAPHORE = 1;
527pub const EFD_CLOEXEC = O_CLOEXEC;
528pub const EFD_NONBLOCK = O_NONBLOCK;
529
526pub const MS_RDONLY = 1;530pub const MS_RDONLY = 1;
527pub const MS_NOSUID = 2;531pub const MS_NOSUID = 2;
528pub const MS_NODEV = 4;532pub const MS_NODEV = 4;
...@@ -1193,6 +1197,10 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize {...@@ -1193,6 +1197,10 @@ pub fn fremovexattr(fd: usize, name: [*]const u8) usize {
1193 return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));1197 return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
1194}1198}
11951199
1200pub fn sched_getaffinity(pid: i32, set: []usize) usize {
1201 return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr));
1202}
1203
1196pub const epoll_data = packed union {1204pub const epoll_data = packed union {
1197 ptr: usize,1205 ptr: usize,
1198 fd: i32,1206 fd: i32,
...@@ -1221,6 +1229,10 @@ pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout...@@ -1221,6 +1229,10 @@ pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout
1221 return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout));1229 return syscall4(SYS_epoll_wait, @intCast(usize, epoll_fd), @ptrToInt(events), @intCast(usize, maxevents), @intCast(usize, timeout));
1222}1230}
12231231
1232pub fn eventfd(count: u32, flags: u32) usize {
1233 return syscall2(SYS_eventfd2, count, flags);
1234}
1235
1224pub fn timerfd_create(clockid: i32, flags: u32) usize {1236pub fn timerfd_create(clockid: i32, flags: u32) usize {
1225 return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags));1237 return syscall2(SYS_timerfd_create, @intCast(usize, clockid), @intCast(usize, flags));
1226}1238}
std/os/test.zig+5
...@@ -58,3 +58,8 @@ fn start2(ctx: *i32) u8 {...@@ -58,3 +58,8 @@ fn start2(ctx: *i32) u8 {
58 _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);58 _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
59 return 0;59 return 0;
60}60}
61
62test "cpu count" {
63 const cpu_count = try std.os.cpuCount(a);
64 assert(cpu_count >= 1);
65}
std/os/windows/index.zig+28
...@@ -59,6 +59,9 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(...@@ -59,6 +59,9 @@ pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
59 dwFlags: DWORD,59 dwFlags: DWORD,
60) BOOLEAN;60) BOOLEAN;
6161
62
63pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
64
62pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;65pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
6366
64pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;67pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
...@@ -106,7 +109,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(...@@ -106,7 +109,9 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
106) DWORD;109) DWORD;
107110
108pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;111pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
112pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
109113
114pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
110pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;115pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
111116
112pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;117pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
...@@ -129,6 +134,9 @@ pub extern "kernel32" stdcallcc fn MoveFileExA(...@@ -129,6 +134,9 @@ pub extern "kernel32" stdcallcc fn MoveFileExA(
129 dwFlags: DWORD,134 dwFlags: DWORD,
130) BOOL;135) BOOL;
131136
137
138pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
139
132pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;140pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
133141
134pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;142pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
...@@ -204,6 +212,7 @@ pub const SIZE_T = usize;...@@ -204,6 +212,7 @@ pub const SIZE_T = usize;
204pub const TCHAR = if (UNICODE) WCHAR else u8;212pub const TCHAR = if (UNICODE) WCHAR else u8;
205pub const UINT = c_uint;213pub const UINT = c_uint;
206pub const ULONG_PTR = usize;214pub const ULONG_PTR = usize;
215pub const DWORD_PTR = ULONG_PTR;
207pub const UNICODE = false;216pub const UNICODE = false;
208pub const WCHAR = u16;217pub const WCHAR = u16;
209pub const WORD = u16;218pub const WORD = u16;
...@@ -413,3 +422,22 @@ pub const FILETIME = extern struct {...@@ -413,3 +422,22 @@ pub const FILETIME = extern struct {
413 dwLowDateTime: DWORD,422 dwLowDateTime: DWORD,
414 dwHighDateTime: DWORD,423 dwHighDateTime: DWORD,
415};424};
425
426pub const SYSTEM_INFO = extern struct {
427 anon1: extern union {
428 dwOemId: DWORD,
429 anon2: extern struct {
430 wProcessorArchitecture: WORD,
431 wReserved: WORD,
432 },
433 },
434 dwPageSize: DWORD,
435 lpMinimumApplicationAddress: LPVOID,
436 lpMaximumApplicationAddress: LPVOID,
437 dwActiveProcessorMask: DWORD_PTR,
438 dwNumberOfProcessors: DWORD,
439 dwProcessorType: DWORD,
440 dwAllocationGranularity: DWORD,
441 wProcessorLevel: WORD,
442 wProcessorRevision: WORD,
443};
std/os/windows/util.zig+47
...@@ -214,3 +214,50 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3...@@ -214,3 +214,50 @@ pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN3
214 }214 }
215 return true;215 return true;
216}216}
217
218
219pub const WindowsCreateIoCompletionPortError = error {
220 Unexpected,
221};
222
223pub fn windowsCreateIoCompletionPort(file_handle: windows.HANDLE, existing_completion_port: ?windows.HANDLE, completion_key: usize, concurrent_thread_count: windows.DWORD) !windows.HANDLE {
224 const handle = windows.CreateIoCompletionPort(file_handle, existing_completion_port, completion_key, concurrent_thread_count) orelse {
225 const err = windows.GetLastError();
226 switch (err) {
227 else => return os.unexpectedErrorWindows(err),
228 }
229 };
230 return handle;
231}
232
233pub const WindowsPostQueuedCompletionStatusError = error {
234 Unexpected,
235};
236
237pub fn windowsPostQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: windows.DWORD, completion_key: usize, lpOverlapped: ?*windows.OVERLAPPED) WindowsPostQueuedCompletionStatusError!void {
238 if (windows.PostQueuedCompletionStatus(completion_port, bytes_transferred_count, completion_key, lpOverlapped) == 0) {
239 const err = windows.GetLastError();
240 switch (err) {
241 else => return os.unexpectedErrorWindows(err),
242 }
243 }
244}
245
246pub const WindowsWaitResult = error {
247 Normal,
248 Aborted,
249};
250
251pub fn windowsGetQueuedCompletionStatus(completion_port: windows.HANDLE, bytes_transferred_count: *windows.DWORD, lpCompletionKey: *usize, lpOverlapped: *?*windows.OVERLAPPED, dwMilliseconds: windows.DWORD) WindowsWaitResult {
252 if (windows.GetQueuedCompletionStatus(completion_port, bytes_transferred_count, lpCompletionKey, lpOverlapped, dwMilliseconds) == windows.FALSE) {
253 if (std.debug.runtime_safety) {
254 const err = windows.GetLastError();
255 if (err != windows.ERROR.ABANDONED_WAIT_0) {
256 std.debug.warn("err: {}\n", err);
257 }
258 assert(err == windows.ERROR.ABANDONED_WAIT_0);
259 }
260 return WindowsWaitResult.Aborted;
261 }
262 return WindowsWaitResult.Normal;
263}
std/special/build_runner.zig+6-3
...@@ -122,10 +122,13 @@ pub fn main() !void {...@@ -122,10 +122,13 @@ pub fn main() !void {
122 return usageAndErr(&builder, true, try stderr_stream);122 return usageAndErr(&builder, true, try stderr_stream);
123123
124 builder.make(targets.toSliceConst()) catch |err| {124 builder.make(targets.toSliceConst()) catch |err| {
125 if (err == error.InvalidStepName) {125 switch (err) {
126 return usageAndErr(&builder, true, try stderr_stream);126 error.InvalidStepName => {
127 return usageAndErr(&builder, true, try stderr_stream);
128 },
129 error.UncleanExit => os.exit(1),
130 else => return err,
127 }131 }
128 return err;
129 };132 };
130}133}
131134
std/special/compiler_rt/extendXfYf2_test.zig+20-20
...@@ -31,7 +31,7 @@ fn test__extendhfsf2(a: u16, expected: u32) void {...@@ -31,7 +31,7 @@ fn test__extendhfsf2(a: u16, expected: u32) void {
3131
32 if (rep == expected) {32 if (rep == expected) {
33 if (rep & 0x7fffffff > 0x7f800000) {33 if (rep & 0x7fffffff > 0x7f800000) {
34 return; // NaN is always unequal.34 return; // NaN is always unequal.
35 }35 }
36 if (x == @bitCast(f32, expected)) {36 if (x == @bitCast(f32, expected)) {
37 return;37 return;
...@@ -86,33 +86,33 @@ test "extenddftf2" {...@@ -86,33 +86,33 @@ test "extenddftf2" {
86}86}
8787
88test "extendhfsf2" {88test "extendhfsf2" {
89 test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN89 test__extendhfsf2(0x7e00, 0x7fc00000); // qNaN
90 test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN90 test__extendhfsf2(0x7f00, 0x7fe00000); // sNaN
91 test__extendhfsf2(0x7c01, 0x7f802000); // sNaN91 test__extendhfsf2(0x7c01, 0x7f802000); // sNaN
9292
93 test__extendhfsf2(0, 0); // 093 test__extendhfsf2(0, 0); // 0
94 test__extendhfsf2(0x8000, 0x80000000); // -094 test__extendhfsf2(0x8000, 0x80000000); // -0
9595
96 test__extendhfsf2(0x7c00, 0x7f800000); // inf96 test__extendhfsf2(0x7c00, 0x7f800000); // inf
97 test__extendhfsf2(0xfc00, 0xff800000); // -inf97 test__extendhfsf2(0xfc00, 0xff800000); // -inf
9898
99 test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-2499 test__extendhfsf2(0x0001, 0x33800000); // denormal (min), 2**-24
100 test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24100 test__extendhfsf2(0x8001, 0xb3800000); // denormal (min), -2**-24
101101
102 test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24102 test__extendhfsf2(0x03ff, 0x387fc000); // denormal (max), 2**-14 - 2**-24
103 test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24103 test__extendhfsf2(0x83ff, 0xb87fc000); // denormal (max), -2**-14 + 2**-24
104104
105 test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14105 test__extendhfsf2(0x0400, 0x38800000); // normal (min), 2**-14
106 test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14106 test__extendhfsf2(0x8400, 0xb8800000); // normal (min), -2**-14
107107
108 test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504108 test__extendhfsf2(0x7bff, 0x477fe000); // normal (max), 65504
109 test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504109 test__extendhfsf2(0xfbff, 0xc77fe000); // normal (max), -65504
110110
111 test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10111 test__extendhfsf2(0x3c01, 0x3f802000); // normal, 1 + 2**-10
112 test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10112 test__extendhfsf2(0xbc01, 0xbf802000); // normal, -1 - 2**-10
113113
114 test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3114 test__extendhfsf2(0x3555, 0x3eaaa000); // normal, approx. 1/3
115 test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3115 test__extendhfsf2(0xb555, 0xbeaaa000); // normal, approx. -1/3
116}116}
117117
118test "extendsftf2" {118test "extendsftf2" {
std/zig/bench.zig+6-8
...@@ -19,20 +19,18 @@ pub fn main() !void {...@@ -19,20 +19,18 @@ pub fn main() !void {
19 }19 }
20 const end = timer.read();20 const end = timer.read();
21 memory_used /= iterations;21 memory_used /= iterations;
22 const elapsed_s = f64(end - start) / std.os.time.ns_per_s;22 const elapsed_s = @intToFloat(f64, end - start) / std.os.time.ns_per_s;
23 const bytes_per_sec = f64(source.len * iterations) / elapsed_s;23 const bytes_per_sec = @intToFloat(f64, source.len * iterations) / elapsed_s;
24 const mb_per_sec = bytes_per_sec / (1024 * 1024);24 const mb_per_sec = bytes_per_sec / (1024 * 1024);
2525
26 var stdout_file = try std.io.getStdOut();26 var stdout_file = try std.io.getStdOut();
27 const stdout = *std.io.FileOutStream.init(*stdout_file).stream;27 const stdout = &std.io.FileOutStream.init(&stdout_file).stream;
28 try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024);28 try stdout.print("{.3} MiB/s, {} KiB used \n", mb_per_sec, memory_used / 1024);
29}29}
3030
31fn testOnce() usize {31fn testOnce() usize {
32 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);32 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
33 var allocator = *fixed_buf_alloc.allocator;33 var allocator = &fixed_buf_alloc.allocator;
34 var tokenizer = Tokenizer.init(source);34 _ = std.zig.parse(allocator, source) catch @panic("parse failure");
35 var parser = Parser.init(*tokenizer, allocator, "(memory buffer)");
36 _ = parser.parse() catch @panic("parse failure");
37 return fixed_buf_alloc.end_index;35 return fixed_buf_alloc.end_index;
38}36}
test/behavior.zig+4-3
...@@ -8,17 +8,17 @@ comptime {...@@ -8,17 +8,17 @@ comptime {
8 _ = @import("cases/atomics.zig");8 _ = @import("cases/atomics.zig");
9 _ = @import("cases/bitcast.zig");9 _ = @import("cases/bitcast.zig");
10 _ = @import("cases/bool.zig");10 _ = @import("cases/bool.zig");
11 _ = @import("cases/bugs/1111.zig");
11 _ = @import("cases/bugs/394.zig");12 _ = @import("cases/bugs/394.zig");
12 _ = @import("cases/bugs/655.zig");13 _ = @import("cases/bugs/655.zig");
13 _ = @import("cases/bugs/656.zig");14 _ = @import("cases/bugs/656.zig");
14 _ = @import("cases/bugs/828.zig");15 _ = @import("cases/bugs/828.zig");
15 _ = @import("cases/bugs/920.zig");16 _ = @import("cases/bugs/920.zig");
16 _ = @import("cases/bugs/1111.zig");
17 _ = @import("cases/byval_arg_var.zig");17 _ = @import("cases/byval_arg_var.zig");
18 _ = @import("cases/cast.zig");18 _ = @import("cases/cast.zig");
19 _ = @import("cases/const_slice_child.zig");19 _ = @import("cases/const_slice_child.zig");
20 _ = @import("cases/coroutines.zig");
21 _ = @import("cases/coroutine_await_struct.zig");20 _ = @import("cases/coroutine_await_struct.zig");
21 _ = @import("cases/coroutines.zig");
22 _ = @import("cases/defer.zig");22 _ = @import("cases/defer.zig");
23 _ = @import("cases/enum.zig");23 _ = @import("cases/enum.zig");
24 _ = @import("cases/enum_with_members.zig");24 _ = @import("cases/enum_with_members.zig");
...@@ -36,11 +36,12 @@ comptime {...@@ -36,11 +36,12 @@ comptime {
36 _ = @import("cases/math.zig");36 _ = @import("cases/math.zig");
37 _ = @import("cases/merge_error_sets.zig");37 _ = @import("cases/merge_error_sets.zig");
38 _ = @import("cases/misc.zig");38 _ = @import("cases/misc.zig");
39 _ = @import("cases/optional.zig");
40 _ = @import("cases/namespace_depends_on_compile_var/index.zig");39 _ = @import("cases/namespace_depends_on_compile_var/index.zig");
41 _ = @import("cases/new_stack_call.zig");40 _ = @import("cases/new_stack_call.zig");
42 _ = @import("cases/null.zig");41 _ = @import("cases/null.zig");
42 _ = @import("cases/optional.zig");
43 _ = @import("cases/pointers.zig");43 _ = @import("cases/pointers.zig");
44 _ = @import("cases/popcount.zig");
44 _ = @import("cases/pub_enum/index.zig");45 _ = @import("cases/pub_enum/index.zig");
45 _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig");46 _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig");
46 _ = @import("cases/reflection.zig");47 _ = @import("cases/reflection.zig");
test/cases/popcount.zig created+24
...@@ -0,0 +1,24 @@
1const assert = @import("std").debug.assert;
2
3test "@popCount" {
4 comptime testPopCount();
5 testPopCount();
6}
7
8fn testPopCount() void {
9 {
10 var x: u32 = 0xaa;
11 assert(@popCount(x) == 4);
12 }
13 {
14 var x: u32 = 0xaaaaaaaa;
15 assert(@popCount(x) == 16);
16 }
17 {
18 var x: i16 = -1;
19 assert(@popCount(x) == 16);
20 }
21 comptime {
22 assert(@popCount(0b11111111000110001100010000100001000011000011100101010001) == 24);
23 }
24}
test/cases/void.zig+12
...@@ -16,3 +16,15 @@ test "compare void with void compile time known" {...@@ -16,3 +16,15 @@ test "compare void with void compile time known" {
16 assert(foo.a == {});16 assert(foo.a == {});
17 }17 }
18}18}
19
20test "iterate over a void slice" {
21 var j: usize = 0;
22 for (times(10)) |_, i| {
23 assert(i == j);
24 j += 1;
25 }
26}
27
28fn times(n: usize) []const void {
29 return ([*]void)(undefined)[0..n];
30}
test/compile_errors.zig+98
...@@ -1,6 +1,90 @@...@@ -1,6 +1,90 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "use of comptime-known undefined function value",
6 \\const Cmd = struct {
7 \\ exec: fn () void,
8 \\};
9 \\export fn entry() void {
10 \\ const command = Cmd{ .exec = undefined };
11 \\ command.exec();
12 \\}
13 ,
14 ".tmp_source.zig:6:12: error: use of undefined value",
15 );
16
17 cases.add(
18 "bad @alignCast at comptime",
19 \\comptime {
20 \\ const ptr = @intToPtr(*i32, 0x1);
21 \\ const aligned = @alignCast(4, ptr);
22 \\}
23 ,
24 ".tmp_source.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes",
25 );
26
27 cases.add(
28 "@ptrToInt on *void",
29 \\export fn entry() bool {
30 \\ return @ptrToInt(&{}) == @ptrToInt(&{});
31 \\}
32 ,
33 ".tmp_source.zig:2:23: error: pointer to size 0 type has no address",
34 );
35
36 cases.add(
37 "@popCount - non-integer",
38 \\export fn entry(x: f32) u32 {
39 \\ return @popCount(x);
40 \\}
41 ,
42 ".tmp_source.zig:2:22: error: expected integer type, found 'f32'",
43 );
44
45 cases.add(
46 "@popCount - negative comptime_int",
47 \\comptime {
48 \\ _ = @popCount(-1);
49 \\}
50 ,
51 ".tmp_source.zig:2:9: error: @popCount on negative comptime_int value -1",
52 );
53
54 cases.addCase(x: {
55 const tc = cases.create(
56 "wrong same named struct",
57 \\const a = @import("a.zig");
58 \\const b = @import("b.zig");
59 \\
60 \\export fn entry() void {
61 \\ var a1: a.Foo = undefined;
62 \\ bar(&a1);
63 \\}
64 \\
65 \\fn bar(x: *b.Foo) void {}
66 ,
67 ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'",
68 ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'",
69 "a.zig:1:17: note: Foo declared here",
70 "b.zig:1:17: note: Foo declared here",
71 );
72
73 tc.addSourceFile("a.zig",
74 \\pub const Foo = struct {
75 \\ x: i32,
76 \\};
77 );
78
79 tc.addSourceFile("b.zig",
80 \\pub const Foo = struct {
81 \\ z: f64,
82 \\};
83 );
84
85 break :x tc;
86 });
87
4 cases.add(88 cases.add(
5 "enum field value references enum",89 "enum field value references enum",
6 \\pub const Foo = extern enum {90 \\pub const Foo = extern enum {
...@@ -358,6 +442,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -358,6 +442,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
358 ".tmp_source.zig:3:14: note: other value is here",442 ".tmp_source.zig:3:14: note: other value is here",
359 );443 );
360444
445 cases.add("invalid cast from integral type to enum",
446 \\const E = enum(usize) { One, Two };
447 \\
448 \\export fn entry() void {
449 \\ foo(1);
450 \\}
451 \\
452 \\fn foo(x: usize) void {
453 \\ switch (x) {
454 \\ E.One => {},
455 \\ }
456 \\}
457 , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'");
458
361 cases.add(459 cases.add(
362 "range operator in switch used on error set",460 "range operator in switch used on error set",
363 \\export fn entry() void {461 \\export fn entry() void {