| author | |
| committer | |
| log | ebb81ebe59d56a2ccb104e100b9c96df82eedc97 |
| tree | b58694f89da99e9a78c7e470bd2affb4f141fdf5 |
| parent | 81a01bd4815779ebeb5898a825bf91628b75ff47 |
4 files changed, 249 insertions(+), 61 deletions(-)
src-self-hosted/codegen.zig+57-21| ... | ... | @@ -33,6 +33,7 @@ pub fn generateSymbol( |
| 33 | 33 | |
| 34 | 34 | var function = Function{ |
| 35 | 35 | .target = &bin_file.options.target, |
| 36 | .bin_file = bin_file, | |
| 36 | 37 | .mod_fn = module_fn, |
| 37 | 38 | .code = code, |
| 38 | 39 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator), |
| ... | ... | @@ -144,6 +145,7 @@ pub fn generateSymbol( |
| 144 | 145 | } |
| 145 | 146 | |
| 146 | 147 | const Function = struct { |
| 148 | bin_file: *link.ElfFile, | |
| 147 | 149 | target: *const std.Target, |
| 148 | 150 | mod_fn: *const ir.Module.Fn, |
| 149 | 151 | code: *std.ArrayList(u8), |
| ... | ... | @@ -160,6 +162,8 @@ const Function = struct { |
| 160 | 162 | /// The value is in a target-specific register. The value can |
| 161 | 163 | /// be @intToEnum casted to the respective Reg enum. |
| 162 | 164 | register: usize, |
| 165 | /// The value is in memory at a hard-coded address. | |
| 166 | memory: u64, | |
| 163 | 167 | }; |
| 164 | 168 | |
| 165 | 169 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { |
| ... | ... | @@ -375,6 +379,7 @@ const Function = struct { |
| 375 | 379 | }, |
| 376 | 380 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rax = embedded_in_code", .{}), |
| 377 | 381 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rax = register", .{}), |
| 382 | .memory => return self.fail(src, "TODO implement x86_64 genSetReg %rax = memory", .{}), | |
| 378 | 383 | }, |
| 379 | 384 | .rdx => switch (mcv) { |
| 380 | 385 | .none, .unreach => unreachable, |
| ... | ... | @@ -406,6 +411,7 @@ const Function = struct { |
| 406 | 411 | }, |
| 407 | 412 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = embedded_in_code", .{}), |
| 408 | 413 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = register", .{}), |
| 414 | .memory => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = memory", .{}), | |
| 409 | 415 | }, |
| 410 | 416 | .rdi => switch (mcv) { |
| 411 | 417 | .none, .unreach => unreachable, |
| ... | ... | @@ -437,10 +443,37 @@ const Function = struct { |
| 437 | 443 | }, |
| 438 | 444 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = embedded_in_code", .{}), |
| 439 | 445 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = register", .{}), |
| 446 | .memory => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = memory", .{}), | |
| 440 | 447 | }, |
| 441 | 448 | .rsi => switch (mcv) { |
| 442 | 449 | .none, .unreach => unreachable, |
| 443 | .immediate => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = immediate", .{}), | |
| 450 | .immediate => |x| { | |
| 451 | // Setting the edi register zeroes the upper part of rdi, so if the number is small | |
| 452 | // enough, that is preferable. | |
| 453 | // Best case: zero | |
| 454 | // 31 f6 xor esi,esi | |
| 455 | if (x == 0) { | |
| 456 | return self.code.appendSlice(&[_]u8{ 0x31, 0xf6 }); | |
| 457 | } | |
| 458 | // Next best case: set esi with 4 bytes | |
| 459 | // be 40 30 20 10 mov esi,0x10203040 | |
| 460 | if (x <= std.math.maxInt(u32)) { | |
| 461 | try self.code.resize(self.code.items.len + 5); | |
| 462 | self.code.items[self.code.items.len - 5] = 0xbe; | |
| 463 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | |
| 464 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | |
| 465 | return; | |
| 466 | } | |
| 467 | // Worst case: set rsi with 8 bytes | |
| 468 | // 48 be 80 70 60 50 40 30 20 10 movabs rsi,0x1020304050607080 | |
| 469 | ||
| 470 | try self.code.resize(self.code.items.len + 10); | |
| 471 | self.code.items[self.code.items.len - 10] = 0x48; | |
| 472 | self.code.items[self.code.items.len - 9] = 0xbe; | |
| 473 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | |
| 474 | mem.writeIntLittle(u64, imm_ptr, x); | |
| 475 | return; | |
| 476 | }, | |
| 444 | 477 | .embedded_in_code => |code_offset| { |
| 445 | 478 | // Examples: |
| 446 | 479 | // lea rsi, [rip + 0x01020304] |
| ... | ... | @@ -462,6 +495,21 @@ const Function = struct { |
| 462 | 495 | return; |
| 463 | 496 | }, |
| 464 | 497 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = register", .{}), |
| 498 | .memory => |x| { | |
| 499 | if (x <= std.math.maxInt(u32)) { | |
| 500 | // 48 8b 34 25 40 30 20 10 mov rsi,QWORD PTR ds:0x10203040 | |
| 501 | try self.code.resize(self.code.items.len + 8); | |
| 502 | self.code.items[self.code.items.len - 8] = 0x48; | |
| 503 | self.code.items[self.code.items.len - 7] = 0x8b; | |
| 504 | self.code.items[self.code.items.len - 6] = 0x34; | |
| 505 | self.code.items[self.code.items.len - 5] = 0x25; | |
| 506 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | |
| 507 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | |
| 508 | return; | |
| 509 | } else { | |
| 510 | return self.fail(src, "TODO implement genSetReg for x86_64 setting rsi to 64-bit memory", .{}); | |
| 511 | } | |
| 512 | }, | |
| 465 | 513 | }, |
| 466 | 514 | else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}), |
| 467 | 515 | }, |
| ... | ... | @@ -493,33 +541,21 @@ const Function = struct { |
| 493 | 541 | } |
| 494 | 542 | |
| 495 | 543 | fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue { |
| 544 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 545 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 496 | 546 | const allocator = self.code.allocator; |
| 497 | 547 | switch (typed_value.ty.zigTypeTag()) { |
| 498 | 548 | .Pointer => { |
| 499 | const ptr_elem_type = typed_value.ty.elemType(); | |
| 500 | switch (ptr_elem_type.zigTypeTag()) { | |
| 501 | .Array => { | |
| 502 | // TODO more checks to make sure this can be emitted as a string literal | |
| 503 | const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { | |
| 504 | error.AnalysisFail => unreachable, | |
| 505 | else => |e| return e, | |
| 506 | }; | |
| 507 | defer allocator.free(bytes); | |
| 508 | const smaller_len = std.math.cast(u32, bytes.len) catch | |
| 509 | return self.fail(src, "TODO handle a larger string constant", .{}); | |
| 510 | ||
| 511 | // Emit the string literal directly into the code; jump over it. | |
| 512 | try self.genRelativeFwdJump(src, smaller_len); | |
| 513 | const offset = self.code.items.len; | |
| 514 | try self.code.appendSlice(bytes); | |
| 515 | return MCValue{ .embedded_in_code = offset }; | |
| 516 | }, | |
| 517 | else => |t| return self.fail(src, "TODO implement emitTypedValue for pointer to '{}'", .{@tagName(t)}), | |
| 549 | if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| { | |
| 550 | const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?]; | |
| 551 | const decl = payload.decl; | |
| 552 | const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes; | |
| 553 | return MCValue{ .memory = got_addr }; | |
| 518 | 554 | } |
| 555 | return self.fail(src, "TODO codegen more kinds of const pointers", .{}); | |
| 519 | 556 | }, |
| 520 | 557 | .Int => { |
| 521 | 558 | const info = typed_value.ty.intInfo(self.target.*); |
| 522 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 523 | 559 | if (info.bits > ptr_bits or info.signed) { |
| 524 | 560 | return self.fail(src, "TODO const int bigger than ptr and signed int", .{}); |
| 525 | 561 | } |
src-self-hosted/ir.zig+136-36| ... | ... | @@ -292,6 +292,8 @@ pub const Module = struct { |
| 292 | 292 | /// TODO look into using a lightweight map/set data structure rather than a linear array. |
| 293 | 293 | dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){}, |
| 294 | 294 | |
| 295 | contents_hash: Hash, | |
| 296 | ||
| 295 | 297 | pub fn destroy(self: *Decl, allocator: *Allocator) void { |
| 296 | 298 | allocator.free(mem.spanZ(self.name)); |
| 297 | 299 | if (self.typedValueManaged()) |tvm| { |
| ... | ... | @@ -465,26 +467,42 @@ pub const Module = struct { |
| 465 | 467 | module: *text.Module, |
| 466 | 468 | }, |
| 467 | 469 | status: enum { |
| 468 | unloaded, | |
| 470 | never_loaded, | |
| 471 | unloaded_success, | |
| 469 | 472 | unloaded_parse_failure, |
| 473 | unloaded_sema_failure, | |
| 470 | 474 | loaded_parse_failure, |
| 471 | 475 | loaded_sema_failure, |
| 472 | 476 | loaded_success, |
| 473 | 477 | }, |
| 474 | 478 | |
| 475 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { | |
| 479 | pub fn unload(self: *ZIRModule, allocator: *Allocator) void { | |
| 476 | 480 | switch (self.status) { |
| 477 | .unloaded, | |
| 481 | .never_loaded, | |
| 478 | 482 | .unloaded_parse_failure, |
| 483 | .unloaded_sema_failure, | |
| 484 | .unloaded_success, | |
| 479 | 485 | => {}, |
| 480 | .loaded_success, .loaded_sema_failure => { | |
| 486 | ||
| 487 | .loaded_success => { | |
| 488 | allocator.free(self.source.bytes); | |
| 489 | self.contents.module.deinit(allocator); | |
| 490 | self.status = .unloaded_success; | |
| 491 | }, | |
| 492 | .loaded_sema_failure => { | |
| 481 | 493 | allocator.free(self.source.bytes); |
| 482 | 494 | self.contents.module.deinit(allocator); |
| 495 | self.status = .unloaded_sema_failure; | |
| 483 | 496 | }, |
| 484 | 497 | .loaded_parse_failure => { |
| 485 | 498 | allocator.free(self.source.bytes); |
| 499 | self.status = .unloaded_parse_failure; | |
| 486 | 500 | }, |
| 487 | 501 | } |
| 502 | } | |
| 503 | ||
| 504 | pub fn deinit(self: *ZIRModule, allocator: *Allocator) void { | |
| 505 | self.unload(allocator); | |
| 488 | 506 | self.* = undefined; |
| 489 | 507 | } |
| 490 | 508 | |
| ... | ... | @@ -623,7 +641,8 @@ pub const Module = struct { |
| 623 | 641 | |
| 624 | 642 | try self.performAllTheWork(); |
| 625 | 643 | |
| 626 | // TODO unload all the source files from memory | |
| 644 | // Unload all the source files from memory. | |
| 645 | self.root_scope.unload(self.allocator); | |
| 627 | 646 | |
| 628 | 647 | try self.bin_file.flush(); |
| 629 | 648 | self.link_error_flags = self.bin_file.error_flags; |
| ... | ... | @@ -722,8 +741,8 @@ pub const Module = struct { |
| 722 | 741 | .success => {}, |
| 723 | 742 | } |
| 724 | 743 | } |
| 725 | if (!decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()) | |
| 726 | continue; | |
| 744 | ||
| 745 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | |
| 727 | 746 | |
| 728 | 747 | self.bin_file.updateDecl(self, decl) catch |err| switch (err) { |
| 729 | 748 | error.OutOfMemory => return error.OutOfMemory, |
| ... | ... | @@ -748,7 +767,7 @@ pub const Module = struct { |
| 748 | 767 | |
| 749 | 768 | fn getTextModule(self: *Module, root_scope: *Scope.ZIRModule) !*text.Module { |
| 750 | 769 | switch (root_scope.status) { |
| 751 | .unloaded => { | |
| 770 | .never_loaded, .unloaded_success => { | |
| 752 | 771 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); |
| 753 | 772 | |
| 754 | 773 | var keep_source = false; |
| ... | ... | @@ -789,6 +808,7 @@ pub const Module = struct { |
| 789 | 808 | }, |
| 790 | 809 | |
| 791 | 810 | .unloaded_parse_failure, |
| 811 | .unloaded_sema_failure, | |
| 792 | 812 | .loaded_parse_failure, |
| 793 | 813 | .loaded_sema_failure, |
| 794 | 814 | => return error.AnalysisFail, |
| ... | ... | @@ -804,16 +824,62 @@ pub const Module = struct { |
| 804 | 824 | // Here we simulate adding a source file which was previously not part of the compilation, |
| 805 | 825 | // which means scanning the decls looking for exports. |
| 806 | 826 | // TODO also identify decls that need to be deleted. |
| 807 | const src_module = try self.getTextModule(root_scope); | |
| 827 | switch (root_scope.status) { | |
| 828 | .never_loaded => { | |
| 829 | const src_module = try self.getTextModule(root_scope); | |
| 808 | 830 | |
| 809 | // Here we ensure enough queue capacity to store all the decls, so that later we can use | |
| 810 | // appendAssumeCapacity. | |
| 811 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | |
| 831 | // Here we ensure enough queue capacity to store all the decls, so that later we can use | |
| 832 | // appendAssumeCapacity. | |
| 833 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | |
| 812 | 834 | |
| 813 | for (src_module.decls) |decl| { | |
| 814 | if (decl.cast(text.Inst.Export)) |export_inst| { | |
| 815 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base); | |
| 816 | } | |
| 835 | for (src_module.decls) |decl| { | |
| 836 | if (decl.cast(text.Inst.Export)) |export_inst| { | |
| 837 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty); | |
| 838 | } | |
| 839 | } | |
| 840 | }, | |
| 841 | ||
| 842 | .unloaded_parse_failure, | |
| 843 | .unloaded_sema_failure, | |
| 844 | .loaded_parse_failure, | |
| 845 | .loaded_sema_failure, | |
| 846 | .loaded_success, | |
| 847 | .unloaded_success, | |
| 848 | => { | |
| 849 | const src_module = try self.getTextModule(root_scope); | |
| 850 | ||
| 851 | // Look for changed decls. | |
| 852 | for (src_module.decls) |src_decl| { | |
| 853 | const name_hash = Decl.hashSimpleName(src_decl.name); | |
| 854 | if (self.decl_table.get(name_hash)) |kv| { | |
| 855 | const decl = kv.value; | |
| 856 | const new_contents_hash = Decl.hashSimpleName(src_decl.contents); | |
| 857 | if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) { | |
| 858 | // TODO recursive dependency management | |
| 859 | std.debug.warn("noticed that '{}' changed\n", .{src_decl.name}); | |
| 860 | self.decl_table.removeAssertDiscard(name_hash); | |
| 861 | const saved_link = decl.link; | |
| 862 | decl.destroy(self.allocator); | |
| 863 | if (self.export_owners.getValue(decl)) |exports| { | |
| 864 | @panic("TODO handle updating a decl that does an export"); | |
| 865 | } | |
| 866 | const new_decl = self.resolveDecl( | |
| 867 | &root_scope.base, | |
| 868 | src_decl, | |
| 869 | saved_link, | |
| 870 | ) catch |err| switch (err) { | |
| 871 | error.OutOfMemory => return error.OutOfMemory, | |
| 872 | error.AnalysisFail => continue, | |
| 873 | }; | |
| 874 | if (self.decl_exports.remove(decl)) |entry| { | |
| 875 | self.decl_exports.putAssumeCapacityNoClobber(new_decl, entry.value); | |
| 876 | } | |
| 877 | } | |
| 878 | } else if (src_decl.cast(text.Inst.Export)) |export_inst| { | |
| 879 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty); | |
| 880 | } | |
| 881 | } | |
| 882 | }, | |
| 817 | 883 | } |
| 818 | 884 | } |
| 819 | 885 | |
| ... | ... | @@ -846,11 +912,17 @@ pub const Module = struct { |
| 846 | 912 | }; |
| 847 | 913 | } |
| 848 | 914 | |
| 849 | fn resolveDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl { | |
| 915 | fn resolveDecl( | |
| 916 | self: *Module, | |
| 917 | scope: *Scope, | |
| 918 | old_inst: *text.Inst, | |
| 919 | bin_file_link: link.ElfFile.Decl, | |
| 920 | ) InnerError!*Decl { | |
| 850 | 921 | const hash = Decl.hashSimpleName(old_inst.name); |
| 851 | 922 | if (self.decl_table.get(hash)) |kv| { |
| 852 | 923 | return kv.value; |
| 853 | 924 | } else { |
| 925 | std.debug.warn("creating new decl for {}\n", .{old_inst.name}); | |
| 854 | 926 | const new_decl = blk: { |
| 855 | 927 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); |
| 856 | 928 | const new_decl = try self.allocator.create(Decl); |
| ... | ... | @@ -863,6 +935,8 @@ pub const Module = struct { |
| 863 | 935 | .src = old_inst.src, |
| 864 | 936 | .typed_value = .{ .never_succeeded = {} }, |
| 865 | 937 | .analysis = .initial_in_progress, |
| 938 | .contents_hash = Decl.hashSimpleName(old_inst.contents), | |
| 939 | .link = bin_file_link, | |
| 866 | 940 | }; |
| 867 | 941 | self.decl_table.putAssumeCapacityNoClobber(hash, new_decl); |
| 868 | 942 | break :blk new_decl; |
| ... | ... | @@ -887,6 +961,14 @@ pub const Module = struct { |
| 887 | 961 | }; |
| 888 | 962 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); |
| 889 | 963 | |
| 964 | const has_codegen_bits = typed_value.ty.hasCodeGenBits(); | |
| 965 | if (has_codegen_bits) { | |
| 966 | // We don't fully codegen the decl until later, but we do need to reserve a global | |
| 967 | // offset table index for it. This allows us to codegen decls out of dependency order, | |
| 968 | // increasing how many computations can be done in parallel. | |
| 969 | try self.bin_file.allocateDeclIndexes(new_decl); | |
| 970 | } | |
| 971 | ||
| 890 | 972 | arena_state.* = decl_scope.arena.state; |
| 891 | 973 | |
| 892 | 974 | new_decl.typed_value = .{ |
| ... | ... | @@ -896,14 +978,16 @@ pub const Module = struct { |
| 896 | 978 | }, |
| 897 | 979 | }; |
| 898 | 980 | new_decl.analysis = .complete; |
| 899 | // We ensureCapacity when scanning for decls. | |
| 900 | self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl }); | |
| 981 | if (has_codegen_bits) { | |
| 982 | // We ensureCapacity when scanning for decls. | |
| 983 | self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl }); | |
| 984 | } | |
| 901 | 985 | return new_decl; |
| 902 | 986 | } |
| 903 | 987 | } |
| 904 | 988 | |
| 905 | 989 | fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl { |
| 906 | const decl = try self.resolveDecl(scope, old_inst); | |
| 990 | const decl = try self.resolveDecl(scope, old_inst, link.ElfFile.Decl.empty); | |
| 907 | 991 | switch (decl.analysis) { |
| 908 | 992 | .initial_in_progress => unreachable, |
| 909 | 993 | .repeat_in_progress => unreachable, |
| ... | ... | @@ -2088,8 +2172,8 @@ pub fn main() anyerror!void { |
| 2088 | 2172 | |
| 2089 | 2173 | const src_path = args[1]; |
| 2090 | 2174 | const bin_path = args[2]; |
| 2091 | const debug_error_trace = true; | |
| 2092 | const output_zir = true; | |
| 2175 | const debug_error_trace = false; | |
| 2176 | const output_zir = false; | |
| 2093 | 2177 | const object_format: ?std.builtin.ObjectFormat = null; |
| 2094 | 2178 | |
| 2095 | 2179 | const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); |
| ... | ... | @@ -2112,7 +2196,7 @@ pub fn main() anyerror!void { |
| 2112 | 2196 | .sub_file_path = root_pkg.root_src_path, |
| 2113 | 2197 | .source = .{ .unloaded = {} }, |
| 2114 | 2198 | .contents = .{ .not_available = {} }, |
| 2115 | .status = .unloaded, | |
| 2199 | .status = .never_loaded, | |
| 2116 | 2200 | }; |
| 2117 | 2201 | |
| 2118 | 2202 | break :blk Module{ |
| ... | ... | @@ -2132,22 +2216,38 @@ pub fn main() anyerror!void { |
| 2132 | 2216 | }; |
| 2133 | 2217 | defer module.deinit(); |
| 2134 | 2218 | |
| 2135 | try module.update(); | |
| 2219 | const stdin = std.io.getStdIn().inStream(); | |
| 2220 | const stderr = std.io.getStdErr().outStream(); | |
| 2221 | var repl_buf: [1024]u8 = undefined; | |
| 2136 | 2222 | |
| 2137 | var errors = try module.getAllErrorsAlloc(); | |
| 2138 | defer errors.deinit(allocator); | |
| 2223 | while (true) { | |
| 2224 | try module.update(); | |
| 2139 | 2225 | |
| 2140 | if (errors.list.len != 0) { | |
| 2141 | for (errors.list) |full_err_msg| { | |
| 2142 | std.debug.warn("{}:{}:{}: error: {}\n", .{ | |
| 2143 | full_err_msg.src_path, | |
| 2144 | full_err_msg.line + 1, | |
| 2145 | full_err_msg.column + 1, | |
| 2146 | full_err_msg.msg, | |
| 2147 | }); | |
| 2226 | var errors = try module.getAllErrorsAlloc(); | |
| 2227 | defer errors.deinit(allocator); | |
| 2228 | ||
| 2229 | if (errors.list.len != 0) { | |
| 2230 | for (errors.list) |full_err_msg| { | |
| 2231 | std.debug.warn("{}:{}:{}: error: {}\n", .{ | |
| 2232 | full_err_msg.src_path, | |
| 2233 | full_err_msg.line + 1, | |
| 2234 | full_err_msg.column + 1, | |
| 2235 | full_err_msg.msg, | |
| 2236 | }); | |
| 2237 | } | |
| 2238 | if (debug_error_trace) return error.AnalysisFail; | |
| 2239 | } | |
| 2240 | ||
| 2241 | try stderr.print("🦎 ", .{}); | |
| 2242 | if (try stdin.readUntilDelimiterOrEof(&repl_buf, '\n')) |line| { | |
| 2243 | if (mem.eql(u8, line, "update")) { | |
| 2244 | continue; | |
| 2245 | } else { | |
| 2246 | try stderr.print("unknown command: {}\n", .{line}); | |
| 2247 | } | |
| 2248 | } else { | |
| 2249 | break; | |
| 2148 | 2250 | } |
| 2149 | if (debug_error_trace) return error.AnalysisFail; | |
| 2150 | std.process.exit(1); | |
| 2151 | 2251 | } |
| 2152 | 2252 | |
| 2153 | 2253 | if (output_zir) { |
src-self-hosted/ir/text.zig+11-1| ... | ... | @@ -19,6 +19,9 @@ pub const Inst = struct { |
| 19 | 19 | src: usize, |
| 20 | 20 | name: []const u8, |
| 21 | 21 | |
| 22 | /// Slice into the source of the part after the = and before the next instruction. | |
| 23 | contents: []const u8, | |
| 24 | ||
| 22 | 25 | /// These names are used directly as the instruction names in the text format. |
| 23 | 26 | pub const Tag = enum { |
| 24 | 27 | breakpoint, |
| ... | ... | @@ -798,11 +801,12 @@ const Parser = struct { |
| 798 | 801 | } |
| 799 | 802 | |
| 800 | 803 | fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst { |
| 804 | const contents_start = self.i; | |
| 801 | 805 | const fn_name = try skipToAndOver(self, '('); |
| 802 | 806 | inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { |
| 803 | 807 | if (mem.eql(u8, field.name, fn_name)) { |
| 804 | 808 | const tag = @field(Inst.Tag, field.name); |
| 805 | return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx, name); | |
| 809 | return parseInstructionGeneric(self, field.name, Inst.TagToType(tag), body_ctx, name, contents_start); | |
| 806 | 810 | } |
| 807 | 811 | } |
| 808 | 812 | return self.fail("unknown instruction '{}'", .{fn_name}); |
| ... | ... | @@ -814,12 +818,14 @@ const Parser = struct { |
| 814 | 818 | comptime InstType: type, |
| 815 | 819 | body_ctx: ?*Body, |
| 816 | 820 | inst_name: []const u8, |
| 821 | contents_start: usize, | |
| 817 | 822 | ) InnerError!*Inst { |
| 818 | 823 | const inst_specific = try self.arena.allocator.create(InstType); |
| 819 | 824 | inst_specific.base = .{ |
| 820 | 825 | .name = inst_name, |
| 821 | 826 | .src = self.i, |
| 822 | 827 | .tag = InstType.base_tag, |
| 828 | .contents = undefined, | |
| 823 | 829 | }; |
| 824 | 830 | |
| 825 | 831 | if (@hasField(InstType, "ty")) { |
| ... | ... | @@ -867,6 +873,8 @@ const Parser = struct { |
| 867 | 873 | } |
| 868 | 874 | try requireEatBytes(self, ")"); |
| 869 | 875 | |
| 876 | inst_specific.base.contents = self.source[contents_start..self.i]; | |
| 877 | ||
| 870 | 878 | return &inst_specific.base; |
| 871 | 879 | } |
| 872 | 880 | |
| ... | ... | @@ -952,6 +960,7 @@ const Parser = struct { |
| 952 | 960 | .name = try self.generateName(), |
| 953 | 961 | .src = src, |
| 954 | 962 | .tag = Inst.Str.base_tag, |
| 963 | .contents = undefined, | |
| 955 | 964 | }, |
| 956 | 965 | .positionals = .{ .bytes = ident }, |
| 957 | 966 | .kw_args = .{}, |
| ... | ... | @@ -962,6 +971,7 @@ const Parser = struct { |
| 962 | 971 | .name = try self.generateName(), |
| 963 | 972 | .src = src, |
| 964 | 973 | .tag = Inst.DeclRef.base_tag, |
| 974 | .contents = undefined, | |
| 965 | 975 | }, |
| 966 | 976 | .positionals = .{ .name = &name.base }, |
| 967 | 977 | .kw_args = .{}, |
src-self-hosted/link.zig+45-3| ... | ... | @@ -310,7 +310,7 @@ pub const ElfFile = struct { |
| 310 | 310 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 311 | 311 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 312 | 312 | // else in virtual memory. |
| 313 | const default_got_addr = 0x80000000; | |
| 313 | const default_got_addr = 0x4000000; | |
| 314 | 314 | try self.program_headers.append(self.allocator, .{ |
| 315 | 315 | .p_type = elf.PT_LOAD, |
| 316 | 316 | .p_offset = off, |
| ... | ... | @@ -755,6 +755,35 @@ pub const ElfFile = struct { |
| 755 | 755 | }; |
| 756 | 756 | } |
| 757 | 757 | |
| 758 | pub fn allocateDeclIndexes(self: *ElfFile, decl: *ir.Module.Decl) !void { | |
| 759 | if (decl.link.local_sym_index != 0) return; | |
| 760 | ||
| 761 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); | |
| 762 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); | |
| 763 | const local_sym_index = self.local_symbols.items.len; | |
| 764 | const offset_table_index = self.offset_table.items.len; | |
| 765 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | |
| 766 | ||
| 767 | self.local_symbols.appendAssumeCapacity(.{ | |
| 768 | .st_name = 0, | |
| 769 | .st_info = 0, | |
| 770 | .st_other = 0, | |
| 771 | .st_shndx = 0, | |
| 772 | .st_value = phdr.p_vaddr, | |
| 773 | .st_size = 0, | |
| 774 | }); | |
| 775 | errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1); | |
| 776 | self.offset_table.appendAssumeCapacity(0); | |
| 777 | errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1); | |
| 778 | ||
| 779 | self.offset_table_count_dirty = true; | |
| 780 | ||
| 781 | decl.link = .{ | |
| 782 | .local_sym_index = @intCast(u32, local_sym_index), | |
| 783 | .offset_table_index = @intCast(u32, offset_table_index), | |
| 784 | }; | |
| 785 | } | |
| 786 | ||
| 758 | 787 | pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void { |
| 759 | 788 | var code_buffer = std.ArrayList(u8).init(self.allocator); |
| 760 | 789 | defer code_buffer.deinit(); |
| ... | ... | @@ -781,21 +810,33 @@ pub const ElfFile = struct { |
| 781 | 810 | if (decl.link.local_sym_index != 0) { |
| 782 | 811 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; |
| 783 | 812 | const existing_block = self.findAllocatedTextBlock(local_sym.*); |
| 784 | const need_realloc = code.len > existing_block.size_capacity or | |
| 813 | const need_realloc = local_sym.st_size == 0 or | |
| 814 | code.len > existing_block.size_capacity or | |
| 785 | 815 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 816 | // TODO check for collision with another symbol | |
| 786 | 817 | const file_offset = if (need_realloc) fo: { |
| 787 | 818 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 788 | 819 | local_sym.st_value = new_block.vaddr; |
| 789 | local_sym.st_size = code.len; | |
| 820 | self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr; | |
| 790 | 821 | |
| 822 | //std.debug.warn("{}: writing got index {}=0x{x}\n", .{ | |
| 823 | // decl.name, | |
| 824 | // decl.link.offset_table_index, | |
| 825 | // self.offset_table.items[decl.link.offset_table_index], | |
| 826 | //}); | |
| 791 | 827 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 792 | 828 | |
| 793 | 829 | break :fo new_block.file_offset; |
| 794 | 830 | } else existing_block.file_offset; |
| 831 | local_sym.st_size = code.len; | |
| 795 | 832 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name)); |
| 796 | 833 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; |
| 834 | local_sym.st_other = 0; | |
| 835 | local_sym.st_shndx = self.text_section_index.?; | |
| 797 | 836 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 798 | 837 | try self.writeSymbol(decl.link.local_sym_index); |
| 838 | ||
| 839 | //std.debug.warn("updating {} at vaddr 0x{x}\n", .{ decl.name, local_sym.st_value }); | |
| 799 | 840 | break :blk file_offset; |
| 800 | 841 | } else { |
| 801 | 842 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| ... | ... | @@ -829,6 +870,7 @@ pub const ElfFile = struct { |
| 829 | 870 | .offset_table_index = @intCast(u32, offset_table_index), |
| 830 | 871 | }; |
| 831 | 872 | |
| 873 | //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr }); | |
| 832 | 874 | break :blk new_block.file_offset; |
| 833 | 875 | } |
| 834 | 876 | }; |