self-hosted: fix test regressions

I'm allowing incremental compilation of ZIR modules to be broken. This
is not a real use case of ZIR, and the feature requires a lot of code
duplication with incremental compilation of Zig AST (which works great).
This commit is contained in:
Andrew Kelley
2020-06-24 20:28:52 -04:00
parent fd7a97b3b2
commit 5aa3f56773
9 changed files with 221 additions and 264 deletions

View File

@@ -69,6 +69,8 @@ next_anon_name_index: usize = 0,
/// contains Decls that need to be deleted if they end up having no references to them.
deletion_set: std.ArrayListUnmanaged(*Decl) = .{},
keep_source_files_loaded: bool,
const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql);
const WorkItem = union(enum) {
@@ -580,11 +582,13 @@ pub const Scope = struct {
.loaded_success => {
self.contents.module.deinit(allocator);
allocator.destroy(self.contents.module);
self.contents = .{ .not_available = {} };
self.status = .unloaded_success;
},
.loaded_sema_failure => {
self.contents.module.deinit(allocator);
allocator.destroy(self.contents.module);
self.contents = .{ .not_available = {} };
self.status = .unloaded_sema_failure;
},
}
@@ -719,6 +723,7 @@ pub const InitOptions = struct {
link_mode: ?std.builtin.LinkMode = null,
object_format: ?std.builtin.ObjectFormat = null,
optimize_mode: std.builtin.Mode = .Debug,
keep_source_files_loaded: bool = false,
};
pub fn init(gpa: *Allocator, options: InitOptions) !Module {
@@ -772,6 +777,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
.failed_files = std.AutoHashMap(*Scope, *ErrorMsg).init(gpa),
.failed_exports = std.AutoHashMap(*Export, *ErrorMsg).init(gpa),
.work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa),
.keep_source_files_loaded = options.keep_source_files_loaded,
};
}
@@ -869,21 +875,22 @@ pub fn update(self: *Module) !void {
try self.performAllTheWork();
// Process the deletion set.
for (self.deletion_set.items) |decl| {
while (self.deletion_set.popOrNull()) |decl| {
if (decl.dependants.items.len != 0) {
decl.deletion_flag = false;
continue;
}
try self.deleteDecl(decl);
}
self.deletion_set.shrink(self.allocator, 0);
self.link_error_flags = self.bin_file.error_flags;
// If there are any errors, we anticipate the source files being loaded
// to report error messages. Otherwise we unload all source files to save memory.
if (self.totalErrorCount() == 0) {
self.root_scope.unload(self.allocator);
if (!self.keep_source_files_loaded) {
self.root_scope.unload(self.allocator);
}
try self.bin_file.flush();
}
}
@@ -1025,7 +1032,6 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
defer tracy.end();
const subsequent_analysis = switch (decl.analysis) {
.complete => return,
.in_progress => unreachable,
.sema_failure,
@@ -1035,7 +1041,11 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
.codegen_failure_retryable,
=> return error.AnalysisFail,
.outdated => blk: {
.complete, .outdated => blk: {
if (decl.generation == self.generation) {
assert(decl.analysis == .complete);
return;
}
//std.debug.warn("re-analyzing {}\n", .{decl.name});
// The exports this Decl performs will be re-discovered, so we remove them here
@@ -1044,10 +1054,9 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
// Dependencies will be re-discovered, so we remove them here prior to re-analysis.
for (decl.dependencies.items) |dep| {
dep.removeDependant(decl);
if (dep.dependants.items.len == 0) {
if (dep.dependants.items.len == 0 and !dep.deletion_flag) {
// We don't perform a deletion here, because this Decl or another one
// may end up referencing it before the update is complete.
assert(!dep.deletion_flag);
dep.deletion_flag = true;
try self.deletion_set.append(self.allocator, dep);
}
@@ -1773,6 +1782,9 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
}
}
}
for (exports_to_resolve.items) |export_decl| {
_ = try self.resolveZirDecl(&root_scope.base, export_decl);
}
{
// Handle explicitly deleted decls from the source code. Not to be confused
// with when we delete decls because they are no longer referenced.
@@ -1782,9 +1794,6 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
try self.deleteDecl(kv.key);
}
}
for (exports_to_resolve.items) |export_decl| {
_ = try self.resolveZirDecl(&root_scope.base, export_decl);
}
}
fn deleteDecl(self: *Module, decl: *Decl) !void {
@@ -1800,10 +1809,9 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
// Remove itself from its dependencies, because we are about to destroy the decl pointer.
for (decl.dependencies.items) |dep| {
dep.removeDependant(decl);
if (dep.dependants.items.len == 0) {
if (dep.dependants.items.len == 0 and !dep.deletion_flag) {
// We don't recursively perform a deletion here, because during the update,
// another reference to it may turn up.
assert(!dep.deletion_flag);
dep.deletion_flag = true;
self.deletion_set.appendAssumeCapacity(dep);
}
@@ -2026,9 +2034,10 @@ fn resolveInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
};
const decl = try self.resolveCompleteZirDecl(scope, entry.decl);
const decl_ref = try self.analyzeDeclRef(scope, old_inst.src, decl);
const result = try self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
old_inst.analyzed_inst = result;
return result;
// Note: it would be tempting here to store the result into old_inst.analyzed_inst field,
// but this would prevent the analyzeDeclRef from happening, which is needed to properly
// detect Decl dependencies and dependency failures on updates.
return self.analyzeDeref(scope, old_inst.src, decl_ref, old_inst.src);
}
fn requireRuntimeBlock(self: *Module, scope: *Scope, src: usize) !*Scope.Block {

View File

@@ -369,7 +369,7 @@ pub const ElfFile = struct {
const file_size = self.options.program_code_size_hint;
const p_align = 0x1000;
const off = self.findFreeSpace(file_size, p_align);
//std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
//std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
try self.program_headers.append(self.allocator, .{
.p_type = elf.PT_LOAD,
.p_offset = off,
@@ -390,7 +390,7 @@ pub const ElfFile = struct {
// page align.
const p_align = 0x1000;
const off = self.findFreeSpace(file_size, p_align);
//std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
//std.log.debug(.link, "found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
// TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
// we'll need to re-use that function anyway, in case the GOT grows and overlaps something
// else in virtual memory.
@@ -412,7 +412,7 @@ pub const ElfFile = struct {
assert(self.shstrtab.items.len == 0);
try self.shstrtab.append(self.allocator, 0); // need a 0 at position 0
const off = self.findFreeSpace(self.shstrtab.items.len, 1);
//std.debug.warn("found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
//std.log.debug(.link, "found shstrtab free space 0x{x} to 0x{x}\n", .{ off, off + self.shstrtab.items.len });
try self.sections.append(self.allocator, .{
.sh_name = try self.makeString(".shstrtab"),
.sh_type = elf.SHT_STRTAB,
@@ -470,7 +470,7 @@ pub const ElfFile = struct {
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 off = self.findFreeSpace(file_size, min_align);
//std.debug.warn("found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
//std.log.debug(.link, "found symtab free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
try self.sections.append(self.allocator, .{
.sh_name = try self.makeString(".symtab"),
@@ -586,7 +586,7 @@ pub const ElfFile = struct {
shstrtab_sect.sh_offset = self.findFreeSpace(needed_size, 1);
}
shstrtab_sect.sh_size = needed_size;
//std.debug.warn("shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
//std.log.debug(.link, "shstrtab start=0x{x} end=0x{x}\n", .{ shstrtab_sect.sh_offset, shstrtab_sect.sh_offset + needed_size });
try self.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
if (!self.shdr_table_dirty) {
@@ -632,7 +632,7 @@ pub const ElfFile = struct {
for (buf) |*shdr, i| {
shdr.* = self.sections.items[i];
//std.debug.warn("writing section {}\n", .{shdr.*});
//std.log.debug(.link, "writing section {}\n", .{shdr.*});
if (foreign_endian) {
bswapAllFields(elf.Elf64_Shdr, shdr);
}
@@ -956,10 +956,10 @@ pub const ElfFile = struct {
try self.offset_table_free_list.ensureCapacity(self.allocator, self.local_symbols.items.len);
if (self.local_symbol_free_list.popOrNull()) |i| {
//std.debug.warn("reusing symbol index {} for {}\n", .{i, decl.name});
//std.log.debug(.link, "reusing symbol index {} for {}\n", .{i, decl.name});
decl.link.local_sym_index = i;
} else {
//std.debug.warn("allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
//std.log.debug(.link, "allocating symbol index {} for {}\n", .{self.local_symbols.items.len, decl.name});
decl.link.local_sym_index = @intCast(u32, self.local_symbols.items.len);
_ = self.local_symbols.addOneAssumeCapacity();
}
@@ -1027,11 +1027,11 @@ pub const ElfFile = struct {
!mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
if (need_realloc) {
const vaddr = try self.growTextBlock(&decl.link, code.len, required_alignment);
//std.debug.warn("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
//std.log.debug(.link, "growing {} from 0x{x} to 0x{x}\n", .{ decl.name, local_sym.st_value, vaddr });
if (vaddr != local_sym.st_value) {
local_sym.st_value = vaddr;
//std.debug.warn(" (writing new offset table entry)\n", .{});
//std.log.debug(.link, " (writing new offset table entry)\n", .{});
self.offset_table.items[decl.link.offset_table_index] = vaddr;
try self.writeOffsetTableEntry(decl.link.offset_table_index);
}
@@ -1049,7 +1049,7 @@ pub const ElfFile = struct {
const decl_name = mem.spanZ(decl.name);
const name_str_index = try self.makeString(decl_name);
const vaddr = try self.allocateTextBlock(&decl.link, code.len, required_alignment);
//std.debug.warn("allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
//std.log.debug(.link, "allocated text block for {} at 0x{x}\n", .{ decl_name, vaddr });
errdefer self.freeTextBlock(&decl.link);
local_sym.* = .{
@@ -1307,7 +1307,6 @@ pub const ElfFile = struct {
.p32 => @sizeOf(elf.Elf32_Sym),
.p64 => @sizeOf(elf.Elf64_Sym),
};
//std.debug.warn("symtab start=0x{x} end=0x{x}\n", .{ syms_sect.sh_offset, syms_sect.sh_offset + needed_size });
const foreign_endian = self.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) {

View File

@@ -38,6 +38,30 @@ const usage =
\\
;
pub fn log(
comptime level: std.log.Level,
comptime scope: @TypeOf(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
if (@enumToInt(level) > @enumToInt(std.log.level))
return;
const scope_prefix = "(" ++ switch (scope) {
// Uncomment to hide logs
//.compiler,
.link,
=> return,
else => @tagName(scope),
} ++ "): ";
const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
// Print the message to stderr, silently ignoring any errors
std.debug.print(prefix ++ format, args);
}
pub fn main() !void {
// TODO general purpose allocator in the zig std lib
const gpa = if (std.builtin.link_libc) std.heap.c_allocator else std.heap.page_allocator;
@@ -450,6 +474,7 @@ fn buildOutputType(
.link_mode = link_mode,
.object_format = object_format,
.optimize_mode = build_mode,
.keep_source_files_loaded = zir_out_path != null,
});
defer module.deinit();

View File

@@ -226,20 +226,36 @@ pub const TestContext = struct {
for (self.zir_cases.items) |case| {
std.testing.base_allocator_instance.reset();
var prg_node = root_node.start(case.name, case.updates.items.len);
prg_node.activate();
defer prg_node.end();
// So that we can see which test case failed when the leak checker goes off.
progress.refresh();
const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
try self.runOneZIRCase(std.testing.allocator, root_node, case, info.target);
try self.runOneZIRCase(std.testing.allocator, &prg_node, case, info.target);
try std.testing.allocator_instance.validate();
}
// TODO: wipe the rest of this function
for (self.zir_cmp_output_cases.items) |case| {
std.testing.base_allocator_instance.reset();
try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target);
var prg_node = root_node.start(case.name, case.src_list.len);
prg_node.activate();
defer prg_node.end();
// So that we can see which test case failed when the leak checker goes off.
progress.refresh();
try self.runOneZIRCmpOutputCase(std.testing.allocator, &prg_node, case, native_info.target);
try std.testing.allocator_instance.validate();
}
}
fn runOneZIRCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: ZIRCase, target: std.Target) !void {
fn runOneZIRCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: ZIRCase, target: std.Target) !void {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
@@ -247,10 +263,6 @@ pub const TestContext = struct {
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
defer root_pkg.destroy();
var prg_node = root_node.start(case.name, case.updates.items.len);
prg_node.activate();
defer prg_node.end();
var module = try Module.init(allocator, .{
.target = target,
// This is an Executable, as opposed to e.g. a *library*. This does
@@ -265,6 +277,7 @@ pub const TestContext = struct {
.bin_file_dir = tmp.dir,
.bin_file_path = "test_case.o",
.root_pkg = root_pkg,
.keep_source_files_loaded = true,
});
defer module.deinit();
@@ -329,7 +342,7 @@ pub const TestContext = struct {
}
},
else => return error.unimplemented,
else => return error.Unimplemented,
}
}
}
@@ -337,7 +350,7 @@ pub const TestContext = struct {
fn runOneZIRCmpOutputCase(
self: *TestContext,
allocator: *Allocator,
root_node: *std.Progress.Node,
prg_node: *std.Progress.Node,
case: ZIRCompareOutputCase,
target: std.Target,
) !void {
@@ -348,10 +361,6 @@ pub const TestContext = struct {
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
defer root_pkg.destroy();
var prg_node = root_node.start(case.name, case.src_list.len);
prg_node.activate();
defer prg_node.end();
var module = try Module.init(allocator, .{
.target = target,
.output_mode = .Exe,

View File

@@ -1,6 +1,6 @@
pub const std = @import("std");
pub const enable = @import("build_options").enable_tracy;
pub const enable = if (std.builtin.is_test) false else @import("build_options").enable_tracy;
extern fn ___tracy_emit_zone_begin_callstack(
srcloc: *const ___tracy_source_location_data,

View File

@@ -113,6 +113,12 @@ pub const Type = extern union {
.Undefined => return true,
.Null => return true,
.Pointer => {
// Hot path for common case:
if (a.cast(Payload.SingleConstPointer)) |a_payload| {
if (b.cast(Payload.SingleConstPointer)) |b_payload| {
return eql(a_payload.pointee_type, b_payload.pointee_type);
}
}
const is_slice_a = isSlice(a);
const is_slice_b = isSlice(b);
if (is_slice_a != is_slice_b)

View File

@@ -710,8 +710,9 @@ pub const Module = struct {
} else if (inst.cast(Inst.DeclValInModule)) |decl_val| {
try stream.print("@{}", .{decl_val.positionals.decl.name});
} else {
//try stream.print("?", .{});
unreachable;
// This should be unreachable in theory, but since ZIR is used for debugging the compiler
// we output some debug text instead.
try stream.print("?{}?", .{@tagName(inst.tag)});
}
}
};
@@ -1175,6 +1176,39 @@ const EmitZIR = struct {
// Emit all the decls.
for (src_decls.items) |ir_decl| {
switch (ir_decl.analysis) {
.unreferenced => continue,
.complete => {},
.in_progress => unreachable,
.outdated => unreachable,
.sema_failure,
.sema_failure_retryable,
.codegen_failure,
.dependency_failure,
.codegen_failure_retryable,
=> if (self.old_module.failed_decls.getValue(ir_decl)) |err_msg| {
const fail_inst = try self.arena.allocator.create(Inst.CompileError);
fail_inst.* = .{
.base = .{
.src = ir_decl.src(),
.tag = Inst.CompileError.base_tag,
},
.positionals = .{
.msg = try self.arena.allocator.dupe(u8, err_msg.msg),
},
.kw_args = .{},
};
const decl = try self.arena.allocator.create(Decl);
decl.* = .{
.name = mem.spanZ(ir_decl.name),
.contents_hash = undefined,
.inst = &fail_inst.base,
};
try self.decls.append(self.allocator, decl);
continue;
},
}
if (self.old_module.export_owners.getValue(ir_decl)) |exports| {
for (exports) |module_export| {
const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name);
@@ -1199,20 +1233,27 @@ const EmitZIR = struct {
}
}
fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst {
const ZirBody = struct {
inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
instructions: *std.ArrayList(*Inst),
};
fn resolveInst(self: *EmitZIR, new_body: ZirBody, inst: *ir.Inst) !*Inst {
if (inst.cast(ir.Inst.Constant)) |const_inst| {
const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
const new_inst = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: {
const owner_decl = func_pl.func.owner_decl;
break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name));
} else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: {
break :blk try self.emitDeclRef(inst.src, declref.decl);
const decl_ref = try self.emitDeclRef(inst.src, declref.decl);
try new_body.instructions.append(decl_ref);
break :blk decl_ref;
} else blk: {
break :blk (try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val })).inst;
};
try inst_table.putNoClobber(inst, new_decl);
return new_decl;
try new_body.inst_table.putNoClobber(inst, new_inst);
return new_inst;
} else {
return inst_table.getValue(inst).?;
return new_body.inst_table.getValue(inst).?;
}
}
@@ -1419,6 +1460,10 @@ const EmitZIR = struct {
inst_table: *std.AutoHashMap(*ir.Inst, *Inst),
instructions: *std.ArrayList(*Inst),
) Allocator.Error!void {
const new_body = ZirBody{
.inst_table = inst_table,
.instructions = instructions,
};
for (body.instructions) |inst| {
const new_inst = switch (inst.tag) {
.breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint),
@@ -1428,7 +1473,7 @@ const EmitZIR = struct {
const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
for (args) |*elem, i| {
elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
}
new_inst.* = .{
.base = .{
@@ -1436,7 +1481,7 @@ const EmitZIR = struct {
.tag = Inst.Call.base_tag,
},
.positionals = .{
.func = try self.resolveInst(inst_table, old_inst.args.func),
.func = try self.resolveInst(new_body, old_inst.args.func),
.args = args,
},
.kw_args = .{},
@@ -1453,7 +1498,7 @@ const EmitZIR = struct {
.tag = Inst.Return.base_tag,
},
.positionals = .{
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
.operand = try self.resolveInst(new_body, old_inst.args.operand),
},
.kw_args = .{},
};
@@ -1477,7 +1522,7 @@ const EmitZIR = struct {
const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len);
for (args) |*elem, i| {
elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]);
elem.* = try self.resolveInst(new_body, old_inst.args.args[i]);
}
new_inst.* = .{
@@ -1511,7 +1556,7 @@ const EmitZIR = struct {
.tag = Inst.PtrToInt.base_tag,
},
.positionals = .{
.ptr = try self.resolveInst(inst_table, old_inst.args.ptr),
.ptr = try self.resolveInst(new_body, old_inst.args.ptr),
},
.kw_args = .{},
};
@@ -1527,7 +1572,7 @@ const EmitZIR = struct {
},
.positionals = .{
.dest_type = (try self.emitType(inst.src, inst.ty)).inst,
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
.operand = try self.resolveInst(new_body, old_inst.args.operand),
},
.kw_args = .{},
};
@@ -1542,8 +1587,8 @@ const EmitZIR = struct {
.tag = Inst.Cmp.base_tag,
},
.positionals = .{
.lhs = try self.resolveInst(inst_table, old_inst.args.lhs),
.rhs = try self.resolveInst(inst_table, old_inst.args.rhs),
.lhs = try self.resolveInst(new_body, old_inst.args.lhs),
.rhs = try self.resolveInst(new_body, old_inst.args.rhs),
.op = old_inst.args.op,
},
.kw_args = .{},
@@ -1569,7 +1614,7 @@ const EmitZIR = struct {
.tag = Inst.CondBr.base_tag,
},
.positionals = .{
.condition = try self.resolveInst(inst_table, old_inst.args.condition),
.condition = try self.resolveInst(new_body, old_inst.args.condition),
.true_body = .{ .instructions = true_body.toOwnedSlice() },
.false_body = .{ .instructions = false_body.toOwnedSlice() },
},
@@ -1586,7 +1631,7 @@ const EmitZIR = struct {
.tag = Inst.IsNull.base_tag,
},
.positionals = .{
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
.operand = try self.resolveInst(new_body, old_inst.args.operand),
},
.kw_args = .{},
};
@@ -1601,7 +1646,7 @@ const EmitZIR = struct {
.tag = Inst.IsNonNull.base_tag,
},
.positionals = .{
.operand = try self.resolveInst(inst_table, old_inst.args.operand),
.operand = try self.resolveInst(new_body, old_inst.args.operand),
},
.kw_args = .{},
};

View File

@@ -27,9 +27,8 @@ pub fn addCases(ctx: *TestContext) !void {
\\ %0 = call(@notafunc, [])
\\})
\\@0 = str("_start")
\\@1 = ref(@0)
\\@2 = export(@1, @start)
, &[_][]const u8{":5:13: error: use of undeclared identifier 'notafunc'"});
\\@1 = export(@0, "start")
, &[_][]const u8{":5:13: error: decl 'notafunc' not found"});
// TODO: this error should occur at the call site, not the fntype decl
ctx.addZIRError("call naked function", linux_x64,
@@ -41,8 +40,7 @@ pub fn addCases(ctx: *TestContext) !void {
\\ %0 = call(@s, [])
\\})
\\@0 = str("_start")
\\@1 = ref(@0)
\\@2 = export(@1, @start)
\\@1 = export(@0, "start")
, &[_][]const u8{":4:9: error: unable to call function with naked calling convention"});
// TODO: re-enable these tests.

View File

@@ -14,23 +14,21 @@ pub fn addCases(ctx: *TestContext) void {
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@10 = ref(@9)
\\@11 = export(@10, @entry)
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %11 = return()
\\ %11 = returnvoid()
\\})
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = str("entry")
\\@10 = ref(@9)
\\@unnamed$6 = str("entry")
\\@unnamed$7 = ref(@unnamed$6)
\\@unnamed$8 = export(@unnamed$7, @entry)
\\@unnamed$10 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$10, {
\\ %0 = return()
\\@9 = declref("9$0")
\\@9$0 = str("entry")
\\@unnamed$4 = str("entry")
\\@unnamed$5 = export(@unnamed$4, "entry")
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = returnvoid()
\\})
\\
);
@@ -45,11 +43,10 @@ pub fn addCases(ctx: *TestContext) void {
\\
\\@entry = fn(@fnty, {
\\ %a = str("\x32\x08\x01\x0a")
\\ %aref = ref(%a)
\\ %eptr0 = elemptr(%aref, @0)
\\ %eptr1 = elemptr(%aref, @1)
\\ %eptr2 = elemptr(%aref, @2)
\\ %eptr3 = elemptr(%aref, @3)
\\ %eptr0 = elemptr(%a, @0)
\\ %eptr1 = elemptr(%a, @1)
\\ %eptr2 = elemptr(%a, @2)
\\ %eptr3 = elemptr(%a, @3)
\\ %v0 = deref(%eptr0)
\\ %v1 = deref(%eptr1)
\\ %v2 = deref(%eptr2)
@@ -61,15 +58,14 @@ pub fn addCases(ctx: *TestContext) void {
\\ %expected = int(69)
\\ %ok = cmp(%result, eq, %expected)
\\ %10 = condbr(%ok, {
\\ %11 = return()
\\ %11 = returnvoid()
\\ }, {
\\ %12 = breakpoint()
\\ })
\\})
\\
\\@9 = str("entry")
\\@10 = ref(@9)
\\@11 = export(@10, @entry)
\\@11 = export(@9, "entry")
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
@@ -77,16 +73,15 @@ pub fn addCases(ctx: *TestContext) void {
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\@unnamed$7 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$7, {
\\ %0 = return()
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = returnvoid()
\\})
\\@a = str("2\x08\x01\n")
\\@9 = str("entry")
\\@10 = ref(@9)
\\@unnamed$14 = str("entry")
\\@unnamed$15 = ref(@unnamed$14)
\\@unnamed$16 = export(@unnamed$15, @entry)
\\@entry$1 = str("2\x08\x01\n")
\\@9 = declref("9$0")
\\@9$0 = str("entry")
\\@unnamed$11 = str("entry")
\\@unnamed$12 = export(@unnamed$11, "entry")
\\
);
@@ -97,45 +92,43 @@ pub fn addCases(ctx: *TestContext) void {
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@10 = ref(@9)
\\@11 = export(@10, @entry)
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %0 = call(@a, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\
\\@a = fn(@fnty, {
\\ %0 = call(@b, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\
\\@b = fn(@fnty, {
\\ %0 = call(@a, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = str("entry")
\\@10 = ref(@9)
\\@unnamed$6 = str("entry")
\\@unnamed$7 = ref(@unnamed$6)
\\@unnamed$8 = export(@unnamed$7, @entry)
\\@unnamed$12 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$12, {
\\@9 = declref("9$0")
\\@9$0 = str("entry")
\\@unnamed$4 = str("entry")
\\@unnamed$5 = export(@unnamed$4, "entry")
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = call(@a, [], modifier=auto)
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\@unnamed$17 = fntype([], @void, cc=C)
\\@a = fn(@unnamed$17, {
\\@unnamed$8 = fntype([], @void, cc=C)
\\@a = fn(@unnamed$8, {
\\ %0 = call(@b, [], modifier=auto)
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\@unnamed$22 = fntype([], @void, cc=C)
\\@b = fn(@unnamed$22, {
\\@unnamed$10 = fntype([], @void, cc=C)
\\@b = fn(@unnamed$10, {
\\ %0 = call(@a, [], modifier=auto)
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\
);
@@ -145,27 +138,26 @@ pub fn addCases(ctx: *TestContext) void {
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@10 = ref(@9)
\\@11 = export(@10, @entry)
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %0 = call(@a, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\
\\@a = fn(@fnty, {
\\ %0 = call(@b, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\
\\@b = fn(@fnty, {
\\ %9 = compileerror("message")
\\ %0 = call(@a, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
,
&[_][]const u8{
":19:21: error: message",
":18:21: error: message",
},
);
// Now we remove the call to `a`. `a` and `b` form a cycle, but no entry points are
@@ -176,34 +168,32 @@ pub fn addCases(ctx: *TestContext) void {
\\@fnty = fntype([], @void, cc=C)
\\
\\@9 = str("entry")
\\@10 = ref(@9)
\\@11 = export(@10, @entry)
\\@11 = export(@9, "entry")
\\
\\@entry = fn(@fnty, {
\\ %1 = return()
\\ %0 = returnvoid()
\\})
\\
\\@a = fn(@fnty, {
\\ %0 = call(@b, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
\\
\\@b = fn(@fnty, {
\\ %9 = compileerror("message")
\\ %0 = call(@a, [])
\\ %1 = return()
\\ %1 = returnvoid()
\\})
,
\\@void = primitive(void)
\\@fnty = fntype([], @void, cc=C)
\\@9 = str("entry")
\\@10 = ref(@9)
\\@unnamed$6 = str("entry")
\\@unnamed$7 = ref(@unnamed$6)
\\@unnamed$8 = export(@unnamed$7, @entry)
\\@unnamed$10 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$10, {
\\ %0 = return()
\\@9 = declref("9$2")
\\@9$2 = str("entry")
\\@unnamed$4 = str("entry")
\\@unnamed$5 = export(@unnamed$4, "entry")
\\@unnamed$6 = fntype([], @void, cc=C)
\\@entry = fn(@unnamed$6, {
\\ %0 = returnvoid()
\\})
\\
);
@@ -218,7 +208,7 @@ pub fn addCases(ctx: *TestContext) void {
}
ctx.addZIRCompareOutput(
"hello world ZIR, update msg",
"hello world ZIR",
&[_][]const u8{
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
@@ -272,125 +262,10 @@ pub fn addCases(ctx: *TestContext) void {
\\
\\@9 = str("_start")
\\@11 = export(@9, "start")
,
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@msg = str("Hello, world!\n")
\\@msg2 = str("HELL WORLD\n")
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %rdx = str("{rdx}")
\\ %rsi = str("{rsi}")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_addr = ptrtoint(@msg2)
\\
\\ %len_name = str("len")
\\ %msg_len_ptr = fieldptr(@msg2, %len_name)
\\ %msg_len = deref(%msg_len_ptr)
\\ %rc_write = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi, %rsi, %rdx],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
\\
\\ %rc_exit = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@9 = str("_start")
\\@11 = export(@9, "start")
,
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@msg = str("Hello, world!\n")
\\@msg2 = str("Editing the same msg2 decl but this time with a much longer message which will\ncause the data to need to be relocated in virtual address space.\n")
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %rdx = str("{rdx}")
\\ %rsi = str("{rsi}")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_addr = ptrtoint(@msg2)
\\
\\ %len_name = str("len")
\\ %msg_len_ptr = fieldptr(@msg2, %len_name)
\\ %msg_len = deref(%msg_len_ptr)
\\ %rc_write = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi, %rsi, %rdx],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_write, %STDOUT_FILENO, %msg_addr, %msg_len])
\\
\\ %rc_exit = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@9 = str("_start")
\\@11 = export(@9, "start")
},
&[_][]const u8{
\\Hello, world!
\\
,
\\HELL WORLD
\\
,
\\Editing the same msg2 decl but this time with a much longer message which will
\\cause the data to need to be relocated in virtual address space.
\\
},
);
@@ -405,26 +280,18 @@ pub fn addCases(ctx: *TestContext) void {
\\@2 = int(2)
\\@3 = int(3)
\\
\\@syscall_array = str("syscall")
\\@sysoutreg_array = str("={rax}")
\\@rax_array = str("{rax}")
\\@rdi_array = str("{rdi}")
\\@rcx_array = str("rcx")
\\@r11_array = str("r11")
\\@memory_array = str("memory")
\\
\\@exit0_fnty = fntype([], @noreturn)
\\@exit0 = fn(@exit0_fnty, {
\\ %SYS_exit_group = int(231)
\\ %exit_code = as(@usize, @0)
\\
\\ %syscall = ref(@syscall_array)
\\ %sysoutreg = ref(@sysoutreg_array)
\\ %rax = ref(@rax_array)
\\ %rdi = ref(@rdi_array)
\\ %rcx = ref(@rcx_array)
\\ %r11 = ref(@r11_array)
\\ %memory = ref(@memory_array)
\\ %syscall = str("syscall")
\\ %sysoutreg = str("={rax}")
\\ %rax = str("{rax}")
\\ %rdi = str("{rdi}")
\\ %rcx = str("rcx")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %rc = asm(%syscall, @usize,
\\ volatile=1,
@@ -441,8 +308,7 @@ pub fn addCases(ctx: *TestContext) void {
\\ %0 = call(@exit0, [])
\\})
\\@9 = str("_start")
\\@10 = ref(@9)
\\@11 = export(@10, @start)
\\@11 = export(@9, "start")
},
&[_][]const u8{""},
);