| ... | ... | @@ -35,7 +35,7 @@ const fs = event.fs; |
| 35 | 35 | const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB |
| 36 | 36 | |
| 37 | 37 | /// Data that is local to the event loop. |
| 38 | | pub const EventLoopLocal = struct { |
| 38 | pub const ZigCompiler = struct { |
| 39 | 39 | loop: *event.Loop, |
| 40 | 40 | llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), |
| 41 | 41 | lld_lock: event.Lock, |
| ... | ... | @@ -47,7 +47,7 @@ pub const EventLoopLocal = struct { |
| 47 | 47 | |
| 48 | 48 | var lazy_init_targets = std.lazyInit(void); |
| 49 | 49 | |
| 50 | | fn init(loop: *event.Loop) !EventLoopLocal { |
| 50 | fn init(loop: *event.Loop) !ZigCompiler { |
| 51 | 51 | lazy_init_targets.get() orelse { |
| 52 | 52 | Target.initializeAll(); |
| 53 | 53 | lazy_init_targets.resolve(); |
| ... | ... | @@ -57,7 +57,7 @@ pub const EventLoopLocal = struct { |
| 57 | 57 | try std.os.getRandomBytes(seed_bytes[0..]); |
| 58 | 58 | const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); |
| 59 | 59 | |
| 60 | | return EventLoopLocal{ |
| 60 | return ZigCompiler{ |
| 61 | 61 | .loop = loop, |
| 62 | 62 | .lld_lock = event.Lock.init(loop), |
| 63 | 63 | .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), |
| ... | ... | @@ -67,7 +67,7 @@ pub const EventLoopLocal = struct { |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | 69 | /// Must be called only after EventLoop.run completes. |
| 70 | | fn deinit(self: *EventLoopLocal) void { |
| 70 | fn deinit(self: *ZigCompiler) void { |
| 71 | 71 | self.lld_lock.deinit(); |
| 72 | 72 | while (self.llvm_handle_pool.pop()) |node| { |
| 73 | 73 | c.LLVMContextDispose(node.data); |
| ... | ... | @@ -77,7 +77,7 @@ pub const EventLoopLocal = struct { |
| 77 | 77 | |
| 78 | 78 | /// Gets an exclusive handle on any LlvmContext. |
| 79 | 79 | /// Caller must release the handle when done. |
| 80 | | pub fn getAnyLlvmContext(self: *EventLoopLocal) !LlvmHandle { |
| 80 | pub fn getAnyLlvmContext(self: *ZigCompiler) !LlvmHandle { |
| 81 | 81 | if (self.llvm_handle_pool.pop()) |node| return LlvmHandle{ .node = node }; |
| 82 | 82 | |
| 83 | 83 | const context_ref = c.LLVMContextCreate() orelse return error.OutOfMemory; |
| ... | ... | @@ -92,24 +92,36 @@ pub const EventLoopLocal = struct { |
| 92 | 92 | return LlvmHandle{ .node = node }; |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | | pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation { |
| 95 | pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation { |
| 96 | 96 | if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr; |
| 97 | 97 | try await (async self.native_libc.data.findNative(self.loop) catch unreachable); |
| 98 | 98 | self.native_libc.resolve(); |
| 99 | 99 | return &self.native_libc.data; |
| 100 | 100 | } |
| 101 | |
| 102 | /// Must be called only once, ever. Sets global state. |
| 103 | pub fn setLlvmArgv(allocator: *Allocator, llvm_argv: []const []const u8) !void { |
| 104 | if (llvm_argv.len != 0) { |
| 105 | var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(allocator, [][]const []const u8{ |
| 106 | [][]const u8{"zig (LLVM option parsing)"}, |
| 107 | llvm_argv, |
| 108 | }); |
| 109 | defer c_compatible_args.deinit(); |
| 110 | c.ZigLLVMParseCommandLineOptions(llvm_argv.len + 1, c_compatible_args.ptr); |
| 111 | } |
| 112 | } |
| 101 | 113 | }; |
| 102 | 114 | |
| 103 | 115 | pub const LlvmHandle = struct { |
| 104 | 116 | node: *std.atomic.Stack(llvm.ContextRef).Node, |
| 105 | 117 | |
| 106 | | pub fn release(self: LlvmHandle, event_loop_local: *EventLoopLocal) void { |
| 107 | | event_loop_local.llvm_handle_pool.push(self.node); |
| 118 | pub fn release(self: LlvmHandle, zig_compiler: *ZigCompiler) void { |
| 119 | zig_compiler.llvm_handle_pool.push(self.node); |
| 108 | 120 | } |
| 109 | 121 | }; |
| 110 | 122 | |
| 111 | 123 | pub const Compilation = struct { |
| 112 | | event_loop_local: *EventLoopLocal, |
| 124 | zig_compiler: *ZigCompiler, |
| 113 | 125 | loop: *event.Loop, |
| 114 | 126 | name: Buffer, |
| 115 | 127 | llvm_triple: Buffer, |
| ... | ... | @@ -137,7 +149,6 @@ pub const Compilation = struct { |
| 137 | 149 | linker_rdynamic: bool, |
| 138 | 150 | |
| 139 | 151 | clang_argv: []const []const u8, |
| 140 | | llvm_argv: []const []const u8, |
| 141 | 152 | lib_dirs: []const []const u8, |
| 142 | 153 | rpath_list: []const []const u8, |
| 143 | 154 | assembly_files: []const []const u8, |
| ... | ... | @@ -217,6 +228,8 @@ pub const Compilation = struct { |
| 217 | 228 | deinit_group: event.Group(void), |
| 218 | 229 | |
| 219 | 230 | destroy_handle: promise, |
| 231 | main_loop_handle: promise, |
| 232 | main_loop_future: event.Future(void), |
| 220 | 233 | |
| 221 | 234 | have_err_ret_tracing: bool, |
| 222 | 235 | |
| ... | ... | @@ -325,7 +338,7 @@ pub const Compilation = struct { |
| 325 | 338 | }; |
| 326 | 339 | |
| 327 | 340 | pub fn create( |
| 328 | | event_loop_local: *EventLoopLocal, |
| 341 | zig_compiler: *ZigCompiler, |
| 329 | 342 | name: []const u8, |
| 330 | 343 | root_src_path: ?[]const u8, |
| 331 | 344 | target: Target, |
| ... | ... | @@ -334,12 +347,45 @@ pub const Compilation = struct { |
| 334 | 347 | is_static: bool, |
| 335 | 348 | zig_lib_dir: []const u8, |
| 336 | 349 | ) !*Compilation { |
| 337 | | const loop = event_loop_local.loop; |
| 338 | | const comp = try event_loop_local.loop.allocator.createOne(Compilation); |
| 339 | | comp.* = Compilation{ |
| 350 | var optional_comp: ?*Compilation = null; |
| 351 | const handle = try async<zig_compiler.loop.allocator> createAsync( |
| 352 | &optional_comp, |
| 353 | zig_compiler, |
| 354 | name, |
| 355 | root_src_path, |
| 356 | target, |
| 357 | kind, |
| 358 | build_mode, |
| 359 | is_static, |
| 360 | zig_lib_dir, |
| 361 | ); |
| 362 | return optional_comp orelse if (getAwaitResult( |
| 363 | zig_compiler.loop.allocator, |
| 364 | handle, |
| 365 | )) |_| unreachable else |err| err; |
| 366 | } |
| 367 | |
| 368 | async fn createAsync( |
| 369 | out_comp: *?*Compilation, |
| 370 | zig_compiler: *ZigCompiler, |
| 371 | name: []const u8, |
| 372 | root_src_path: ?[]const u8, |
| 373 | target: Target, |
| 374 | kind: Kind, |
| 375 | build_mode: builtin.Mode, |
| 376 | is_static: bool, |
| 377 | zig_lib_dir: []const u8, |
| 378 | ) !void { |
| 379 | // workaround for https://github.com/ziglang/zig/issues/1194 |
| 380 | suspend { |
| 381 | resume @handle(); |
| 382 | } |
| 383 | |
| 384 | const loop = zig_compiler.loop; |
| 385 | var comp = Compilation{ |
| 340 | 386 | .loop = loop, |
| 341 | 387 | .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), |
| 342 | | .event_loop_local = event_loop_local, |
| 388 | .zig_compiler = zig_compiler, |
| 343 | 389 | .events = undefined, |
| 344 | 390 | .root_src_path = root_src_path, |
| 345 | 391 | .target = target, |
| ... | ... | @@ -349,6 +395,9 @@ pub const Compilation = struct { |
| 349 | 395 | .zig_lib_dir = zig_lib_dir, |
| 350 | 396 | .zig_std_dir = undefined, |
| 351 | 397 | .tmp_dir = event.Future(BuildError![]u8).init(loop), |
| 398 | .destroy_handle = @handle(), |
| 399 | .main_loop_handle = undefined, |
| 400 | .main_loop_future = event.Future(void).init(loop), |
| 352 | 401 | |
| 353 | 402 | .name = undefined, |
| 354 | 403 | .llvm_triple = undefined, |
| ... | ... | @@ -373,7 +422,6 @@ pub const Compilation = struct { |
| 373 | 422 | .is_static = is_static, |
| 374 | 423 | .linker_rdynamic = false, |
| 375 | 424 | .clang_argv = [][]const u8{}, |
| 376 | | .llvm_argv = [][]const u8{}, |
| 377 | 425 | .lib_dirs = [][]const u8{}, |
| 378 | 426 | .rpath_list = [][]const u8{}, |
| 379 | 427 | .assembly_files = [][]const u8{}, |
| ... | ... | @@ -381,7 +429,7 @@ pub const Compilation = struct { |
| 381 | 429 | .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()), |
| 382 | 430 | .windows_subsystem_windows = false, |
| 383 | 431 | .windows_subsystem_console = false, |
| 384 | | .link_libs_list = ArrayList(*LinkLib).init(comp.arena()), |
| 432 | .link_libs_list = undefined, |
| 385 | 433 | .libc_link_lib = null, |
| 386 | 434 | .err_color = errmsg.Color.Auto, |
| 387 | 435 | .darwin_frameworks = [][]const u8{}, |
| ... | ... | @@ -420,19 +468,20 @@ pub const Compilation = struct { |
| 420 | 468 | .std_package = undefined, |
| 421 | 469 | |
| 422 | 470 | .override_libc = null, |
| 423 | | .destroy_handle = undefined, |
| 424 | 471 | .have_err_ret_tracing = false, |
| 425 | | .primitive_type_table = TypeTable.init(comp.arena()), |
| 472 | .primitive_type_table = undefined, |
| 426 | 473 | |
| 427 | 474 | .fs_watch = undefined, |
| 428 | 475 | }; |
| 429 | | errdefer { |
| 476 | comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); |
| 477 | comp.primitive_type_table = TypeTable.init(comp.arena()); |
| 478 | |
| 479 | defer { |
| 430 | 480 | comp.int_type_table.private_data.deinit(); |
| 431 | 481 | comp.array_type_table.private_data.deinit(); |
| 432 | 482 | comp.ptr_type_table.private_data.deinit(); |
| 433 | 483 | comp.fn_type_table.private_data.deinit(); |
| 434 | 484 | comp.arena_allocator.deinit(); |
| 435 | | comp.loop.allocator.destroy(comp); |
| 436 | 485 | } |
| 437 | 486 | |
| 438 | 487 | comp.name = try Buffer.init(comp.arena(), name); |
| ... | ... | @@ -452,8 +501,8 @@ pub const Compilation = struct { |
| 452 | 501 | // As a workaround we do not use target native features on Windows. |
| 453 | 502 | var target_specific_cpu_args: ?[*]u8 = null; |
| 454 | 503 | var target_specific_cpu_features: ?[*]u8 = null; |
| 455 | | errdefer llvm.DisposeMessage(target_specific_cpu_args); |
| 456 | | errdefer llvm.DisposeMessage(target_specific_cpu_features); |
| 504 | defer llvm.DisposeMessage(target_specific_cpu_args); |
| 505 | defer llvm.DisposeMessage(target_specific_cpu_features); |
| 457 | 506 | if (target == Target.Native and !target.isWindows()) { |
| 458 | 507 | target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; |
| 459 | 508 | target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; |
| ... | ... | @@ -468,16 +517,16 @@ pub const Compilation = struct { |
| 468 | 517 | reloc_mode, |
| 469 | 518 | llvm.CodeModelDefault, |
| 470 | 519 | ) orelse return error.OutOfMemory; |
| 471 | | errdefer llvm.DisposeTargetMachine(comp.target_machine); |
| 520 | defer llvm.DisposeTargetMachine(comp.target_machine); |
| 472 | 521 | |
| 473 | 522 | comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory; |
| 474 | | errdefer llvm.DisposeTargetData(comp.target_data_ref); |
| 523 | defer llvm.DisposeTargetData(comp.target_data_ref); |
| 475 | 524 | |
| 476 | 525 | comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory; |
| 477 | | errdefer llvm.DisposeMessage(comp.target_layout_str); |
| 526 | defer llvm.DisposeMessage(comp.target_layout_str); |
| 478 | 527 | |
| 479 | 528 | comp.events = try event.Channel(Event).create(comp.loop, 0); |
| 480 | | errdefer comp.events.destroy(); |
| 529 | defer comp.events.destroy(); |
| 481 | 530 | |
| 482 | 531 | if (root_src_path) |root_src| { |
| 483 | 532 | const dirname = std.os.path.dirname(root_src) orelse "."; |
| ... | ... | @@ -491,13 +540,25 @@ pub const Compilation = struct { |
| 491 | 540 | } |
| 492 | 541 | |
| 493 | 542 | comp.fs_watch = try fs.Watch(*Scope.Root).create(loop, 16); |
| 494 | | errdefer comp.fs_watch.destroy(); |
| 543 | defer comp.fs_watch.destroy(); |
| 495 | 544 | |
| 496 | 545 | try comp.initTypes(); |
| 546 | defer comp.primitive_type_table.deinit(); |
| 547 | |
| 548 | // Set this to indicate that initialization completed successfully. |
| 549 | // from here on out we must not return an error. |
| 550 | // This must occur before the first suspend/await. |
| 551 | comp.main_loop_handle = async comp.mainLoop() catch unreachable; |
| 552 | out_comp.* = &comp; |
| 553 | suspend; |
| 497 | 554 | |
| 498 | | comp.destroy_handle = try async<loop.allocator> comp.internalDeinit(); |
| 555 | // From here on is cleanup. |
| 556 | await (async comp.deinit_group.wait() catch unreachable); |
| 499 | 557 | |
| 500 | | return comp; |
| 558 | if (comp.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { |
| 559 | // TODO evented I/O? |
| 560 | os.deleteTree(comp.arena(), tmp_dir) catch {}; |
| 561 | } else |_| {}; |
| 501 | 562 | } |
| 502 | 563 | |
| 503 | 564 | /// it does ref the result because it could be an arbitrary integer size |
| ... | ... | @@ -683,49 +744,19 @@ pub const Compilation = struct { |
| 683 | 744 | assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null); |
| 684 | 745 | } |
| 685 | 746 | |
| 686 | | /// This function can safely use async/await, because it manages Compilation's lifetime, |
| 687 | | /// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes. |
| 688 | | async fn internalDeinit(self: *Compilation) void { |
| 689 | | suspend; |
| 690 | | |
| 691 | | await (async self.deinit_group.wait() catch unreachable); |
| 692 | | if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { |
| 693 | | // TODO evented I/O? |
| 694 | | os.deleteTree(self.arena(), tmp_dir) catch {}; |
| 695 | | } else |_| {}; |
| 696 | | |
| 697 | | self.fs_watch.destroy(); |
| 698 | | self.events.destroy(); |
| 699 | | |
| 700 | | llvm.DisposeMessage(self.target_layout_str); |
| 701 | | llvm.DisposeTargetData(self.target_data_ref); |
| 702 | | llvm.DisposeTargetMachine(self.target_machine); |
| 703 | | |
| 704 | | self.primitive_type_table.deinit(); |
| 705 | | |
| 706 | | self.arena_allocator.deinit(); |
| 707 | | self.gpa().destroy(self); |
| 708 | | } |
| 709 | | |
| 710 | 747 | pub fn destroy(self: *Compilation) void { |
| 748 | cancel self.main_loop_handle; |
| 711 | 749 | resume self.destroy_handle; |
| 712 | 750 | } |
| 713 | 751 | |
| 714 | | pub fn build(self: *Compilation) !void { |
| 715 | | if (self.llvm_argv.len != 0) { |
| 716 | | var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ |
| 717 | | [][]const u8{"zig (LLVM option parsing)"}, |
| 718 | | self.llvm_argv, |
| 719 | | }); |
| 720 | | defer c_compatible_args.deinit(); |
| 721 | | // TODO this sets global state |
| 722 | | c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); |
| 723 | | } |
| 724 | | |
| 725 | | _ = try async<self.gpa()> self.buildAsync(); |
| 752 | fn start(self: *Compilation) void { |
| 753 | self.main_loop_future.resolve(); |
| 726 | 754 | } |
| 727 | 755 | |
| 728 | | async fn buildAsync(self: *Compilation) void { |
| 756 | async fn mainLoop(self: *Compilation) void { |
| 757 | // wait until start() is called |
| 758 | _ = await (async self.main_loop_future.get() catch unreachable); |
| 759 | |
| 729 | 760 | var build_result = await (async self.initialCompile() catch unreachable); |
| 730 | 761 | |
| 731 | 762 | while (true) { |
| ... | ... | @@ -1131,7 +1162,7 @@ pub const Compilation = struct { |
| 1131 | 1162 | async fn startFindingNativeLibC(self: *Compilation) void { |
| 1132 | 1163 | await (async self.loop.yield() catch unreachable); |
| 1133 | 1164 | // we don't care if it fails, we're just trying to kick off the future resolution |
| 1134 | | _ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return; |
| 1165 | _ = (await (async self.zig_compiler.getNativeLibC() catch unreachable)) catch return; |
| 1135 | 1166 | } |
| 1136 | 1167 | |
| 1137 | 1168 | /// General Purpose Allocator. Must free when done. |
| ... | ... | @@ -1189,7 +1220,7 @@ pub const Compilation = struct { |
| 1189 | 1220 | var rand_bytes: [9]u8 = undefined; |
| 1190 | 1221 | |
| 1191 | 1222 | { |
| 1192 | | const held = await (async self.event_loop_local.prng.acquire() catch unreachable); |
| 1223 | const held = await (async self.zig_compiler.prng.acquire() catch unreachable); |
| 1193 | 1224 | defer held.release(); |
| 1194 | 1225 | |
| 1195 | 1226 | held.value.random.bytes(rand_bytes[0..]); |
| ... | ... | @@ -1424,3 +1455,14 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void { |
| 1424 | 1455 | fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val }; |
| 1425 | 1456 | symbol_name_consumed = true; |
| 1426 | 1457 | } |
| 1458 | |
| 1459 | // TODO these are hacks which should probably be solved by the language |
| 1460 | fn getAwaitResult(allocator: *Allocator, handle: var) @typeInfo(@typeOf(handle)).Promise.child.? { |
| 1461 | var result: ?@typeInfo(@typeOf(handle)).Promise.child.? = null; |
| 1462 | cancel (async<allocator> getAwaitResultAsync(handle, &result) catch unreachable); |
| 1463 | return result.?; |
| 1464 | } |
| 1465 | |
| 1466 | async fn getAwaitResultAsync(handle: var, out: *?@typeInfo(@typeOf(handle)).Promise.child.?) void { |
| 1467 | out.* = await handle; |
| 1468 | } |