authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-28 21:57:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-29 02:29:37-07:00
log4beff80b2f30ad85f2127b1281053b8b25b0cc33
tree9937e9ee95d3060afa021449439caefd6476b5c2
parent2b8e7deeda5d4d0d74f42265a4ddf466f91c8fc2

stage2: codegen handles undefined values

* `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.
48decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},48decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
4949
50optimize_mode: std.builtin.Mode,
51link_error_flags: link.File.ErrorFlags = .{},50link_error_flags: link.File.ErrorFlags = .{},
5251
53work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),52work_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 }
387386
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 and387 /// 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();
807795
...@@ -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}
895882
896pub fn target(self: Module) std.Target {883pub fn target(self: Module) std.Target {
897 return self.bin_file.options().target;884 return self.bin_file.options.target;
885}
886
887pub fn optimizeMode(self: Module) std.builtin.Mode {
888 return self.bin_file.options.optimize_mode;
898}889}
899890
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 {
1991pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {1982pub 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}
19971988
1998pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {1989pub 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}
20041995
...@@ -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}
21632154
2164pub fn wantSafety(self: *Module, scope: *Scope) bool {2155pub 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
25112503
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);
25162508
2517 // TODO handle comptime pointer writes2509 // 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
2799pub 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(
5050
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,
286289
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 {
360363
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 }
971981
...@@ -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 with1503 // 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);
14751507
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 }
15811613
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 }
16931727
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 {
165165
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);
170169
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 file20 /// 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(
8890
89fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {91fn 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
114119
115pub const File = struct {120pub 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 {
123130
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 }
130137
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 }
137144
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 }
144151
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 }
151158
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 }
158165
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 {
173180
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 }
180187
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 }
187194
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 }
201201
...@@ -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 }
214214
215 pub const Tag = enum {215 pub const Tag = enum {
216 Elf,216 elf,
217 C,217 c,
218 };218 };
219219
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 };
223223
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,
227228
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 };
295295
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,
299300
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 },
305305
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 }
466466
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 requires592 // 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 });
677677
...@@ -726,7 +726,7 @@ pub const File = struct {...@@ -726,7 +726,7 @@ pub const File = struct {
726726
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();
730730
731 // Unfortunately these have to be buffered and done at the end because ELF does not allow731 // 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;
877877
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 {
893893
894 assert(index == 16);894 assert(index == 16);
895895
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;
906906
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;
910910
...@@ -1216,7 +1216,7 @@ pub const File = struct {...@@ -1216,7 +1216,7 @@ pub const File = struct {
1216 },1216 },
1217 };1217 };
12181218
1219 const required_alignment = typed_value.ty.abiAlignment(self.options.target);1219 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
12201220
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 }
13621362
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 }
13841384
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 {
14331433
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 later1475 syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
1476 self.shdr_table_dirty = true; // TODO look into only writing one section1476 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 }
15781578
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 }
16551655
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),
17031702
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 {
6363
64 undef,64 undef,
65 zero,65 zero,
66 the_one_possible_value, // when the type only has one possible value66 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 {
388394
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,
470480
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,
542555
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,
547561
...@@ -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),
572586
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,
641658
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,
718737
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,
724742
...@@ -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,
880900
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,
943966
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,
948970
...@@ -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();
971993
...@@ -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,
10751100
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,
11511176
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 }
11771204
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,
12421269
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
882fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {882fn 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 calculations883 // 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)
10831083
1084fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {1084fn 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);