authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-14 16:34:04-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-14 16:34:04-04:00
log81a01bd4815779ebeb5898a825bf91628b75ff47
tree01ee2cdca6ba698915667b17cd4dc52735eece02
parent0986dcf1cf5b67ad2b2e606622bf9b2c22e01194

fix codegen of sentinel-terminated arrays and .got alignment

we now have an exit(0) program working

5 files changed, 275 insertions(+), 107 deletions(-)

lib/std/mem.zig+5-1
...@@ -2099,7 +2099,11 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {...@@ -2099,7 +2099,11 @@ pub fn alignBackwardGeneric(comptime T: type, addr: T, alignment: T) T {
2099/// Given an address and an alignment, return true if the address is a multiple of the alignment2099/// Given an address and an alignment, return true if the address is a multiple of the alignment
2100/// The alignment must be a power of 2 and greater than 0.2100/// The alignment must be a power of 2 and greater than 0.
2101pub fn isAligned(addr: usize, alignment: usize) bool {2101pub fn isAligned(addr: usize, alignment: usize) bool {
2102 return alignBackward(addr, alignment) == addr;2102 return isAlignedGeneric(u64, addr, alignment);
2103}
2104
2105pub fn isAlignedGeneric(comptime T: type, addr: T, alignment: T) bool {
2106 return alignBackwardGeneric(T, addr, alignment) == addr;
2103}2107}
21042108
2105test "isAligned" {2109test "isAligned" {
src-self-hosted/codegen.zig+47-7
...@@ -10,8 +10,10 @@ const Target = std.Target;...@@ -10,8 +10,10 @@ const Target = std.Target;
10const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
1111
12pub const Result = union(enum) {12pub const Result = union(enum) {
13 /// This value might or might not alias the `code` parameter passed to `generateSymbol`.13 /// The `code` parameter passed to `generateSymbol` has the value appended.
14 ok: []const u8,14 appended: void,
15 /// The value is available externally, `code` is unused.
16 externally_managed: []const u8,
15 fail: *ir.ErrorMsg,17 fail: *ir.ErrorMsg,
16};18};
1719
...@@ -20,7 +22,11 @@ pub fn generateSymbol(...@@ -20,7 +22,11 @@ pub fn generateSymbol(
20 src: usize,22 src: usize,
21 typed_value: TypedValue,23 typed_value: TypedValue,
22 code: *std.ArrayList(u8),24 code: *std.ArrayList(u8),
23) error{OutOfMemory}!Result {25) error{
26 OutOfMemory,
27 /// A Decl that this symbol depends on had a semantic analysis failure.
28 AnalysisFail,
29}!Result {
24 switch (typed_value.ty.zigTypeTag()) {30 switch (typed_value.ty.zigTypeTag()) {
25 .Fn => {31 .Fn => {
26 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;32 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
...@@ -45,12 +51,29 @@ pub fn generateSymbol(...@@ -45,12 +51,29 @@ pub fn generateSymbol(
45 if (function.err_msg) |em| {51 if (function.err_msg) |em| {
46 return Result{ .fail = em };52 return Result{ .fail = em };
47 } else {53 } else {
48 return Result{ .ok = code.items };54 return Result{ .appended = {} };
49 }55 }
50 },56 },
51 .Array => {57 .Array => {
52 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {58 if (typed_value.val.cast(Value.Payload.Bytes)) |payload| {
53 return Result{ .ok = payload.data };59 if (typed_value.ty.arraySentinel()) |sentinel| {
60 try code.ensureCapacity(code.items.len + payload.data.len + 1);
61 code.appendSliceAssumeCapacity(payload.data);
62 const prev_len = code.items.len;
63 switch (try generateSymbol(bin_file, src, .{
64 .ty = typed_value.ty.elemType(),
65 .val = sentinel,
66 }, code)) {
67 .appended => return Result{ .appended = {} },
68 .externally_managed => |slice| {
69 code.appendSliceAssumeCapacity(slice);
70 return Result{ .appended = {} };
71 },
72 .fail => |em| return Result{ .fail = em },
73 }
74 } else {
75 return Result{ .externally_managed = payload.data };
76 }
54 }77 }
55 return Result{78 return Result{
56 .fail = try ir.ErrorMsg.create(79 .fail = try ir.ErrorMsg.create(
...@@ -64,10 +87,11 @@ pub fn generateSymbol(...@@ -64,10 +87,11 @@ pub fn generateSymbol(
64 .Pointer => {87 .Pointer => {
65 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {88 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
66 const decl = payload.decl;89 const decl = payload.decl;
90 if (decl.analysis != .complete) return error.AnalysisFail;
67 assert(decl.link.local_sym_index != 0);91 assert(decl.link.local_sym_index != 0);
68 // TODO handle the dependency of this symbol on the decl's vaddr.92 // 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.93 // 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;94 const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
71 const endian = bin_file.options.target.cpu.arch.endian();95 const endian = bin_file.options.target.cpu.arch.endian();
72 switch (bin_file.ptr_width) {96 switch (bin_file.ptr_width) {
73 .p32 => {97 .p32 => {
...@@ -79,7 +103,7 @@ pub fn generateSymbol(...@@ -79,7 +103,7 @@ pub fn generateSymbol(
79 mem.writeInt(u64, code.items[0..8], vaddr, endian);103 mem.writeInt(u64, code.items[0..8], vaddr, endian);
80 },104 },
81 }105 }
82 return Result{ .ok = code.items };106 return Result{ .appended = {} };
83 }107 }
84 return Result{108 return Result{
85 .fail = try ir.ErrorMsg.create(109 .fail = try ir.ErrorMsg.create(
...@@ -90,6 +114,22 @@ pub fn generateSymbol(...@@ -90,6 +114,22 @@ pub fn generateSymbol(
90 ),114 ),
91 };115 };
92 },116 },
117 .Int => {
118 const info = typed_value.ty.intInfo(bin_file.options.target);
119 if (info.bits == 8 and !info.signed) {
120 const x = typed_value.val.toUnsignedInt();
121 try code.append(@intCast(u8, x));
122 return Result{ .appended = {} };
123 }
124 return Result{
125 .fail = try ir.ErrorMsg.create(
126 bin_file.allocator,
127 src,
128 "TODO implement generateSymbol for int type '{}'",
129 .{typed_value.ty},
130 ),
131 };
132 },
93 else => |t| {133 else => |t| {
94 return Result{134 return Result{
95 .fail = try ir.ErrorMsg.create(135 .fail = try ir.ErrorMsg.create(
src-self-hosted/ir.zig+11-2
...@@ -334,8 +334,14 @@ pub const Module = struct {...@@ -334,8 +334,14 @@ pub const Module = struct {
334 }334 }
335335
336 pub fn dump(self: *Decl) void {336 pub fn dump(self: *Decl) void {
337 self.scope.dumpSrc(self.src);337 const loc = std.zig.findLineColumn(self.scope.source.bytes, self.src);
338 std.debug.warn(" name={} status={}", .{ mem.spanZ(self.name), @tagName(self.analysis) });338 std.debug.warn("{}:{}:{} name={} status={}", .{
339 self.scope.sub_file_path,
340 loc.line + 1,
341 loc.column + 1,
342 mem.spanZ(self.name),
343 @tagName(self.analysis),
344 });
339 if (self.typedValueManaged()) |tvm| {345 if (self.typedValueManaged()) |tvm| {
340 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });346 std.debug.warn(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val });
341 }347 }
...@@ -721,6 +727,9 @@ pub const Module = struct {...@@ -721,6 +727,9 @@ pub const Module = struct {
721727
722 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {728 self.bin_file.updateDecl(self, decl) catch |err| switch (err) {
723 error.OutOfMemory => return error.OutOfMemory,729 error.OutOfMemory => return error.OutOfMemory,
730 error.AnalysisFail => {
731 decl.analysis = .repeat_dependency_failure;
732 },
724 else => {733 else => {
725 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);734 try self.failed_decls.ensureCapacity(self.failed_decls.size + 1);
726 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(735 self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
src-self-hosted/link.zig+104-93
...@@ -118,8 +118,12 @@ pub const ElfFile = struct {...@@ -118,8 +118,12 @@ pub const ElfFile = struct {
118 symtab_section_index: ?u16 = null,118 symtab_section_index: ?u16 = null,
119 got_section_index: ?u16 = null,119 got_section_index: ?u16 = null,
120120
121 /// The same order as in the file121 /// The same order as in the file. ELF requires global symbols to all be after the
122 symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},122 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
123 /// write them at the end. These are only the local symbols. The length of this array
124 /// is the value used for sh_info in the .symtab section.
125 local_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
126 global_symbols: std.ArrayListUnmanaged(elf.Elf64_Sym) = std.ArrayListUnmanaged(elf.Elf64_Sym){},
123127
124 /// Same order as in the file. The value is the absolute vaddr value.128 /// Same order as in the file. The value is the absolute vaddr value.
125 /// If the vaddr of the executable program header changes, the entire129 /// If the vaddr of the executable program header changes, the entire
...@@ -130,7 +134,6 @@ pub const ElfFile = struct {...@@ -130,7 +134,6 @@ pub const ElfFile = struct {
130 shdr_table_dirty: bool = false,134 shdr_table_dirty: bool = false,
131 shstrtab_dirty: bool = false,135 shstrtab_dirty: bool = false,
132 offset_table_count_dirty: bool = false,136 offset_table_count_dirty: bool = false,
133 symbol_count_dirty: bool = false,
134137
135 error_flags: ErrorFlags = ErrorFlags{},138 error_flags: ErrorFlags = ErrorFlags{},
136139
...@@ -156,14 +159,15 @@ pub const ElfFile = struct {...@@ -156,14 +159,15 @@ pub const ElfFile = struct {
156 };159 };
157160
158 pub const Export = struct {161 pub const Export = struct {
159 sym_index: ?usize = null,162 sym_index: ?u32 = null,
160 };163 };
161164
162 pub fn deinit(self: *ElfFile) void {165 pub fn deinit(self: *ElfFile) void {
163 self.sections.deinit(self.allocator);166 self.sections.deinit(self.allocator);
164 self.program_headers.deinit(self.allocator);167 self.program_headers.deinit(self.allocator);
165 self.shstrtab.deinit(self.allocator);168 self.shstrtab.deinit(self.allocator);
166 self.symbols.deinit(self.allocator);169 self.local_symbols.deinit(self.allocator);
170 self.global_symbols.deinit(self.allocator);
167 self.offset_table.deinit(self.allocator);171 self.offset_table.deinit(self.allocator);
168 if (self.owns_file_handle)172 if (self.owns_file_handle)
169 self.file.close();173 self.file.close();
...@@ -298,7 +302,10 @@ pub const ElfFile = struct {...@@ -298,7 +302,10 @@ pub const ElfFile = struct {
298 if (self.phdr_got_index == null) {302 if (self.phdr_got_index == null) {
299 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);303 self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
300 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;304 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
301 const off = self.findFreeSpace(file_size, ptr_size);305 // We really only need ptr alignment but since we are using PROGBITS, linux requires
306 // page align.
307 const p_align = 0x1000;
308 const off = self.findFreeSpace(file_size, p_align);
302 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });309 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
303 // 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.
304 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something311 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
...@@ -311,7 +318,7 @@ pub const ElfFile = struct {...@@ -311,7 +318,7 @@ pub const ElfFile = struct {
311 .p_vaddr = default_got_addr,318 .p_vaddr = default_got_addr,
312 .p_paddr = default_got_addr,319 .p_paddr = default_got_addr,
313 .p_memsz = file_size,320 .p_memsz = file_size,
314 .p_align = ptr_size,321 .p_align = p_align,
315 .p_flags = elf.PF_R,322 .p_flags = elf.PF_R,
316 });323 });
317 self.phdr_table_dirty = true;324 self.phdr_table_dirty = true;
...@@ -369,7 +376,7 @@ pub const ElfFile = struct {...@@ -369,7 +376,7 @@ pub const ElfFile = struct {
369 .sh_link = 0,376 .sh_link = 0,
370 .sh_info = 0,377 .sh_info = 0,
371 .sh_addralign = phdr.p_align,378 .sh_addralign = phdr.p_align,
372 .sh_entsize = ptr_size,379 .sh_entsize = 0,
373 });380 });
374 self.shdr_table_dirty = true;381 self.shdr_table_dirty = true;
375 }382 }
...@@ -390,12 +397,12 @@ pub const ElfFile = struct {...@@ -390,12 +397,12 @@ pub const ElfFile = struct {
390 .sh_size = file_size,397 .sh_size = file_size,
391 // The section header index of the associated string table.398 // The section header index of the associated string table.
392 .sh_link = self.shstrtab_index.?,399 .sh_link = self.shstrtab_index.?,
393 .sh_info = @intCast(u32, self.symbols.items.len),400 .sh_info = @intCast(u32, self.local_symbols.items.len),
394 .sh_addralign = min_align,401 .sh_addralign = min_align,
395 .sh_entsize = each_size,402 .sh_entsize = each_size,
396 });403 });
397 self.shdr_table_dirty = true;404 self.shdr_table_dirty = true;
398 try self.writeAllSymbols();405 try self.writeSymbol(0);
399 }406 }
400 const shsize: u64 = switch (self.ptr_width) {407 const shsize: u64 = switch (self.ptr_width) {
401 .p32 => @sizeOf(elf.Elf32_Shdr),408 .p32 => @sizeOf(elf.Elf32_Shdr),
...@@ -427,6 +434,10 @@ pub const ElfFile = struct {...@@ -427,6 +434,10 @@ pub const ElfFile = struct {
427 pub fn flush(self: *ElfFile) !void {434 pub fn flush(self: *ElfFile) !void {
428 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();435 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
429436
437 // Unfortunately these have to be buffered and done at the end because ELF does not allow
438 // mixing local and global symbols within a symbol table.
439 try self.writeAllGlobalSymbols();
440
430 if (self.phdr_table_dirty) {441 if (self.phdr_table_dirty) {
431 const phsize: u64 = switch (self.ptr_width) {442 const phsize: u64 = switch (self.ptr_width) {
432 .p32 => @sizeOf(elf.Elf32_Phdr),443 .p32 => @sizeOf(elf.Elf32_Phdr),
...@@ -552,8 +563,9 @@ pub const ElfFile = struct {...@@ -552,8 +563,9 @@ pub const ElfFile = struct {
552 assert(!self.phdr_table_dirty);563 assert(!self.phdr_table_dirty);
553 assert(!self.shdr_table_dirty);564 assert(!self.shdr_table_dirty);
554 assert(!self.shstrtab_dirty);565 assert(!self.shstrtab_dirty);
555 assert(!self.symbol_count_dirty);
556 assert(!self.offset_table_count_dirty);566 assert(!self.offset_table_count_dirty);
567 const syms_sect = &self.sections.items[self.symtab_section_index.?];
568 assert(syms_sect.sh_info == self.local_symbols.items.len);
557 }569 }
558570
559 fn writeElfHeader(self: *ElfFile) !void {571 fn writeElfHeader(self: *ElfFile) !void {
...@@ -683,7 +695,7 @@ pub const ElfFile = struct {...@@ -683,7 +695,7 @@ pub const ElfFile = struct {
683 size_capacity: u64,695 size_capacity: u64,
684 };696 };
685697
686 fn allocateTextBlock(self: *ElfFile, new_block_size: u64) !AllocatedBlock {698 fn allocateTextBlock(self: *ElfFile, new_block_size: u64, alignment: u64) !AllocatedBlock {
687 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];699 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
688 const shdr = &self.sections.items[self.text_section_index.?];700 const shdr = &self.sections.items[self.text_section_index.?];
689701
...@@ -692,14 +704,15 @@ pub const ElfFile = struct {...@@ -692,14 +704,15 @@ pub const ElfFile = struct {
692 // TODO instead of looping here, maintain a free list and a pointer to the end.704 // TODO instead of looping here, maintain a free list and a pointer to the end.
693 var last_start: u64 = phdr.p_vaddr;705 var last_start: u64 = phdr.p_vaddr;
694 var last_size: u64 = 0;706 var last_size: u64 = 0;
695 for (self.symbols.items) |sym| {707 for (self.local_symbols.items) |sym| {
696 if (sym.st_value > last_start) {708 if (sym.st_value + sym.st_size > last_start + last_size) {
697 last_start = sym.st_value;709 last_start = sym.st_value;
698 last_size = sym.st_size;710 last_size = sym.st_size;
699 }711 }
700 }712 }
701 const end_vaddr = last_start + (last_size * alloc_num / alloc_den);713 const end_vaddr = last_start + (last_size * alloc_num / alloc_den);
702 const needed_size = (end_vaddr + new_block_size) - phdr.p_vaddr;714 const aligned_start_vaddr = mem.alignForwardGeneric(u64, end_vaddr, alignment);
715 const needed_size = (aligned_start_vaddr + new_block_size) - phdr.p_vaddr;
703 if (needed_size > text_capacity) {716 if (needed_size > text_capacity) {
704 // Must move the entire text section.717 // Must move the entire text section.
705 const new_offset = self.findFreeSpace(needed_size, 0x1000);718 const new_offset = self.findFreeSpace(needed_size, 0x1000);
...@@ -717,9 +730,9 @@ pub const ElfFile = struct {...@@ -717,9 +730,9 @@ pub const ElfFile = struct {
717 self.shdr_table_dirty = true; // TODO look into making only the one section dirty730 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
718731
719 return AllocatedBlock{732 return AllocatedBlock{
720 .vaddr = end_vaddr,733 .vaddr = aligned_start_vaddr,
721 .file_offset = shdr.sh_offset + (end_vaddr - phdr.p_vaddr),734 .file_offset = shdr.sh_offset + (aligned_start_vaddr - phdr.p_vaddr),
722 .size_capacity = text_capacity - end_vaddr,735 .size_capacity = text_capacity - needed_size,
723 };736 };
724 }737 }
725738
...@@ -731,7 +744,7 @@ pub const ElfFile = struct {...@@ -731,7 +744,7 @@ pub const ElfFile = struct {
731 // TODO look into using a hash map to speed up perf.744 // TODO look into using a hash map to speed up perf.
732 const text_capacity = self.allocatedSize(shdr.sh_offset);745 const text_capacity = self.allocatedSize(shdr.sh_offset);
733 var next_vaddr_start = phdr.p_vaddr + text_capacity;746 var next_vaddr_start = phdr.p_vaddr + text_capacity;
734 for (self.symbols.items) |elem| {747 for (self.local_symbols.items) |elem| {
735 if (elem.st_value < sym.st_value) continue;748 if (elem.st_value < sym.st_value) continue;
736 if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value;749 if (elem.st_value < next_vaddr_start) next_vaddr_start = elem.st_value;
737 }750 }
...@@ -748,7 +761,8 @@ pub const ElfFile = struct {...@@ -748,7 +761,8 @@ pub const ElfFile = struct {
748761
749 const typed_value = decl.typed_value.most_recent.typed_value;762 const typed_value = decl.typed_value.most_recent.typed_value;
750 const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {763 const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
751 .ok => |x| x,764 .externally_managed => |x| x,
765 .appended => code_buffer.items,
752 .fail => |em| {766 .fail => |em| {
753 decl.analysis = .codegen_failure;767 decl.analysis = .codegen_failure;
754 _ = try module.failed_decls.put(decl, em);768 _ = try module.failed_decls.put(decl, em);
...@@ -756,20 +770,23 @@ pub const ElfFile = struct {...@@ -756,20 +770,23 @@ pub const ElfFile = struct {
756 },770 },
757 };771 };
758772
773 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
774
759 const file_offset = blk: {775 const file_offset = blk: {
760 const code_size = code.len;
761 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {776 const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
762 .Fn => elf.STT_FUNC,777 .Fn => elf.STT_FUNC,
763 else => elf.STT_OBJECT,778 else => elf.STT_OBJECT,
764 };779 };
765780
766 if (decl.link.local_sym_index != 0) {781 if (decl.link.local_sym_index != 0) {
767 const local_sym = &self.symbols.items[decl.link.local_sym_index];782 const local_sym = &self.local_symbols.items[decl.link.local_sym_index];
768 const existing_block = self.findAllocatedTextBlock(local_sym.*);783 const existing_block = self.findAllocatedTextBlock(local_sym.*);
769 const file_offset = if (code_size > existing_block.size_capacity) fo: {784 const need_realloc = code.len > existing_block.size_capacity or
770 const new_block = try self.allocateTextBlock(code_size);785 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
786 const file_offset = if (need_realloc) fo: {
787 const new_block = try self.allocateTextBlock(code.len, required_alignment);
771 local_sym.st_value = new_block.vaddr;788 local_sym.st_value = new_block.vaddr;
772 local_sym.st_size = code_size;789 local_sym.st_size = code.len;
773790
774 try self.writeOffsetTableEntry(decl.link.offset_table_index);791 try self.writeOffsetTableEntry(decl.link.offset_table_index);
775792
...@@ -781,27 +798,27 @@ pub const ElfFile = struct {...@@ -781,27 +798,27 @@ pub const ElfFile = struct {
781 try self.writeSymbol(decl.link.local_sym_index);798 try self.writeSymbol(decl.link.local_sym_index);
782 break :blk file_offset;799 break :blk file_offset;
783 } else {800 } else {
784 try self.symbols.ensureCapacity(self.allocator, self.symbols.items.len + 1);801 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
785 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);802 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
786 const decl_name = mem.spanZ(decl.name);803 const decl_name = mem.spanZ(decl.name);
787 const name_str_index = try self.makeString(decl_name);804 const name_str_index = try self.makeString(decl_name);
788 const new_block = try self.allocateTextBlock(code_size);805 const new_block = try self.allocateTextBlock(code.len, required_alignment);
789 const local_sym_index = self.symbols.items.len;806 const local_sym_index = self.local_symbols.items.len;
790 const offset_table_index = self.offset_table.items.len;807 const offset_table_index = self.offset_table.items.len;
791808
792 self.symbols.appendAssumeCapacity(.{809 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });
810 self.local_symbols.appendAssumeCapacity(.{
793 .st_name = name_str_index,811 .st_name = name_str_index,
794 .st_info = (elf.STB_LOCAL << 4) | stt_bits,812 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
795 .st_other = 0,813 .st_other = 0,
796 .st_shndx = self.text_section_index.?,814 .st_shndx = self.text_section_index.?,
797 .st_value = new_block.vaddr,815 .st_value = new_block.vaddr,
798 .st_size = code_size,816 .st_size = code.len,
799 });817 });
800 errdefer self.symbols.shrink(self.allocator, self.symbols.items.len - 1);818 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
801 self.offset_table.appendAssumeCapacity(new_block.vaddr);819 self.offset_table.appendAssumeCapacity(new_block.vaddr);
802 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);820 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
803821
804 self.symbol_count_dirty = true;
805 self.offset_table_count_dirty = true;822 self.offset_table_count_dirty = true;
806823
807 try self.writeSymbol(local_sym_index);824 try self.writeSymbol(local_sym_index);
...@@ -830,10 +847,10 @@ pub const ElfFile = struct {...@@ -830,10 +847,10 @@ pub const ElfFile = struct {
830 decl: *const ir.Module.Decl,847 decl: *const ir.Module.Decl,
831 exports: []const *ir.Module.Export,848 exports: []const *ir.Module.Export,
832 ) !void {849 ) !void {
833 try self.symbols.ensureCapacity(self.allocator, self.symbols.items.len + exports.len);850 try self.global_symbols.ensureCapacity(self.allocator, self.global_symbols.items.len + exports.len);
834 const typed_value = decl.typed_value.most_recent.typed_value;851 const typed_value = decl.typed_value.most_recent.typed_value;
835 if (decl.link.local_sym_index == 0) return;852 if (decl.link.local_sym_index == 0) return;
836 const decl_sym = self.symbols.items[decl.link.local_sym_index];853 const decl_sym = self.local_symbols.items[decl.link.local_sym_index];
837854
838 for (exports) |exp| {855 for (exports) |exp| {
839 if (exp.options.section) |section_name| {856 if (exp.options.section) |section_name| {
...@@ -866,7 +883,7 @@ pub const ElfFile = struct {...@@ -866,7 +883,7 @@ pub const ElfFile = struct {
866 };883 };
867 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);884 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
868 if (exp.link.sym_index) |i| {885 if (exp.link.sym_index) |i| {
869 const sym = &self.symbols.items[i];886 const sym = &self.global_symbols.items[i];
870 sym.* = .{887 sym.* = .{
871 .st_name = try self.updateString(sym.st_name, exp.options.name),888 .st_name = try self.updateString(sym.st_name, exp.options.name),
872 .st_info = (stb_bits << 4) | stt_bits,889 .st_info = (stb_bits << 4) | stt_bits,
...@@ -875,11 +892,10 @@ pub const ElfFile = struct {...@@ -875,11 +892,10 @@ pub const ElfFile = struct {
875 .st_value = decl_sym.st_value,892 .st_value = decl_sym.st_value,
876 .st_size = decl_sym.st_size,893 .st_size = decl_sym.st_size,
877 };894 };
878 try self.writeSymbol(i);
879 } else {895 } else {
880 const name = try self.makeString(exp.options.name);896 const name = try self.makeString(exp.options.name);
881 const i = self.symbols.items.len;897 const i = self.global_symbols.items.len;
882 self.symbols.appendAssumeCapacity(.{898 self.global_symbols.appendAssumeCapacity(.{
883 .st_name = name,899 .st_name = name,
884 .st_info = (stb_bits << 4) | stt_bits,900 .st_info = (stb_bits << 4) | stt_bits,
885 .st_other = 0,901 .st_other = 0,
...@@ -887,11 +903,9 @@ pub const ElfFile = struct {...@@ -887,11 +903,9 @@ pub const ElfFile = struct {
887 .st_value = decl_sym.st_value,903 .st_value = decl_sym.st_value,
888 .st_size = decl_sym.st_size,904 .st_size = decl_sym.st_size,
889 });905 });
890 errdefer self.symbols.shrink(self.allocator, self.symbols.items.len - 1);906 errdefer self.global_symbols.shrink(self.allocator, self.global_symbols.items.len - 1);
891 try self.writeSymbol(i);
892907
893 self.symbol_count_dirty = true;908 exp.link.sym_index = @intCast(u32, i);
894 exp.link.sym_index = i;
895 }909 }
896 }910 }
897 }911 }
...@@ -944,13 +958,17 @@ pub const ElfFile = struct {...@@ -944,13 +958,17 @@ pub const ElfFile = struct {
944 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {958 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
945 const shdr = &self.sections.items[self.got_section_index.?];959 const shdr = &self.sections.items[self.got_section_index.?];
946 const phdr = &self.program_headers.items[self.phdr_got_index.?];960 const phdr = &self.program_headers.items[self.phdr_got_index.?];
961 const entry_size: u16 = switch (self.ptr_width) {
962 .p32 => 4,
963 .p64 => 8,
964 };
947 if (self.offset_table_count_dirty) {965 if (self.offset_table_count_dirty) {
948 // TODO Also detect virtual address collisions.966 // TODO Also detect virtual address collisions.
949 const allocated_size = self.allocatedSize(shdr.sh_offset);967 const allocated_size = self.allocatedSize(shdr.sh_offset);
950 const needed_size = self.symbols.items.len * shdr.sh_entsize;968 const needed_size = self.local_symbols.items.len * entry_size;
951 if (needed_size > allocated_size) {969 if (needed_size > allocated_size) {
952 // Must move the entire got section.970 // Must move the entire got section.
953 const new_offset = self.findFreeSpace(needed_size, @intCast(u16, shdr.sh_entsize));971 const new_offset = self.findFreeSpace(needed_size, entry_size);
954 const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size);972 const amt = try self.file.copyRangeAll(shdr.sh_offset, self.file, new_offset, shdr.sh_size);
955 if (amt != shdr.sh_size) return error.InputOutput;973 if (amt != shdr.sh_size) return error.InputOutput;
956 shdr.sh_offset = new_offset;974 shdr.sh_offset = new_offset;
...@@ -965,7 +983,7 @@ pub const ElfFile = struct {...@@ -965,7 +983,7 @@ pub const ElfFile = struct {
965 self.offset_table_count_dirty = false;983 self.offset_table_count_dirty = false;
966 }984 }
967 const endian = self.options.target.cpu.arch.endian();985 const endian = self.options.target.cpu.arch.endian();
968 const off = shdr.sh_offset + shdr.sh_entsize * index;986 const off = shdr.sh_offset + @as(u64, entry_size) * index;
969 switch (self.ptr_width) {987 switch (self.ptr_width) {
970 .p32 => {988 .p32 => {
971 var buf: [4]u8 = undefined;989 var buf: [4]u8 = undefined;
...@@ -981,35 +999,42 @@ pub const ElfFile = struct {...@@ -981,35 +999,42 @@ pub const ElfFile = struct {
981 }999 }
9821000
983 fn writeSymbol(self: *ElfFile, index: usize) !void {1001 fn writeSymbol(self: *ElfFile, index: usize) !void {
984 assert(index != 0);
985 const syms_sect = &self.sections.items[self.symtab_section_index.?];1002 const syms_sect = &self.sections.items[self.symtab_section_index.?];
986 // Make sure we are not pointlessly writing symbol data that will have to get relocated1003 // Make sure we are not pointlessly writing symbol data that will have to get relocated
987 // due to running out of space.1004 // due to running out of space.
988 if (self.symbol_count_dirty) {1005 if (self.local_symbols.items.len != syms_sect.sh_info) {
989 const sym_size: u64 = switch (self.ptr_width) {1006 const sym_size: u64 = switch (self.ptr_width) {
990 .p32 => @sizeOf(elf.Elf32_Sym),1007 .p32 => @sizeOf(elf.Elf32_Sym),
991 .p64 => @sizeOf(elf.Elf64_Sym),1008 .p64 => @sizeOf(elf.Elf64_Sym),
992 };1009 };
993 const allocated_size = self.allocatedSize(syms_sect.sh_offset);1010 const sym_align: u16 = switch (self.ptr_width) {
994 const needed_size = self.symbols.items.len * sym_size;1011 .p32 => @alignOf(elf.Elf32_Sym),
995 if (needed_size > allocated_size) {1012 .p64 => @alignOf(elf.Elf64_Sym),
996 return self.writeAllSymbols();1013 };
1014 const needed_size = (self.local_symbols.items.len + self.global_symbols.items.len) * sym_size;
1015 if (needed_size > self.allocatedSize(syms_sect.sh_offset)) {
1016 // Move all the symbols to a new file location.
1017 const new_offset = self.findFreeSpace(needed_size, sym_align);
1018 const existing_size = @as(u64, syms_sect.sh_info) * sym_size;
1019 const amt = try self.file.copyRangeAll(syms_sect.sh_offset, self.file, new_offset, existing_size);
1020 if (amt != existing_size) return error.InputOutput;
1021 syms_sect.sh_offset = new_offset;
997 }1022 }
998 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);1023 syms_sect.sh_info = @intCast(u32, self.local_symbols.items.len);
1024 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
999 self.shdr_table_dirty = true; // TODO look into only writing one section1025 self.shdr_table_dirty = true; // TODO look into only writing one section
1000 self.symbol_count_dirty = false;
1001 }1026 }
1002 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1027 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1003 switch (self.ptr_width) {1028 switch (self.ptr_width) {
1004 .p32 => {1029 .p32 => {
1005 var sym = [1]elf.Elf32_Sym{1030 var sym = [1]elf.Elf32_Sym{
1006 .{1031 .{
1007 .st_name = self.symbols.items[index].st_name,1032 .st_name = self.local_symbols.items[index].st_name,
1008 .st_value = @intCast(u32, self.symbols.items[index].st_value),1033 .st_value = @intCast(u32, self.local_symbols.items[index].st_value),
1009 .st_size = @intCast(u32, self.symbols.items[index].st_size),1034 .st_size = @intCast(u32, self.local_symbols.items[index].st_size),
1010 .st_info = self.symbols.items[index].st_info,1035 .st_info = self.local_symbols.items[index].st_info,
1011 .st_other = self.symbols.items[index].st_other,1036 .st_other = self.local_symbols.items[index].st_other,
1012 .st_shndx = self.symbols.items[index].st_shndx,1037 .st_shndx = self.local_symbols.items[index].st_shndx,
1013 },1038 },
1014 };1039 };
1015 if (foreign_endian) {1040 if (foreign_endian) {
...@@ -1019,7 +1044,7 @@ pub const ElfFile = struct {...@@ -1019,7 +1044,7 @@ pub const ElfFile = struct {
1019 try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);1044 try self.file.pwriteAll(mem.sliceAsBytes(sym[0..1]), off);
1020 },1045 },
1021 .p64 => {1046 .p64 => {
1022 var sym = [1]elf.Elf64_Sym{self.symbols.items[index]};1047 var sym = [1]elf.Elf64_Sym{self.local_symbols.items[index]};
1023 if (foreign_endian) {1048 if (foreign_endian) {
1024 bswapAllFields(elf.Elf64_Sym, &sym[0]);1049 bswapAllFields(elf.Elf64_Sym, &sym[0]);
1025 }1050 }
...@@ -1029,67 +1054,53 @@ pub const ElfFile = struct {...@@ -1029,67 +1054,53 @@ pub const ElfFile = struct {
1029 }1054 }
1030 }1055 }
10311056
1032 fn writeAllSymbols(self: *ElfFile) !void {1057 fn writeAllGlobalSymbols(self: *ElfFile) !void {
1033 const syms_sect = &self.sections.items[self.symtab_section_index.?];1058 const syms_sect = &self.sections.items[self.symtab_section_index.?];
1034 const sym_align: u16 = switch (self.ptr_width) {
1035 .p32 => @alignOf(elf.Elf32_Sym),
1036 .p64 => @alignOf(elf.Elf64_Sym),
1037 };
1038 const sym_size: u64 = switch (self.ptr_width) {1059 const sym_size: u64 = switch (self.ptr_width) {
1039 .p32 => @sizeOf(elf.Elf32_Sym),1060 .p32 => @sizeOf(elf.Elf32_Sym),
1040 .p64 => @sizeOf(elf.Elf64_Sym),1061 .p64 => @sizeOf(elf.Elf64_Sym),
1041 };1062 };
1042 const allocated_size = self.allocatedSize(syms_sect.sh_offset);
1043 const needed_size = self.symbols.items.len * sym_size;
1044 if (needed_size > allocated_size) {
1045 syms_sect.sh_size = 0; // free the space
1046 syms_sect.sh_offset = self.findFreeSpace(needed_size, sym_align);
1047 //std.debug.warn("moved symtab to 0x{x} to 0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
1048 }
1049 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });1063 //std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
1050 syms_sect.sh_size = needed_size;
1051 syms_sect.sh_info = @intCast(u32, self.symbols.items.len);
1052 self.symbol_count_dirty = false;
1053 self.shdr_table_dirty = true; // TODO look into only writing one section
1054 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();1064 const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
1065 const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
1055 switch (self.ptr_width) {1066 switch (self.ptr_width) {
1056 .p32 => {1067 .p32 => {
1057 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.symbols.items.len);1068 const buf = try self.allocator.alloc(elf.Elf32_Sym, self.global_symbols.items.len);
1058 defer self.allocator.free(buf);1069 defer self.allocator.free(buf);
10591070
1060 for (buf) |*sym, i| {1071 for (buf) |*sym, i| {
1061 sym.* = .{1072 sym.* = .{
1062 .st_name = self.symbols.items[i].st_name,1073 .st_name = self.global_symbols.items[i].st_name,
1063 .st_value = @intCast(u32, self.symbols.items[i].st_value),1074 .st_value = @intCast(u32, self.global_symbols.items[i].st_value),
1064 .st_size = @intCast(u32, self.symbols.items[i].st_size),1075 .st_size = @intCast(u32, self.global_symbols.items[i].st_size),
1065 .st_info = self.symbols.items[i].st_info,1076 .st_info = self.global_symbols.items[i].st_info,
1066 .st_other = self.symbols.items[i].st_other,1077 .st_other = self.global_symbols.items[i].st_other,
1067 .st_shndx = self.symbols.items[i].st_shndx,1078 .st_shndx = self.global_symbols.items[i].st_shndx,
1068 };1079 };
1069 if (foreign_endian) {1080 if (foreign_endian) {
1070 bswapAllFields(elf.Elf32_Sym, sym);1081 bswapAllFields(elf.Elf32_Sym, sym);
1071 }1082 }
1072 }1083 }
1073 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);1084 try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1074 },1085 },
1075 .p64 => {1086 .p64 => {
1076 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.symbols.items.len);1087 const buf = try self.allocator.alloc(elf.Elf64_Sym, self.global_symbols.items.len);
1077 defer self.allocator.free(buf);1088 defer self.allocator.free(buf);
10781089
1079 for (buf) |*sym, i| {1090 for (buf) |*sym, i| {
1080 sym.* = .{1091 sym.* = .{
1081 .st_name = self.symbols.items[i].st_name,1092 .st_name = self.global_symbols.items[i].st_name,
1082 .st_value = self.symbols.items[i].st_value,1093 .st_value = self.global_symbols.items[i].st_value,
1083 .st_size = self.symbols.items[i].st_size,1094 .st_size = self.global_symbols.items[i].st_size,
1084 .st_info = self.symbols.items[i].st_info,1095 .st_info = self.global_symbols.items[i].st_info,
1085 .st_other = self.symbols.items[i].st_other,1096 .st_other = self.global_symbols.items[i].st_other,
1086 .st_shndx = self.symbols.items[i].st_shndx,1097 .st_shndx = self.global_symbols.items[i].st_shndx,
1087 };1098 };
1088 if (foreign_endian) {1099 if (foreign_endian) {
1089 bswapAllFields(elf.Elf64_Sym, sym);1100 bswapAllFields(elf.Elf64_Sym, sym);
1090 }1101 }
1091 }1102 }
1092 try self.file.pwriteAll(mem.sliceAsBytes(buf), syms_sect.sh_offset);1103 try self.file.pwriteAll(mem.sliceAsBytes(buf), global_syms_off);
1093 },1104 },
1094 }1105 }
1095 }1106 }
...@@ -1126,7 +1137,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El...@@ -1126,7 +1137,7 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !El
1126 errdefer self.deinit();1137 errdefer self.deinit();
11271138
1128 // Index 0 is always a null symbol.1139 // Index 0 is always a null symbol.
1129 try self.symbols.append(allocator, .{1140 try self.local_symbols.append(allocator, .{
1130 .st_name = 0,1141 .st_name = 0,
1131 .st_info = 0,1142 .st_info = 0,
1132 .st_other = 0,1143 .st_other = 0,
src-self-hosted/type.zig+108-4
...@@ -287,12 +287,12 @@ pub const Type = extern union {...@@ -287,12 +287,12 @@ pub const Type = extern union {
287 .fn_naked_noreturn_no_args,287 .fn_naked_noreturn_no_args,
288 .fn_ccc_void_no_args,288 .fn_ccc_void_no_args,
289 .single_const_pointer_to_comptime_int,289 .single_const_pointer_to_comptime_int,
290 .const_slice_u8, // See last_no_payload_tag below.290 .const_slice_u8,
291 .array_u8_sentinel_0,291 .array_u8_sentinel_0,
292 .array,292 .array, // TODO check for zero bits
293 .single_const_pointer,293 .single_const_pointer,
294 .int_signed,294 .int_signed, // TODO check for zero bits
295 .int_unsigned,295 .int_unsigned, // TODO check for zero bits
296 => true,296 => true,
297297
298 .c_void,298 .c_void,
...@@ -306,6 +306,66 @@ pub const Type = extern union {...@@ -306,6 +306,66 @@ pub const Type = extern union {
306 };306 };
307 }307 }
308308
309 /// Asserts that hasCodeGenBits() is true.
310 pub fn abiAlignment(self: Type, target: Target) u32 {
311 return switch (self.tag()) {
312 .u8,
313 .i8,
314 .bool,
315 .fn_noreturn_no_args, // represents machine code; not a pointer
316 .fn_naked_noreturn_no_args, // represents machine code; not a pointer
317 .fn_ccc_void_no_args, // represents machine code; not a pointer
318 .array_u8_sentinel_0,
319 => return 1,
320
321 .isize,
322 .usize,
323 .single_const_pointer_to_comptime_int,
324 .const_slice_u8,
325 .single_const_pointer,
326 => return @divExact(target.cpu.arch.ptrBitWidth(), 8),
327
328 .c_short => return @divExact(CType.short.sizeInBits(target), 8),
329 .c_ushort => return @divExact(CType.ushort.sizeInBits(target), 8),
330 .c_int => return @divExact(CType.int.sizeInBits(target), 8),
331 .c_uint => return @divExact(CType.uint.sizeInBits(target), 8),
332 .c_long => return @divExact(CType.long.sizeInBits(target), 8),
333 .c_ulong => return @divExact(CType.ulong.sizeInBits(target), 8),
334 .c_longlong => return @divExact(CType.longlong.sizeInBits(target), 8),
335 .c_ulonglong => return @divExact(CType.ulonglong.sizeInBits(target), 8),
336
337 .f16 => return 2,
338 .f32 => return 4,
339 .f64 => return 8,
340 .f128 => return 16,
341 .c_longdouble => return 16,
342
343 .anyerror => return 2, // TODO revisit this when we have the concept of the error tag type
344
345 .array => return self.cast(Payload.Array).?.elem_type.abiAlignment(target),
346
347 .int_signed, .int_unsigned => {
348 const bits: u16 = if (self.cast(Payload.IntSigned)) |pl|
349 pl.bits
350 else if (self.cast(Payload.IntUnsigned)) |pl|
351 pl.bits
352 else
353 unreachable;
354
355 return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8);
356 },
357
358 .c_void,
359 .void,
360 .type,
361 .comptime_int,
362 .comptime_float,
363 .noreturn,
364 .@"null",
365 => unreachable,
366 };
367 }
368
309 pub fn isSinglePointer(self: Type) bool {369 pub fn isSinglePointer(self: Type) bool {
310 return switch (self.tag()) {370 return switch (self.tag()) {
311 .u8,371 .u8,
...@@ -525,6 +585,50 @@ pub const Type = extern union {...@@ -525,6 +585,50 @@ pub const Type = extern union {
525 };585 };
526 }586 }
527587
588 /// Asserts the type is an array or vector.
589 pub fn arraySentinel(self: Type) ?Value {
590 return switch (self.tag()) {
591 .u8,
592 .i8,
593 .isize,
594 .usize,
595 .c_short,
596 .c_ushort,
597 .c_int,
598 .c_uint,
599 .c_long,
600 .c_ulong,
601 .c_longlong,
602 .c_ulonglong,
603 .c_longdouble,
604 .f16,
605 .f32,
606 .f64,
607 .f128,
608 .c_void,
609 .bool,
610 .void,
611 .type,
612 .anyerror,
613 .comptime_int,
614 .comptime_float,
615 .noreturn,
616 .@"null",
617 .fn_noreturn_no_args,
618 .fn_naked_noreturn_no_args,
619 .fn_ccc_void_no_args,
620 .single_const_pointer,
621 .single_const_pointer_to_comptime_int,
622 .const_slice_u8,
623 .int_unsigned,
624 .int_signed,
625 => unreachable,
626
627 .array => return null,
628 .array_u8_sentinel_0 => return Value.initTag(.zero),
629 };
630 }
631
528 /// Returns true if and only if the type is a fixed-width, signed integer.632 /// Returns true if and only if the type is a fixed-width, signed integer.
529 pub fn isSignedInt(self: Type) bool {633 pub fn isSignedInt(self: Type) bool {
530 return switch (self.tag()) {634 return switch (self.tag()) {