mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
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.
This commit is contained in:
parent
2b8e7deeda
commit
4beff80b2f
@ -47,7 +47,6 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
|
||||
/// Maps fully qualified namespaced names to the Decl struct for them.
|
||||
decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
|
||||
|
||||
optimize_mode: std.builtin.Mode,
|
||||
link_error_flags: link.File.ErrorFlags = .{},
|
||||
|
||||
work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
|
||||
@ -385,18 +384,6 @@ pub const Scope = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dumpInst(self: *Scope, inst: *Inst) void {
|
||||
const zir_module = self.namespace();
|
||||
const loc = std.zig.findLineColumn(zir_module.source.bytes, inst.src);
|
||||
std.debug.warn("{}:{}:{}: {}: ty={}\n", .{
|
||||
zir_module.sub_file_path,
|
||||
loc.line + 1,
|
||||
loc.column + 1,
|
||||
@tagName(inst.tag),
|
||||
inst.ty,
|
||||
});
|
||||
}
|
||||
|
||||
/// Asserts the scope has a parent which is a ZIRModule or File and
|
||||
/// returns the sub_file_path field.
|
||||
pub fn subFilePath(base: *Scope) []const u8 {
|
||||
@ -802,6 +789,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
.output_mode = options.output_mode,
|
||||
.link_mode = options.link_mode orelse .Static,
|
||||
.object_format = options.object_format orelse options.target.getObjectFormat(),
|
||||
.optimize_mode = options.optimize_mode,
|
||||
});
|
||||
errdefer bin_file.destroy();
|
||||
|
||||
@ -838,7 +826,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
.bin_file_dir = bin_file_dir,
|
||||
.bin_file_path = options.bin_file_path,
|
||||
.bin_file = bin_file,
|
||||
.optimize_mode = options.optimize_mode,
|
||||
.work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
|
||||
.keep_source_files_loaded = options.keep_source_files_loaded,
|
||||
};
|
||||
@ -894,7 +881,11 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
|
||||
}
|
||||
|
||||
pub fn target(self: Module) std.Target {
|
||||
return self.bin_file.options().target;
|
||||
return self.bin_file.options.target;
|
||||
}
|
||||
|
||||
pub fn optimizeMode(self: Module) std.builtin.Mode {
|
||||
return self.bin_file.options.optimize_mode;
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
pub fn constVoid(self: *Module, scope: *Scope, src: usize) !*Inst {
|
||||
return self.constInst(scope, src, .{
|
||||
.ty = Type.initTag(.void),
|
||||
.val = Value.initTag(.the_one_possible_value),
|
||||
.val = Value.initTag(.void_value),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn constNoReturn(self: *Module, scope: *Scope, src: usize) !*Inst {
|
||||
return self.constInst(scope, src, .{
|
||||
.ty = Type.initTag(.noreturn),
|
||||
.val = Value.initTag(.the_one_possible_value),
|
||||
.val = Value.initTag(.unreachable_value),
|
||||
});
|
||||
}
|
||||
|
||||
@ -2162,7 +2153,8 @@ pub fn analyzeDeclRefByName(self: *Module, scope: *Scope, src: usize, decl_name:
|
||||
}
|
||||
|
||||
pub fn wantSafety(self: *Module, scope: *Scope) bool {
|
||||
return switch (self.optimize_mode) {
|
||||
// TODO take into account scope's safety overrides
|
||||
return switch (self.optimizeMode()) {
|
||||
.Debug => true,
|
||||
.ReleaseSafe => true,
|
||||
.ReleaseFast => false,
|
||||
@ -2511,7 +2503,7 @@ pub fn storePtr(self: *Module, scope: *Scope, src: usize, ptr: *Inst, uncasted_v
|
||||
|
||||
const elem_ty = ptr.ty.elemType();
|
||||
const value = try self.coerce(scope, elem_ty, uncasted_value);
|
||||
if (elem_ty.onePossibleValue())
|
||||
if (elem_ty.onePossibleValue() != null)
|
||||
return self.constVoid(scope, src);
|
||||
|
||||
// TODO handle comptime pointer writes
|
||||
@ -2803,3 +2795,35 @@ pub fn singleConstPtrType(self: *Module, scope: *Scope, src: usize, elem_ty: Typ
|
||||
type_payload.* = .{ .pointee_type = elem_ty };
|
||||
return Type.initPayload(&type_payload.base);
|
||||
}
|
||||
|
||||
pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
|
||||
const zir_module = scope.namespace();
|
||||
const source = zir_module.getSource(self) catch @panic("dumpInst failed to get source");
|
||||
const loc = std.zig.findLineColumn(source, inst.src);
|
||||
if (inst.tag == .constant) {
|
||||
std.debug.warn("constant ty={} val={} src={}:{}:{}\n", .{
|
||||
inst.ty,
|
||||
inst.castTag(.constant).?.val,
|
||||
zir_module.subFilePath(),
|
||||
loc.line + 1,
|
||||
loc.column + 1,
|
||||
});
|
||||
} else if (inst.deaths == 0) {
|
||||
std.debug.warn("{} ty={} src={}:{}:{}\n", .{
|
||||
@tagName(inst.tag),
|
||||
inst.ty,
|
||||
zir_module.subFilePath(),
|
||||
loc.line + 1,
|
||||
loc.column + 1,
|
||||
});
|
||||
} else {
|
||||
std.debug.warn("{} ty={} deaths={b} src={}:{}:{}\n", .{
|
||||
@tagName(inst.tag),
|
||||
inst.ty,
|
||||
inst.deaths,
|
||||
zir_module.subFilePath(),
|
||||
loc.line + 1,
|
||||
loc.column + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ pub fn generateSymbol(
|
||||
|
||||
switch (typed_value.ty.zigTypeTag()) {
|
||||
.Fn => {
|
||||
switch (bin_file.options.target.cpu.arch) {
|
||||
switch (bin_file.base.options.target.cpu.arch) {
|
||||
//.arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
|
||||
//.armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
|
||||
//.aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
|
||||
@ -143,7 +143,7 @@ pub fn generateSymbol(
|
||||
// TODO handle the dependency of this symbol on the decl's vaddr.
|
||||
// If the decl changes vaddr, then this symbol needs to get regenerated.
|
||||
const vaddr = bin_file.local_symbols.items[decl.link.local_sym_index].st_value;
|
||||
const endian = bin_file.options.target.cpu.arch.endian();
|
||||
const endian = bin_file.base.options.target.cpu.arch.endian();
|
||||
switch (bin_file.ptr_width) {
|
||||
.p32 => {
|
||||
try code.resize(4);
|
||||
@ -166,7 +166,7 @@ pub fn generateSymbol(
|
||||
};
|
||||
},
|
||||
.Int => {
|
||||
const info = typed_value.ty.intInfo(bin_file.options.target);
|
||||
const info = typed_value.ty.intInfo(bin_file.base.options.target);
|
||||
if (info.bits == 8 and !info.signed) {
|
||||
const x = typed_value.val.toUnsignedInt();
|
||||
try code.append(@intCast(u8, x));
|
||||
@ -230,6 +230,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
unreach,
|
||||
/// No more references to this value remain.
|
||||
dead,
|
||||
/// The value is undefined.
|
||||
undef,
|
||||
/// A pointer-sized integer that fits in a register.
|
||||
/// If the type is a pointer, this is the pointer address in virtual address space.
|
||||
immediate: u64,
|
||||
@ -282,6 +284,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.compare_flags_signed,
|
||||
.ptr_stack_offset,
|
||||
.ptr_embedded_in_code,
|
||||
.undef,
|
||||
=> false,
|
||||
|
||||
.register,
|
||||
@ -360,7 +363,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
|
||||
var function = Self{
|
||||
.gpa = bin_file.allocator,
|
||||
.target = &bin_file.options.target,
|
||||
.target = &bin_file.base.options.target,
|
||||
.bin_file = bin_file,
|
||||
.mod_fn = module_fn,
|
||||
.code = code,
|
||||
@ -656,6 +659,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
};
|
||||
switch (ptr) {
|
||||
.none => unreachable,
|
||||
.undef => unreachable,
|
||||
.unreach => unreachable,
|
||||
.dead => unreachable,
|
||||
.compare_flags_unsigned => unreachable,
|
||||
@ -687,6 +691,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
const elem_ty = inst.rhs.ty;
|
||||
switch (ptr) {
|
||||
.none => unreachable,
|
||||
.undef => unreachable,
|
||||
.unreach => unreachable,
|
||||
.dead => unreachable,
|
||||
.compare_flags_unsigned => unreachable,
|
||||
@ -798,6 +803,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
|
||||
switch (dst_mcv) {
|
||||
.none => unreachable,
|
||||
.undef => unreachable,
|
||||
.dead, .unreach, .immediate => unreachable,
|
||||
.compare_flags_unsigned => unreachable,
|
||||
.compare_flags_signed => unreachable,
|
||||
@ -806,6 +812,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.register => |dst_reg| {
|
||||
switch (src_mcv) {
|
||||
.none => unreachable,
|
||||
.undef => try self.genSetReg(src, dst_reg, .undef),
|
||||
.dead, .unreach => unreachable,
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
@ -905,11 +912,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
|
||||
},
|
||||
.ptr_stack_offset => {
|
||||
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset", .{});
|
||||
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{});
|
||||
},
|
||||
.ptr_embedded_in_code => {
|
||||
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code", .{});
|
||||
return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
|
||||
},
|
||||
.undef => unreachable,
|
||||
.immediate => unreachable,
|
||||
.unreach => unreachable,
|
||||
.dead => unreachable,
|
||||
@ -966,6 +974,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.stack_offset => |offset| return MCValue{ .ptr_stack_offset = offset },
|
||||
.embedded_in_code => |offset| return MCValue{ .ptr_embedded_in_code = offset },
|
||||
.memory => |vaddr| return MCValue{ .immediate = vaddr },
|
||||
|
||||
.undef => return self.fail(inst.base.src, "TODO implement ref on an undefined value", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1243,6 +1253,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
.unreach, .none => return, // Nothing to do.
|
||||
.undef => {
|
||||
if (!self.wantSafety())
|
||||
return; // The already existing value will do just fine.
|
||||
// TODO Upgrade this to a memset call when we have that available.
|
||||
return self.genSetStack(src, ty, stack_offset, .{ .immediate = 0xaaaaaaaa });
|
||||
},
|
||||
.compare_flags_unsigned => |op| {
|
||||
return self.fail(src, "TODO implement set stack variable with compare flags value (unsigned)", .{});
|
||||
},
|
||||
@ -1250,6 +1266,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(src, "TODO implement set stack variable with compare flags value (signed)", .{});
|
||||
},
|
||||
.immediate => |x_big| {
|
||||
if (ty.abiSize(self.target.*) != 4) {
|
||||
// TODO after fixing this, need to update the undef case above
|
||||
return self.fail(src, "TODO implement set non 4 abi size stack variable with immediate", .{});
|
||||
}
|
||||
try self.code.ensureCapacity(self.code.items.len + 7);
|
||||
if (x_big <= math.maxInt(u32)) {
|
||||
const x = @intCast(u32, x_big);
|
||||
@ -1311,6 +1331,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.ptr_stack_offset => unreachable,
|
||||
.ptr_embedded_in_code => unreachable,
|
||||
.unreach, .none => return, // Nothing to do.
|
||||
.undef => {
|
||||
if (!self.wantSafety())
|
||||
return; // The already existing value will do just fine.
|
||||
// Write the debug undefined value.
|
||||
switch (reg.size()) {
|
||||
8 => return self.genSetReg(src, reg, .{ .immediate = 0xaa }),
|
||||
16 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaa }),
|
||||
32 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaa }),
|
||||
64 => return self.genSetReg(src, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
|
||||
else => unreachable,
|
||||
}
|
||||
},
|
||||
.compare_flags_unsigned => |op| {
|
||||
try self.code.ensureCapacity(self.code.items.len + 3);
|
||||
self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
|
||||
@ -1471,7 +1503,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
// is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
|
||||
// this instruction.
|
||||
const id3 = @truncate(u3, reg.id());
|
||||
std.debug.assert(id3 != 4 and id3 != 5);
|
||||
assert(id3 != 4 and id3 != 5);
|
||||
|
||||
// Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
|
||||
try self.genSetReg(src, reg, MCValue{ .immediate = x });
|
||||
@ -1580,6 +1612,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
}
|
||||
|
||||
fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
|
||||
if (typed_value.val.isUndef())
|
||||
return MCValue.undef;
|
||||
const ptr_bits = self.target.cpu.arch.ptrBitWidth();
|
||||
const ptr_bytes: u64 = @divExact(ptr_bits, 8);
|
||||
switch (typed_value.ty.zigTypeTag()) {
|
||||
@ -1691,6 +1725,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return result;
|
||||
}
|
||||
|
||||
/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
|
||||
fn wantSafety(self: *Self) bool {
|
||||
return switch (self.bin_file.base.options.optimize_mode) {
|
||||
.Debug => true,
|
||||
.ReleaseSafe => true,
|
||||
.ReleaseFast => false,
|
||||
.ReleaseSmall => false,
|
||||
};
|
||||
}
|
||||
|
||||
fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
|
||||
@setCold(true);
|
||||
assert(self.err_msg == null);
|
||||
|
||||
@ -165,8 +165,7 @@ pub const Inst = struct {
|
||||
|
||||
/// Returns `null` if runtime-known.
|
||||
pub fn value(base: *Inst) ?Value {
|
||||
if (base.ty.onePossibleValue())
|
||||
return Value.initTag(.the_one_possible_value);
|
||||
if (base.ty.onePossibleValue()) |opv| return opv;
|
||||
|
||||
const inst = base.cast(Constant) orelse return null;
|
||||
return inst.val;
|
||||
|
||||
@ -16,6 +16,7 @@ pub const Options = struct {
|
||||
output_mode: std.builtin.OutputMode,
|
||||
link_mode: std.builtin.LinkMode,
|
||||
object_format: std.builtin.ObjectFormat,
|
||||
optimize_mode: std.builtin.Mode,
|
||||
/// Used for calculating how much space to reserve for symbols in case the binary file
|
||||
/// does not already have a symbol table.
|
||||
symbol_count_hint: u64 = 32,
|
||||
@ -66,6 +67,7 @@ pub fn writeFilePath(
|
||||
.link_mode = module.link_mode,
|
||||
.object_format = module.object_format,
|
||||
.symbol_count_hint = module.decls.items.len,
|
||||
.optimize_mode = module.optimize_mode,
|
||||
};
|
||||
const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(options) });
|
||||
defer af.deinit();
|
||||
@ -88,9 +90,12 @@ pub fn writeFilePath(
|
||||
|
||||
fn openCFile(allocator: *Allocator, file: fs.File, options: Options) !File.C {
|
||||
return File.C{
|
||||
.base = .{
|
||||
.tag = .c,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.options = options,
|
||||
.main = std.ArrayList(u8).init(allocator),
|
||||
.header = std.ArrayList(u8).init(allocator),
|
||||
.constants = std.ArrayList(u8).init(allocator),
|
||||
@ -114,6 +119,8 @@ pub fn openBinFile(allocator: *Allocator, file: fs.File, options: Options) !File
|
||||
|
||||
pub const File = struct {
|
||||
tag: Tag,
|
||||
options: Options,
|
||||
|
||||
pub fn cast(base: *File, comptime T: type) ?*T {
|
||||
if (base.tag != T.base_tag)
|
||||
return null;
|
||||
@ -123,47 +130,47 @@ pub const File = struct {
|
||||
|
||||
pub fn makeWritable(base: *File, dir: fs.Dir, sub_path: []const u8) !void {
|
||||
switch (base.tag) {
|
||||
.Elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
|
||||
.C => {},
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).makeWritable(dir, sub_path),
|
||||
.c => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeExecutable(base: *File) !void {
|
||||
switch (base.tag) {
|
||||
.Elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
|
||||
.C => unreachable,
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).makeExecutable(),
|
||||
.c => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void {
|
||||
switch (base.tag) {
|
||||
.Elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
|
||||
.C => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl),
|
||||
.c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void {
|
||||
switch (base.tag) {
|
||||
.Elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
|
||||
.C => {},
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl),
|
||||
.c => {},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(base: *File) void {
|
||||
switch (base.tag) {
|
||||
.Elf => @fieldParentPtr(Elf, "base", base).deinit(),
|
||||
.C => @fieldParentPtr(C, "base", base).deinit(),
|
||||
.elf => @fieldParentPtr(Elf, "base", base).deinit(),
|
||||
.c => @fieldParentPtr(C, "base", base).deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn destroy(base: *File) void {
|
||||
switch (base.tag) {
|
||||
.Elf => {
|
||||
.elf => {
|
||||
const parent = @fieldParentPtr(Elf, "base", base);
|
||||
parent.deinit();
|
||||
parent.allocator.destroy(parent);
|
||||
},
|
||||
.C => {
|
||||
.c => {
|
||||
const parent = @fieldParentPtr(C, "base", base);
|
||||
parent.deinit();
|
||||
parent.allocator.destroy(parent);
|
||||
@ -173,29 +180,22 @@ pub const File = struct {
|
||||
|
||||
pub fn flush(base: *File) !void {
|
||||
try switch (base.tag) {
|
||||
.Elf => @fieldParentPtr(Elf, "base", base).flush(),
|
||||
.C => @fieldParentPtr(C, "base", base).flush(),
|
||||
.elf => @fieldParentPtr(Elf, "base", base).flush(),
|
||||
.c => @fieldParentPtr(C, "base", base).flush(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn freeDecl(base: *File, decl: *Module.Decl) void {
|
||||
switch (base.tag) {
|
||||
.Elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
|
||||
.C => unreachable,
|
||||
.elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl),
|
||||
.c => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn errorFlags(base: *File) ErrorFlags {
|
||||
return switch (base.tag) {
|
||||
.Elf => @fieldParentPtr(Elf, "base", base).error_flags,
|
||||
.C => return .{ .no_entry_point_found = false },
|
||||
};
|
||||
}
|
||||
|
||||
pub fn options(base: *File) Options {
|
||||
return switch (base.tag) {
|
||||
.Elf => @fieldParentPtr(Elf, "base", base).options,
|
||||
.C => @fieldParentPtr(C, "base", base).options,
|
||||
.elf => @fieldParentPtr(Elf, "base", base).error_flags,
|
||||
.c => return .{ .no_entry_point_found = false },
|
||||
};
|
||||
}
|
||||
|
||||
@ -207,14 +207,14 @@ pub const File = struct {
|
||||
exports: []const *Module.Export,
|
||||
) !void {
|
||||
switch (base.tag) {
|
||||
.Elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
|
||||
.C => return {},
|
||||
.elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports),
|
||||
.c => return {},
|
||||
}
|
||||
}
|
||||
|
||||
pub const Tag = enum {
|
||||
Elf,
|
||||
C,
|
||||
elf,
|
||||
c,
|
||||
};
|
||||
|
||||
pub const ErrorFlags = struct {
|
||||
@ -222,15 +222,15 @@ pub const File = struct {
|
||||
};
|
||||
|
||||
pub const C = struct {
|
||||
pub const base_tag: Tag = .C;
|
||||
base: File = File{ .tag = base_tag },
|
||||
pub const base_tag: Tag = .c;
|
||||
|
||||
base: File,
|
||||
|
||||
allocator: *Allocator,
|
||||
header: std.ArrayList(u8),
|
||||
constants: std.ArrayList(u8),
|
||||
main: std.ArrayList(u8),
|
||||
file: ?fs.File,
|
||||
options: Options,
|
||||
called: std.StringHashMap(void),
|
||||
need_stddef: bool = false,
|
||||
need_stdint: bool = false,
|
||||
@ -294,13 +294,13 @@ pub const File = struct {
|
||||
};
|
||||
|
||||
pub const Elf = struct {
|
||||
pub const base_tag: Tag = .Elf;
|
||||
base: File = File{ .tag = base_tag },
|
||||
pub const base_tag: Tag = .elf;
|
||||
|
||||
base: File,
|
||||
|
||||
allocator: *Allocator,
|
||||
file: ?fs.File,
|
||||
owns_file_handle: bool,
|
||||
options: Options,
|
||||
ptr_width: enum { p32, p64 },
|
||||
|
||||
/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
|
||||
@ -460,13 +460,13 @@ pub const File = struct {
|
||||
self.file = try dir.createFile(sub_path, .{
|
||||
.truncate = false,
|
||||
.read = true,
|
||||
.mode = determineMode(self.options),
|
||||
.mode = determineMode(self.base.options),
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns end pos of collision, if any.
|
||||
fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
|
||||
const small_ptr = self.options.target.cpu.arch.ptrBitWidth() == 32;
|
||||
const small_ptr = self.base.options.target.cpu.arch.ptrBitWidth() == 32;
|
||||
const ehdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Ehdr) else @sizeOf(elf.Elf64_Ehdr);
|
||||
if (start < ehdr_size)
|
||||
return ehdr_size;
|
||||
@ -569,7 +569,7 @@ pub const File = struct {
|
||||
};
|
||||
if (self.phdr_load_re_index == null) {
|
||||
self.phdr_load_re_index = @intCast(u16, self.program_headers.items.len);
|
||||
const file_size = self.options.program_code_size_hint;
|
||||
const file_size = self.base.options.program_code_size_hint;
|
||||
const p_align = 0x1000;
|
||||
const off = self.findFreeSpace(file_size, p_align);
|
||||
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 {
|
||||
}
|
||||
if (self.phdr_got_index == null) {
|
||||
self.phdr_got_index = @intCast(u16, self.program_headers.items.len);
|
||||
const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
|
||||
const file_size = @as(u64, ptr_size) * self.base.options.symbol_count_hint;
|
||||
// We really only need ptr alignment but since we are using PROGBITS, linux requires
|
||||
// page align.
|
||||
const p_align = 0x1000;
|
||||
@ -671,7 +671,7 @@ pub const File = struct {
|
||||
self.symtab_section_index = @intCast(u16, self.sections.items.len);
|
||||
const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
|
||||
const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
|
||||
const file_size = self.options.symbol_count_hint * each_size;
|
||||
const file_size = self.base.options.symbol_count_hint * each_size;
|
||||
const off = self.findFreeSpace(file_size, min_align);
|
||||
std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
|
||||
|
||||
@ -726,7 +726,7 @@ pub const File = struct {
|
||||
|
||||
/// Commit pending changes and write headers.
|
||||
pub fn flush(self: *Elf) !void {
|
||||
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
|
||||
// Unfortunately these have to be buffered and done at the end because ELF does not allow
|
||||
// mixing local and global symbols within a symbol table.
|
||||
@ -845,7 +845,7 @@ pub const File = struct {
|
||||
}
|
||||
self.shdr_table_dirty = false;
|
||||
}
|
||||
if (self.entry_addr == null and self.options.output_mode == .Exe) {
|
||||
if (self.entry_addr == null and self.base.options.output_mode == .Exe) {
|
||||
std.log.debug(.link, "no_entry_point_found = true\n", .{});
|
||||
self.error_flags.no_entry_point_found = true;
|
||||
} else {
|
||||
@ -875,7 +875,7 @@ pub const File = struct {
|
||||
};
|
||||
index += 1;
|
||||
|
||||
const endian = self.options.target.cpu.arch.endian();
|
||||
const endian = self.base.options.target.cpu.arch.endian();
|
||||
hdr_buf[index] = switch (endian) {
|
||||
.Little => elf.ELFDATA2LSB,
|
||||
.Big => elf.ELFDATA2MSB,
|
||||
@ -893,10 +893,10 @@ pub const File = struct {
|
||||
|
||||
assert(index == 16);
|
||||
|
||||
const elf_type = switch (self.options.output_mode) {
|
||||
const elf_type = switch (self.base.options.output_mode) {
|
||||
.Exe => elf.ET.EXEC,
|
||||
.Obj => elf.ET.REL,
|
||||
.Lib => switch (self.options.link_mode) {
|
||||
.Lib => switch (self.base.options.link_mode) {
|
||||
.Static => elf.ET.REL,
|
||||
.Dynamic => elf.ET.DYN,
|
||||
},
|
||||
@ -904,7 +904,7 @@ pub const File = struct {
|
||||
mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian);
|
||||
index += 2;
|
||||
|
||||
const machine = self.options.target.cpu.arch.toElfMachine();
|
||||
const machine = self.base.options.target.cpu.arch.toElfMachine();
|
||||
mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(machine), endian);
|
||||
index += 2;
|
||||
|
||||
@ -1216,7 +1216,7 @@ pub const File = struct {
|
||||
},
|
||||
};
|
||||
|
||||
const required_alignment = typed_value.ty.abiAlignment(self.options.target);
|
||||
const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
|
||||
|
||||
const stt_bits: u8 = switch (typed_value.ty.zigTypeTag()) {
|
||||
.Fn => elf.STT_FUNC,
|
||||
@ -1361,9 +1361,9 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
fn writeProgHeader(self: *Elf, index: usize) !void {
|
||||
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const offset = self.program_headers.items[index].p_offset;
|
||||
switch (self.options.target.cpu.arch.ptrBitWidth()) {
|
||||
switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => {
|
||||
var phdr = [1]elf.Elf32_Phdr{progHeaderTo32(self.program_headers.items[index])};
|
||||
if (foreign_endian) {
|
||||
@ -1383,9 +1383,9 @@ pub const File = struct {
|
||||
}
|
||||
|
||||
fn writeSectHeader(self: *Elf, index: usize) !void {
|
||||
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const offset = self.sections.items[index].sh_offset;
|
||||
switch (self.options.target.cpu.arch.ptrBitWidth()) {
|
||||
switch (self.base.options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => {
|
||||
var shdr: [1]elf.Elf32_Shdr = undefined;
|
||||
shdr[0] = sectHeaderTo32(self.sections.items[index]);
|
||||
@ -1433,7 +1433,7 @@ pub const File = struct {
|
||||
|
||||
self.offset_table_count_dirty = false;
|
||||
}
|
||||
const endian = self.options.target.cpu.arch.endian();
|
||||
const endian = self.base.options.target.cpu.arch.endian();
|
||||
const off = shdr.sh_offset + @as(u64, entry_size) * index;
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
@ -1475,7 +1475,7 @@ pub const File = struct {
|
||||
syms_sect.sh_size = needed_size; // anticipating adding the global symbols later
|
||||
self.shdr_table_dirty = true; // TODO look into only writing one section
|
||||
}
|
||||
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
var sym = [1]elf.Elf32_Sym{
|
||||
@ -1511,7 +1511,7 @@ pub const File = struct {
|
||||
.p32 => @sizeOf(elf.Elf32_Sym),
|
||||
.p64 => @sizeOf(elf.Elf64_Sym),
|
||||
};
|
||||
const foreign_endian = self.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const foreign_endian = self.base.options.target.cpu.arch.endian() != std.Target.current.cpu.arch.endian();
|
||||
const global_syms_off = syms_sect.sh_offset + self.local_symbols.items.len * sym_size;
|
||||
switch (self.ptr_width) {
|
||||
.p32 => {
|
||||
@ -1577,9 +1577,12 @@ pub fn createElfFile(allocator: *Allocator, file: fs.File, options: Options) !Fi
|
||||
}
|
||||
|
||||
var self: File.Elf = .{
|
||||
.base = .{
|
||||
.tag = .elf,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.options = options,
|
||||
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => .p32,
|
||||
64 => .p64,
|
||||
@ -1637,10 +1640,13 @@ fn openBinFileInner(allocator: *Allocator, file: fs.File, options: Options) !Fil
|
||||
.raw => return error.IncrFailed,
|
||||
}
|
||||
var self: File.Elf = .{
|
||||
.base = .{
|
||||
.tag = .elf,
|
||||
.options = options,
|
||||
},
|
||||
.allocator = allocator,
|
||||
.file = file,
|
||||
.owns_file_handle = false,
|
||||
.options = options,
|
||||
.ptr_width = switch (options.target.cpu.arch.ptrBitWidth()) {
|
||||
32 => .p32,
|
||||
64 => .p64,
|
||||
|
||||
@ -1653,7 +1653,7 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn onePossibleValue(self: Type) bool {
|
||||
pub fn onePossibleValue(self: Type) ?Value {
|
||||
var ty = self;
|
||||
while (true) switch (ty.tag()) {
|
||||
.f16,
|
||||
@ -1692,21 +1692,32 @@ pub const Type = extern union {
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
=> return false,
|
||||
|
||||
.c_void,
|
||||
.void,
|
||||
.noreturn,
|
||||
.@"null",
|
||||
.@"undefined",
|
||||
=> return true,
|
||||
=> return null,
|
||||
|
||||
.int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
|
||||
.int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,
|
||||
.void => return Value.initTag(.void_value),
|
||||
.noreturn => return Value.initTag(.unreachable_value),
|
||||
.@"null" => return Value.initTag(.null_value),
|
||||
.@"undefined" => return Value.initTag(.undef),
|
||||
|
||||
.int_unsigned => {
|
||||
if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
|
||||
return Value.initTag(.zero);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.int_signed => {
|
||||
if (ty.cast(Payload.IntSigned).?.bits == 0) {
|
||||
return Value.initTag(.zero);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.array => {
|
||||
const array = ty.cast(Payload.Array).?;
|
||||
if (array.len == 0)
|
||||
return true;
|
||||
return Value.initTag(.empty_array);
|
||||
ty = array.elem_type;
|
||||
continue;
|
||||
},
|
||||
|
||||
@ -63,7 +63,9 @@ pub const Value = extern union {
|
||||
|
||||
undef,
|
||||
zero,
|
||||
the_one_possible_value, // when the type only has one possible value
|
||||
void_value,
|
||||
unreachable_value,
|
||||
empty_array,
|
||||
null_value,
|
||||
bool_true,
|
||||
bool_false, // See last_no_payload_tag below.
|
||||
@ -164,7 +166,9 @@ pub const Value = extern union {
|
||||
.const_slice_u8_type,
|
||||
.undef,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
.null_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
@ -285,7 +289,8 @@ pub const Value = extern union {
|
||||
.null_value => return out_stream.writeAll("null"),
|
||||
.undef => return out_stream.writeAll("undefined"),
|
||||
.zero => return out_stream.writeAll("0"),
|
||||
.the_one_possible_value => return out_stream.writeAll("(one possible value)"),
|
||||
.void_value => return out_stream.writeAll("{}"),
|
||||
.unreachable_value => return out_stream.writeAll("unreachable"),
|
||||
.bool_true => return out_stream.writeAll("true"),
|
||||
.bool_false => return out_stream.writeAll("false"),
|
||||
.ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
|
||||
@ -312,6 +317,7 @@ pub const Value = extern union {
|
||||
try out_stream.print("&[{}] ", .{elem_ptr.index});
|
||||
val = elem_ptr.array_ptr;
|
||||
},
|
||||
.empty_array => return out_stream.writeAll(".{}"),
|
||||
.bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
|
||||
.repeated => {
|
||||
try out_stream.writeAll("(repeated) ");
|
||||
@ -388,7 +394,9 @@ pub const Value = extern union {
|
||||
|
||||
.undef,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
@ -460,15 +468,18 @@ pub const Value = extern union {
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // An integer with one possible value is always zero.
|
||||
.undef => unreachable,
|
||||
|
||||
.zero,
|
||||
.bool_false,
|
||||
=> return BigIntMutable.init(&space.limbs, 0).toConst(),
|
||||
@ -532,16 +543,19 @@ pub const Value = extern union {
|
||||
.decl_ref,
|
||||
.elem_ptr,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.undef => unreachable,
|
||||
|
||||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> return 0,
|
||||
|
||||
@ -570,7 +584,7 @@ pub const Value = extern union {
|
||||
.float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
|
||||
.float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
|
||||
|
||||
.zero, .the_one_possible_value => 0,
|
||||
.zero => 0,
|
||||
.int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
|
||||
// .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),
|
||||
.int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
|
||||
@ -637,9 +651,11 @@ pub const Value = extern union {
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.zero,
|
||||
.bool_false,
|
||||
=> return 0,
|
||||
@ -714,11 +730,13 @@ pub const Value = extern union {
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
.undef,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> return true,
|
||||
|
||||
@ -797,13 +815,13 @@ pub const Value = extern union {
|
||||
// return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
32 => {
|
||||
var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)};
|
||||
var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
|
||||
if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
return error.Overflow;
|
||||
return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
64 => {
|
||||
var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)};
|
||||
var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
|
||||
if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
return error.Overflow;
|
||||
return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
@ -875,7 +893,9 @@ pub const Value = extern union {
|
||||
.int_i64,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.the_one_possible_value,
|
||||
.empty_array,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
=> unreachable,
|
||||
|
||||
.zero => false,
|
||||
@ -939,10 +959,12 @@ pub const Value = extern union {
|
||||
.bytes,
|
||||
.repeated,
|
||||
.undef,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> .eq,
|
||||
|
||||
@ -964,8 +986,8 @@ pub const Value = extern union {
|
||||
pub fn order(lhs: Value, rhs: Value) std.math.Order {
|
||||
const lhs_tag = lhs.tag();
|
||||
const rhs_tag = rhs.tag();
|
||||
const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
|
||||
const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
|
||||
const lhs_is_zero = lhs_tag == .zero;
|
||||
const rhs_is_zero = rhs_tag == .zero;
|
||||
if (lhs_is_zero) return rhs.orderAgainstZero().invert();
|
||||
if (rhs_is_zero) return lhs.orderAgainstZero();
|
||||
|
||||
@ -1071,9 +1093,11 @@ pub const Value = extern union {
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value => Value.initTag(.the_one_possible_value),
|
||||
.ref_val => self.cast(Payload.RefVal).?.val,
|
||||
.decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
|
||||
.elem_ptr => {
|
||||
@ -1130,7 +1154,6 @@ pub const Value = extern union {
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
@ -1147,8 +1170,12 @@ pub const Value = extern union {
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
=> unreachable,
|
||||
|
||||
.empty_array => unreachable, // out of bounds array index
|
||||
|
||||
.bytes => {
|
||||
const int_payload = try allocator.create(Payload.Int_u64);
|
||||
int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
|
||||
@ -1175,8 +1202,7 @@ pub const Value = extern union {
|
||||
return self.tag() == .undef;
|
||||
}
|
||||
|
||||
/// Valid for all types. Asserts the value is not undefined.
|
||||
/// `.the_one_possible_value` is reported as not null.
|
||||
/// Valid for all types. Asserts the value is not undefined and not unreachable.
|
||||
pub fn isNull(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
@ -1221,7 +1247,7 @@ pub const Value = extern union {
|
||||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.empty_array,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.function,
|
||||
@ -1238,9 +1264,11 @@ pub const Value = extern union {
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
=> false,
|
||||
|
||||
.undef => unreachable,
|
||||
.unreachable_value => unreachable,
|
||||
.null_value => true,
|
||||
};
|
||||
}
|
||||
|
||||
@ -742,7 +742,7 @@ pub const Inst = struct {
|
||||
.@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
|
||||
.@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
|
||||
.@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
|
||||
.void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) },
|
||||
.void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -1598,6 +1598,21 @@ const EmitZIR = struct {
|
||||
const decl = decl_ref.decl;
|
||||
return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
|
||||
}
|
||||
if (typed_value.val.isUndef()) {
|
||||
const as_inst = try self.arena.allocator.create(Inst.BinOp);
|
||||
as_inst.* = .{
|
||||
.base = .{
|
||||
.tag = .as,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{
|
||||
.lhs = (try self.emitType(src, typed_value.ty)).inst,
|
||||
.rhs = (try self.emitPrimitive(src, .@"undefined")).inst,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
return self.emitUnnamedDecl(&as_inst.base);
|
||||
}
|
||||
switch (typed_value.ty.zigTypeTag()) {
|
||||
.Pointer => {
|
||||
const ptr_elem_type = typed_value.ty.elemType();
|
||||
|
||||
@ -882,7 +882,7 @@ fn analyzeInstArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) Inn
|
||||
fn analyzeInstComptimeOp(mod: *Module, scope: *Scope, res_type: Type, inst: *zir.Inst.BinOp, lhs_val: Value, rhs_val: Value) InnerError!*Inst {
|
||||
// incase rhs is 0, simply return lhs without doing any calculations
|
||||
// TODO Once division is implemented we should throw an error when dividing by 0.
|
||||
if (rhs_val.tag() == .zero or rhs_val.tag() == .the_one_possible_value) {
|
||||
if (rhs_val.compareWithZero(.eq)) {
|
||||
return mod.constInst(scope, inst.base.src, .{
|
||||
.ty = res_type,
|
||||
.val = lhs_val,
|
||||
@ -1083,6 +1083,7 @@ fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp)
|
||||
|
||||
fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst {
|
||||
const b = try mod.requireRuntimeBlock(scope, unreach.base.src);
|
||||
// TODO Add compile error for @optimizeFor occurring too late in a scope.
|
||||
if (mod.wantSafety(scope)) {
|
||||
// TODO Once we have a panic function to call, call it here instead of this.
|
||||
_ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user