Merge pull request #5583 from ziglang/zig-ast-to-zir

self-hosted: hook up Zig AST to ZIR
This commit is contained in:
Andrew Kelley 2020-06-24 22:37:58 -04:00 committed by GitHub
commit d337469e44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 2575 additions and 1199 deletions

View File

@ -72,9 +72,22 @@ pub fn build(b: *Builder) !void {
if (!only_install_lib_files) {
exe.install();
}
const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source");
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
if (link_libc) exe.linkLibC();
exe.addBuildOption(bool, "enable_tracy", tracy != null);
if (tracy) |tracy_path| {
const client_cpp = fs.path.join(
b.allocator,
&[_][]const u8{ tracy_path, "TracyClient.cpp" },
) catch unreachable;
exe.addIncludeDir(tracy_path);
exe.addCSourceFile(client_cpp, &[_][]const u8{ "-DTRACY_ENABLE=1", "-fno-sanitize=undefined" });
exe.linkSystemLibraryName("c++");
exe.linkLibC();
}
b.installDirectory(InstallDirectoryOptions{
.source_dir = "lib",
.install_dir = .Lib,

View File

@ -1905,10 +1905,11 @@ pub const LibExeObjStep = struct {
builder.allocator,
&[_][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", .{self.name}) },
);
try fs.cwd().writeFile(build_options_file, self.build_options_contents.span());
const path_from_root = builder.pathFromRoot(build_options_file);
try fs.cwd().writeFile(path_from_root, self.build_options_contents.span());
try zig_args.append("--pkg-begin");
try zig_args.append("build_options");
try zig_args.append(builder.pathFromRoot(build_options_file));
try zig_args.append(path_from_root);
try zig_args.append("--pkg-end");
}

View File

@ -1,4 +1,6 @@
const std = @import("std.zig");
const tokenizer = @import("zig/tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
pub const parse = @import("zig/parse.zig").parse;
@ -9,6 +11,21 @@ pub const ast = @import("zig/ast.zig");
pub const system = @import("zig/system.zig");
pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget;
pub const SrcHash = [16]u8;
/// If the source is small enough, it is used directly as the hash.
/// If it is long, blake3 hash is computed.
pub fn hashSrc(src: []const u8) SrcHash {
var out: SrcHash = undefined;
if (src.len <= SrcHash.len) {
std.mem.copy(u8, &out, src);
std.mem.set(u8, out[src.len..], 0);
} else {
std.crypto.Blake3.hash(src, &out);
}
return out;
}
pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
var line: usize = 0;
var column: usize = 0;
@ -26,6 +43,27 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
return .{ .line = line, .column = column };
}
/// Returns the standard file system basename of a binary generated by the Zig compiler.
pub fn binNameAlloc(
allocator: *std.mem.Allocator,
root_name: []const u8,
target: std.Target,
output_mode: std.builtin.OutputMode,
link_mode: ?std.builtin.LinkMode,
) error{OutOfMemory}![]u8 {
switch (output_mode) {
.Exe => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.exeFileExt() }),
.Lib => {
const suffix = switch (link_mode orelse .Static) {
.Static => target.staticLibSuffix(),
.Dynamic => target.dynamicLibSuffix(),
};
return std.fmt.allocPrint(allocator, "{}{}{}", .{ target.libPrefix(), root_name, suffix });
},
.Obj => return std.fmt.allocPrint(allocator, "{}{}", .{ root_name, target.oFileExt() }),
}
}
test "" {
@import("std").meta.refAllDecls(@This());
}

View File

@ -2260,6 +2260,8 @@ pub const Node = struct {
}
};
/// TODO break this into separate Break, Continue, Return AST Nodes to save memory.
/// Could be further broken into LabeledBreak, LabeledContinue, and ReturnVoid to save even more.
pub const ControlFlowExpression = struct {
base: Node = Node{ .id = .ControlFlowExpression },
ltoken: TokenIndex,

View File

@ -3222,7 +3222,7 @@ const Parser = struct {
}
/// Op* Child
fn parsePrefixOpExpr(p: *Parser, opParseFn: NodeParseFn, childParseFn: NodeParseFn) Error!?*Node {
fn parsePrefixOpExpr(p: *Parser, comptime opParseFn: NodeParseFn, comptime childParseFn: NodeParseFn) Error!?*Node {
if (try opParseFn(p)) |first_op| {
var rightmost_op = first_op;
while (true) {

File diff suppressed because it is too large Load Diff

View File

@ -21,3 +21,11 @@ pub const Managed = struct {
self.* = undefined;
}
};
/// Assumes arena allocation. Does a recursive copy.
pub fn copy(self: TypedValue, allocator: *Allocator) error{OutOfMemory}!TypedValue {
return TypedValue{
.ty = try self.ty.copy(allocator),
.val = try self.val.copy(allocator),
};
}

View File

@ -10,6 +10,7 @@ const Module = @import("Module.zig");
const ErrorMsg = Module.ErrorMsg;
const Target = std.Target;
const Allocator = mem.Allocator;
const trace = @import("tracy.zig").trace;
pub const Result = union(enum) {
/// The `code` parameter passed to `generateSymbol` has the value appended.
@ -29,6 +30,9 @@ pub fn generateSymbol(
/// A Decl that this symbol depends on had a semantic analysis failure.
AnalysisFail,
}!Result {
const tracy = trace(@src());
defer tracy.end();
switch (typed_value.ty.zigTypeTag()) {
.Fn => {
const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
@ -178,6 +182,7 @@ const Function = struct {
.ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
.bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
.ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
.retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
.cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
.condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
.isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
@ -213,7 +218,7 @@ const Function = struct {
try self.code.resize(self.code.items.len + 7);
self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };
mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);
const return_type = func.fn_type.fnReturnType();
const return_type = func.owner_decl.typed_value.most_recent.typed_value.ty.fnReturnType();
switch (return_type.zigTypeTag()) {
.Void => return MCValue{ .none = {} },
.NoReturn => return MCValue{ .unreach = {} },
@ -230,16 +235,28 @@ const Function = struct {
}
}
fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
fn ret(self: *Function, src: usize, mcv: MCValue) !MCValue {
if (mcv != .none) {
return self.fail(src, "TODO implement return with non-void operand", .{});
}
switch (self.target.cpu.arch) {
.i386, .x86_64 => {
try self.code.append(0xc3); // ret
},
else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.target.cpu.arch}),
else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
}
return .unreach;
}
fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue {
const operand = try self.resolveInst(inst.args.operand);
return self.ret(inst.base.src, operand);
}
fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid) !MCValue {
return self.ret(inst.base.src, .none);
}
fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue {
switch (self.target.cpu.arch) {
else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),

View File

@ -26,6 +26,7 @@ pub const Inst = struct {
isnull,
ptrtoint,
ret,
retvoid,
unreach,
};
@ -146,6 +147,14 @@ pub const Inst = struct {
pub const Ret = struct {
pub const base_tag = Tag.ret;
base: Inst,
args: struct {
operand: *Inst,
},
};
pub const RetVoid = struct {
pub const base_tag = Tag.retvoid;
base: Inst,
args: void,
};

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();
}
@ -1002,7 +1002,7 @@ pub const ElfFile = struct {
defer code_buffer.deinit();
const typed_value = decl.typed_value.most_recent.typed_value;
const code = switch (try codegen.generateSymbol(self, decl.src, typed_value, &code_buffer)) {
const code = switch (try codegen.generateSymbol(self, decl.src(), typed_value, &code_buffer)) {
.externally_managed => |x| x,
.appended => code_buffer.items,
.fail => |em| {
@ -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,29 @@ 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;
@ -86,7 +109,7 @@ const usage_build_generic =
\\ zig build-obj <options> [files]
\\
\\Supported file types:
\\ (planned) .zig Zig source code
\\ .zig Zig source code
\\ .zir Zig Intermediate Representation code
\\ (planned) .o ELF object file
\\ (planned) .o MACH-O (macOS) object file
@ -407,21 +430,7 @@ fn buildOutputType(
std.debug.warn("-fno-emit-bin not supported yet", .{});
process.exit(1);
},
.yes_default_path => switch (output_mode) {
.Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
.Lib => blk: {
const suffix = switch (link_mode orelse .Static) {
.Static => target_info.target.staticLibSuffix(),
.Dynamic => target_info.target.dynamicLibSuffix(),
};
break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{
target_info.target.libPrefix(),
root_name,
suffix,
});
},
.Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }),
},
.yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
.yes => |p| p,
};
@ -450,6 +459,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();
@ -487,7 +497,9 @@ fn buildOutputType(
}
fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
var timer = try std.time.Timer.start();
try module.update();
const update_nanos = timer.read();
var errors = try module.getAllErrorsAlloc();
defer errors.deinit(module.allocator);
@ -501,6 +513,8 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
full_err_msg.msg,
});
}
} else {
std.log.info(.compiler, "Update completed in {} ms\n", .{update_nanos / std.time.ns_per_ms});
}
if (zir_out_path) |zop| {

View File

@ -21,32 +21,7 @@ const ErrorMsg = struct {
};
pub const TestContext = struct {
// TODO: remove these. They are deprecated.
zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase),
/// TODO: find a way to treat cases as individual tests (shouldn't show "1 test passed" if there are 200 cases)
zir_cases: std.ArrayList(ZIRCase),
// TODO: remove
pub const ZIRCompareOutputCase = struct {
name: []const u8,
src_list: []const []const u8,
expected_stdout_list: []const []const u8,
};
pub const ZIRUpdateType = enum {
/// A transformation update transforms the input ZIR and tests against
/// the expected output
Transformation,
/// An error update attempts to compile bad code, and ensures that it
/// fails to compile, and for the expected reasons
Error,
/// An execution update compiles and runs the input ZIR, feeding in
/// provided input and ensuring that the outputs match what is expected
Execution,
/// A compilation update checks that the ZIR compiles without any issues
Compiles,
};
zir_cases: std.ArrayList(Case),
pub const ZIRUpdate = struct {
/// The input to the current update. We simulate an incremental update
@ -57,58 +32,55 @@ pub const TestContext = struct {
/// you can keep it mostly consistent, with small changes, testing the
/// effects of the incremental compilation.
src: [:0]const u8,
case: union(ZIRUpdateType) {
/// The expected output ZIR
case: union(enum) {
/// A transformation update transforms the input ZIR and tests against
/// the expected output ZIR.
Transformation: [:0]const u8,
/// An error update attempts to compile bad code, and ensures that it
/// fails to compile, and for the expected reasons.
/// A slice containing the expected errors *in sequential order*.
Error: []const ErrorMsg,
/// Input to feed to the program, and expected outputs.
///
/// If stdout, stderr, and exit_code are all null, addZIRCase will
/// discard the test. To test for successful compilation, use a
/// dedicated Compile update instead.
Execution: struct {
stdin: ?[]const u8,
stdout: ?[]const u8,
stderr: ?[]const u8,
exit_code: ?u8,
},
/// A Compiles test checks only that compilation of the given ZIR
/// succeeds. To test outputs, use an Execution test. It is good to
/// use a Compiles test before an Execution, as the overhead should
/// be low (due to incremental compilation) and TODO: provide a way
/// to check changed / new / etc decls in testing mode
/// (usingnamespace a debug info struct with a comptime flag?)
Compiles: void,
/// An execution update compiles and runs the input ZIR, feeding in
/// provided input and ensuring that the stdout match what is expected.
Execution: []const u8,
},
};
/// A ZIRCase consists of a set of *updates*. A update can transform ZIR,
/// A Case consists of a set of *updates*. A update can transform ZIR,
/// compile it, ensure that compilation fails, and more. The same Module is
/// used for each update, so each update's source is treated as a single file
/// being updated by the test harness and incrementally compiled.
pub const ZIRCase = struct {
pub const Case = struct {
name: []const u8,
/// The platform the ZIR targets. For non-native platforms, an emulator
/// such as QEMU is required for tests to complete.
target: std.zig.CrossTarget,
updates: std.ArrayList(ZIRUpdate),
output_mode: std.builtin.OutputMode,
/// Either ".zir" or ".zig"
extension: [4]u8,
/// Adds a subcase in which the module is updated with new ZIR, and the
/// resulting ZIR is validated.
pub fn addTransform(self: *ZIRCase, src: [:0]const u8, result: [:0]const u8) void {
pub fn addTransform(self: *Case, src: [:0]const u8, result: [:0]const u8) void {
self.updates.append(.{
.src = src,
.case = .{ .Transformation = result },
}) catch unreachable;
}
pub fn addCompareOutput(self: *Case, src: [:0]const u8, result: []const u8) void {
self.updates.append(.{
.src = src,
.case = .{ .Execution = result },
}) catch unreachable;
}
/// Adds a subcase in which the module is updated with invalid ZIR, and
/// ensures that compilation fails for the expected reasons.
///
/// Errors must be specified in sequential order.
pub fn addError(self: *ZIRCase, src: [:0]const u8, errors: []const []const u8) void {
pub fn addError(self: *Case, src: [:0]const u8, errors: []const []const u8) void {
var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
for (errors) |e, i| {
if (e[0] != ':') {
@ -146,15 +118,65 @@ pub const TestContext = struct {
}
};
pub fn addZIRMulti(
pub fn addExeZIR(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *ZIRCase {
const case = ZIRCase{
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Exe,
.extension = ".zir".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
}
pub fn addObjZIR(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Obj,
.extension = ".zir".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
}
pub fn addExe(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Exe,
.extension = ".zig".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
}
pub fn addObj(
ctx: *TestContext,
name: []const u8,
target: std.zig.CrossTarget,
) *Case {
const case = Case{
.name = name,
.target = target,
.updates = std.ArrayList(ZIRUpdate).init(ctx.zir_cases.allocator),
.output_mode = .Obj,
.extension = ".zig".*,
};
ctx.zir_cases.append(case) catch unreachable;
return &ctx.zir_cases.items[ctx.zir_cases.items.len - 1];
@ -163,14 +185,21 @@ pub const TestContext = struct {
pub fn addZIRCompareOutput(
ctx: *TestContext,
name: []const u8,
src_list: []const []const u8,
expected_stdout_list: []const []const u8,
src: [:0]const u8,
expected_stdout: []const u8,
) void {
ctx.zir_cmp_output_cases.append(.{
.name = name,
.src_list = src_list,
.expected_stdout_list = expected_stdout_list,
}) catch unreachable;
var c = ctx.addExeZIR(name, .{});
c.addCompareOutput(src, expected_stdout);
}
pub fn addCompareOutput(
ctx: *TestContext,
name: []const u8,
src: [:0]const u8,
expected_stdout: []const u8,
) void {
var c = ctx.addExe(name, .{});
c.addCompareOutput(src, expected_stdout);
}
pub fn addZIRTransform(
@ -180,7 +209,7 @@ pub const TestContext = struct {
src: [:0]const u8,
result: [:0]const u8,
) void {
var c = ctx.addZIRMulti(name, target);
var c = ctx.addObjZIR(name, target);
c.addTransform(src, result);
}
@ -191,20 +220,18 @@ pub const TestContext = struct {
src: [:0]const u8,
expected_errors: []const []const u8,
) void {
var c = ctx.addZIRMulti(name, target);
var c = ctx.addObjZIR(name, target);
c.addError(src, expected_errors);
}
fn init() TestContext {
const allocator = std.heap.page_allocator;
return .{
.zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator),
.zir_cases = std.ArrayList(ZIRCase).init(allocator),
.zir_cases = std.ArrayList(Case).init(allocator),
};
}
fn deinit(self: *TestContext) void {
self.zir_cmp_output_cases.deinit();
for (self.zir_cases.items) |c| {
for (c.updates.items) |u| {
if (u.case == .Error) {
@ -226,30 +253,32 @@ pub const TestContext = struct {
for (self.zir_cases.items) |case| {
std.testing.base_allocator_instance.reset();
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 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.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.runOneCase(std.testing.allocator, &prg_node, case, 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 runOneCase(self: *TestContext, allocator: *Allocator, prg_node: *std.Progress.Node, case: Case, target: std.Target) !void {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const tmp_src_path = "test_case.zir";
const root_name = "test_case";
const tmp_src_path = try std.fmt.allocPrint(allocator, "{}{}", .{ root_name, case.extension });
defer allocator.free(tmp_src_path);
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();
const bin_name = try std.zig.binNameAlloc(allocator, root_name, target, case.output_mode, null);
defer allocator.free(bin_name);
var module = try Module.init(allocator, .{
.target = target,
@ -259,16 +288,17 @@ pub const TestContext = struct {
// TODO: support tests for object file building, and library builds
// and linking. This will require a rework to support multi-file
// tests.
.output_mode = .Obj,
.output_mode = case.output_mode,
// TODO: support testing optimizations
.optimize_mode = .Debug,
.bin_file_dir = tmp.dir,
.bin_file_path = "test_case.o",
.bin_file_path = bin_name,
.root_pkg = root_pkg,
.keep_source_files_loaded = true,
});
defer module.deinit();
for (case.updates.items) |update| {
for (case.updates.items) |update, update_index| {
var update_node = prg_node.start("update", 4);
update_node.activate();
defer update_node.end();
@ -280,6 +310,7 @@ pub const TestContext = struct {
var module_node = update_node.start("parse/analysis/codegen", null);
module_node.activate();
try module.makeBinFileWritable();
try module.update();
module_node.end();
@ -328,82 +359,41 @@ pub const TestContext = struct {
}
}
},
.Execution => |expected_stdout| {
var exec_result = x: {
var exec_node = update_node.start("execute", null);
exec_node.activate();
defer exec_node.end();
else => return error.unimplemented,
}
}
}
try module.makeBinFileExecutable();
fn runOneZIRCmpOutputCase(
self: *TestContext,
allocator: *Allocator,
root_node: *std.Progress.Node,
case: ZIRCompareOutputCase,
target: std.Target,
) !void {
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
defer allocator.free(exe_path);
const tmp_src_path = "test-case.zir";
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,
.optimize_mode = .Debug,
.bin_file_dir = tmp.dir,
.bin_file_path = "a.out",
.root_pkg = root_pkg,
});
defer module.deinit();
for (case.src_list) |source, i| {
var src_node = prg_node.start("update", 2);
src_node.activate();
defer src_node.end();
try tmp.dir.writeFile(tmp_src_path, source);
var update_node = src_node.start("parse,analysis,codegen", null);
update_node.activate();
try module.makeBinFileWritable();
try module.update();
update_node.end();
var exec_result = x: {
var exec_node = src_node.start("execute", null);
exec_node.activate();
defer exec_node.end();
try module.makeBinFileExecutable();
break :x try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = &[_][]const u8{"./a.out"},
.cwd_dir = tmp.dir,
});
};
defer allocator.free(exec_result.stdout);
defer allocator.free(exec_result.stderr);
switch (exec_result.term) {
.Exited => |code| {
if (code != 0) {
std.debug.warn("elf file exited with code {}\n", .{code});
return error.BinaryBadExitCode;
break :x try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = &[_][]const u8{exe_path},
.cwd_dir = tmp.dir,
});
};
defer allocator.free(exec_result.stdout);
defer allocator.free(exec_result.stderr);
switch (exec_result.term) {
.Exited => |code| {
if (code != 0) {
std.debug.warn("elf file exited with code {}\n", .{code});
return error.BinaryBadExitCode;
}
},
else => return error.BinaryCrashed,
}
if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
std.debug.panic(
"update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
.{ update_index, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
);
}
},
else => return error.BinaryCrashed,
}
const expected_stdout = case.expected_stdout_list[i];
if (!std.mem.eql(u8, expected_stdout, exec_result.stdout)) {
std.debug.panic(
"update index {}, mismatched stdout\n====Expected (len={}):====\n{}\n====Actual (len={}):====\n{}\n========\n",
.{ i, expected_stdout.len, expected_stdout, exec_result.stdout.len, exec_result.stdout },
);
}
}
}

45
src-self-hosted/tracy.zig Normal file
View File

@ -0,0 +1,45 @@
pub const std = @import("std");
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,
depth: c_int,
active: c_int,
) ___tracy_c_zone_context;
extern fn ___tracy_emit_zone_end(ctx: ___tracy_c_zone_context) void;
pub const ___tracy_source_location_data = extern struct {
name: ?[*:0]const u8,
function: [*:0]const u8,
file: [*:0]const u8,
line: u32,
color: u32,
};
pub const ___tracy_c_zone_context = extern struct {
id: u32,
active: c_int,
pub fn end(self: ___tracy_c_zone_context) void {
___tracy_emit_zone_end(self);
}
};
pub const Ctx = if (enable) ___tracy_c_zone_context else struct {
pub fn end(self: Ctx) void {}
};
pub inline fn trace(comptime src: std.builtin.SourceLocation) Ctx {
if (!enable) return .{};
const loc: ___tracy_source_location_data = .{
.name = null,
.function = src.fn_name.ptr,
.file = src.file.ptr,
.line = src.line,
.color = 0,
};
return ___tracy_emit_zone_begin_callstack(&loc, 1, 1);
}

View File

@ -54,6 +54,7 @@ pub const Type = extern union {
.@"undefined" => return .Undefined,
.fn_noreturn_no_args => return .Fn,
.fn_void_no_args => return .Fn,
.fn_naked_noreturn_no_args => return .Fn,
.fn_ccc_void_no_args => return .Fn,
@ -112,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)
@ -163,6 +170,77 @@ pub const Type = extern union {
}
}
pub fn copy(self: Type, allocator: *Allocator) error{OutOfMemory}!Type {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return Type{ .tag_if_small_enough = self.tag_if_small_enough };
} else switch (self.ptr_otherwise.tag) {
.u8,
.i8,
.isize,
.usize,
.c_short,
.c_ushort,
.c_int,
.c_uint,
.c_long,
.c_ulong,
.c_longlong,
.c_ulonglong,
.c_longdouble,
.c_void,
.f16,
.f32,
.f64,
.f128,
.bool,
.void,
.type,
.anyerror,
.comptime_int,
.comptime_float,
.noreturn,
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
.const_slice_u8,
=> unreachable,
.array_u8_sentinel_0 => return self.copyPayloadShallow(allocator, Payload.Array_u8_Sentinel0),
.array => {
const payload = @fieldParentPtr(Payload.Array, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Array);
new_payload.* = .{
.base = payload.base,
.len = payload.len,
.elem_type = try payload.elem_type.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.single_const_pointer => {
const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.SingleConstPointer);
new_payload.* = .{
.base = payload.base,
.pointee_type = try payload.pointee_type.copy(allocator),
};
return Type{ .ptr_otherwise = &new_payload.base };
},
.int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
.int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
}
}
fn copyPayloadShallow(self: Type, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Type {
const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
const new_payload = try allocator.create(T);
new_payload.* = payload.*;
return Type{ .ptr_otherwise = &new_payload.base };
}
pub fn format(
self: Type,
comptime fmt: []const u8,
@ -206,6 +284,7 @@ pub const Type = extern union {
.const_slice_u8 => return out_stream.writeAll("[]const u8"),
.fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"),
.fn_void_no_args => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"),
@ -269,6 +348,7 @@ pub const Type = extern union {
.@"null" => return Value.initTag(.null_type),
.@"undefined" => return Value.initTag(.undefined_type),
.fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type),
.fn_void_no_args => return Value.initTag(.fn_void_no_args_type),
.fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type),
.fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type),
.single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type),
@ -303,6 +383,7 @@ pub const Type = extern union {
.bool,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@ -333,6 +414,7 @@ pub const Type = extern union {
.i8,
.bool,
.fn_noreturn_no_args, // represents machine code; not a pointer
.fn_void_no_args, // represents machine code; not a pointer
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
.fn_ccc_void_no_args, // represents machine code; not a pointer
.array_u8_sentinel_0,
@ -420,6 +502,7 @@ pub const Type = extern union {
.array_u8_sentinel_0,
.const_slice_u8,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -466,6 +549,7 @@ pub const Type = extern union {
.single_const_pointer,
.single_const_pointer_to_comptime_int,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -509,6 +593,7 @@ pub const Type = extern union {
.array,
.array_u8_sentinel_0,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -553,6 +638,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.int_unsigned,
@ -597,6 +683,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer,
@ -642,6 +729,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer,
@ -675,6 +763,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@ -721,6 +810,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@ -777,6 +867,7 @@ pub const Type = extern union {
pub fn fnParamLen(self: Type) usize {
return switch (self.tag()) {
.fn_noreturn_no_args => 0,
.fn_void_no_args => 0,
.fn_naked_noreturn_no_args => 0,
.fn_ccc_void_no_args => 0,
@ -823,6 +914,7 @@ pub const Type = extern union {
pub fn fnParamTypes(self: Type, types: []Type) void {
switch (self.tag()) {
.fn_noreturn_no_args => return,
.fn_void_no_args => return,
.fn_naked_noreturn_no_args => return,
.fn_ccc_void_no_args => return,
@ -869,7 +961,10 @@ pub const Type = extern union {
return switch (self.tag()) {
.fn_noreturn_no_args => Type.initTag(.noreturn),
.fn_naked_noreturn_no_args => Type.initTag(.noreturn),
.fn_ccc_void_no_args => Type.initTag(.void),
.fn_void_no_args,
.fn_ccc_void_no_args,
=> Type.initTag(.void),
.f16,
.f32,
@ -913,6 +1008,7 @@ pub const Type = extern union {
pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention {
return switch (self.tag()) {
.fn_noreturn_no_args => .Unspecified,
.fn_void_no_args => .Unspecified,
.fn_naked_noreturn_no_args => .Naked,
.fn_ccc_void_no_args => .C,
@ -958,6 +1054,7 @@ pub const Type = extern union {
pub fn fnIsVarArgs(self: Type) bool {
return switch (self.tag()) {
.fn_noreturn_no_args => false,
.fn_void_no_args => false,
.fn_naked_noreturn_no_args => false,
.fn_ccc_void_no_args => false,
@ -1033,6 +1130,7 @@ pub const Type = extern union {
.@"null",
.@"undefined",
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.array,
@ -1070,6 +1168,7 @@ pub const Type = extern union {
.type,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@ -1126,6 +1225,7 @@ pub const Type = extern union {
.type,
.anyerror,
.fn_noreturn_no_args,
.fn_void_no_args,
.fn_naked_noreturn_no_args,
.fn_ccc_void_no_args,
.single_const_pointer_to_comptime_int,
@ -1180,6 +1280,7 @@ pub const Type = extern union {
@"null",
@"undefined",
fn_noreturn_no_args,
fn_void_no_args,
fn_naked_noreturn_no_args,
fn_ccc_void_no_args,
single_const_pointer_to_comptime_int,

View File

@ -49,6 +49,7 @@ pub const Value = extern union {
null_type,
undefined_type,
fn_noreturn_no_args_type,
fn_void_no_args_type,
fn_naked_noreturn_no_args_type,
fn_ccc_void_no_args_type,
single_const_pointer_to_comptime_int_type,
@ -78,8 +79,8 @@ pub const Value = extern union {
pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
};
pub fn initTag(comptime small_tag: Tag) Value {
comptime assert(@enumToInt(small_tag) < Tag.no_payload_count);
pub fn initTag(small_tag: Tag) Value {
assert(@enumToInt(small_tag) < Tag.no_payload_count);
return .{ .tag_if_small_enough = @enumToInt(small_tag) };
}
@ -107,6 +108,109 @@ pub const Value = extern union {
return @fieldParentPtr(T, "base", self.ptr_otherwise);
}
pub fn copy(self: Value, allocator: *Allocator) error{OutOfMemory}!Value {
if (self.tag_if_small_enough < Tag.no_payload_count) {
return Value{ .tag_if_small_enough = self.tag_if_small_enough };
} else switch (self.ptr_otherwise.tag) {
.u8_type,
.i8_type,
.isize_type,
.usize_type,
.c_short_type,
.c_ushort_type,
.c_int_type,
.c_uint_type,
.c_long_type,
.c_ulong_type,
.c_longlong_type,
.c_ulonglong_type,
.c_longdouble_type,
.f16_type,
.f32_type,
.f64_type,
.f128_type,
.c_void_type,
.bool_type,
.void_type,
.type_type,
.anyerror_type,
.comptime_int_type,
.comptime_float_type,
.noreturn_type,
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
.const_slice_u8_type,
.undef,
.zero,
.the_one_possible_value,
.null_value,
.bool_true,
.bool_false,
=> unreachable,
.ty => {
const payload = @fieldParentPtr(Payload.Ty, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Ty);
new_payload.* = .{
.base = payload.base,
.ty = try payload.ty.copy(allocator),
};
return Value{ .ptr_otherwise = &new_payload.base };
},
.int_u64 => return self.copyPayloadShallow(allocator, Payload.Int_u64),
.int_i64 => return self.copyPayloadShallow(allocator, Payload.Int_i64),
.int_big_positive => {
@panic("TODO implement copying of big ints");
},
.int_big_negative => {
@panic("TODO implement copying of big ints");
},
.function => return self.copyPayloadShallow(allocator, Payload.Function),
.ref_val => {
const payload = @fieldParentPtr(Payload.RefVal, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.RefVal);
new_payload.* = .{
.base = payload.base,
.val = try payload.val.copy(allocator),
};
return Value{ .ptr_otherwise = &new_payload.base };
},
.decl_ref => return self.copyPayloadShallow(allocator, Payload.DeclRef),
.elem_ptr => {
const payload = @fieldParentPtr(Payload.ElemPtr, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.ElemPtr);
new_payload.* = .{
.base = payload.base,
.array_ptr = try payload.array_ptr.copy(allocator),
.index = payload.index,
};
return Value{ .ptr_otherwise = &new_payload.base };
},
.bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
.repeated => {
const payload = @fieldParentPtr(Payload.Repeated, "base", self.ptr_otherwise);
const new_payload = try allocator.create(Payload.Repeated);
new_payload.* = .{
.base = payload.base,
.val = try payload.val.copy(allocator),
};
return Value{ .ptr_otherwise = &new_payload.base };
},
}
}
fn copyPayloadShallow(self: Value, allocator: *Allocator, comptime T: type) error{OutOfMemory}!Value {
const payload = @fieldParentPtr(T, "base", self.ptr_otherwise);
const new_payload = try allocator.create(T);
new_payload.* = payload.*;
return Value{ .ptr_otherwise = &new_payload.base };
}
pub fn format(
self: Value,
comptime fmt: []const u8,
@ -144,6 +248,7 @@ pub const Value = extern union {
.null_type => return out_stream.writeAll("@TypeOf(null)"),
.undefined_type => return out_stream.writeAll("@TypeOf(undefined)"),
.fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"),
.fn_void_no_args_type => return out_stream.writeAll("fn() void"),
.fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"),
.fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"),
.single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"),
@ -229,6 +334,7 @@ pub const Value = extern union {
.null_type => Type.initTag(.@"null"),
.undefined_type => Type.initTag(.@"undefined"),
.fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args),
.fn_void_no_args_type => Type.initTag(.fn_void_no_args),
.fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args),
.fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args),
.single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int),
@ -286,6 +392,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -345,6 +452,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -405,6 +513,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -470,6 +579,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -564,6 +674,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -620,6 +731,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -721,6 +833,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -783,6 +896,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -862,6 +976,7 @@ pub const Value = extern union {
.null_type,
.undefined_type,
.fn_noreturn_no_args_type,
.fn_void_no_args_type,
.fn_naked_noreturn_no_args_type,
.fn_ccc_void_no_args_type,
.single_const_pointer_to_comptime_int_type,
@ -929,11 +1044,6 @@ pub const Value = extern union {
len: u64,
};
pub const SingleConstPtrType = struct {
base: Payload = Payload{ .tag = .single_const_ptr_type },
elem_type: *Type,
};
/// Represents a pointer to another immutable value.
pub const RefVal = struct {
base: Payload = Payload{ .tag = .ref_val },

File diff suppressed because it is too large Load Diff

View File

@ -5583,8 +5583,12 @@ static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, Ir
bool val_is_undef = value_is_all_undef(g, instruction->byte->value);
LLVMValueRef fill_char;
if (val_is_undef && ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
if (val_is_undef) {
if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) {
fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false);
} else {
return nullptr;
}
} else {
fill_char = ir_llvm_value(g, instruction->byte);
}
@ -7473,6 +7477,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n
continue;
}
ZigValue *field_val = const_val->data.x_struct.fields[i];
if (field_val == nullptr) {
add_node_error(g, type_struct_field->decl_node,
buf_sprintf("compiler bug: generating const value for struct field '%s'",
buf_ptr(type_struct_field->name)));
codegen_report_errors_and_exit(g);
}
ZigType *field_type = field_val->type;
assert(field_type != nullptr);
if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) {
@ -9465,9 +9475,15 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include",
buf_ptr(g->zig_lib_dir)));
const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include",
buf_ptr(g->zig_lib_dir)));
args.append("-isystem");
args.append(libcxx_include_path);
args.append("-isystem");
args.append(libcxxabi_include_path);
if (target_abi_is_musl(g->zig_target->abi)) {
args.append("-D_LIBCPP_HAS_MUSL_LIBC");
}

View File

@ -1,28 +1,121 @@
const std = @import("std");
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
// self-hosted does not yet support PE executable files / COFF object files
// or mach-o files. So we do these test cases cross compiling for x86_64-linux.
const linux_x64 = std.zig.CrossTarget{
.cpu_arch = .x86_64,
.os_tag = .linux,
};
pub fn addCases(ctx: *TestContext) !void {
// TODO: re-enable these tests.
// https://github.com/ziglang/zig/issues/1364
if (std.Target.current.os.tag != .linux or
std.Target.current.cpu.arch != .x86_64)
{
// TODO implement self-hosted PE (.exe file) linking
// TODO implement more ZIR so we don't depend on x86_64-linux
return;
}
//// hello world
//try ctx.testCompareOutputLibC(
// \\extern fn puts([*]const u8) void;
// \\pub export fn main() c_int {
// \\ puts("Hello, world!");
// \\ return 0;
// \\}
//, "Hello, world!" ++ std.cstr.line_sep);
//// function calling another function
//try ctx.testCompareOutputLibC(
// \\extern fn puts(s: [*]const u8) void;
// \\pub export fn main() c_int {
// \\ return foo("OK");
// \\}
// \\fn foo(s: [*]const u8) c_int {
// \\ puts(s);
// \\ return 0;
// \\}
//, "OK" ++ std.cstr.line_sep);
{
var case = ctx.addExe("hello world with updates", linux_x64);
// Regular old hello world
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("Hello, World!\n")),
\\ [arg3] "{rdx}" (14)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"Hello, World!\n",
);
// Now change the message only
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
\\ [arg3] "{rdx}" (104)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
"What is up? This is a longer message that will force the data to be relocated in virtual address space.\n",
);
// Now we print it twice.
case.addCompareOutput(
\\export fn _start() noreturn {
\\ print();
\\ print();
\\
\\ exit();
\\}
\\
\\fn print() void {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (1),
\\ [arg1] "{rdi}" (1),
\\ [arg2] "{rsi}" (@ptrToInt("What is up? This is a longer message that will force the data to be relocated in virtual address space.\n")),
\\ [arg3] "{rdx}" (104)
\\ : "rcx", "r11", "memory"
\\ );
\\ return;
\\}
\\
\\fn exit() noreturn {
\\ asm volatile ("syscall"
\\ :
\\ : [number] "{rax}" (231),
\\ [arg1] "{rdi}" (0)
\\ : "rcx", "r11", "memory"
\\ );
\\ unreachable;
\\}
,
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
\\What is up? This is a longer message that will force the data to be relocated in virtual address space.
\\
);
}
}

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,65 +73,62 @@ 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")
\\
);
{
var case = ctx.addZIRMulti("reference cycle with compile error in the cycle", linux_x64);
var case = ctx.addObjZIR("reference cycle with compile error in the cycle", linux_x64);
case.addTransform(
\\@void = primitive(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()
\\})
\\
);
@ -217,272 +207,101 @@ pub fn addCases(ctx: *TestContext) void {
return;
}
ctx.addZIRCompareOutput(
"hello world ZIR, update msg",
&[_][]const u8{
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@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")
\\@rdx_array = str("{rdx}")
\\@rsi_array = str("{rsi}")
\\@memory_array = str("memory")
\\@len_array = str("len")
\\
\\@msg = str("Hello, world!\n")
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_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)
\\ %rdx = ref(@rdx_array)
\\ %rsi = ref(@rsi_array)
\\ %r11 = ref(@r11_array)
\\ %memory = ref(@memory_array)
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_ptr = ref(@msg)
\\ %msg_addr = ptrtoint(%msg_ptr)
\\
\\ %len_name = ref(@len_array)
\\ %msg_len_ptr = fieldptr(%msg_ptr, %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")
\\@10 = ref(@9)
\\@11 = export(@10, @start)
,
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@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")
\\@rdx_array = str("{rdx}")
\\@rsi_array = str("{rsi}")
\\@memory_array = str("memory")
\\@len_array = str("len")
\\
\\@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 = ref(@syscall_array)
\\ %sysoutreg = ref(@sysoutreg_array)
\\ %rax = ref(@rax_array)
\\ %rdi = ref(@rdi_array)
\\ %rcx = ref(@rcx_array)
\\ %rdx = ref(@rdx_array)
\\ %rsi = ref(@rsi_array)
\\ %r11 = ref(@r11_array)
\\ %memory = ref(@memory_array)
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_ptr = ref(@msg2)
\\ %msg_addr = ptrtoint(%msg_ptr)
\\
\\ %len_name = ref(@len_array)
\\ %msg_len_ptr = fieldptr(%msg_ptr, %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")
\\@10 = ref(@9)
\\@11 = export(@10, @start)
,
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@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")
\\@rdx_array = str("{rdx}")
\\@rsi_array = str("{rsi}")
\\@memory_array = str("memory")
\\@len_array = str("len")
\\
\\@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 = ref(@syscall_array)
\\ %sysoutreg = ref(@sysoutreg_array)
\\ %rax = ref(@rax_array)
\\ %rdi = ref(@rdi_array)
\\ %rcx = ref(@rcx_array)
\\ %rdx = ref(@rdx_array)
\\ %rsi = ref(@rsi_array)
\\ %r11 = ref(@r11_array)
\\ %memory = ref(@memory_array)
\\
\\ %SYS_write = as(@usize, @1)
\\ %STDOUT_FILENO = as(@usize, @1)
\\
\\ %msg_ptr = ref(@msg2)
\\ %msg_addr = ptrtoint(%msg_ptr)
\\
\\ %len_name = ref(@len_array)
\\ %msg_len_ptr = fieldptr(%msg_ptr, %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")
\\@10 = ref(@9)
\\@11 = export(@10, @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.
\\
},
ctx.addZIRCompareOutput("hello world ZIR",
\\@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")
\\
\\@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(@msg)
\\
\\ %len_name = str("len")
\\ %msg_len_ptr = fieldptr(@msg, %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")
,
\\Hello, world!
\\
);
ctx.addZIRCompareOutput(
"function call with no args no return value",
&[_][]const u8{
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@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)
\\
\\ %rc = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %0 = call(@exit0, [])
\\})
\\@9 = str("_start")
\\@10 = ref(@9)
\\@11 = export(@10, @start)
},
&[_][]const u8{""},
);
ctx.addZIRCompareOutput("function call with no args no return value",
\\@noreturn = primitive(noreturn)
\\@void = primitive(void)
\\@usize = primitive(usize)
\\@0 = int(0)
\\@1 = int(1)
\\@2 = int(2)
\\@3 = int(3)
\\
\\@exit0_fnty = fntype([], @noreturn)
\\@exit0 = fn(@exit0_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")
\\ %r11 = str("r11")
\\ %memory = str("memory")
\\
\\ %rc = asm(%syscall, @usize,
\\ volatile=1,
\\ output=%sysoutreg,
\\ inputs=[%rax, %rdi],
\\ clobbers=[%rcx, %r11, %memory],
\\ args=[%SYS_exit_group, %exit_code])
\\
\\ %99 = unreachable()
\\});
\\
\\@start_fnty = fntype([], @noreturn, cc=Naked)
\\@start = fn(@start_fnty, {
\\ %0 = call(@exit0, [])
\\})
\\@9 = str("_start")
\\@11 = export(@9, "start")
, "");
}