| author | |
| committer | |
| log | 0986dcf1cf5b67ad2b2e606622bf9b2c22e01194 |
| tree | 0f2a6536a654932e15985717d2ddd78b944ccb66 |
| parent | fb947c365e95298a4619b8db2c0d40b9d69172f2 |
7 files changed, 302 insertions(+), 79 deletions(-)
lib/std/fifo.zig+20-17| ... | ... | @@ -191,8 +191,8 @@ pub fn LinearFifo( |
| 191 | 191 | } |
| 192 | 192 | |
| 193 | 193 | /// Read the next item from the fifo |
| 194 | pub fn readItem(self: *Self) !T { | |
| 195 | if (self.count == 0) return error.EndOfStream; | |
| 194 | pub fn readItem(self: *Self) ?T { | |
| 195 | if (self.count == 0) return null; | |
| 196 | 196 | |
| 197 | 197 | const c = self.buf[self.head]; |
| 198 | 198 | self.discard(1); |
| ... | ... | @@ -282,7 +282,10 @@ pub fn LinearFifo( |
| 282 | 282 | /// Write a single item to the fifo |
| 283 | 283 | pub fn writeItem(self: *Self, item: T) !void { |
| 284 | 284 | try self.ensureUnusedCapacity(1); |
| 285 | return self.writeItemAssumeCapacity(item); | |
| 286 | } | |
| 285 | 287 | |
| 288 | pub fn writeItemAssumeCapacity(self: *Self, item: T) void { | |
| 286 | 289 | var tail = self.head + self.count; |
| 287 | 290 | if (powers_of_two) { |
| 288 | 291 | tail &= self.buf.len - 1; |
| ... | ... | @@ -342,10 +345,10 @@ pub fn LinearFifo( |
| 342 | 345 | } |
| 343 | 346 | } |
| 344 | 347 | |
| 345 | /// Peek at the item at `offset` | |
| 346 | pub fn peekItem(self: Self, offset: usize) error{EndOfStream}!T { | |
| 347 | if (offset >= self.count) | |
| 348 | return error.EndOfStream; | |
| 348 | /// Returns the item at `offset`. | |
| 349 | /// Asserts offset is within bounds. | |
| 350 | pub fn peekItem(self: Self, offset: usize) T { | |
| 351 | assert(offset < self.count); | |
| 349 | 352 | |
| 350 | 353 | var index = self.head + offset; |
| 351 | 354 | if (powers_of_two) { |
| ... | ... | @@ -369,18 +372,18 @@ test "LinearFifo(u8, .Dynamic)" { |
| 369 | 372 | { |
| 370 | 373 | var i: usize = 0; |
| 371 | 374 | while (i < 5) : (i += 1) { |
| 372 | try fifo.write(&[_]u8{try fifo.peekItem(i)}); | |
| 375 | try fifo.write(&[_]u8{fifo.peekItem(i)}); | |
| 373 | 376 | } |
| 374 | 377 | testing.expectEqual(@as(usize, 10), fifo.readableLength()); |
| 375 | 378 | testing.expectEqualSlices(u8, "HELLOHELLO", fifo.readableSlice(0)); |
| 376 | 379 | } |
| 377 | 380 | |
| 378 | 381 | { |
| 379 | testing.expectEqual(@as(u8, 'H'), try fifo.readItem()); | |
| 380 | testing.expectEqual(@as(u8, 'E'), try fifo.readItem()); | |
| 381 | testing.expectEqual(@as(u8, 'L'), try fifo.readItem()); | |
| 382 | testing.expectEqual(@as(u8, 'L'), try fifo.readItem()); | |
| 383 | testing.expectEqual(@as(u8, 'O'), try fifo.readItem()); | |
| 382 | testing.expectEqual(@as(u8, 'H'), fifo.readItem().?); | |
| 383 | testing.expectEqual(@as(u8, 'E'), fifo.readItem().?); | |
| 384 | testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); | |
| 385 | testing.expectEqual(@as(u8, 'L'), fifo.readItem().?); | |
| 386 | testing.expectEqual(@as(u8, 'O'), fifo.readItem().?); | |
| 384 | 387 | } |
| 385 | 388 | testing.expectEqual(@as(usize, 5), fifo.readableLength()); |
| 386 | 389 | |
| ... | ... | @@ -451,11 +454,11 @@ test "LinearFifo" { |
| 451 | 454 | testing.expectEqual(@as(usize, 5), fifo.readableLength()); |
| 452 | 455 | |
| 453 | 456 | { |
| 454 | testing.expectEqual(@as(T, 0), try fifo.readItem()); | |
| 455 | testing.expectEqual(@as(T, 1), try fifo.readItem()); | |
| 456 | testing.expectEqual(@as(T, 1), try fifo.readItem()); | |
| 457 | testing.expectEqual(@as(T, 0), try fifo.readItem()); | |
| 458 | testing.expectEqual(@as(T, 1), try fifo.readItem()); | |
| 457 | testing.expectEqual(@as(T, 0), fifo.readItem().?); | |
| 458 | testing.expectEqual(@as(T, 1), fifo.readItem().?); | |
| 459 | testing.expectEqual(@as(T, 1), fifo.readItem().?); | |
| 460 | testing.expectEqual(@as(T, 0), fifo.readItem().?); | |
| 461 | testing.expectEqual(@as(T, 1), fifo.readItem().?); | |
| 459 | 462 | testing.expectEqual(@as(usize, 0), fifo.readableLength()); |
| 460 | 463 | } |
| 461 | 464 |
src-self-hosted/codegen.zig+77-10| ... | ... | @@ -9,7 +9,18 @@ const link = @import("link.zig"); |
| 9 | 9 | const Target = std.Target; |
| 10 | 10 | const Allocator = mem.Allocator; |
| 11 | 11 | |
| 12 | pub fn generateSymbol(bin_file: *link.ElfFile, typed_value: TypedValue, code: *std.ArrayList(u8)) !?*ir.ErrorMsg { | |
| 12 | pub const Result = union(enum) { | |
| 13 | /// This value might or might not alias the `code` parameter passed to `generateSymbol`. | |
| 14 | ok: []const u8, | |
| 15 | fail: *ir.ErrorMsg, | |
| 16 | }; | |
| 17 | ||
| 18 | pub fn generateSymbol( | |
| 19 | bin_file: *link.ElfFile, | |
| 20 | src: usize, | |
| 21 | typed_value: TypedValue, | |
| 22 | code: *std.ArrayList(u8), | |
| 23 | ) error{OutOfMemory}!Result { | |
| 13 | 24 | switch (typed_value.ty.zigTypeTag()) { |
| 14 | 25 | .Fn => { |
| 15 | 26 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; |
| ... | ... | @@ -18,25 +29,77 @@ pub fn generateSymbol(bin_file: *link.ElfFile, typed_value: TypedValue, code: *s |
| 18 | 29 | .target = &bin_file.options.target, |
| 19 | 30 | .mod_fn = module_fn, |
| 20 | 31 | .code = code, |
| 21 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), | |
| 32 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator), | |
| 22 | 33 | .err_msg = null, |
| 23 | 34 | }; |
| 24 | 35 | defer function.inst_table.deinit(); |
| 25 | 36 | |
| 26 | 37 | for (module_fn.analysis.success.instructions) |inst| { |
| 27 | 38 | const new_inst = function.genFuncInst(inst) catch |err| switch (err) { |
| 28 | error.CodegenFail => { | |
| 29 | assert(function.err_msg != null); | |
| 30 | break; | |
| 31 | }, | |
| 39 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 32 | 40 | else => |e| return e, |
| 33 | 41 | }; |
| 34 | 42 | try function.inst_table.putNoClobber(inst, new_inst); |
| 35 | 43 | } |
| 36 | 44 | |
| 37 | return function.err_msg; | |
| 45 | if (function.err_msg) |em| { | |
| 46 | return Result{ .fail = em }; | |
| 47 | } else { | |
| 48 | return Result{ .ok = code.items }; | |
| 49 | } | |
| 50 | }, | |
| 51 | .Array => { | |
| 52 | if (typed_value.val.cast(Value.Payload.Bytes)) |payload| { | |
| 53 | return Result{ .ok = payload.data }; | |
| 54 | } | |
| 55 | return Result{ | |
| 56 | .fail = try ir.ErrorMsg.create( | |
| 57 | bin_file.allocator, | |
| 58 | src, | |
| 59 | "TODO implement generateSymbol for more kinds of arrays", | |
| 60 | .{}, | |
| 61 | ), | |
| 62 | }; | |
| 63 | }, | |
| 64 | .Pointer => { | |
| 65 | if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| { | |
| 66 | const decl = payload.decl; | |
| 67 | assert(decl.link.local_sym_index != 0); | |
| 68 | // TODO handle the dependency of this symbol on the decl's vaddr. | |
| 69 | // If the decl changes vaddr, then this symbol needs to get regenerated. | |
| 70 | const vaddr = bin_file.symbols.items[decl.link.local_sym_index].st_value; | |
| 71 | const endian = bin_file.options.target.cpu.arch.endian(); | |
| 72 | switch (bin_file.ptr_width) { | |
| 73 | .p32 => { | |
| 74 | try code.resize(4); | |
| 75 | mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); | |
| 76 | }, | |
| 77 | .p64 => { | |
| 78 | try code.resize(8); | |
| 79 | mem.writeInt(u64, code.items[0..8], vaddr, endian); | |
| 80 | }, | |
| 81 | } | |
| 82 | return Result{ .ok = code.items }; | |
| 83 | } | |
| 84 | return Result{ | |
| 85 | .fail = try ir.ErrorMsg.create( | |
| 86 | bin_file.allocator, | |
| 87 | src, | |
| 88 | "TODO implement generateSymbol for pointer {}", | |
| 89 | .{typed_value.val}, | |
| 90 | ), | |
| 91 | }; | |
| 92 | }, | |
| 93 | else => |t| { | |
| 94 | return Result{ | |
| 95 | .fail = try ir.ErrorMsg.create( | |
| 96 | bin_file.allocator, | |
| 97 | src, | |
| 98 | "TODO implement generateSymbol for type '{}'", | |
| 99 | .{@tagName(t)}, | |
| 100 | ), | |
| 101 | }; | |
| 38 | 102 | }, |
| 39 | else => @panic("TODO implement generateSymbol for non-function decls"), | |
| 40 | 103 | } |
| 41 | 104 | } |
| 42 | 105 | |
| ... | ... | @@ -390,14 +453,18 @@ const Function = struct { |
| 390 | 453 | } |
| 391 | 454 | |
| 392 | 455 | fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue { |
| 456 | const allocator = self.code.allocator; | |
| 393 | 457 | switch (typed_value.ty.zigTypeTag()) { |
| 394 | 458 | .Pointer => { |
| 395 | 459 | const ptr_elem_type = typed_value.ty.elemType(); |
| 396 | 460 | switch (ptr_elem_type.zigTypeTag()) { |
| 397 | 461 | .Array => { |
| 398 | 462 | // TODO more checks to make sure this can be emitted as a string literal |
| 399 | const bytes = try typed_value.val.toAllocatedBytes(self.code.allocator); | |
| 400 | defer self.code.allocator.free(bytes); | |
| 463 | const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { | |
| 464 | error.AnalysisFail => unreachable, | |
| 465 | else => |e| return e, | |
| 466 | }; | |
| 467 | defer allocator.free(bytes); | |
| 401 | 468 | const smaller_len = std.math.cast(u32, bytes.len) catch |
| 402 | 469 | return self.fail(src, "TODO handle a larger string constant", .{}); |
| 403 | 470 |
src-self-hosted/ir.zig+101-26| ... | ... | @@ -191,7 +191,7 @@ pub const Module = struct { |
| 191 | 191 | optimize_mode: std.builtin.Mode, |
| 192 | 192 | link_error_flags: link.ElfFile.ErrorFlags = link.ElfFile.ErrorFlags{}, |
| 193 | 193 | |
| 194 | work_stack: ArrayListUnmanaged(WorkItem) = ArrayListUnmanaged(WorkItem){}, | |
| 194 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | |
| 195 | 195 | |
| 196 | 196 | /// We optimize memory usage for a compilation with no compile errors by storing the |
| 197 | 197 | /// error messages and mapping outside of `Decl`. |
| ... | ... | @@ -333,6 +333,15 @@ pub const Module = struct { |
| 333 | 333 | return (try self.typedValue()).val; |
| 334 | 334 | } |
| 335 | 335 | |
| 336 | pub fn dump(self: *Decl) void { | |
| 337 | self.scope.dumpSrc(self.src); | |
| 338 | std.debug.warn(" name={} status={}", .{ mem.spanZ(self.name), @tagName(self.analysis) }); | |
| 339 | if (self.typedValueManaged()) |tvm| { | |
| 340 | std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); | |
| 341 | } | |
| 342 | std.debug.warn("\n", .{}); | |
| 343 | } | |
| 344 | ||
| 336 | 345 | fn typedValueManaged(self: *Decl) ?*TypedValue.Managed { |
| 337 | 346 | switch (self.analysis) { |
| 338 | 347 | .initial_in_progress, |
| ... | ... | @@ -359,7 +368,10 @@ pub const Module = struct { |
| 359 | 368 | queued: *text.Inst.Fn, |
| 360 | 369 | in_progress: *Analysis, |
| 361 | 370 | /// There will be a corresponding ErrorMsg in Module.failed_decls |
| 362 | failure, | |
| 371 | sema_failure, | |
| 372 | /// This Fn might be OK but it depends on another Decl which did not successfully complete | |
| 373 | /// semantic analysis. | |
| 374 | dependency_failure, | |
| 363 | 375 | success: Body, |
| 364 | 376 | }, |
| 365 | 377 | |
| ... | ... | @@ -390,7 +402,7 @@ pub const Module = struct { |
| 390 | 402 | switch (self.tag) { |
| 391 | 403 | .block => return self.cast(Block).?.arena, |
| 392 | 404 | .decl => return &self.cast(DeclAnalysis).?.arena.allocator, |
| 393 | .zir_module => unreachable, | |
| 405 | .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator, | |
| 394 | 406 | } |
| 395 | 407 | } |
| 396 | 408 | |
| ... | ... | @@ -414,6 +426,18 @@ pub const Module = struct { |
| 414 | 426 | } |
| 415 | 427 | } |
| 416 | 428 | |
| 429 | pub fn dumpInst(self: *Scope, inst: *Inst) void { | |
| 430 | const zir_module = self.namespace(); | |
| 431 | const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src); | |
| 432 | std.debug.warn("{}:{}:{}: {}: ty={}\n", .{ | |
| 433 | zir_module.sub_file_path, | |
| 434 | loc.line + 1, | |
| 435 | loc.column + 1, | |
| 436 | @tagName(inst.tag), | |
| 437 | inst.ty, | |
| 438 | }); | |
| 439 | } | |
| 440 | ||
| 417 | 441 | pub const Tag = enum { |
| 418 | 442 | zir_module, |
| 419 | 443 | block, |
| ... | ... | @@ -438,6 +462,7 @@ pub const Module = struct { |
| 438 | 462 | unloaded, |
| 439 | 463 | unloaded_parse_failure, |
| 440 | 464 | loaded_parse_failure, |
| 465 | loaded_sema_failure, | |
| 441 | 466 | loaded_success, |
| 442 | 467 | }, |
| 443 | 468 | |
| ... | ... | @@ -446,7 +471,7 @@ pub const Module = struct { |
| 446 | 471 | .unloaded, |
| 447 | 472 | .unloaded_parse_failure, |
| 448 | 473 | => {}, |
| 449 | .loaded_success => { | |
| 474 | .loaded_success, .loaded_sema_failure => { | |
| 450 | 475 | allocator.free(self.source.bytes); |
| 451 | 476 | self.contents.module.deinit(allocator); |
| 452 | 477 | }, |
| ... | ... | @@ -456,6 +481,11 @@ pub const Module = struct { |
| 456 | 481 | } |
| 457 | 482 | self.* = undefined; |
| 458 | 483 | } |
| 484 | ||
| 485 | pub fn dumpSrc(self: *ZIRModule, src: usize) void { | |
| 486 | const loc = std.zig.findLineColumn(self.source.bytes, src); | |
| 487 | std.debug.warn("{}:{}:{}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 }); | |
| 488 | } | |
| 459 | 489 | }; |
| 460 | 490 | |
| 461 | 491 | /// This is a temporary structure, references to it are valid only |
| ... | ... | @@ -520,7 +550,7 @@ pub const Module = struct { |
| 520 | 550 | |
| 521 | 551 | pub fn deinit(self: *Module) void { |
| 522 | 552 | const allocator = self.allocator; |
| 523 | self.work_stack.deinit(allocator); | |
| 553 | self.work_queue.deinit(); | |
| 524 | 554 | { |
| 525 | 555 | var it = self.decl_table.iterator(); |
| 526 | 556 | while (it.next()) |kv| { |
| ... | ... | @@ -587,6 +617,8 @@ pub const Module = struct { |
| 587 | 617 | |
| 588 | 618 | try self.performAllTheWork(); |
| 589 | 619 | |
| 620 | // TODO unload all the source files from memory | |
| 621 | ||
| 590 | 622 | try self.bin_file.flush(); |
| 591 | 623 | self.link_error_flags = self.bin_file.error_flags; |
| 592 | 624 | } |
| ... | ... | @@ -654,7 +686,7 @@ pub const Module = struct { |
| 654 | 686 | const InnerError = error{ OutOfMemory, AnalysisFail }; |
| 655 | 687 | |
| 656 | 688 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 657 | while (self.work_stack.popOrNull()) |work_item| switch (work_item) { | |
| 689 | while (self.work_queue.readItem()) |work_item| switch (work_item) { | |
| 658 | 690 | .codegen_decl => |decl| switch (decl.analysis) { |
| 659 | 691 | .initial_in_progress, |
| 660 | 692 | .repeat_in_progress, |
| ... | ... | @@ -671,14 +703,22 @@ pub const Module = struct { |
| 671 | 703 | if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { |
| 672 | 704 | switch (payload.func.analysis) { |
| 673 | 705 | .queued => self.analyzeFnBody(decl, payload.func) catch |err| switch (err) { |
| 674 | error.AnalysisFail => continue, | |
| 706 | error.AnalysisFail => { | |
| 707 | if (payload.func.analysis == .queued) { | |
| 708 | payload.func.analysis = .dependency_failure; | |
| 709 | } | |
| 710 | continue; | |
| 711 | }, | |
| 675 | 712 | else => |e| return e, |
| 676 | 713 | }, |
| 677 | 714 | .in_progress => unreachable, |
| 678 | .failure => continue, | |
| 715 | .sema_failure, .dependency_failure => continue, | |
| 679 | 716 | .success => {}, |
| 680 | 717 | } |
| 681 | 718 | } |
| 719 | if (!decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()) | |
| 720 | continue; | |
| 721 | ||
| 682 | 722 | self.bin_file.updateDecl(self, decl) catch |err| switch (err) { |
| 683 | 723 | error.OutOfMemory => return error.OutOfMemory, |
| 684 | 724 | else => { |
| ... | ... | @@ -739,7 +779,10 @@ pub const Module = struct { |
| 739 | 779 | return zir_module; |
| 740 | 780 | }, |
| 741 | 781 | |
| 742 | .unloaded_parse_failure, .loaded_parse_failure => return error.AnalysisFail, | |
| 782 | .unloaded_parse_failure, | |
| 783 | .loaded_parse_failure, | |
| 784 | .loaded_sema_failure, | |
| 785 | => return error.AnalysisFail, | |
| 743 | 786 | .loaded_success => return root_scope.contents.module, |
| 744 | 787 | } |
| 745 | 788 | } |
| ... | ... | @@ -756,14 +799,11 @@ pub const Module = struct { |
| 756 | 799 | |
| 757 | 800 | // Here we ensure enough queue capacity to store all the decls, so that later we can use |
| 758 | 801 | // appendAssumeCapacity. |
| 759 | try self.work_stack.ensureCapacity( | |
| 760 | self.allocator, | |
| 761 | self.work_stack.items.len + src_module.decls.len, | |
| 762 | ); | |
| 802 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | |
| 763 | 803 | |
| 764 | 804 | for (src_module.decls) |decl| { |
| 765 | 805 | if (decl.cast(text.Inst.Export)) |export_inst| { |
| 766 | try analyzeExport(self, &root_scope.base, export_inst); | |
| 806 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base); | |
| 767 | 807 | } |
| 768 | 808 | } |
| 769 | 809 | } |
| ... | ... | @@ -825,10 +865,19 @@ pub const Module = struct { |
| 825 | 865 | }; |
| 826 | 866 | errdefer decl_scope.arena.deinit(); |
| 827 | 867 | |
| 868 | const typed_value = self.analyzeInstConst(&decl_scope.base, old_inst) catch |err| switch (err) { | |
| 869 | error.OutOfMemory => return error.OutOfMemory, | |
| 870 | error.AnalysisFail => { | |
| 871 | switch (new_decl.analysis) { | |
| 872 | .initial_in_progress => new_decl.analysis = .initial_dependency_failure, | |
| 873 | .repeat_in_progress => new_decl.analysis = .repeat_dependency_failure, | |
| 874 | else => {}, | |
| 875 | } | |
| 876 | return error.AnalysisFail; | |
| 877 | }, | |
| 878 | }; | |
| 828 | 879 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); |
| 829 | 880 | |
| 830 | const typed_value = try self.analyzeInstConst(&decl_scope.base, old_inst); | |
| 831 | ||
| 832 | 881 | arena_state.* = decl_scope.arena.state; |
| 833 | 882 | |
| 834 | 883 | new_decl.typed_value = .{ |
| ... | ... | @@ -839,7 +888,7 @@ pub const Module = struct { |
| 839 | 888 | }; |
| 840 | 889 | new_decl.analysis = .complete; |
| 841 | 890 | // We ensureCapacity when scanning for decls. |
| 842 | self.work_stack.appendAssumeCapacity(.{ .codegen_decl = new_decl }); | |
| 891 | self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl }); | |
| 843 | 892 | return new_decl; |
| 844 | 893 | } |
| 845 | 894 | } |
| ... | ... | @@ -1021,11 +1070,8 @@ pub const Module = struct { |
| 1021 | 1070 | } |
| 1022 | 1071 | |
| 1023 | 1072 | fn constStr(self: *Module, scope: *Scope, src: usize, str: []const u8) !*Inst { |
| 1024 | const array_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | |
| 1025 | array_payload.* = .{ .len = str.len }; | |
| 1026 | ||
| 1027 | const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); | |
| 1028 | ty_payload.* = .{ .pointee_type = Type.initPayload(&array_payload.base) }; | |
| 1073 | const ty_payload = try scope.arena().create(Type.Payload.Array_u8_Sentinel0); | |
| 1074 | ty_payload.* = .{ .len = str.len }; | |
| 1029 | 1075 | |
| 1030 | 1076 | const bytes_payload = try scope.arena().create(Value.Payload.Bytes); |
| 1031 | 1077 | bytes_payload.* = .{ .data = str }; |
| ... | ... | @@ -1150,6 +1196,7 @@ pub const Module = struct { |
| 1150 | 1196 | return self.constVoid(scope, old_inst.src); |
| 1151 | 1197 | }, |
| 1152 | 1198 | .primitive => return self.analyzeInstPrimitive(scope, old_inst.cast(text.Inst.Primitive).?), |
| 1199 | .ref => return self.analyzeInstRef(scope, old_inst.cast(text.Inst.Ref).?), | |
| 1153 | 1200 | .fntype => return self.analyzeInstFnType(scope, old_inst.cast(text.Inst.FnType).?), |
| 1154 | 1201 | .intcast => return self.analyzeInstIntCast(scope, old_inst.cast(text.Inst.IntCast).?), |
| 1155 | 1202 | .bitcast => return self.analyzeInstBitCast(scope, old_inst.cast(text.Inst.BitCast).?), |
| ... | ... | @@ -1167,12 +1214,34 @@ pub const Module = struct { |
| 1167 | 1214 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); |
| 1168 | 1215 | } |
| 1169 | 1216 | |
| 1217 | fn analyzeInstRef(self: *Module, scope: *Scope, inst: *text.Inst.Ref) InnerError!*Inst { | |
| 1218 | const decl = try self.resolveCompleteDecl(scope, inst.positionals.operand); | |
| 1219 | return self.analyzeDeclRef(scope, inst.base.src, decl); | |
| 1220 | } | |
| 1221 | ||
| 1170 | 1222 | fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *text.Inst.DeclRef) InnerError!*Inst { |
| 1171 | return self.fail(scope, inst.base.src, "TODO implement analyzeInstDeclFef", .{}); | |
| 1223 | const decl_name = try self.resolveConstString(scope, inst.positionals.name); | |
| 1224 | // This will need to get more fleshed out when there are proper structs & namespaces. | |
| 1225 | const zir_module = scope.namespace(); | |
| 1226 | for (zir_module.contents.module.decls) |src_decl| { | |
| 1227 | if (mem.eql(u8, src_decl.name, decl_name)) { | |
| 1228 | const decl = try self.resolveCompleteDecl(scope, src_decl); | |
| 1229 | return self.analyzeDeclRef(scope, inst.base.src, decl); | |
| 1230 | } | |
| 1231 | } | |
| 1232 | return self.fail(scope, inst.positionals.name.src, "use of undeclared identifier '{}'", .{decl_name}); | |
| 1172 | 1233 | } |
| 1173 | 1234 | |
| 1174 | 1235 | fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { |
| 1175 | return self.fail(scope, src, "TODO implement analyzeDeclRef", .{}); | |
| 1236 | const decl_tv = try decl.typedValue(); | |
| 1237 | const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); | |
| 1238 | ty_payload.* = .{ .pointee_type = decl_tv.ty }; | |
| 1239 | const val_payload = try scope.arena().create(Value.Payload.DeclRef); | |
| 1240 | val_payload.* = .{ .decl = decl }; | |
| 1241 | return self.constInst(scope, src, .{ | |
| 1242 | .ty = Type.initPayload(&ty_payload.base), | |
| 1243 | .val = Value.initPayload(&val_payload.base), | |
| 1244 | }); | |
| 1176 | 1245 | } |
| 1177 | 1246 | |
| 1178 | 1247 | fn analyzeInstCall(self: *Module, scope: *Scope, inst: *text.Inst.Call) InnerError!*Inst { |
| ... | ... | @@ -1929,6 +1998,7 @@ pub const Module = struct { |
| 1929 | 1998 | fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: var) InnerError { |
| 1930 | 1999 | @setCold(true); |
| 1931 | 2000 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); |
| 2001 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | |
| 1932 | 2002 | const err_msg = try ErrorMsg.create(self.allocator, src, format, args); |
| 1933 | 2003 | switch (scope.tag) { |
| 1934 | 2004 | .decl => { |
| ... | ... | @@ -1942,10 +2012,14 @@ pub const Module = struct { |
| 1942 | 2012 | }, |
| 1943 | 2013 | .block => { |
| 1944 | 2014 | const block = scope.cast(Scope.Block).?; |
| 1945 | block.func.analysis = .failure; | |
| 2015 | block.func.analysis = .sema_failure; | |
| 1946 | 2016 | self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg); |
| 1947 | 2017 | }, |
| 1948 | .zir_module => unreachable, | |
| 2018 | .zir_module => { | |
| 2019 | const zir_module = scope.cast(Scope.ZIRModule).?; | |
| 2020 | zir_module.status = .loaded_sema_failure; | |
| 2021 | self.failed_files.putAssumeCapacityNoClobber(zir_module, err_msg); | |
| 2022 | }, | |
| 1949 | 2023 | } |
| 1950 | 2024 | return error.AnalysisFail; |
| 1951 | 2025 | } |
| ... | ... | @@ -2044,6 +2118,7 @@ pub fn main() anyerror!void { |
| 2044 | 2118 | .failed_decls = std.AutoHashMap(*Module.Decl, *ErrorMsg).init(allocator), |
| 2045 | 2119 | .failed_files = std.AutoHashMap(*Module.Scope.ZIRModule, *ErrorMsg).init(allocator), |
| 2046 | 2120 | .failed_exports = std.AutoHashMap(*Module.Export, *ErrorMsg).init(allocator), |
| 2121 | .work_queue = std.fifo.LinearFifo(Module.WorkItem, .Dynamic).init(allocator), | |
| 2047 | 2122 | }; |
| 2048 | 2123 | }; |
| 2049 | 2124 | defer module.deinit(); |
src-self-hosted/ir/text.zig+25-9| ... | ... | @@ -24,8 +24,7 @@ pub const Inst = struct { |
| 24 | 24 | breakpoint, |
| 25 | 25 | call, |
| 26 | 26 | /// Represents a reference to a global decl by name. |
| 27 | /// Canonicalized ZIR will not have any of these. The | |
| 28 | /// syntax `@foo` is equivalent to `declref("foo")`. | |
| 27 | /// The syntax `@foo` is equivalent to `declref("foo")`. | |
| 29 | 28 | declref, |
| 30 | 29 | str, |
| 31 | 30 | int, |
| ... | ... | @@ -39,6 +38,7 @@ pub const Inst = struct { |
| 39 | 38 | @"fn", |
| 40 | 39 | @"export", |
| 41 | 40 | primitive, |
| 41 | ref, | |
| 42 | 42 | fntype, |
| 43 | 43 | intcast, |
| 44 | 44 | bitcast, |
| ... | ... | @@ -67,6 +67,7 @@ pub const Inst = struct { |
| 67 | 67 | .@"fn" => Fn, |
| 68 | 68 | .@"export" => Export, |
| 69 | 69 | .primitive => Primitive, |
| 70 | .ref => Ref, | |
| 70 | 71 | .fntype => FnType, |
| 71 | 72 | .intcast => IntCast, |
| 72 | 73 | .bitcast => BitCast, |
| ... | ... | @@ -234,6 +235,16 @@ pub const Inst = struct { |
| 234 | 235 | kw_args: struct {}, |
| 235 | 236 | }; |
| 236 | 237 | |
| 238 | pub const Ref = struct { | |
| 239 | pub const base_tag = Tag.ref; | |
| 240 | base: Inst, | |
| 241 | ||
| 242 | positionals: struct { | |
| 243 | operand: *Inst, | |
| 244 | }, | |
| 245 | kw_args: struct {}, | |
| 246 | }; | |
| 247 | ||
| 237 | 248 | pub const Primitive = struct { |
| 238 | 249 | pub const base_tag = Tag.primitive; |
| 239 | 250 | base: Inst, |
| ... | ... | @@ -407,7 +418,7 @@ pub const ErrorMsg = struct { |
| 407 | 418 | |
| 408 | 419 | pub const Module = struct { |
| 409 | 420 | decls: []*Inst, |
| 410 | arena: std.heap.ArenaAllocator.State, | |
| 421 | arena: std.heap.ArenaAllocator, | |
| 411 | 422 | error_msg: ?ErrorMsg = null, |
| 412 | 423 | |
| 413 | 424 | pub const Body = struct { |
| ... | ... | @@ -416,7 +427,7 @@ pub const Module = struct { |
| 416 | 427 | |
| 417 | 428 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 418 | 429 | allocator.free(self.decls); |
| 419 | self.arena.promote(allocator).deinit(); | |
| 430 | self.arena.deinit(); | |
| 420 | 431 | self.* = undefined; |
| 421 | 432 | } |
| 422 | 433 | |
| ... | ... | @@ -475,6 +486,7 @@ pub const Module = struct { |
| 475 | 486 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table), |
| 476 | 487 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table), |
| 477 | 488 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table), |
| 489 | .ref => return self.writeInstToStreamGeneric(stream, .ref, decl, inst_table), | |
| 478 | 490 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table), |
| 479 | 491 | .fntype => return self.writeInstToStreamGeneric(stream, .fntype, decl, inst_table), |
| 480 | 492 | .intcast => return self.writeInstToStreamGeneric(stream, .intcast, decl, inst_table), |
| ... | ... | @@ -591,7 +603,7 @@ pub fn parse(allocator: *Allocator, source: [:0]const u8) Allocator.Error!Module |
| 591 | 603 | |
| 592 | 604 | return Module{ |
| 593 | 605 | .decls = parser.decls.toOwnedSlice(allocator), |
| 594 | .arena = parser.arena.state, | |
| 606 | .arena = parser.arena, | |
| 595 | 607 | .error_msg = parser.error_msg, |
| 596 | 608 | }; |
| 597 | 609 | } |
| ... | ... | @@ -630,7 +642,7 @@ const Parser = struct { |
| 630 | 642 | skipSpace(self); |
| 631 | 643 | try requireEatBytes(self, "="); |
| 632 | 644 | skipSpace(self); |
| 633 | const inst = try parseInstruction(self, &body_context, ident[1..]); | |
| 645 | const inst = try parseInstruction(self, &body_context, ident); | |
| 634 | 646 | const ident_index = body_context.instructions.items.len; |
| 635 | 647 | if (try body_context.name_map.put(ident, ident_index)) |_| { |
| 636 | 648 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| ... | ... | @@ -716,7 +728,7 @@ const Parser = struct { |
| 716 | 728 | skipSpace(self); |
| 717 | 729 | try requireEatBytes(self, "="); |
| 718 | 730 | skipSpace(self); |
| 719 | const inst = try parseInstruction(self, null, ident[1..]); | |
| 731 | const inst = try parseInstruction(self, null, ident); | |
| 720 | 732 | const ident_index = self.decls.items.len; |
| 721 | 733 | if (try self.global_name_map.put(ident, ident_index)) |_| { |
| 722 | 734 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| ... | ... | @@ -987,7 +999,7 @@ pub fn emit_zir(allocator: *Allocator, old_module: ir.Module) !Module { |
| 987 | 999 | |
| 988 | 1000 | return Module{ |
| 989 | 1001 | .decls = ctx.decls.toOwnedSlice(allocator), |
| 990 | .arena = ctx.arena.state, | |
| 1002 | .arena = ctx.arena, | |
| 991 | 1003 | }; |
| 992 | 1004 | } |
| 993 | 1005 | |
| ... | ... | @@ -1056,6 +1068,7 @@ const EmitZIR = struct { |
| 1056 | 1068 | } |
| 1057 | 1069 | |
| 1058 | 1070 | fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst { |
| 1071 | const allocator = &self.arena.allocator; | |
| 1059 | 1072 | switch (typed_value.ty.zigTypeTag()) { |
| 1060 | 1073 | .Pointer => { |
| 1061 | 1074 | const ptr_elem_type = typed_value.ty.elemType(); |
| ... | ... | @@ -1067,7 +1080,10 @@ const EmitZIR = struct { |
| 1067 | 1080 | // ptr_elem_type.hasSentinel(Value.initTag(.zero))) |
| 1068 | 1081 | //{ |
| 1069 | 1082 | //} |
| 1070 | const bytes = try typed_value.val.toAllocatedBytes(&self.arena.allocator); | |
| 1083 | const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { | |
| 1084 | error.AnalysisFail => unreachable, | |
| 1085 | else => |e| return e, | |
| 1086 | }; | |
| 1071 | 1087 | return self.emitStringLiteral(src, bytes); |
| 1072 | 1088 | }, |
| 1073 | 1089 | else => |t| std.debug.panic("TODO implement emitTypedValue for pointer to {}", .{@tagName(t)}), |
src-self-hosted/link.zig+27-16| ... | ... | @@ -33,9 +33,11 @@ pub fn openBinFilePath( |
| 33 | 33 | options: Options, |
| 34 | 34 | ) !ElfFile { |
| 35 | 35 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(options) }); |
| 36 | defer file.close(); | |
| 36 | errdefer file.close(); | |
| 37 | 37 | |
| 38 | return openBinFile(allocator, file, options); | |
| 38 | var bin_file = try openBinFile(allocator, file, options); | |
| 39 | bin_file.owns_file_handle = true; | |
| 40 | return bin_file; | |
| 39 | 41 | } |
| 40 | 42 | |
| 41 | 43 | /// Atomically overwrites the old file, if present. |
| ... | ... | @@ -89,6 +91,7 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !ElfF |
| 89 | 91 | pub const ElfFile = struct { |
| 90 | 92 | allocator: *Allocator, |
| 91 | 93 | file: fs.File, |
| 94 | owns_file_handle: bool, | |
| 92 | 95 | options: Options, |
| 93 | 96 | ptr_width: enum { p32, p64 }, |
| 94 | 97 | |
| ... | ... | @@ -162,6 +165,8 @@ pub const ElfFile = struct { |
| 162 | 165 | self.shstrtab.deinit(self.allocator); |
| 163 | 166 | self.symbols.deinit(self.allocator); |
| 164 | 167 | self.offset_table.deinit(self.allocator); |
| 168 | if (self.owns_file_handle) | |
| 169 | self.file.close(); | |
| 165 | 170 | } |
| 166 | 171 | |
| 167 | 172 | // `alloc_num / alloc_den` is the factor of padding when allocation |
| ... | ... | @@ -685,7 +690,7 @@ pub const ElfFile = struct { |
| 685 | 690 | // TODO Also detect virtual address collisions. |
| 686 | 691 | const text_capacity = self.allocatedSize(shdr.sh_offset); |
| 687 | 692 | // TODO instead of looping here, maintain a free list and a pointer to the end. |
| 688 | var last_start: u64 = 0; | |
| 693 | var last_start: u64 = phdr.p_vaddr; | |
| 689 | 694 | var last_size: u64 = 0; |
| 690 | 695 | for (self.symbols.items) |sym| { |
| 691 | 696 | if (sym.st_value > last_start) { |
| ... | ... | @@ -738,19 +743,21 @@ pub const ElfFile = struct { |
| 738 | 743 | } |
| 739 | 744 | |
| 740 | 745 | pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void { |
| 741 | var code = std.ArrayList(u8).init(self.allocator); | |
| 742 | defer code.deinit(); | |
| 746 | var code_buffer = std.ArrayList(u8).init(self.allocator); | |
| 747 | defer code_buffer.deinit(); | |
| 743 | 748 | |
| 744 | 749 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 745 | const err_msg = try codegen.generateSymbol(self, typed_value, &code); | |
| 746 | if (err_msg) |em| { | |
| 747 | decl.analysis = .codegen_failure; | |
| 748 | _ = try module.failed_decls.put(decl, em); | |
| 749 | return; | |
| 750 | } | |
| 750 | const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) { | |
| 751 | .ok => |x| x, | |
| 752 | .fail => |em| { | |
| 753 | decl.analysis = .codegen_failure; | |
| 754 | _ = try module.failed_decls.put(decl, em); | |
| 755 | return; | |
| 756 | }, | |
| 757 | }; | |
| 751 | 758 | |
| 752 | 759 | const file_offset = blk: { |
| 753 | const code_size = code.items.len; | |
| 760 | const code_size = code.len; | |
| 754 | 761 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { |
| 755 | 762 | .Fn => elf.STT_FUNC, |
| 756 | 763 | else => elf.STT_OBJECT, |
| ... | ... | @@ -793,11 +800,13 @@ pub const ElfFile = struct { |
| 793 | 800 | errdefer self.symbols.shrink(self.allocator, self.symbols.items.len - 1); |
| 794 | 801 | self.offset_table.appendAssumeCapacity(new_block.vaddr); |
| 795 | 802 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); |
| 796 | try self.writeSymbol(local_sym_index); | |
| 797 | try self.writeOffsetTableEntry(offset_table_index); | |
| 798 | 803 | |
| 799 | 804 | self.symbol_count_dirty = true; |
| 800 | 805 | self.offset_table_count_dirty = true; |
| 806 | ||
| 807 | try self.writeSymbol(local_sym_index); | |
| 808 | try self.writeOffsetTableEntry(offset_table_index); | |
| 809 | ||
| 801 | 810 | decl.link = .{ |
| 802 | 811 | .local_sym_index = @intCast(u32, local_sym_index), |
| 803 | 812 | .offset_table_index = @intCast(u32, offset_table_index), |
| ... | ... | @@ -807,7 +816,7 @@ pub const ElfFile = struct { |
| 807 | 816 | } |
| 808 | 817 | }; |
| 809 | 818 | |
| 810 | try self.file.pwriteAll(code.items, file_offset); | |
| 819 | try self.file.pwriteAll(code, file_offset); | |
| 811 | 820 | |
| 812 | 821 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 813 | 822 | const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*ir.Module.Export{}; |
| ... | ... | @@ -823,7 +832,7 @@ pub const ElfFile = struct { |
| 823 | 832 | ) !void { |
| 824 | 833 | try self.symbols.ensureCapacity(self.allocator, self.symbols.items.len + exports.len); |
| 825 | 834 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 826 | assert(decl.link.local_sym_index != 0); | |
| 835 | if (decl.link.local_sym_index == 0) return; | |
| 827 | 836 | const decl_sym = self.symbols.items[decl.link.local_sym_index]; |
| 828 | 837 | |
| 829 | 838 | for (exports) |exp| { |
| ... | ... | @@ -1112,6 +1121,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El |
| 1112 | 1121 | else => return error.UnsupportedELFArchitecture, |
| 1113 | 1122 | }, |
| 1114 | 1123 | .shdr_table_dirty = true, |
| 1124 | .owns_file_handle = false, | |
| 1115 | 1125 | }; |
| 1116 | 1126 | errdefer self.deinit(); |
| 1117 | 1127 | |
| ... | ... | @@ -1161,6 +1171,7 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Elf |
| 1161 | 1171 | var self: ElfFile = .{ |
| 1162 | 1172 | .allocator = allocator, |
| 1163 | 1173 | .file = file, |
| 1174 | .owns_file_handle = false, | |
| 1164 | 1175 | .options = options, |
| 1165 | 1176 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 1166 | 1177 | 32 => .p32, |
src-self-hosted/type.zig+44| ... | ... | @@ -262,6 +262,50 @@ pub const Type = extern union { |
| 262 | 262 | } |
| 263 | 263 | } |
| 264 | 264 | |
| 265 | pub fn hasCodeGenBits(self: Type) bool { | |
| 266 | return switch (self.tag()) { | |
| 267 | .u8, | |
| 268 | .i8, | |
| 269 | .isize, | |
| 270 | .usize, | |
| 271 | .c_short, | |
| 272 | .c_ushort, | |
| 273 | .c_int, | |
| 274 | .c_uint, | |
| 275 | .c_long, | |
| 276 | .c_ulong, | |
| 277 | .c_longlong, | |
| 278 | .c_ulonglong, | |
| 279 | .c_longdouble, | |
| 280 | .f16, | |
| 281 | .f32, | |
| 282 | .f64, | |
| 283 | .f128, | |
| 284 | .bool, | |
| 285 | .anyerror, | |
| 286 | .fn_noreturn_no_args, | |
| 287 | .fn_naked_noreturn_no_args, | |
| 288 | .fn_ccc_void_no_args, | |
| 289 | .single_const_pointer_to_comptime_int, | |
| 290 | .const_slice_u8, // See last_no_payload_tag below. | |
| 291 | .array_u8_sentinel_0, | |
| 292 | .array, | |
| 293 | .single_const_pointer, | |
| 294 | .int_signed, | |
| 295 | .int_unsigned, | |
| 296 | => true, | |
| 297 | ||
| 298 | .c_void, | |
| 299 | .void, | |
| 300 | .type, | |
| 301 | .comptime_int, | |
| 302 | .comptime_float, | |
| 303 | .noreturn, | |
| 304 | .@"null", | |
| 305 | => false, | |
| 306 | }; | |
| 307 | } | |
| 308 | ||
| 265 | 309 | pub fn isSinglePointer(self: Type) bool { |
| 266 | 310 | return switch (self.tag()) { |
| 267 | 311 | .u8, |
src-self-hosted/value.zig+8-1| ... | ... | @@ -180,10 +180,17 @@ pub const Value = extern union { |
| 180 | 180 | |
| 181 | 181 | /// Asserts that the value is representable as an array of bytes. |
| 182 | 182 | /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. |
| 183 | pub fn toAllocatedBytes(self: Value, allocator: *Allocator) Allocator.Error![]u8 { | |
| 183 | pub fn toAllocatedBytes(self: Value, allocator: *Allocator) ![]u8 { | |
| 184 | 184 | if (self.cast(Payload.Bytes)) |bytes| { |
| 185 | 185 | return std.mem.dupe(allocator, u8, bytes.data); |
| 186 | 186 | } |
| 187 | if (self.cast(Payload.Repeated)) |repeated| { | |
| 188 | @panic("TODO implement toAllocatedBytes for this Value tag"); | |
| 189 | } | |
| 190 | if (self.cast(Payload.DeclRef)) |declref| { | |
| 191 | const val = try declref.decl.value(); | |
| 192 | return val.toAllocatedBytes(allocator); | |
| 193 | } | |
| 187 | 194 | unreachable; |
| 188 | 195 | } |
| 189 | 196 |