| author | |
| committer | |
| log | 6a2425c38c2a776d2aafd68b25da8c4c1164f614 |
| tree | 418f27f237b06c20907434b90d99350a6f6a9787 |
| parent | 080022f6c670b0f74c39fe01096ebdbaafeda1b2 |
3 files changed, 57 insertions(+), 50 deletions(-)
lib/std/fs/file.zig+7-4| ... | ... | @@ -527,19 +527,21 @@ pub const File = struct { |
| 527 | 527 | } |
| 528 | 528 | } |
| 529 | 529 | |
| 530 | pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) PWriteError!usize { | |
| 530 | pub const CopyRangeError = PWriteError || PReadError; | |
| 531 | ||
| 532 | pub fn copyRange(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize { | |
| 531 | 533 | // TODO take advantage of copy_file_range OS APIs |
| 532 | 534 | var buf: [8 * 4096]u8 = undefined; |
| 533 | 535 | const adjusted_count = math.min(buf.len, len); |
| 534 | 536 | const amt_read = try in.pread(buf[0..adjusted_count], in_offset); |
| 535 | if (amt_read == 0) return 0; | |
| 537 | if (amt_read == 0) return @as(usize, 0); | |
| 536 | 538 | return out.pwrite(buf[0..amt_read], out_offset); |
| 537 | 539 | } |
| 538 | 540 | |
| 539 | 541 | /// Returns the number of bytes copied. If the number read is smaller than `buffer.len`, it |
| 540 | 542 | /// means the in file reached the end. Reaching the end of a file is not an error condition. |
| 541 | pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) PWriteError!usize { | |
| 542 | var total_bytes_copied = 0; | |
| 543 | pub fn copyRangeAll(in: File, in_offset: u64, out: File, out_offset: u64, len: usize) CopyRangeError!usize { | |
| 544 | var total_bytes_copied: usize = 0; | |
| 543 | 545 | var in_off = in_offset; |
| 544 | 546 | var out_off = out_offset; |
| 545 | 547 | while (total_bytes_copied < len) { |
| ... | ... | @@ -549,6 +551,7 @@ pub const File = struct { |
| 549 | 551 | in_off += amt_copied; |
| 550 | 552 | out_off += amt_copied; |
| 551 | 553 | } |
| 554 | return total_bytes_copied; | |
| 552 | 555 | } |
| 553 | 556 | |
| 554 | 557 | pub const WriteFileOptions = struct { |
src-self-hosted/codegen.zig+24-23| ... | ... | @@ -5,16 +5,17 @@ const ir = @import("ir.zig"); |
| 5 | 5 | const Type = @import("type.zig").Type; |
| 6 | 6 | const Value = @import("value.zig").Value; |
| 7 | 7 | const TypedValue = @import("TypedValue.zig"); |
| 8 | const link = @import("link.zig"); | |
| 8 | 9 | const Target = std.Target; |
| 9 | 10 | const Allocator = mem.Allocator; |
| 10 | 11 | |
| 11 | pub fn generateSymbol(typed_value: TypedValue, module: ir.Module, code: *std.ArrayList(u8)) !?*ir.ErrorMsg { | |
| 12 | pub fn generateSymbol(bin_file: *link.ElfFile, typed_value: TypedValue, code: *std.ArrayList(u8)) !?*ir.ErrorMsg { | |
| 12 | 13 | switch (typed_value.ty.zigTypeTag()) { |
| 13 | 14 | .Fn => { |
| 14 | 15 | const module_fn = typed_value.val.cast(Value.Payload.Function).?.func; |
| 15 | 16 | |
| 16 | 17 | var function = Function{ |
| 17 | .module = &module, | |
| 18 | .target = &bin_file.options.target, | |
| 18 | 19 | .mod_fn = module_fn, |
| 19 | 20 | .code = code, |
| 20 | 21 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), |
| ... | ... | @@ -22,7 +23,7 @@ pub fn generateSymbol(typed_value: TypedValue, module: ir.Module, code: *std.Arr |
| 22 | 23 | }; |
| 23 | 24 | defer function.inst_table.deinit(); |
| 24 | 25 | |
| 25 | for (module_fn.body.instructions) |inst| { | |
| 26 | for (module_fn.analysis.success.instructions) |inst| { | |
| 26 | 27 | const new_inst = function.genFuncInst(inst) catch |err| switch (err) { |
| 27 | 28 | error.CodegenFail => { |
| 28 | 29 | assert(function.err_msg != null); |
| ... | ... | @@ -40,7 +41,7 @@ pub fn generateSymbol(typed_value: TypedValue, module: ir.Module, code: *std.Arr |
| 40 | 41 | } |
| 41 | 42 | |
| 42 | 43 | const Function = struct { |
| 43 | module: *const ir.Module, | |
| 44 | target: *const std.Target, | |
| 44 | 45 | mod_fn: *const ir.Module.Fn, |
| 45 | 46 | code: *std.ArrayList(u8), |
| 46 | 47 | inst_table: std.AutoHashMap(*ir.Inst, MCValue), |
| ... | ... | @@ -76,60 +77,60 @@ const Function = struct { |
| 76 | 77 | } |
| 77 | 78 | |
| 78 | 79 | fn genBreakpoint(self: *Function, src: usize) !MCValue { |
| 79 | switch (self.module.target.cpu.arch) { | |
| 80 | switch (self.target.cpu.arch) { | |
| 80 | 81 | .i386, .x86_64 => { |
| 81 | 82 | try self.code.append(0xcc); // int3 |
| 82 | 83 | }, |
| 83 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}), | |
| 84 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}), | |
| 84 | 85 | } |
| 85 | 86 | return .unreach; |
| 86 | 87 | } |
| 87 | 88 | |
| 88 | 89 | fn genCall(self: *Function, inst: *ir.Inst.Call) !MCValue { |
| 89 | switch (self.module.target.cpu.arch) { | |
| 90 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.module.target.cpu.arch}), | |
| 90 | switch (self.target.cpu.arch) { | |
| 91 | else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}), | |
| 91 | 92 | } |
| 92 | 93 | return .unreach; |
| 93 | 94 | } |
| 94 | 95 | |
| 95 | 96 | fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { |
| 96 | switch (self.module.target.cpu.arch) { | |
| 97 | switch (self.target.cpu.arch) { | |
| 97 | 98 | .i386, .x86_64 => { |
| 98 | 99 | try self.code.append(0xc3); // ret |
| 99 | 100 | }, |
| 100 | else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}), | |
| 101 | else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}), | |
| 101 | 102 | } |
| 102 | 103 | return .unreach; |
| 103 | 104 | } |
| 104 | 105 | |
| 105 | 106 | fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { |
| 106 | switch (self.module.target.cpu.arch) { | |
| 107 | else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}), | |
| 107 | switch (self.target.cpu.arch) { | |
| 108 | else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}), | |
| 108 | 109 | } |
| 109 | 110 | } |
| 110 | 111 | |
| 111 | 112 | fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue { |
| 112 | switch (self.module.target.cpu.arch) { | |
| 113 | else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}), | |
| 113 | switch (self.target.cpu.arch) { | |
| 114 | else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}), | |
| 114 | 115 | } |
| 115 | 116 | } |
| 116 | 117 | |
| 117 | 118 | fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue { |
| 118 | switch (self.module.target.cpu.arch) { | |
| 119 | else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}), | |
| 119 | switch (self.target.cpu.arch) { | |
| 120 | else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}), | |
| 120 | 121 | } |
| 121 | 122 | } |
| 122 | 123 | |
| 123 | 124 | fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue { |
| 124 | 125 | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 125 | 126 | // will call genIsNull and invert the result. |
| 126 | switch (self.module.target.cpu.arch) { | |
| 127 | switch (self.target.cpu.arch) { | |
| 127 | 128 | else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}), |
| 128 | 129 | } |
| 129 | 130 | } |
| 130 | 131 | |
| 131 | 132 | fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void { |
| 132 | switch (self.module.target.cpu.arch) { | |
| 133 | switch (self.target.cpu.arch) { | |
| 133 | 134 | .i386, .x86_64 => { |
| 134 | 135 | // TODO x86 treats the operands as signed |
| 135 | 136 | if (amount <= std.math.maxInt(u8)) { |
| ... | ... | @@ -143,13 +144,13 @@ const Function = struct { |
| 143 | 144 | mem.writeIntLittle(u32, imm_ptr, amount); |
| 144 | 145 | } |
| 145 | 146 | }, |
| 146 | else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.module.target.cpu.arch}), | |
| 147 | else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.target.cpu.arch}), | |
| 147 | 148 | } |
| 148 | 149 | } |
| 149 | 150 | |
| 150 | 151 | fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue { |
| 151 | 152 | // TODO convert to inline function |
| 152 | switch (self.module.target.cpu.arch) { | |
| 153 | switch (self.target.cpu.arch) { | |
| 153 | 154 | .arm => return self.genAsmArch(.arm, inst), |
| 154 | 155 | .armeb => return self.genAsmArch(.armeb, inst), |
| 155 | 156 | .aarch64 => return self.genAsmArch(.aarch64, inst), |
| ... | ... | @@ -388,7 +389,7 @@ const Function = struct { |
| 388 | 389 | } |
| 389 | 390 | } |
| 390 | 391 | |
| 391 | fn genTypedValue(self: *Function, src: usize, typed_value: ir.TypedValue) !MCValue { | |
| 392 | fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue { | |
| 392 | 393 | switch (typed_value.ty.zigTypeTag()) { |
| 393 | 394 | .Pointer => { |
| 394 | 395 | const ptr_elem_type = typed_value.ty.elemType(); |
| ... | ... | @@ -410,8 +411,8 @@ const Function = struct { |
| 410 | 411 | } |
| 411 | 412 | }, |
| 412 | 413 | .Int => { |
| 413 | const info = typed_value.ty.intInfo(self.module.target); | |
| 414 | const ptr_bits = self.module.target.cpu.arch.ptrBitWidth(); | |
| 414 | const info = typed_value.ty.intInfo(self.target.*); | |
| 415 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 415 | 416 | if (info.bits > ptr_bits or info.signed) { |
| 416 | 417 | return self.fail(src, "TODO const int bigger than ptr and signed int", .{}); |
| 417 | 418 | } |
src-self-hosted/link.zig+26-23| ... | ... | @@ -685,23 +685,20 @@ pub const ElfFile = struct { |
| 685 | 685 | // TODO Also detect virtual address collisions. |
| 686 | 686 | const text_capacity = self.allocatedSize(shdr.sh_offset); |
| 687 | 687 | // TODO instead of looping here, maintain a free list and a pointer to the end. |
| 688 | const end_vaddr = blk: { | |
| 689 | var start: u64 = 0; | |
| 690 | var size: u64 = 0; | |
| 691 | for (self.symbols.items) |sym| { | |
| 692 | if (sym.st_value > start) { | |
| 693 | start = sm.st_value; | |
| 694 | size = sym.st_size; | |
| 695 | } | |
| 688 | var last_start: u64 = 0; | |
| 689 | var last_size: u64 = 0; | |
| 690 | for (self.symbols.items) |sym| { | |
| 691 | if (sym.st_value > last_start) { | |
| 692 | last_start = sym.st_value; | |
| 693 | last_size = sym.st_size; | |
| 696 | 694 | } |
| 697 | break :blk start + (size * alloc_num / alloc_den); | |
| 698 | }; | |
| 699 | ||
| 700 | const text_size = end_vaddr - phdr.p_vaddr; | |
| 701 | const needed_size = text_size + new_block_size; | |
| 695 | } | |
| 696 | const end_vaddr = last_start + (last_size * alloc_num / alloc_den); | |
| 697 | const needed_size = (end_vaddr + new_block_size) - phdr.p_vaddr; | |
| 702 | 698 | if (needed_size > text_capacity) { |
| 703 | 699 | // Must move the entire text section. |
| 704 | 700 | const new_offset = self.findFreeSpace(needed_size, 0x1000); |
| 701 | const text_size = (last_start + last_size) - phdr.p_vaddr; | |
| 705 | 702 | const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, text_size); |
| 706 | 703 | if (amt != text_size) return error.InputOutput; |
| 707 | 704 | shdr.sh_offset = new_offset; |
| ... | ... | @@ -713,6 +710,12 @@ pub const ElfFile = struct { |
| 713 | 710 | |
| 714 | 711 | self.phdr_table_dirty = true; // TODO look into making only the one program header dirty |
| 715 | 712 | self.shdr_table_dirty = true; // TODO look into making only the one section dirty |
| 713 | ||
| 714 | return AllocatedBlock{ | |
| 715 | .vaddr = end_vaddr, | |
| 716 | .file_offset = shdr.sh_offset + (end_vaddr - phdr.p_vaddr), | |
| 717 | .size_capacity = text_capacity - end_vaddr, | |
| 718 | }; | |
| 716 | 719 | } |
| 717 | 720 | |
| 718 | 721 | fn findAllocatedTextBlock(self: *ElfFile, sym: elf.Elf64_Sym) AllocatedBlock { |
| ... | ... | @@ -739,8 +742,8 @@ pub const ElfFile = struct { |
| 739 | 742 | defer code.deinit(); |
| 740 | 743 | |
| 741 | 744 | const typed_value = decl.typed_value.most_recent.typed_value; |
| 742 | const err_msg = try codegen.generateSymbol(typed_value, module.*, &code); | |
| 743 | if (err_msg != null) |em| { | |
| 745 | const err_msg = try codegen.generateSymbol(self, typed_value, &code); | |
| 746 | if (err_msg) |em| { | |
| 744 | 747 | decl.analysis = .codegen_failure; |
| 745 | 748 | _ = try module.failed_decls.put(decl, em); |
| 746 | 749 | return; |
| ... | ... | @@ -755,7 +758,7 @@ pub const ElfFile = struct { |
| 755 | 758 | |
| 756 | 759 | if (decl.link.local_sym_index != 0) { |
| 757 | 760 | const local_sym = &self.symbols.items[decl.link.local_sym_index]; |
| 758 | const existing_block = self.findAllocatedTextBlock(local_sym); | |
| 761 | const existing_block = self.findAllocatedTextBlock(local_sym.*); | |
| 759 | 762 | const file_offset = if (code_size > existing_block.size_capacity) fo: { |
| 760 | 763 | const new_block = try self.allocateTextBlock(code_size); |
| 761 | 764 | local_sym.st_value = new_block.vaddr; |
| ... | ... | @@ -765,7 +768,7 @@ pub const ElfFile = struct { |
| 765 | 768 | |
| 766 | 769 | break :fo new_block.file_offset; |
| 767 | 770 | } else existing_block.file_offset; |
| 768 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(u8, decl.name)); | |
| 771 | local_sym.st_name = try self.updateString(local_sym.st_name, mem.spanZ(decl.name)); | |
| 769 | 772 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; |
| 770 | 773 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 771 | 774 | try self.writeSymbol(decl.link.local_sym_index); |
| ... | ... | @@ -773,7 +776,7 @@ pub const ElfFile = struct { |
| 773 | 776 | } else { |
| 774 | 777 | try self.symbols.ensureCapacity(self.allocator, self.symbols.items.len + 1); |
| 775 | 778 | try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1); |
| 776 | const decl_name = mem.spanZ(u8, decl.name); | |
| 779 | const decl_name = mem.spanZ(decl.name); | |
| 777 | 780 | const name_str_index = try self.makeString(decl_name); |
| 778 | 781 | const new_block = try self.allocateTextBlock(code_size); |
| 779 | 782 | const local_sym_index = self.symbols.items.len; |
| ... | ... | @@ -796,8 +799,8 @@ pub const ElfFile = struct { |
| 796 | 799 | self.symbol_count_dirty = true; |
| 797 | 800 | self.offset_table_count_dirty = true; |
| 798 | 801 | decl.link = .{ |
| 799 | .local_sym_index = local_sym_index, | |
| 800 | .offset_table_index = offset_table_index, | |
| 802 | .local_sym_index = @intCast(u32, local_sym_index), | |
| 803 | .offset_table_index = @intCast(u32, offset_table_index), | |
| 801 | 804 | }; |
| 802 | 805 | |
| 803 | 806 | break :blk new_block.file_offset; |
| ... | ... | @@ -807,7 +810,7 @@ pub const ElfFile = struct { |
| 807 | 810 | try self.file.pwriteAll(code.items, file_offset); |
| 808 | 811 | |
| 809 | 812 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 810 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*ir.Module.Export{}; | |
| 813 | const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*ir.Module.Export{}; | |
| 811 | 814 | return self.updateDeclExports(module, decl, decl_exports); |
| 812 | 815 | } |
| 813 | 816 | |
| ... | ... | @@ -938,9 +941,9 @@ pub const ElfFile = struct { |
| 938 | 941 | const needed_size = self.symbols.items.len * shdr.sh_entsize; |
| 939 | 942 | if (needed_size > allocated_size) { |
| 940 | 943 | // Must move the entire got section. |
| 941 | const new_offset = self.findFreeSpace(needed_size, shdr.sh_entsize); | |
| 944 | const new_offset = self.findFreeSpace(needed_size, @intCast(u16, shdr.sh_entsize)); | |
| 942 | 945 | const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size); |
| 943 | if (amt != text_size) return error.InputOutput; | |
| 946 | if (amt != shdr.sh_size) return error.InputOutput; | |
| 944 | 947 | shdr.sh_offset = new_offset; |
| 945 | 948 | } |
| 946 | 949 | shdr.sh_size = needed_size; |