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.
7474 * Reddit: [/r/zig](https://www.reddit.com/r/zig)
7575 * 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
11577## Building
11678
11779[![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 {
4545 .c_header_files = nextValue(&index, build_info),
4646 .dia_guids_lib = nextValue(&index, build_info),
4747 .llvm = undefined,
48 .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false,
4849 };
4950 ctx.llvm = try findLLVM(b, ctx.llvm_config_exe);
5051
......@@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
228229 // TODO turn this into -Dextra-lib-path=/lib option
229230 exe.addLibPath("/lib");
230231
232 exe.setNoRoSegment(ctx.no_rosegment);
233
231234 exe.addIncludeDir("src");
232235 exe.addIncludeDir(ctx.cmake_binary_dir);
233236 addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp");
......@@ -286,4 +289,5 @@ const Context = struct {
286289 c_header_files: []const u8,
287290 dia_guids_lib: []const u8,
288291 llvm: LibraryDep,
292 no_rosegment: bool,
289293};
deps/lld/COFF/Driver.cpp+3
......@@ -72,6 +72,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) {
7272 exitLld(errorCount() ? 1 : 0);
7373
7474 freeArena();
75 ObjFile::Instances.clear();
76 ImportFile::Instances.clear();
77 BitcodeFile::Instances.clear();
7578 return !errorCount();
7679}
7780
doc/langref.html.in+59-7
......@@ -134,6 +134,58 @@ pub fn main() void {
134134 </p>
135135 {#see_also|Values|@import|Errors|Root Source File#}
136136 {#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#}
137189 {#header_open|Values#}
138190 {#code_begin|exe|values#}
139191const std = @import("std");
......@@ -4665,24 +4717,24 @@ async fn testSuspendBlock() void {
46654717 block, while the old thread continued executing the suspend block.
46664718 </p>
46674719 <p>
4668 However, if you use labeled <code>break</code> on the suspend block, the coroutine
4720 However, the coroutine can be directly resumed from the suspend block, in which case it
46694721 never returns to its resumer and continues executing.
46704722 </p>
46714723 {#code_begin|test#}
46724724const std = @import("std");
46734725const assert = std.debug.assert;
46744726
4675test "break from suspend" {
4727test "resume from suspend" {
46764728 var buf: [500]u8 = undefined;
46774729 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
46784730 var my_result: i32 = 1;
4679 const p = try async<a> testBreakFromSuspend(&my_result);
4731 const p = try async<a> testResumeFromSuspend(&my_result);
46804732 cancel p;
46814733 std.debug.assert(my_result == 2);
46824734}
4683async fn testBreakFromSuspend(my_result: *i32) void {
4684 s: suspend |p| {
4685 break :s;
4735async fn testResumeFromSuspend(my_result: *i32) void {
4736 suspend |p| {
4737 resume p;
46864738 }
46874739 my_result.* += 1;
46884740 suspend;
......@@ -7336,7 +7388,7 @@ Defer(body) = ("defer" | "deferror") body
73367388
73377389IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73387390
7339SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
7391SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
73407392
73417393IfErrorExpression(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");
66const ir = @import("ir.zig");
77const Value = @import("value.zig").Value;
88const Type = @import("type.zig").Type;
9const Scope = @import("scope.zig").Scope;
910const event = std.event;
1011const assert = std.debug.assert;
1112const DW = std.dwarf;
......@@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
156157 llvm_fn_type,
157158 ) 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);
160161 if (want_fn_safety and ofile.comp.haveLibC()) {
161162 try addLLVMFnAttr(ofile, llvm_fn, "sspstrong");
162163 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)
168169 //}
169170
170171 const fn_type = fn_val.base.typ.cast(Type.Fn).?;
172 const fn_type_normal = &fn_type.key.data.Normal;
171173
172174 try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
173175 //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)
209211 // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull");
210212 //}
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
214216 // build all basic blocks
215217 for (code.basic_block_list.toSlice()) |bb| {
......@@ -226,9 +228,86 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
226228
227229 // TODO set up error return tracing
228230 // TODO allocate temporary stack values
229 // TODO create debug variable declarations for variables and allocate all local variables
231
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
230280 // TODO finishing error return trace setup. we have to do this after all the allocas.
231 // TODO create debug variable declarations for parameters
281
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
233312 for (code.basic_block_list.toSlice()) |current_block| {
234313 llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block);
......@@ -293,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons
293372fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void {
294373 return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val);
295374}
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;
3535pub const EventLoopLocal = struct {
3636 loop: *event.Loop,
3737 llvm_handle_pool: std.atomic.Stack(llvm.ContextRef),
38 lld_lock: event.Lock,
3839
3940 /// TODO pool these so that it doesn't have to lock
4041 prng: event.Locked(std.rand.DefaultPrng),
......@@ -55,6 +56,7 @@ pub const EventLoopLocal = struct {
5556
5657 return EventLoopLocal{
5758 .loop = loop,
59 .lld_lock = event.Lock.init(loop),
5860 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
5961 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
6062 .native_libc = event.Future(LibCInstallation).init(loop),
......@@ -63,6 +65,7 @@ pub const EventLoopLocal = struct {
6365
6466 /// Must be called only after EventLoop.run completes.
6567 fn deinit(self: *EventLoopLocal) void {
68 self.lld_lock.deinit();
6669 while (self.llvm_handle_pool.pop()) |node| {
6770 c.LLVMContextDispose(node.data);
6871 self.loop.allocator.destroy(node);
......@@ -220,12 +223,14 @@ pub const Compilation = struct {
220223 int_type_table: event.Locked(IntTypeTable),
221224 array_type_table: event.Locked(ArrayTypeTable),
222225 ptr_type_table: event.Locked(PtrTypeTable),
226 fn_type_table: event.Locked(FnTypeTable),
223227
224228 c_int_types: [CInt.list.len]*Type.Int,
225229
226230 const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
227231 const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
228232 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);
229234 const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
230235
231236 const CompileErrList = std.ArrayList(*Msg);
......@@ -384,6 +389,7 @@ pub const Compilation = struct {
384389 .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
385390 .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
386391 .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)),
387393 .c_int_types = undefined,
388394
389395 .meta_type = undefined,
......@@ -414,6 +420,7 @@ pub const Compilation = struct {
414420 comp.int_type_table.private_data.deinit();
415421 comp.array_type_table.private_data.deinit();
416422 comp.ptr_type_table.private_data.deinit();
423 comp.fn_type_table.private_data.deinit();
417424 comp.arena_allocator.deinit();
418425 comp.loop.allocator.destroy(comp);
419426 }
......@@ -1160,13 +1167,48 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
11601167 fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
11611168 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
11631203 const analyzed_code = try await (async comp.genAndAnalyzeCode(
1164 &fndef_scope.base,
1204 fn_val.child_scope,
11651205 body_node,
1166 fn_type.return_type,
1206 fn_type.key.data.Normal.return_type,
11671207 ) catch unreachable);
11681208 errdefer analyzed_code.destroy(comp.gpa());
11691209
1210 assert(fn_val.block_scope != null);
1211
11701212 // Kick off rendering to LLVM module, but it doesn't block the fn decl
11711213 // analysis from being complete.
11721214 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
11991241
12001242 var params = ArrayList(Type.Fn.Param).init(comp.gpa());
12011243 var params_consumed = false;
1202 defer if (params_consumed) {
1244 defer if (!params_consumed) {
12031245 for (params.toSliceConst()) |param| {
12041246 param.typ.base.deref(comp);
12051247 }
12061248 params.deinit();
12071249 };
12081250
1209 const is_var_args = false;
12101251 {
12111252 var it = fn_proto.params.iterator(0);
12121253 while (it.next()) |param_node_ptr| {
......@@ -1219,8 +1260,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn
12191260 });
12201261 }
12211262 }
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 };
12231275 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;
12241286 errdefer fn_type.base.base.deref(comp);
12251287
12261288 return fn_type;
src-self-hosted/ir.zig+181-24
......@@ -10,8 +10,10 @@ const assert = std.debug.assert;
1010const Token = std.zig.Token;
1111const Span = @import("errmsg.zig").Span;
1212const llvm = @import("llvm.zig");
13const ObjectFile = @import("codegen.zig").ObjectFile;
13const codegen = @import("codegen.zig");
14const ObjectFile = codegen.ObjectFile;
1415const Decl = @import("decl.zig").Decl;
16const mem = std.mem;
1517
1618pub const LVal = enum {
1719 None,
......@@ -122,6 +124,8 @@ pub const Inst = struct {
122124 Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira),
123125 Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira),
124126 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),
125129 }
126130 }
127131
......@@ -130,6 +134,8 @@ pub const Inst = struct {
130134 Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val),
131135 Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val),
132136 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),
133139 Id.DeclRef => unreachable,
134140 Id.PtrType => unreachable,
135141 Id.Ref => @panic("TODO"),
......@@ -248,6 +254,8 @@ pub const Inst = struct {
248254 Call,
249255 DeclRef,
250256 PtrType,
257 VarPtr,
258 LoadPtr,
251259 };
252260
253261 pub const Call = struct {
......@@ -281,11 +289,13 @@ pub const Inst = struct {
281289 return error.SemanticAnalysisFailed;
282290 };
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) {
285295 try ira.addCompileError(
286296 self.base.span,
287297 "expected {} arguments, found {}",
288 fn_type.params.len,
298 fn_type_param_count,
289299 self.params.args.len,
290300 );
291301 return error.SemanticAnalysisFailed;
......@@ -299,7 +309,7 @@ pub const Inst = struct {
299309 .fn_ref = fn_ref,
300310 .args = args,
301311 });
302 new_inst.val = IrVal{ .KnownType = fn_type.return_type };
312 new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type };
303313 return new_inst;
304314 }
305315
......@@ -489,6 +499,133 @@ pub const Inst = struct {
489499 }
490500 };
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
492629 pub const PtrType = struct {
493630 base: Inst,
494631 params: Params,
......@@ -1158,6 +1295,7 @@ pub const Builder = struct {
11581295 Scope.Id.Block,
11591296 Scope.Id.Defer,
11601297 Scope.Id.DeferExpr,
1298 Scope.Id.Var,
11611299 => scope = scope.parent.?,
11621300 }
11631301 }
......@@ -1259,8 +1397,8 @@ pub const Builder = struct {
12591397 var child_scope = outer_block_scope;
12601398
12611399 if (parent_scope.findFnDef()) |fndef_scope| {
1262 if (fndef_scope.fn_val.child_scope == parent_scope) {
1263 fndef_scope.fn_val.block_scope = block_scope;
1400 if (fndef_scope.fn_val.?.block_scope == null) {
1401 fndef_scope.fn_val.?.block_scope = block_scope;
12641402 }
12651403 }
12661404
......@@ -1490,20 +1628,23 @@ pub const Builder = struct {
14901628 error.OutOfMemory => return error.OutOfMemory,
14911629 }
14921630
1493 //VariableTableEntry *var = find_variable(irb->codegen, scope, variable_name);
1494 //if (var) {
1495 // IrInstruction *var_ptr = ir_build_var_ptr(irb, scope, node, var);
1496 // if (lval == LValPtr)
1497 // return var_ptr;
1498 // else
1499 // return ir_build_load_ptr(irb, scope, node, var_ptr);
1500 //}
1501
1502 if (await (async irb.findDecl(scope, name) catch unreachable)) |decl| {
1503 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1504 .decl = decl,
1505 .lval = lval,
1506 });
1631 switch (await (async irb.findIdent(scope, name) catch unreachable)) {
1632 Ident.Decl => |decl| {
1633 return irb.build(Inst.DeclRef, scope, src_span, Inst.DeclRef.Params{
1634 .decl = decl,
1635 .lval = lval,
1636 });
1637 },
1638 Ident.VarScope => |var_scope| {
1639 const var_ptr = try irb.build(Inst.VarPtr, scope, src_span, Inst.VarPtr.Params{ .var_scope = var_scope });
1640 switch (lval) {
1641 LVal.Ptr => return var_ptr,
1642 LVal.None => {
1643 return irb.build(Inst.LoadPtr, scope, src_span, Inst.LoadPtr.Params{ .target = var_ptr });
1644 },
1645 }
1646 },
1647 Ident.NotFound => {},
15071648 }
15081649
15091650 //if (node->owner->any_imports_failed) {
......@@ -1544,6 +1685,7 @@ pub const Builder = struct {
15441685 Scope.Id.Block,
15451686 Scope.Id.Decls,
15461687 Scope.Id.Root,
1688 Scope.Id.Var,
15471689 => scope = scope.parent orelse break,
15481690
15491691 Scope.Id.DeferExpr => unreachable,
......@@ -1594,6 +1736,7 @@ pub const Builder = struct {
15941736
15951737 Scope.Id.CompTime,
15961738 Scope.Id.Block,
1739 Scope.Id.Var,
15971740 => scope = scope.parent orelse return is_noreturn,
15981741
15991742 Scope.Id.DeferExpr => unreachable,
......@@ -1672,8 +1815,10 @@ pub const Builder = struct {
16721815 Type.Pointer.Size,
16731816 LVal,
16741817 *Decl,
1818 *Scope.Var,
16751819 => {},
1676 // it's ok to add more types here, just make sure any instructions are ref'd appropriately
1820 // it's ok to add more types here, just make sure that
1821 // any instructions and basic blocks are ref'd appropriately
16771822 else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)),
16781823 }
16791824 }
......@@ -1771,18 +1916,30 @@ pub const Builder = struct {
17711916 //// the above blocks are rendered by ir_gen after the rest of codegen
17721917 }
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 {
17751926 var s = scope;
17761927 while (true) {
17771928 switch (s.id) {
1929 Scope.Id.Root => return Ident.NotFound,
17781930 Scope.Id.Decls => {
17791931 const decls = @fieldParentPtr(Scope.Decls, "base", s);
17801932 const table = await (async decls.getTableReadOnly() catch unreachable);
17811933 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 };
17831941 }
17841942 },
1785 Scope.Id.Root => return null,
17861943 else => {},
17871944 }
17881945 s = s.parent.?;
src-self-hosted/link.zig+23-10
......@@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void {
8080
8181 const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
8282 const args_slice = ctx.args.toSlice();
83 // Not evented I/O. LLD does its own multithreading internally.
84 if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
85 if (!ctx.link_msg.isNull()) {
86 // TODO capture these messages and pass them through the system, reporting them through the
87 // event system instead of printing them directly here.
88 // perhaps try to parse and understand them.
89 std.debug.warn("{}\n", ctx.link_msg.toSliceConst());
83
84 {
85 // LLD is not thread-safe, so we grab a global lock.
86 const held = await (async comp.event_loop_local.lld_lock.acquire() catch unreachable);
87 defer held.release();
88
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;
9098 }
91 return error.LinkFailed;
9299 }
93100}
94101
......@@ -672,7 +679,13 @@ const DarwinPlatform = struct {
672679 };
673680
674681 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 );
676689 if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
677690 return error.InvalidDarwinVersionString;
678691 }
......@@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3
713726 return error.InvalidDarwinVersionString;
714727
715728 var start_pos: usize = 0;
716 for ([]*u32{major, minor, micro}) |v| {
729 for ([]*u32{ major, minor, micro }) |v| {
717730 const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
718731 const end_pos = dot_pos orelse str.len;
719732 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;
3030pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
3131pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
3232pub const ArrayType = c.LLVMArrayType;
33pub const BuildLoad = c.LLVMBuildLoad;
3334pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
3435pub const ConstAllOnes = c.LLVMConstAllOnes;
3536pub const ConstArray = c.LLVMConstArray;
......@@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer;
9596pub const SetLinkage = c.LLVMSetLinkage;
9697pub const SetTarget = c.LLVMSetTarget;
9798pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
99pub const SetVolatile = c.LLVMSetVolatile;
98100pub const StructTypeInContext = c.LLVMStructTypeInContext;
99101pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
100pub const TypeOf = c.LLVMTypeOf;
101102pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
102103pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
103104pub 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
105118pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
106119pub 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;
66const mem = std.mem;
77const ast = std.zig.ast;
88const Value = @import("value.zig").Value;
9const Type = @import("type.zig").Type;
910const ir = @import("ir.zig");
1011const Span = @import("errmsg.zig").Span;
1112const assert = std.debug.assert;
1213const event = std.event;
14const llvm = @import("llvm.zig");
1315
1416pub const Scope = struct {
1517 id: Id,
1618 parent: ?*Scope,
17 ref_count: usize,
19 ref_count: std.atomic.Int(usize),
1820
21 /// Thread-safe
1922 pub fn ref(base: *Scope) void {
20 base.ref_count += 1;
23 _ = base.ref_count.incr();
2124 }
2225
26 /// Thread-safe
2327 pub fn deref(base: *Scope, comp: *Compilation) void {
24 base.ref_count -= 1;
25 if (base.ref_count == 0) {
28 if (base.ref_count.decr() == 1) {
2629 if (base.parent) |parent| parent.deref(comp);
2730 switch (base.id) {
2831 Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
......@@ -32,6 +35,7 @@ pub const Scope = struct {
3235 Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
3336 Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp),
3437 Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp),
38 Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp),
3539 }
3640 }
3741 }
......@@ -49,15 +53,15 @@ pub const Scope = struct {
4953 var scope = base;
5054 while (true) {
5155 switch (scope.id) {
52 Id.FnDef => return @fieldParentPtr(FnDef, "base", base),
53 Id.Decls => return null,
56 Id.FnDef => return @fieldParentPtr(FnDef, "base", scope),
57 Id.Root, Id.Decls => return null,
5458
5559 Id.Block,
5660 Id.Defer,
5761 Id.DeferExpr,
5862 Id.CompTime,
59 Id.Root,
60 => scope = scope.parent orelse return null,
63 Id.Var,
64 => scope = scope.parent.?,
6165 }
6266 }
6367 }
......@@ -66,7 +70,7 @@ pub const Scope = struct {
6670 var scope = base;
6771 while (true) {
6872 switch (scope.id) {
69 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
73 Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope),
7074
7175 Id.FnDef,
7276 Id.Decls,
......@@ -76,11 +80,21 @@ pub const Scope = struct {
7680 Id.Defer,
7781 Id.CompTime,
7882 Id.Root,
83 Id.Var,
7984 => scope = scope.parent orelse return null,
8085 }
8186 }
8287 }
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
8498 pub const Id = enum {
8599 Root,
86100 Decls,
......@@ -89,6 +103,7 @@ pub const Scope = struct {
89103 CompTime,
90104 Defer,
91105 DeferExpr,
106 Var,
92107 };
93108
94109 pub const Root = struct {
......@@ -100,16 +115,16 @@ pub const Scope = struct {
100115 /// Takes ownership of realpath
101116 /// Takes ownership of tree, will deinit and destroy when done.
102117 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{
104120 .base = Scope{
105121 .id = Id.Root,
106122 .parent = null,
107 .ref_count = 1,
123 .ref_count = std.atomic.Int(usize).init(1),
108124 },
109125 .tree = tree,
110126 .realpath = realpath,
111 });
112 errdefer comp.gpa().destroy(self);
127 };
113128
114129 return self;
115130 }
......@@ -137,16 +152,13 @@ pub const Scope = struct {
137152
138153 /// Creates a Decls scope with 1 reference
139154 pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
140 const self = try comp.gpa().create(Decls{
141 .base = Scope{
142 .id = Id.Decls,
143 .parent = parent,
144 .ref_count = 1,
145 },
155 const self = try comp.gpa().createOne(Decls);
156 self.* = Decls{
157 .base = undefined,
146158 .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
147159 .name_future = event.Future(void).init(comp.loop),
148 });
149 parent.ref();
160 };
161 self.base.init(Id.Decls, parent);
150162 return self;
151163 }
152164
......@@ -199,21 +211,16 @@ pub const Scope = struct {
199211
200212 /// Creates a Block scope with 1 reference
201213 pub fn create(comp: *Compilation, parent: *Scope) !*Block {
202 const self = try comp.gpa().create(Block{
203 .base = Scope{
204 .id = Id.Block,
205 .parent = parent,
206 .ref_count = 1,
207 },
214 const self = try comp.gpa().createOne(Block);
215 self.* = Block{
216 .base = undefined,
208217 .incoming_values = undefined,
209218 .incoming_blocks = undefined,
210219 .end_block = undefined,
211220 .is_comptime = undefined,
212221 .safety = Safety.Auto,
213 });
214 errdefer comp.gpa().destroy(self);
215
216 parent.ref();
222 };
223 self.base.init(Id.Block, parent);
217224 return self;
218225 }
219226
......@@ -226,22 +233,17 @@ pub const Scope = struct {
226233 base: Scope,
227234
228235 /// 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
231238 /// Creates a FnDef scope with 1 reference
232239 /// Must set the fn_val later
233240 pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
234 const self = try comp.gpa().create(FnDef{
235 .base = Scope{
236 .id = Id.FnDef,
237 .parent = parent,
238 .ref_count = 1,
239 },
240 .fn_val = undefined,
241 });
242
243 parent.ref();
244
241 const self = try comp.gpa().createOne(FnDef);
242 self.* = FnDef{
243 .base = undefined,
244 .fn_val = null,
245 };
246 self.base.init(Id.FnDef, parent);
245247 return self;
246248 }
247249
......@@ -255,15 +257,9 @@ pub const Scope = struct {
255257
256258 /// Creates a CompTime scope with 1 reference
257259 pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
258 const self = try comp.gpa().create(CompTime{
259 .base = Scope{
260 .id = Id.CompTime,
261 .parent = parent,
262 .ref_count = 1,
263 },
264 });
265
266 parent.ref();
260 const self = try comp.gpa().createOne(CompTime);
261 self.* = CompTime{ .base = undefined };
262 self.base.init(Id.CompTime, parent);
267263 return self;
268264 }
269265
......@@ -289,20 +285,14 @@ pub const Scope = struct {
289285 kind: Kind,
290286 defer_expr_scope: *DeferExpr,
291287 ) !*Defer {
292 const self = try comp.gpa().create(Defer{
293 .base = Scope{
294 .id = Id.Defer,
295 .parent = parent,
296 .ref_count = 1,
297 },
288 const self = try comp.gpa().createOne(Defer);
289 self.* = Defer{
290 .base = undefined,
298291 .defer_expr_scope = defer_expr_scope,
299292 .kind = kind,
300 });
301 errdefer comp.gpa().destroy(self);
302
293 };
294 self.base.init(Id.Defer, parent);
303295 defer_expr_scope.base.ref();
304
305 parent.ref();
306296 return self;
307297 }
308298
......@@ -319,18 +309,13 @@ pub const Scope = struct {
319309
320310 /// Creates a DeferExpr scope with 1 reference
321311 pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
322 const self = try comp.gpa().create(DeferExpr{
323 .base = Scope{
324 .id = Id.DeferExpr,
325 .parent = parent,
326 .ref_count = 1,
327 },
312 const self = try comp.gpa().createOne(DeferExpr);
313 self.* = DeferExpr{
314 .base = undefined,
328315 .expr_node = expr_node,
329316 .reported_err = false,
330 });
331 errdefer comp.gpa().destroy(self);
332
333 parent.ref();
317 };
318 self.base.init(Id.DeferExpr, parent);
334319 return self;
335320 }
336321
......@@ -338,4 +323,74 @@ pub const Scope = struct {
338323 comp.gpa().destroy(self);
339324 }
340325 };
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 };
341396};
src-self-hosted/type.zig+314-57
......@@ -141,9 +141,13 @@ pub const Type = struct {
141141 Id.Promise,
142142 => return true,
143143
144 Id.Pointer => {
145 const ptr_type = @fieldParentPtr(Pointer, "base", base);
146 return ptr_type.key.child_type.hasBits();
147 },
148
144149 Id.ErrorSet => @panic("TODO"),
145150 Id.Enum => @panic("TODO"),
146 Id.Pointer => @panic("TODO"),
147151 Id.Struct => @panic("TODO"),
148152 Id.Array => @panic("TODO"),
149153 Id.Optional => @panic("TODO"),
......@@ -221,57 +225,294 @@ pub const Type = struct {
221225
222226 pub const Fn = struct {
223227 base: Type,
224 return_type: *Type,
225 params: []Param,
226 is_var_args: bool,
228 key: Key,
229 non_key: NonKey,
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
228378 pub const Param = struct {
229379 is_noalias: bool,
230380 typ: *Type,
231381 };
232382
233 pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
234 const result = try comp.gpa().create(Fn{
383 fn ccFnTypeStr(cc: CallingConvention) []const u8 {
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{
235418 .base = undefined,
236 .return_type = return_type,
237 .params = params,
238 .is_var_args = is_var_args,
239 });
240 errdefer comp.gpa().destroy(result);
419 .key = key,
420 .non_key = undefined,
421 .garbage_node = undefined,
422 };
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();
245 for (result.params) |param| {
246 param.typ.base.ref();
479 {
480 const held = await (async comp.fn_type_table.acquire() catch unreachable);
481 defer held.release();
482
483 _ = try held.value.put(&self.key, self);
247484 }
248 return result;
485 return self;
249486 }
250487
251488 pub fn destroy(self: *Fn, comp: *Compilation) void {
252 self.return_type.base.deref(comp);
253 for (self.params) |param| {
254 param.typ.base.deref(comp);
489 self.key.deref(comp);
490 switch (self.key.data) {
491 Kind.Generic => {},
492 Kind.Normal => {
493 self.non_key.Normal.variable_list.deinit();
494 },
255495 }
256496 comp.gpa().destroy(self);
257497 }
258498
259499 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) {
261502 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),
263504 };
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);
265506 defer allocator.free(llvm_param_types);
266507 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);
268509 }
269510
270511 return llvm.FunctionType(
271512 llvm_return_type,
272513 llvm_param_types.ptr,
273514 @intCast(c_uint, llvm_param_types.len),
274 @boolToInt(self.is_var_args),
515 @boolToInt(normal.is_var_args),
275516 ) orelse error.OutOfMemory;
276517 }
277518 };
......@@ -347,8 +588,10 @@ pub const Type = struct {
347588 is_signed: bool,
348589
349590 pub fn hash(self: *const Key) u32 {
350 const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
351 return rands[@boolToInt(self.is_signed)] *% self.bit_count;
591 var result: u32 = 0;
592 result +%= hashAny(self.is_signed, 0);
593 result +%= hashAny(self.bit_count, 1);
594 return result;
352595 }
353596
354597 pub fn eql(self: *const Key, other: *const Key) bool {
......@@ -443,15 +686,16 @@ pub const Type = struct {
443686 alignment: Align,
444687
445688 pub fn hash(self: *const Key) u32 {
446 const align_hash = switch (self.alignment) {
689 var result: u32 = 0;
690 result +%= switch (self.alignment) {
447691 Align.Abi => 0xf201c090,
448 Align.Override => |x| x,
692 Align.Override => |x| hashAny(x, 0),
449693 };
450 return hash_usize(@ptrToInt(self.child_type)) *%
451 hash_enum(self.mut) *%
452 hash_enum(self.vol) *%
453 hash_enum(self.size) *%
454 align_hash;
694 result +%= hashAny(self.child_type, 1);
695 result +%= hashAny(self.mut, 2);
696 result +%= hashAny(self.vol, 3);
697 result +%= hashAny(self.size, 4);
698 return result;
455699 }
456700
457701 pub fn eql(self: *const Key, other: *const Key) bool {
......@@ -605,7 +849,10 @@ pub const Type = struct {
605849 len: usize,
606850
607851 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;
609856 }
610857
611858 pub fn eql(self: *const Key, other: *const Key) bool {
......@@ -818,27 +1065,37 @@ pub const Type = struct {
8181065 };
8191066};
8201067
821fn hash_usize(x: usize) u32 {
822 return switch (@sizeOf(usize)) {
823 4 => x,
824 8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
825 else => @compileError("implement this hash function"),
826 };
827}
828
829fn hash_enum(x: var) u32 {
830 const rands = []u32{
831 0x85ebf64f,
832 0x3fcb3211,
833 0x240a4e8e,
834 0x40bb0e3c,
835 0x78be45af,
836 0x1ca98e37,
837 0xec56053a,
838 0x906adc48,
839 0xd4fe9763,
840 0x54c80dac,
841 };
842 comptime assert(@memberCount(@typeOf(x)) < rands.len);
843 return rands[@enumToInt(x)];
1068fn hashAny(x: var, comptime seed: u64) u32 {
1069 switch (@typeInfo(@typeOf(x))) {
1070 builtin.TypeId.Int => |info| {
1071 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1072 const unsigned_x = @bitCast(@IntType(false, info.bits), x);
1073 if (info.bits <= 32) {
1074 return u32(unsigned_x) *% comptime rng.random.scalar(u32);
1075 } else {
1076 return @truncate(u32, unsigned_x *% comptime rng.random.scalar(@typeOf(unsigned_x)));
1077 }
1078 },
1079 builtin.TypeId.Pointer => |info| {
1080 switch (info.size) {
1081 builtin.TypeInfo.Pointer.Size.One => return hashAny(@ptrToInt(x), seed),
1082 builtin.TypeInfo.Pointer.Size.Many => @compileError("implement hash function"),
1083 builtin.TypeInfo.Pointer.Size.Slice => @compileError("implement hash function"),
1084 }
1085 },
1086 builtin.TypeId.Enum => return hashAny(@enumToInt(x), seed),
1087 builtin.TypeId.Bool => {
1088 comptime var rng = comptime std.rand.DefaultPrng.init(seed);
1089 const vals = comptime [2]u32{ rng.random.scalar(u32), rng.random.scalar(u32) };
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 }
8441101}
src-self-hosted/value.zig+19-3
......@@ -60,7 +60,7 @@ pub const Value = struct {
6060 pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) {
6161 switch (base.id) {
6262 Id.Type => unreachable,
63 Id.Fn => @panic("TODO"),
63 Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile),
6464 Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
6565 Id.Void => return null,
6666 Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
......@@ -180,7 +180,7 @@ pub const Value = struct {
180180 child_scope: *Scope,
181181
182182 /// parent is child_scope
183 block_scope: *Scope.Block,
183 block_scope: ?*Scope.Block,
184184
185185 /// Path to the object file that contains this function
186186 containing_object: Buffer,
......@@ -205,7 +205,7 @@ pub const Value = struct {
205205 },
206206 .fndef_scope = fndef_scope,
207207 .child_scope = &fndef_scope.base,
208 .block_scope = undefined,
208 .block_scope = null,
209209 .symbol_name = symbol_name,
210210 .containing_object = Buffer.initNull(comp.gpa()),
211211 .link_set_node = link_set_node,
......@@ -231,6 +231,22 @@ pub const Value = struct {
231231 self.symbol_name.deinit();
232232 comp.gpa().destroy(self);
233233 }
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 }
234250 };
235251
236252 pub const Void = struct {
src/all_types.hpp+2-4
......@@ -60,7 +60,7 @@ struct IrExecutable {
6060 ZigList<Tld *> tld_list;
6161
6262 IrInstruction *coro_handle;
63 IrInstruction *coro_awaiter_field_ptr; // this one is shared and in the promise
63 IrInstruction *atomic_state_field_ptr; // this one is shared and in the promise
6464 IrInstruction *coro_result_ptr_field_ptr;
6565 IrInstruction *coro_result_field_ptr;
6666 IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise
......@@ -898,7 +898,6 @@ struct AstNodeAwaitExpr {
898898};
899899
900900struct AstNodeSuspend {
901 Buf *name;
902901 AstNode *block;
903902 AstNode *promise_symbol;
904903};
......@@ -1927,7 +1926,6 @@ struct ScopeLoop {
19271926struct ScopeSuspend {
19281927 Scope base;
19291928
1930 Buf *name;
19311929 IrBasicBlock *resume_block;
19321930 bool reported_err;
19331931};
......@@ -3243,7 +3241,7 @@ static const size_t stack_trace_ptr_count = 30;
32433241#define RESULT_FIELD_NAME "result"
32443242#define ASYNC_ALLOC_FIELD_NAME "allocFn"
32453243#define ASYNC_FREE_FIELD_NAME "freeFn"
3246#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"
3244#define ATOMIC_STATE_FIELD_NAME "atomic_state"
32473245// these point to data belonging to the awaiter
32483246#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
32493247#define RESULT_PTR_FIELD_NAME "result_ptr"
src/analyze.cpp+21-17
......@@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
161161 assert(node->type == NodeTypeSuspend);
162162 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
163163 init_scope(&scope->base, ScopeIdSuspend, node, parent);
164 scope->name = node->data.suspend.name;
165164 return scope;
166165}
167166
......@@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
519518 return return_type->promise_frame_parent;
520519 }
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;
523522 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
524523
525524 ZigList<const char *> field_names = {};
526 field_names.append(AWAITER_HANDLE_FIELD_NAME);
525 field_names.append(ATOMIC_STATE_FIELD_NAME);
527526 field_names.append(RESULT_FIELD_NAME);
528527 field_names.append(RESULT_PTR_FIELD_NAME);
529528 if (g->have_err_ret_tracing) {
......@@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
533532 }
534533
535534 ZigList<TypeTableEntry *> field_types = {};
536 field_types.append(awaiter_handle_type);
535 field_types.append(atomic_state_type);
537536 field_types.append(return_type);
538537 field_types.append(result_ptr_type);
539538 if (g->have_err_ret_tracing) {
......@@ -1585,10 +1584,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
15851584 case TypeTableEntryIdBlock:
15861585 case TypeTableEntryIdBoundFn:
15871586 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;
15921587 case TypeTableEntryIdVoid:
15931588 case TypeTableEntryIdBool:
15941589 case TypeTableEntryIdInt:
......@@ -1603,6 +1598,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
16031598 case TypeTableEntryIdUnion:
16041599 case TypeTableEntryIdFn:
16051600 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 }
16061608 break;
16071609 }
16081610 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) {
39383940 return nullptr;
39393941}
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) {
39423944 TypeTableEntry *fn_type = fn_table_entry->type_entry;
39433945 assert(!fn_type->data.fn.is_generic);
39443946 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
39763978 if (fn_type->data.fn.gen_param_info) {
39773979 var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index;
39783980 }
3979
3980 if (arg_vars) {
3981 arg_vars[i] = var;
3982 }
39833981 }
39843982}
39853983
......@@ -4057,7 +4055,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
40574055 }
40584056
40594057 if (g->verbose_ir) {
4060 fprintf(stderr, "{ // (analyzed)\n");
4058 fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name));
40614059 ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4);
40624060 fprintf(stderr, "}\n");
40634061 }
......@@ -4079,7 +4077,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) {
40794077 if (!fn_table_entry->child_scope)
40804078 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
40844082 TypeTableEntry *fn_type = fn_table_entry->type_entry;
40854083 assert(!fn_type->data.fn.is_generic);
......@@ -5019,9 +5017,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
50195017 } else {
50205018 return type_requires_comptime(type_entry->data.pointer.child_type);
50215019 }
5020 case TypeTableEntryIdFn:
5021 return type_entry->data.fn.is_generic;
50225022 case TypeTableEntryIdEnum:
50235023 case TypeTableEntryIdErrorSet:
5024 case TypeTableEntryIdFn:
50255024 case TypeTableEntryIdBool:
50265025 case TypeTableEntryIdInt:
50275026 case TypeTableEntryIdFloat:
......@@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) {
62286227 } else if (type_entry->id == TypeTableEntryIdOpaque) {
62296228 return 1;
62306229 } 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;
62326236 }
62336237}
62346238
src/ir.cpp+371-101
......@@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
30973097 return return_inst;
30983098 }
30993099
3100 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_field_ptr, return_value);
3101 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node,
3102 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
3103 // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig
3104 IrInstruction *replacement_value = irb->exec->coro_handle;
3105 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, scope, node,
3106 promise_type_val, irb->exec->coro_awaiter_field_ptr, nullptr, replacement_value, nullptr,
3107 AtomicRmwOp_xchg, AtomicOrderSeqCst);
3108 ir_build_store_ptr(irb, scope, node, irb->exec->await_handle_var_ptr, maybe_await_handle);
3109 IrInstruction *is_non_null = ir_build_test_nonnull(irb, scope, node, maybe_await_handle);
3100 IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "Suspended");
3101 IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "NotSuspended");
3102 IrBasicBlock *store_awaiter_block = ir_create_basic_block(irb, scope, "StoreAwaiter");
3103 IrBasicBlock *check_canceled_block = ir_create_basic_block(irb, scope, "CheckCanceled");
3104
3105 IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, scope, node, 0x7); // 0b111
3106 IrInstruction *ptr_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000
3107 IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001
3108 IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010
3109 IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_promise);
31103110 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,
3112 is_comptime);
3113 // the above blocks are rendered by ir_gen after the rest of codegen
3111 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
3112
3113 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);
31143141}
31153142
31163143static 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
52515278 if (body_result == irb->codegen->invalid_instruction)
52525279 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));
52555283 ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
5284 }
52565285
52575286 if (continue_expr_node) {
52585287 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
53315360 if (body_result == irb->codegen->invalid_instruction)
53325361 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));
53355365 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
5366 }
53365367
53375368 if (continue_expr_node) {
53385369 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
53925423 if (body_result == irb->codegen->invalid_instruction)
53935424 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));
53965428 ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
5429 }
53975430
53985431 if (continue_expr_node) {
53995432 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
61536186 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
61546187}
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
61656189static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
61666190 assert(node->type == NodeTypeBreak);
61676191
......@@ -6202,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
62026226 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
62036227 }
62046228 } else if (search_scope->id == ScopeIdSuspend) {
6205 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;
6206 if (node->data.break_expr.name != nullptr &&
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 }
6229 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block"));
6230 return irb->codegen->invalid_instruction;
62116231 }
62126232 search_scope = search_scope->parent;
62136233 }
......@@ -6643,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
66436663 async_allocator_type_value, is_var_args);
66446664}
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) {
66476738 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);
66506741 if (target_inst == irb->codegen->invalid_instruction)
66516742 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);
66546794}
66556795
6656static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
6796static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) {
66576797 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);
66606800 if (target_inst == irb->codegen->invalid_instruction)
66616801 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);
66646804}
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) {
66676807 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);
66706810 if (target_inst == irb->codegen->invalid_instruction)
66716811 return irb->codegen->invalid_instruction;
66726812
......@@ -6680,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
66806820 return irb->codegen->invalid_instruction;
66816821 }
66826822
6683 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
6823 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
66846824 if (scope_defer_expr) {
66856825 if (!scope_defer_expr->reported_err) {
66866826 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
66916831
66926832 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);
66956835 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
66986838 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);
67006840 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);
6702 ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6703 }
6704
6705 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
6706 IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,
6707 awaiter_handle_field_name);
6708
6709 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
6710 VariableTableEntry *result_var = ir_create_var(irb, node, parent_scope, nullptr,
6841 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6842 ir_build_store_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6843 }
6844
6845 IrBasicBlock *already_awaited_block = ir_create_basic_block(irb, scope, "AlreadyAwaited");
6846 IrBasicBlock *not_awaited_block = ir_create_basic_block(irb, scope, "NotAwaited");
6847 IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled");
6848 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, scope, "YesSuspend");
6849 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, scope, "NoSuspend");
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,
67116877 false, false, true, const_bool_false);
6712 IrInstruction *undefined_value = ir_build_const_undefined(irb, parent_scope, node);
6713 IrInstruction *target_promise_type = ir_build_typeof(irb, parent_scope, node, target_inst);
6714 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, parent_scope, node, target_promise_type);
6715 ir_build_await_bookkeeping(irb, parent_scope, node, promise_result_type);
6716 ir_build_var_decl(irb, parent_scope, node, result_var, promise_result_type, nullptr, undefined_value);
6717 IrInstruction *my_result_var_ptr = ir_build_var_ptr(irb, parent_scope, node, result_var);
6718 ir_build_store_ptr(irb, parent_scope, node, result_ptr_field_ptr, my_result_var_ptr);
6719 IrInstruction *save_token = ir_build_coro_save(irb, parent_scope, node, irb->exec->coro_handle);
6720 IrInstruction *promise_type_val = ir_build_const_type(irb, parent_scope, node,
6721 get_optional_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
6722 IrInstruction *maybe_await_handle = ir_build_atomic_rmw(irb, parent_scope, node,
6723 promise_type_val, awaiter_field_ptr, nullptr, irb->exec->coro_handle, nullptr,
6724 AtomicRmwOp_xchg, AtomicOrderSeqCst);
6725 IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);
6726 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");
6727 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");
6728 IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend");
6729 ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
6878 IrInstruction *target_promise_type = ir_build_typeof(irb, scope, node, target_inst);
6879 IrInstruction *promise_result_type = ir_build_promise_result_type(irb, scope, node, target_promise_type);
6880 ir_build_await_bookkeeping(irb, scope, node, promise_result_type);
6881 ir_build_var_decl(irb, 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);
6883 ir_build_store_ptr(irb, 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);
6885
6886 IrInstruction *coro_handle_addr = ir_build_ptr_to_int(irb, scope, node, irb->exec->coro_handle);
6887 IrInstruction *mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, coro_handle_addr, await_mask, false);
6888 IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6889 usize_type_val, atomic_state_ptr, nullptr, mask_bits, nullptr,
6890 AtomicRmwOp_or, AtomicOrderSeqCst);
6891
6892 IrInstruction *is_awaited_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, await_mask, false);
6893 IrInstruction *is_awaited_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_awaited_value, zero, false);
6894 ir_build_cond_br(irb, scope, node, is_awaited_bool, already_awaited_block, not_awaited_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
67316913 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
67326914 if (irb->codegen->have_err_ret_tracing) {
67336915 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);
6735 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_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);
6916 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
6917 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull);
6918 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
67376919 }
67386920 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);
67406922 // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to,
67416923 // 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);
6743 ir_build_store_ptr(irb, parent_scope, node, my_result_var_ptr, no_suspend_result);
6744 ir_build_cancel(irb, parent_scope, node, target_inst);
6745 ir_build_br(irb, parent_scope, node, merge_block, const_bool_false);
6924 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, scope, node, promise_result_ptr);
6925 ir_build_store_ptr(irb, scope, node, my_result_var_ptr, no_suspend_result);
6926 ir_build_cancel(irb, scope, node, target_inst);
6927 ir_build_br(irb, scope, node, merge_block, const_bool_false);
6928
67466929
67476930 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);
6749 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6750 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6931 IrInstruction *my_prev_atomic_value = ir_build_atomic_rmw(irb, scope, node,
6932 usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr,
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
67526949 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);
67546951 cases[0].block = resume_block;
6755 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);
6756 cases[1].block = cleanup_block;
6757 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6952 cases[1].value = ir_build_const_u8(irb, scope, node, 1);
6953 cases[1].block = destroy_block;
6954 ir_build_switch_br(irb, scope, node, suspend_code, irb->exec->coro_suspend_block,
67586955 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
67606961 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6761 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6762 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
6962 IrInstruction *my_mask_bits = ir_build_bin_op(irb, scope, node, IrBinOpBinOr, ptr_mask, is_canceled_mask, 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
67646980 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
67676983 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);
67696985}
67706986
67716987static 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
68047020
68057021 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
68067022 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6807
6808 IrInstruction *suspend_code;
7023 IrBasicBlock *suspended_block = ir_create_basic_block(irb, parent_scope, "AlreadySuspended");
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);
68097031 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;
68107069 if (node->data.suspend.block == nullptr) {
68117070 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
68127071 } else {
......@@ -6834,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
68347093 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
68357094 cases[0].block = resume_block;
68367095 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;
68387097 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
68397098 2, cases, const_bool_false, nullptr));
68407099
68417100 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);
68427108 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
68457111 ir_set_cursor_at_end_and_append_block(irb, resume_block);
68467112 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
70817347 IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
70827348 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);
7085 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
7086 awaiter_handle_field_name);
7087 ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value);
7350 Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME);
7351 irb->exec->atomic_state_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
7352 atomic_state_field_name);
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);
70887355 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
70897356 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
70907357 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
71027369 // coordinate with builtin.zig
71037370 Buf *index_name = buf_create_from_str("index");
71047371 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);
71067372 ir_build_store_ptr(irb, scope, node, index_ptr, zero);
71077373
71087374 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
72257491 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
72267492
72277493 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);
72297495 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
72307496 }
72317497
......@@ -12142,7 +12408,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
1214212408 result_type = ira->codegen->builtin_types.entry_invalid;
1214312409 } else if (type_requires_comptime(result_type)) {
1214412410 var_class_requires_const = true;
12145 if (!var->src_is_const && !is_comptime_var) {
12411 if (!var->gen_is_const && !is_comptime_var) {
1214612412 ir_add_error_node(ira, source_node,
1214712413 buf_sprintf("variable of type '%s' must be const or comptime",
1214812414 buf_ptr(&result_type->name)));
......@@ -12591,6 +12857,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
1259112857 }
1259212858
1259312859 Buf *param_name = param_decl_node->data.param_decl.name;
12860 if (!param_name) return false;
1259412861 if (!is_var_args) {
1259512862 VariableTableEntry *var = add_variable(ira->codegen, param_decl_node,
1259612863 *child_scope, param_name, true, arg_val, nullptr);
......@@ -18991,6 +19258,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
1899119258 return ira->codegen->builtin_types.entry_invalid;
1899219259 } else if (type_entry->id == TypeTableEntryIdErrorUnion) {
1899319260 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 }
1899419264 TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type,
1899519265 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
1899619266 PtrLenSingle,
src/ir_print.cpp+4
......@@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) {
4545}
4646
4747static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) {
48 if (instruction == nullptr) {
49 fprintf(irp->f, "(null)");
50 return;
51 }
4852 if (instruction->value.special != ConstValSpecialRuntime) {
4953 ir_print_const_value(irp, &instruction->value);
5054 } else {
src/parser.cpp+2-23
......@@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648648}
649649
650650/*
651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
652652*/
653653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654654 size_t orig_token_index = *token_index;
655655
656 Token *name_token = nullptr;
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;
656 Token *suspend_token = &pc->tokens->at(*token_index);
675657 if (suspend_token->id == TokenIdKeywordSuspend) {
676658 *token_index += 1;
677659 } else if (mandatory) {
......@@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
693675 }
694676
695677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
699678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
700679 ast_eat_token(pc, token_index, TokenIdBinOr);
701680 node->data.suspend.block = ast_parse_block(pc, token_index, true);
std/build.zig+21
......@@ -807,6 +807,7 @@ pub const LibExeObjStep = struct {
807807 disable_libc: bool,
808808 frameworks: BufSet,
809809 verbose_link: bool,
810 no_rosegment: bool,
810811
811812 // zig only stuff
812813 root_src: ?[]const u8,
......@@ -874,6 +875,7 @@ pub const LibExeObjStep = struct {
874875
875876 fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep {
876877 var self = LibExeObjStep{
878 .no_rosegment = false,
877879 .strip = false,
878880 .builder = builder,
879881 .verbose_link = false,
......@@ -914,6 +916,7 @@ pub const LibExeObjStep = struct {
914916
915917 fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep {
916918 var self = LibExeObjStep{
919 .no_rosegment = false,
917920 .builder = builder,
918921 .name = name,
919922 .kind = kind,
......@@ -953,6 +956,10 @@ pub const LibExeObjStep = struct {
953956 return self;
954957 }
955958
959 pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void {
960 self.no_rosegment = value;
961 }
962
956963 fn computeOutFileNames(self: *LibExeObjStep) void {
957964 switch (self.kind) {
958965 Kind.Obj => {
......@@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct {
13061313 }
13071314 }
13081315
1316 if (self.no_rosegment) {
1317 try zig_args.append("--no-rosegment");
1318 }
1319
13091320 try builder.spawnChild(zig_args.toSliceConst());
13101321
13111322 if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) {
......@@ -1598,6 +1609,7 @@ pub const TestStep = struct {
15981609 include_dirs: ArrayList([]const u8),
15991610 lib_paths: ArrayList([]const u8),
16001611 object_files: ArrayList([]const u8),
1612 no_rosegment: bool,
16011613
16021614 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
16031615 const step_name = builder.fmt("test {}", root_src);
......@@ -1615,9 +1627,14 @@ pub const TestStep = struct {
16151627 .include_dirs = ArrayList([]const u8).init(builder.allocator),
16161628 .lib_paths = ArrayList([]const u8).init(builder.allocator),
16171629 .object_files = ArrayList([]const u8).init(builder.allocator),
1630 .no_rosegment = false,
16181631 };
16191632 }
16201633
1634 pub fn setNoRoSegment(self: *TestStep, value: bool) void {
1635 self.no_rosegment = value;
1636 }
1637
16211638 pub fn addLibPath(self: *TestStep, path: []const u8) void {
16221639 self.lib_paths.append(path) catch unreachable;
16231640 }
......@@ -1761,6 +1778,10 @@ pub const TestStep = struct {
17611778 try zig_args.append(lib_path);
17621779 }
17631780
1781 if (self.no_rosegment) {
1782 try zig_args.append("--no-rosegment");
1783 }
1784
17641785 try builder.spawnChild(zig_args.toSliceConst());
17651786 }
17661787};
std/debug/index.zig+12-2
......@@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void {
2727 const stderr = getStderrStream() catch return;
2828 stderr.print(fmt, args) catch return;
2929}
30fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
30pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) {
3131 if (stderr_stream) |st| {
3232 return st;
3333 } else {
......@@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var,
172172 }
173173}
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
175185pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void {
176186 const AddressState = union(enum) {
177187 NotLookingForStartAddress,
......@@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_
205215 }
206216}
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 {
209219 switch (builtin.os) {
210220 builtin.Os.windows => return error.UnsupportedDebugInfo,
211221 builtin.Os.macosx => {
std/event/channel.zig+3-22
......@@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type {
7171 /// puts a data item in the channel. The promise completes when the value has been added to the
7272 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
7373 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
7974 suspend |handle| {
8075 var my_tick_node = Loop.NextTickNode{
8176 .next = undefined,
......@@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type {
9186 self.putters.put(&queue_node);
9287 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
9388
94 self.loop.onNextTick(dispatch_tick_node_ptr);
89 self.dispatch();
9590 }
9691 }
9792
9893 /// await this function to get an item from the channel. If the buffer is empty, the promise will
9994 /// complete when the next item is put in the channel.
10095 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
10696 // TODO integrate this function with named return values
10797 // so we can get rid of this extra result copy
10898 var result: T = undefined;
......@@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type {
121111 self.getters.put(&queue_node);
122112 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
123113
124 self.loop.onNextTick(dispatch_tick_node_ptr);
114 self.dispatch();
125115 }
126116 return result;
127117 }
128118
129 async fn dispatch(self: *SelfChannel, tick_node_ptr: **Loop.NextTickNode) void {
130 // resumed by onNextTick
131 suspend |handle| {
132 var tick_node = Loop.NextTickNode{
133 .data = handle,
134 .next = undefined,
135 };
136 tick_node_ptr.* = &tick_node;
137 }
138
119 fn dispatch(self: *SelfChannel) void {
139120 // set the "need dispatch" flag
140121 _ = @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 {
5555 /// After initialization, call run().
5656 /// TODO copy elision / named return values so that the threads referencing *Loop
5757 /// have the correct pointer value.
58 fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
58 pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void {
5959 return self.initInternal(allocator, 1);
6060 }
6161
......@@ -64,7 +64,7 @@ pub const Loop = struct {
6464 /// After initialization, call run().
6565 /// TODO copy elision / named return values so that the threads referencing *Loop
6666 /// have the correct pointer value.
67 fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
67 pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void {
6868 const core_count = try std.os.cpuCount(allocator);
6969 return self.initInternal(allocator, core_count);
7070 }
std/fmt/index.zig+34
......@@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
1818 OpenBrace,
1919 CloseBrace,
2020 FormatString,
21 Pointer,
2122 };
2223
2324 comptime var start_index = 0;
......@@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
5455 state = State.Start;
5556 start_index = i + 1;
5657 },
58 '*' => state = State.Pointer,
5759 else => {
5860 state = State.FormatString;
5961 },
......@@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
7577 },
7678 else => {},
7779 },
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 },
7891 }
7992 }
8093 comptime {
......@@ -861,6 +874,27 @@ test "fmt.format" {
861874 const value: u8 = 'a';
862875 try testFmt("u8: a\n", "u8: {c}\n", value);
863876 }
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 }
864898 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
865899 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
866900 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 {
200200 try self.readNoEof(input_slice);
201201 return mem.readInt(input_slice, T, endian);
202202 }
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 }
203210 };
204211}
205212
......@@ -230,6 +237,20 @@ pub fn OutStream(comptime WriteError: type) type {
230237 try self.writeFn(self, slice);
231238 }
232239 }
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 }
233254 };
234255}
235256
......@@ -331,6 +352,150 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type)
331352 };
332353}
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
334499pub fn BufferedOutStream(comptime Error: type) type {
335500 return BufferedOutStreamCustom(os.page_size, Error);
336501}
std/io_test.zig+72
......@@ -2,6 +2,7 @@ const std = @import("index.zig");
22const io = std.io;
33const DefaultPrng = std.rand.DefaultPrng;
44const assert = std.debug.assert;
5const assertError = std.debug.assertError;
56const mem = std.mem;
67const os = std.os;
78const builtin = @import("builtin");
......@@ -60,3 +61,74 @@ test "BufferOutStream" {
6061
6162 assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
6263}
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 {
1616 _ = @import("cases/bugs/828.zig");
1717 _ = @import("cases/bugs/920.zig");
1818 _ = @import("cases/byval_arg_var.zig");
19 _ = @import("cases/cancel.zig");
1920 _ = @import("cases/cast.zig");
2021 _ = @import("cases/const_slice_child.zig");
2122 _ = @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" {
244244 std.debug.assert(my_result == 2);
245245}
246246async fn testBreakFromSuspend(my_result: *i32) void {
247 s: suspend |p| {
248 break :s;
247 suspend |p| {
248 resume p;
249249 }
250250 my_result.* += 1;
251251 suspend;
test/compile_errors.zig+58
......@@ -1,6 +1,64 @@
11const tests = @import("tests.zig");
22
33pub 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
462 cases.add(
563 "optional pointer to void in extern struct",
664 \\comptime {
test/stage2/compare_output.zig+13
......@@ -2,6 +2,7 @@ const std = @import("std");
22const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
33
44pub fn addCases(ctx: *TestContext) !void {
5 // hello world
56 try ctx.testCompareOutputLibC(
67 \\extern fn puts([*]const u8) void;
78 \\export fn main() c_int {
......@@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void {
910 \\ return 0;
1011 \\}
1112 , "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);
1225}