| author | |
| committer | |
| log | 4beff80b2f30ad85f2127b1281053b8b25b0cc33 |
| tree | 9937e9ee95d3060afa021449439caefd6476b5c2 |
| parent | 2b8e7deeda5d4d0d74f42265a4ddf466f91c8fc2 |
* `optimize_mode` is passed to `link.File` and stored there
* improve the debugging function `Module.dumpInst`
* get rid of `Value.the_one_possible_value` in favor of a few more
specific values for different types. This is less buggy, one less
footgun.
* `Type.onePossibleValue` now returns a `?Value` instead of `bool`.
* codegen handles undefined values. `undef` is a new `MCValue` tag.
It uses 0xaa values depending on optimization mode. However
optimization mode does not yet support scope overrides.
* link.zig: move the `Options` field from `File.Elf` and `File.C` to
the base struct.
- fix the Tag enum to adhere to style conventions
* ZIR now supports emitting undefined values.
* Fix the logic of comptime math to properly compare against zero using
the `compareWithZero` function.8 files changed, 248 insertions(+), 120 deletions(-)
src-self-hosted/Module.zig+43-19| ... | ... | @@ -47,7 +47,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 47 | 47 | /// Maps fully qualified namespaced names to the Decl struct for them. |
| 48 | 48 | decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{}, |
| 49 | 49 | |
| 50 | optimize_mode: std.builtin.Mode, | |
| 51 | 50 | link_error_flags: link.File.ErrorFlags = .{}, |
| 52 | 51 | |
| 53 | 52 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), |
| ... | ... | @@ -385,18 +384,6 @@ pub const Scope = struct { |
| 385 | 384 | }; |
| 386 | 385 | } |
| 387 | 386 | |
| 388 | pub fn dumpInst(self: *Scope, inst: *Inst) void { | |
| 389 | const zir_module = self.namespace(); | |
| 390 | const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src); | |
| 391 | std.debug.warn("{}:{}:{}: {}: ty={}\n", .{ | |
| 392 | zir_module.sub_file_path, | |
| 393 | loc.line + 1, | |
| 394 | loc.column + 1, | |
| 395 | @tagName(inst.tag), | |
| 396 | inst.ty, | |
| 397 | }); | |
| 398 | } | |
| 399 | ||
| 400 | 387 | /// Asserts the scope has a parent which is a ZIRModule or File and |
| 401 | 388 | /// returns the sub_file_path field. |
| 402 | 389 | pub fn subFilePath(base: *Scope) []const u8 { |
| ... | ... | @@ -802,6 +789,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 802 | 789 | .output_mode = options.output_mode, |
| 803 | 790 | .link_mode = options.link_mode orelse .Static, |
| 804 | 791 | .object_format = options.object_format orelse options.target.getObjectFormat(), |
| 792 | .optimize_mode = options.optimize_mode, | |
| 805 | 793 | }); |
| 806 | 794 | errdefer bin_file.destroy(); |
| 807 | 795 | |
| ... | ... | @@ -838,7 +826,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { |
| 838 | 826 | .bin_file_dir = bin_file_dir, |
| 839 | 827 | .bin_file_path = options.bin_file_path, |
| 840 | 828 | .bin_file = bin_file, |
| 841 | .optimize_mode = options.optimize_mode, | |
| 842 | 829 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), |
| 843 | 830 | .keep_source_files_loaded = options.keep_source_files_loaded, |
| 844 | 831 | }; |
| ... | ... | @@ -894,7 +881,11 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void { |
| 894 | 881 | } |
| 895 | 882 | |
| 896 | 883 | pub fn target(self: Module) std.Target { |
| 897 | return self.bin_file.options().target; | |
| 884 | return self.bin_file.options.target; | |
| 885 | } | |
| 886 | ||
| 887 | pub fn optimizeMode(self: Module) std.builtin.Mode { | |
| 888 | return self.bin_file.options.optimize_mode; | |
| 898 | 889 | } |
| 899 | 890 | |
| 900 | 891 | /// Detect changes to source files, perform semantic analysis, and update the output files. |
| ... | ... | @@ -1991,14 +1982,14 @@ pub fn constType(self: *Module, scope: *Scope, src: usize, ty: Type) !*Inst { |
| 1991 | 1982 | pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst { |
| 1992 | 1983 | return self.constInst(scope, src, .{ |
| 1993 | 1984 | .ty = Type.initTag(.void), |
| 1994 | .val = Value.initTag(.the_one_possible_value), | |
| 1985 | .val = Value.initTag(.void_value), | |
| 1995 | 1986 | }); |
| 1996 | 1987 | } |
| 1997 | 1988 | |
| 1998 | 1989 | pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst { |
| 1999 | 1990 | return self.constInst(scope, src, .{ |
| 2000 | 1991 | .ty = Type.initTag(.noreturn), |
| 2001 | .val = Value.initTag(.the_one_possible_value), | |
| 1992 | .val = Value.initTag(.unreachable_value), | |
| 2002 | 1993 | }); |
| 2003 | 1994 | } |
| 2004 | 1995 | |
| ... | ... | @@ -2162,7 +2153,8 @@ pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name: |
| 2162 | 2153 | } |
| 2163 | 2154 | |
| 2164 | 2155 | pub fn wantSafety(self: *Module, scope: *Scope) bool { |
| 2165 | return switch (self.optimize_mode) { | |
| 2156 | // TODO take into account scope's safety overrides | |
| 2157 | return switch (self.optimizeMode()) { | |
| 2166 | 2158 | .Debug => true, |
| 2167 | 2159 | .ReleaseSafe => true, |
| 2168 | 2160 | .ReleaseFast => false, |
| ... | ... | @@ -2511,7 +2503,7 @@ pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_v |
| 2511 | 2503 | |
| 2512 | 2504 | const elem_ty = ptr.ty.elemType(); |
| 2513 | 2505 | const value = try self.coerce(scope, elem_ty, uncasted_value); |
| 2514 | if (elem_ty.onePossibleValue()) | |
| 2506 | if (elem_ty.onePossibleValue() != null) | |
| 2515 | 2507 | return self.constVoid(scope, src); |
| 2516 | 2508 | |
| 2517 | 2509 | // TODO handle comptime pointer writes |
| ... | ... | @@ -2803,3 +2795,35 @@ pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Typ |
| 2803 | 2795 | type_payload.* = .{ .pointee_type = elem_ty }; |
| 2804 | 2796 | return Type.initPayload(&type_payload.base); |
| 2805 | 2797 | } |
| 2798 | ||
| 2799 | pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | |
| 2800 | const zir_module = scope.namespace(); | |
| 2801 | const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source"); | |
| 2802 | const loc = std.zig.findLineColumn(source, inst.src); | |
| 2803 | if (inst.tag == .constant) { | |
| 2804 | std.debug.warn("constant ty={} val={} src={}:{}:{}\n", .{ | |
| 2805 | inst.ty, | |
| 2806 | inst.castTag(.constant).?.val, | |
| 2807 | zir_module.subFilePath(), | |
| 2808 | loc.line + 1, | |
| 2809 | loc.column + 1, | |
| 2810 | }); | |
| 2811 | } else if (inst.deaths == 0) { | |
| 2812 | std.debug.warn("{} ty={} src={}:{}:{}\n", .{ | |
| 2813 | @tagName(inst.tag), | |
| 2814 | inst.ty, | |
| 2815 | zir_module.subFilePath(), | |
| 2816 | loc.line + 1, | |
| 2817 | loc.column + 1, | |
| 2818 | }); | |
| 2819 | } else { | |
| 2820 | std.debug.warn("{} ty={} deaths={b} src={}:{}:{}\n", .{ | |
| 2821 | @tagName(inst.tag), | |
| 2822 | inst.ty, | |
| 2823 | inst.deaths, | |
| 2824 | zir_module.subFilePath(), | |
| 2825 | loc.line + 1, | |
| 2826 | loc.column + 1, | |
| 2827 | }); | |
| 2828 | } | |
| 2829 | } |
src-self-hosted/codegen.zig+51-7| ... | ... | @@ -50,7 +50,7 @@ pub fn generateSymbol( |
| 50 | 50 | |
| 51 | 51 | switch (typed_value.ty.zigTypeTag()) { |
| 52 | 52 | .Fn => { |
| 53 | switch (bin_file.options.target.cpu.arch) { | |
| 53 | switch (bin_file.base.options.target.cpu.arch) { | |
| 54 | 54 | //.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code), |
| 55 | 55 | //.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code), |
| 56 | 56 | //.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code), |
| ... | ... | @@ -143,7 +143,7 @@ pub fn generateSymbol( |
| 143 | 143 | // TODO handle the dependency of this symbol on the decl's vaddr. |
| 144 | 144 | // If the decl changes vaddr, then this symbol needs to get regenerated. |
| 145 | 145 | const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value; |
| 146 | const endian = bin_file.options.target.cpu.arch.endian(); | |
| 146 | const endian = bin_file.base.options.target.cpu.arch.endian(); | |
| 147 | 147 | switch (bin_file.ptr_width) { |
| 148 | 148 | .p32 => { |
| 149 | 149 | try code.resize(4); |
| ... | ... | @@ -166,7 +166,7 @@ pub fn generateSymbol( |
| 166 | 166 | }; |
| 167 | 167 | }, |
| 168 | 168 | .Int => { |
| 169 | const info = typed_value.ty.intInfo(bin_file.options.target); | |
| 169 | const info = typed_value.ty.intInfo(bin_file.base.options.target); | |
| 170 | 170 | if (info.bits == 8 and !info.signed) { |
| 171 | 171 | const x = typed_value.val.toUnsignedInt(); |
| 172 | 172 | try code.append(@intCast(u8, x)); |
| ... | ... | @@ -230,6 +230,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 230 | 230 | unreach, |
| 231 | 231 | /// No more references to this value remain. |
| 232 | 232 | dead, |
| 233 | /// The value is undefined. | |
| 234 | undef, | |
| 233 | 235 | /// A pointer-sized integer that fits in a register. |
| 234 | 236 | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 235 | 237 | immediate: u64, |
| ... | ... | @@ -282,6 +284,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 282 | 284 | .compare_flags_signed, |
| 283 | 285 | .ptr_stack_offset, |
| 284 | 286 | .ptr_embedded_in_code, |
| 287 | .undef, | |
| 285 | 288 | => false, |
| 286 | 289 | |
| 287 | 290 | .register, |
| ... | ... | @@ -360,7 +363,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 360 | 363 | |
| 361 | 364 | var function = Self{ |
| 362 | 365 | .gpa = bin_file.allocator, |
| 363 | .target = &bin_file.options.target, | |
| 366 | .target = &bin_file.base.options.target, | |
| 364 | 367 | .bin_file = bin_file, |
| 365 | 368 | .mod_fn = module_fn, |
| 366 | 369 | .code = code, |
| ... | ... | @@ -656,6 +659,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 656 | 659 | }; |
| 657 | 660 | switch (ptr) { |
| 658 | 661 | .none => unreachable, |
| 662 | .undef => unreachable, | |
| 659 | 663 | .unreach => unreachable, |
| 660 | 664 | .dead => unreachable, |
| 661 | 665 | .compare_flags_unsigned => unreachable, |
| ... | ... | @@ -687,6 +691,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 687 | 691 | const elem_ty = inst.rhs.ty; |
| 688 | 692 | switch (ptr) { |
| 689 | 693 | .none => unreachable, |
| 694 | .undef => unreachable, | |
| 690 | 695 | .unreach => unreachable, |
| 691 | 696 | .dead => unreachable, |
| 692 | 697 | .compare_flags_unsigned => unreachable, |
| ... | ... | @@ -798,6 +803,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 798 | 803 | fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void { |
| 799 | 804 | switch (dst_mcv) { |
| 800 | 805 | .none => unreachable, |
| 806 | .undef => unreachable, | |
| 801 | 807 | .dead, .unreach, .immediate => unreachable, |
| 802 | 808 | .compare_flags_unsigned => unreachable, |
| 803 | 809 | .compare_flags_signed => unreachable, |
| ... | ... | @@ -806,6 +812,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 806 | 812 | .register => |dst_reg| { |
| 807 | 813 | switch (src_mcv) { |
| 808 | 814 | .none => unreachable, |
| 815 | .undef => try self.genSetReg(src, dst_reg, .undef), | |
| 809 | 816 | .dead, .unreach => unreachable, |
| 810 | 817 | .ptr_stack_offset => unreachable, |
| 811 | 818 | .ptr_embedded_in_code => unreachable, |
| ... | ... | @@ -905,11 +912,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 905 | 912 | return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); |
| 906 | 913 | }, |
| 907 | 914 | .ptr_stack_offset => { |
| 908 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset", .{}); | |
| 915 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); | |
| 909 | 916 | }, |
| 910 | 917 | .ptr_embedded_in_code => { |
| 911 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code", .{}); | |
| 918 | return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); | |
| 912 | 919 | }, |
| 920 | .undef => unreachable, | |
| 913 | 921 | .immediate => unreachable, |
| 914 | 922 | .unreach => unreachable, |
| 915 | 923 | .dead => unreachable, |
| ... | ... | @@ -966,6 +974,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 966 | 974 | .stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset }, |
| 967 | 975 | .embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset }, |
| 968 | 976 | .memory => |vaddr| return MCValue{ .immediate = vaddr }, |
| 977 | ||
| 978 | .undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}), | |
| 969 | 979 | } |
| 970 | 980 | } |
| 971 | 981 | |
| ... | ... | @@ -1243,6 +1253,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1243 | 1253 | .ptr_stack_offset => unreachable, |
| 1244 | 1254 | .ptr_embedded_in_code => unreachable, |
| 1245 | 1255 | .unreach, .none => return, // Nothing to do. |
| 1256 | .undef => { | |
| 1257 | if (!self.wantSafety()) | |
| 1258 | return; // The already existing value will do just fine. | |
| 1259 | // TODO Upgrade this to a memset call when we have that available. | |
| 1260 | return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa }); | |
| 1261 | }, | |
| 1246 | 1262 | .compare_flags_unsigned => |op| { |
| 1247 | 1263 | return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{}); |
| 1248 | 1264 | }, |
| ... | ... | @@ -1250,6 +1266,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1250 | 1266 | return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{}); |
| 1251 | 1267 | }, |
| 1252 | 1268 | .immediate => |x_big| { |
| 1269 | if (ty.abiSize(self.target.*) != 4) { | |
| 1270 | // TODO after fixing this, need to update the undef case above | |
| 1271 | return self.fail(src, "TODO implement set non 4 abi size stack variable with immediate", .{}); | |
| 1272 | } | |
| 1253 | 1273 | try self.code.ensureCapacity(self.code.items.len + 7); |
| 1254 | 1274 | if (x_big <= math.maxInt(u32)) { |
| 1255 | 1275 | const x = @intCast(u32, x_big); |
| ... | ... | @@ -1311,6 +1331,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1311 | 1331 | .ptr_stack_offset => unreachable, |
| 1312 | 1332 | .ptr_embedded_in_code => unreachable, |
| 1313 | 1333 | .unreach, .none => return, // Nothing to do. |
| 1334 | .undef => { | |
| 1335 | if (!self.wantSafety()) | |
| 1336 | return; // The already existing value will do just fine. | |
| 1337 | // Write the debug undefined value. | |
| 1338 | switch (reg.size()) { | |
| 1339 | 8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }), | |
| 1340 | 16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }), | |
| 1341 | 32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }), | |
| 1342 | 64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), | |
| 1343 | else => unreachable, | |
| 1344 | } | |
| 1345 | }, | |
| 1314 | 1346 | .compare_flags_unsigned => |op| { |
| 1315 | 1347 | try self.code.ensureCapacity(self.code.items.len + 3); |
| 1316 | 1348 | self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 }); |
| ... | ... | @@ -1471,7 +1503,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1471 | 1503 | // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with |
| 1472 | 1504 | // this instruction. |
| 1473 | 1505 | const id3 = @truncate(u3, reg.id()); |
| 1474 | std.debug.assert(id3 != 4 and id3 != 5); | |
| 1506 | assert(id3 != 4 and id3 != 5); | |
| 1475 | 1507 | |
| 1476 | 1508 | // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue. |
| 1477 | 1509 | try self.genSetReg(src, reg, MCValue{ .immediate = x }); |
| ... | ... | @@ -1580,6 +1612,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1580 | 1612 | } |
| 1581 | 1613 | |
| 1582 | 1614 | fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue { |
| 1615 | if (typed_value.val.isUndef()) | |
| 1616 | return MCValue.undef; | |
| 1583 | 1617 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); |
| 1584 | 1618 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); |
| 1585 | 1619 | switch (typed_value.ty.zigTypeTag()) { |
| ... | ... | @@ -1691,6 +1725,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1691 | 1725 | return result; |
| 1692 | 1726 | } |
| 1693 | 1727 | |
| 1728 | /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. | |
| 1729 | fn wantSafety(self: *Self) bool { | |
| 1730 | return switch (self.bin_file.base.options.optimize_mode) { | |
| 1731 | .Debug => true, | |
| 1732 | .ReleaseSafe => true, | |
| 1733 | .ReleaseFast => false, | |
| 1734 | .ReleaseSmall => false, | |
| 1735 | }; | |
| 1736 | } | |
| 1737 | ||
| 1694 | 1738 | fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } { |
| 1695 | 1739 | @setCold(true); |
| 1696 | 1740 | assert(self.err_msg == null); |
src-self-hosted/ir.zig+1-2| ... | ... | @@ -165,8 +165,7 @@ pub const Inst = struct { |
| 165 | 165 | |
| 166 | 166 | /// Returns `null` if runtime-known. |
| 167 | 167 | pub fn value(base: *Inst) ?Value { |
| 168 | if (base.ty.onePossibleValue()) | |
| 169 | return Value.initTag(.the_one_possible_value); | |
| 168 | if (base.ty.onePossibleValue()) |opv| return opv; | |
| 170 | 169 | |
| 171 | 170 | const inst = base.cast(Constant) orelse return null; |
| 172 | 171 | return inst.val; |
src-self-hosted/link.zig+63-57| ... | ... | @@ -16,6 +16,7 @@ pub const Options = struct { |
| 16 | 16 | output_mode: std.builtin.OutputMode, |
| 17 | 17 | link_mode: std.builtin.LinkMode, |
| 18 | 18 | object_format: std.builtin.ObjectFormat, |
| 19 | optimize_mode: std.builtin.Mode, | |
| 19 | 20 | /// Used for calculating how much space to reserve for symbols in case the binary file |
| 20 | 21 | /// does not already have a symbol table. |
| 21 | 22 | symbol_count_hint: u64 = 32, |
| ... | ... | @@ -66,6 +67,7 @@ pub fn writeFilePath( |
| 66 | 67 | .link_mode = module.link_mode, |
| 67 | 68 | .object_format = module.object_format, |
| 68 | 69 | .symbol_count_hint = module.decls.items.len, |
| 70 | .optimize_mode = module.optimize_mode, | |
| 69 | 71 | }; |
| 70 | 72 | const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) }); |
| 71 | 73 | defer af.deinit(); |
| ... | ... | @@ -88,9 +90,12 @@ pub fn writeFilePath( |
| 88 | 90 | |
| 89 | 91 | fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C { |
| 90 | 92 | return File.C{ |
| 93 | .base = .{ | |
| 94 | .tag = .c, | |
| 95 | .options = options, | |
| 96 | }, | |
| 91 | 97 | .allocator = allocator, |
| 92 | 98 | .file = file, |
| 93 | .options = options, | |
| 94 | 99 | .main = std.ArrayList(u8).init(allocator), |
| 95 | 100 | .header = std.ArrayList(u8).init(allocator), |
| 96 | 101 | .constants = std.ArrayList(u8).init(allocator), |
| ... | ... | @@ -114,6 +119,8 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File |
| 114 | 119 | |
| 115 | 120 | pub const File = struct { |
| 116 | 121 | tag: Tag, |
| 122 | options: Options, | |
| 123 | ||
| 117 | 124 | pub fn cast(base: *File, comptime T: type) ?*T { |
| 118 | 125 | if (base.tag != T.base_tag) |
| 119 | 126 | return null; |
| ... | ... | @@ -123,47 +130,47 @@ pub const File = struct { |
| 123 | 130 | |
| 124 | 131 | pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void { |
| 125 | 132 | switch (base.tag) { |
| 126 | .Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path), | |
| 127 | .C => {}, | |
| 133 | .elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path), | |
| 134 | .c => {}, | |
| 128 | 135 | } |
| 129 | 136 | } |
| 130 | 137 | |
| 131 | 138 | pub fn makeExecutable(base: *File) !void { |
| 132 | 139 | switch (base.tag) { |
| 133 | .Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(), | |
| 134 | .C => unreachable, | |
| 140 | .elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(), | |
| 141 | .c => unreachable, | |
| 135 | 142 | } |
| 136 | 143 | } |
| 137 | 144 | |
| 138 | 145 | pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void { |
| 139 | 146 | switch (base.tag) { |
| 140 | .Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), | |
| 141 | .C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), | |
| 147 | .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), | |
| 148 | .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), | |
| 142 | 149 | } |
| 143 | 150 | } |
| 144 | 151 | |
| 145 | 152 | pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void { |
| 146 | 153 | switch (base.tag) { |
| 147 | .Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), | |
| 148 | .C => {}, | |
| 154 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), | |
| 155 | .c => {}, | |
| 149 | 156 | } |
| 150 | 157 | } |
| 151 | 158 | |
| 152 | 159 | pub fn deinit(base: *File) void { |
| 153 | 160 | switch (base.tag) { |
| 154 | .Elf => @fieldParentPtr(Elf, "base", base).deinit(), | |
| 155 | .C => @fieldParentPtr(C, "base", base).deinit(), | |
| 161 | .elf => @fieldParentPtr(Elf, "base", base).deinit(), | |
| 162 | .c => @fieldParentPtr(C, "base", base).deinit(), | |
| 156 | 163 | } |
| 157 | 164 | } |
| 158 | 165 | |
| 159 | 166 | pub fn destroy(base: *File) void { |
| 160 | 167 | switch (base.tag) { |
| 161 | .Elf => { | |
| 168 | .elf => { | |
| 162 | 169 | const parent = @fieldParentPtr(Elf, "base", base); |
| 163 | 170 | parent.deinit(); |
| 164 | 171 | parent.allocator.destroy(parent); |
| 165 | 172 | }, |
| 166 | .C => { | |
| 173 | .c => { | |
| 167 | 174 | const parent = @fieldParentPtr(C, "base", base); |
| 168 | 175 | parent.deinit(); |
| 169 | 176 | parent.allocator.destroy(parent); |
| ... | ... | @@ -173,29 +180,22 @@ pub const File = struct { |
| 173 | 180 | |
| 174 | 181 | pub fn flush(base: *File) !void { |
| 175 | 182 | try switch (base.tag) { |
| 176 | .Elf => @fieldParentPtr(Elf, "base", base).flush(), | |
| 177 | .C => @fieldParentPtr(C, "base", base).flush(), | |
| 183 | .elf => @fieldParentPtr(Elf, "base", base).flush(), | |
| 184 | .c => @fieldParentPtr(C, "base", base).flush(), | |
| 178 | 185 | }; |
| 179 | 186 | } |
| 180 | 187 | |
| 181 | 188 | pub fn freeDecl(base: *File, decl: *Module.Decl) void { |
| 182 | 189 | switch (base.tag) { |
| 183 | .Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), | |
| 184 | .C => unreachable, | |
| 190 | .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), | |
| 191 | .c => unreachable, | |
| 185 | 192 | } |
| 186 | 193 | } |
| 187 | 194 | |
| 188 | 195 | pub fn errorFlags(base: *File) ErrorFlags { |
| 189 | 196 | return switch (base.tag) { |
| 190 | .Elf => @fieldParentPtr(Elf, "base", base).error_flags, | |
| 191 | .C => return .{ .no_entry_point_found = false }, | |
| 192 | }; | |
| 193 | } | |
| 194 | ||
| 195 | pub fn options(base: *File) Options { | |
| 196 | return switch (base.tag) { | |
| 197 | .Elf => @fieldParentPtr(Elf, "base", base).options, | |
| 198 | .C => @fieldParentPtr(C, "base", base).options, | |
| 197 | .elf => @fieldParentPtr(Elf, "base", base).error_flags, | |
| 198 | .c => return .{ .no_entry_point_found = false }, | |
| 199 | 199 | }; |
| 200 | 200 | } |
| 201 | 201 | |
| ... | ... | @@ -207,14 +207,14 @@ pub const File = struct { |
| 207 | 207 | exports: []const *Module.Export, |
| 208 | 208 | ) !void { |
| 209 | 209 | switch (base.tag) { |
| 210 | .Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports), | |
| 211 | .C => return {}, | |
| 210 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports), | |
| 211 | .c => return {}, | |
| 212 | 212 | } |
| 213 | 213 | } |
| 214 | 214 | |
| 215 | 215 | pub const Tag = enum { |
| 216 | Elf, | |
| 217 | C, | |
| 216 | elf, | |
| 217 | c, | |
| 218 | 218 | }; |
| 219 | 219 | |
| 220 | 220 | pub const ErrorFlags = struct { |
| ... | ... | @@ -222,15 +222,15 @@ pub const File = struct { |
| 222 | 222 | }; |
| 223 | 223 | |
| 224 | 224 | pub const C = struct { |
| 225 | pub const base_tag: Tag = .C; | |
| 226 | base: File = File{ .tag = base_tag }, | |
| 225 | pub const base_tag: Tag = .c; | |
| 226 | ||
| 227 | base: File, | |
| 227 | 228 | |
| 228 | 229 | allocator: *Allocator, |
| 229 | 230 | header: std.ArrayList(u8), |
| 230 | 231 | constants: std.ArrayList(u8), |
| 231 | 232 | main: std.ArrayList(u8), |
| 232 | 233 | file: ?fs.File, |
| 233 | options: Options, | |
| 234 | 234 | called: std.StringHashMap(void), |
| 235 | 235 | need_stddef: bool = false, |
| 236 | 236 | need_stdint: bool = false, |
| ... | ... | @@ -294,13 +294,13 @@ pub const File = struct { |
| 294 | 294 | }; |
| 295 | 295 | |
| 296 | 296 | pub const Elf = struct { |
| 297 | pub const base_tag: Tag = .Elf; | |
| 298 | base: File = File{ .tag = base_tag }, | |
| 297 | pub const base_tag: Tag = .elf; | |
| 298 | ||
| 299 | base: File, | |
| 299 | 300 | |
| 300 | 301 | allocator: *Allocator, |
| 301 | 302 | file: ?fs.File, |
| 302 | 303 | owns_file_handle: bool, |
| 303 | options: Options, | |
| 304 | 304 | ptr_width: enum { p32, p64 }, |
| 305 | 305 | |
| 306 | 306 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| ... | ... | @@ -460,13 +460,13 @@ pub const File = struct { |
| 460 | 460 | self.file = try dir.createFile(sub_path, .{ |
| 461 | 461 | .truncate = false, |
| 462 | 462 | .read = true, |
| 463 | .mode = determineMode(self.options), | |
| 463 | .mode = determineMode(self.base.options), | |
| 464 | 464 | }); |
| 465 | 465 | } |
| 466 | 466 | |
| 467 | 467 | /// Returns end pos of collision, if any. |
| 468 | 468 | fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 469 | const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32; | |
| 469 | const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32; | |
| 470 | 470 | const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr); |
| 471 | 471 | if (start < ehdr_size) |
| 472 | 472 | return ehdr_size; |
| ... | ... | @@ -569,7 +569,7 @@ pub const File = struct { |
| 569 | 569 | }; |
| 570 | 570 | if (self.phdr_load_re_index == null) { |
| 571 | 571 | self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len); |
| 572 | const file_size = self.options.program_code_size_hint; | |
| 572 | const file_size = self.base.options.program_code_size_hint; | |
| 573 | 573 | const p_align = 0x1000; |
| 574 | 574 | const off = self.findFreeSpace(file_size, p_align); |
| 575 | 575 | std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| ... | ... | @@ -588,7 +588,7 @@ pub const File = struct { |
| 588 | 588 | } |
| 589 | 589 | if (self.phdr_got_index == null) { |
| 590 | 590 | self.phdr_got_index = @intCast(u16, self.program_headers.items.len); |
| 591 | const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint; | |
| 591 | const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint; | |
| 592 | 592 | // We really only need ptr alignment but since we are using PROGBITS, linux requires |
| 593 | 593 | // page align. |
| 594 | 594 | const p_align = 0x1000; |
| ... | ... | @@ -671,7 +671,7 @@ pub const File = struct { |
| 671 | 671 | self.symtab_section_index = @intCast(u16, self.sections.items.len); |
| 672 | 672 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| 673 | 673 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 674 | const file_size = self.options.symbol_count_hint * each_size; | |
| 674 | const file_size = self.base.options.symbol_count_hint * each_size; | |
| 675 | 675 | const off = self.findFreeSpace(file_size, min_align); |
| 676 | 676 | std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size }); |
| 677 | 677 | |
| ... | ... | @@ -726,7 +726,7 @@ pub const File = struct { |
| 726 | 726 | |
| 727 | 727 | /// Commit pending changes and write headers. |
| 728 | 728 | pub fn flush(self: *Elf) !void { |
| 729 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 729 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 730 | 730 | |
| 731 | 731 | // Unfortunately these have to be buffered and done at the end because ELF does not allow |
| 732 | 732 | // mixing local and global symbols within a symbol table. |
| ... | ... | @@ -845,7 +845,7 @@ pub const File = struct { |
| 845 | 845 | } |
| 846 | 846 | self.shdr_table_dirty = false; |
| 847 | 847 | } |
| 848 | if (self.entry_addr == null and self.options.output_mode == .Exe) { | |
| 848 | if (self.entry_addr == null and self.base.options.output_mode == .Exe) { | |
| 849 | 849 | std.log.debug(.link, "no_entry_point_found = true\n", .{}); |
| 850 | 850 | self.error_flags.no_entry_point_found = true; |
| 851 | 851 | } else { |
| ... | ... | @@ -875,7 +875,7 @@ pub const File = struct { |
| 875 | 875 | }; |
| 876 | 876 | index += 1; |
| 877 | 877 | |
| 878 | const endian = self.options.target.cpu.arch.endian(); | |
| 878 | const endian = self.base.options.target.cpu.arch.endian(); | |
| 879 | 879 | hdr_buf[index] = switch (endian) { |
| 880 | 880 | .Little => elf.ELFDATA2LSB, |
| 881 | 881 | .Big => elf.ELFDATA2MSB, |
| ... | ... | @@ -893,10 +893,10 @@ pub const File = struct { |
| 893 | 893 | |
| 894 | 894 | assert(index == 16); |
| 895 | 895 | |
| 896 | const elf_type = switch (self.options.output_mode) { | |
| 896 | const elf_type = switch (self.base.options.output_mode) { | |
| 897 | 897 | .Exe => elf.ET.EXEC, |
| 898 | 898 | .Obj => elf.ET.REL, |
| 899 | .Lib => switch (self.options.link_mode) { | |
| 899 | .Lib => switch (self.base.options.link_mode) { | |
| 900 | 900 | .Static => elf.ET.REL, |
| 901 | 901 | .Dynamic => elf.ET.DYN, |
| 902 | 902 | }, |
| ... | ... | @@ -904,7 +904,7 @@ pub const File = struct { |
| 904 | 904 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian); |
| 905 | 905 | index += 2; |
| 906 | 906 | |
| 907 | const machine = self.options.target.cpu.arch.toElfMachine(); | |
| 907 | const machine = self.base.options.target.cpu.arch.toElfMachine(); | |
| 908 | 908 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian); |
| 909 | 909 | index += 2; |
| 910 | 910 | |
| ... | ... | @@ -1216,7 +1216,7 @@ pub const File = struct { |
| 1216 | 1216 | }, |
| 1217 | 1217 | }; |
| 1218 | 1218 | |
| 1219 | const required_alignment = typed_value.ty.abiAlignment(self.options.target); | |
| 1219 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); | |
| 1220 | 1220 | |
| 1221 | 1221 | const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) { |
| 1222 | 1222 | .Fn => elf.STT_FUNC, |
| ... | ... | @@ -1361,9 +1361,9 @@ pub const File = struct { |
| 1361 | 1361 | } |
| 1362 | 1362 | |
| 1363 | 1363 | fn writeProgHeader(self: *Elf, index: usize) !void { |
| 1364 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1364 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1365 | 1365 | const offset = self.program_headers.items[index].p_offset; |
| 1366 | switch (self.options.target.cpu.arch.ptrBitWidth()) { | |
| 1366 | switch (self.base.options.target.cpu.arch.ptrBitWidth()) { | |
| 1367 | 1367 | 32 => { |
| 1368 | 1368 | var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])}; |
| 1369 | 1369 | if (foreign_endian) { |
| ... | ... | @@ -1383,9 +1383,9 @@ pub const File = struct { |
| 1383 | 1383 | } |
| 1384 | 1384 | |
| 1385 | 1385 | fn writeSectHeader(self: *Elf, index: usize) !void { |
| 1386 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1386 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1387 | 1387 | const offset = self.sections.items[index].sh_offset; |
| 1388 | switch (self.options.target.cpu.arch.ptrBitWidth()) { | |
| 1388 | switch (self.base.options.target.cpu.arch.ptrBitWidth()) { | |
| 1389 | 1389 | 32 => { |
| 1390 | 1390 | var shdr: [1]elf.Elf32_Shdr = undefined; |
| 1391 | 1391 | shdr[0] = sectHeaderTo32(self.sections.items[index]); |
| ... | ... | @@ -1433,7 +1433,7 @@ pub const File = struct { |
| 1433 | 1433 | |
| 1434 | 1434 | self.offset_table_count_dirty = false; |
| 1435 | 1435 | } |
| 1436 | const endian = self.options.target.cpu.arch.endian(); | |
| 1436 | const endian = self.base.options.target.cpu.arch.endian(); | |
| 1437 | 1437 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 1438 | 1438 | switch (self.ptr_width) { |
| 1439 | 1439 | .p32 => { |
| ... | ... | @@ -1475,7 +1475,7 @@ pub const File = struct { |
| 1475 | 1475 | syms_sect.sh_size = needed_size; // anticipating adding the global symbols later |
| 1476 | 1476 | self.shdr_table_dirty = true; // TODO look into only writing one section |
| 1477 | 1477 | } |
| 1478 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1478 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1479 | 1479 | switch (self.ptr_width) { |
| 1480 | 1480 | .p32 => { |
| 1481 | 1481 | var sym = [1]elf.Elf32_Sym{ |
| ... | ... | @@ -1511,7 +1511,7 @@ pub const File = struct { |
| 1511 | 1511 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 1512 | 1512 | .p64 => @sizeOf(elf.Elf64_Sym), |
| 1513 | 1513 | }; |
| 1514 | const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1514 | const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian(); | |
| 1515 | 1515 | const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size; |
| 1516 | 1516 | switch (self.ptr_width) { |
| 1517 | 1517 | .p32 => { |
| ... | ... | @@ -1577,9 +1577,12 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !Fi |
| 1577 | 1577 | } |
| 1578 | 1578 | |
| 1579 | 1579 | var self: File.Elf = .{ |
| 1580 | .base = .{ | |
| 1581 | .tag = .elf, | |
| 1582 | .options = options, | |
| 1583 | }, | |
| 1580 | 1584 | .allocator = allocator, |
| 1581 | 1585 | .file = file, |
| 1582 | .options = options, | |
| 1583 | 1586 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 1584 | 1587 | 32 => .p32, |
| 1585 | 1588 | 64 => .p64, |
| ... | ... | @@ -1637,10 +1640,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Fil |
| 1637 | 1640 | .raw => return error.IncrFailed, |
| 1638 | 1641 | } |
| 1639 | 1642 | var self: File.Elf = .{ |
| 1643 | .base = .{ | |
| 1644 | .tag = .elf, | |
| 1645 | .options = options, | |
| 1646 | }, | |
| 1640 | 1647 | .allocator = allocator, |
| 1641 | 1648 | .file = file, |
| 1642 | 1649 | .owns_file_handle = false, |
| 1643 | .options = options, | |
| 1644 | 1650 | .ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) { |
| 1645 | 1651 | 32 => .p32, |
| 1646 | 1652 | 64 => .p64, |
src-self-hosted/type.zig+22-11| ... | ... | @@ -1653,7 +1653,7 @@ pub const Type = extern union { |
| 1653 | 1653 | }; |
| 1654 | 1654 | } |
| 1655 | 1655 | |
| 1656 | pub fn onePossibleValue(self: Type) bool { | |
| 1656 | pub fn onePossibleValue(self: Type) ?Value { | |
| 1657 | 1657 | var ty = self; |
| 1658 | 1658 | while (true) switch (ty.tag()) { |
| 1659 | 1659 | .f16, |
| ... | ... | @@ -1692,21 +1692,32 @@ pub const Type = extern union { |
| 1692 | 1692 | .single_const_pointer_to_comptime_int, |
| 1693 | 1693 | .array_u8_sentinel_0, |
| 1694 | 1694 | .const_slice_u8, |
| 1695 | => return false, | |
| 1696 | ||
| 1697 | 1695 | .c_void, |
| 1698 | .void, | |
| 1699 | .noreturn, | |
| 1700 | .@"null", | |
| 1701 | .@"undefined", | |
| 1702 | => return true, | |
| 1696 | => return null, | |
| 1697 | ||
| 1698 | .void => return Value.initTag(.void_value), | |
| 1699 | .noreturn => return Value.initTag(.unreachable_value), | |
| 1700 | .@"null" => return Value.initTag(.null_value), | |
| 1701 | .@"undefined" => return Value.initTag(.undef), | |
| 1703 | 1702 | |
| 1704 | .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0, | |
| 1705 | .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0, | |
| 1703 | .int_unsigned => { | |
| 1704 | if (ty.cast(Payload.IntUnsigned).?.bits == 0) { | |
| 1705 | return Value.initTag(.zero); | |
| 1706 | } else { | |
| 1707 | return null; | |
| 1708 | } | |
| 1709 | }, | |
| 1710 | .int_signed => { | |
| 1711 | if (ty.cast(Payload.IntSigned).?.bits == 0) { | |
| 1712 | return Value.initTag(.zero); | |
| 1713 | } else { | |
| 1714 | return null; | |
| 1715 | } | |
| 1716 | }, | |
| 1706 | 1717 | .array => { |
| 1707 | 1718 | const array = ty.cast(Payload.Array).?; |
| 1708 | 1719 | if (array.len == 0) |
| 1709 | return true; | |
| 1720 | return Value.initTag(.empty_array); | |
| 1710 | 1721 | ty = array.elem_type; |
| 1711 | 1722 | continue; |
| 1712 | 1723 | }, |
src-self-hosted/value.zig+50-22| ... | ... | @@ -63,7 +63,9 @@ pub const Value = extern union { |
| 63 | 63 | |
| 64 | 64 | undef, |
| 65 | 65 | zero, |
| 66 | the_one_possible_value, // when the type only has one possible value | |
| 66 | void_value, | |
| 67 | unreachable_value, | |
| 68 | empty_array, | |
| 67 | 69 | null_value, |
| 68 | 70 | bool_true, |
| 69 | 71 | bool_false, // See last_no_payload_tag below. |
| ... | ... | @@ -164,7 +166,9 @@ pub const Value = extern union { |
| 164 | 166 | .const_slice_u8_type, |
| 165 | 167 | .undef, |
| 166 | 168 | .zero, |
| 167 | .the_one_possible_value, | |
| 169 | .void_value, | |
| 170 | .unreachable_value, | |
| 171 | .empty_array, | |
| 168 | 172 | .null_value, |
| 169 | 173 | .bool_true, |
| 170 | 174 | .bool_false, |
| ... | ... | @@ -285,7 +289,8 @@ pub const Value = extern union { |
| 285 | 289 | .null_value => return out_stream.writeAll("null"), |
| 286 | 290 | .undef => return out_stream.writeAll("undefined"), |
| 287 | 291 | .zero => return out_stream.writeAll("0"), |
| 288 | .the_one_possible_value => return out_stream.writeAll("(one possible value)"), | |
| 292 | .void_value => return out_stream.writeAll("{}"), | |
| 293 | .unreachable_value => return out_stream.writeAll("unreachable"), | |
| 289 | 294 | .bool_true => return out_stream.writeAll("true"), |
| 290 | 295 | .bool_false => return out_stream.writeAll("false"), |
| 291 | 296 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), |
| ... | ... | @@ -312,6 +317,7 @@ pub const Value = extern union { |
| 312 | 317 | try out_stream.print("&[{}] ", .{elem_ptr.index}); |
| 313 | 318 | val = elem_ptr.array_ptr; |
| 314 | 319 | }, |
| 320 | .empty_array => return out_stream.writeAll(".{}"), | |
| 315 | 321 | .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), |
| 316 | 322 | .repeated => { |
| 317 | 323 | try out_stream.writeAll("(repeated) "); |
| ... | ... | @@ -388,7 +394,9 @@ pub const Value = extern union { |
| 388 | 394 | |
| 389 | 395 | .undef, |
| 390 | 396 | .zero, |
| 391 | .the_one_possible_value, | |
| 397 | .void_value, | |
| 398 | .unreachable_value, | |
| 399 | .empty_array, | |
| 392 | 400 | .bool_true, |
| 393 | 401 | .bool_false, |
| 394 | 402 | .null_value, |
| ... | ... | @@ -460,15 +468,18 @@ pub const Value = extern union { |
| 460 | 468 | .decl_ref, |
| 461 | 469 | .elem_ptr, |
| 462 | 470 | .bytes, |
| 463 | .undef, | |
| 464 | 471 | .repeated, |
| 465 | 472 | .float_16, |
| 466 | 473 | .float_32, |
| 467 | 474 | .float_64, |
| 468 | 475 | .float_128, |
| 476 | .void_value, | |
| 477 | .unreachable_value, | |
| 478 | .empty_array, | |
| 469 | 479 | => unreachable, |
| 470 | 480 | |
| 471 | .the_one_possible_value, // An integer with one possible value is always zero. | |
| 481 | .undef => unreachable, | |
| 482 | ||
| 472 | 483 | .zero, |
| 473 | 484 | .bool_false, |
| 474 | 485 | => return BigIntMutable.init(&space.limbs, 0).toConst(), |
| ... | ... | @@ -532,16 +543,19 @@ pub const Value = extern union { |
| 532 | 543 | .decl_ref, |
| 533 | 544 | .elem_ptr, |
| 534 | 545 | .bytes, |
| 535 | .undef, | |
| 536 | 546 | .repeated, |
| 537 | 547 | .float_16, |
| 538 | 548 | .float_32, |
| 539 | 549 | .float_64, |
| 540 | 550 | .float_128, |
| 551 | .void_value, | |
| 552 | .unreachable_value, | |
| 553 | .empty_array, | |
| 541 | 554 | => unreachable, |
| 542 | 555 | |
| 556 | .undef => unreachable, | |
| 557 | ||
| 543 | 558 | .zero, |
| 544 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 545 | 559 | .bool_false, |
| 546 | 560 | => return 0, |
| 547 | 561 | |
| ... | ... | @@ -570,7 +584,7 @@ pub const Value = extern union { |
| 570 | 584 | .float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val), |
| 571 | 585 | .float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val), |
| 572 | 586 | |
| 573 | .zero, .the_one_possible_value => 0, | |
| 587 | .zero => 0, | |
| 574 | 588 | .int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int), |
| 575 | 589 | // .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int), |
| 576 | 590 | .int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"), |
| ... | ... | @@ -637,9 +651,11 @@ pub const Value = extern union { |
| 637 | 651 | .float_32, |
| 638 | 652 | .float_64, |
| 639 | 653 | .float_128, |
| 654 | .void_value, | |
| 655 | .unreachable_value, | |
| 656 | .empty_array, | |
| 640 | 657 | => unreachable, |
| 641 | 658 | |
| 642 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 643 | 659 | .zero, |
| 644 | 660 | .bool_false, |
| 645 | 661 | => return 0, |
| ... | ... | @@ -714,11 +730,13 @@ pub const Value = extern union { |
| 714 | 730 | .float_32, |
| 715 | 731 | .float_64, |
| 716 | 732 | .float_128, |
| 733 | .void_value, | |
| 734 | .unreachable_value, | |
| 735 | .empty_array, | |
| 717 | 736 | => unreachable, |
| 718 | 737 | |
| 719 | 738 | .zero, |
| 720 | 739 | .undef, |
| 721 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 722 | 740 | .bool_false, |
| 723 | 741 | => return true, |
| 724 | 742 | |
| ... | ... | @@ -797,13 +815,13 @@ pub const Value = extern union { |
| 797 | 815 | // return Value.initPayload(&res_payload.base).copy(allocator); |
| 798 | 816 | }, |
| 799 | 817 | 32 => { |
| 800 | var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)}; | |
| 818 | var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) }; | |
| 801 | 819 | if (!self.eql(Value.initPayload(&res_payload.base))) |
| 802 | 820 | return error.Overflow; |
| 803 | 821 | return Value.initPayload(&res_payload.base).copy(allocator); |
| 804 | 822 | }, |
| 805 | 823 | 64 => { |
| 806 | var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)}; | |
| 824 | var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) }; | |
| 807 | 825 | if (!self.eql(Value.initPayload(&res_payload.base))) |
| 808 | 826 | return error.Overflow; |
| 809 | 827 | return Value.initPayload(&res_payload.base).copy(allocator); |
| ... | ... | @@ -875,7 +893,9 @@ pub const Value = extern union { |
| 875 | 893 | .int_i64, |
| 876 | 894 | .int_big_positive, |
| 877 | 895 | .int_big_negative, |
| 878 | .the_one_possible_value, | |
| 896 | .empty_array, | |
| 897 | .void_value, | |
| 898 | .unreachable_value, | |
| 879 | 899 | => unreachable, |
| 880 | 900 | |
| 881 | 901 | .zero => false, |
| ... | ... | @@ -939,10 +959,12 @@ pub const Value = extern union { |
| 939 | 959 | .bytes, |
| 940 | 960 | .repeated, |
| 941 | 961 | .undef, |
| 962 | .void_value, | |
| 963 | .unreachable_value, | |
| 964 | .empty_array, | |
| 942 | 965 | => unreachable, |
| 943 | 966 | |
| 944 | 967 | .zero, |
| 945 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 946 | 968 | .bool_false, |
| 947 | 969 | => .eq, |
| 948 | 970 | |
| ... | ... | @@ -964,8 +986,8 @@ pub const Value = extern union { |
| 964 | 986 | pub fn order(lhs: Value, rhs: Value) std.math.Order { |
| 965 | 987 | const lhs_tag = lhs.tag(); |
| 966 | 988 | const rhs_tag = rhs.tag(); |
| 967 | const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; | |
| 968 | const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; | |
| 989 | const lhs_is_zero = lhs_tag == .zero; | |
| 990 | const rhs_is_zero = rhs_tag == .zero; | |
| 969 | 991 | if (lhs_is_zero) return rhs.orderAgainstZero().invert(); |
| 970 | 992 | if (rhs_is_zero) return lhs.orderAgainstZero(); |
| 971 | 993 | |
| ... | ... | @@ -1071,9 +1093,11 @@ pub const Value = extern union { |
| 1071 | 1093 | .float_32, |
| 1072 | 1094 | .float_64, |
| 1073 | 1095 | .float_128, |
| 1096 | .void_value, | |
| 1097 | .unreachable_value, | |
| 1098 | .empty_array, | |
| 1074 | 1099 | => unreachable, |
| 1075 | 1100 | |
| 1076 | .the_one_possible_value => Value.initTag(.the_one_possible_value), | |
| 1077 | 1101 | .ref_val => self.cast(Payload.RefVal).?.val, |
| 1078 | 1102 | .decl_ref => self.cast(Payload.DeclRef).?.decl.value(), |
| 1079 | 1103 | .elem_ptr => { |
| ... | ... | @@ -1130,7 +1154,6 @@ pub const Value = extern union { |
| 1130 | 1154 | .single_const_pointer_to_comptime_int_type, |
| 1131 | 1155 | .const_slice_u8_type, |
| 1132 | 1156 | .zero, |
| 1133 | .the_one_possible_value, | |
| 1134 | 1157 | .bool_true, |
| 1135 | 1158 | .bool_false, |
| 1136 | 1159 | .null_value, |
| ... | ... | @@ -1147,8 +1170,12 @@ pub const Value = extern union { |
| 1147 | 1170 | .float_32, |
| 1148 | 1171 | .float_64, |
| 1149 | 1172 | .float_128, |
| 1173 | .void_value, | |
| 1174 | .unreachable_value, | |
| 1150 | 1175 | => unreachable, |
| 1151 | 1176 | |
| 1177 | .empty_array => unreachable, // out of bounds array index | |
| 1178 | ||
| 1152 | 1179 | .bytes => { |
| 1153 | 1180 | const int_payload = try allocator.create(Payload.Int_u64); |
| 1154 | 1181 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; |
| ... | ... | @@ -1175,8 +1202,7 @@ pub const Value = extern union { |
| 1175 | 1202 | return self.tag() == .undef; |
| 1176 | 1203 | } |
| 1177 | 1204 | |
| 1178 | /// Valid for all types. Asserts the value is not undefined. | |
| 1179 | /// `.the_one_possible_value` is reported as not null. | |
| 1205 | /// Valid for all types. Asserts the value is not undefined and not unreachable. | |
| 1180 | 1206 | pub fn isNull(self: Value) bool { |
| 1181 | 1207 | return switch (self.tag()) { |
| 1182 | 1208 | .ty, |
| ... | ... | @@ -1221,7 +1247,7 @@ pub const Value = extern union { |
| 1221 | 1247 | .single_const_pointer_to_comptime_int_type, |
| 1222 | 1248 | .const_slice_u8_type, |
| 1223 | 1249 | .zero, |
| 1224 | .the_one_possible_value, | |
| 1250 | .empty_array, | |
| 1225 | 1251 | .bool_true, |
| 1226 | 1252 | .bool_false, |
| 1227 | 1253 | .function, |
| ... | ... | @@ -1238,9 +1264,11 @@ pub const Value = extern union { |
| 1238 | 1264 | .float_32, |
| 1239 | 1265 | .float_64, |
| 1240 | 1266 | .float_128, |
| 1267 | .void_value, | |
| 1241 | 1268 | => false, |
| 1242 | 1269 | |
| 1243 | 1270 | .undef => unreachable, |
| 1271 | .unreachable_value => unreachable, | |
| 1244 | 1272 | .null_value => true, |
| 1245 | 1273 | }; |
| 1246 | 1274 | } |
src-self-hosted/zir.zig+16-1| ... | ... | @@ -742,7 +742,7 @@ pub const Inst = struct { |
| 742 | 742 | .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) }, |
| 743 | 743 | .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) }, |
| 744 | 744 | .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) }, |
| 745 | .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) }, | |
| 745 | .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) }, | |
| 746 | 746 | }; |
| 747 | 747 | } |
| 748 | 748 | }; |
| ... | ... | @@ -1598,6 +1598,21 @@ const EmitZIR = struct { |
| 1598 | 1598 | const decl = decl_ref.decl; |
| 1599 | 1599 | return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl)); |
| 1600 | 1600 | } |
| 1601 | if (typed_value.val.isUndef()) { | |
| 1602 | const as_inst = try self.arena.allocator.create(Inst.BinOp); | |
| 1603 | as_inst.* = .{ | |
| 1604 | .base = .{ | |
| 1605 | .tag = .as, | |
| 1606 | .src = src, | |
| 1607 | }, | |
| 1608 | .positionals = .{ | |
| 1609 | .lhs = (try self.emitType(src, typed_value.ty)).inst, | |
| 1610 | .rhs = (try self.emitPrimitive(src, .@"undefined")).inst, | |
| 1611 | }, | |
| 1612 | .kw_args = .{}, | |
| 1613 | }; | |
| 1614 | return self.emitUnnamedDecl(&as_inst.base); | |
| 1615 | } | |
| 1601 | 1616 | switch (typed_value.ty.zigTypeTag()) { |
| 1602 | 1617 | .Pointer => { |
| 1603 | 1618 | const ptr_elem_type = typed_value.ty.elemType(); |
src-self-hosted/zir_sema.zig+2-1| ... | ... | @@ -882,7 +882,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn |
| 882 | 882 | fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst { |
| 883 | 883 | // incase rhs is 0, simply return lhs without doing any calculations |
| 884 | 884 | // TODO Once division is implemented we should throw an error when dividing by 0. |
| 885 | if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) { | |
| 885 | if (rhs_val.compareWithZero(.eq)) { | |
| 886 | 886 | return mod.constInst(scope, inst.base.src, .{ |
| 887 | 887 | .ty = res_type, |
| 888 | 888 | .val = lhs_val, |
| ... | ... | @@ -1083,6 +1083,7 @@ fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) |
| 1083 | 1083 | |
| 1084 | 1084 | fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { |
| 1085 | 1085 | const b = try mod.requireRuntimeBlock(scope, unreach.base.src); |
| 1086 | // TODO Add compile error for @optimizeFor occurring too late in a scope. | |
| 1086 | 1087 | if (mod.wantSafety(scope)) { |
| 1087 | 1088 | // TODO Once we have a panic function to call, call it here instead of this. |
| 1088 | 1089 | _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint); |