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