authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-31 14:36:27-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-31 14:36:27-04:00
logf804310d9f953c9d78a4271ba8d75133341840e6
tree71a9db15bcabbec32d6f2c795927603f9b278ceb
parentdd9728c5a03844267bc378c326c353fd2b0e084e
parent058bfb254c4c0e1cfb254791f771c88c74f299e8

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


29 files changed, 1846 insertions(+), 395 deletions(-)

README.md-38
...@@ -74,44 +74,6 @@ that counts as "freestanding" for the purposes of this table....@@ -74,44 +74,6 @@ that counts as "freestanding" for the purposes of this table.
74 * Reddit: [/r/zig](https://www.reddit.com/r/zig)74 * Reddit: [/r/zig](https://www.reddit.com/r/zig)
75 * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)75 * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang)
7676
77### Wanted: Windows Developers
78
79Flesh out the standard library for Windows, streamline Zig installation and
80distribution for Windows. Work with LLVM and LLD teams to improve
81PDB/CodeView/MSVC debugging. Implement stack traces for Windows in the MinGW
82environment and the MSVC environment.
83
84### Wanted: MacOS and iOS Developers
85
86Flesh out the standard library for MacOS. Improve the MACH-O linker. Implement
87stack traces for MacOS. Streamline the process of using Zig to build for
88iOS.
89
90### Wanted: Android Developers
91
92Flesh out the standard library for Android. Streamline the process of using
93Zig to build for Android and for depending on Zig code on Android.
94
95### Wanted: Web Developers
96
97Figure out what are the use cases for compiling Zig to WebAssembly. Create demo
98projects with it and streamline experience for users trying to output
99WebAssembly. Work on the documentation generator outputting useful searchable html
100documentation. Create Zig modules for common web tasks such as WebSockets and gzip.
101
102### Wanted: Embedded Developers
103
104Flesh out the standard library for uncommon CPU architectures and OS targets.
105Drive issue discussion for cross compiling and using Zig in constrained
106or unusual environments.
107
108### Wanted: Game Developers
109
110Create cross platform Zig modules to compete with SDL and GLFW. Create an
111OpenGL library that does not depend on libc. Drive the usability of Zig
112for video games. Create a general purpose allocator that does not depend on
113libc. Create demo games using Zig.
114
115## Building77## Building
11678
117[![Build Status](https://travis-ci.org/ziglang/zig.svg?branch=master)](https://travis-ci.org/ziglang/zig)79[![Build Status](https://travis-ci.org/ziglang/zig.svg?branch=master)](https://travis-ci.org/ziglang/zig)
build.zig+4
...@@ -45,6 +45,7 @@ pub fn build(b: *Builder) !void {...@@ -45,6 +45,7 @@ pub fn build(b: *Builder) !void {
45 .c_header_files = nextValue(&index, build_info),45 .c_header_files = nextValue(&index, build_info),
46 .dia_guids_lib = nextValue(&index, build_info),46 .dia_guids_lib = nextValue(&index, build_info),
47 .llvm = undefined,47 .llvm = undefined,
48 .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false,
48 };49 };
49 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);50 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
5051
...@@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {...@@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
228 // TODO turn this into -Dextra-lib-path=/lib option229 // TODO turn this into -Dextra-lib-path=/lib option
229 exe.addLibPath("/lib");230 exe.addLibPath("/lib");
230231
232 exe.setNoRoSegment(ctx.no_rosegment);
233
231 exe.addIncludeDir("src");234 exe.addIncludeDir("src");
232 exe.addIncludeDir(ctx.cmake_binary_dir);235 exe.addIncludeDir(ctx.cmake_binary_dir);
233 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");236 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
...@@ -286,4 +289,5 @@ const Context = struct {...@@ -286,4 +289,5 @@ const Context = struct {
286 c_header_files: []const u8,289 c_header_files: []const u8,
287 dia_guids_lib: []const u8,290 dia_guids_lib: []const u8,
288 llvm: LibraryDep,291 llvm: LibraryDep,
292 no_rosegment: bool,
289};293};
deps/lld/COFF/Driver.cpp+3
...@@ -72,6 +72,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {...@@ -72,6 +72,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
72 exitLld(errorCount() ? 1 : 0);72 exitLld(errorCount() ? 1 : 0);
7373
74 freeArena();74 freeArena();
75 ObjFile::Instances.clear();
76 ImportFile::Instances.clear();
77 BitcodeFile::Instances.clear();
75 return !errorCount();78 return !errorCount();
76}79}
7780
doc/langref.html.in+59-7
...@@ -134,6 +134,58 @@ pub fn main() void {...@@ -134,6 +134,58 @@ pub fn main() void {
134 </p>134 </p>
135 {#see_also|Values|@import|Errors|Root Source File#}135 {#see_also|Values|@import|Errors|Root Source File#}
136 {#header_close#}136 {#header_close#}
137 {#header_open|Comments#}
138 {#code_begin|test|comments#}
139const assert = @import("std").debug.assert;
140
141test "comments" {
142 // Comments in Zig start with "//" and end at the next LF byte (end of line).
143 // The below line is a comment, and won't be executed.
144
145 //assert(false);
146
147 const x = true; // another comment
148 assert(x);
149}
150 {#code_end#}
151 <p>
152 There are no multiline comments in Zig (e.g. like <code>/* */</code>
153 comments in C). This helps allow Zig to have the property that each line
154 of code can be tokenized out of context.
155 </p>
156 {#header_open|Doc comments#}
157 <p>
158 A doc comment is one that begins with exactly three slashes (i.e.
159 <code class="zig">///</code> but not <code class="zig">////</code>);
160 multiple doc comments in a row are merged together to form a multiline
161 doc comment. The doc comment documents whatever immediately follows it.
162 </p>
163 {#code_begin|syntax|doc_comments#}
164/// A structure for storing a timestamp, with nanosecond precision (this is a
165/// multiline doc comment).
166const Timestamp = struct {
167 /// The number of seconds since the epoch (this is also a doc comment).
168 seconds: i64, // signed so we can represent pre-1970 (not a doc comment)
169 /// The number of nanoseconds past the second (doc comment again).
170 nanos: u32,
171
172 /// Returns a `Timestamp` struct representing the Unix epoch; that is, the
173 /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too).
174 pub fn unixEpoch() Timestamp {
175 return Timestamp{
176 .seconds = 0,
177 .nanos = 0,
178 };
179 }
180};
181 {#code_end#}
182 <p>
183 Doc comments are only allowed in certain places; eventually, it will
184 become a compile error have a doc comment in an unexpected place, such as
185 in the middle of an expression, or just before a non-doc comment.
186 </p>
187 {#header_close#}
188 {#header_close#}
137 {#header_open|Values#}189 {#header_open|Values#}
138 {#code_begin|exe|values#}190 {#code_begin|exe|values#}
139const std = @import("std");191const std = @import("std");
...@@ -4665,24 +4717,24 @@ async fn testSuspendBlock() void {...@@ -4665,24 +4717,24 @@ async fn testSuspendBlock() void {
4665 block, while the old thread continued executing the suspend block.4717 block, while the old thread continued executing the suspend block.
4666 </p>4718 </p>
4667 <p>4719 <p>
4668 However, if you use labeled <code>break</code> on the suspend block, the coroutine4720 However, the coroutine can be directly resumed from the suspend block, in which case it
4669 never returns to its resumer and continues executing.4721 never returns to its resumer and continues executing.
4670 </p>4722 </p>
4671 {#code_begin|test#}4723 {#code_begin|test#}
4672const std = @import("std");4724const std = @import("std");
4673const assert = std.debug.assert;4725const assert = std.debug.assert;
46744726
4675test "break from suspend" {4727test "resume from suspend" {
4676 var buf: [500]u8 = undefined;4728 var buf: [500]u8 = undefined;
4677 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;4729 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
4678 var my_result: i32 = 1;4730 var my_result: i32 = 1;
4679 const p = try async<a> testBreakFromSuspend(&my_result);4731 const p = try async<a> testResumeFromSuspend(&my_result);
4680 cancel p;4732 cancel p;
4681 std.debug.assert(my_result == 2);4733 std.debug.assert(my_result == 2);
4682}4734}
4683async fn testBreakFromSuspend(my_result: *i32) void {4735async fn testResumeFromSuspend(my_result: *i32) void {
4684 s: suspend |p| {4736 suspend |p| {
4685 break :s;4737 resume p;
4686 }4738 }
4687 my_result.* += 1;4739 my_result.* += 1;
4688 suspend;4740 suspend;
...@@ -7336,7 +7388,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -7336,7 +7388,7 @@ Defer(body) = ("defer" | "deferror") body
73367388
7337IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))7389IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73387390
7339SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))7391SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
73407392
7341IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)7393IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
73427394
src-self-hosted/codegen.zig+159-4
...@@ -6,6 +6,7 @@ const c = @import("c.zig");...@@ -6,6 +6,7 @@ const c = @import("c.zig");
6const ir = @import("ir.zig");6const ir = @import("ir.zig");
7const Value = @import("value.zig").Value;7const Value = @import("value.zig").Value;
8const Type = @import("type.zig").Type;8const Type = @import("type.zig").Type;
9const Scope = @import("scope.zig").Scope;
9const event = std.event;10const event = std.event;
10const assert = std.debug.assert;11const assert = std.debug.assert;
11const DW = std.dwarf;12const DW = std.dwarf;
...@@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)...@@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
156 llvm_fn_type,157 llvm_fn_type,
157 ) orelse return error.OutOfMemory;158 ) orelse return error.OutOfMemory;
158159
159 const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp);160 const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp);
160 if (want_fn_safety and ofile.comp.haveLibC()) {161 if (want_fn_safety and ofile.comp.haveLibC()) {
161 try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");162 try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");
162 try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4");163 try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4");
...@@ -168,6 +169,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)...@@ -168,6 +169,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
168 //}169 //}
169170
170 const fn_type = fn_val.base.typ.cast(Type.Fn).?;171 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
172 const fn_type_normal = &fn_type.key.data.Normal;
171173
172 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");174 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
173 //add_uwtable_attr(g, fn_table_entry->llvm_value);175 //add_uwtable_attr(g, fn_table_entry->llvm_value);
...@@ -209,7 +211,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)...@@ -209,7 +211,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
209 // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");211 // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
210 //}212 //}
211213
212 const cur_ret_ptr = if (fn_type.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;214 const cur_ret_ptr = if (fn_type_normal.return_type.handleIsPtr()) llvm.GetParam(llvm_fn, 0) else null;
213215
214 // build all basic blocks216 // build all basic blocks
215 for (code.basic_block_list.toSlice()) |bb| {217 for (code.basic_block_list.toSlice()) |bb| {
...@@ -226,9 +228,86 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)...@@ -226,9 +228,86 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
226228
227 // TODO set up error return tracing229 // TODO set up error return tracing
228 // TODO allocate temporary stack values230 // TODO allocate temporary stack values
229 // TODO create debug variable declarations for variables and allocate all local variables231
232 const var_list = fn_type.non_key.Normal.variable_list.toSliceConst();
233 // create debug variable declarations for variables and allocate all local variables
234 for (var_list) |var_scope, i| {
235 const var_type = switch (var_scope.data) {
236 Scope.Var.Data.Const => unreachable,
237 Scope.Var.Data.Param => |param| param.typ,
238 };
239 // if (!type_has_bits(var->value->type)) {
240 // continue;
241 // }
242 // if (ir_get_var_is_comptime(var))
243 // continue;
244 // if (type_requires_comptime(var->value->type))
245 // continue;
246 // if (var->src_arg_index == SIZE_MAX) {
247 // var->value_ref = build_alloca(g, var->value->type, buf_ptr(&var->name), var->align_bytes);
248
249 // var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
250 // buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
251 // var->value->type->di_type, !g->strip_debug_symbols, 0);
252
253 // } else {
254 // it's a parameter
255 // assert(var->gen_arg_index != SIZE_MAX);
256 // TypeTableEntry *gen_type;
257 // FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index];
258
259 if (var_type.handleIsPtr()) {
260 // if (gen_info->is_byval) {
261 // gen_type = var->value->type;
262 // } else {
263 // gen_type = gen_info->type;
264 // }
265 var_scope.data.Param.llvm_value = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
266 } else {
267 // gen_type = var->value->type;
268 var_scope.data.Param.llvm_value = try renderAlloca(ofile, var_type, var_scope.name, Type.Pointer.Align.Abi);
269 }
270 // if (var->decl_node) {
271 // var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
272 // buf_ptr(&var->name), import->di_file,
273 // (unsigned)(var->decl_node->line + 1),
274 // gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1));
275 // }
276
277 // }
278 }
279
230 // TODO finishing error return trace setup. we have to do this after all the allocas.280 // TODO finishing error return trace setup. we have to do this after all the allocas.
231 // TODO create debug variable declarations for parameters281
282 // create debug variable declarations for parameters
283 // rely on the first variables in the variable_list being parameters.
284 //size_t next_var_i = 0;
285 for (fn_type.key.data.Normal.params) |param, i| {
286 //FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
287 //if (info->gen_index == SIZE_MAX)
288 // continue;
289 const scope_var = var_list[i];
290 //assert(variable->src_arg_index != SIZE_MAX);
291 //next_var_i += 1;
292 //assert(variable);
293 //assert(variable->value_ref);
294
295 if (!param.typ.handleIsPtr()) {
296 //clear_debug_source_node(g);
297 const llvm_param = llvm.GetParam(llvm_fn, @intCast(c_uint, i));
298 _ = renderStoreUntyped(
299 ofile,
300 llvm_param,
301 scope_var.data.Param.llvm_value,
302 Type.Pointer.Align.Abi,
303 Type.Pointer.Vol.Non,
304 );
305 }
306
307 //if (variable->decl_node) {
308 // gen_var_debug_decl(g, variable);
309 //}
310 }
232311
233 for (code.basic_block_list.toSlice()) |current_block| {312 for (code.basic_block_list.toSlice()) |current_block| {
234 llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);313 llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);
...@@ -293,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons...@@ -293,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons
293fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {372fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
294 return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);373 return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);
295}374}
375
376fn renderLoadUntyped(
377 ofile: *ObjectFile,
378 ptr: llvm.ValueRef,
379 alignment: Type.Pointer.Align,
380 vol: Type.Pointer.Vol,
381 name: [*]const u8,
382) !llvm.ValueRef {
383 const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
384 switch (vol) {
385 Type.Pointer.Vol.Non => {},
386 Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
387 }
388 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.GetElementType(llvm.TypeOf(ptr))));
389 return result;
390}
391
392fn renderLoad(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer, name: [*]const u8) !llvm.ValueRef {
393 return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
394}
395
396pub fn getHandleValue(ofile: *ObjectFile, ptr: llvm.ValueRef, ptr_type: *Type.Pointer) !?llvm.ValueRef {
397 const child_type = ptr_type.key.child_type;
398 if (!child_type.hasBits()) {
399 return null;
400 }
401 if (child_type.handleIsPtr()) {
402 return ptr;
403 }
404 return try renderLoad(ofile, ptr, ptr_type, c"");
405}
406
407pub fn renderStoreUntyped(
408 ofile: *ObjectFile,
409 value: llvm.ValueRef,
410 ptr: llvm.ValueRef,
411 alignment: Type.Pointer.Align,
412 vol: Type.Pointer.Vol,
413) !llvm.ValueRef {
414 const result = llvm.BuildStore(ofile.builder, value, ptr) orelse return error.OutOfMemory;
415 switch (vol) {
416 Type.Pointer.Vol.Non => {},
417 Type.Pointer.Vol.Volatile => llvm.SetVolatile(result, 1),
418 }
419 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm.TypeOf(value)));
420 return result;
421}
422
423pub fn renderStore(
424 ofile: *ObjectFile,
425 value: llvm.ValueRef,
426 ptr: llvm.ValueRef,
427 ptr_type: *Type.Pointer,
428) !llvm.ValueRef {
429 return renderStoreUntyped(ofile, value, ptr, ptr_type.key.alignment, ptr_type.key.vol);
430}
431
432pub fn renderAlloca(
433 ofile: *ObjectFile,
434 var_type: *Type,
435 name: []const u8,
436 alignment: Type.Pointer.Align,
437) !llvm.ValueRef {
438 const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
439 const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
440 const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
441 llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
442 return result;
443}
444
445pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 {
446 return switch (alignment) {
447 Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type),
448 Type.Pointer.Align.Override => |a| a,
449 };
450}
src-self-hosted/compilation.zig+67-5
...@@ -35,6 +35,7 @@ const CInt = @import("c_int.zig").CInt;...@@ -35,6 +35,7 @@ const CInt = @import("c_int.zig").CInt;
35pub const EventLoopLocal = struct {35pub const EventLoopLocal = struct {
36 loop: *event.Loop,36 loop: *event.Loop,
37 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),37 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
38 lld_lock: event.Lock,
3839
39 /// TODO pool these so that it doesn't have to lock40 /// TODO pool these so that it doesn't have to lock
40 prng: event.Locked(std.rand.DefaultPrng),41 prng: event.Locked(std.rand.DefaultPrng),
...@@ -55,6 +56,7 @@ pub const EventLoopLocal = struct {...@@ -55,6 +56,7 @@ pub const EventLoopLocal = struct {
5556
56 return EventLoopLocal{57 return EventLoopLocal{
57 .loop = loop,58 .loop = loop,
59 .lld_lock = event.Lock.init(loop),
58 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),60 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
59 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),61 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
60 .native_libc = event.Future(LibCInstallation).init(loop),62 .native_libc = event.Future(LibCInstallation).init(loop),
...@@ -63,6 +65,7 @@ pub const EventLoopLocal = struct {...@@ -63,6 +65,7 @@ pub const EventLoopLocal = struct {
6365
64 /// Must be called only after EventLoop.run completes.66 /// Must be called only after EventLoop.run completes.
65 fn deinit(self: *EventLoopLocal) void {67 fn deinit(self: *EventLoopLocal) void {
68 self.lld_lock.deinit();
66 while (self.llvm_handle_pool.pop()) |node| {69 while (self.llvm_handle_pool.pop()) |node| {
67 c.LLVMContextDispose(node.data);70 c.LLVMContextDispose(node.data);
68 self.loop.allocator.destroy(node);71 self.loop.allocator.destroy(node);
...@@ -220,12 +223,14 @@ pub const Compilation = struct {...@@ -220,12 +223,14 @@ pub const Compilation = struct {
220 int_type_table: event.Locked(IntTypeTable),223 int_type_table: event.Locked(IntTypeTable),
221 array_type_table: event.Locked(ArrayTypeTable),224 array_type_table: event.Locked(ArrayTypeTable),
222 ptr_type_table: event.Locked(PtrTypeTable),225 ptr_type_table: event.Locked(PtrTypeTable),
226 fn_type_table: event.Locked(FnTypeTable),
223227
224 c_int_types: [CInt.list.len]*Type.Int,228 c_int_types: [CInt.list.len]*Type.Int,
225229
226 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);230 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
227 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);231 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
228 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);232 const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
233 const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql);
229 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);234 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
230235
231 const CompileErrList = std.ArrayList(*Msg);236 const CompileErrList = std.ArrayList(*Msg);
...@@ -384,6 +389,7 @@ pub const Compilation = struct {...@@ -384,6 +389,7 @@ pub const Compilation = struct {
384 .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),389 .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
385 .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),390 .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
386 .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),391 .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
392 .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)),
387 .c_int_types = undefined,393 .c_int_types = undefined,
388394
389 .meta_type = undefined,395 .meta_type = undefined,
...@@ -414,6 +420,7 @@ pub const Compilation = struct {...@@ -414,6 +420,7 @@ pub const Compilation = struct {
414 comp.int_type_table.private_data.deinit();420 comp.int_type_table.private_data.deinit();
415 comp.array_type_table.private_data.deinit();421 comp.array_type_table.private_data.deinit();
416 comp.ptr_type_table.private_data.deinit();422 comp.ptr_type_table.private_data.deinit();
423 comp.fn_type_table.private_data.deinit();
417 comp.arena_allocator.deinit();424 comp.arena_allocator.deinit();
418 comp.loop.allocator.destroy(comp);425 comp.loop.allocator.destroy(comp);
419 }426 }
...@@ -1160,13 +1167,48 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {...@@ -1160,13 +1167,48 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
1160 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };1167 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
1161 symbol_name_consumed = true;1168 symbol_name_consumed = true;
11621169
1170 // Define local parameter variables
1171 const root_scope = fn_decl.base.findRootScope();
1172 for (fn_type.key.data.Normal.params) |param, i| {
1173 //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i);
1174 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*);
1175 const name_token = param_decl.name_token orelse {
1176 try comp.addCompileError(root_scope, Span{
1177 .first = param_decl.firstToken(),
1178 .last = param_decl.type_node.firstToken(),
1179 }, "missing parameter name");
1180 return error.SemanticAnalysisFailed;
1181 };
1182 const param_name = root_scope.tree.tokenSlice(name_token);
1183
1184 // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) {
1185 // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter"));
1186 // }
1187
1188 // TODO check for shadowing
1189
1190 const var_scope = try Scope.Var.createParam(
1191 comp,
1192 fn_val.child_scope,
1193 param_name,
1194 &param_decl.base,
1195 i,
1196 param.typ,
1197 );
1198 fn_val.child_scope = &var_scope.base;
1199
1200 try fn_type.non_key.Normal.variable_list.append(var_scope);
1201 }
1202
1163 const analyzed_code = try await (async comp.genAndAnalyzeCode(1203 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1164 &fndef_scope.base,1204 fn_val.child_scope,
1165 body_node,1205 body_node,
1166 fn_type.return_type,1206 fn_type.key.data.Normal.return_type,
1167 ) catch unreachable);1207 ) catch unreachable);
1168 errdefer analyzed_code.destroy(comp.gpa());1208 errdefer analyzed_code.destroy(comp.gpa());
11691209
1210 assert(fn_val.block_scope != null);
1211
1170 // Kick off rendering to LLVM module, but it doesn't block the fn decl1212 // Kick off rendering to LLVM module, but it doesn't block the fn decl
1171 // analysis from being complete.1213 // analysis from being complete.
1172 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);1214 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
...@@ -1199,14 +1241,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn...@@ -1199,14 +1241,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
11991241
1200 var params = ArrayList(Type.Fn.Param).init(comp.gpa());1242 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
1201 var params_consumed = false;1243 var params_consumed = false;
1202 defer if (params_consumed) {1244 defer if (!params_consumed) {
1203 for (params.toSliceConst()) |param| {1245 for (params.toSliceConst()) |param| {
1204 param.typ.base.deref(comp);1246 param.typ.base.deref(comp);
1205 }1247 }
1206 params.deinit();1248 params.deinit();
1207 };1249 };
12081250
1209 const is_var_args = false;
1210 {1251 {
1211 var it = fn_proto.params.iterator(0);1252 var it = fn_proto.params.iterator(0);
1212 while (it.next()) |param_node_ptr| {1253 while (it.next()) |param_node_ptr| {
...@@ -1219,8 +1260,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn...@@ -1219,8 +1260,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
1219 });1260 });
1220 }1261 }
1221 }1262 }
1222 const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);1263
1264 const key = Type.Fn.Key{
1265 .alignment = null,
1266 .data = Type.Fn.Key.Data{
1267 .Normal = Type.Fn.Key.Normal{
1268 .return_type = return_type,
1269 .params = params.toOwnedSlice(),
1270 .is_var_args = false, // TODO
1271 .cc = Type.Fn.CallingConvention.Auto, // TODO
1272 },
1273 },
1274 };
1223 params_consumed = true;1275 params_consumed = true;
1276 var key_consumed = false;
1277 defer if (!key_consumed) {
1278 for (key.data.Normal.params) |param| {
1279 param.typ.base.deref(comp);
1280 }
1281 comp.gpa().free(key.data.Normal.params);
1282 };
1283
1284 const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable);
1285 key_consumed = true;
1224 errdefer fn_type.base.base.deref(comp);1286 errdefer fn_type.base.base.deref(comp);
12251287
1226 return fn_type;1288 return fn_type;
src-self-hosted/ir.zig+181-24
...@@ -10,8 +10,10 @@ const assert = std.debug.assert;...@@ -10,8 +10,10 @@ const assert = std.debug.assert;
10const Token = std.zig.Token;10const Token = std.zig.Token;
11const Span = @import("errmsg.zig").Span;11const Span = @import("errmsg.zig").Span;
12const llvm = @import("llvm.zig");12const llvm = @import("llvm.zig");
13const ObjectFile = @import("codegen.zig").ObjectFile;13const codegen = @import("codegen.zig");
14const ObjectFile = codegen.ObjectFile;
14const Decl = @import("decl.zig").Decl;15const Decl = @import("decl.zig").Decl;
16const mem = std.mem;
1517
16pub const LVal = enum {18pub const LVal = enum {
17 None,19 None,
...@@ -122,6 +124,8 @@ pub const Inst = struct {...@@ -122,6 +124,8 @@ pub const Inst = struct {
122 Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),124 Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
123 Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),125 Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
124 Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),126 Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable),
127 Id.VarPtr => return await (async @fieldParentPtr(VarPtr, "base", base).analyze(ira) catch unreachable),
128 Id.LoadPtr => return await (async @fieldParentPtr(LoadPtr, "base", base).analyze(ira) catch unreachable),
125 }129 }
126 }130 }
127131
...@@ -130,6 +134,8 @@ pub const Inst = struct {...@@ -130,6 +134,8 @@ pub const Inst = struct {
130 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),134 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
131 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),135 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132 Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),136 Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val),
137 Id.VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val),
138 Id.LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val),
133 Id.DeclRef => unreachable,139 Id.DeclRef => unreachable,
134 Id.PtrType => unreachable,140 Id.PtrType => unreachable,
135 Id.Ref => @panic("TODO"),141 Id.Ref => @panic("TODO"),
...@@ -248,6 +254,8 @@ pub const Inst = struct {...@@ -248,6 +254,8 @@ pub const Inst = struct {
248 Call,254 Call,
249 DeclRef,255 DeclRef,
250 PtrType,256 PtrType,
257 VarPtr,
258 LoadPtr,
251 };259 };
252260
253 pub const Call = struct {261 pub const Call = struct {
...@@ -281,11 +289,13 @@ pub const Inst = struct {...@@ -281,11 +289,13 @@ pub const Inst = struct {
281 return error.SemanticAnalysisFailed;289 return error.SemanticAnalysisFailed;
282 };290 };
283291
284 if (fn_type.params.len != self.params.args.len) {292 const fn_type_param_count = fn_type.paramCount();
293
294 if (fn_type_param_count != self.params.args.len) {
285 try ira.addCompileError(295 try ira.addCompileError(
286 self.base.span,296 self.base.span,
287 "expected {} arguments, found {}",297 "expected {} arguments, found {}",
288 fn_type.params.len,298 fn_type_param_count,
289 self.params.args.len,299 self.params.args.len,
290 );300 );
291 return error.SemanticAnalysisFailed;301 return error.SemanticAnalysisFailed;
...@@ -299,7 +309,7 @@ pub const Inst = struct {...@@ -299,7 +309,7 @@ pub const Inst = struct {
299 .fn_ref = fn_ref,309 .fn_ref = fn_ref,
300 .args = args,310 .args = args,
301 });311 });
302 new_inst.val = IrVal{ .KnownType = fn_type.return_type };312 new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type };
303 return new_inst;313 return new_inst;
304 }314 }
305315
...@@ -489,6 +499,133 @@ pub const Inst = struct {...@@ -489,6 +499,133 @@ pub const Inst = struct {
489 }499 }
490 };500 };
491501
502 pub const VarPtr = struct {
503 base: Inst,
504 params: Params,
505
506 const Params = struct {
507 var_scope: *Scope.Var,
508 };
509
510 const ir_val_init = IrVal.Init.Unknown;
511
512 pub fn dump(inst: *const VarPtr) void {
513 std.debug.warn("{}", inst.params.var_scope.name);
514 }
515
516 pub fn hasSideEffects(inst: *const VarPtr) bool {
517 return false;
518 }
519
520 pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
521 switch (self.params.var_scope.data) {
522 Scope.Var.Data.Const => @panic("TODO"),
523 Scope.Var.Data.Param => |param| {
524 const new_inst = try ira.irb.build(
525 Inst.VarPtr,
526 self.base.scope,
527 self.base.span,
528 Inst.VarPtr.Params{ .var_scope = self.params.var_scope },
529 );
530 const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
531 .child_type = param.typ,
532 .mut = Type.Pointer.Mut.Const,
533 .vol = Type.Pointer.Vol.Non,
534 .size = Type.Pointer.Size.One,
535 .alignment = Type.Pointer.Align.Abi,
536 }) catch unreachable);
537 new_inst.val = IrVal{ .KnownType = &ptr_type.base };
538 return new_inst;
539 },
540 }
541 }
542
543 pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef {
544 switch (self.params.var_scope.data) {
545 Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass
546 Scope.Var.Data.Param => |param| return param.llvm_value,
547 }
548 }
549 };
550
551 pub const LoadPtr = struct {
552 base: Inst,
553 params: Params,
554
555 const Params = struct {
556 target: *Inst,
557 };
558
559 const ir_val_init = IrVal.Init.Unknown;
560
561 pub fn dump(inst: *const LoadPtr) void {}
562
563 pub fn hasSideEffects(inst: *const LoadPtr) bool {
564 return false;
565 }
566
567 pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
568 const target = try self.params.target.getAsParam();
569 const target_type = target.getKnownType();
570 if (target_type.id != Type.Id.Pointer) {
571 try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", target_type.name);
572 return error.SemanticAnalysisFailed;
573 }
574 const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type);
575 // if (instr_is_comptime(ptr)) {
576 // if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst ||
577 // ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
578 // {
579 // ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value);
580 // if (pointee->special != ConstValSpecialRuntime) {
581 // IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope,
582 // source_instruction->source_node, child_type);
583 // copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst);
584 // result->value.type = child_type;
585 // return result;
586 // }
587 // }
588 // }
589 const new_inst = try ira.irb.build(
590 Inst.LoadPtr,
591 self.base.scope,
592 self.base.span,
593 Inst.LoadPtr.Params{ .target = target },
594 );
595 new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type };
596 return new_inst;
597 }
598
599 pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef {
600 const child_type = self.base.getKnownType();
601 if (!child_type.hasBits()) {
602 return null;
603 }
604 const ptr = self.params.target.llvm_value.?;
605 const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?;
606
607 return try codegen.getHandleValue(ofile, ptr, ptr_type);
608
609 //uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count;
610 //if (unaligned_bit_count == 0)
611 // return get_handle_value(g, ptr, child_type, ptr_type);
612
613 //bool big_endian = g->is_big_endian;
614
615 //assert(!handle_is_ptr(child_type));
616 //LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
617
618 //uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
619 //uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
620 //uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
621
622 //LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
623 //LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
624
625 //return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, "");
626 }
627 };
628
492 pub const PtrType = struct {629 pub const PtrType = struct {
493 base: Inst,630 base: Inst,
494 params: Params,631 params: Params,
...@@ -1158,6 +1295,7 @@ pub const Builder = struct {...@@ -1158,6 +1295,7 @@ pub const Builder = struct {
1158 Scope.Id.Block,1295 Scope.Id.Block,
1159 Scope.Id.Defer,1296 Scope.Id.Defer,
1160 Scope.Id.DeferExpr,1297 Scope.Id.DeferExpr,
1298 Scope.Id.Var,
1161 => scope = scope.parent.?,1299 => scope = scope.parent.?,
1162 }1300 }
1163 }1301 }
...@@ -1259,8 +1397,8 @@ pub const Builder = struct {...@@ -1259,8 +1397,8 @@ pub const Builder = struct {
1259 var child_scope = outer_block_scope;1397 var child_scope = outer_block_scope;
12601398
1261 if (parent_scope.findFnDef()) |fndef_scope| {1399 if (parent_scope.findFnDef()) |fndef_scope| {
1262 if (fndef_scope.fn_val.child_scope == parent_scope) {1400 if (fndef_scope.fn_val.?.block_scope == null) {
1263 fndef_scope.fn_val.block_scope = block_scope;1401 fndef_scope.fn_val.?.block_scope = block_scope;
1264 }1402 }
1265 }1403 }
12661404
...@@ -1490,20 +1628,23 @@ pub const Builder = struct {...@@ -1490,20 +1628,23 @@ pub const Builder = struct {
1490 error.OutOfMemory => return error.OutOfMemory,1628 error.OutOfMemory => return error.OutOfMemory,
1491 }1629 }
14921630
1493 //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);1631 switch (await (async irb.findIdent(scope, name) catch unreachable)) {
1494 //if (var) {1632 Ident.Decl => |decl| {
1495 // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);1633 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1496 // if (lval == LValPtr)1634 .decl = decl,
1497 // return var_ptr;1635 .lval = lval,
1498 // else1636 });
1499 // return ir_build_load_ptr(irb, scope, node, var_ptr);1637 },
1500 //}1638 Ident.VarScope => |var_scope| {
15011639 const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope });
1502 if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {1640 switch (lval) {
1503 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{1641 LVal.Ptr => return var_ptr,
1504 .decl = decl,1642 LVal.None => {
1505 .lval = lval,1643 return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr });
1506 });1644 },
1645 }
1646 },
1647 Ident.NotFound => {},
1507 }1648 }
15081649
1509 //if (node->owner->any_imports_failed) {1650 //if (node->owner->any_imports_failed) {
...@@ -1544,6 +1685,7 @@ pub const Builder = struct {...@@ -1544,6 +1685,7 @@ pub const Builder = struct {
1544 Scope.Id.Block,1685 Scope.Id.Block,
1545 Scope.Id.Decls,1686 Scope.Id.Decls,
1546 Scope.Id.Root,1687 Scope.Id.Root,
1688 Scope.Id.Var,
1547 => scope = scope.parent orelse break,1689 => scope = scope.parent orelse break,
15481690
1549 Scope.Id.DeferExpr => unreachable,1691 Scope.Id.DeferExpr => unreachable,
...@@ -1594,6 +1736,7 @@ pub const Builder = struct {...@@ -1594,6 +1736,7 @@ pub const Builder = struct {
15941736
1595 Scope.Id.CompTime,1737 Scope.Id.CompTime,
1596 Scope.Id.Block,1738 Scope.Id.Block,
1739 Scope.Id.Var,
1597 => scope = scope.parent orelse return is_noreturn,1740 => scope = scope.parent orelse return is_noreturn,
15981741
1599 Scope.Id.DeferExpr => unreachable,1742 Scope.Id.DeferExpr => unreachable,
...@@ -1672,8 +1815,10 @@ pub const Builder = struct {...@@ -1672,8 +1815,10 @@ pub const Builder = struct {
1672 Type.Pointer.Size,1815 Type.Pointer.Size,
1673 LVal,1816 LVal,
1674 *Decl,1817 *Decl,
1818 *Scope.Var,
1675 => {},1819 => {},
1676 // it's ok to add more types here, just make sure any instructions are ref'd appropriately1820 // it's ok to add more types here, just make sure that
1821 // any instructions and basic blocks are ref'd appropriately
1677 else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),1822 else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
1678 }1823 }
1679 }1824 }
...@@ -1771,18 +1916,30 @@ pub const Builder = struct {...@@ -1771,18 +1916,30 @@ pub const Builder = struct {
1771 //// the above blocks are rendered by ir_gen after the rest of codegen1916 //// the above blocks are rendered by ir_gen after the rest of codegen
1772 }1917 }
17731918
1774 async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl {1919 const Ident = union(enum) {
1920 NotFound,
1921 Decl: *Decl,
1922 VarScope: *Scope.Var,
1923 };
1924
1925 async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
1775 var s = scope;1926 var s = scope;
1776 while (true) {1927 while (true) {
1777 switch (s.id) {1928 switch (s.id) {
1929 Scope.Id.Root => return Ident.NotFound,
1778 Scope.Id.Decls => {1930 Scope.Id.Decls => {
1779 const decls = @fieldParentPtr(Scope.Decls, "base", s);1931 const decls = @fieldParentPtr(Scope.Decls, "base", s);
1780 const table = await (async decls.getTableReadOnly() catch unreachable);1932 const table = await (async decls.getTableReadOnly() catch unreachable);
1781 if (table.get(name)) |entry| {1933 if (table.get(name)) |entry| {
1782 return entry.value;1934 return Ident{ .Decl = entry.value };
1935 }
1936 },
1937 Scope.Id.Var => {
1938 const var_scope = @fieldParentPtr(Scope.Var, "base", s);
1939 if (mem.eql(u8, var_scope.name, name)) {
1940 return Ident{ .VarScope = var_scope };
1783 }1941 }
1784 },1942 },
1785 Scope.Id.Root => return null,
1786 else => {},1943 else => {},
1787 }1944 }
1788 s = s.parent.?;1945 s = s.parent.?;
src-self-hosted/link.zig+23-10
...@@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void {...@@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void {
8080
81 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());81 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
82 const args_slice = ctx.args.toSlice();82 const args_slice = ctx.args.toSlice();
83 // Not evented I/O. LLD does its own multithreading internally.83
84 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {84 {
85 if (!ctx.link_msg.isNull()) {85 // LLD is not thread-safe, so we grab a global lock.
86 // TODO capture these messages and pass them through the system, reporting them through the86 const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable);
87 // event system instead of printing them directly here.87 defer held.release();
88 // perhaps try to parse and understand them.88
89 std.debug.warn("{}\n", ctx.link_msg.toSliceConst());89 // Not evented I/O. LLD does its own multithreading internally.
90 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
91 if (!ctx.link_msg.isNull()) {
92 // TODO capture these messages and pass them through the system, reporting them through the
93 // event system instead of printing them directly here.
94 // perhaps try to parse and understand them.
95 std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
96 }
97 return error.LinkFailed;
90 }98 }
91 return error.LinkFailed;
92 }99 }
93}100}
94101
...@@ -672,7 +679,13 @@ const DarwinPlatform = struct {...@@ -672,7 +679,13 @@ const DarwinPlatform = struct {
672 };679 };
673680
674 var had_extra: bool = undefined;681 var had_extra: bool = undefined;
675 try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,);682 try darwinGetReleaseVersion(
683 ver_str,
684 &result.major,
685 &result.minor,
686 &result.micro,
687 &had_extra,
688 );
676 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {689 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
677 return error.InvalidDarwinVersionString;690 return error.InvalidDarwinVersionString;
678 }691 }
...@@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3...@@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3
713 return error.InvalidDarwinVersionString;726 return error.InvalidDarwinVersionString;
714727
715 var start_pos: usize = 0;728 var start_pos: usize = 0;
716 for ([]*u32{major, minor, micro}) |v| {729 for ([]*u32{ major, minor, micro }) |v| {
717 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');730 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
718 const end_pos = dot_pos orelse str.len;731 const end_pos = dot_pos orelse str.len;
719 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;732 v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
src-self-hosted/llvm.zig+14-1
...@@ -30,6 +30,7 @@ pub const AddGlobal = c.LLVMAddGlobal;...@@ -30,6 +30,7 @@ pub const AddGlobal = c.LLVMAddGlobal;
30pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;30pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
31pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;31pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
32pub const ArrayType = c.LLVMArrayType;32pub const ArrayType = c.LLVMArrayType;
33pub const BuildLoad = c.LLVMBuildLoad;
33pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;34pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
34pub const ConstAllOnes = c.LLVMConstAllOnes;35pub const ConstAllOnes = c.LLVMConstAllOnes;
35pub const ConstArray = c.LLVMConstArray;36pub const ConstArray = c.LLVMConstArray;
...@@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer;...@@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer;
95pub const SetLinkage = c.LLVMSetLinkage;96pub const SetLinkage = c.LLVMSetLinkage;
96pub const SetTarget = c.LLVMSetTarget;97pub const SetTarget = c.LLVMSetTarget;
97pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;98pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
99pub const SetVolatile = c.LLVMSetVolatile;
98pub const StructTypeInContext = c.LLVMStructTypeInContext;100pub const StructTypeInContext = c.LLVMStructTypeInContext;
99pub const TokenTypeInContext = c.LLVMTokenTypeInContext;101pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
100pub const TypeOf = c.LLVMTypeOf;
101pub const VoidTypeInContext = c.LLVMVoidTypeInContext;102pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
102pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;103pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
103pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;104pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
104105
106pub const GetElementType = LLVMGetElementType;
107extern fn LLVMGetElementType(Ty: TypeRef) TypeRef;
108
109pub const TypeOf = LLVMTypeOf;
110extern fn LLVMTypeOf(Val: ValueRef) TypeRef;
111
112pub const BuildStore = LLVMBuildStore;
113extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef;
114
115pub const BuildAlloca = LLVMBuildAlloca;
116extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef;
117
105pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;118pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
106pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;119pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
107120
src-self-hosted/scope.zig+128-73
...@@ -6,23 +6,26 @@ const Compilation = @import("compilation.zig").Compilation;...@@ -6,23 +6,26 @@ const Compilation = @import("compilation.zig").Compilation;
6const mem = std.mem;6const mem = std.mem;
7const ast = std.zig.ast;7const ast = std.zig.ast;
8const Value = @import("value.zig").Value;8const Value = @import("value.zig").Value;
9const Type = @import("type.zig").Type;
9const ir = @import("ir.zig");10const ir = @import("ir.zig");
10const Span = @import("errmsg.zig").Span;11const Span = @import("errmsg.zig").Span;
11const assert = std.debug.assert;12const assert = std.debug.assert;
12const event = std.event;13const event = std.event;
14const llvm = @import("llvm.zig");
1315
14pub const Scope = struct {16pub const Scope = struct {
15 id: Id,17 id: Id,
16 parent: ?*Scope,18 parent: ?*Scope,
17 ref_count: usize,19 ref_count: std.atomic.Int(usize),
1820
21 /// Thread-safe
19 pub fn ref(base: *Scope) void {22 pub fn ref(base: *Scope) void {
20 base.ref_count += 1;23 _ = base.ref_count.incr();
21 }24 }
2225
26 /// Thread-safe
23 pub fn deref(base: *Scope, comp: *Compilation) void {27 pub fn deref(base: *Scope, comp: *Compilation) void {
24 base.ref_count -= 1;28 if (base.ref_count.decr() == 1) {
25 if (base.ref_count == 0) {
26 if (base.parent) |parent| parent.deref(comp);29 if (base.parent) |parent| parent.deref(comp);
27 switch (base.id) {30 switch (base.id) {
28 Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),31 Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
...@@ -32,6 +35,7 @@ pub const Scope = struct {...@@ -32,6 +35,7 @@ pub const Scope = struct {
32 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),35 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
33 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),36 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
34 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),37 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
35 }39 }
36 }40 }
37 }41 }
...@@ -49,15 +53,15 @@ pub const Scope = struct {...@@ -49,15 +53,15 @@ pub const Scope = struct {
49 var scope = base;53 var scope = base;
50 while (true) {54 while (true) {
51 switch (scope.id) {55 switch (scope.id) {
52 Id.FnDef => return @fieldParentPtr(FnDef, "base", base),56 Id.FnDef => return @fieldParentPtr(FnDef, "base", scope),
53 Id.Decls => return null,57 Id.Root, Id.Decls => return null,
5458
55 Id.Block,59 Id.Block,
56 Id.Defer,60 Id.Defer,
57 Id.DeferExpr,61 Id.DeferExpr,
58 Id.CompTime,62 Id.CompTime,
59 Id.Root,63 Id.Var,
60 => scope = scope.parent orelse return null,64 => scope = scope.parent.?,
61 }65 }
62 }66 }
63 }67 }
...@@ -66,7 +70,7 @@ pub const Scope = struct {...@@ -66,7 +70,7 @@ pub const Scope = struct {
66 var scope = base;70 var scope = base;
67 while (true) {71 while (true) {
68 switch (scope.id) {72 switch (scope.id) {
69 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),73 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope),
7074
71 Id.FnDef,75 Id.FnDef,
72 Id.Decls,76 Id.Decls,
...@@ -76,11 +80,21 @@ pub const Scope = struct {...@@ -76,11 +80,21 @@ pub const Scope = struct {
76 Id.Defer,80 Id.Defer,
77 Id.CompTime,81 Id.CompTime,
78 Id.Root,82 Id.Root,
83 Id.Var,
79 => scope = scope.parent orelse return null,84 => scope = scope.parent orelse return null,
80 }85 }
81 }86 }
82 }87 }
8388
89 fn init(base: *Scope, id: Id, parent: *Scope) void {
90 base.* = Scope{
91 .id = id,
92 .parent = parent,
93 .ref_count = std.atomic.Int(usize).init(1),
94 };
95 parent.ref();
96 }
97
84 pub const Id = enum {98 pub const Id = enum {
85 Root,99 Root,
86 Decls,100 Decls,
...@@ -89,6 +103,7 @@ pub const Scope = struct {...@@ -89,6 +103,7 @@ pub const Scope = struct {
89 CompTime,103 CompTime,
90 Defer,104 Defer,
91 DeferExpr,105 DeferExpr,
106 Var,
92 };107 };
93108
94 pub const Root = struct {109 pub const Root = struct {
...@@ -100,16 +115,16 @@ pub const Scope = struct {...@@ -100,16 +115,16 @@ pub const Scope = struct {
100 /// Takes ownership of realpath115 /// Takes ownership of realpath
101 /// Takes ownership of tree, will deinit and destroy when done.116 /// Takes ownership of tree, will deinit and destroy when done.
102 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {117 pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
103 const self = try comp.gpa().create(Root{118 const self = try comp.gpa().createOne(Root);
119 self.* = Root{
104 .base = Scope{120 .base = Scope{
105 .id = Id.Root,121 .id = Id.Root,
106 .parent = null,122 .parent = null,
107 .ref_count = 1,123 .ref_count = std.atomic.Int(usize).init(1),
108 },124 },
109 .tree = tree,125 .tree = tree,
110 .realpath = realpath,126 .realpath = realpath,
111 });127 };
112 errdefer comp.gpa().destroy(self);
113128
114 return self;129 return self;
115 }130 }
...@@ -137,16 +152,13 @@ pub const Scope = struct {...@@ -137,16 +152,13 @@ pub const Scope = struct {
137152
138 /// Creates a Decls scope with 1 reference153 /// Creates a Decls scope with 1 reference
139 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {154 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
140 const self = try comp.gpa().create(Decls{155 const self = try comp.gpa().createOne(Decls);
141 .base = Scope{156 self.* = Decls{
142 .id = Id.Decls,157 .base = undefined,
143 .parent = parent,
144 .ref_count = 1,
145 },
146 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
147 .name_future = event.Future(void).init(comp.loop),159 .name_future = event.Future(void).init(comp.loop),
148 });160 };
149 parent.ref();161 self.base.init(Id.Decls, parent);
150 return self;162 return self;
151 }163 }
152164
...@@ -199,21 +211,16 @@ pub const Scope = struct {...@@ -199,21 +211,16 @@ pub const Scope = struct {
199211
200 /// Creates a Block scope with 1 reference212 /// Creates a Block scope with 1 reference
201 pub fn create(comp: *Compilation, parent: *Scope) !*Block {213 pub fn create(comp: *Compilation, parent: *Scope) !*Block {
202 const self = try comp.gpa().create(Block{214 const self = try comp.gpa().createOne(Block);
203 .base = Scope{215 self.* = Block{
204 .id = Id.Block,216 .base = undefined,
205 .parent = parent,
206 .ref_count = 1,
207 },
208 .incoming_values = undefined,217 .incoming_values = undefined,
209 .incoming_blocks = undefined,218 .incoming_blocks = undefined,
210 .end_block = undefined,219 .end_block = undefined,
211 .is_comptime = undefined,220 .is_comptime = undefined,
212 .safety = Safety.Auto,221 .safety = Safety.Auto,
213 });222 };
214 errdefer comp.gpa().destroy(self);223 self.base.init(Id.Block, parent);
215
216 parent.ref();
217 return self;224 return self;
218 }225 }
219226
...@@ -226,22 +233,17 @@ pub const Scope = struct {...@@ -226,22 +233,17 @@ pub const Scope = struct {
226 base: Scope,233 base: Scope,
227234
228 /// This reference is not counted so that the scope can get destroyed with the function235 /// This reference is not counted so that the scope can get destroyed with the function
229 fn_val: *Value.Fn,236 fn_val: ?*Value.Fn,
230237
231 /// Creates a FnDef scope with 1 reference238 /// Creates a FnDef scope with 1 reference
232 /// Must set the fn_val later239 /// Must set the fn_val later
233 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {240 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
234 const self = try comp.gpa().create(FnDef{241 const self = try comp.gpa().createOne(FnDef);
235 .base = Scope{242 self.* = FnDef{
236 .id = Id.FnDef,243 .base = undefined,
237 .parent = parent,244 .fn_val = null,
238 .ref_count = 1,245 };
239 },246 self.base.init(Id.FnDef, parent);
240 .fn_val = undefined,
241 });
242
243 parent.ref();
244
245 return self;247 return self;
246 }248 }
247249
...@@ -255,15 +257,9 @@ pub const Scope = struct {...@@ -255,15 +257,9 @@ pub const Scope = struct {
255257
256 /// Creates a CompTime scope with 1 reference258 /// Creates a CompTime scope with 1 reference
257 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {259 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
258 const self = try comp.gpa().create(CompTime{260 const self = try comp.gpa().createOne(CompTime);
259 .base = Scope{261 self.* = CompTime{ .base = undefined };
260 .id = Id.CompTime,262 self.base.init(Id.CompTime, parent);
261 .parent = parent,
262 .ref_count = 1,
263 },
264 });
265
266 parent.ref();
267 return self;263 return self;
268 }264 }
269265
...@@ -289,20 +285,14 @@ pub const Scope = struct {...@@ -289,20 +285,14 @@ pub const Scope = struct {
289 kind: Kind,285 kind: Kind,
290 defer_expr_scope: *DeferExpr,286 defer_expr_scope: *DeferExpr,
291 ) !*Defer {287 ) !*Defer {
292 const self = try comp.gpa().create(Defer{288 const self = try comp.gpa().createOne(Defer);
293 .base = Scope{289 self.* = Defer{
294 .id = Id.Defer,290 .base = undefined,
295 .parent = parent,
296 .ref_count = 1,
297 },
298 .defer_expr_scope = defer_expr_scope,291 .defer_expr_scope = defer_expr_scope,
299 .kind = kind,292 .kind = kind,
300 });293 };
301 errdefer comp.gpa().destroy(self);294 self.base.init(Id.Defer, parent);
302
303 defer_expr_scope.base.ref();295 defer_expr_scope.base.ref();
304
305 parent.ref();
306 return self;296 return self;
307 }297 }
308298
...@@ -319,18 +309,13 @@ pub const Scope = struct {...@@ -319,18 +309,13 @@ pub const Scope = struct {
319309
320 /// Creates a DeferExpr scope with 1 reference310 /// Creates a DeferExpr scope with 1 reference
321 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {311 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
322 const self = try comp.gpa().create(DeferExpr{312 const self = try comp.gpa().createOne(DeferExpr);
323 .base = Scope{313 self.* = DeferExpr{
324 .id = Id.DeferExpr,314 .base = undefined,
325 .parent = parent,
326 .ref_count = 1,
327 },
328 .expr_node = expr_node,315 .expr_node = expr_node,
329 .reported_err = false,316 .reported_err = false,
330 });317 };
331 errdefer comp.gpa().destroy(self);318 self.base.init(Id.DeferExpr, parent);
332
333 parent.ref();
334 return self;319 return self;
335 }320 }
336321
...@@ -338,4 +323,74 @@ pub const Scope = struct {...@@ -338,4 +323,74 @@ pub const Scope = struct {
338 comp.gpa().destroy(self);323 comp.gpa().destroy(self);
339 }324 }
340 };325 };
326
327 pub const Var = struct {
328 base: Scope,
329 name: []const u8,
330 src_node: *ast.Node,
331 data: Data,
332
333 pub const Data = union(enum) {
334 Param: Param,
335 Const: *Value,
336 };
337
338 pub const Param = struct {
339 index: usize,
340 typ: *Type,
341 llvm_value: llvm.ValueRef,
342 };
343
344 pub fn createParam(
345 comp: *Compilation,
346 parent: *Scope,
347 name: []const u8,
348 src_node: *ast.Node,
349 param_index: usize,
350 param_type: *Type,
351 ) !*Var {
352 const self = try create(comp, parent, name, src_node);
353 self.data = Data{
354 .Param = Param{
355 .index = param_index,
356 .typ = param_type,
357 .llvm_value = undefined,
358 },
359 };
360 return self;
361 }
362
363 pub fn createConst(
364 comp: *Compilation,
365 parent: *Scope,
366 name: []const u8,
367 src_node: *ast.Node,
368 value: *Value,
369 ) !*Var {
370 const self = try create(comp, parent, name, src_node);
371 self.data = Data{ .Const = value };
372 value.ref();
373 return self;
374 }
375
376 fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var {
377 const self = try comp.gpa().createOne(Var);
378 self.* = Var{
379 .base = undefined,
380 .name = name,
381 .src_node = src_node,
382 .data = undefined,
383 };
384 self.base.init(Id.Var, parent);
385 return self;
386 }
387
388 pub fn destroy(self: *Var, comp: *Compilation) void {
389 switch (self.data) {
390 Data.Param => {},
391 Data.Const => |value| value.deref(comp),
392 }
393 comp.gpa().destroy(self);
394 }
395 };
341};396};
src-self-hosted/type.zig+314-57
...@@ -141,9 +141,13 @@ pub const Type = struct {...@@ -141,9 +141,13 @@ pub const Type = struct {
141 Id.Promise,141 Id.Promise,
142 => return true,142 => return true,
143143
144 Id.Pointer => {
145 const ptr_type = @fieldParentPtr(Pointer, "base", base);
146 return ptr_type.key.child_type.hasBits();
147 },
148
144 Id.ErrorSet => @panic("TODO"),149 Id.ErrorSet => @panic("TODO"),
145 Id.Enum => @panic("TODO"),150 Id.Enum => @panic("TODO"),
146 Id.Pointer => @panic("TODO"),
147 Id.Struct => @panic("TODO"),151 Id.Struct => @panic("TODO"),
148 Id.Array => @panic("TODO"),152 Id.Array => @panic("TODO"),
149 Id.Optional => @panic("TODO"),153 Id.Optional => @panic("TODO"),
...@@ -221,57 +225,294 @@ pub const Type = struct {...@@ -221,57 +225,294 @@ pub const Type = struct {
221225
222 pub const Fn = struct {226 pub const Fn = struct {
223 base: Type,227 base: Type,
224 return_type: *Type,228 key: Key,
225 params: []Param,229 non_key: NonKey,
226 is_var_args: bool,230 garbage_node: std.atomic.Stack(*Fn).Node,
231
232 pub const Kind = enum {
233 Normal,
234 Generic,
235 };
236
237 pub const NonKey = union {
238 Normal: Normal,
239 Generic: void,
240
241 pub const Normal = struct {
242 variable_list: std.ArrayList(*Scope.Var),
243 };
244 };
245
246 pub const Key = struct {
247 data: Data,
248 alignment: ?u32,
249
250 pub const Data = union(Kind) {
251 Generic: Generic,
252 Normal: Normal,
253 };
254
255 pub const Normal = struct {
256 params: []Param,
257 return_type: *Type,
258 is_var_args: bool,
259 cc: CallingConvention,
260 };
261
262 pub const Generic = struct {
263 param_count: usize,
264 cc: CC,
265
266 pub const CC = union(CallingConvention) {
267 Auto,
268 C,
269 Cold,
270 Naked,
271 Stdcall,
272 Async: *Type, // allocator type
273 };
274 };
275
276 pub fn hash(self: *const Key) u32 {
277 var result: u32 = 0;
278 result +%= hashAny(self.alignment, 0);
279 switch (self.data) {
280 Kind.Generic => |generic| {
281 result +%= hashAny(generic.param_count, 1);
282 switch (generic.cc) {
283 CallingConvention.Async => |allocator_type| result +%= hashAny(allocator_type, 2),
284 else => result +%= hashAny(CallingConvention(generic.cc), 3),
285 }
286 },
287 Kind.Normal => |normal| {
288 result +%= hashAny(normal.return_type, 4);
289 result +%= hashAny(normal.is_var_args, 5);
290 result +%= hashAny(normal.cc, 6);
291 for (normal.params) |param| {
292 result +%= hashAny(param.is_noalias, 7);
293 result +%= hashAny(param.typ, 8);
294 }
295 },
296 }
297 return result;
298 }
299
300 pub fn eql(self: *const Key, other: *const Key) bool {
301 if ((self.alignment == null) != (other.alignment == null)) return false;
302 if (self.alignment) |self_align| {
303 if (self_align != other.alignment.?) return false;
304 }
305 if (@TagType(Data)(self.data) != @TagType(Data)(other.data)) return false;
306 switch (self.data) {
307 Kind.Generic => |*self_generic| {
308 const other_generic = &other.data.Generic;
309 if (self_generic.param_count != other_generic.param_count) return false;
310 if (CallingConvention(self_generic.cc) != CallingConvention(other_generic.cc)) return false;
311 switch (self_generic.cc) {
312 CallingConvention.Async => |self_allocator_type| {
313 const other_allocator_type = other_generic.cc.Async;
314 if (self_allocator_type != other_allocator_type) return false;
315 },
316 else => {},
317 }
318 },
319 Kind.Normal => |*self_normal| {
320 const other_normal = &other.data.Normal;
321 if (self_normal.cc != other_normal.cc) return false;
322 if (self_normal.is_var_args != other_normal.is_var_args) return false;
323 if (self_normal.return_type != other_normal.return_type) return false;
324 for (self_normal.params) |*self_param, i| {
325 const other_param = &other_normal.params[i];
326 if (self_param.is_noalias != other_param.is_noalias) return false;
327 if (self_param.typ != other_param.typ) return false;
328 }
329 },
330 }
331 return true;
332 }
333
334 pub fn deref(key: Key, comp: *Compilation) void {
335 switch (key.data) {
336 Kind.Generic => |generic| {
337 switch (generic.cc) {
338 CallingConvention.Async => |allocator_type| allocator_type.base.deref(comp),
339 else => {},
340 }
341 },
342 Kind.Normal => |normal| {
343 normal.return_type.base.deref(comp);
344 for (normal.params) |param| {
345 param.typ.base.deref(comp);
346 }
347 },
348 }
349 }
350
351 pub fn ref(key: Key) void {
352 switch (key.data) {
353 Kind.Generic => |generic| {
354 switch (generic.cc) {
355 CallingConvention.Async => |allocator_type| allocator_type.base.ref(),
356 else => {},
357 }
358 },
359 Kind.Normal => |normal| {
360 normal.return_type.base.ref();
361 for (normal.params) |param| {
362 param.typ.base.ref();
363 }
364 },
365 }
366 }
367 };
368
369 pub const CallingConvention = enum {
370 Auto,
371 C,
372 Cold,
373 Naked,
374 Stdcall,
375 Async,
376 };
227377
228 pub const Param = struct {378 pub const Param = struct {
229 is_noalias: bool,379 is_noalias: bool,
230 typ: *Type,380 typ: *Type,
231 };381 };
232382
233 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {383 fn ccFnTypeStr(cc: CallingConvention) []const u8 {
234 const result = try comp.gpa().create(Fn{384 return switch (cc) {
385 CallingConvention.Auto => "",
386 CallingConvention.C => "extern ",
387 CallingConvention.Cold => "coldcc ",
388 CallingConvention.Naked => "nakedcc ",
389 CallingConvention.Stdcall => "stdcallcc ",
390 CallingConvention.Async => unreachable,
391 };
392 }
393
394 pub fn paramCount(self: *Fn) usize {
395 return switch (self.key.data) {
396 Kind.Generic => |generic| generic.param_count,
397 Kind.Normal => |normal| normal.params.len,
398 };
399 }
400
401 /// takes ownership of key.Normal.params on success
402 pub async fn get(comp: *Compilation, key: Key) !*Fn {
403 {
404 const held = await (async comp.fn_type_table.acquire() catch unreachable);
405 defer held.release();
406
407 if (held.value.get(&key)) |entry| {
408 entry.value.base.base.ref();
409 return entry.value;
410 }
411 }
412
413 key.ref();
414 errdefer key.deref(comp);
415
416 const self = try comp.gpa().createOne(Fn);
417 self.* = Fn{
235 .base = undefined,418 .base = undefined,
236 .return_type = return_type,419 .key = key,
237 .params = params,420 .non_key = undefined,
238 .is_var_args = is_var_args,421 .garbage_node = undefined,
239 });422 };
240 errdefer comp.gpa().destroy(result);423 errdefer comp.gpa().destroy(self);
424
425 var name_buf = try std.Buffer.initSize(comp.gpa(), 0);
426 defer name_buf.deinit();
427
428 const name_stream = &std.io.BufferOutStream.init(&name_buf).stream;
429
430 switch (key.data) {
431 Kind.Generic => |generic| {
432 self.non_key = NonKey{ .Generic = {} };
433 switch (generic.cc) {
434 CallingConvention.Async => |async_allocator_type| {
435 try name_stream.print("async<{}> ", async_allocator_type.name);
436 },
437 else => {
438 const cc_str = ccFnTypeStr(generic.cc);
439 try name_stream.write(cc_str);
440 },
441 }
442 try name_stream.write("fn(");
443 var param_i: usize = 0;
444 while (param_i < generic.param_count) : (param_i += 1) {
445 const arg = if (param_i == 0) "var" else ", var";
446 try name_stream.write(arg);
447 }
448 try name_stream.write(")");
449 if (key.alignment) |alignment| {
450 try name_stream.print(" align<{}>", alignment);
451 }
452 try name_stream.write(" var");
453 },
454 Kind.Normal => |normal| {
455 self.non_key = NonKey{
456 .Normal = NonKey.Normal{ .variable_list = std.ArrayList(*Scope.Var).init(comp.gpa()) },
457 };
458 const cc_str = ccFnTypeStr(normal.cc);
459 try name_stream.print("{}fn(", cc_str);
460 for (normal.params) |param, i| {
461 if (i != 0) try name_stream.write(", ");
462 if (param.is_noalias) try name_stream.write("noalias ");
463 try name_stream.write(param.typ.name);
464 }
465 if (normal.is_var_args) {
466 if (normal.params.len != 0) try name_stream.write(", ");
467 try name_stream.write("...");
468 }
469 try name_stream.write(")");
470 if (key.alignment) |alignment| {
471 try name_stream.print(" align<{}>", alignment);
472 }
473 try name_stream.print(" {}", normal.return_type.name);
474 },
475 }
241476
242 result.base.init(comp, Id.Fn, "TODO fn type name");477 self.base.init(comp, Id.Fn, name_buf.toOwnedSlice());
243478
244 result.return_type.base.ref();479 {
245 for (result.params) |param| {480 const held = await (async comp.fn_type_table.acquire() catch unreachable);
246 param.typ.base.ref();481 defer held.release();
482
483 _ = try held.value.put(&self.key, self);
247 }484 }
248 return result;485 return self;
249 }486 }
250487
251 pub fn destroy(self: *Fn, comp: *Compilation) void {488 pub fn destroy(self: *Fn, comp: *Compilation) void {
252 self.return_type.base.deref(comp);489 self.key.deref(comp);
253 for (self.params) |param| {490 switch (self.key.data) {
254 param.typ.base.deref(comp);491 Kind.Generic => {},
492 Kind.Normal => {
493 self.non_key.Normal.variable_list.deinit();
494 },
255 }495 }
256 comp.gpa().destroy(self);496 comp.gpa().destroy(self);
257 }497 }
258498
259 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {499 pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
260 const llvm_return_type = switch (self.return_type.id) {500 const normal = &self.key.data.Normal;
501 const llvm_return_type = switch (normal.return_type.id) {
261 Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,502 Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
262 else => try self.return_type.getLlvmType(allocator, llvm_context),503 else => try normal.return_type.getLlvmType(allocator, llvm_context),
263 };504 };
264 const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);505 const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len);
265 defer allocator.free(llvm_param_types);506 defer allocator.free(llvm_param_types);
266 for (llvm_param_types) |*llvm_param_type, i| {507 for (llvm_param_types) |*llvm_param_type, i| {
267 llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);508 llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context);
268 }509 }
269510
270 return llvm.FunctionType(511 return llvm.FunctionType(
271 llvm_return_type,512 llvm_return_type,
272 llvm_param_types.ptr,513 llvm_param_types.ptr,
273 @intCast(c_uint, llvm_param_types.len),514 @intCast(c_uint, llvm_param_types.len),
274 @boolToInt(self.is_var_args),515 @boolToInt(normal.is_var_args),
275 ) orelse error.OutOfMemory;516 ) orelse error.OutOfMemory;
276 }517 }
277 };518 };
...@@ -347,8 +588,10 @@ pub const Type = struct {...@@ -347,8 +588,10 @@ pub const Type = struct {
347 is_signed: bool,588 is_signed: bool,
348589
349 pub fn hash(self: *const Key) u32 {590 pub fn hash(self: *const Key) u32 {
350 const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };591 var result: u32 = 0;
351 return rands[@boolToInt(self.is_signed)] *% self.bit_count;592 result +%= hashAny(self.is_signed, 0);
593 result +%= hashAny(self.bit_count, 1);
594 return result;
352 }595 }
353596
354 pub fn eql(self: *const Key, other: *const Key) bool {597 pub fn eql(self: *const Key, other: *const Key) bool {
...@@ -443,15 +686,16 @@ pub const Type = struct {...@@ -443,15 +686,16 @@ pub const Type = struct {
443 alignment: Align,686 alignment: Align,
444687
445 pub fn hash(self: *const Key) u32 {688 pub fn hash(self: *const Key) u32 {
446 const align_hash = switch (self.alignment) {689 var result: u32 = 0;
690 result +%= switch (self.alignment) {
447 Align.Abi => 0xf201c090,691 Align.Abi => 0xf201c090,
448 Align.Override => |x| x,692 Align.Override => |x| hashAny(x, 0),
449 };693 };
450 return hash_usize(@ptrToInt(self.child_type)) *%694 result +%= hashAny(self.child_type, 1);
451 hash_enum(self.mut) *%695 result +%= hashAny(self.mut, 2);
452 hash_enum(self.vol) *%696 result +%= hashAny(self.vol, 3);
453 hash_enum(self.size) *%697 result +%= hashAny(self.size, 4);
454 align_hash;698 return result;
455 }699 }
456700
457 pub fn eql(self: *const Key, other: *const Key) bool {701 pub fn eql(self: *const Key, other: *const Key) bool {
...@@ -605,7 +849,10 @@ pub const Type = struct {...@@ -605,7 +849,10 @@ pub const Type = struct {
605 len: usize,849 len: usize,
606850
607 pub fn hash(self: *const Key) u32 {851 pub fn hash(self: *const Key) u32 {
608 return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);852 var result: u32 = 0;
853 result +%= hashAny(self.elem_type, 0);
854 result +%= hashAny(self.len, 1);
855 return result;
609 }856 }
610857
611 pub fn eql(self: *const Key, other: *const Key) bool {858 pub fn eql(self: *const Key, other: *const Key) bool {
...@@ -818,27 +1065,37 @@ pub const Type = struct {...@@ -818,27 +1065,37 @@ pub const Type = struct {
818 };1065 };
819};1066};
8201067
821fn hash_usize(x: usize) u32 {1068fn hashAny(x: var, comptime seed: u64) u32 {
822 return switch (@sizeOf(usize)) {1069 switch (@typeInfo(@typeOf(x))) {
823 4 => x,1070 builtin.TypeId.Int => |info| {
824 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),1071 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
825 else => @compileError("implement this hash function"),1072 const unsigned_x = @bitCast(@IntType(false, info.bits), x);
826 };1073 if (info.bits <= 32) {
827}1074 return u32(unsigned_x) *% comptime rng.random.scalar(u32);
8281075 } else {
829fn hash_enum(x: var) u32 {1076 return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x)));
830 const rands = []u32{1077 }
831 0x85ebf64f,1078 },
832 0x3fcb3211,1079 builtin.TypeId.Pointer => |info| {
833 0x240a4e8e,1080 switch (info.size) {
834 0x40bb0e3c,1081 builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
835 0x78be45af,1082 builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
836 0x1ca98e37,1083 builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
837 0xec56053a,1084 }
838 0x906adc48,1085 },
839 0xd4fe9763,1086 builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),
840 0x54c80dac,1087 builtin.TypeId.Bool => {
841 };1088 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
842 comptime assert(@memberCount(@typeOf(x)) < rands.len);1089 const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) };
843 return rands[@enumToInt(x)];1090 return vals[@boolToInt(x)];
1091 },
1092 builtin.TypeId.Optional => {
1093 if (x) |non_opt| {
1094 return hashAny(non_opt, seed);
1095 } else {
1096 return hashAny(u32(1), seed);
1097 }
1098 },
1099 else => @compileError("implement hash function for " ++ @typeName(@typeOf(x))),
1100 }
844}1101}
src-self-hosted/value.zig+19-3
...@@ -60,7 +60,7 @@ pub const Value = struct {...@@ -60,7 +60,7 @@ pub const Value = struct {
60 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {60 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
61 switch (base.id) {61 switch (base.id) {
62 Id.Type => unreachable,62 Id.Type => unreachable,
63 Id.Fn => @panic("TODO"),63 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
64 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),64 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
65 Id.Void => return null,65 Id.Void => return null,
66 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),66 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
...@@ -180,7 +180,7 @@ pub const Value = struct {...@@ -180,7 +180,7 @@ pub const Value = struct {
180 child_scope: *Scope,180 child_scope: *Scope,
181181
182 /// parent is child_scope182 /// parent is child_scope
183 block_scope: *Scope.Block,183 block_scope: ?*Scope.Block,
184184
185 /// Path to the object file that contains this function185 /// Path to the object file that contains this function
186 containing_object: Buffer,186 containing_object: Buffer,
...@@ -205,7 +205,7 @@ pub const Value = struct {...@@ -205,7 +205,7 @@ pub const Value = struct {
205 },205 },
206 .fndef_scope = fndef_scope,206 .fndef_scope = fndef_scope,
207 .child_scope = &fndef_scope.base,207 .child_scope = &fndef_scope.base,
208 .block_scope = undefined,208 .block_scope = null,
209 .symbol_name = symbol_name,209 .symbol_name = symbol_name,
210 .containing_object = Buffer.initNull(comp.gpa()),210 .containing_object = Buffer.initNull(comp.gpa()),
211 .link_set_node = link_set_node,211 .link_set_node = link_set_node,
...@@ -231,6 +231,22 @@ pub const Value = struct {...@@ -231,6 +231,22 @@ pub const Value = struct {
231 self.symbol_name.deinit();231 self.symbol_name.deinit();
232 comp.gpa().destroy(self);232 comp.gpa().destroy(self);
233 }233 }
234
235 /// We know that the function definition will end up in an .o file somewhere.
236 /// Here, all we have to do is generate a global prototype.
237 /// TODO cache the prototype per ObjectFile
238 pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef {
239 const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
240 const llvm_fn = llvm.AddFunction(
241 ofile.module,
242 self.symbol_name.ptr(),
243 llvm_fn_type,
244 ) orelse return error.OutOfMemory;
245
246 // TODO port more logic from codegen.cpp:fn_llvm_value
247
248 return llvm_fn;
249 }
234 };250 };
235251
236 pub const Void = struct {252 pub const Void = struct {
src/all_types.hpp+2-4
...@@ -60,7 +60,7 @@ struct IrExecutable {...@@ -60,7 +60,7 @@ struct IrExecutable {
60 ZigList<Tld *> tld_list;60 ZigList<Tld *> tld_list;
6161
62 IrInstruction *coro_handle;62 IrInstruction *coro_handle;
63 IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise63 IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise
64 IrInstruction *coro_result_ptr_field_ptr;64 IrInstruction *coro_result_ptr_field_ptr;
65 IrInstruction *coro_result_field_ptr;65 IrInstruction *coro_result_field_ptr;
66 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise66 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise
...@@ -898,7 +898,6 @@ struct AstNodeAwaitExpr {...@@ -898,7 +898,6 @@ struct AstNodeAwaitExpr {
898};898};
899899
900struct AstNodeSuspend {900struct AstNodeSuspend {
901 Buf *name;
902 AstNode *block;901 AstNode *block;
903 AstNode *promise_symbol;902 AstNode *promise_symbol;
904};903};
...@@ -1927,7 +1926,6 @@ struct ScopeLoop {...@@ -1927,7 +1926,6 @@ struct ScopeLoop {
1927struct ScopeSuspend {1926struct ScopeSuspend {
1928 Scope base;1927 Scope base;
19291928
1930 Buf *name;
1931 IrBasicBlock *resume_block;1929 IrBasicBlock *resume_block;
1932 bool reported_err;1930 bool reported_err;
1933};1931};
...@@ -3243,7 +3241,7 @@ static const size_t stack_trace_ptr_count = 30;...@@ -3243,7 +3241,7 @@ static const size_t stack_trace_ptr_count = 30;
3243#define RESULT_FIELD_NAME "result"3241#define RESULT_FIELD_NAME "result"
3244#define ASYNC_ALLOC_FIELD_NAME "allocFn"3242#define ASYNC_ALLOC_FIELD_NAME "allocFn"
3245#define ASYNC_FREE_FIELD_NAME "freeFn"3243#define ASYNC_FREE_FIELD_NAME "freeFn"
3246#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"3244#define ATOMIC_STATE_FIELD_NAME "atomic_state"
3247// these point to data belonging to the awaiter3245// these point to data belonging to the awaiter
3248#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"3246#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
3249#define RESULT_PTR_FIELD_NAME "result_ptr"3247#define RESULT_PTR_FIELD_NAME "result_ptr"
src/analyze.cpp+21-17
...@@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {...@@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
161 assert(node->type == NodeTypeSuspend);161 assert(node->type == NodeTypeSuspend);
162 ScopeSuspend *scope = allocate<ScopeSuspend>(1);162 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
163 init_scope(&scope->base, ScopeIdSuspend, node, parent);163 init_scope(&scope->base, ScopeIdSuspend, node, parent);
164 scope->name = node->data.suspend.name;
165 return scope;164 return scope;
166}165}
167166
...@@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)...@@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
519 return return_type->promise_frame_parent;518 return return_type->promise_frame_parent;
520 }519 }
521520
522 TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise);521 TypeTableEntry *atomic_state_type = g->builtin_types.entry_usize;
523 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);522 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
524523
525 ZigList<const char *> field_names = {};524 ZigList<const char *> field_names = {};
526 field_names.append(AWAITER_HANDLE_FIELD_NAME);525 field_names.append(ATOMIC_STATE_FIELD_NAME);
527 field_names.append(RESULT_FIELD_NAME);526 field_names.append(RESULT_FIELD_NAME);
528 field_names.append(RESULT_PTR_FIELD_NAME);527 field_names.append(RESULT_PTR_FIELD_NAME);
529 if (g->have_err_ret_tracing) {528 if (g->have_err_ret_tracing) {
...@@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)...@@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
533 }532 }
534533
535 ZigList<TypeTableEntry *> field_types = {};534 ZigList<TypeTableEntry *> field_types = {};
536 field_types.append(awaiter_handle_type);535 field_types.append(atomic_state_type);
537 field_types.append(return_type);536 field_types.append(return_type);
538 field_types.append(result_ptr_type);537 field_types.append(result_ptr_type);
539 if (g->have_err_ret_tracing) {538 if (g->have_err_ret_tracing) {
...@@ -1585,10 +1584,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1585,10 +1584,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1585 case TypeTableEntryIdBlock:1584 case TypeTableEntryIdBlock:
1586 case TypeTableEntryIdBoundFn:1585 case TypeTableEntryIdBoundFn:
1587 case TypeTableEntryIdMetaType:1586 case TypeTableEntryIdMetaType:
1588 add_node_error(g, param_node->data.param_decl.type,
1589 buf_sprintf("parameter of type '%s' must be declared comptime",
1590 buf_ptr(&type_entry->name)));
1591 return g->builtin_types.entry_invalid;
1592 case TypeTableEntryIdVoid:1587 case TypeTableEntryIdVoid:
1593 case TypeTableEntryIdBool:1588 case TypeTableEntryIdBool:
1594 case TypeTableEntryIdInt:1589 case TypeTableEntryIdInt:
...@@ -1603,6 +1598,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c...@@ -1603,6 +1598,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
1603 case TypeTableEntryIdUnion:1598 case TypeTableEntryIdUnion:
1604 case TypeTableEntryIdFn:1599 case TypeTableEntryIdFn:
1605 case TypeTableEntryIdPromise:1600 case TypeTableEntryIdPromise:
1601 type_ensure_zero_bits_known(g, type_entry);
1602 if (type_requires_comptime(type_entry)) {
1603 add_node_error(g, param_node->data.param_decl.type,
1604 buf_sprintf("parameter of type '%s' must be declared comptime",
1605 buf_ptr(&type_entry->name)));
1606 return g->builtin_types.entry_invalid;
1607 }
1606 break;1608 break;
1607 }1609 }
1608 FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];1610 FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index];
...@@ -3938,7 +3940,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) {...@@ -3938,7 +3940,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) {
3938 return nullptr;3940 return nullptr;
3939}3941}
39403942
3941static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) {3943static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry) {
3942 TypeTableEntry *fn_type = fn_table_entry->type_entry;3944 TypeTableEntry *fn_type = fn_table_entry->type_entry;
3943 assert(!fn_type->data.fn.is_generic);3945 assert(!fn_type->data.fn.is_generic);
3944 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;3946 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
...@@ -3976,10 +3978,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr...@@ -3976,10 +3978,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr
3976 if (fn_type->data.fn.gen_param_info) {3978 if (fn_type->data.fn.gen_param_info) {
3977 var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;3979 var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
3978 }3980 }
3979
3980 if (arg_vars) {
3981 arg_vars[i] = var;
3982 }
3983 }3981 }
3984}3982}
39853983
...@@ -4057,7 +4055,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ...@@ -4057,7 +4055,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
4057 }4055 }
40584056
4059 if (g->verbose_ir) {4057 if (g->verbose_ir) {
4060 fprintf(stderr, "{ // (analyzed)\n");4058 fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name));
4061 ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);4059 ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);
4062 fprintf(stderr, "}\n");4060 fprintf(stderr, "}\n");
4063 }4061 }
...@@ -4079,7 +4077,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -4079,7 +4077,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
4079 if (!fn_table_entry->child_scope)4077 if (!fn_table_entry->child_scope)
4080 fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;4078 fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base;
40814079
4082 define_local_param_variables(g, fn_table_entry, nullptr);4080 define_local_param_variables(g, fn_table_entry);
40834081
4084 TypeTableEntry *fn_type = fn_table_entry->type_entry;4082 TypeTableEntry *fn_type = fn_table_entry->type_entry;
4085 assert(!fn_type->data.fn.is_generic);4083 assert(!fn_type->data.fn.is_generic);
...@@ -5019,9 +5017,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {...@@ -5019,9 +5017,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
5019 } else {5017 } else {
5020 return type_requires_comptime(type_entry->data.pointer.child_type);5018 return type_requires_comptime(type_entry->data.pointer.child_type);
5021 }5019 }
5020 case TypeTableEntryIdFn:
5021 return type_entry->data.fn.is_generic;
5022 case TypeTableEntryIdEnum:5022 case TypeTableEntryIdEnum:
5023 case TypeTableEntryIdErrorSet:5023 case TypeTableEntryIdErrorSet:
5024 case TypeTableEntryIdFn:
5025 case TypeTableEntryIdBool:5024 case TypeTableEntryIdBool:
5026 case TypeTableEntryIdInt:5025 case TypeTableEntryIdInt:
5027 case TypeTableEntryIdFloat:5026 case TypeTableEntryIdFloat:
...@@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {...@@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
6228 } else if (type_entry->id == TypeTableEntryIdOpaque) {6227 } else if (type_entry->id == TypeTableEntryIdOpaque) {
6229 return 1;6228 return 1;
6230 } else {6229 } else {
6231 return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref);6230 uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref);
6231 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
6232 if (type_entry->id == TypeTableEntryIdPromise && llvm_alignment < 8) {
6233 return 8;
6234 }
6235 return llvm_alignment;
6232 }6236 }
6233}6237}
62346238
src/ir.cpp+371-101
...@@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode...@@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
3097 return return_inst;3097 return return_inst;
3098 }3098 }
30993099
3100 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);3100 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended");
3101 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,3101 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended");
3102 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));3102 IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter");
3103 // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig3103 IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled");
3104 IrInstruction *replacement_value = irb->exec->coro_handle;3104
3105 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,3105 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
3106 promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr,3106 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
3107 AtomicRmwOp_xchg, AtomicOrderSeqCst);3107 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
3108 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle);3108 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
3109 IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle);3109 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
3110 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);3110 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
3111 return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final,3111 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
3112 is_comptime);3112
3113 // the above blocks are rendered by ir_gen after the rest of codegen3113 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
3114 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
3115 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
3116 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, ptr_mask, nullptr,
3117 AtomicRmwOp_or, AtomicOrderSeqCst);
3118
3119 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
3120 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
3121 ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
3122
3123 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
3124 ir_build_unreachable(irb, scope, node);
3125
3126 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
3127 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
3128 // if we ever add null checking safety to the ptrtoint instruction, it needs to be disabled here
3129 IrInstruction *have_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
3130 ir_build_cond_br(irb, scope, node, have_await_handle, store_awaiter_block, check_canceled_block, is_comptime);
3131
3132 ir_set_cursor_at_end_and_append_block(irb, store_awaiter_block);
3133 IrInstruction *await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, await_handle_addr);
3134 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, await_handle);
3135 ir_build_br(irb, scope, node, irb->exec->coro_normal_final, is_comptime);
3136
3137 ir_set_cursor_at_end_and_append_block(irb, check_canceled_block);
3138 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
3139 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
3140 return ir_build_cond_br(irb, scope, node, is_canceled_bool, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, is_comptime);
3114}3141}
31153142
3116static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {3143static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
...@@ -5251,8 +5278,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -5251,8 +5278,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
5251 if (body_result == irb->codegen->invalid_instruction)5278 if (body_result == irb->codegen->invalid_instruction)
5252 return body_result;5279 return body_result;
52535280
5254 if (!instr_is_unreachable(body_result))5281 if (!instr_is_unreachable(body_result)) {
5282 ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result));
5255 ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));5283 ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
5284 }
52565285
5257 if (continue_expr_node) {5286 if (continue_expr_node) {
5258 ir_set_cursor_at_end_and_append_block(irb, continue_block);5287 ir_set_cursor_at_end_and_append_block(irb, continue_block);
...@@ -5331,8 +5360,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -5331,8 +5360,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
5331 if (body_result == irb->codegen->invalid_instruction)5360 if (body_result == irb->codegen->invalid_instruction)
5332 return body_result;5361 return body_result;
53335362
5334 if (!instr_is_unreachable(body_result))5363 if (!instr_is_unreachable(body_result)) {
5364 ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result));
5335 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));5365 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
5366 }
53365367
5337 if (continue_expr_node) {5368 if (continue_expr_node) {
5338 ir_set_cursor_at_end_and_append_block(irb, continue_block);5369 ir_set_cursor_at_end_and_append_block(irb, continue_block);
...@@ -5392,8 +5423,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n...@@ -5392,8 +5423,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
5392 if (body_result == irb->codegen->invalid_instruction)5423 if (body_result == irb->codegen->invalid_instruction)
5393 return body_result;5424 return body_result;
53945425
5395 if (!instr_is_unreachable(body_result))5426 if (!instr_is_unreachable(body_result)) {
5427 ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result));
5396 ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));5428 ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
5429 }
53975430
5398 if (continue_expr_node) {5431 if (continue_expr_node) {
5399 ir_set_cursor_at_end_and_append_block(irb, continue_block);5432 ir_set_cursor_at_end_and_append_block(irb, continue_block);
...@@ -6153,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop...@@ -6153,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop
6153 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);6186 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
6154}6187}
61556188
6156static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) {
6157 IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false);
6158
6159 IrBasicBlock *dest_block = suspend_scope->resume_block;
6160 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
6161
6162 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
6163}
6164
6165static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {6189static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
6166 assert(node->type == NodeTypeBreak);6190 assert(node->type == NodeTypeBreak);
61676191
...@@ -6202,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *...@@ -6202,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
6202 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);6226 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
6203 }6227 }
6204 } else if (search_scope->id == ScopeIdSuspend) {6228 } else if (search_scope->id == ScopeIdSuspend) {
6205 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;6229 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
6206 if (node->data.break_expr.name != nullptr &&6230 return irb->codegen->invalid_instruction;
6207 (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name)))
6208 {
6209 return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope);
6210 }
6211 }6231 }
6212 search_scope = search_scope->parent;6232 search_scope = search_scope->parent;
6213 }6233 }
...@@ -6643,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo...@@ -6643,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
6643 async_allocator_type_value, is_var_args);6663 async_allocator_type_value, is_var_args);
6644}6664}
66456665
6646static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6666static IrInstruction *ir_gen_cancel_target(IrBuilder *irb, Scope *scope, AstNode *node,
6667 IrInstruction *target_inst, bool cancel_non_suspended, bool cancel_awaited)
6668{
6669 IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "CancelDone");
6670 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6671 IrBasicBlock *pre_return_block = ir_create_basic_block(irb, scope, "PreReturn");
6672 IrBasicBlock *post_return_block = ir_create_basic_block(irb, scope, "PostReturn");
6673 IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
6674
6675 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6676 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6677 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
6678 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6679 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
6680 get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
6681 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
6682 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
6683 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
6684 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6685
6686 // TODO relies on Zig not re-ordering fields
6687 IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst);
6688 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
6689 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6690 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6691 atomic_state_field_name);
6692
6693 // set the is_canceled bit
6694 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6695 usize_type_val, atomic_state_ptr, nullptr, is_canceled_mask, nullptr,
6696 AtomicRmwOp_or, AtomicOrderSeqCst);
6697
6698 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6699 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6700 ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
6701
6702 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6703 IrInstruction *awaiter_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
6704 IrInstruction *is_returned_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, awaiter_addr, ptr_mask, false);
6705 ir_build_cond_br(irb, scope, node, is_returned_bool, post_return_block, pre_return_block, is_comptime);
6706
6707 ir_set_cursor_at_end_and_append_block(irb, post_return_block);
6708 if (cancel_awaited) {
6709 ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
6710 } else {
6711 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6712 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6713 ir_build_cond_br(irb, scope, node, is_awaited_bool, done_block, do_cancel_block, is_comptime);
6714 }
6715
6716 ir_set_cursor_at_end_and_append_block(irb, pre_return_block);
6717 if (cancel_awaited) {
6718 if (cancel_non_suspended) {
6719 ir_build_br(irb, scope, node, do_cancel_block, is_comptime);
6720 } else {
6721 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
6722 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
6723 ir_build_cond_br(irb, scope, node, is_suspended_bool, do_cancel_block, done_block, is_comptime);
6724 }
6725 } else {
6726 ir_build_br(irb, scope, node, done_block, is_comptime);
6727 }
6728
6729 ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
6730 ir_build_cancel(irb, scope, node, target_inst);
6731 ir_build_br(irb, scope, node, done_block, is_comptime);
6732
6733 ir_set_cursor_at_end_and_append_block(irb, done_block);
6734 return ir_build_const_void(irb, scope, node);
6735}
6736
6737static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) {
6647 assert(node->type == NodeTypeCancel);6738 assert(node->type == NodeTypeCancel);
66486739
6649 IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope);6740 IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope);
6650 if (target_inst == irb->codegen->invalid_instruction)6741 if (target_inst == irb->codegen->invalid_instruction)
6651 return irb->codegen->invalid_instruction;6742 return irb->codegen->invalid_instruction;
66526743
6653 return ir_build_cancel(irb, parent_scope, node, target_inst);6744 return ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
6745}
6746
6747static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node,
6748 IrInstruction *target_inst)
6749{
6750 IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone");
6751 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6752 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended");
6753 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended");
6754
6755 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6756 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6757 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6758 IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask);
6759 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false);
6760 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6761 IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node,
6762 get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void));
6763
6764 // TODO relies on Zig not re-ordering fields
6765 IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst);
6766 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst);
6767 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6768 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6769 atomic_state_field_name);
6770
6771 // clear the is_suspended bit
6772 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6773 usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr,
6774 AtomicRmwOp_and, AtomicOrderSeqCst);
6775
6776 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6777 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6778 ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime);
6779
6780 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6781 IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
6782 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
6783 ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime);
6784
6785 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
6786 ir_build_unreachable(irb, scope, node);
6787
6788 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
6789 ir_build_coro_resume(irb, scope, node, target_inst);
6790 ir_build_br(irb, scope, node, done_block, is_comptime);
6791
6792 ir_set_cursor_at_end_and_append_block(irb, done_block);
6793 return ir_build_const_void(irb, scope, node);
6654}6794}
66556795
6656static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6796static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
6657 assert(node->type == NodeTypeResume);6797 assert(node->type == NodeTypeResume);
66586798
6659 IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, parent_scope);6799 IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope);
6660 if (target_inst == irb->codegen->invalid_instruction)6800 if (target_inst == irb->codegen->invalid_instruction)
6661 return irb->codegen->invalid_instruction;6801 return irb->codegen->invalid_instruction;
66626802
6663 return ir_build_coro_resume(irb, parent_scope, node, target_inst);6803 return ir_gen_resume_target(irb, scope, node, target_inst);
6664}6804}
66656805
6666static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6806static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
6667 assert(node->type == NodeTypeAwaitExpr);6807 assert(node->type == NodeTypeAwaitExpr);
66686808
6669 IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope);6809 IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope);
6670 if (target_inst == irb->codegen->invalid_instruction)6810 if (target_inst == irb->codegen->invalid_instruction)
6671 return irb->codegen->invalid_instruction;6811 return irb->codegen->invalid_instruction;
66726812
...@@ -6680,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6680,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
6680 return irb->codegen->invalid_instruction;6820 return irb->codegen->invalid_instruction;
6681 }6821 }
66826822
6683 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);6823 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
6684 if (scope_defer_expr) {6824 if (scope_defer_expr) {
6685 if (!scope_defer_expr->reported_err) {6825 if (!scope_defer_expr->reported_err) {
6686 add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression"));6826 add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression"));
...@@ -6691,81 +6831,157 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6691,81 +6831,157 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
66916831
6692 Scope *outer_scope = irb->exec->begin_scope;6832 Scope *outer_scope = irb->exec->begin_scope;
66936833
6694 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, parent_scope, node, target_inst);6834 IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst);
6695 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);6835 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
6696 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name);6836 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
66976837
6698 if (irb->codegen->have_err_ret_tracing) {6838 if (irb->codegen->have_err_ret_tracing) {
6699 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);6839 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
6700 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);6840 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
6701 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);6841 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6702 ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);6842 ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6703 }6843 }
67046844
6705 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);6845 IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited");
6706 IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,6846 IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited");
6707 awaiter_handle_field_name);6847 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
67086848 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend");
6709 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);6849 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend");
6710 VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr,6850 IrBasicBlock *merge_block = ir_create_basic_block(irb, scope, "MergeSuspend");
6851 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, scope, "SuspendCleanup");
6852 IrBasicBlock *resume_block = ir_create_basic_block(irb, scope, "SuspendResume");
6853 IrBasicBlock *cancel_target_block = ir_create_basic_block(irb, scope, "CancelTarget");
6854 IrBasicBlock *do_cancel_block = ir_create_basic_block(irb, scope, "DoCancel");
6855 IrBasicBlock *do_defers_block = ir_create_basic_block(irb, scope, "DoDefers");
6856 IrBasicBlock *destroy_block = ir_create_basic_block(irb, scope, "DestroyBlock");
6857 IrBasicBlock *my_suspended_block = ir_create_basic_block(irb, scope, "AlreadySuspended");
6858 IrBasicBlock *my_not_suspended_block = ir_create_basic_block(irb, scope, "NotAlreadySuspended");
6859 IrBasicBlock *do_suspend_block = ir_create_basic_block(irb, scope, "DoSuspend");
6860
6861 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
6862 IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6863 atomic_state_field_name);
6864
6865 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
6866 IrInstruction *const_bool_false = ir_build_const_bool(irb, scope, node, false);
6867 IrInstruction *undefined_value = ir_build_const_undefined(irb, scope, node);
6868 IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize);
6869 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6870 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
6871 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
6872 IrInstruction *await_mask = ir_build_const_usize(irb, scope, node, 0x4); // 0b100
6873 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
6874 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
6875
6876 VariableTableEntry *result_var = ir_create_var(irb, node, scope, nullptr,
6711 false, false, true, const_bool_false);6877 false, false, true, const_bool_false);
6712 IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node);6878 IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst);
6713 IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst);6879 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type);
6714 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type);6880 ir_build_await_bookkeeping(irb, scope, node, promise_result_type);
6715 ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type);6881 ir_build_var_decl(irb, scope, node, result_var, promise_result_type, nullptr, undefined_value);
6716 ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value);6882 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, scope, node, result_var);
6717 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var);6883 ir_build_store_ptr(irb, scope, node, result_ptr_field_ptr, my_result_var_ptr);
6718 ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr);6884 IrInstruction *save_token = ir_build_coro_save(irb, scope, node, irb->exec->coro_handle);
6719 IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle);6885
6720 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node,6886 IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
6721 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));6887 IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
6722 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node, 6888 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6723 promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr,6889 usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
6724 AtomicRmwOp_xchg, AtomicOrderSeqCst);6890 AtomicRmwOp_or, AtomicOrderSeqCst);
6725 IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);6891
6726 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");6892 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6727 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");6893 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6728 IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend");6894 ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_block, const_bool_false);
6729 ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);6895
6896 ir_set_cursor_at_end_and_append_block(irb, already_awaited_block);
6897 ir_build_unreachable(irb, scope, node);
6898
6899 ir_set_cursor_at_end_and_append_block(irb, not_awaited_block);
6900 IrInstruction *await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
6901 IrInstruction *is_non_null = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
6902 IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
6903 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
6904 ir_build_cond_br(irb, scope, node, is_canceled_bool, cancel_target_block, not_canceled_block, const_bool_false);
6905
6906 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
6907 ir_build_cond_br(irb, scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
6908
6909 ir_set_cursor_at_end_and_append_block(irb, cancel_target_block);
6910 ir_build_cancel(irb, scope, node, target_inst);
6911 ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
67306912
6731 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);6913 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
6732 if (irb->codegen->have_err_ret_tracing) {6914 if (irb->codegen->have_err_ret_tracing) {
6733 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);6915 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
6734 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name);6916 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
6735 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);6917 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
6736 ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);6918 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
6737 }6919 }
6738 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);6920 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
6739 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);6921 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
6740 // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,6922 // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
6741 // because we're about to destroy the memory. So we store it into our result variable.6923 // because we're about to destroy the memory. So we store it into our result variable.
6742 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);6924 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr);
6743 ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result);6925 ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result);
6744 ir_build_cancel(irb, parent_scope, node, target_inst);6926 ir_build_cancel(irb, scope, node, target_inst);
6745 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);6927 ir_build_br(irb, scope, node, merge_block, const_bool_false);
6928
67466929
6747 ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);6930 ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block);
6748 IrInstruction *suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false);6931 IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6749 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");6932 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
6750 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");6933 AtomicRmwOp_or, AtomicOrderSeqCst);
6934 IrInstruction *my_is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_suspended_mask, false);
6935 IrInstruction *my_is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_suspended_value, zero, false);
6936 ir_build_cond_br(irb, scope, node, my_is_suspended_bool, my_suspended_block, my_not_suspended_block, const_bool_false);
6937
6938 ir_set_cursor_at_end_and_append_block(irb, my_suspended_block);
6939 ir_build_unreachable(irb, scope, node);
6940
6941 ir_set_cursor_at_end_and_append_block(irb, my_not_suspended_block);
6942 IrInstruction *my_is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, my_prev_atomic_value, is_canceled_mask, false);
6943 IrInstruction *my_is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, my_is_canceled_value, zero, false);
6944 ir_build_cond_br(irb, scope, node, my_is_canceled_bool, cleanup_block, do_suspend_block, const_bool_false);
6945
6946 ir_set_cursor_at_end_and_append_block(irb, do_suspend_block);
6947 IrInstruction *suspend_code = ir_build_coro_suspend(irb, scope, node, save_token, const_bool_false);
67516948
6752 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);6949 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
6753 cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0);6950 cases[0].value = ir_build_const_u8(irb, scope, node, 0);
6754 cases[0].block = resume_block;6951 cases[0].block = resume_block;
6755 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);6952 cases[1].value = ir_build_const_u8(irb, scope, node, 1);
6756 cases[1].block = cleanup_block;6953 cases[1].block = destroy_block;
6757 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6954 ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block,
6758 2, cases, const_bool_false, nullptr);6955 2, cases, const_bool_false, nullptr);
67596956
6957 ir_set_cursor_at_end_and_append_block(irb, destroy_block);
6958 ir_gen_cancel_target(irb, scope, node, target_inst, false, true);
6959 ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false));
6960
6760 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6961 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6761 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6962 IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, false);
6762 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));6963 IrInstruction *b_my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6964 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, my_mask_bits, nullptr,
6965 AtomicRmwOp_or, AtomicOrderSeqCst);
6966 IrInstruction *my_await_handle_addr = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, b_my_prev_atomic_value, ptr_mask, false);
6967 IrInstruction *dont_have_my_await_handle = ir_build_bin_op(irb, scope, node, IrBinOpCmpEq, my_await_handle_addr, zero, false);
6968 IrInstruction *dont_destroy_ourselves = ir_build_bin_op(irb, scope, node, IrBinOpBoolAnd, dont_have_my_await_handle, is_canceled_bool, false);
6969 ir_build_cond_br(irb, scope, node, dont_have_my_await_handle, do_defers_block, do_cancel_block, const_bool_false);
6970
6971 ir_set_cursor_at_end_and_append_block(irb, do_cancel_block);
6972 IrInstruction *my_await_handle = ir_build_int_to_ptr(irb, scope, node, promise_type_val, my_await_handle_addr);
6973 ir_gen_cancel_target(irb, scope, node, my_await_handle, true, false);
6974 ir_mark_gen(ir_build_br(irb, scope, node, do_defers_block, const_bool_false));
6975
6976 ir_set_cursor_at_end_and_append_block(irb, do_defers_block);
6977 ir_gen_defers_for_block(irb, scope, outer_scope, true);
6978 ir_mark_gen(ir_build_cond_br(irb, scope, node, dont_destroy_ourselves, irb->exec->coro_early_final, irb->exec->coro_final_cleanup_block, const_bool_false));
67636979
6764 ir_set_cursor_at_end_and_append_block(irb, resume_block);6980 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6765 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);6981 ir_build_br(irb, scope, node, merge_block, const_bool_false);
67666982
6767 ir_set_cursor_at_end_and_append_block(irb, merge_block);6983 ir_set_cursor_at_end_and_append_block(irb, merge_block);
6768 return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);6984 return ir_build_load_ptr(irb, scope, node, my_result_var_ptr);
6769}6985}
67706986
6771static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {6987static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
...@@ -6804,9 +7020,52 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6804,9 +7020,52 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
68047020
6805 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");7021 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6806 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");7022 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
68077023 IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended");
6808 IrInstruction *suspend_code;7024 IrBasicBlock *canceled_block = ir_create_basic_block(irb, parent_scope, "IsCanceled");
7025 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, parent_scope, "NotCanceled");
7026 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, parent_scope, "NotAlreadySuspended");
7027 IrBasicBlock *cancel_awaiter_block = ir_create_basic_block(irb, parent_scope, "CancelAwaiter");
7028
7029 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_promise);
7030 IrInstruction *const_bool_true = ir_build_const_bool(irb, parent_scope, node, true);
6809 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);7031 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
7032 IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize);
7033 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001
7034 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010
7035 IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0);
7036 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111
7037 IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
7038
7039 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node,
7040 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
7041 AtomicRmwOp_or, AtomicOrderSeqCst);
7042
7043 IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false);
7044 IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false);
7045 ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false);
7046
7047 ir_set_cursor_at_end_and_append_block(irb, canceled_block);
7048 IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false);
7049 IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false);
7050 IrBasicBlock *post_canceled_block = irb->current_basic_block;
7051 ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false);
7052
7053 ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block);
7054 IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr);
7055 ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false);
7056 IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block;
7057 ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false);
7058
7059 ir_set_cursor_at_end_and_append_block(irb, not_canceled_block);
7060 IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false);
7061 IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false);
7062 ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false);
7063
7064 ir_set_cursor_at_end_and_append_block(irb, suspended_block);
7065 ir_build_unreachable(irb, parent_scope, node);
7066
7067 ir_set_cursor_at_end_and_append_block(irb, not_suspended_block);
7068 IrInstruction *suspend_code;
6810 if (node->data.suspend.block == nullptr) {7069 if (node->data.suspend.block == nullptr) {
6811 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);7070 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
6812 } else {7071 } else {
...@@ -6834,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6834,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6834 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));7093 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
6835 cases[0].block = resume_block;7094 cases[0].block = resume_block;
6836 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));7095 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6837 cases[1].block = cleanup_block;7096 cases[1].block = canceled_block;
6838 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,7097 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6839 2, cases, const_bool_false, nullptr));7098 2, cases, const_bool_false, nullptr));
68407099
6841 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);7100 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
7101 IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
7102 IrInstruction **incoming_values = allocate<IrInstruction *>(2);
7103 incoming_blocks[0] = post_canceled_block;
7104 incoming_values[0] = const_bool_true;
7105 incoming_blocks[1] = post_cancel_awaiter_block;
7106 incoming_values[1] = const_bool_false;
7107 IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values);
6842 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);7108 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6843 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));7109 ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false));
68447110
6845 ir_set_cursor_at_end_and_append_block(irb, resume_block);7111 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6846 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));7112 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
...@@ -7081,10 +7347,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7081,10 +7347,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7081 IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);7347 IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
7082 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);7348 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
70837349
7084 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);7350 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
7085 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,7351 irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
7086 awaiter_handle_field_name);7352 atomic_state_field_name);
7087 ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value);7353 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
7354 ir_build_store_ptr(irb, scope, node, irb->exec->atomic_state_field_ptr, zero);
7088 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);7355 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
7089 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);7356 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
7090 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);7357 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
...@@ -7102,7 +7369,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7102,7 +7369,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7102 // coordinate with builtin.zig7369 // coordinate with builtin.zig
7103 Buf *index_name = buf_create_from_str("index");7370 Buf *index_name = buf_create_from_str("index");
7104 IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);7371 IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
7105 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
7106 ir_build_store_ptr(irb, scope, node, index_ptr, zero);7372 ir_build_store_ptr(irb, scope, node, index_ptr, zero);
71077373
7108 Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");7374 Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
...@@ -7225,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7225,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7225 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);7491 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
72267492
7227 ir_set_cursor_at_end_and_append_block(irb, resume_block);7493 ir_set_cursor_at_end_and_append_block(irb, resume_block);
7228 ir_build_coro_resume(irb, scope, node, awaiter_handle);7494 ir_gen_resume_target(irb, scope, node, awaiter_handle);
7229 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);7495 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
7230 }7496 }
72317497
...@@ -12142,7 +12408,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc...@@ -12142,7 +12408,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
12142 result_type = ira->codegen->builtin_types.entry_invalid;12408 result_type = ira->codegen->builtin_types.entry_invalid;
12143 } else if (type_requires_comptime(result_type)) {12409 } else if (type_requires_comptime(result_type)) {
12144 var_class_requires_const = true;12410 var_class_requires_const = true;
12145 if (!var->src_is_const && !is_comptime_var) {12411 if (!var->gen_is_const && !is_comptime_var) {
12146 ir_add_error_node(ira, source_node,12412 ir_add_error_node(ira, source_node,
12147 buf_sprintf("variable of type '%s' must be const or comptime",12413 buf_sprintf("variable of type '%s' must be const or comptime",
12148 buf_ptr(&result_type->name)));12414 buf_ptr(&result_type->name)));
...@@ -12591,6 +12857,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod...@@ -12591,6 +12857,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
12591 }12857 }
1259212858
12593 Buf *param_name = param_decl_node->data.param_decl.name;12859 Buf *param_name = param_decl_node->data.param_decl.name;
12860 if (!param_name) return false;
12594 if (!is_var_args) {12861 if (!is_var_args) {
12595 VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,12862 VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
12596 *child_scope, param_name, true, arg_val, nullptr);12863 *child_scope, param_name, true, arg_val, nullptr);
...@@ -18991,6 +19258,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,...@@ -18991,6 +19258,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
18991 return ira->codegen->builtin_types.entry_invalid;19258 return ira->codegen->builtin_types.entry_invalid;
18992 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {19259 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
18993 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;19260 TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
19261 if (type_is_invalid(payload_type)) {
19262 return ira->codegen->builtin_types.entry_invalid;
19263 }
18994 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,19264 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
18995 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,19265 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
18996 PtrLenSingle,19266 PtrLenSingle,
src/ir_print.cpp+4
...@@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {
45}45}
4646
47static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {47static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {
48 if (instruction == nullptr) {
49 fprintf(irp->f, "(null)");
50 return;
51 }
48 if (instruction->value.special != ConstValSpecialRuntime) {52 if (instruction->value.special != ConstValSpecialRuntime) {
49 ir_print_const_value(irp, &instruction->value);53 ir_print_const_value(irp, &instruction->value);
50 } else {54 } else {
src/parser.cpp+2-23
...@@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648}648}
649649
650/*650/*
651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
652*/652*/
653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;654 size_t orig_token_index = *token_index;
655655
656 Token *name_token = nullptr;656 Token *suspend_token = &pc->tokens->at(*token_index);
657 Token *token = &pc->tokens->at(*token_index);
658 if (token->id == TokenIdSymbol) {
659 *token_index += 1;
660 Token *colon_token = &pc->tokens->at(*token_index);
661 if (colon_token->id == TokenIdColon) {
662 *token_index += 1;
663 name_token = token;
664 token = &pc->tokens->at(*token_index);
665 } else if (mandatory) {
666 ast_expect_token(pc, colon_token, TokenIdColon);
667 zig_unreachable();
668 } else {
669 *token_index = orig_token_index;
670 return nullptr;
671 }
672 }
673
674 Token *suspend_token = token;
675 if (suspend_token->id == TokenIdKeywordSuspend) {657 if (suspend_token->id == TokenIdKeywordSuspend) {
676 *token_index += 1;658 *token_index += 1;
677 } else if (mandatory) {659 } else if (mandatory) {
...@@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b...@@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
693 }675 }
694676
695 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
699 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
700 ast_eat_token(pc, token_index, TokenIdBinOr);679 ast_eat_token(pc, token_index, TokenIdBinOr);
701 node->data.suspend.block = ast_parse_block(pc, token_index, true);680 node->data.suspend.block = ast_parse_block(pc, token_index, true);
std/build.zig+21
...@@ -807,6 +807,7 @@ pub const LibExeObjStep = struct {...@@ -807,6 +807,7 @@ pub const LibExeObjStep = struct {
807 disable_libc: bool,807 disable_libc: bool,
808 frameworks: BufSet,808 frameworks: BufSet,
809 verbose_link: bool,809 verbose_link: bool,
810 no_rosegment: bool,
810811
811 // zig only stuff812 // zig only stuff
812 root_src: ?[]const u8,813 root_src: ?[]const u8,
...@@ -874,6 +875,7 @@ pub const LibExeObjStep = struct {...@@ -874,6 +875,7 @@ pub const LibExeObjStep = struct {
874875
875 fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep {876 fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep {
876 var self = LibExeObjStep{877 var self = LibExeObjStep{
878 .no_rosegment = false,
877 .strip = false,879 .strip = false,
878 .builder = builder,880 .builder = builder,
879 .verbose_link = false,881 .verbose_link = false,
...@@ -914,6 +916,7 @@ pub const LibExeObjStep = struct {...@@ -914,6 +916,7 @@ pub const LibExeObjStep = struct {
914916
915 fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep {917 fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep {
916 var self = LibExeObjStep{918 var self = LibExeObjStep{
919 .no_rosegment = false,
917 .builder = builder,920 .builder = builder,
918 .name = name,921 .name = name,
919 .kind = kind,922 .kind = kind,
...@@ -953,6 +956,10 @@ pub const LibExeObjStep = struct {...@@ -953,6 +956,10 @@ pub const LibExeObjStep = struct {
953 return self;956 return self;
954 }957 }
955958
959 pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void {
960 self.no_rosegment = value;
961 }
962
956 fn computeOutFileNames(self: *LibExeObjStep) void {963 fn computeOutFileNames(self: *LibExeObjStep) void {
957 switch (self.kind) {964 switch (self.kind) {
958 Kind.Obj => {965 Kind.Obj => {
...@@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct {...@@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct {
1306 }1313 }
1307 }1314 }
13081315
1316 if (self.no_rosegment) {
1317 try zig_args.append("--no-rosegment");
1318 }
1319
1309 try builder.spawnChild(zig_args.toSliceConst());1320 try builder.spawnChild(zig_args.toSliceConst());
13101321
1311 if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) {1322 if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) {
...@@ -1598,6 +1609,7 @@ pub const TestStep = struct {...@@ -1598,6 +1609,7 @@ pub const TestStep = struct {
1598 include_dirs: ArrayList([]const u8),1609 include_dirs: ArrayList([]const u8),
1599 lib_paths: ArrayList([]const u8),1610 lib_paths: ArrayList([]const u8),
1600 object_files: ArrayList([]const u8),1611 object_files: ArrayList([]const u8),
1612 no_rosegment: bool,
16011613
1602 pub fn init(builder: *Builder, root_src: []const u8) TestStep {1614 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
1603 const step_name = builder.fmt("test {}", root_src);1615 const step_name = builder.fmt("test {}", root_src);
...@@ -1615,9 +1627,14 @@ pub const TestStep = struct {...@@ -1615,9 +1627,14 @@ pub const TestStep = struct {
1615 .include_dirs = ArrayList([]const u8).init(builder.allocator),1627 .include_dirs = ArrayList([]const u8).init(builder.allocator),
1616 .lib_paths = ArrayList([]const u8).init(builder.allocator),1628 .lib_paths = ArrayList([]const u8).init(builder.allocator),
1617 .object_files = ArrayList([]const u8).init(builder.allocator),1629 .object_files = ArrayList([]const u8).init(builder.allocator),
1630 .no_rosegment = false,
1618 };1631 };
1619 }1632 }
16201633
1634 pub fn setNoRoSegment(self: *TestStep, value: bool) void {
1635 self.no_rosegment = value;
1636 }
1637
1621 pub fn addLibPath(self: *TestStep, path: []const u8) void {1638 pub fn addLibPath(self: *TestStep, path: []const u8) void {
1622 self.lib_paths.append(path) catch unreachable;1639 self.lib_paths.append(path) catch unreachable;
1623 }1640 }
...@@ -1761,6 +1778,10 @@ pub const TestStep = struct {...@@ -1761,6 +1778,10 @@ pub const TestStep = struct {
1761 try zig_args.append(lib_path);1778 try zig_args.append(lib_path);
1762 }1779 }
17631780
1781 if (self.no_rosegment) {
1782 try zig_args.append("--no-rosegment");
1783 }
1784
1764 try builder.spawnChild(zig_args.toSliceConst());1785 try builder.spawnChild(zig_args.toSliceConst());
1765 }1786 }
1766};1787};
std/debug/index.zig+12-2
...@@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void {...@@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void {
27 const stderr = getStderrStream() catch return;27 const stderr = getStderrStream() catch return;
28 stderr.print(fmt, args) catch return;28 stderr.print(fmt, args) catch return;
29}29}
30fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {30pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
31 if (stderr_stream) |st| {31 if (stderr_stream) |st| {
32 return st;32 return st;
33 } else {33 } else {
...@@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,...@@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,
172 }172 }
173}173}
174174
175pub inline fn getReturnAddress(frame_count: usize) usize {
176 var fp = @ptrToInt(@frameAddress());
177 var i: usize = 0;
178 while (fp != 0 and i < frame_count) {
179 fp = @intToPtr(*const usize, fp).*;
180 i += 1;
181 }
182 return @intToPtr(*const usize, fp + @sizeOf(usize)).*;
183}
184
175pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {185pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {
176 const AddressState = union(enum) {186 const AddressState = union(enum) {
177 NotLookingForStartAddress,187 NotLookingForStartAddress,
...@@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_...@@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
205 }215 }
206}216}
207217
208fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {218pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void {
209 switch (builtin.os) {219 switch (builtin.os) {
210 builtin.Os.windows => return error.UnsupportedDebugInfo,220 builtin.Os.windows => return error.UnsupportedDebugInfo,
211 builtin.Os.macosx => {221 builtin.Os.macosx => {
std/event/channel.zig+3-22
...@@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type {...@@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type {
71 /// puts a data item in the channel. The promise completes when the value has been added to the71 /// 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.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 {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| {74 suspend |handle| {
80 var my_tick_node = Loop.NextTickNode{75 var my_tick_node = Loop.NextTickNode{
81 .next = undefined,76 .next = undefined,
...@@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type {...@@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type {
91 self.putters.put(&queue_node);86 self.putters.put(&queue_node);
92 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);87 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
9388
94 self.loop.onNextTick(dispatch_tick_node_ptr);89 self.dispatch();
95 }90 }
96 }91 }
9792
98 /// await this function to get an item from the channel. If the buffer is empty, the promise will93 /// 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.94 /// complete when the next item is put in the channel.
100 pub async fn get(self: *SelfChannel) T {95 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 values96 // TODO integrate this function with named return values
107 // so we can get rid of this extra result copy97 // so we can get rid of this extra result copy
108 var result: T = undefined;98 var result: T = undefined;
...@@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type {...@@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type {
121 self.getters.put(&queue_node);111 self.getters.put(&queue_node);
122 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);112 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
123113
124 self.loop.onNextTick(dispatch_tick_node_ptr);114 self.dispatch();
125 }115 }
126 return result;116 return result;
127 }117 }
128118
129 async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void {119 fn dispatch(self: *SelfChannel) 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" flag120 // set the "need dispatch" flag
140 _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);121 _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
141122
std/event/loop.zig+2-2
...@@ -55,7 +55,7 @@ pub const Loop = struct {...@@ -55,7 +55,7 @@ pub const Loop = struct {
55 /// After initialization, call run().55 /// After initialization, call run().
56 /// TODO copy elision / named return values so that the threads referencing *Loop56 /// TODO copy elision / named return values so that the threads referencing *Loop
57 /// have the correct pointer value.57 /// have the correct pointer value.
58 fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {58 pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
59 return self.initInternal(allocator, 1);59 return self.initInternal(allocator, 1);
60 }60 }
6161
...@@ -64,7 +64,7 @@ pub const Loop = struct {...@@ -64,7 +64,7 @@ pub const Loop = struct {
64 /// After initialization, call run().64 /// After initialization, call run().
65 /// TODO copy elision / named return values so that the threads referencing *Loop65 /// TODO copy elision / named return values so that the threads referencing *Loop
66 /// have the correct pointer value.66 /// have the correct pointer value.
67 fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
68 const core_count = try std.os.cpuCount(allocator);68 const core_count = try std.os.cpuCount(allocator);
69 return self.initInternal(allocator, core_count);69 return self.initInternal(allocator, core_count);
70 }70 }
std/fmt/index.zig+34
...@@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),...@@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
18 OpenBrace,18 OpenBrace,
19 CloseBrace,19 CloseBrace,
20 FormatString,20 FormatString,
21 Pointer,
21 };22 };
2223
23 comptime var start_index = 0;24 comptime var start_index = 0;
...@@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),...@@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
54 state = State.Start;55 state = State.Start;
55 start_index = i + 1;56 start_index = i + 1;
56 },57 },
58 '*' => state = State.Pointer,
57 else => {59 else => {
58 state = State.FormatString;60 state = State.FormatString;
59 },61 },
...@@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),...@@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
75 },77 },
76 else => {},78 else => {},
77 },79 },
80 State.Pointer => switch (c) {
81 '}' => {
82 try output(context, @typeName(@typeOf(args[next_arg]).Child));
83 try output(context, "@");
84 try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output);
85 next_arg += 1;
86 state = State.Start;
87 start_index = i + 1;
88 },
89 else => @compileError("Unexpected format character after '*'"),
90 },
78 }91 }
79 }92 }
80 comptime {93 comptime {
...@@ -861,6 +874,27 @@ test "fmt.format" {...@@ -861,6 +874,27 @@ test "fmt.format" {
861 const value: u8 = 'a';874 const value: u8 = 'a';
862 try testFmt("u8: a\n", "u8: {c}\n", value);875 try testFmt("u8: a\n", "u8: {c}\n", value);
863 }876 }
877 {
878 const value: [3]u8 = "abc";
879 try testFmt("array: abc\n", "array: {}\n", value);
880 try testFmt("array: abc\n", "array: {}\n", &value);
881
882 var buf: [100]u8 = undefined;
883 try testFmt(
884 try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)),
885 "array: {*}\n",
886 &value,
887 );
888 }
889 {
890 const value: []const u8 = "abc";
891 try testFmt("slice: abc\n", "slice: {}\n", value);
892 }
893 {
894 const value = @intToPtr(*i32, 0xdeadbeef);
895 try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
896 try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value);
897 }
864 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");898 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
865 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");899 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
866 try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");900 try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
std/io.zig+165
...@@ -200,6 +200,13 @@ pub fn InStream(comptime ReadError: type) type {...@@ -200,6 +200,13 @@ pub fn InStream(comptime ReadError: type) type {
200 try self.readNoEof(input_slice);200 try self.readNoEof(input_slice);
201 return mem.readInt(input_slice, T, endian);201 return mem.readInt(input_slice, T, endian);
202 }202 }
203
204 pub fn skipBytes(self: *Self, num_bytes: usize) !void {
205 var i: usize = 0;
206 while (i < num_bytes) : (i += 1) {
207 _ = try self.readByte();
208 }
209 }
203 };210 };
204}211}
205212
...@@ -230,6 +237,20 @@ pub fn OutStream(comptime WriteError: type) type {...@@ -230,6 +237,20 @@ pub fn OutStream(comptime WriteError: type) type {
230 try self.writeFn(self, slice);237 try self.writeFn(self, slice);
231 }238 }
232 }239 }
240
241 pub fn writeIntLe(self: *Self, comptime T: type, value: T) !void {
242 return self.writeInt(builtin.Endian.Little, T, value);
243 }
244
245 pub fn writeIntBe(self: *Self, comptime T: type, value: T) !void {
246 return self.writeInt(builtin.Endian.Big, T, value);
247 }
248
249 pub fn writeInt(self: *Self, endian: builtin.Endian, comptime T: type, value: T) !void {
250 var bytes: [@sizeOf(T)]u8 = undefined;
251 mem.writeInt(bytes[0..], value, endian);
252 return self.writeFn(self, bytes);
253 }
233 };254 };
234}255}
235256
...@@ -331,6 +352,150 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)...@@ -331,6 +352,150 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
331 };352 };
332}353}
333354
355/// Creates a stream which supports 'un-reading' data, so that it can be read again.
356/// This makes look-ahead style parsing much easier.
357pub fn PeekStream(comptime buffer_size: usize, comptime InStreamError: type) type {
358 return struct {
359 const Self = this;
360 pub const Error = InStreamError;
361 pub const Stream = InStream(Error);
362
363 pub stream: Stream,
364 base: *Stream,
365
366 // Right now the look-ahead space is statically allocated, but a version with dynamic allocation
367 // is not too difficult to derive from this.
368 buffer: [buffer_size]u8,
369 index: usize,
370 at_end: bool,
371
372 pub fn init(base: *Stream) Self {
373 return Self{
374 .base = base,
375 .buffer = undefined,
376 .index = 0,
377 .at_end = false,
378 .stream = Stream{ .readFn = readFn },
379 };
380 }
381
382 pub fn putBackByte(self: *Self, byte: u8) void {
383 self.buffer[self.index] = byte;
384 self.index += 1;
385 }
386
387 pub fn putBack(self: *Self, bytes: []const u8) void {
388 var pos = bytes.len;
389 while (pos != 0) {
390 pos -= 1;
391 self.putBackByte(bytes[pos]);
392 }
393 }
394
395 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
396 const self = @fieldParentPtr(Self, "stream", in_stream);
397
398 // copy over anything putBack()'d
399 var pos: usize = 0;
400 while (pos < dest.len and self.index != 0) {
401 dest[pos] = self.buffer[self.index - 1];
402 self.index -= 1;
403 pos += 1;
404 }
405
406 if (pos == dest.len or self.at_end) {
407 return pos;
408 }
409
410 // ask the backing stream for more
411 const left = dest.len - pos;
412 const read = try self.base.read(dest[pos..]);
413 assert(read <= left);
414
415 self.at_end = (read < left);
416 return pos + read;
417 }
418
419 };
420}
421
422pub const SliceInStream = struct {
423 const Self = this;
424 pub const Error = error { };
425 pub const Stream = InStream(Error);
426
427 pub stream: Stream,
428
429 pos: usize,
430 slice: []const u8,
431
432 pub fn init(slice: []const u8) Self {
433 return Self{
434 .slice = slice,
435 .pos = 0,
436 .stream = Stream{ .readFn = readFn },
437 };
438 }
439
440 fn readFn(in_stream: *Stream, dest: []u8) Error!usize {
441 const self = @fieldParentPtr(Self, "stream", in_stream);
442 const size = math.min(dest.len, self.slice.len - self.pos);
443 const end = self.pos + size;
444
445 mem.copy(u8, dest[0..size], self.slice[self.pos..end]);
446 self.pos = end;
447
448 return size;
449 }
450};
451
452/// This is a simple OutStream that writes to a slice, and returns an error
453/// when it runs out of space.
454pub const SliceOutStream = struct {
455 pub const Error = error{OutOfSpace};
456 pub const Stream = OutStream(Error);
457
458 pub stream: Stream,
459
460 pos: usize,
461 slice: []u8,
462
463 pub fn init(slice: []u8) SliceOutStream {
464 return SliceOutStream{
465 .slice = slice,
466 .pos = 0,
467 .stream = Stream{ .writeFn = writeFn },
468 };
469 }
470
471 pub fn getWritten(self: *const SliceOutStream) []const u8 {
472 return self.slice[0..self.pos];
473 }
474
475 pub fn reset(self: *SliceOutStream) void {
476 self.pos = 0;
477 }
478
479 fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
480 const self = @fieldParentPtr(SliceOutStream, "stream", out_stream);
481
482 assert(self.pos <= self.slice.len);
483
484 const n =
485 if (self.pos + bytes.len <= self.slice.len)
486 bytes.len
487 else
488 self.slice.len - self.pos;
489
490 std.mem.copy(u8, self.slice[self.pos..self.pos + n], bytes[0..n]);
491 self.pos += n;
492
493 if (n < bytes.len) {
494 return Error.OutOfSpace;
495 }
496 }
497};
498
334pub fn BufferedOutStream(comptime Error: type) type {499pub fn BufferedOutStream(comptime Error: type) type {
335 return BufferedOutStreamCustom(os.page_size, Error);500 return BufferedOutStreamCustom(os.page_size, Error);
336}501}
std/io_test.zig+72
...@@ -2,6 +2,7 @@ const std = @import("index.zig");...@@ -2,6 +2,7 @@ const std = @import("index.zig");
2const io = std.io;2const io = std.io;
3const DefaultPrng = std.rand.DefaultPrng;3const DefaultPrng = std.rand.DefaultPrng;
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const assertError = std.debug.assertError;
5const mem = std.mem;6const mem = std.mem;
6const os = std.os;7const os = std.os;
7const builtin = @import("builtin");8const builtin = @import("builtin");
...@@ -60,3 +61,74 @@ test "BufferOutStream" {...@@ -60,3 +61,74 @@ test "BufferOutStream" {
6061
61 assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));62 assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
62}63}
64
65test "SliceInStream" {
66 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7 };
67 var ss = io.SliceInStream.init(bytes);
68
69 var dest: [4]u8 = undefined;
70
71 var read = try ss.stream.read(dest[0..4]);
72 assert(read == 4);
73 assert(mem.eql(u8, dest[0..4], bytes[0..4]));
74
75 read = try ss.stream.read(dest[0..4]);
76 assert(read == 3);
77 assert(mem.eql(u8, dest[0..3], bytes[4..7]));
78
79 read = try ss.stream.read(dest[0..4]);
80 assert(read == 0);
81}
82
83test "PeekStream" {
84 const bytes = []const u8 { 1, 2, 3, 4, 5, 6, 7, 8 };
85 var ss = io.SliceInStream.init(bytes);
86 var ps = io.PeekStream(2, io.SliceInStream.Error).init(&ss.stream);
87
88 var dest: [4]u8 = undefined;
89
90 ps.putBackByte(9);
91 ps.putBackByte(10);
92
93 var read = try ps.stream.read(dest[0..4]);
94 assert(read == 4);
95 assert(dest[0] == 10);
96 assert(dest[1] == 9);
97 assert(mem.eql(u8, dest[2..4], bytes[0..2]));
98
99 read = try ps.stream.read(dest[0..4]);
100 assert(read == 4);
101 assert(mem.eql(u8, dest[0..4], bytes[2..6]));
102
103 read = try ps.stream.read(dest[0..4]);
104 assert(read == 2);
105 assert(mem.eql(u8, dest[0..2], bytes[6..8]));
106
107 ps.putBackByte(11);
108 ps.putBackByte(12);
109
110 read = try ps.stream.read(dest[0..4]);
111 assert(read == 2);
112 assert(dest[0] == 12);
113 assert(dest[1] == 11);
114}
115
116test "SliceOutStream" {
117 var buffer: [10]u8 = undefined;
118 var ss = io.SliceOutStream.init(buffer[0..]);
119
120 try ss.stream.write("Hello");
121 assert(mem.eql(u8, ss.getWritten(), "Hello"));
122
123 try ss.stream.write("world");
124 assert(mem.eql(u8, ss.getWritten(), "Helloworld"));
125
126 assertError(ss.stream.write("!"), error.OutOfSpace);
127 assert(mem.eql(u8, ss.getWritten(), "Helloworld"));
128
129 ss.reset();
130 assert(ss.getWritten().len == 0);
131
132 assertError(ss.stream.write("Hello world!"), error.OutOfSpace);
133 assert(mem.eql(u8, ss.getWritten(), "Hello worl"));
134}
test/behavior.zig+1
...@@ -16,6 +16,7 @@ comptime {...@@ -16,6 +16,7 @@ comptime {
16 _ = @import("cases/bugs/828.zig");16 _ = @import("cases/bugs/828.zig");
17 _ = @import("cases/bugs/920.zig");17 _ = @import("cases/bugs/920.zig");
18 _ = @import("cases/byval_arg_var.zig");18 _ = @import("cases/byval_arg_var.zig");
19 _ = @import("cases/cancel.zig");
19 _ = @import("cases/cast.zig");20 _ = @import("cases/cast.zig");
20 _ = @import("cases/const_slice_child.zig");21 _ = @import("cases/const_slice_child.zig");
21 _ = @import("cases/coroutine_await_struct.zig");22 _ = @import("cases/coroutine_await_struct.zig");
test/cases/cancel.zig created+92
...@@ -0,0 +1,92 @@
1const std = @import("std");
2
3var defer_f1: bool = false;
4var defer_f2: bool = false;
5var defer_f3: bool = false;
6
7test "cancel forwards" {
8 var da = std.heap.DirectAllocator.init();
9 defer da.deinit();
10
11 const p = async<&da.allocator> f1() catch unreachable;
12 cancel p;
13 std.debug.assert(defer_f1);
14 std.debug.assert(defer_f2);
15 std.debug.assert(defer_f3);
16}
17
18async fn f1() void {
19 defer {
20 defer_f1 = true;
21 }
22 await (async f2() catch unreachable);
23}
24
25async fn f2() void {
26 defer {
27 defer_f2 = true;
28 }
29 await (async f3() catch unreachable);
30}
31
32async fn f3() void {
33 defer {
34 defer_f3 = true;
35 }
36 suspend;
37}
38
39var defer_b1: bool = false;
40var defer_b2: bool = false;
41var defer_b3: bool = false;
42var defer_b4: bool = false;
43
44test "cancel backwards" {
45 var da = std.heap.DirectAllocator.init();
46 defer da.deinit();
47
48 const p = async<&da.allocator> b1() catch unreachable;
49 cancel p;
50 std.debug.assert(defer_b1);
51 std.debug.assert(defer_b2);
52 std.debug.assert(defer_b3);
53 std.debug.assert(defer_b4);
54}
55
56async fn b1() void {
57 defer {
58 defer_b1 = true;
59 }
60 await (async b2() catch unreachable);
61}
62
63var b4_handle: promise = undefined;
64
65async fn b2() void {
66 const b3_handle = async b3() catch unreachable;
67 resume b4_handle;
68 cancel b4_handle;
69 defer {
70 defer_b2 = true;
71 }
72 const value = await b3_handle;
73 @panic("unreachable");
74}
75
76async fn b3() i32 {
77 defer {
78 defer_b3 = true;
79 }
80 await (async b4() catch unreachable);
81 return 1234;
82}
83
84async fn b4() void {
85 defer {
86 defer_b4 = true;
87 }
88 suspend |p| {
89 b4_handle = p;
90 }
91 suspend;
92}
test/cases/coroutines.zig+2-2
...@@ -244,8 +244,8 @@ test "break from suspend" {...@@ -244,8 +244,8 @@ test "break from suspend" {
244 std.debug.assert(my_result == 2);244 std.debug.assert(my_result == 2);
245}245}
246async fn testBreakFromSuspend(my_result: *i32) void {246async fn testBreakFromSuspend(my_result: *i32) void {
247 s: suspend |p| {247 suspend |p| {
248 break :s;248 resume p;
249 }249 }
250 my_result.* += 1;250 my_result.* += 1;
251 suspend;251 suspend;
test/compile_errors.zig+58
...@@ -1,6 +1,64 @@...@@ -1,6 +1,64 @@
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 "while loop body expression ignored",
6 \\fn returns() usize {
7 \\ return 2;
8 \\}
9 \\export fn f1() void {
10 \\ while (true) returns();
11 \\}
12 \\export fn f2() void {
13 \\ var x: ?i32 = null;
14 \\ while (x) |_| returns();
15 \\}
16 \\export fn f3() void {
17 \\ var x: error!i32 = error.Bad;
18 \\ while (x) |_| returns() else |_| unreachable;
19 \\}
20 ,
21 ".tmp_source.zig:5:25: error: expression value is ignored",
22 ".tmp_source.zig:9:26: error: expression value is ignored",
23 ".tmp_source.zig:13:26: error: expression value is ignored",
24 );
25
26 cases.add(
27 "missing parameter name of generic function",
28 \\fn dump(var) void {}
29 \\export fn entry() void {
30 \\ var a: u8 = 9;
31 \\ dump(a);
32 \\}
33 ,
34 ".tmp_source.zig:1:9: error: missing parameter name",
35 );
36
37 cases.add(
38 "non-inline for loop on a type that requires comptime",
39 \\const Foo = struct {
40 \\ name: []const u8,
41 \\ T: type,
42 \\};
43 \\export fn entry() void {
44 \\ const xx: [2]Foo = undefined;
45 \\ for (xx) |f| {}
46 \\}
47 ,
48 ".tmp_source.zig:7:15: error: variable of type 'Foo' must be const or comptime",
49 );
50
51 cases.add(
52 "generic fn as parameter without comptime keyword",
53 \\fn f(_: fn (var) void) void {}
54 \\fn g(_: var) void {}
55 \\export fn entry() void {
56 \\ f(g);
57 \\}
58 ,
59 ".tmp_source.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime",
60 );
61
4 cases.add(62 cases.add(
5 "optional pointer to void in extern struct",63 "optional pointer to void in extern struct",
6 \\comptime {64 \\comptime {
test/stage2/compare_output.zig+13
...@@ -2,6 +2,7 @@ const std = @import("std");...@@ -2,6 +2,7 @@ const std = @import("std");
2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;2const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
4pub fn addCases(ctx: *TestContext) !void {4pub fn addCases(ctx: *TestContext) !void {
5 // hello world
5 try ctx.testCompareOutputLibC(6 try ctx.testCompareOutputLibC(
6 \\extern fn puts([*]const u8) void;7 \\extern fn puts([*]const u8) void;
7 \\export fn main() c_int {8 \\export fn main() c_int {
...@@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void {
9 \\ return 0;10 \\ return 0;
10 \\}11 \\}
11 , "Hello, world!" ++ std.cstr.line_sep);12 , "Hello, world!" ++ std.cstr.line_sep);
13
14 // function calling another function
15 try ctx.testCompareOutputLibC(
16 \\extern fn puts(s: [*]const u8) void;
17 \\export fn main() c_int {
18 \\ return foo(c"OK");
19 \\}
20 \\fn foo(s: [*]const u8) c_int {
21 \\ puts(s);
22 \\ return 0;
23 \\}
24 , "OK" ++ std.cstr.line_sep);
12}25}