| author | |
| committer | |
| log | f804310d9f953c9d78a4271ba8d75133341840e6 |
| tree | 71a9db15bcabbec32d6f2c795927603f9b278ceb |
| parent | dd9728c5a03844267bc378c326c353fd2b0e084e |
| parent | 058bfb254c4c0e1cfb254791f771c88c74f299e8 |
29 files changed, 1846 insertions(+), 395 deletions(-)
README.md-38| ... | ... | @@ -74,44 +74,6 @@ that counts as "freestanding" for the purposes of this table. |
| 74 | 74 | * Reddit: [/r/zig](https://www.reddit.com/r/zig) |
| 75 | 75 | * Email list: [ziglang@googlegroups.com](https://groups.google.com/forum/#!forum/ziglang) |
| 76 | 76 | |
| 77 | ### Wanted: Windows Developers | |
| 78 | ||
| 79 | Flesh out the standard library for Windows, streamline Zig installation and | |
| 80 | distribution for Windows. Work with LLVM and LLD teams to improve | |
| 81 | PDB/CodeView/MSVC debugging. Implement stack traces for Windows in the MinGW | |
| 82 | environment and the MSVC environment. | |
| 83 | ||
| 84 | ### Wanted: MacOS and iOS Developers | |
| 85 | ||
| 86 | Flesh out the standard library for MacOS. Improve the MACH-O linker. Implement | |
| 87 | stack traces for MacOS. Streamline the process of using Zig to build for | |
| 88 | iOS. | |
| 89 | ||
| 90 | ### Wanted: Android Developers | |
| 91 | ||
| 92 | Flesh out the standard library for Android. Streamline the process of using | |
| 93 | Zig to build for Android and for depending on Zig code on Android. | |
| 94 | ||
| 95 | ### Wanted: Web Developers | |
| 96 | ||
| 97 | Figure out what are the use cases for compiling Zig to WebAssembly. Create demo | |
| 98 | projects with it and streamline experience for users trying to output | |
| 99 | WebAssembly. Work on the documentation generator outputting useful searchable html | |
| 100 | documentation. Create Zig modules for common web tasks such as WebSockets and gzip. | |
| 101 | ||
| 102 | ### Wanted: Embedded Developers | |
| 103 | ||
| 104 | Flesh out the standard library for uncommon CPU architectures and OS targets. | |
| 105 | Drive issue discussion for cross compiling and using Zig in constrained | |
| 106 | or unusual environments. | |
| 107 | ||
| 108 | ### Wanted: Game Developers | |
| 109 | ||
| 110 | Create cross platform Zig modules to compete with SDL and GLFW. Create an | |
| 111 | OpenGL library that does not depend on libc. Drive the usability of Zig | |
| 112 | for video games. Create a general purpose allocator that does not depend on | |
| 113 | libc. Create demo games using Zig. | |
| 114 | ||
| 115 | 77 | ## Building |
| 116 | 78 | |
| 117 | 79 | [](https://travis-ci.org/ziglang/zig) |
build.zig+4| ... | ... | @@ -45,6 +45,7 @@ pub fn build(b: *Builder) !void { |
| 45 | 45 | .c_header_files = nextValue(&index, build_info), |
| 46 | 46 | .dia_guids_lib = nextValue(&index, build_info), |
| 47 | 47 | .llvm = undefined, |
| 48 | .no_rosegment = b.option(bool, "no-rosegment", "Workaround to enable valgrind builds") orelse false, | |
| 48 | 49 | }; |
| 49 | 50 | ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); |
| 50 | 51 | |
| ... | ... | @@ -228,6 +229,8 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void { |
| 228 | 229 | // TODO turn this into -Dextra-lib-path=/lib option |
| 229 | 230 | exe.addLibPath("/lib"); |
| 230 | 231 | |
| 232 | exe.setNoRoSegment(ctx.no_rosegment); | |
| 233 | ||
| 231 | 234 | exe.addIncludeDir("src"); |
| 232 | 235 | exe.addIncludeDir(ctx.cmake_binary_dir); |
| 233 | 236 | addCppLib(b, exe, ctx.cmake_binary_dir, "zig_cpp"); |
| ... | ... | @@ -286,4 +289,5 @@ const Context = struct { |
| 286 | 289 | c_header_files: []const u8, |
| 287 | 290 | dia_guids_lib: []const u8, |
| 288 | 291 | llvm: LibraryDep, |
| 292 | no_rosegment: bool, | |
| 289 | 293 | }; |
deps/lld/COFF/Driver.cpp+3| ... | ... | @@ -72,6 +72,9 @@ bool link(ArrayRef<const char *> Args, bool CanExitEarly, raw_ostream &Diag) { |
| 72 | 72 | exitLld(errorCount() ? 1 : 0); |
| 73 | 73 | |
| 74 | 74 | freeArena(); |
| 75 | ObjFile::Instances.clear(); | |
| 76 | ImportFile::Instances.clear(); | |
| 77 | BitcodeFile::Instances.clear(); | |
| 75 | 78 | return !errorCount(); |
| 76 | 79 | } |
| 77 | 80 |
doc/langref.html.in+59-7| ... | ... | @@ -134,6 +134,58 @@ pub fn main() void { |
| 134 | 134 | </p> |
| 135 | 135 | {#see_also|Values|@import|Errors|Root Source File#} |
| 136 | 136 | {#header_close#} |
| 137 | {#header_open|Comments#} | |
| 138 | {#code_begin|test|comments#} | |
| 139 | const assert = @import("std").debug.assert; | |
| 140 | ||
| 141 | test "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). | |
| 166 | const Timestamp = struct { | |
| 167 | /// The number of seconds since the epoch (this is also a doc comment). | |
| 168 | seconds: i64, // signed so we can represent pre-1970 (not a doc comment) | |
| 169 | /// The number of nanoseconds past the second (doc comment again). | |
| 170 | nanos: u32, | |
| 171 | ||
| 172 | /// Returns a `Timestamp` struct representing the Unix epoch; that is, the | |
| 173 | /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too). | |
| 174 | pub fn unixEpoch() Timestamp { | |
| 175 | return Timestamp{ | |
| 176 | .seconds = 0, | |
| 177 | .nanos = 0, | |
| 178 | }; | |
| 179 | } | |
| 180 | }; | |
| 181 | {#code_end#} | |
| 182 | <p> | |
| 183 | Doc comments are only allowed in certain places; eventually, it will | |
| 184 | become a compile error have a doc comment in an unexpected place, such as | |
| 185 | in the middle of an expression, or just before a non-doc comment. | |
| 186 | </p> | |
| 187 | {#header_close#} | |
| 188 | {#header_close#} | |
| 137 | 189 | {#header_open|Values#} |
| 138 | 190 | {#code_begin|exe|values#} |
| 139 | 191 | const std = @import("std"); |
| ... | ... | @@ -4665,24 +4717,24 @@ async fn testSuspendBlock() void { |
| 4665 | 4717 | block, while the old thread continued executing the suspend block. |
| 4666 | 4718 | </p> |
| 4667 | 4719 | <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 | |
| 4669 | 4721 | never returns to its resumer and continues executing. |
| 4670 | 4722 | </p> |
| 4671 | 4723 | {#code_begin|test#} |
| 4672 | 4724 | const std = @import("std"); |
| 4673 | 4725 | const assert = std.debug.assert; |
| 4674 | 4726 | |
| 4675 | test "break from suspend" { | |
| 4727 | test "resume from suspend" { | |
| 4676 | 4728 | var buf: [500]u8 = undefined; |
| 4677 | 4729 | var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; |
| 4678 | 4730 | var my_result: i32 = 1; |
| 4679 | const p = try async<a> testBreakFromSuspend(&my_result); | |
| 4731 | const p = try async<a> testResumeFromSuspend(&my_result); | |
| 4680 | 4732 | cancel p; |
| 4681 | 4733 | std.debug.assert(my_result == 2); |
| 4682 | 4734 | } |
| 4683 | async fn testBreakFromSuspend(my_result: *i32) void { | |
| 4684 | s: suspend |p| { | |
| 4685 | break :s; | |
| 4735 | async fn testResumeFromSuspend(my_result: *i32) void { | |
| 4736 | suspend |p| { | |
| 4737 | resume p; | |
| 4686 | 4738 | } |
| 4687 | 4739 | my_result.* += 1; |
| 4688 | 4740 | suspend; |
| ... | ... | @@ -7336,7 +7388,7 @@ Defer(body) = ("defer" | "deferror") body |
| 7336 | 7388 | |
| 7337 | 7389 | IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) |
| 7338 | 7390 | |
| 7339 | SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) | |
| 7391 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | |
| 7340 | 7392 | |
| 7341 | 7393 | IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) |
| 7342 | 7394 |
src-self-hosted/codegen.zig+159-4| ... | ... | @@ -6,6 +6,7 @@ const c = @import("c.zig"); |
| 6 | 6 | const ir = @import("ir.zig"); |
| 7 | 7 | const Value = @import("value.zig").Value; |
| 8 | 8 | const Type = @import("type.zig").Type; |
| 9 | const Scope = @import("scope.zig").Scope; | |
| 9 | 10 | const event = std.event; |
| 10 | 11 | const assert = std.debug.assert; |
| 11 | 12 | const DW = std.dwarf; |
| ... | ... | @@ -156,7 +157,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) |
| 156 | 157 | llvm_fn_type, |
| 157 | 158 | ) orelse return error.OutOfMemory; |
| 158 | 159 | |
| 159 | const want_fn_safety = fn_val.block_scope.safety.get(ofile.comp); | |
| 160 | const want_fn_safety = fn_val.block_scope.?.safety.get(ofile.comp); | |
| 160 | 161 | if (want_fn_safety and ofile.comp.haveLibC()) { |
| 161 | 162 | try addLLVMFnAttr(ofile, llvm_fn, "sspstrong"); |
| 162 | 163 | try addLLVMFnAttrStr(ofile, llvm_fn, "stack-protector-buffer-size", "4"); |
| ... | ... | @@ -168,6 +169,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) |
| 168 | 169 | //} |
| 169 | 170 | |
| 170 | 171 | const fn_type = fn_val.base.typ.cast(Type.Fn).?; |
| 172 | const fn_type_normal = &fn_type.key.data.Normal; | |
| 171 | 173 | |
| 172 | 174 | try addLLVMFnAttr(ofile, llvm_fn, "nounwind"); |
| 173 | 175 | //add_uwtable_attr(g, fn_table_entry->llvm_value); |
| ... | ... | @@ -209,7 +211,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) |
| 209 | 211 | // addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)err_ret_trace_arg_index, "nonnull"); |
| 210 | 212 | //} |
| 211 | 213 | |
| 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; | |
| 213 | 215 | |
| 214 | 216 | // build all basic blocks |
| 215 | 217 | for (code.basic_block_list.toSlice()) |bb| { |
| ... | ... | @@ -226,9 +228,86 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) |
| 226 | 228 | |
| 227 | 229 | // TODO set up error return tracing |
| 228 | 230 | // 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 | ||
| 230 | 280 | // 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 | } | |
| 232 | 311 | |
| 233 | 312 | for (code.basic_block_list.toSlice()) |current_block| { |
| 234 | 313 | llvm.PositionBuilderAtEnd(ofile.builder, current_block.llvm_block); |
| ... | ... | @@ -293,3 +372,79 @@ fn addLLVMFnAttrStr(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []cons |
| 293 | 372 | fn addLLVMFnAttrInt(ofile: *ObjectFile, fn_val: llvm.ValueRef, attr_name: []const u8, attr_val: u64) !void { |
| 294 | 373 | return addLLVMAttrInt(ofile, fn_val, @maxValue(llvm.AttributeIndex), attr_name, attr_val); |
| 295 | 374 | } |
| 375 | ||
| 376 | fn 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 | ||
| 392 | fn 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 | ||
| 396 | pub 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 | ||
| 407 | pub 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 | ||
| 423 | pub 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 | ||
| 432 | pub 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 | ||
| 445 | pub fn resolveAlign(ofile: *ObjectFile, alignment: Type.Pointer.Align, llvm_type: llvm.TypeRef) u32 { | |
| 446 | return switch (alignment) { | |
| 447 | Type.Pointer.Align.Abi => return llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, llvm_type), | |
| 448 | Type.Pointer.Align.Override => |a| a, | |
| 449 | }; | |
| 450 | } |
src-self-hosted/compilation.zig+67-5| ... | ... | @@ -35,6 +35,7 @@ const CInt = @import("c_int.zig").CInt; |
| 35 | 35 | pub const EventLoopLocal = struct { |
| 36 | 36 | loop: *event.Loop, |
| 37 | 37 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), |
| 38 | lld_lock: event.Lock, | |
| 38 | 39 | |
| 39 | 40 | /// TODO pool these so that it doesn't have to lock |
| 40 | 41 | prng: event.Locked(std.rand.DefaultPrng), |
| ... | ... | @@ -55,6 +56,7 @@ pub const EventLoopLocal = struct { |
| 55 | 56 | |
| 56 | 57 | return EventLoopLocal{ |
| 57 | 58 | .loop = loop, |
| 59 | .lld_lock = event.Lock.init(loop), | |
| 58 | 60 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), |
| 59 | 61 | .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), |
| 60 | 62 | .native_libc = event.Future(LibCInstallation).init(loop), |
| ... | ... | @@ -63,6 +65,7 @@ pub const EventLoopLocal = struct { |
| 63 | 65 | |
| 64 | 66 | /// Must be called only after EventLoop.run completes. |
| 65 | 67 | fn deinit(self: *EventLoopLocal) void { |
| 68 | self.lld_lock.deinit(); | |
| 66 | 69 | while (self.llvm_handle_pool.pop()) |node| { |
| 67 | 70 | c.LLVMContextDispose(node.data); |
| 68 | 71 | self.loop.allocator.destroy(node); |
| ... | ... | @@ -220,12 +223,14 @@ pub const Compilation = struct { |
| 220 | 223 | int_type_table: event.Locked(IntTypeTable), |
| 221 | 224 | array_type_table: event.Locked(ArrayTypeTable), |
| 222 | 225 | ptr_type_table: event.Locked(PtrTypeTable), |
| 226 | fn_type_table: event.Locked(FnTypeTable), | |
| 223 | 227 | |
| 224 | 228 | c_int_types: [CInt.list.len]*Type.Int, |
| 225 | 229 | |
| 226 | 230 | const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql); |
| 227 | 231 | const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); |
| 228 | 232 | const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); |
| 233 | const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql); | |
| 229 | 234 | const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); |
| 230 | 235 | |
| 231 | 236 | const CompileErrList = std.ArrayList(*Msg); |
| ... | ... | @@ -384,6 +389,7 @@ pub const Compilation = struct { |
| 384 | 389 | .int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)), |
| 385 | 390 | .array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)), |
| 386 | 391 | .ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)), |
| 392 | .fn_type_table = event.Locked(FnTypeTable).init(loop, FnTypeTable.init(loop.allocator)), | |
| 387 | 393 | .c_int_types = undefined, |
| 388 | 394 | |
| 389 | 395 | .meta_type = undefined, |
| ... | ... | @@ -414,6 +420,7 @@ pub const Compilation = struct { |
| 414 | 420 | comp.int_type_table.private_data.deinit(); |
| 415 | 421 | comp.array_type_table.private_data.deinit(); |
| 416 | 422 | comp.ptr_type_table.private_data.deinit(); |
| 423 | comp.fn_type_table.private_data.deinit(); | |
| 417 | 424 | comp.arena_allocator.deinit(); |
| 418 | 425 | comp.loop.allocator.destroy(comp); |
| 419 | 426 | } |
| ... | ... | @@ -1160,13 +1167,48 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 1160 | 1167 | fn_decl.value = Decl.Fn.Val{ .Fn = fn_val }; |
| 1161 | 1168 | symbol_name_consumed = true; |
| 1162 | 1169 | |
| 1170 | // Define local parameter variables | |
| 1171 | const root_scope = fn_decl.base.findRootScope(); | |
| 1172 | for (fn_type.key.data.Normal.params) |param, i| { | |
| 1173 | //AstNode *param_decl_node = get_param_decl_node(fn_table_entry, i); | |
| 1174 | const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", fn_decl.fn_proto.params.at(i).*); | |
| 1175 | const name_token = param_decl.name_token orelse { | |
| 1176 | try comp.addCompileError(root_scope, Span{ | |
| 1177 | .first = param_decl.firstToken(), | |
| 1178 | .last = param_decl.type_node.firstToken(), | |
| 1179 | }, "missing parameter name"); | |
| 1180 | return error.SemanticAnalysisFailed; | |
| 1181 | }; | |
| 1182 | const param_name = root_scope.tree.tokenSlice(name_token); | |
| 1183 | ||
| 1184 | // if (is_noalias && get_codegen_ptr_type(param_type) == nullptr) { | |
| 1185 | // add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); | |
| 1186 | // } | |
| 1187 | ||
| 1188 | // TODO check for shadowing | |
| 1189 | ||
| 1190 | const var_scope = try Scope.Var.createParam( | |
| 1191 | comp, | |
| 1192 | fn_val.child_scope, | |
| 1193 | param_name, | |
| 1194 | &param_decl.base, | |
| 1195 | i, | |
| 1196 | param.typ, | |
| 1197 | ); | |
| 1198 | fn_val.child_scope = &var_scope.base; | |
| 1199 | ||
| 1200 | try fn_type.non_key.Normal.variable_list.append(var_scope); | |
| 1201 | } | |
| 1202 | ||
| 1163 | 1203 | const analyzed_code = try await (async comp.genAndAnalyzeCode( |
| 1164 | &fndef_scope.base, | |
| 1204 | fn_val.child_scope, | |
| 1165 | 1205 | body_node, |
| 1166 | fn_type.return_type, | |
| 1206 | fn_type.key.data.Normal.return_type, | |
| 1167 | 1207 | ) catch unreachable); |
| 1168 | 1208 | errdefer analyzed_code.destroy(comp.gpa()); |
| 1169 | 1209 | |
| 1210 | assert(fn_val.block_scope != null); | |
| 1211 | ||
| 1170 | 1212 | // Kick off rendering to LLVM module, but it doesn't block the fn decl |
| 1171 | 1213 | // analysis from being complete. |
| 1172 | 1214 | try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); |
| ... | ... | @@ -1199,14 +1241,13 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn |
| 1199 | 1241 | |
| 1200 | 1242 | var params = ArrayList(Type.Fn.Param).init(comp.gpa()); |
| 1201 | 1243 | var params_consumed = false; |
| 1202 | defer if (params_consumed) { | |
| 1244 | defer if (!params_consumed) { | |
| 1203 | 1245 | for (params.toSliceConst()) |param| { |
| 1204 | 1246 | param.typ.base.deref(comp); |
| 1205 | 1247 | } |
| 1206 | 1248 | params.deinit(); |
| 1207 | 1249 | }; |
| 1208 | 1250 | |
| 1209 | const is_var_args = false; | |
| 1210 | 1251 | { |
| 1211 | 1252 | var it = fn_proto.params.iterator(0); |
| 1212 | 1253 | while (it.next()) |param_node_ptr| { |
| ... | ... | @@ -1219,8 +1260,29 @@ async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.Fn |
| 1219 | 1260 | }); |
| 1220 | 1261 | } |
| 1221 | 1262 | } |
| 1222 | const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args); | |
| 1263 | ||
| 1264 | const key = Type.Fn.Key{ | |
| 1265 | .alignment = null, | |
| 1266 | .data = Type.Fn.Key.Data{ | |
| 1267 | .Normal = Type.Fn.Key.Normal{ | |
| 1268 | .return_type = return_type, | |
| 1269 | .params = params.toOwnedSlice(), | |
| 1270 | .is_var_args = false, // TODO | |
| 1271 | .cc = Type.Fn.CallingConvention.Auto, // TODO | |
| 1272 | }, | |
| 1273 | }, | |
| 1274 | }; | |
| 1223 | 1275 | params_consumed = true; |
| 1276 | var key_consumed = false; | |
| 1277 | defer if (!key_consumed) { | |
| 1278 | for (key.data.Normal.params) |param| { | |
| 1279 | param.typ.base.deref(comp); | |
| 1280 | } | |
| 1281 | comp.gpa().free(key.data.Normal.params); | |
| 1282 | }; | |
| 1283 | ||
| 1284 | const fn_type = try await (async Type.Fn.get(comp, key) catch unreachable); | |
| 1285 | key_consumed = true; | |
| 1224 | 1286 | errdefer fn_type.base.base.deref(comp); |
| 1225 | 1287 | |
| 1226 | 1288 | return fn_type; |
src-self-hosted/ir.zig+181-24| ... | ... | @@ -10,8 +10,10 @@ const assert = std.debug.assert; |
| 10 | 10 | const Token = std.zig.Token; |
| 11 | 11 | const Span = @import("errmsg.zig").Span; |
| 12 | 12 | const llvm = @import("llvm.zig"); |
| 13 | const ObjectFile = @import("codegen.zig").ObjectFile; | |
| 13 | const codegen = @import("codegen.zig"); | |
| 14 | const ObjectFile = codegen.ObjectFile; | |
| 14 | 15 | const Decl = @import("decl.zig").Decl; |
| 16 | const mem = std.mem; | |
| 15 | 17 | |
| 16 | 18 | pub const LVal = enum { |
| 17 | 19 | None, |
| ... | ... | @@ -122,6 +124,8 @@ pub const Inst = struct { |
| 122 | 124 | Id.Br => return @fieldParentPtr(Br, "base", base).analyze(ira), |
| 123 | 125 | Id.AddImplicitReturnType => return @fieldParentPtr(AddImplicitReturnType, "base", base).analyze(ira), |
| 124 | 126 | Id.PtrType => return await (async @fieldParentPtr(PtrType, "base", base).analyze(ira) catch unreachable), |
| 127 | Id.VarPtr => return await (async @fieldParentPtr(VarPtr, "base", base).analyze(ira) catch unreachable), | |
| 128 | Id.LoadPtr => return await (async @fieldParentPtr(LoadPtr, "base", base).analyze(ira) catch unreachable), | |
| 125 | 129 | } |
| 126 | 130 | } |
| 127 | 131 | |
| ... | ... | @@ -130,6 +134,8 @@ pub const Inst = struct { |
| 130 | 134 | Id.Return => return @fieldParentPtr(Return, "base", base).render(ofile, fn_val), |
| 131 | 135 | Id.Const => return @fieldParentPtr(Const, "base", base).render(ofile, fn_val), |
| 132 | 136 | Id.Call => return @fieldParentPtr(Call, "base", base).render(ofile, fn_val), |
| 137 | Id.VarPtr => return @fieldParentPtr(VarPtr, "base", base).render(ofile, fn_val), | |
| 138 | Id.LoadPtr => return @fieldParentPtr(LoadPtr, "base", base).render(ofile, fn_val), | |
| 133 | 139 | Id.DeclRef => unreachable, |
| 134 | 140 | Id.PtrType => unreachable, |
| 135 | 141 | Id.Ref => @panic("TODO"), |
| ... | ... | @@ -248,6 +254,8 @@ pub const Inst = struct { |
| 248 | 254 | Call, |
| 249 | 255 | DeclRef, |
| 250 | 256 | PtrType, |
| 257 | VarPtr, | |
| 258 | LoadPtr, | |
| 251 | 259 | }; |
| 252 | 260 | |
| 253 | 261 | pub const Call = struct { |
| ... | ... | @@ -281,11 +289,13 @@ pub const Inst = struct { |
| 281 | 289 | return error.SemanticAnalysisFailed; |
| 282 | 290 | }; |
| 283 | 291 | |
| 284 | if (fn_type.params.len != self.params.args.len) { | |
| 292 | const fn_type_param_count = fn_type.paramCount(); | |
| 293 | ||
| 294 | if (fn_type_param_count != self.params.args.len) { | |
| 285 | 295 | try ira.addCompileError( |
| 286 | 296 | self.base.span, |
| 287 | 297 | "expected {} arguments, found {}", |
| 288 | fn_type.params.len, | |
| 298 | fn_type_param_count, | |
| 289 | 299 | self.params.args.len, |
| 290 | 300 | ); |
| 291 | 301 | return error.SemanticAnalysisFailed; |
| ... | ... | @@ -299,7 +309,7 @@ pub const Inst = struct { |
| 299 | 309 | .fn_ref = fn_ref, |
| 300 | 310 | .args = args, |
| 301 | 311 | }); |
| 302 | new_inst.val = IrVal{ .KnownType = fn_type.return_type }; | |
| 312 | new_inst.val = IrVal{ .KnownType = fn_type.key.data.Normal.return_type }; | |
| 303 | 313 | return new_inst; |
| 304 | 314 | } |
| 305 | 315 | |
| ... | ... | @@ -489,6 +499,133 @@ pub const Inst = struct { |
| 489 | 499 | } |
| 490 | 500 | }; |
| 491 | 501 | |
| 502 | pub const VarPtr = struct { | |
| 503 | base: Inst, | |
| 504 | params: Params, | |
| 505 | ||
| 506 | const Params = struct { | |
| 507 | var_scope: *Scope.Var, | |
| 508 | }; | |
| 509 | ||
| 510 | const ir_val_init = IrVal.Init.Unknown; | |
| 511 | ||
| 512 | pub fn dump(inst: *const VarPtr) void { | |
| 513 | std.debug.warn("{}", inst.params.var_scope.name); | |
| 514 | } | |
| 515 | ||
| 516 | pub fn hasSideEffects(inst: *const VarPtr) bool { | |
| 517 | return false; | |
| 518 | } | |
| 519 | ||
| 520 | pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst { | |
| 521 | switch (self.params.var_scope.data) { | |
| 522 | Scope.Var.Data.Const => @panic("TODO"), | |
| 523 | Scope.Var.Data.Param => |param| { | |
| 524 | const new_inst = try ira.irb.build( | |
| 525 | Inst.VarPtr, | |
| 526 | self.base.scope, | |
| 527 | self.base.span, | |
| 528 | Inst.VarPtr.Params{ .var_scope = self.params.var_scope }, | |
| 529 | ); | |
| 530 | const ptr_type = try await (async Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{ | |
| 531 | .child_type = param.typ, | |
| 532 | .mut = Type.Pointer.Mut.Const, | |
| 533 | .vol = Type.Pointer.Vol.Non, | |
| 534 | .size = Type.Pointer.Size.One, | |
| 535 | .alignment = Type.Pointer.Align.Abi, | |
| 536 | }) catch unreachable); | |
| 537 | new_inst.val = IrVal{ .KnownType = &ptr_type.base }; | |
| 538 | return new_inst; | |
| 539 | }, | |
| 540 | } | |
| 541 | } | |
| 542 | ||
| 543 | pub fn render(self: *VarPtr, ofile: *ObjectFile, fn_val: *Value.Fn) llvm.ValueRef { | |
| 544 | switch (self.params.var_scope.data) { | |
| 545 | Scope.Var.Data.Const => unreachable, // turned into Inst.Const in analyze pass | |
| 546 | Scope.Var.Data.Param => |param| return param.llvm_value, | |
| 547 | } | |
| 548 | } | |
| 549 | }; | |
| 550 | ||
| 551 | pub const LoadPtr = struct { | |
| 552 | base: Inst, | |
| 553 | params: Params, | |
| 554 | ||
| 555 | const Params = struct { | |
| 556 | target: *Inst, | |
| 557 | }; | |
| 558 | ||
| 559 | const ir_val_init = IrVal.Init.Unknown; | |
| 560 | ||
| 561 | pub fn dump(inst: *const LoadPtr) void {} | |
| 562 | ||
| 563 | pub fn hasSideEffects(inst: *const LoadPtr) bool { | |
| 564 | return false; | |
| 565 | } | |
| 566 | ||
| 567 | pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst { | |
| 568 | const target = try self.params.target.getAsParam(); | |
| 569 | const target_type = target.getKnownType(); | |
| 570 | if (target_type.id != Type.Id.Pointer) { | |
| 571 | try ira.addCompileError(self.base.span, "dereference of non pointer type '{}'", target_type.name); | |
| 572 | return error.SemanticAnalysisFailed; | |
| 573 | } | |
| 574 | const ptr_type = @fieldParentPtr(Type.Pointer, "base", target_type); | |
| 575 | // if (instr_is_comptime(ptr)) { | |
| 576 | // if (ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst || | |
| 577 | // ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar) | |
| 578 | // { | |
| 579 | // ConstExprValue *pointee = const_ptr_pointee(ira->codegen, &ptr->value); | |
| 580 | // if (pointee->special != ConstValSpecialRuntime) { | |
| 581 | // IrInstruction *result = ir_create_const(&ira->new_irb, source_instruction->scope, | |
| 582 | // source_instruction->source_node, child_type); | |
| 583 | // copy_const_val(&result->value, pointee, ptr->value.data.x_ptr.mut == ConstPtrMutComptimeConst); | |
| 584 | // result->value.type = child_type; | |
| 585 | // return result; | |
| 586 | // } | |
| 587 | // } | |
| 588 | // } | |
| 589 | const new_inst = try ira.irb.build( | |
| 590 | Inst.LoadPtr, | |
| 591 | self.base.scope, | |
| 592 | self.base.span, | |
| 593 | Inst.LoadPtr.Params{ .target = target }, | |
| 594 | ); | |
| 595 | new_inst.val = IrVal{ .KnownType = ptr_type.key.child_type }; | |
| 596 | return new_inst; | |
| 597 | } | |
| 598 | ||
| 599 | pub fn render(self: *LoadPtr, ofile: *ObjectFile, fn_val: *Value.Fn) !?llvm.ValueRef { | |
| 600 | const child_type = self.base.getKnownType(); | |
| 601 | if (!child_type.hasBits()) { | |
| 602 | return null; | |
| 603 | } | |
| 604 | const ptr = self.params.target.llvm_value.?; | |
| 605 | const ptr_type = self.params.target.getKnownType().cast(Type.Pointer).?; | |
| 606 | ||
| 607 | return try codegen.getHandleValue(ofile, ptr, ptr_type); | |
| 608 | ||
| 609 | //uint32_t unaligned_bit_count = ptr_type->data.pointer.unaligned_bit_count; | |
| 610 | //if (unaligned_bit_count == 0) | |
| 611 | // return get_handle_value(g, ptr, child_type, ptr_type); | |
| 612 | ||
| 613 | //bool big_endian = g->is_big_endian; | |
| 614 | ||
| 615 | //assert(!handle_is_ptr(child_type)); | |
| 616 | //LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, ""); | |
| 617 | ||
| 618 | //uint32_t bit_offset = ptr_type->data.pointer.bit_offset; | |
| 619 | //uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); | |
| 620 | //uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset; | |
| 621 | ||
| 622 | //LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); | |
| 623 | //LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); | |
| 624 | ||
| 625 | //return LLVMBuildTrunc(g->builder, shifted_value, child_type->type_ref, ""); | |
| 626 | } | |
| 627 | }; | |
| 628 | ||
| 492 | 629 | pub const PtrType = struct { |
| 493 | 630 | base: Inst, |
| 494 | 631 | params: Params, |
| ... | ... | @@ -1158,6 +1295,7 @@ pub const Builder = struct { |
| 1158 | 1295 | Scope.Id.Block, |
| 1159 | 1296 | Scope.Id.Defer, |
| 1160 | 1297 | Scope.Id.DeferExpr, |
| 1298 | Scope.Id.Var, | |
| 1161 | 1299 | => scope = scope.parent.?, |
| 1162 | 1300 | } |
| 1163 | 1301 | } |
| ... | ... | @@ -1259,8 +1397,8 @@ pub const Builder = struct { |
| 1259 | 1397 | var child_scope = outer_block_scope; |
| 1260 | 1398 | |
| 1261 | 1399 | 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; | |
| 1264 | 1402 | } |
| 1265 | 1403 | } |
| 1266 | 1404 | |
| ... | ... | @@ -1490,20 +1628,23 @@ pub const Builder = struct { |
| 1490 | 1628 | error.OutOfMemory => return error.OutOfMemory, |
| 1491 | 1629 | } |
| 1492 | 1630 | |
| 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 => {}, | |
| 1507 | 1648 | } |
| 1508 | 1649 | |
| 1509 | 1650 | //if (node->owner->any_imports_failed) { |
| ... | ... | @@ -1544,6 +1685,7 @@ pub const Builder = struct { |
| 1544 | 1685 | Scope.Id.Block, |
| 1545 | 1686 | Scope.Id.Decls, |
| 1546 | 1687 | Scope.Id.Root, |
| 1688 | Scope.Id.Var, | |
| 1547 | 1689 | => scope = scope.parent orelse break, |
| 1548 | 1690 | |
| 1549 | 1691 | Scope.Id.DeferExpr => unreachable, |
| ... | ... | @@ -1594,6 +1736,7 @@ pub const Builder = struct { |
| 1594 | 1736 | |
| 1595 | 1737 | Scope.Id.CompTime, |
| 1596 | 1738 | Scope.Id.Block, |
| 1739 | Scope.Id.Var, | |
| 1597 | 1740 | => scope = scope.parent orelse return is_noreturn, |
| 1598 | 1741 | |
| 1599 | 1742 | Scope.Id.DeferExpr => unreachable, |
| ... | ... | @@ -1672,8 +1815,10 @@ pub const Builder = struct { |
| 1672 | 1815 | Type.Pointer.Size, |
| 1673 | 1816 | LVal, |
| 1674 | 1817 | *Decl, |
| 1818 | *Scope.Var, | |
| 1675 | 1819 | => {}, |
| 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 | |
| 1677 | 1822 | else => @compileError("unrecognized type in Params: " ++ @typeName(FieldType)), |
| 1678 | 1823 | } |
| 1679 | 1824 | } |
| ... | ... | @@ -1771,18 +1916,30 @@ pub const Builder = struct { |
| 1771 | 1916 | //// the above blocks are rendered by ir_gen after the rest of codegen |
| 1772 | 1917 | } |
| 1773 | 1918 | |
| 1774 | async fn findDecl(irb: *Builder, scope: *Scope, name: []const u8) ?*Decl { | |
| 1919 | const Ident = union(enum) { | |
| 1920 | NotFound, | |
| 1921 | Decl: *Decl, | |
| 1922 | VarScope: *Scope.Var, | |
| 1923 | }; | |
| 1924 | ||
| 1925 | async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident { | |
| 1775 | 1926 | var s = scope; |
| 1776 | 1927 | while (true) { |
| 1777 | 1928 | switch (s.id) { |
| 1929 | Scope.Id.Root => return Ident.NotFound, | |
| 1778 | 1930 | Scope.Id.Decls => { |
| 1779 | 1931 | const decls = @fieldParentPtr(Scope.Decls, "base", s); |
| 1780 | 1932 | const table = await (async decls.getTableReadOnly() catch unreachable); |
| 1781 | 1933 | if (table.get(name)) |entry| { |
| 1782 | return entry.value; | |
| 1934 | return Ident{ .Decl = entry.value }; | |
| 1935 | } | |
| 1936 | }, | |
| 1937 | Scope.Id.Var => { | |
| 1938 | const var_scope = @fieldParentPtr(Scope.Var, "base", s); | |
| 1939 | if (mem.eql(u8, var_scope.name, name)) { | |
| 1940 | return Ident{ .VarScope = var_scope }; | |
| 1783 | 1941 | } |
| 1784 | 1942 | }, |
| 1785 | Scope.Id.Root => return null, | |
| 1786 | 1943 | else => {}, |
| 1787 | 1944 | } |
| 1788 | 1945 | s = s.parent.?; |
src-self-hosted/link.zig+23-10| ... | ... | @@ -80,15 +80,22 @@ pub async fn link(comp: *Compilation) !void { |
| 80 | 80 | |
| 81 | 81 | const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat()); |
| 82 | 82 | 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; | |
| 90 | 98 | } |
| 91 | return error.LinkFailed; | |
| 92 | 99 | } |
| 93 | 100 | } |
| 94 | 101 | |
| ... | ... | @@ -672,7 +679,13 @@ const DarwinPlatform = struct { |
| 672 | 679 | }; |
| 673 | 680 | |
| 674 | 681 | var had_extra: bool = undefined; |
| 675 | try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,); | |
| 682 | try darwinGetReleaseVersion( | |
| 683 | ver_str, | |
| 684 | &result.major, | |
| 685 | &result.minor, | |
| 686 | &result.micro, | |
| 687 | &had_extra, | |
| 688 | ); | |
| 676 | 689 | if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) { |
| 677 | 690 | return error.InvalidDarwinVersionString; |
| 678 | 691 | } |
| ... | ... | @@ -713,7 +726,7 @@ fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u3 |
| 713 | 726 | return error.InvalidDarwinVersionString; |
| 714 | 727 | |
| 715 | 728 | var start_pos: usize = 0; |
| 716 | for ([]*u32{major, minor, micro}) |v| { | |
| 729 | for ([]*u32{ major, minor, micro }) |v| { | |
| 717 | 730 | const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.'); |
| 718 | 731 | const end_pos = dot_pos orelse str.len; |
| 719 | 732 | v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString; |
src-self-hosted/llvm.zig+14-1| ... | ... | @@ -30,6 +30,7 @@ pub const AddGlobal = c.LLVMAddGlobal; |
| 30 | 30 | pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; |
| 31 | 31 | pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; |
| 32 | 32 | pub const ArrayType = c.LLVMArrayType; |
| 33 | pub const BuildLoad = c.LLVMBuildLoad; | |
| 33 | 34 | pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; |
| 34 | 35 | pub const ConstAllOnes = c.LLVMConstAllOnes; |
| 35 | 36 | pub const ConstArray = c.LLVMConstArray; |
| ... | ... | @@ -95,13 +96,25 @@ pub const SetInitializer = c.LLVMSetInitializer; |
| 95 | 96 | pub const SetLinkage = c.LLVMSetLinkage; |
| 96 | 97 | pub const SetTarget = c.LLVMSetTarget; |
| 97 | 98 | pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr; |
| 99 | pub const SetVolatile = c.LLVMSetVolatile; | |
| 98 | 100 | pub const StructTypeInContext = c.LLVMStructTypeInContext; |
| 99 | 101 | pub const TokenTypeInContext = c.LLVMTokenTypeInContext; |
| 100 | pub const TypeOf = c.LLVMTypeOf; | |
| 101 | 102 | pub const VoidTypeInContext = c.LLVMVoidTypeInContext; |
| 102 | 103 | pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; |
| 103 | 104 | pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; |
| 104 | 105 | |
| 106 | pub const GetElementType = LLVMGetElementType; | |
| 107 | extern fn LLVMGetElementType(Ty: TypeRef) TypeRef; | |
| 108 | ||
| 109 | pub const TypeOf = LLVMTypeOf; | |
| 110 | extern fn LLVMTypeOf(Val: ValueRef) TypeRef; | |
| 111 | ||
| 112 | pub const BuildStore = LLVMBuildStore; | |
| 113 | extern fn LLVMBuildStore(arg0: BuilderRef, Val: ValueRef, Ptr: ValueRef) ?ValueRef; | |
| 114 | ||
| 115 | pub const BuildAlloca = LLVMBuildAlloca; | |
| 116 | extern fn LLVMBuildAlloca(arg0: BuilderRef, Ty: TypeRef, Name: ?[*]const u8) ?ValueRef; | |
| 117 | ||
| 105 | 118 | pub const ConstInBoundsGEP = LLVMConstInBoundsGEP; |
| 106 | 119 | pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef; |
| 107 | 120 |
src-self-hosted/scope.zig+128-73| ... | ... | @@ -6,23 +6,26 @@ const Compilation = @import("compilation.zig").Compilation; |
| 6 | 6 | const mem = std.mem; |
| 7 | 7 | const ast = std.zig.ast; |
| 8 | 8 | const Value = @import("value.zig").Value; |
| 9 | const Type = @import("type.zig").Type; | |
| 9 | 10 | const ir = @import("ir.zig"); |
| 10 | 11 | const Span = @import("errmsg.zig").Span; |
| 11 | 12 | const assert = std.debug.assert; |
| 12 | 13 | const event = std.event; |
| 14 | const llvm = @import("llvm.zig"); | |
| 13 | 15 | |
| 14 | 16 | pub const Scope = struct { |
| 15 | 17 | id: Id, |
| 16 | 18 | parent: ?*Scope, |
| 17 | ref_count: usize, | |
| 19 | ref_count: std.atomic.Int(usize), | |
| 18 | 20 | |
| 21 | /// Thread-safe | |
| 19 | 22 | pub fn ref(base: *Scope) void { |
| 20 | base.ref_count += 1; | |
| 23 | _ = base.ref_count.incr(); | |
| 21 | 24 | } |
| 22 | 25 | |
| 26 | /// Thread-safe | |
| 23 | 27 | 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) { | |
| 26 | 29 | if (base.parent) |parent| parent.deref(comp); |
| 27 | 30 | switch (base.id) { |
| 28 | 31 | Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp), |
| ... | ... | @@ -32,6 +35,7 @@ pub const Scope = struct { |
| 32 | 35 | Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp), |
| 33 | 36 | Id.Defer => @fieldParentPtr(Defer, "base", base).destroy(comp), |
| 34 | 37 | Id.DeferExpr => @fieldParentPtr(DeferExpr, "base", base).destroy(comp), |
| 38 | Id.Var => @fieldParentPtr(Var, "base", base).destroy(comp), | |
| 35 | 39 | } |
| 36 | 40 | } |
| 37 | 41 | } |
| ... | ... | @@ -49,15 +53,15 @@ pub const Scope = struct { |
| 49 | 53 | var scope = base; |
| 50 | 54 | while (true) { |
| 51 | 55 | 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, | |
| 54 | 58 | |
| 55 | 59 | Id.Block, |
| 56 | 60 | Id.Defer, |
| 57 | 61 | Id.DeferExpr, |
| 58 | 62 | Id.CompTime, |
| 59 | Id.Root, | |
| 60 | => scope = scope.parent orelse return null, | |
| 63 | Id.Var, | |
| 64 | => scope = scope.parent.?, | |
| 61 | 65 | } |
| 62 | 66 | } |
| 63 | 67 | } |
| ... | ... | @@ -66,7 +70,7 @@ pub const Scope = struct { |
| 66 | 70 | var scope = base; |
| 67 | 71 | while (true) { |
| 68 | 72 | switch (scope.id) { |
| 69 | Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base), | |
| 73 | Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", scope), | |
| 70 | 74 | |
| 71 | 75 | Id.FnDef, |
| 72 | 76 | Id.Decls, |
| ... | ... | @@ -76,11 +80,21 @@ pub const Scope = struct { |
| 76 | 80 | Id.Defer, |
| 77 | 81 | Id.CompTime, |
| 78 | 82 | Id.Root, |
| 83 | Id.Var, | |
| 79 | 84 | => scope = scope.parent orelse return null, |
| 80 | 85 | } |
| 81 | 86 | } |
| 82 | 87 | } |
| 83 | 88 | |
| 89 | fn init(base: *Scope, id: Id, parent: *Scope) void { | |
| 90 | base.* = Scope{ | |
| 91 | .id = id, | |
| 92 | .parent = parent, | |
| 93 | .ref_count = std.atomic.Int(usize).init(1), | |
| 94 | }; | |
| 95 | parent.ref(); | |
| 96 | } | |
| 97 | ||
| 84 | 98 | pub const Id = enum { |
| 85 | 99 | Root, |
| 86 | 100 | Decls, |
| ... | ... | @@ -89,6 +103,7 @@ pub const Scope = struct { |
| 89 | 103 | CompTime, |
| 90 | 104 | Defer, |
| 91 | 105 | DeferExpr, |
| 106 | Var, | |
| 92 | 107 | }; |
| 93 | 108 | |
| 94 | 109 | pub const Root = struct { |
| ... | ... | @@ -100,16 +115,16 @@ pub const Scope = struct { |
| 100 | 115 | /// Takes ownership of realpath |
| 101 | 116 | /// Takes ownership of tree, will deinit and destroy when done. |
| 102 | 117 | pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root { |
| 103 | const self = try comp.gpa().create(Root{ | |
| 118 | const self = try comp.gpa().createOne(Root); | |
| 119 | self.* = Root{ | |
| 104 | 120 | .base = Scope{ |
| 105 | 121 | .id = Id.Root, |
| 106 | 122 | .parent = null, |
| 107 | .ref_count = 1, | |
| 123 | .ref_count = std.atomic.Int(usize).init(1), | |
| 108 | 124 | }, |
| 109 | 125 | .tree = tree, |
| 110 | 126 | .realpath = realpath, |
| 111 | }); | |
| 112 | errdefer comp.gpa().destroy(self); | |
| 127 | }; | |
| 113 | 128 | |
| 114 | 129 | return self; |
| 115 | 130 | } |
| ... | ... | @@ -137,16 +152,13 @@ pub const Scope = struct { |
| 137 | 152 | |
| 138 | 153 | /// Creates a Decls scope with 1 reference |
| 139 | 154 | 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, | |
| 146 | 158 | .table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())), |
| 147 | 159 | .name_future = event.Future(void).init(comp.loop), |
| 148 | }); | |
| 149 | parent.ref(); | |
| 160 | }; | |
| 161 | self.base.init(Id.Decls, parent); | |
| 150 | 162 | return self; |
| 151 | 163 | } |
| 152 | 164 | |
| ... | ... | @@ -199,21 +211,16 @@ pub const Scope = struct { |
| 199 | 211 | |
| 200 | 212 | /// Creates a Block scope with 1 reference |
| 201 | 213 | 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, | |
| 208 | 217 | .incoming_values = undefined, |
| 209 | 218 | .incoming_blocks = undefined, |
| 210 | 219 | .end_block = undefined, |
| 211 | 220 | .is_comptime = undefined, |
| 212 | 221 | .safety = Safety.Auto, |
| 213 | }); | |
| 214 | errdefer comp.gpa().destroy(self); | |
| 215 | ||
| 216 | parent.ref(); | |
| 222 | }; | |
| 223 | self.base.init(Id.Block, parent); | |
| 217 | 224 | return self; |
| 218 | 225 | } |
| 219 | 226 | |
| ... | ... | @@ -226,22 +233,17 @@ pub const Scope = struct { |
| 226 | 233 | base: Scope, |
| 227 | 234 | |
| 228 | 235 | /// 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, | |
| 230 | 237 | |
| 231 | 238 | /// Creates a FnDef scope with 1 reference |
| 232 | 239 | /// Must set the fn_val later |
| 233 | 240 | 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); | |
| 245 | 247 | return self; |
| 246 | 248 | } |
| 247 | 249 | |
| ... | ... | @@ -255,15 +257,9 @@ pub const Scope = struct { |
| 255 | 257 | |
| 256 | 258 | /// Creates a CompTime scope with 1 reference |
| 257 | 259 | 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); | |
| 267 | 263 | return self; |
| 268 | 264 | } |
| 269 | 265 | |
| ... | ... | @@ -289,20 +285,14 @@ pub const Scope = struct { |
| 289 | 285 | kind: Kind, |
| 290 | 286 | defer_expr_scope: *DeferExpr, |
| 291 | 287 | ) !*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, | |
| 298 | 291 | .defer_expr_scope = defer_expr_scope, |
| 299 | 292 | .kind = kind, |
| 300 | }); | |
| 301 | errdefer comp.gpa().destroy(self); | |
| 302 | ||
| 293 | }; | |
| 294 | self.base.init(Id.Defer, parent); | |
| 303 | 295 | defer_expr_scope.base.ref(); |
| 304 | ||
| 305 | parent.ref(); | |
| 306 | 296 | return self; |
| 307 | 297 | } |
| 308 | 298 | |
| ... | ... | @@ -319,18 +309,13 @@ pub const Scope = struct { |
| 319 | 309 | |
| 320 | 310 | /// Creates a DeferExpr scope with 1 reference |
| 321 | 311 | 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, | |
| 328 | 315 | .expr_node = expr_node, |
| 329 | 316 | .reported_err = false, |
| 330 | }); | |
| 331 | errdefer comp.gpa().destroy(self); | |
| 332 | ||
| 333 | parent.ref(); | |
| 317 | }; | |
| 318 | self.base.init(Id.DeferExpr, parent); | |
| 334 | 319 | return self; |
| 335 | 320 | } |
| 336 | 321 | |
| ... | ... | @@ -338,4 +323,74 @@ pub const Scope = struct { |
| 338 | 323 | comp.gpa().destroy(self); |
| 339 | 324 | } |
| 340 | 325 | }; |
| 326 | ||
| 327 | pub const Var = struct { | |
| 328 | base: Scope, | |
| 329 | name: []const u8, | |
| 330 | src_node: *ast.Node, | |
| 331 | data: Data, | |
| 332 | ||
| 333 | pub const Data = union(enum) { | |
| 334 | Param: Param, | |
| 335 | Const: *Value, | |
| 336 | }; | |
| 337 | ||
| 338 | pub const Param = struct { | |
| 339 | index: usize, | |
| 340 | typ: *Type, | |
| 341 | llvm_value: llvm.ValueRef, | |
| 342 | }; | |
| 343 | ||
| 344 | pub fn createParam( | |
| 345 | comp: *Compilation, | |
| 346 | parent: *Scope, | |
| 347 | name: []const u8, | |
| 348 | src_node: *ast.Node, | |
| 349 | param_index: usize, | |
| 350 | param_type: *Type, | |
| 351 | ) !*Var { | |
| 352 | const self = try create(comp, parent, name, src_node); | |
| 353 | self.data = Data{ | |
| 354 | .Param = Param{ | |
| 355 | .index = param_index, | |
| 356 | .typ = param_type, | |
| 357 | .llvm_value = undefined, | |
| 358 | }, | |
| 359 | }; | |
| 360 | return self; | |
| 361 | } | |
| 362 | ||
| 363 | pub fn createConst( | |
| 364 | comp: *Compilation, | |
| 365 | parent: *Scope, | |
| 366 | name: []const u8, | |
| 367 | src_node: *ast.Node, | |
| 368 | value: *Value, | |
| 369 | ) !*Var { | |
| 370 | const self = try create(comp, parent, name, src_node); | |
| 371 | self.data = Data{ .Const = value }; | |
| 372 | value.ref(); | |
| 373 | return self; | |
| 374 | } | |
| 375 | ||
| 376 | fn create(comp: *Compilation, parent: *Scope, name: []const u8, src_node: *ast.Node) !*Var { | |
| 377 | const self = try comp.gpa().createOne(Var); | |
| 378 | self.* = Var{ | |
| 379 | .base = undefined, | |
| 380 | .name = name, | |
| 381 | .src_node = src_node, | |
| 382 | .data = undefined, | |
| 383 | }; | |
| 384 | self.base.init(Id.Var, parent); | |
| 385 | return self; | |
| 386 | } | |
| 387 | ||
| 388 | pub fn destroy(self: *Var, comp: *Compilation) void { | |
| 389 | switch (self.data) { | |
| 390 | Data.Param => {}, | |
| 391 | Data.Const => |value| value.deref(comp), | |
| 392 | } | |
| 393 | comp.gpa().destroy(self); | |
| 394 | } | |
| 395 | }; | |
| 341 | 396 | }; |
src-self-hosted/type.zig+314-57| ... | ... | @@ -141,9 +141,13 @@ pub const Type = struct { |
| 141 | 141 | Id.Promise, |
| 142 | 142 | => return true, |
| 143 | 143 | |
| 144 | Id.Pointer => { | |
| 145 | const ptr_type = @fieldParentPtr(Pointer, "base", base); | |
| 146 | return ptr_type.key.child_type.hasBits(); | |
| 147 | }, | |
| 148 | ||
| 144 | 149 | Id.ErrorSet => @panic("TODO"), |
| 145 | 150 | Id.Enum => @panic("TODO"), |
| 146 | Id.Pointer => @panic("TODO"), | |
| 147 | 151 | Id.Struct => @panic("TODO"), |
| 148 | 152 | Id.Array => @panic("TODO"), |
| 149 | 153 | Id.Optional => @panic("TODO"), |
| ... | ... | @@ -221,57 +225,294 @@ pub const Type = struct { |
| 221 | 225 | |
| 222 | 226 | pub const Fn = struct { |
| 223 | 227 | 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 | }; | |
| 227 | 377 | |
| 228 | 378 | pub const Param = struct { |
| 229 | 379 | is_noalias: bool, |
| 230 | 380 | typ: *Type, |
| 231 | 381 | }; |
| 232 | 382 | |
| 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{ | |
| 235 | 418 | .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 | } | |
| 241 | 476 | |
| 242 | result.base.init(comp, Id.Fn, "TODO fn type name"); | |
| 477 | self.base.init(comp, Id.Fn, name_buf.toOwnedSlice()); | |
| 243 | 478 | |
| 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); | |
| 247 | 484 | } |
| 248 | return result; | |
| 485 | return self; | |
| 249 | 486 | } |
| 250 | 487 | |
| 251 | 488 | 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 | }, | |
| 255 | 495 | } |
| 256 | 496 | comp.gpa().destroy(self); |
| 257 | 497 | } |
| 258 | 498 | |
| 259 | 499 | pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef { |
| 260 | const llvm_return_type = switch (self.return_type.id) { | |
| 500 | const normal = &self.key.data.Normal; | |
| 501 | const llvm_return_type = switch (normal.return_type.id) { | |
| 261 | 502 | Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory, |
| 262 | else => try self.return_type.getLlvmType(allocator, llvm_context), | |
| 503 | else => try normal.return_type.getLlvmType(allocator, llvm_context), | |
| 263 | 504 | }; |
| 264 | const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len); | |
| 505 | const llvm_param_types = try allocator.alloc(llvm.TypeRef, normal.params.len); | |
| 265 | 506 | defer allocator.free(llvm_param_types); |
| 266 | 507 | for (llvm_param_types) |*llvm_param_type, i| { |
| 267 | llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context); | |
| 508 | llvm_param_type.* = try normal.params[i].typ.getLlvmType(allocator, llvm_context); | |
| 268 | 509 | } |
| 269 | 510 | |
| 270 | 511 | return llvm.FunctionType( |
| 271 | 512 | llvm_return_type, |
| 272 | 513 | llvm_param_types.ptr, |
| 273 | 514 | @intCast(c_uint, llvm_param_types.len), |
| 274 | @boolToInt(self.is_var_args), | |
| 515 | @boolToInt(normal.is_var_args), | |
| 275 | 516 | ) orelse error.OutOfMemory; |
| 276 | 517 | } |
| 277 | 518 | }; |
| ... | ... | @@ -347,8 +588,10 @@ pub const Type = struct { |
| 347 | 588 | is_signed: bool, |
| 348 | 589 | |
| 349 | 590 | 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; | |
| 352 | 595 | } |
| 353 | 596 | |
| 354 | 597 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | ... | @@ -443,15 +686,16 @@ pub const Type = struct { |
| 443 | 686 | alignment: Align, |
| 444 | 687 | |
| 445 | 688 | pub fn hash(self: *const Key) u32 { |
| 446 | const align_hash = switch (self.alignment) { | |
| 689 | var result: u32 = 0; | |
| 690 | result +%= switch (self.alignment) { | |
| 447 | 691 | Align.Abi => 0xf201c090, |
| 448 | Align.Override => |x| x, | |
| 692 | Align.Override => |x| hashAny(x, 0), | |
| 449 | 693 | }; |
| 450 | return hash_usize(@ptrToInt(self.child_type)) *% | |
| 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; | |
| 455 | 699 | } |
| 456 | 700 | |
| 457 | 701 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | ... | @@ -605,7 +849,10 @@ pub const Type = struct { |
| 605 | 849 | len: usize, |
| 606 | 850 | |
| 607 | 851 | pub fn hash(self: *const Key) u32 { |
| 608 | return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len); | |
| 852 | var result: u32 = 0; | |
| 853 | result +%= hashAny(self.elem_type, 0); | |
| 854 | result +%= hashAny(self.len, 1); | |
| 855 | return result; | |
| 609 | 856 | } |
| 610 | 857 | |
| 611 | 858 | pub fn eql(self: *const Key, other: *const Key) bool { |
| ... | ... | @@ -818,27 +1065,37 @@ pub const Type = struct { |
| 818 | 1065 | }; |
| 819 | 1066 | }; |
| 820 | 1067 | |
| 821 | fn 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 | ||
| 829 | fn 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)]; | |
| 1068 | fn 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 | } | |
| 844 | 1101 | } |
src-self-hosted/value.zig+19-3| ... | ... | @@ -60,7 +60,7 @@ pub const Value = struct { |
| 60 | 60 | pub fn getLlvmConst(base: *Value, ofile: *ObjectFile) (error{OutOfMemory}!?llvm.ValueRef) { |
| 61 | 61 | switch (base.id) { |
| 62 | 62 | Id.Type => unreachable, |
| 63 | Id.Fn => @panic("TODO"), | |
| 63 | Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmConst(ofile), | |
| 64 | 64 | Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile), |
| 65 | 65 | Id.Void => return null, |
| 66 | 66 | Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile), |
| ... | ... | @@ -180,7 +180,7 @@ pub const Value = struct { |
| 180 | 180 | child_scope: *Scope, |
| 181 | 181 | |
| 182 | 182 | /// parent is child_scope |
| 183 | block_scope: *Scope.Block, | |
| 183 | block_scope: ?*Scope.Block, | |
| 184 | 184 | |
| 185 | 185 | /// Path to the object file that contains this function |
| 186 | 186 | containing_object: Buffer, |
| ... | ... | @@ -205,7 +205,7 @@ pub const Value = struct { |
| 205 | 205 | }, |
| 206 | 206 | .fndef_scope = fndef_scope, |
| 207 | 207 | .child_scope = &fndef_scope.base, |
| 208 | .block_scope = undefined, | |
| 208 | .block_scope = null, | |
| 209 | 209 | .symbol_name = symbol_name, |
| 210 | 210 | .containing_object = Buffer.initNull(comp.gpa()), |
| 211 | 211 | .link_set_node = link_set_node, |
| ... | ... | @@ -231,6 +231,22 @@ pub const Value = struct { |
| 231 | 231 | self.symbol_name.deinit(); |
| 232 | 232 | comp.gpa().destroy(self); |
| 233 | 233 | } |
| 234 | ||
| 235 | /// We know that the function definition will end up in an .o file somewhere. | |
| 236 | /// Here, all we have to do is generate a global prototype. | |
| 237 | /// TODO cache the prototype per ObjectFile | |
| 238 | pub fn getLlvmConst(self: *Fn, ofile: *ObjectFile) !?llvm.ValueRef { | |
| 239 | const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context); | |
| 240 | const llvm_fn = llvm.AddFunction( | |
| 241 | ofile.module, | |
| 242 | self.symbol_name.ptr(), | |
| 243 | llvm_fn_type, | |
| 244 | ) orelse return error.OutOfMemory; | |
| 245 | ||
| 246 | // TODO port more logic from codegen.cpp:fn_llvm_value | |
| 247 | ||
| 248 | return llvm_fn; | |
| 249 | } | |
| 234 | 250 | }; |
| 235 | 251 | |
| 236 | 252 | pub const Void = struct { |
src/all_types.hpp+2-4| ... | ... | @@ -60,7 +60,7 @@ struct IrExecutable { |
| 60 | 60 | ZigList<Tld *> tld_list; |
| 61 | 61 | |
| 62 | 62 | 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 | |
| 64 | 64 | IrInstruction *coro_result_ptr_field_ptr; |
| 65 | 65 | IrInstruction *coro_result_field_ptr; |
| 66 | 66 | IrInstruction *await_handle_var_ptr; // this one is where we put the one we extracted from the promise |
| ... | ... | @@ -898,7 +898,6 @@ struct AstNodeAwaitExpr { |
| 898 | 898 | }; |
| 899 | 899 | |
| 900 | 900 | struct AstNodeSuspend { |
| 901 | Buf *name; | |
| 902 | 901 | AstNode *block; |
| 903 | 902 | AstNode *promise_symbol; |
| 904 | 903 | }; |
| ... | ... | @@ -1927,7 +1926,6 @@ struct ScopeLoop { |
| 1927 | 1926 | struct ScopeSuspend { |
| 1928 | 1927 | Scope base; |
| 1929 | 1928 | |
| 1930 | Buf *name; | |
| 1931 | 1929 | IrBasicBlock *resume_block; |
| 1932 | 1930 | bool reported_err; |
| 1933 | 1931 | }; |
| ... | ... | @@ -3243,7 +3241,7 @@ static const size_t stack_trace_ptr_count = 30; |
| 3243 | 3241 | #define RESULT_FIELD_NAME "result" |
| 3244 | 3242 | #define ASYNC_ALLOC_FIELD_NAME "allocFn" |
| 3245 | 3243 | #define ASYNC_FREE_FIELD_NAME "freeFn" |
| 3246 | #define AWAITER_HANDLE_FIELD_NAME "awaiter_handle" | |
| 3244 | #define ATOMIC_STATE_FIELD_NAME "atomic_state" | |
| 3247 | 3245 | // these point to data belonging to the awaiter |
| 3248 | 3246 | #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" |
| 3249 | 3247 | #define RESULT_PTR_FIELD_NAME "result_ptr" |
src/analyze.cpp+21-17| ... | ... | @@ -161,7 +161,6 @@ ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) { |
| 161 | 161 | assert(node->type == NodeTypeSuspend); |
| 162 | 162 | ScopeSuspend *scope = allocate<ScopeSuspend>(1); |
| 163 | 163 | init_scope(&scope->base, ScopeIdSuspend, node, parent); |
| 164 | scope->name = node->data.suspend.name; | |
| 165 | 164 | return scope; |
| 166 | 165 | } |
| 167 | 166 | |
| ... | ... | @@ -519,11 +518,11 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) |
| 519 | 518 | return return_type->promise_frame_parent; |
| 520 | 519 | } |
| 521 | 520 | |
| 522 | TypeTableEntry *awaiter_handle_type = get_optional_type(g, g->builtin_types.entry_promise); | |
| 521 | TypeTableEntry *atomic_state_type = g->builtin_types.entry_usize; | |
| 523 | 522 | TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); |
| 524 | 523 | |
| 525 | 524 | ZigList<const char *> field_names = {}; |
| 526 | field_names.append(AWAITER_HANDLE_FIELD_NAME); | |
| 525 | field_names.append(ATOMIC_STATE_FIELD_NAME); | |
| 527 | 526 | field_names.append(RESULT_FIELD_NAME); |
| 528 | 527 | field_names.append(RESULT_PTR_FIELD_NAME); |
| 529 | 528 | if (g->have_err_ret_tracing) { |
| ... | ... | @@ -533,7 +532,7 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) |
| 533 | 532 | } |
| 534 | 533 | |
| 535 | 534 | ZigList<TypeTableEntry *> field_types = {}; |
| 536 | field_types.append(awaiter_handle_type); | |
| 535 | field_types.append(atomic_state_type); | |
| 537 | 536 | field_types.append(return_type); |
| 538 | 537 | field_types.append(result_ptr_type); |
| 539 | 538 | if (g->have_err_ret_tracing) { |
| ... | ... | @@ -1585,10 +1584,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1585 | 1584 | case TypeTableEntryIdBlock: |
| 1586 | 1585 | case TypeTableEntryIdBoundFn: |
| 1587 | 1586 | case TypeTableEntryIdMetaType: |
| 1588 | add_node_error(g, param_node->data.param_decl.type, | |
| 1589 | buf_sprintf("parameter of type '%s' must be declared comptime", | |
| 1590 | buf_ptr(&type_entry->name))); | |
| 1591 | return g->builtin_types.entry_invalid; | |
| 1592 | 1587 | case TypeTableEntryIdVoid: |
| 1593 | 1588 | case TypeTableEntryIdBool: |
| 1594 | 1589 | case TypeTableEntryIdInt: |
| ... | ... | @@ -1603,6 +1598,13 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c |
| 1603 | 1598 | case TypeTableEntryIdUnion: |
| 1604 | 1599 | case TypeTableEntryIdFn: |
| 1605 | 1600 | case TypeTableEntryIdPromise: |
| 1601 | type_ensure_zero_bits_known(g, type_entry); | |
| 1602 | if (type_requires_comptime(type_entry)) { | |
| 1603 | add_node_error(g, param_node->data.param_decl.type, | |
| 1604 | buf_sprintf("parameter of type '%s' must be declared comptime", | |
| 1605 | buf_ptr(&type_entry->name))); | |
| 1606 | return g->builtin_types.entry_invalid; | |
| 1607 | } | |
| 1606 | 1608 | break; |
| 1607 | 1609 | } |
| 1608 | 1610 | FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; |
| ... | ... | @@ -3938,7 +3940,7 @@ AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) { |
| 3938 | 3940 | return nullptr; |
| 3939 | 3941 | } |
| 3940 | 3942 | |
| 3941 | static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, VariableTableEntry **arg_vars) { | |
| 3943 | static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry) { | |
| 3942 | 3944 | TypeTableEntry *fn_type = fn_table_entry->type_entry; |
| 3943 | 3945 | assert(!fn_type->data.fn.is_generic); |
| 3944 | 3946 | FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; |
| ... | ... | @@ -3976,10 +3978,6 @@ static void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entr |
| 3976 | 3978 | if (fn_type->data.fn.gen_param_info) { |
| 3977 | 3979 | var->gen_arg_index = fn_type->data.fn.gen_param_info[i].gen_index; |
| 3978 | 3980 | } |
| 3979 | ||
| 3980 | if (arg_vars) { | |
| 3981 | arg_vars[i] = var; | |
| 3982 | } | |
| 3983 | 3981 | } |
| 3984 | 3982 | } |
| 3985 | 3983 | |
| ... | ... | @@ -4057,7 +4055,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ |
| 4057 | 4055 | } |
| 4058 | 4056 | |
| 4059 | 4057 | if (g->verbose_ir) { |
| 4060 | fprintf(stderr, "{ // (analyzed)\n"); | |
| 4058 | fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn_table_entry->symbol_name)); | |
| 4061 | 4059 | ir_print(g, stderr, &fn_table_entry->analyzed_executable, 4); |
| 4062 | 4060 | fprintf(stderr, "}\n"); |
| 4063 | 4061 | } |
| ... | ... | @@ -4079,7 +4077,7 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 4079 | 4077 | if (!fn_table_entry->child_scope) |
| 4080 | 4078 | fn_table_entry->child_scope = &fn_table_entry->fndef_scope->base; |
| 4081 | 4079 | |
| 4082 | define_local_param_variables(g, fn_table_entry, nullptr); | |
| 4080 | define_local_param_variables(g, fn_table_entry); | |
| 4083 | 4081 | |
| 4084 | 4082 | TypeTableEntry *fn_type = fn_table_entry->type_entry; |
| 4085 | 4083 | assert(!fn_type->data.fn.is_generic); |
| ... | ... | @@ -5019,9 +5017,10 @@ bool type_requires_comptime(TypeTableEntry *type_entry) { |
| 5019 | 5017 | } else { |
| 5020 | 5018 | return type_requires_comptime(type_entry->data.pointer.child_type); |
| 5021 | 5019 | } |
| 5020 | case TypeTableEntryIdFn: | |
| 5021 | return type_entry->data.fn.is_generic; | |
| 5022 | 5022 | case TypeTableEntryIdEnum: |
| 5023 | 5023 | case TypeTableEntryIdErrorSet: |
| 5024 | case TypeTableEntryIdFn: | |
| 5025 | 5024 | case TypeTableEntryIdBool: |
| 5026 | 5025 | case TypeTableEntryIdInt: |
| 5027 | 5026 | case TypeTableEntryIdFloat: |
| ... | ... | @@ -6228,7 +6227,12 @@ uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry) { |
| 6228 | 6227 | } else if (type_entry->id == TypeTableEntryIdOpaque) { |
| 6229 | 6228 | return 1; |
| 6230 | 6229 | } else { |
| 6231 | return LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); | |
| 6230 | uint32_t llvm_alignment = LLVMABIAlignmentOfType(g->target_data_ref, type_entry->type_ref); | |
| 6231 | // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw | |
| 6232 | if (type_entry->id == TypeTableEntryIdPromise && llvm_alignment < 8) { | |
| 6233 | return 8; | |
| 6234 | } | |
| 6235 | return llvm_alignment; | |
| 6232 | 6236 | } |
| 6233 | 6237 | } |
| 6234 | 6238 |
src/ir.cpp+371-101| ... | ... | @@ -3097,20 +3097,47 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode |
| 3097 | 3097 | return return_inst; |
| 3098 | 3098 | } |
| 3099 | 3099 | |
| 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); | |
| 3110 | 3110 | IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); |
| 3111 | return ir_build_cond_br(irb, scope, node, is_non_null, irb->exec->coro_normal_final, irb->exec->coro_early_final, | |
| 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); | |
| 3114 | 3141 | } |
| 3115 | 3142 | |
| 3116 | 3143 | static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { |
| ... | ... | @@ -5251,8 +5278,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n |
| 5251 | 5278 | if (body_result == irb->codegen->invalid_instruction) |
| 5252 | 5279 | return body_result; |
| 5253 | 5280 | |
| 5254 | if (!instr_is_unreachable(body_result)) | |
| 5281 | if (!instr_is_unreachable(body_result)) { | |
| 5282 | ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result)); | |
| 5255 | 5283 | ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime)); |
| 5284 | } | |
| 5256 | 5285 | |
| 5257 | 5286 | if (continue_expr_node) { |
| 5258 | 5287 | ir_set_cursor_at_end_and_append_block(irb, continue_block); |
| ... | ... | @@ -5331,8 +5360,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n |
| 5331 | 5360 | if (body_result == irb->codegen->invalid_instruction) |
| 5332 | 5361 | return body_result; |
| 5333 | 5362 | |
| 5334 | if (!instr_is_unreachable(body_result)) | |
| 5363 | if (!instr_is_unreachable(body_result)) { | |
| 5364 | ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result)); | |
| 5335 | 5365 | ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); |
| 5366 | } | |
| 5336 | 5367 | |
| 5337 | 5368 | if (continue_expr_node) { |
| 5338 | 5369 | ir_set_cursor_at_end_and_append_block(irb, continue_block); |
| ... | ... | @@ -5392,8 +5423,10 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n |
| 5392 | 5423 | if (body_result == irb->codegen->invalid_instruction) |
| 5393 | 5424 | return body_result; |
| 5394 | 5425 | |
| 5395 | if (!instr_is_unreachable(body_result)) | |
| 5426 | if (!instr_is_unreachable(body_result)) { | |
| 5427 | ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result)); | |
| 5396 | 5428 | ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime)); |
| 5429 | } | |
| 5397 | 5430 | |
| 5398 | 5431 | if (continue_expr_node) { |
| 5399 | 5432 | ir_set_cursor_at_end_and_append_block(irb, continue_block); |
| ... | ... | @@ -6153,15 +6186,6 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop |
| 6153 | 6186 | return ir_build_br(irb, break_scope, node, dest_block, is_comptime); |
| 6154 | 6187 | } |
| 6155 | 6188 | |
| 6156 | static 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 | ||
| 6165 | 6189 | static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { |
| 6166 | 6190 | assert(node->type == NodeTypeBreak); |
| 6167 | 6191 | |
| ... | ... | @@ -6202,12 +6226,8 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * |
| 6202 | 6226 | return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); |
| 6203 | 6227 | } |
| 6204 | 6228 | } 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; | |
| 6211 | 6231 | } |
| 6212 | 6232 | search_scope = search_scope->parent; |
| 6213 | 6233 | } |
| ... | ... | @@ -6643,30 +6663,150 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo |
| 6643 | 6663 | async_allocator_type_value, is_var_args); |
| 6644 | 6664 | } |
| 6645 | 6665 | |
| 6646 | static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) { | |
| 6666 | static 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 | ||
| 6737 | static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 6647 | 6738 | assert(node->type == NodeTypeCancel); |
| 6648 | 6739 | |
| 6649 | IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, parent_scope); | |
| 6740 | IrInstruction *target_inst = ir_gen_node(irb, node->data.cancel_expr.expr, scope); | |
| 6650 | 6741 | if (target_inst == irb->codegen->invalid_instruction) |
| 6651 | 6742 | return irb->codegen->invalid_instruction; |
| 6652 | 6743 | |
| 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 | ||
| 6747 | static IrInstruction *ir_gen_resume_target(IrBuilder *irb, Scope *scope, AstNode *node, | |
| 6748 | IrInstruction *target_inst) | |
| 6749 | { | |
| 6750 | IrBasicBlock *done_block = ir_create_basic_block(irb, scope, "ResumeDone"); | |
| 6751 | IrBasicBlock *not_canceled_block = ir_create_basic_block(irb, scope, "NotCanceled"); | |
| 6752 | IrBasicBlock *suspended_block = ir_create_basic_block(irb, scope, "IsSuspended"); | |
| 6753 | IrBasicBlock *not_suspended_block = ir_create_basic_block(irb, scope, "IsNotSuspended"); | |
| 6754 | ||
| 6755 | IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); | |
| 6756 | IrInstruction *is_canceled_mask = ir_build_const_usize(irb, scope, node, 0x1); // 0b001 | |
| 6757 | IrInstruction *is_suspended_mask = ir_build_const_usize(irb, scope, node, 0x2); // 0b010 | |
| 6758 | IrInstruction *and_mask = ir_build_un_op(irb, scope, node, IrUnOpBinNot, is_suspended_mask); | |
| 6759 | IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node, false); | |
| 6760 | IrInstruction *usize_type_val = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); | |
| 6761 | IrInstruction *promise_T_type_val = ir_build_const_type(irb, scope, node, | |
| 6762 | get_promise_type(irb->codegen, irb->codegen->builtin_types.entry_void)); | |
| 6763 | ||
| 6764 | // TODO relies on Zig not re-ordering fields | |
| 6765 | IrInstruction *casted_target_inst = ir_build_ptr_cast(irb, scope, node, promise_T_type_val, target_inst); | |
| 6766 | IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, casted_target_inst); | |
| 6767 | Buf *atomic_state_field_name = buf_create_from_str(ATOMIC_STATE_FIELD_NAME); | |
| 6768 | IrInstruction *atomic_state_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, | |
| 6769 | atomic_state_field_name); | |
| 6770 | ||
| 6771 | // clear the is_suspended bit | |
| 6772 | IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, scope, node, | |
| 6773 | usize_type_val, atomic_state_ptr, nullptr, and_mask, nullptr, | |
| 6774 | AtomicRmwOp_and, AtomicOrderSeqCst); | |
| 6775 | ||
| 6776 | IrInstruction *is_canceled_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); | |
| 6777 | IrInstruction *is_canceled_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); | |
| 6778 | ir_build_cond_br(irb, scope, node, is_canceled_bool, done_block, not_canceled_block, is_comptime); | |
| 6779 | ||
| 6780 | ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); | |
| 6781 | IrInstruction *is_suspended_value = ir_build_bin_op(irb, scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); | |
| 6782 | IrInstruction *is_suspended_bool = ir_build_bin_op(irb, scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); | |
| 6783 | ir_build_cond_br(irb, scope, node, is_suspended_bool, suspended_block, not_suspended_block, is_comptime); | |
| 6784 | ||
| 6785 | ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); | |
| 6786 | ir_build_unreachable(irb, scope, node); | |
| 6787 | ||
| 6788 | ir_set_cursor_at_end_and_append_block(irb, suspended_block); | |
| 6789 | ir_build_coro_resume(irb, scope, node, target_inst); | |
| 6790 | ir_build_br(irb, scope, node, done_block, is_comptime); | |
| 6791 | ||
| 6792 | ir_set_cursor_at_end_and_append_block(irb, done_block); | |
| 6793 | return ir_build_const_void(irb, scope, node); | |
| 6654 | 6794 | } |
| 6655 | 6795 | |
| 6656 | static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *parent_scope, AstNode *node) { | |
| 6796 | static IrInstruction *ir_gen_resume(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 6657 | 6797 | assert(node->type == NodeTypeResume); |
| 6658 | 6798 | |
| 6659 | IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, parent_scope); | |
| 6799 | IrInstruction *target_inst = ir_gen_node(irb, node->data.resume_expr.expr, scope); | |
| 6660 | 6800 | if (target_inst == irb->codegen->invalid_instruction) |
| 6661 | 6801 | return irb->codegen->invalid_instruction; |
| 6662 | 6802 | |
| 6663 | return ir_build_coro_resume(irb, parent_scope, node, target_inst); | |
| 6803 | return ir_gen_resume_target(irb, scope, node, target_inst); | |
| 6664 | 6804 | } |
| 6665 | 6805 | |
| 6666 | static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, AstNode *node) { | |
| 6806 | static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 6667 | 6807 | assert(node->type == NodeTypeAwaitExpr); |
| 6668 | 6808 | |
| 6669 | IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, parent_scope); | |
| 6809 | IrInstruction *target_inst = ir_gen_node(irb, node->data.await_expr.expr, scope); | |
| 6670 | 6810 | if (target_inst == irb->codegen->invalid_instruction) |
| 6671 | 6811 | return irb->codegen->invalid_instruction; |
| 6672 | 6812 | |
| ... | ... | @@ -6680,7 +6820,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6680 | 6820 | return irb->codegen->invalid_instruction; |
| 6681 | 6821 | } |
| 6682 | 6822 | |
| 6683 | ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); | |
| 6823 | ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); | |
| 6684 | 6824 | if (scope_defer_expr) { |
| 6685 | 6825 | if (!scope_defer_expr->reported_err) { |
| 6686 | 6826 | add_node_error(irb->codegen, node, buf_sprintf("cannot await inside defer expression")); |
| ... | ... | @@ -6691,81 +6831,157 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6691 | 6831 | |
| 6692 | 6832 | Scope *outer_scope = irb->exec->begin_scope; |
| 6693 | 6833 | |
| 6694 | IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, parent_scope, node, target_inst); | |
| 6834 | IrInstruction *coro_promise_ptr = ir_build_coro_promise(irb, scope, node, target_inst); | |
| 6695 | 6835 | Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); |
| 6696 | IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name); | |
| 6836 | IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); | |
| 6697 | 6837 | |
| 6698 | 6838 | if (irb->codegen->have_err_ret_tracing) { |
| 6699 | IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull); | |
| 6839 | IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::NonNull); | |
| 6700 | 6840 | Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME); |
| 6701 | IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name); | |
| 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, | |
| 6711 | 6877 | 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)); | |
| 6730 | 6912 | |
| 6731 | 6913 | ir_set_cursor_at_end_and_append_block(irb, no_suspend_block); |
| 6732 | 6914 | if (irb->codegen->have_err_ret_tracing) { |
| 6733 | 6915 | Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME); |
| 6734 | IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name); | |
| 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); | |
| 6737 | 6919 | } |
| 6738 | 6920 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 6739 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); | |
| 6921 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); | |
| 6740 | 6922 | // If the type of the result handle_is_ptr then this does not actually perform a load. But we need it to, |
| 6741 | 6923 | // because we're about to destroy the memory. So we store it into our result variable. |
| 6742 | IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr); | |
| 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 | ||
| 6746 | 6929 | |
| 6747 | 6930 | ir_set_cursor_at_end_and_append_block(irb, yes_suspend_block); |
| 6748 | IrInstruction *suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false); | |
| 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); | |
| 6751 | 6948 | |
| 6752 | 6949 | IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2); |
| 6753 | cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0); | |
| 6950 | cases[0].value = ir_build_const_u8(irb, scope, node, 0); | |
| 6754 | 6951 | 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, | |
| 6758 | 6955 | 2, cases, const_bool_false, nullptr); |
| 6759 | 6956 | |
| 6957 | ir_set_cursor_at_end_and_append_block(irb, destroy_block); | |
| 6958 | ir_gen_cancel_target(irb, scope, node, target_inst, false, true); | |
| 6959 | ir_mark_gen(ir_build_br(irb, scope, node, cleanup_block, const_bool_false)); | |
| 6960 | ||
| 6760 | 6961 | 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)); | |
| 6763 | 6979 | |
| 6764 | 6980 | ir_set_cursor_at_end_and_append_block(irb, resume_block); |
| 6765 | ir_build_br(irb, parent_scope, node, merge_block, const_bool_false); | |
| 6981 | ir_build_br(irb, scope, node, merge_block, const_bool_false); | |
| 6766 | 6982 | |
| 6767 | 6983 | ir_set_cursor_at_end_and_append_block(irb, merge_block); |
| 6768 | return ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr); | |
| 6984 | return ir_build_load_ptr(irb, scope, node, my_result_var_ptr); | |
| 6769 | 6985 | } |
| 6770 | 6986 | |
| 6771 | 6987 | static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNode *node) { |
| ... | ... | @@ -6804,9 +7020,52 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod |
| 6804 | 7020 | |
| 6805 | 7021 | IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); |
| 6806 | 7022 | 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); | |
| 6809 | 7031 | IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false); |
| 7032 | IrInstruction *usize_type_val = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_usize); | |
| 7033 | IrInstruction *is_canceled_mask = ir_build_const_usize(irb, parent_scope, node, 0x1); // 0b001 | |
| 7034 | IrInstruction *is_suspended_mask = ir_build_const_usize(irb, parent_scope, node, 0x2); // 0b010 | |
| 7035 | IrInstruction *zero = ir_build_const_usize(irb, parent_scope, node, 0); | |
| 7036 | IrInstruction *inverted_ptr_mask = ir_build_const_usize(irb, parent_scope, node, 0x7); // 0b111 | |
| 7037 | IrInstruction *ptr_mask = ir_build_un_op(irb, parent_scope, node, IrUnOpBinNot, inverted_ptr_mask); // 0b111...000 | |
| 7038 | ||
| 7039 | IrInstruction *prev_atomic_value = ir_build_atomic_rmw(irb, parent_scope, node, | |
| 7040 | usize_type_val, irb->exec->atomic_state_field_ptr, nullptr, is_suspended_mask, nullptr, | |
| 7041 | AtomicRmwOp_or, AtomicOrderSeqCst); | |
| 7042 | ||
| 7043 | IrInstruction *is_canceled_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_canceled_mask, false); | |
| 7044 | IrInstruction *is_canceled_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_canceled_value, zero, false); | |
| 7045 | ir_build_cond_br(irb, parent_scope, node, is_canceled_bool, canceled_block, not_canceled_block, const_bool_false); | |
| 7046 | ||
| 7047 | ir_set_cursor_at_end_and_append_block(irb, canceled_block); | |
| 7048 | IrInstruction *await_handle_addr = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, ptr_mask, false); | |
| 7049 | IrInstruction *have_await_handle = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, await_handle_addr, zero, false); | |
| 7050 | IrBasicBlock *post_canceled_block = irb->current_basic_block; | |
| 7051 | ir_build_cond_br(irb, parent_scope, node, have_await_handle, cancel_awaiter_block, cleanup_block, const_bool_false); | |
| 7052 | ||
| 7053 | ir_set_cursor_at_end_and_append_block(irb, cancel_awaiter_block); | |
| 7054 | IrInstruction *await_handle = ir_build_int_to_ptr(irb, parent_scope, node, promise_type_val, await_handle_addr); | |
| 7055 | ir_gen_cancel_target(irb, parent_scope, node, await_handle, true, false); | |
| 7056 | IrBasicBlock *post_cancel_awaiter_block = irb->current_basic_block; | |
| 7057 | ir_build_br(irb, parent_scope, node, cleanup_block, const_bool_false); | |
| 7058 | ||
| 7059 | ir_set_cursor_at_end_and_append_block(irb, not_canceled_block); | |
| 7060 | IrInstruction *is_suspended_value = ir_build_bin_op(irb, parent_scope, node, IrBinOpBinAnd, prev_atomic_value, is_suspended_mask, false); | |
| 7061 | IrInstruction *is_suspended_bool = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpNotEq, is_suspended_value, zero, false); | |
| 7062 | ir_build_cond_br(irb, parent_scope, node, is_suspended_bool, suspended_block, not_suspended_block, const_bool_false); | |
| 7063 | ||
| 7064 | ir_set_cursor_at_end_and_append_block(irb, suspended_block); | |
| 7065 | ir_build_unreachable(irb, parent_scope, node); | |
| 7066 | ||
| 7067 | ir_set_cursor_at_end_and_append_block(irb, not_suspended_block); | |
| 7068 | IrInstruction *suspend_code; | |
| 6810 | 7069 | if (node->data.suspend.block == nullptr) { |
| 6811 | 7070 | suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); |
| 6812 | 7071 | } else { |
| ... | ... | @@ -6834,13 +7093,20 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod |
| 6834 | 7093 | cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0)); |
| 6835 | 7094 | cases[0].block = resume_block; |
| 6836 | 7095 | cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1)); |
| 6837 | cases[1].block = cleanup_block; | |
| 7096 | cases[1].block = canceled_block; | |
| 6838 | 7097 | ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, |
| 6839 | 7098 | 2, cases, const_bool_false, nullptr)); |
| 6840 | 7099 | |
| 6841 | 7100 | ir_set_cursor_at_end_and_append_block(irb, cleanup_block); |
| 7101 | IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2); | |
| 7102 | IrInstruction **incoming_values = allocate<IrInstruction *>(2); | |
| 7103 | incoming_blocks[0] = post_canceled_block; | |
| 7104 | incoming_values[0] = const_bool_true; | |
| 7105 | incoming_blocks[1] = post_cancel_awaiter_block; | |
| 7106 | incoming_values[1] = const_bool_false; | |
| 7107 | IrInstruction *destroy_ourselves = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values); | |
| 6842 | 7108 | ir_gen_defers_for_block(irb, parent_scope, outer_scope, true); |
| 6843 | ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false)); | |
| 7109 | ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, destroy_ourselves, irb->exec->coro_final_cleanup_block, irb->exec->coro_early_final, const_bool_false)); | |
| 6844 | 7110 | |
| 6845 | 7111 | ir_set_cursor_at_end_and_append_block(irb, resume_block); |
| 6846 | 7112 | return ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); |
| ... | ... | @@ -7081,10 +7347,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 7081 | 7347 | IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr); |
| 7082 | 7348 | irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); |
| 7083 | 7349 | |
| 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); | |
| 7088 | 7355 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 7089 | 7356 | irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); |
| 7090 | 7357 | result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); |
| ... | ... | @@ -7102,7 +7369,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 7102 | 7369 | // coordinate with builtin.zig |
| 7103 | 7370 | Buf *index_name = buf_create_from_str("index"); |
| 7104 | 7371 | IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name); |
| 7105 | IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0); | |
| 7106 | 7372 | ir_build_store_ptr(irb, scope, node, index_ptr, zero); |
| 7107 | 7373 | |
| 7108 | 7374 | Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses"); |
| ... | ... | @@ -7225,7 +7491,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 7225 | 7491 | ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false); |
| 7226 | 7492 | |
| 7227 | 7493 | ir_set_cursor_at_end_and_append_block(irb, resume_block); |
| 7228 | ir_build_coro_resume(irb, scope, node, awaiter_handle); | |
| 7494 | ir_gen_resume_target(irb, scope, node, awaiter_handle); | |
| 7229 | 7495 | ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false); |
| 7230 | 7496 | } |
| 7231 | 7497 | |
| ... | ... | @@ -12142,7 +12408,7 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 12142 | 12408 | result_type = ira->codegen->builtin_types.entry_invalid; |
| 12143 | 12409 | } else if (type_requires_comptime(result_type)) { |
| 12144 | 12410 | var_class_requires_const = true; |
| 12145 | if (!var->src_is_const && !is_comptime_var) { | |
| 12411 | if (!var->gen_is_const && !is_comptime_var) { | |
| 12146 | 12412 | ir_add_error_node(ira, source_node, |
| 12147 | 12413 | buf_sprintf("variable of type '%s' must be const or comptime", |
| 12148 | 12414 | buf_ptr(&result_type->name))); |
| ... | ... | @@ -12591,6 +12857,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod |
| 12591 | 12857 | } |
| 12592 | 12858 | |
| 12593 | 12859 | Buf *param_name = param_decl_node->data.param_decl.name; |
| 12860 | if (!param_name) return false; | |
| 12594 | 12861 | if (!is_var_args) { |
| 12595 | 12862 | VariableTableEntry *var = add_variable(ira->codegen, param_decl_node, |
| 12596 | 12863 | *child_scope, param_name, true, arg_val, nullptr); |
| ... | ... | @@ -18991,6 +19258,9 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, |
| 18991 | 19258 | return ira->codegen->builtin_types.entry_invalid; |
| 18992 | 19259 | } else if (type_entry->id == TypeTableEntryIdErrorUnion) { |
| 18993 | 19260 | TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; |
| 19261 | if (type_is_invalid(payload_type)) { | |
| 19262 | return ira->codegen->builtin_types.entry_invalid; | |
| 19263 | } | |
| 18994 | 19264 | TypeTableEntry *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, |
| 18995 | 19265 | ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, |
| 18996 | 19266 | PtrLenSingle, |
src/ir_print.cpp+4| ... | ... | @@ -45,6 +45,10 @@ static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | 47 | static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 48 | if (instruction == nullptr) { | |
| 49 | fprintf(irp->f, "(null)"); | |
| 50 | return; | |
| 51 | } | |
| 48 | 52 | if (instruction->value.special != ConstValSpecialRuntime) { |
| 49 | 53 | ir_print_const_value(irp, &instruction->value); |
| 50 | 54 | } else { |
src/parser.cpp+2-23| ... | ... | @@ -648,30 +648,12 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m |
| 648 | 648 | } |
| 649 | 649 | |
| 650 | 650 | /* |
| 651 | SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) | |
| 651 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | |
| 652 | 652 | */ |
| 653 | 653 | static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 654 | 654 | size_t orig_token_index = *token_index; |
| 655 | 655 | |
| 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); | |
| 675 | 657 | if (suspend_token->id == TokenIdKeywordSuspend) { |
| 676 | 658 | *token_index += 1; |
| 677 | 659 | } else if (mandatory) { |
| ... | ... | @@ -693,9 +675,6 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b |
| 693 | 675 | } |
| 694 | 676 | |
| 695 | 677 | AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); |
| 696 | if (name_token != nullptr) { | |
| 697 | node->data.suspend.name = token_buf(name_token); | |
| 698 | } | |
| 699 | 678 | node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); |
| 700 | 679 | ast_eat_token(pc, token_index, TokenIdBinOr); |
| 701 | 680 | node->data.suspend.block = ast_parse_block(pc, token_index, true); |
std/build.zig+21| ... | ... | @@ -807,6 +807,7 @@ pub const LibExeObjStep = struct { |
| 807 | 807 | disable_libc: bool, |
| 808 | 808 | frameworks: BufSet, |
| 809 | 809 | verbose_link: bool, |
| 810 | no_rosegment: bool, | |
| 810 | 811 | |
| 811 | 812 | // zig only stuff |
| 812 | 813 | root_src: ?[]const u8, |
| ... | ... | @@ -874,6 +875,7 @@ pub const LibExeObjStep = struct { |
| 874 | 875 | |
| 875 | 876 | fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, static: bool, ver: *const Version) LibExeObjStep { |
| 876 | 877 | var self = LibExeObjStep{ |
| 878 | .no_rosegment = false, | |
| 877 | 879 | .strip = false, |
| 878 | 880 | .builder = builder, |
| 879 | 881 | .verbose_link = false, |
| ... | ... | @@ -914,6 +916,7 @@ pub const LibExeObjStep = struct { |
| 914 | 916 | |
| 915 | 917 | fn initC(builder: *Builder, name: []const u8, kind: Kind, version: *const Version, static: bool) LibExeObjStep { |
| 916 | 918 | var self = LibExeObjStep{ |
| 919 | .no_rosegment = false, | |
| 917 | 920 | .builder = builder, |
| 918 | 921 | .name = name, |
| 919 | 922 | .kind = kind, |
| ... | ... | @@ -953,6 +956,10 @@ pub const LibExeObjStep = struct { |
| 953 | 956 | return self; |
| 954 | 957 | } |
| 955 | 958 | |
| 959 | pub fn setNoRoSegment(self: *LibExeObjStep, value: bool) void { | |
| 960 | self.no_rosegment = value; | |
| 961 | } | |
| 962 | ||
| 956 | 963 | fn computeOutFileNames(self: *LibExeObjStep) void { |
| 957 | 964 | switch (self.kind) { |
| 958 | 965 | Kind.Obj => { |
| ... | ... | @@ -1306,6 +1313,10 @@ pub const LibExeObjStep = struct { |
| 1306 | 1313 | } |
| 1307 | 1314 | } |
| 1308 | 1315 | |
| 1316 | if (self.no_rosegment) { | |
| 1317 | try zig_args.append("--no-rosegment"); | |
| 1318 | } | |
| 1319 | ||
| 1309 | 1320 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1310 | 1321 | |
| 1311 | 1322 | if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { |
| ... | ... | @@ -1598,6 +1609,7 @@ pub const TestStep = struct { |
| 1598 | 1609 | include_dirs: ArrayList([]const u8), |
| 1599 | 1610 | lib_paths: ArrayList([]const u8), |
| 1600 | 1611 | object_files: ArrayList([]const u8), |
| 1612 | no_rosegment: bool, | |
| 1601 | 1613 | |
| 1602 | 1614 | pub fn init(builder: *Builder, root_src: []const u8) TestStep { |
| 1603 | 1615 | const step_name = builder.fmt("test {}", root_src); |
| ... | ... | @@ -1615,9 +1627,14 @@ pub const TestStep = struct { |
| 1615 | 1627 | .include_dirs = ArrayList([]const u8).init(builder.allocator), |
| 1616 | 1628 | .lib_paths = ArrayList([]const u8).init(builder.allocator), |
| 1617 | 1629 | .object_files = ArrayList([]const u8).init(builder.allocator), |
| 1630 | .no_rosegment = false, | |
| 1618 | 1631 | }; |
| 1619 | 1632 | } |
| 1620 | 1633 | |
| 1634 | pub fn setNoRoSegment(self: *TestStep, value: bool) void { | |
| 1635 | self.no_rosegment = value; | |
| 1636 | } | |
| 1637 | ||
| 1621 | 1638 | pub fn addLibPath(self: *TestStep, path: []const u8) void { |
| 1622 | 1639 | self.lib_paths.append(path) catch unreachable; |
| 1623 | 1640 | } |
| ... | ... | @@ -1761,6 +1778,10 @@ pub const TestStep = struct { |
| 1761 | 1778 | try zig_args.append(lib_path); |
| 1762 | 1779 | } |
| 1763 | 1780 | |
| 1781 | if (self.no_rosegment) { | |
| 1782 | try zig_args.append("--no-rosegment"); | |
| 1783 | } | |
| 1784 | ||
| 1764 | 1785 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1765 | 1786 | } |
| 1766 | 1787 | }; |
std/debug/index.zig+12-2| ... | ... | @@ -27,7 +27,7 @@ pub fn warn(comptime fmt: []const u8, args: ...) void { |
| 27 | 27 | const stderr = getStderrStream() catch return; |
| 28 | 28 | stderr.print(fmt, args) catch return; |
| 29 | 29 | } |
| 30 | fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { | |
| 30 | pub fn getStderrStream() !*io.OutStream(io.FileOutStream.Error) { | |
| 31 | 31 | if (stderr_stream) |st| { |
| 32 | 32 | return st; |
| 33 | 33 | } else { |
| ... | ... | @@ -172,6 +172,16 @@ pub fn writeStackTrace(stack_trace: *const builtin.StackTrace, out_stream: var, |
| 172 | 172 | } |
| 173 | 173 | } |
| 174 | 174 | |
| 175 | pub 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 | ||
| 175 | 185 | pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_info: *ElfStackTrace, tty_color: bool, start_addr: ?usize) !void { |
| 176 | 186 | const AddressState = union(enum) { |
| 177 | 187 | NotLookingForStartAddress, |
| ... | ... | @@ -205,7 +215,7 @@ pub fn writeCurrentStackTrace(out_stream: var, allocator: *mem.Allocator, debug_ |
| 205 | 215 | } |
| 206 | 216 | } |
| 207 | 217 | |
| 208 | fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { | |
| 218 | pub fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: usize, tty_color: bool) !void { | |
| 209 | 219 | switch (builtin.os) { |
| 210 | 220 | builtin.Os.windows => return error.UnsupportedDebugInfo, |
| 211 | 221 | builtin.Os.macosx => { |
std/event/channel.zig+3-22| ... | ... | @@ -71,11 +71,6 @@ pub fn Channel(comptime T: type) type { |
| 71 | 71 | /// puts a data item in the channel. The promise completes when the value has been added to the |
| 72 | 72 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. |
| 73 | 73 | pub async fn put(self: *SelfChannel, data: T) void { |
| 74 | // TODO should be able to group memory allocation failure before first suspend point | |
| 75 | // so that the async invocation catches it | |
| 76 | var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; | |
| 77 | _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; | |
| 78 | ||
| 79 | 74 | suspend |handle| { |
| 80 | 75 | var my_tick_node = Loop.NextTickNode{ |
| 81 | 76 | .next = undefined, |
| ... | ... | @@ -91,18 +86,13 @@ pub fn Channel(comptime T: type) type { |
| 91 | 86 | self.putters.put(&queue_node); |
| 92 | 87 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 93 | 88 | |
| 94 | self.loop.onNextTick(dispatch_tick_node_ptr); | |
| 89 | self.dispatch(); | |
| 95 | 90 | } |
| 96 | 91 | } |
| 97 | 92 | |
| 98 | 93 | /// await this function to get an item from the channel. If the buffer is empty, the promise will |
| 99 | 94 | /// complete when the next item is put in the channel. |
| 100 | 95 | pub async fn get(self: *SelfChannel) T { |
| 101 | // TODO should be able to group memory allocation failure before first suspend point | |
| 102 | // so that the async invocation catches it | |
| 103 | var dispatch_tick_node_ptr: *Loop.NextTickNode = undefined; | |
| 104 | _ = async self.dispatch(&dispatch_tick_node_ptr) catch unreachable; | |
| 105 | ||
| 106 | 96 | // TODO integrate this function with named return values |
| 107 | 97 | // so we can get rid of this extra result copy |
| 108 | 98 | var result: T = undefined; |
| ... | ... | @@ -121,21 +111,12 @@ pub fn Channel(comptime T: type) type { |
| 121 | 111 | self.getters.put(&queue_node); |
| 122 | 112 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 123 | 113 | |
| 124 | self.loop.onNextTick(dispatch_tick_node_ptr); | |
| 114 | self.dispatch(); | |
| 125 | 115 | } |
| 126 | 116 | return result; |
| 127 | 117 | } |
| 128 | 118 | |
| 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 { | |
| 139 | 120 | // set the "need dispatch" flag |
| 140 | 121 | _ = @atomicRmw(u8, &self.need_dispatch, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); |
| 141 | 122 |
std/event/loop.zig+2-2| ... | ... | @@ -55,7 +55,7 @@ pub const Loop = struct { |
| 55 | 55 | /// After initialization, call run(). |
| 56 | 56 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 57 | 57 | /// have the correct pointer value. |
| 58 | fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { | |
| 58 | pub fn initSingleThreaded(self: *Loop, allocator: *mem.Allocator) !void { | |
| 59 | 59 | return self.initInternal(allocator, 1); |
| 60 | 60 | } |
| 61 | 61 | |
| ... | ... | @@ -64,7 +64,7 @@ pub const Loop = struct { |
| 64 | 64 | /// After initialization, call run(). |
| 65 | 65 | /// TODO copy elision / named return values so that the threads referencing *Loop |
| 66 | 66 | /// have the correct pointer value. |
| 67 | fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { | |
| 67 | pub fn initMultiThreaded(self: *Loop, allocator: *mem.Allocator) !void { | |
| 68 | 68 | const core_count = try std.os.cpuCount(allocator); |
| 69 | 69 | return self.initInternal(allocator, core_count); |
| 70 | 70 | } |
std/fmt/index.zig+34| ... | ... | @@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 18 | 18 | OpenBrace, |
| 19 | 19 | CloseBrace, |
| 20 | 20 | FormatString, |
| 21 | Pointer, | |
| 21 | 22 | }; |
| 22 | 23 | |
| 23 | 24 | comptime var start_index = 0; |
| ... | ... | @@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 54 | 55 | state = State.Start; |
| 55 | 56 | start_index = i + 1; |
| 56 | 57 | }, |
| 58 | '*' => state = State.Pointer, | |
| 57 | 59 | else => { |
| 58 | 60 | state = State.FormatString; |
| 59 | 61 | }, |
| ... | ... | @@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 75 | 77 | }, |
| 76 | 78 | else => {}, |
| 77 | 79 | }, |
| 80 | State.Pointer => switch (c) { | |
| 81 | '}' => { | |
| 82 | try output(context, @typeName(@typeOf(args[next_arg]).Child)); | |
| 83 | try output(context, "@"); | |
| 84 | try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); | |
| 85 | next_arg += 1; | |
| 86 | state = State.Start; | |
| 87 | start_index = i + 1; | |
| 88 | }, | |
| 89 | else => @compileError("Unexpected format character after '*'"), | |
| 90 | }, | |
| 78 | 91 | } |
| 79 | 92 | } |
| 80 | 93 | comptime { |
| ... | ... | @@ -861,6 +874,27 @@ test "fmt.format" { |
| 861 | 874 | const value: u8 = 'a'; |
| 862 | 875 | try testFmt("u8: a\n", "u8: {c}\n", value); |
| 863 | 876 | } |
| 877 | { | |
| 878 | const value: [3]u8 = "abc"; | |
| 879 | try testFmt("array: abc\n", "array: {}\n", value); | |
| 880 | try testFmt("array: abc\n", "array: {}\n", &value); | |
| 881 | ||
| 882 | var buf: [100]u8 = undefined; | |
| 883 | try testFmt( | |
| 884 | try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), | |
| 885 | "array: {*}\n", | |
| 886 | &value, | |
| 887 | ); | |
| 888 | } | |
| 889 | { | |
| 890 | const value: []const u8 = "abc"; | |
| 891 | try testFmt("slice: abc\n", "slice: {}\n", value); | |
| 892 | } | |
| 893 | { | |
| 894 | const value = @intToPtr(*i32, 0xdeadbeef); | |
| 895 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); | |
| 896 | try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); | |
| 897 | } | |
| 864 | 898 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); |
| 865 | 899 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); |
| 866 | 900 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); |
std/io.zig+165| ... | ... | @@ -200,6 +200,13 @@ pub fn InStream(comptime ReadError: type) type { |
| 200 | 200 | try self.readNoEof(input_slice); |
| 201 | 201 | return mem.readInt(input_slice, T, endian); |
| 202 | 202 | } |
| 203 | ||
| 204 | pub fn skipBytes(self: *Self, num_bytes: usize) !void { | |
| 205 | var i: usize = 0; | |
| 206 | while (i < num_bytes) : (i += 1) { | |
| 207 | _ = try self.readByte(); | |
| 208 | } | |
| 209 | } | |
| 203 | 210 | }; |
| 204 | 211 | } |
| 205 | 212 | |
| ... | ... | @@ -230,6 +237,20 @@ pub fn OutStream(comptime WriteError: type) type { |
| 230 | 237 | try self.writeFn(self, slice); |
| 231 | 238 | } |
| 232 | 239 | } |
| 240 | ||
| 241 | pub fn writeIntLe(self: *Self, comptime T: type, value: T) !void { | |
| 242 | return self.writeInt(builtin.Endian.Little, T, value); | |
| 243 | } | |
| 244 | ||
| 245 | pub fn writeIntBe(self: *Self, comptime T: type, value: T) !void { | |
| 246 | return self.writeInt(builtin.Endian.Big, T, value); | |
| 247 | } | |
| 248 | ||
| 249 | pub fn writeInt(self: *Self, endian: builtin.Endian, comptime T: type, value: T) !void { | |
| 250 | var bytes: [@sizeOf(T)]u8 = undefined; | |
| 251 | mem.writeInt(bytes[0..], value, endian); | |
| 252 | return self.writeFn(self, bytes); | |
| 253 | } | |
| 233 | 254 | }; |
| 234 | 255 | } |
| 235 | 256 | |
| ... | ... | @@ -331,6 +352,150 @@ pub fn BufferedInStreamCustom(comptime buffer_size: usize, comptime Error: type) |
| 331 | 352 | }; |
| 332 | 353 | } |
| 333 | 354 | |
| 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. | |
| 357 | pub 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 | ||
| 422 | pub 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. | |
| 454 | pub 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 | ||
| 334 | 499 | pub fn BufferedOutStream(comptime Error: type) type { |
| 335 | 500 | return BufferedOutStreamCustom(os.page_size, Error); |
| 336 | 501 | } |
std/io_test.zig+72| ... | ... | @@ -2,6 +2,7 @@ const std = @import("index.zig"); |
| 2 | 2 | const io = std.io; |
| 3 | 3 | const DefaultPrng = std.rand.DefaultPrng; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | const assertError = std.debug.assertError; | |
| 5 | 6 | const mem = std.mem; |
| 6 | 7 | const os = std.os; |
| 7 | 8 | const builtin = @import("builtin"); |
| ... | ... | @@ -60,3 +61,74 @@ test "BufferOutStream" { |
| 60 | 61 | |
| 61 | 62 | assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n")); |
| 62 | 63 | } |
| 64 | ||
| 65 | test "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 | ||
| 83 | test "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 | ||
| 116 | test "SliceOutStream" { | |
| 117 | var buffer: [10]u8 = undefined; | |
| 118 | var ss = io.SliceOutStream.init(buffer[0..]); | |
| 119 | ||
| 120 | try ss.stream.write("Hello"); | |
| 121 | assert(mem.eql(u8, ss.getWritten(), "Hello")); | |
| 122 | ||
| 123 | try ss.stream.write("world"); | |
| 124 | assert(mem.eql(u8, ss.getWritten(), "Helloworld")); | |
| 125 | ||
| 126 | assertError(ss.stream.write("!"), error.OutOfSpace); | |
| 127 | assert(mem.eql(u8, ss.getWritten(), "Helloworld")); | |
| 128 | ||
| 129 | ss.reset(); | |
| 130 | assert(ss.getWritten().len == 0); | |
| 131 | ||
| 132 | assertError(ss.stream.write("Hello world!"), error.OutOfSpace); | |
| 133 | assert(mem.eql(u8, ss.getWritten(), "Hello worl")); | |
| 134 | } |
test/behavior.zig+1| ... | ... | @@ -16,6 +16,7 @@ comptime { |
| 16 | 16 | _ = @import("cases/bugs/828.zig"); |
| 17 | 17 | _ = @import("cases/bugs/920.zig"); |
| 18 | 18 | _ = @import("cases/byval_arg_var.zig"); |
| 19 | _ = @import("cases/cancel.zig"); | |
| 19 | 20 | _ = @import("cases/cast.zig"); |
| 20 | 21 | _ = @import("cases/const_slice_child.zig"); |
| 21 | 22 | _ = @import("cases/coroutine_await_struct.zig"); |
test/cases/cancel.zig created+92| ... | ... | @@ -0,0 +1,92 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | var defer_f1: bool = false; | |
| 4 | var defer_f2: bool = false; | |
| 5 | var defer_f3: bool = false; | |
| 6 | ||
| 7 | test "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 | ||
| 18 | async fn f1() void { | |
| 19 | defer { | |
| 20 | defer_f1 = true; | |
| 21 | } | |
| 22 | await (async f2() catch unreachable); | |
| 23 | } | |
| 24 | ||
| 25 | async fn f2() void { | |
| 26 | defer { | |
| 27 | defer_f2 = true; | |
| 28 | } | |
| 29 | await (async f3() catch unreachable); | |
| 30 | } | |
| 31 | ||
| 32 | async fn f3() void { | |
| 33 | defer { | |
| 34 | defer_f3 = true; | |
| 35 | } | |
| 36 | suspend; | |
| 37 | } | |
| 38 | ||
| 39 | var defer_b1: bool = false; | |
| 40 | var defer_b2: bool = false; | |
| 41 | var defer_b3: bool = false; | |
| 42 | var defer_b4: bool = false; | |
| 43 | ||
| 44 | test "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 | ||
| 56 | async fn b1() void { | |
| 57 | defer { | |
| 58 | defer_b1 = true; | |
| 59 | } | |
| 60 | await (async b2() catch unreachable); | |
| 61 | } | |
| 62 | ||
| 63 | var b4_handle: promise = undefined; | |
| 64 | ||
| 65 | async 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 | ||
| 76 | async fn b3() i32 { | |
| 77 | defer { | |
| 78 | defer_b3 = true; | |
| 79 | } | |
| 80 | await (async b4() catch unreachable); | |
| 81 | return 1234; | |
| 82 | } | |
| 83 | ||
| 84 | async fn b4() void { | |
| 85 | defer { | |
| 86 | defer_b4 = true; | |
| 87 | } | |
| 88 | suspend |p| { | |
| 89 | b4_handle = p; | |
| 90 | } | |
| 91 | suspend; | |
| 92 | } |
test/cases/coroutines.zig+2-2| ... | ... | @@ -244,8 +244,8 @@ test "break from suspend" { |
| 244 | 244 | std.debug.assert(my_result == 2); |
| 245 | 245 | } |
| 246 | 246 | async fn testBreakFromSuspend(my_result: *i32) void { |
| 247 | s: suspend |p| { | |
| 248 | break :s; | |
| 247 | suspend |p| { | |
| 248 | resume p; | |
| 249 | 249 | } |
| 250 | 250 | my_result.* += 1; |
| 251 | 251 | suspend; |
test/compile_errors.zig+58| ... | ... | @@ -1,6 +1,64 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | |
| 5 | "while loop body expression ignored", | |
| 6 | \\fn returns() usize { | |
| 7 | \\ return 2; | |
| 8 | \\} | |
| 9 | \\export fn f1() void { | |
| 10 | \\ while (true) returns(); | |
| 11 | \\} | |
| 12 | \\export fn f2() void { | |
| 13 | \\ var x: ?i32 = null; | |
| 14 | \\ while (x) |_| returns(); | |
| 15 | \\} | |
| 16 | \\export fn f3() void { | |
| 17 | \\ var x: error!i32 = error.Bad; | |
| 18 | \\ while (x) |_| returns() else |_| unreachable; | |
| 19 | \\} | |
| 20 | , | |
| 21 | ".tmp_source.zig:5:25: error: expression value is ignored", | |
| 22 | ".tmp_source.zig:9:26: error: expression value is ignored", | |
| 23 | ".tmp_source.zig:13:26: error: expression value is ignored", | |
| 24 | ); | |
| 25 | ||
| 26 | cases.add( | |
| 27 | "missing parameter name of generic function", | |
| 28 | \\fn dump(var) void {} | |
| 29 | \\export fn entry() void { | |
| 30 | \\ var a: u8 = 9; | |
| 31 | \\ dump(a); | |
| 32 | \\} | |
| 33 | , | |
| 34 | ".tmp_source.zig:1:9: error: missing parameter name", | |
| 35 | ); | |
| 36 | ||
| 37 | cases.add( | |
| 38 | "non-inline for loop on a type that requires comptime", | |
| 39 | \\const Foo = struct { | |
| 40 | \\ name: []const u8, | |
| 41 | \\ T: type, | |
| 42 | \\}; | |
| 43 | \\export fn entry() void { | |
| 44 | \\ const xx: [2]Foo = undefined; | |
| 45 | \\ for (xx) |f| {} | |
| 46 | \\} | |
| 47 | , | |
| 48 | ".tmp_source.zig:7:15: error: variable of type 'Foo' must be const or comptime", | |
| 49 | ); | |
| 50 | ||
| 51 | cases.add( | |
| 52 | "generic fn as parameter without comptime keyword", | |
| 53 | \\fn f(_: fn (var) void) void {} | |
| 54 | \\fn g(_: var) void {} | |
| 55 | \\export fn entry() void { | |
| 56 | \\ f(g); | |
| 57 | \\} | |
| 58 | , | |
| 59 | ".tmp_source.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime", | |
| 60 | ); | |
| 61 | ||
| 4 | 62 | cases.add( |
| 5 | 63 | "optional pointer to void in extern struct", |
| 6 | 64 | \\comptime { |
test/stage2/compare_output.zig+13| ... | ... | @@ -2,6 +2,7 @@ const std = @import("std"); |
| 2 | 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // hello world | |
| 5 | 6 | try ctx.testCompareOutputLibC( |
| 6 | 7 | \\extern fn puts([*]const u8) void; |
| 7 | 8 | \\export fn main() c_int { |
| ... | ... | @@ -9,4 +10,16 @@ pub fn addCases(ctx: *TestContext) !void { |
| 9 | 10 | \\ return 0; |
| 10 | 11 | \\} |
| 11 | 12 | , "Hello, world!" ++ std.cstr.line_sep); |
| 13 | ||
| 14 | // function calling another function | |
| 15 | try ctx.testCompareOutputLibC( | |
| 16 | \\extern fn puts(s: [*]const u8) void; | |
| 17 | \\export fn main() c_int { | |
| 18 | \\ return foo(c"OK"); | |
| 19 | \\} | |
| 20 | \\fn foo(s: [*]const u8) c_int { | |
| 21 | \\ puts(s); | |
| 22 | \\ return 0; | |
| 23 | \\} | |
| 24 | , "OK" ++ std.cstr.line_sep); | |
| 12 | 25 | } |