| 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,6 +33,7 @@ pub fn generateSymbol( |
| 33 | 33 | ||
| 34 | var function = Function{ | 34 | var function = Function{ |
| 35 | .target = &bin_file.options.target, | 35 | .target = &bin_file.options.target, |
| 36 | .bin_file = bin_file, | ||
| 36 | .mod_fn = module_fn, | 37 | .mod_fn = module_fn, |
| 37 | .code = code, | 38 | .code = code, |
| 38 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator), | 39 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(bin_file.allocator), |
| ... | @@ -144,6 +145,7 @@ pub fn generateSymbol( | ... | @@ -144,6 +145,7 @@ pub fn generateSymbol( |
| 144 | } | 145 | } |
| 145 | 146 | ||
| 146 | const Function = struct { | 147 | const Function = struct { |
| 148 | bin_file: *link.ElfFile, | ||
| 147 | target: *const std.Target, | 149 | target: *const std.Target, |
| 148 | mod_fn: *const ir.Module.Fn, | 150 | mod_fn: *const ir.Module.Fn, |
| 149 | code: *std.ArrayList(u8), | 151 | code: *std.ArrayList(u8), |
| ... | @@ -160,6 +162,8 @@ const Function = struct { | ... | @@ -160,6 +162,8 @@ const Function = struct { |
| 160 | /// The value is in a target-specific register. The value can | 162 | /// The value is in a target-specific register. The value can |
| 161 | /// be @intToEnum casted to the respective Reg enum. | 163 | /// be @intToEnum casted to the respective Reg enum. |
| 162 | register: usize, | 164 | register: usize, |
| 165 | /// The value is in memory at a hard-coded address. | ||
| 166 | memory: u64, | ||
| 163 | }; | 167 | }; |
| 164 | 168 | ||
| 165 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { | 169 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { |
| ... | @@ -375,6 +379,7 @@ const Function = struct { | ... | @@ -375,6 +379,7 @@ const Function = struct { |
| 375 | }, | 379 | }, |
| 376 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rax = embedded_in_code", .{}), | 380 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rax = embedded_in_code", .{}), |
| 377 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rax = register", .{}), | 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 | .rdx => switch (mcv) { | 384 | .rdx => switch (mcv) { |
| 380 | .none, .unreach => unreachable, | 385 | .none, .unreach => unreachable, |
| ... | @@ -406,6 +411,7 @@ const Function = struct { | ... | @@ -406,6 +411,7 @@ const Function = struct { |
| 406 | }, | 411 | }, |
| 407 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = embedded_in_code", .{}), | 412 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = embedded_in_code", .{}), |
| 408 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdx = register", .{}), | 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 | .rdi => switch (mcv) { | 416 | .rdi => switch (mcv) { |
| 411 | .none, .unreach => unreachable, | 417 | .none, .unreach => unreachable, |
| ... | @@ -437,10 +443,37 @@ const Function = struct { | ... | @@ -437,10 +443,37 @@ const Function = struct { |
| 437 | }, | 443 | }, |
| 438 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = embedded_in_code", .{}), | 444 | .embedded_in_code => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = embedded_in_code", .{}), |
| 439 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rdi = register", .{}), | 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 | .rsi => switch (mcv) { | 448 | .rsi => switch (mcv) { |
| 442 | .none, .unreach => unreachable, | 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 | .embedded_in_code => |code_offset| { | 477 | .embedded_in_code => |code_offset| { |
| 445 | // Examples: | 478 | // Examples: |
| 446 | // lea rsi, [rip + 0x01020304] | 479 | // lea rsi, [rip + 0x01020304] |
| ... | @@ -462,6 +495,21 @@ const Function = struct { | ... | @@ -462,6 +495,21 @@ const Function = struct { |
| 462 | return; | 495 | return; |
| 463 | }, | 496 | }, |
| 464 | .register => return self.fail(src, "TODO implement x86_64 genSetReg %rsi = register", .{}), | 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 | else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}), | 514 | else => return self.fail(src, "TODO implement genSetReg for x86_64 '{}'", .{@tagName(reg)}), |
| 467 | }, | 515 | }, |
| ... | @@ -493,33 +541,21 @@ const Function = struct { | ... | @@ -493,33 +541,21 @@ const Function = struct { |
| 493 | } | 541 | } |
| 494 | 542 | ||
| 495 | fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue { | 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 | const allocator = self.code.allocator; | 546 | const allocator = self.code.allocator; |
| 497 | switch (typed_value.ty.zigTypeTag()) { | 547 | switch (typed_value.ty.zigTypeTag()) { |
| 498 | .Pointer => { | 548 | .Pointer => { |
| 499 | const ptr_elem_type = typed_value.ty.elemType(); | 549 | if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| { |
| 500 | switch (ptr_elem_type.zigTypeTag()) { | 550 | const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?]; |
| 501 | .Array => { | 551 | const decl = payload.decl; |
| 502 | // TODO more checks to make sure this can be emitted as a string literal | 552 | const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes; |
| 503 | const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { | 553 | return MCValue{ .memory = got_addr }; |
| 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)}), | ||
| 518 | } | 554 | } |
| 555 | return self.fail(src, "TODO codegen more kinds of const pointers", .{}); | ||
| 519 | }, | 556 | }, |
| 520 | .Int => { | 557 | .Int => { |
| 521 | const info = typed_value.ty.intInfo(self.target.*); | 558 | const info = typed_value.ty.intInfo(self.target.*); |
| 522 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | ||
| 523 | if (info.bits > ptr_bits or info.signed) { | 559 | if (info.bits > ptr_bits or info.signed) { |
| 524 | return self.fail(src, "TODO const int bigger than ptr and signed int", .{}); | 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,6 +292,8 @@ pub const Module = struct { |
| 292 | /// TODO look into using a lightweight map/set data structure rather than a linear array. | 292 | /// TODO look into using a lightweight map/set data structure rather than a linear array. |
| 293 | dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){}, | 293 | dependants: ArrayListUnmanaged(*Decl) = ArrayListUnmanaged(*Decl){}, |
| 294 | 294 | ||
| 295 | contents_hash: Hash, | ||
| 296 | |||
| 295 | pub fn destroy(self: *Decl, allocator: *Allocator) void { | 297 | pub fn destroy(self: *Decl, allocator: *Allocator) void { |
| 296 | allocator.free(mem.spanZ(self.name)); | 298 | allocator.free(mem.spanZ(self.name)); |
| 297 | if (self.typedValueManaged()) |tvm| { | 299 | if (self.typedValueManaged()) |tvm| { |
| ... | @@ -465,26 +467,42 @@ pub const Module = struct { | ... | @@ -465,26 +467,42 @@ pub const Module = struct { |
| 465 | module: *text.Module, | 467 | module: *text.Module, |
| 466 | }, | 468 | }, |
| 467 | status: enum { | 469 | status: enum { |
| 468 | unloaded, | 470 | never_loaded, |
| 471 | unloaded_success, | ||
| 469 | unloaded_parse_failure, | 472 | unloaded_parse_failure, |
| 473 | unloaded_sema_failure, | ||
| 470 | loaded_parse_failure, | 474 | loaded_parse_failure, |
| 471 | loaded_sema_failure, | 475 | loaded_sema_failure, |
| 472 | loaded_success, | 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 | switch (self.status) { | 480 | switch (self.status) { |
| 477 | .unloaded, | 481 | .never_loaded, |
| 478 | .unloaded_parse_failure, | 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 | allocator.free(self.source.bytes); | 493 | allocator.free(self.source.bytes); |
| 482 | self.contents.module.deinit(allocator); | 494 | self.contents.module.deinit(allocator); |
| 495 | self.status = .unloaded_sema_failure; | ||
| 483 | }, | 496 | }, |
| 484 | .loaded_parse_failure => { | 497 | .loaded_parse_failure => { |
| 485 | allocator.free(self.source.bytes); | 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 | self.* = undefined; | 506 | self.* = undefined; |
| 489 | } | 507 | } |
| 490 | 508 | ||
| ... | @@ -623,7 +641,8 @@ pub const Module = struct { | ... | @@ -623,7 +641,8 @@ pub const Module = struct { |
| 623 | 641 | ||
| 624 | try self.performAllTheWork(); | 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 | try self.bin_file.flush(); | 647 | try self.bin_file.flush(); |
| 629 | self.link_error_flags = self.bin_file.error_flags; | 648 | self.link_error_flags = self.bin_file.error_flags; |
| ... | @@ -722,8 +741,8 @@ pub const Module = struct { | ... | @@ -722,8 +741,8 @@ pub const Module = struct { |
| 722 | .success => {}, | 741 | .success => {}, |
| 723 | } | 742 | } |
| 724 | } | 743 | } |
| 725 | if (!decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()) | 744 | |
| 726 | continue; | 745 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); |
| 727 | 746 | ||
| 728 | self.bin_file.updateDecl(self, decl) catch |err| switch (err) { | 747 | self.bin_file.updateDecl(self, decl) catch |err| switch (err) { |
| 729 | error.OutOfMemory => return error.OutOfMemory, | 748 | error.OutOfMemory => return error.OutOfMemory, |
| ... | @@ -748,7 +767,7 @@ pub const Module = struct { | ... | @@ -748,7 +767,7 @@ pub const Module = struct { |
| 748 | 767 | ||
| 749 | fn getTextModule(self: *Module, root_scope: *Scope.ZIRModule) !*text.Module { | 768 | fn getTextModule(self: *Module, root_scope: *Scope.ZIRModule) !*text.Module { |
| 750 | switch (root_scope.status) { | 769 | switch (root_scope.status) { |
| 751 | .unloaded => { | 770 | .never_loaded, .unloaded_success => { |
| 752 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | 771 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); |
| 753 | 772 | ||
| 754 | var keep_source = false; | 773 | var keep_source = false; |
| ... | @@ -789,6 +808,7 @@ pub const Module = struct { | ... | @@ -789,6 +808,7 @@ pub const Module = struct { |
| 789 | }, | 808 | }, |
| 790 | 809 | ||
| 791 | .unloaded_parse_failure, | 810 | .unloaded_parse_failure, |
| 811 | .unloaded_sema_failure, | ||
| 792 | .loaded_parse_failure, | 812 | .loaded_parse_failure, |
| 793 | .loaded_sema_failure, | 813 | .loaded_sema_failure, |
| 794 | => return error.AnalysisFail, | 814 | => return error.AnalysisFail, |
| ... | @@ -804,16 +824,62 @@ pub const Module = struct { | ... | @@ -804,16 +824,62 @@ pub const Module = struct { |
| 804 | // Here we simulate adding a source file which was previously not part of the compilation, | 824 | // Here we simulate adding a source file which was previously not part of the compilation, |
| 805 | // which means scanning the decls looking for exports. | 825 | // which means scanning the decls looking for exports. |
| 806 | // TODO also identify decls that need to be deleted. | 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 | 831 | // Here we ensure enough queue capacity to store all the decls, so that later we can use |
| 810 | // appendAssumeCapacity. | 832 | // appendAssumeCapacity. |
| 811 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); | 833 | try self.work_queue.ensureUnusedCapacity(src_module.decls.len); |
| 812 | 834 | ||
| 813 | for (src_module.decls) |decl| { | 835 | for (src_module.decls) |decl| { |
| 814 | if (decl.cast(text.Inst.Export)) |export_inst| { | 836 | if (decl.cast(text.Inst.Export)) |export_inst| { |
| 815 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base); | 837 | _ = try self.resolveDecl(&root_scope.base, &export_inst.base, link.ElfFile.Decl.empty); |
| 816 | } | 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,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 | const hash = Decl.hashSimpleName(old_inst.name); | 921 | const hash = Decl.hashSimpleName(old_inst.name); |
| 851 | if (self.decl_table.get(hash)) |kv| { | 922 | if (self.decl_table.get(hash)) |kv| { |
| 852 | return kv.value; | 923 | return kv.value; |
| 853 | } else { | 924 | } else { |
| 925 | std.debug.warn("creating new decl for {}\n", .{old_inst.name}); | ||
| 854 | const new_decl = blk: { | 926 | const new_decl = blk: { |
| 855 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); | 927 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); |
| 856 | const new_decl = try self.allocator.create(Decl); | 928 | const new_decl = try self.allocator.create(Decl); |
| ... | @@ -863,6 +935,8 @@ pub const Module = struct { | ... | @@ -863,6 +935,8 @@ pub const Module = struct { |
| 863 | .src = old_inst.src, | 935 | .src = old_inst.src, |
| 864 | .typed_value = .{ .never_succeeded = {} }, | 936 | .typed_value = .{ .never_succeeded = {} }, |
| 865 | .analysis = .initial_in_progress, | 937 | .analysis = .initial_in_progress, |
| 938 | .contents_hash = Decl.hashSimpleName(old_inst.contents), | ||
| 939 | .link = bin_file_link, | ||
| 866 | }; | 940 | }; |
| 867 | self.decl_table.putAssumeCapacityNoClobber(hash, new_decl); | 941 | self.decl_table.putAssumeCapacityNoClobber(hash, new_decl); |
| 868 | break :blk new_decl; | 942 | break :blk new_decl; |
| ... | @@ -887,6 +961,14 @@ pub const Module = struct { | ... | @@ -887,6 +961,14 @@ pub const Module = struct { |
| 887 | }; | 961 | }; |
| 888 | const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State); | 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 | arena_state.* = decl_scope.arena.state; | 972 | arena_state.* = decl_scope.arena.state; |
| 891 | 973 | ||
| 892 | new_decl.typed_value = .{ | 974 | new_decl.typed_value = .{ |
| ... | @@ -896,14 +978,16 @@ pub const Module = struct { | ... | @@ -896,14 +978,16 @@ pub const Module = struct { |
| 896 | }, | 978 | }, |
| 897 | }; | 979 | }; |
| 898 | new_decl.analysis = .complete; | 980 | new_decl.analysis = .complete; |
| 899 | // We ensureCapacity when scanning for decls. | 981 | if (has_codegen_bits) { |
| 900 | self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl }); | 982 | // We ensureCapacity when scanning for decls. |
| 983 | self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl }); | ||
| 984 | } | ||
| 901 | return new_decl; | 985 | return new_decl; |
| 902 | } | 986 | } |
| 903 | } | 987 | } |
| 904 | 988 | ||
| 905 | fn resolveCompleteDecl(self: *Module, scope: *Scope, old_inst: *text.Inst) InnerError!*Decl { | 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 | switch (decl.analysis) { | 991 | switch (decl.analysis) { |
| 908 | .initial_in_progress => unreachable, | 992 | .initial_in_progress => unreachable, |
| 909 | .repeat_in_progress => unreachable, | 993 | .repeat_in_progress => unreachable, |
| ... | @@ -2088,8 +2172,8 @@ pub fn main() anyerror!void { | ... | @@ -2088,8 +2172,8 @@ pub fn main() anyerror!void { |
| 2088 | 2172 | ||
| 2089 | const src_path = args[1]; | 2173 | const src_path = args[1]; |
| 2090 | const bin_path = args[2]; | 2174 | const bin_path = args[2]; |
| 2091 | const debug_error_trace = true; | 2175 | const debug_error_trace = false; |
| 2092 | const output_zir = true; | 2176 | const output_zir = false; |
| 2093 | const object_format: ?std.builtin.ObjectFormat = null; | 2177 | const object_format: ?std.builtin.ObjectFormat = null; |
| 2094 | 2178 | ||
| 2095 | const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); | 2179 | const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); |
| ... | @@ -2112,7 +2196,7 @@ pub fn main() anyerror!void { | ... | @@ -2112,7 +2196,7 @@ pub fn main() anyerror!void { |
| 2112 | .sub_file_path = root_pkg.root_src_path, | 2196 | .sub_file_path = root_pkg.root_src_path, |
| 2113 | .source = .{ .unloaded = {} }, | 2197 | .source = .{ .unloaded = {} }, |
| 2114 | .contents = .{ .not_available = {} }, | 2198 | .contents = .{ .not_available = {} }, |
| 2115 | .status = .unloaded, | 2199 | .status = .never_loaded, |
| 2116 | }; | 2200 | }; |
| 2117 | 2201 | ||
| 2118 | break :blk Module{ | 2202 | break :blk Module{ |
| ... | @@ -2132,22 +2216,38 @@ pub fn main() anyerror!void { | ... | @@ -2132,22 +2216,38 @@ pub fn main() anyerror!void { |
| 2132 | }; | 2216 | }; |
| 2133 | defer module.deinit(); | 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(); | 2223 | while (true) { |
| 2138 | defer errors.deinit(allocator); | 2224 | try module.update(); |
| 2139 | 2225 | ||
| 2140 | if (errors.list.len != 0) { | 2226 | var errors = try module.getAllErrorsAlloc(); |
| 2141 | for (errors.list) |full_err_msg| { | 2227 | defer errors.deinit(allocator); |
| 2142 | std.debug.warn("{}:{}:{}: error: {}\n", .{ | 2228 | |
| 2143 | full_err_msg.src_path, | 2229 | if (errors.list.len != 0) { |
| 2144 | full_err_msg.line + 1, | 2230 | for (errors.list) |full_err_msg| { |
| 2145 | full_err_msg.column + 1, | 2231 | std.debug.warn("{}:{}:{}: error: {}\n", .{ |
| 2146 | full_err_msg.msg, | 2232 | full_err_msg.src_path, |
| 2147 | }); | 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 | if (output_zir) { | 2253 | if (output_zir) { |
src-self-hosted/ir/text.zig+11-1| ... | @@ -19,6 +19,9 @@ pub const Inst = struct { | ... | @@ -19,6 +19,9 @@ pub const Inst = struct { |
| 19 | src: usize, | 19 | src: usize, |
| 20 | name: []const u8, | 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 | /// These names are used directly as the instruction names in the text format. | 25 | /// These names are used directly as the instruction names in the text format. |
| 23 | pub const Tag = enum { | 26 | pub const Tag = enum { |
| 24 | breakpoint, | 27 | breakpoint, |
| ... | @@ -798,11 +801,12 @@ const Parser = struct { | ... | @@ -798,11 +801,12 @@ const Parser = struct { |
| 798 | } | 801 | } |
| 799 | 802 | ||
| 800 | fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst { | 803 | fn parseInstruction(self: *Parser, body_ctx: ?*Body, name: []const u8) InnerError!*Inst { |
| 804 | const contents_start = self.i; | ||
| 801 | const fn_name = try skipToAndOver(self, '('); | 805 | const fn_name = try skipToAndOver(self, '('); |
| 802 | inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { | 806 | inline for (@typeInfo(Inst.Tag).Enum.fields) |field| { |
| 803 | if (mem.eql(u8, field.name, fn_name)) { | 807 | if (mem.eql(u8, field.name, fn_name)) { |
| 804 | const tag = @field(Inst.Tag, field.name); | 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 | return self.fail("unknown instruction '{}'", .{fn_name}); | 812 | return self.fail("unknown instruction '{}'", .{fn_name}); |
| ... | @@ -814,12 +818,14 @@ const Parser = struct { | ... | @@ -814,12 +818,14 @@ const Parser = struct { |
| 814 | comptime InstType: type, | 818 | comptime InstType: type, |
| 815 | body_ctx: ?*Body, | 819 | body_ctx: ?*Body, |
| 816 | inst_name: []const u8, | 820 | inst_name: []const u8, |
| 821 | contents_start: usize, | ||
| 817 | ) InnerError!*Inst { | 822 | ) InnerError!*Inst { |
| 818 | const inst_specific = try self.arena.allocator.create(InstType); | 823 | const inst_specific = try self.arena.allocator.create(InstType); |
| 819 | inst_specific.base = .{ | 824 | inst_specific.base = .{ |
| 820 | .name = inst_name, | 825 | .name = inst_name, |
| 821 | .src = self.i, | 826 | .src = self.i, |
| 822 | .tag = InstType.base_tag, | 827 | .tag = InstType.base_tag, |
| 828 | .contents = undefined, | ||
| 823 | }; | 829 | }; |
| 824 | 830 | ||
| 825 | if (@hasField(InstType, "ty")) { | 831 | if (@hasField(InstType, "ty")) { |
| ... | @@ -867,6 +873,8 @@ const Parser = struct { | ... | @@ -867,6 +873,8 @@ const Parser = struct { |
| 867 | } | 873 | } |
| 868 | try requireEatBytes(self, ")"); | 874 | try requireEatBytes(self, ")"); |
| 869 | 875 | ||
| 876 | inst_specific.base.contents = self.source[contents_start..self.i]; | ||
| 877 | |||
| 870 | return &inst_specific.base; | 878 | return &inst_specific.base; |
| 871 | } | 879 | } |
| 872 | 880 | ||
| ... | @@ -952,6 +960,7 @@ const Parser = struct { | ... | @@ -952,6 +960,7 @@ const Parser = struct { |
| 952 | .name = try self.generateName(), | 960 | .name = try self.generateName(), |
| 953 | .src = src, | 961 | .src = src, |
| 954 | .tag = Inst.Str.base_tag, | 962 | .tag = Inst.Str.base_tag, |
| 963 | .contents = undefined, | ||
| 955 | }, | 964 | }, |
| 956 | .positionals = .{ .bytes = ident }, | 965 | .positionals = .{ .bytes = ident }, |
| 957 | .kw_args = .{}, | 966 | .kw_args = .{}, |
| ... | @@ -962,6 +971,7 @@ const Parser = struct { | ... | @@ -962,6 +971,7 @@ const Parser = struct { |
| 962 | .name = try self.generateName(), | 971 | .name = try self.generateName(), |
| 963 | .src = src, | 972 | .src = src, |
| 964 | .tag = Inst.DeclRef.base_tag, | 973 | .tag = Inst.DeclRef.base_tag, |
| 974 | .contents = undefined, | ||
| 965 | }, | 975 | }, |
| 966 | .positionals = .{ .name = &name.base }, | 976 | .positionals = .{ .name = &name.base }, |
| 967 | .kw_args = .{}, | 977 | .kw_args = .{}, |
src-self-hosted/link.zig+45-3| ... | @@ -310,7 +310,7 @@ pub const ElfFile = struct { | ... | @@ -310,7 +310,7 @@ pub const ElfFile = struct { |
| 310 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. | 310 | // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at. |
| 311 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something | 311 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 312 | // else in virtual memory. | 312 | // else in virtual memory. |
| 313 | const default_got_addr = 0x80000000; | 313 | const default_got_addr = 0x4000000; |
| 314 | try self.program_headers.append(self.allocator, .{ | 314 | try self.program_headers.append(self.allocator, .{ |
| 315 | .p_type = elf.PT_LOAD, | 315 | .p_type = elf.PT_LOAD, |
| 316 | .p_offset = off, | 316 | .p_offset = off, |
| ... | @@ -755,6 +755,35 @@ pub const ElfFile = struct { | ... | @@ -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 | pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void { | 787 | pub fn updateDecl(self: *ElfFile, module: *ir.Module, decl: *ir.Module.Decl) !void { |
| 759 | var code_buffer = std.ArrayList(u8).init(self.allocator); | 788 | var code_buffer = std.ArrayList(u8).init(self.allocator); |
| 760 | defer code_buffer.deinit(); | 789 | defer code_buffer.deinit(); |
| ... | @@ -781,21 +810,33 @@ pub const ElfFile = struct { | ... | @@ -781,21 +810,33 @@ pub const ElfFile = struct { |
| 781 | if (decl.link.local_sym_index != 0) { | 810 | if (decl.link.local_sym_index != 0) { |
| 782 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; | 811 | const local_sym = &self.local_symbols.items[decl.link.local_sym_index]; |
| 783 | const existing_block = self.findAllocatedTextBlock(local_sym.*); | 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 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); | 815 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 816 | // TODO check for collision with another symbol | ||
| 786 | const file_offset = if (need_realloc) fo: { | 817 | const file_offset = if (need_realloc) fo: { |
| 787 | const new_block = try self.allocateTextBlock(code.len, required_alignment); | 818 | const new_block = try self.allocateTextBlock(code.len, required_alignment); |
| 788 | local_sym.st_value = new_block.vaddr; | 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 | try self.writeOffsetTableEntry(decl.link.offset_table_index); | 827 | try self.writeOffsetTableEntry(decl.link.offset_table_index); |
| 792 | 828 | ||
| 793 | break :fo new_block.file_offset; | 829 | break :fo new_block.file_offset; |
| 794 | } else existing_block.file_offset; | 830 | } else existing_block.file_offset; |
| 831 | local_sym.st_size = code.len; | ||
| 795 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name)); | 832 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name)); |
| 796 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | 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 | // TODO this write could be avoided if no fields of the symbol were changed. | 836 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 798 | try self.writeSymbol(decl.link.local_sym_index); | 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 | break :blk file_offset; | 840 | break :blk file_offset; |
| 800 | } else { | 841 | } else { |
| 801 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); | 842 | try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1); |
| ... | @@ -829,6 +870,7 @@ pub const ElfFile = struct { | ... | @@ -829,6 +870,7 @@ pub const ElfFile = struct { |
| 829 | .offset_table_index = @intCast(u32, offset_table_index), | 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 | break :blk new_block.file_offset; | 874 | break :blk new_block.file_offset; |
| 833 | } | 875 | } |
| 834 | }; | 876 | }; |