commit
6fbb5f0a81
|
@ -26,7 +26,7 @@ root_pkg: *Package,
|
|||
/// Module owns this resource.
|
||||
/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
|
||||
root_scope: *Scope,
|
||||
bin_file: link.ElfFile,
|
||||
bin_file: *link.File,
|
||||
bin_file_dir: std.fs.Dir,
|
||||
bin_file_path: []const u8,
|
||||
/// It's rare for a decl to be exported, so we save memory by having a sparse map of
|
||||
|
@ -45,7 +45,7 @@ export_owners: std.AutoHashMap(*Decl, []*Export),
|
|||
decl_table: DeclTable,
|
||||
|
||||
optimize_mode: std.builtin.Mode,
|
||||
link_error_flags: link.ElfFile.ErrorFlags = .{},
|
||||
link_error_flags: link.File.ErrorFlags = .{},
|
||||
|
||||
work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
|
||||
|
||||
|
@ -91,7 +91,7 @@ pub const Export = struct {
|
|||
/// Byte offset into the file that contains the export directive.
|
||||
src: usize,
|
||||
/// Represents the position of the export, if any, in the output file.
|
||||
link: link.ElfFile.Export,
|
||||
link: link.File.Elf.Export,
|
||||
/// The Decl that performs the export. Note that this is *not* the Decl being exported.
|
||||
owner_decl: *Decl,
|
||||
/// The Decl being exported. Note this is *not* the Decl performing the export.
|
||||
|
@ -169,7 +169,7 @@ pub const Decl = struct {
|
|||
|
||||
/// Represents the position of the code in the output file.
|
||||
/// This is populated regardless of semantic analysis and code generation.
|
||||
link: link.ElfFile.TextBlock = link.ElfFile.TextBlock.empty,
|
||||
link: link.File.Elf.TextBlock = link.File.Elf.TextBlock.empty,
|
||||
|
||||
contents_hash: std.zig.SrcHash,
|
||||
|
||||
|
@ -732,17 +732,19 @@ pub const InitOptions = struct {
|
|||
object_format: ?std.builtin.ObjectFormat = null,
|
||||
optimize_mode: std.builtin.Mode = .Debug,
|
||||
keep_source_files_loaded: bool = false,
|
||||
cbe: bool = false,
|
||||
};
|
||||
|
||||
pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
const bin_file_dir = options.bin_file_dir orelse std.fs.cwd();
|
||||
var bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
|
||||
const bin_file = try link.openBinFilePath(gpa, bin_file_dir, options.bin_file_path, .{
|
||||
.target = options.target,
|
||||
.output_mode = options.output_mode,
|
||||
.link_mode = options.link_mode orelse .Static,
|
||||
.object_format = options.object_format orelse options.target.getObjectFormat(),
|
||||
.cbe = options.cbe,
|
||||
});
|
||||
errdefer bin_file.deinit();
|
||||
errdefer bin_file.destroy();
|
||||
|
||||
const root_scope = blk: {
|
||||
if (mem.endsWith(u8, options.root_pkg.root_src_path, ".zig")) {
|
||||
|
@ -791,7 +793,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
|||
}
|
||||
|
||||
pub fn deinit(self: *Module) void {
|
||||
self.bin_file.deinit();
|
||||
self.bin_file.destroy();
|
||||
const allocator = self.allocator;
|
||||
self.deletion_set.deinit(allocator);
|
||||
self.work_queue.deinit();
|
||||
|
@ -840,7 +842,7 @@ fn freeExportList(allocator: *Allocator, export_list: []*Export) void {
|
|||
}
|
||||
|
||||
pub fn target(self: Module) std.Target {
|
||||
return self.bin_file.options.target;
|
||||
return self.bin_file.options().target;
|
||||
}
|
||||
|
||||
/// Detect changes to source files, perform semantic analysis, and update the output files.
|
||||
|
@ -882,7 +884,7 @@ pub fn update(self: *Module) !void {
|
|||
try self.deleteDecl(decl);
|
||||
}
|
||||
|
||||
self.link_error_flags = self.bin_file.error_flags;
|
||||
self.link_error_flags = self.bin_file.errorFlags();
|
||||
|
||||
// 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.
|
||||
|
@ -1898,8 +1900,9 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void {
|
|||
self.decl_exports.removeAssertDiscard(exp.exported_decl);
|
||||
}
|
||||
}
|
||||
|
||||
self.bin_file.deleteExport(exp.link);
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf| {
|
||||
elf.deleteExport(exp.link);
|
||||
}
|
||||
if (self.failed_exports.remove(exp)) |entry| {
|
||||
entry.value.destroy(self.allocator);
|
||||
}
|
||||
|
@ -1961,7 +1964,7 @@ fn allocateNewDecl(
|
|||
.analysis = .unreferenced,
|
||||
.deletion_flag = false,
|
||||
.contents_hash = contents_hash,
|
||||
.link = link.ElfFile.TextBlock.empty,
|
||||
.link = link.File.Elf.TextBlock.empty,
|
||||
.generation = 0,
|
||||
};
|
||||
return new_decl;
|
||||
|
@ -2189,19 +2192,21 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const
|
|||
}
|
||||
|
||||
try self.symbol_exports.putNoClobber(symbol_name, new_export);
|
||||
self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => {
|
||||
try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
|
||||
self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
|
||||
self.allocator,
|
||||
src,
|
||||
"unable to export: {}",
|
||||
.{@errorName(err)},
|
||||
));
|
||||
new_export.status = .failed_retryable;
|
||||
},
|
||||
};
|
||||
if (self.bin_file.cast(link.File.Elf)) |elf| {
|
||||
elf.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => {
|
||||
try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1);
|
||||
self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create(
|
||||
self.allocator,
|
||||
src,
|
||||
"unable to export: {}",
|
||||
.{@errorName(err)},
|
||||
));
|
||||
new_export.status = .failed_retryable;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn addNewInstArgs(
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#if __STDC_VERSION__ >= 201112L
|
||||
#define noreturn _Noreturn
|
||||
#elif !__STRICT_ANSI__
|
||||
#define noreturn __attribute__ ((noreturn))
|
||||
#else
|
||||
#define noreturn
|
||||
#endif
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
const link = @import("link.zig");
|
||||
const Module = @import("Module.zig");
|
||||
const ir = @import("ir.zig");
|
||||
const Value = @import("value.zig").Value;
|
||||
const Type = @import("type.zig").Type;
|
||||
const std = @import("std");
|
||||
|
||||
const C = link.File.C;
|
||||
const Decl = Module.Decl;
|
||||
const mem = std.mem;
|
||||
|
||||
/// Maps a name from Zig source to C. This will always give the same output for
|
||||
/// any given input.
|
||||
fn map(name: []const u8) ![]const u8 {
|
||||
return name;
|
||||
}
|
||||
|
||||
fn renderType(file: *C, writer: std.ArrayList(u8).Writer, T: Type, src: usize) !void {
|
||||
if (T.tag() == .usize) {
|
||||
file.need_stddef = true;
|
||||
try writer.writeAll("size_t");
|
||||
} else {
|
||||
switch (T.zigTypeTag()) {
|
||||
.NoReturn => {
|
||||
file.need_noreturn = true;
|
||||
try writer.writeAll("noreturn void");
|
||||
},
|
||||
.Void => try writer.writeAll("void"),
|
||||
else => |e| return file.fail(src, "TODO implement type {}", .{e}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn renderFunctionSignature(file: *C, writer: std.ArrayList(u8).Writer, decl: *Decl) !void {
|
||||
const tv = decl.typed_value.most_recent.typed_value;
|
||||
try renderType(file, writer, tv.ty.fnReturnType(), decl.src());
|
||||
const name = try map(mem.spanZ(decl.name));
|
||||
try writer.print(" {}(", .{name});
|
||||
if (tv.ty.fnParamLen() == 0) {
|
||||
try writer.writeAll("void)");
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO implement parameters", .{});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(file: *C, decl: *Decl) !void {
|
||||
const writer = file.main.writer();
|
||||
const header = file.header.writer();
|
||||
const tv = decl.typed_value.most_recent.typed_value;
|
||||
switch (tv.ty.zigTypeTag()) {
|
||||
.Fn => {
|
||||
try renderFunctionSignature(file, writer, decl);
|
||||
|
||||
try writer.writeAll(" {");
|
||||
|
||||
const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
|
||||
const instructions = func.analysis.success.instructions;
|
||||
if (instructions.len > 0) {
|
||||
for (instructions) |inst| {
|
||||
try writer.writeAll("\n\t");
|
||||
switch (inst.tag) {
|
||||
.assembly => {
|
||||
const as = inst.cast(ir.Inst.Assembly).?.args;
|
||||
for (as.inputs) |i, index| {
|
||||
if (i[0] == '{' and i[i.len - 1] == '}') {
|
||||
const reg = i[1 .. i.len - 1];
|
||||
const arg = as.args[index];
|
||||
if (arg.cast(ir.Inst.Constant)) |c| {
|
||||
if (c.val.tag() == .int_u64) {
|
||||
try writer.writeAll("register ");
|
||||
try renderType(file, writer, arg.ty, decl.src());
|
||||
try writer.print(" {}_constant __asm__(\"{}\") = {};\n\t", .{ reg, reg, c.val.toUnsignedInt() });
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO inline asm {} args", .{c.val.tag()});
|
||||
}
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-constant inline asm args", .{});
|
||||
}
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-explicit inline asm regs", .{});
|
||||
}
|
||||
}
|
||||
try writer.print("__asm {} (\"{}\"", .{ if (as.is_volatile) @as([]const u8, "volatile") else "", as.asm_source });
|
||||
if (as.output) |o| {
|
||||
return file.fail(decl.src(), "TODO inline asm output", .{});
|
||||
}
|
||||
if (as.inputs.len > 0) {
|
||||
if (as.output == null) {
|
||||
try writer.writeAll(" :");
|
||||
}
|
||||
try writer.writeAll(": ");
|
||||
for (as.inputs) |i, index| {
|
||||
if (i[0] == '{' and i[i.len - 1] == '}') {
|
||||
const reg = i[1 .. i.len - 1];
|
||||
const arg = as.args[index];
|
||||
if (index > 0) {
|
||||
try writer.writeAll(", ");
|
||||
}
|
||||
if (arg.cast(ir.Inst.Constant)) |c| {
|
||||
try writer.print("\"\"({}_constant)", .{reg});
|
||||
} else {
|
||||
// This is blocked by the earlier test
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
// This is blocked by the earlier test
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
try writer.writeAll(");");
|
||||
},
|
||||
.call => {
|
||||
const call = inst.cast(ir.Inst.Call).?.args;
|
||||
if (call.func.cast(ir.Inst.Constant)) |func_inst| {
|
||||
if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
|
||||
const target = func_val.func.owner_decl;
|
||||
const tname = mem.spanZ(target.name);
|
||||
if (file.called.get(tname) == null) {
|
||||
try file.called.put(tname, void{});
|
||||
try renderFunctionSignature(file, header, target);
|
||||
try header.writeAll(";\n");
|
||||
}
|
||||
try writer.print("{}();", .{tname});
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-function call target?", .{});
|
||||
}
|
||||
if (call.args.len != 0) {
|
||||
return file.fail(decl.src(), "TODO function arguments", .{});
|
||||
}
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-constant call inst?", .{});
|
||||
}
|
||||
},
|
||||
else => |e| {
|
||||
return file.fail(decl.src(), "TODO {}", .{e});
|
||||
},
|
||||
}
|
||||
}
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
|
||||
try writer.writeAll("}\n\n");
|
||||
},
|
||||
.Array => {
|
||||
if (mem.indexOf(u8, mem.span(decl.name), "$") == null) {
|
||||
// TODO: prevent inline asm constants from being emitted
|
||||
if (tv.val.cast(Value.Payload.Bytes)) |payload| {
|
||||
try writer.print("const char *const {} = \"{}\";\n", .{ decl.name, payload.data });
|
||||
std.debug.warn("\n\nARRAYTRANS\n", .{});
|
||||
if (tv.ty.arraySentinel()) |sentinel| {}
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-byte arrays", .{});
|
||||
}
|
||||
}
|
||||
},
|
||||
else => |e| {
|
||||
return file.fail(decl.src(), "TODO {}", .{e});
|
||||
},
|
||||
}
|
||||
}
|
|
@ -21,7 +21,7 @@ pub const Result = union(enum) {
|
|||
};
|
||||
|
||||
pub fn generateSymbol(
|
||||
bin_file: *link.ElfFile,
|
||||
bin_file: *link.File.Elf,
|
||||
src: usize,
|
||||
typed_value: TypedValue,
|
||||
code: *std.ArrayList(u8),
|
||||
|
@ -211,7 +211,7 @@ pub fn generateSymbol(
|
|||
}
|
||||
|
||||
const Function = struct {
|
||||
bin_file: *link.ElfFile,
|
||||
bin_file: *link.File.Elf,
|
||||
target: *const std.Target,
|
||||
mod_fn: *const Module.Fn,
|
||||
code: *std.ArrayList(u8),
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -71,7 +71,7 @@ pub fn main() !void {
|
|||
const args = try process.argsAlloc(arena);
|
||||
|
||||
if (args.len <= 1) {
|
||||
std.debug.warn("expected command argument\n\n{}", .{usage});
|
||||
std.debug.print("expected command argument\n\n{}", .{usage});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -91,14 +91,14 @@ pub fn main() !void {
|
|||
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
|
||||
} else if (mem.eql(u8, cmd, "version")) {
|
||||
// Need to set up the build script to give the version as a comptime value.
|
||||
std.debug.warn("TODO version command not implemented yet\n", .{});
|
||||
std.debug.print("TODO version command not implemented yet\n", .{});
|
||||
return error.Unimplemented;
|
||||
} else if (mem.eql(u8, cmd, "zen")) {
|
||||
try io.getStdOut().writeAll(info_zen);
|
||||
} else if (mem.eql(u8, cmd, "help")) {
|
||||
try io.getStdOut().writeAll(usage);
|
||||
} else {
|
||||
std.debug.warn("unknown command: {}\n\n{}", .{ args[1], usage });
|
||||
std.debug.print("unknown command: {}\n\n{}", .{ args[1], usage });
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -191,6 +191,7 @@ fn buildOutputType(
|
|||
var emit_zir: Emit = .no;
|
||||
var target_arch_os_abi: []const u8 = "native";
|
||||
var target_mcpu: ?[]const u8 = null;
|
||||
var cbe: bool = false;
|
||||
var target_dynamic_linker: ?[]const u8 = null;
|
||||
|
||||
var system_libs = std.ArrayList([]const u8).init(gpa);
|
||||
|
@ -206,7 +207,7 @@ fn buildOutputType(
|
|||
process.exit(0);
|
||||
} else if (mem.eql(u8, arg, "--color")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected [auto|on|off] after --color\n", .{});
|
||||
std.debug.print("expected [auto|on|off] after --color\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
|
@ -218,12 +219,12 @@ fn buildOutputType(
|
|||
} else if (mem.eql(u8, next_arg, "off")) {
|
||||
color = .Off;
|
||||
} else {
|
||||
std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
|
||||
std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--mode")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
|
||||
std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
|
@ -237,52 +238,54 @@ fn buildOutputType(
|
|||
} else if (mem.eql(u8, next_arg, "ReleaseSmall")) {
|
||||
build_mode = .ReleaseSmall;
|
||||
} else {
|
||||
std.debug.warn("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
|
||||
std.debug.print("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode, found '{}'\n", .{next_arg});
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--name")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected parameter after --name\n", .{});
|
||||
std.debug.print("expected parameter after --name\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
provided_name = args[i];
|
||||
} else if (mem.eql(u8, arg, "--library")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected parameter after --library\n", .{});
|
||||
std.debug.print("expected parameter after --library\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
try system_libs.append(args[i]);
|
||||
} else if (mem.eql(u8, arg, "--version")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected parameter after --version\n", .{});
|
||||
std.debug.print("expected parameter after --version\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
version = std.builtin.Version.parse(args[i]) catch |err| {
|
||||
std.debug.warn("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
|
||||
std.debug.print("unable to parse --version '{}': {}\n", .{ args[i], @errorName(err) });
|
||||
process.exit(1);
|
||||
};
|
||||
} else if (mem.eql(u8, arg, "-target")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected parameter after -target\n", .{});
|
||||
std.debug.print("expected parameter after -target\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
target_arch_os_abi = args[i];
|
||||
} else if (mem.eql(u8, arg, "-mcpu")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected parameter after -mcpu\n", .{});
|
||||
std.debug.print("expected parameter after -mcpu\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
target_mcpu = args[i];
|
||||
} else if (mem.eql(u8, arg, "--c")) {
|
||||
cbe = true;
|
||||
} else if (mem.startsWith(u8, arg, "-mcpu=")) {
|
||||
target_mcpu = arg["-mcpu=".len..];
|
||||
} else if (mem.eql(u8, arg, "--dynamic-linker")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected parameter after --dynamic-linker\n", .{});
|
||||
std.debug.print("expected parameter after --dynamic-linker\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
|
@ -324,39 +327,39 @@ fn buildOutputType(
|
|||
} else if (mem.startsWith(u8, arg, "-l")) {
|
||||
try system_libs.append(arg[2..]);
|
||||
} else {
|
||||
std.debug.warn("unrecognized parameter: '{}'", .{arg});
|
||||
std.debug.print("unrecognized parameter: '{}'", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (mem.endsWith(u8, arg, ".s") or mem.endsWith(u8, arg, ".S")) {
|
||||
std.debug.warn("assembly files not supported yet", .{});
|
||||
std.debug.print("assembly files not supported yet", .{});
|
||||
process.exit(1);
|
||||
} else if (mem.endsWith(u8, arg, ".o") or
|
||||
mem.endsWith(u8, arg, ".obj") or
|
||||
mem.endsWith(u8, arg, ".a") or
|
||||
mem.endsWith(u8, arg, ".lib"))
|
||||
{
|
||||
std.debug.warn("object files and static libraries not supported yet", .{});
|
||||
std.debug.print("object files and static libraries not supported yet", .{});
|
||||
process.exit(1);
|
||||
} else if (mem.endsWith(u8, arg, ".c") or
|
||||
mem.endsWith(u8, arg, ".cpp"))
|
||||
{
|
||||
std.debug.warn("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
|
||||
std.debug.print("compilation of C and C++ source code requires LLVM extensions which are not implemented yet", .{});
|
||||
process.exit(1);
|
||||
} else if (mem.endsWith(u8, arg, ".so") or
|
||||
mem.endsWith(u8, arg, ".dylib") or
|
||||
mem.endsWith(u8, arg, ".dll"))
|
||||
{
|
||||
std.debug.warn("linking against dynamic libraries not yet supported", .{});
|
||||
std.debug.print("linking against dynamic libraries not yet supported", .{});
|
||||
process.exit(1);
|
||||
} else if (mem.endsWith(u8, arg, ".zig") or mem.endsWith(u8, arg, ".zir")) {
|
||||
if (root_src_file) |other| {
|
||||
std.debug.warn("found another zig file '{}' after root source file '{}'", .{ arg, other });
|
||||
std.debug.print("found another zig file '{}' after root source file '{}'", .{ arg, other });
|
||||
process.exit(1);
|
||||
} else {
|
||||
root_src_file = arg;
|
||||
}
|
||||
} else {
|
||||
std.debug.warn("unrecognized file extension of parameter '{}'", .{arg});
|
||||
std.debug.print("unrecognized file extension of parameter '{}'", .{arg});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -367,13 +370,13 @@ fn buildOutputType(
|
|||
var it = mem.split(basename, ".");
|
||||
break :blk it.next() orelse basename;
|
||||
} else {
|
||||
std.debug.warn("--name [name] not provided and unable to infer\n", .{});
|
||||
std.debug.print("--name [name] not provided and unable to infer\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if (system_libs.items.len != 0) {
|
||||
std.debug.warn("linking against system libraries not yet supported", .{});
|
||||
std.debug.print("linking against system libraries not yet supported", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -385,17 +388,17 @@ fn buildOutputType(
|
|||
.diagnostics = &diags,
|
||||
}) catch |err| switch (err) {
|
||||
error.UnknownCpuModel => {
|
||||
std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
|
||||
std.debug.print("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{
|
||||
diags.cpu_name.?,
|
||||
@tagName(diags.arch.?),
|
||||
});
|
||||
for (diags.arch.?.allCpuModels()) |cpu| {
|
||||
std.debug.warn(" {}\n", .{cpu.name});
|
||||
std.debug.print(" {}\n", .{cpu.name});
|
||||
}
|
||||
process.exit(1);
|
||||
},
|
||||
error.UnknownCpuFeature => {
|
||||
std.debug.warn(
|
||||
std.debug.print(
|
||||
\\Unknown CPU feature: '{}'
|
||||
\\Available CPU features for architecture '{}':
|
||||
\\
|
||||
|
@ -404,7 +407,7 @@ fn buildOutputType(
|
|||
@tagName(diags.arch.?),
|
||||
});
|
||||
for (diags.arch.?.allFeaturesList()) |feature| {
|
||||
std.debug.warn(" {}: {}\n", .{ feature.name, feature.description });
|
||||
std.debug.print(" {}: {}\n", .{ feature.name, feature.description });
|
||||
}
|
||||
process.exit(1);
|
||||
},
|
||||
|
@ -416,21 +419,22 @@ fn buildOutputType(
|
|||
if (target_info.cpu_detection_unimplemented) {
|
||||
// TODO We want to just use detected_info.target but implementing
|
||||
// CPU model & feature detection is todo so here we rely on LLVM.
|
||||
std.debug.warn("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
|
||||
std.debug.print("CPU features detection is not yet available for this system without LLVM extensions\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const src_path = root_src_file orelse {
|
||||
std.debug.warn("expected at least one file argument", .{});
|
||||
std.debug.print("expected at least one file argument", .{});
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
const bin_path = switch (emit_bin) {
|
||||
.no => {
|
||||
std.debug.warn("-fno-emit-bin not supported yet", .{});
|
||||
std.debug.print("-fno-emit-bin not supported yet", .{});
|
||||
process.exit(1);
|
||||
},
|
||||
.yes_default_path => try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
|
||||
.yes_default_path => try std.fmt.allocPrint(arena, "{}.c", .{root_name}),
|
||||
|
||||
.yes => |p| p,
|
||||
};
|
||||
|
||||
|
@ -460,6 +464,7 @@ fn buildOutputType(
|
|||
.object_format = object_format,
|
||||
.optimize_mode = build_mode,
|
||||
.keep_source_files_loaded = zir_out_path != null,
|
||||
.cbe = cbe,
|
||||
});
|
||||
defer module.deinit();
|
||||
|
||||
|
@ -506,7 +511,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
|
|||
|
||||
if (errors.list.len != 0) {
|
||||
for (errors.list) |full_err_msg| {
|
||||
std.debug.warn("{}:{}:{}: error: {}\n", .{
|
||||
std.debug.print("{}:{}:{}: error: {}\n", .{
|
||||
full_err_msg.src_path,
|
||||
full_err_msg.line + 1,
|
||||
full_err_msg.column + 1,
|
||||
|
@ -583,7 +588,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
process.exit(0);
|
||||
} else if (mem.eql(u8, arg, "--color")) {
|
||||
if (i + 1 >= args.len) {
|
||||
std.debug.warn("expected [auto|on|off] after --color\n", .{});
|
||||
std.debug.print("expected [auto|on|off] after --color\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
|
@ -595,7 +600,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
} else if (mem.eql(u8, next_arg, "off")) {
|
||||
color = .Off;
|
||||
} else {
|
||||
std.debug.warn("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
|
||||
std.debug.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg});
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--stdin")) {
|
||||
|
@ -603,7 +608,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
} else if (mem.eql(u8, arg, "--check")) {
|
||||
check_flag = true;
|
||||
} else {
|
||||
std.debug.warn("unrecognized parameter: '{}'", .{arg});
|
||||
std.debug.print("unrecognized parameter: '{}'", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
|
@ -614,7 +619,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
|
||||
if (stdin_flag) {
|
||||
if (input_files.items.len != 0) {
|
||||
std.debug.warn("cannot use --stdin with positional arguments\n", .{});
|
||||
std.debug.print("cannot use --stdin with positional arguments\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -624,7 +629,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
defer gpa.free(source_code);
|
||||
|
||||
const tree = std.zig.parse(gpa, source_code) catch |err| {
|
||||
std.debug.warn("error parsing stdin: {}\n", .{err});
|
||||
std.debug.print("error parsing stdin: {}\n", .{err});
|
||||
process.exit(1);
|
||||
};
|
||||
defer tree.deinit();
|
||||
|
@ -647,7 +652,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
}
|
||||
|
||||
if (input_files.items.len == 0) {
|
||||
std.debug.warn("expected at least one source file argument\n", .{});
|
||||
std.debug.print("expected at least one source file argument\n", .{});
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
@ -664,7 +669,7 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
|
|||
for (input_files.span()) |file_path| {
|
||||
// Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
|
||||
const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
|
||||
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
|
||||
std.debug.print("unable to open '{}': {}\n", .{ file_path, err });
|
||||
process.exit(1);
|
||||
};
|
||||
defer gpa.free(real_path);
|
||||
|
@ -702,7 +707,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_
|
|||
fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
|
||||
error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
|
||||
else => {
|
||||
std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });
|
||||
std.debug.print("unable to format '{}': {}\n", .{ file_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
},
|
||||
|
@ -733,7 +738,7 @@ fn fmtPathDir(
|
|||
try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
|
||||
} else {
|
||||
fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
|
||||
std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });
|
||||
std.debug.print("unable to format '{}': {}\n", .{ full_path, err });
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
};
|
||||
|
@ -784,7 +789,7 @@ fn fmtPathFile(
|
|||
if (check_mode) {
|
||||
const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree);
|
||||
if (anything_changed) {
|
||||
std.debug.warn("{}\n", .{file_path});
|
||||
std.debug.print("{}\n", .{file_path});
|
||||
fmt.any_error = true;
|
||||
}
|
||||
} else {
|
||||
|
@ -800,7 +805,7 @@ fn fmtPathFile(
|
|||
|
||||
try af.file.writeAll(fmt.out_buffer.items);
|
||||
try af.finish();
|
||||
std.debug.warn("{}\n", .{file_path});
|
||||
std.debug.print("{}\n", .{file_path});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ const Allocator = std.mem.Allocator;
|
|||
const zir = @import("zir.zig");
|
||||
const Package = @import("Package.zig");
|
||||
|
||||
const cheader = @embedFile("cbe.h");
|
||||
|
||||
test "self-hosted" {
|
||||
var ctx = TestContext.init();
|
||||
defer ctx.deinit();
|
||||
|
@ -68,6 +70,7 @@ pub const TestContext = struct {
|
|||
output_mode: std.builtin.OutputMode,
|
||||
updates: std.ArrayList(Update),
|
||||
extension: TestType,
|
||||
cbe: bool = false,
|
||||
|
||||
/// Adds a subcase in which the module is updated with `src`, and the
|
||||
/// resulting ZIR is validated against `result`.
|
||||
|
@ -187,6 +190,22 @@ pub const TestContext = struct {
|
|||
return ctx.addObj(name, target, .ZIR);
|
||||
}
|
||||
|
||||
pub fn addC(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, T: TestType) *Case {
|
||||
ctx.cases.append(Case{
|
||||
.name = name,
|
||||
.target = target,
|
||||
.updates = std.ArrayList(Update).init(ctx.cases.allocator),
|
||||
.output_mode = .Obj,
|
||||
.extension = T,
|
||||
.cbe = true,
|
||||
}) catch unreachable;
|
||||
return &ctx.cases.items[ctx.cases.items.len - 1];
|
||||
}
|
||||
|
||||
pub fn c(ctx: *TestContext, name: []const u8, target: std.zig.CrossTarget, src: [:0]const u8, comptime out: [:0]const u8) void {
|
||||
ctx.addC(name, target, .Zig).addTransform(src, cheader ++ out);
|
||||
}
|
||||
|
||||
pub fn addCompareOutput(
|
||||
ctx: *TestContext,
|
||||
name: []const u8,
|
||||
|
@ -365,13 +384,13 @@ pub const TestContext = struct {
|
|||
}
|
||||
|
||||
fn deinit(self: *TestContext) void {
|
||||
for (self.cases.items) |c| {
|
||||
for (c.updates.items) |u| {
|
||||
for (self.cases.items) |case| {
|
||||
for (case.updates.items) |u| {
|
||||
if (u.case == .Error) {
|
||||
c.updates.allocator.free(u.case.Error);
|
||||
case.updates.allocator.free(u.case.Error);
|
||||
}
|
||||
}
|
||||
c.updates.deinit();
|
||||
case.updates.deinit();
|
||||
}
|
||||
self.cases.deinit();
|
||||
self.* = undefined;
|
||||
|
@ -415,9 +434,6 @@ pub const TestContext = struct {
|
|||
|
||||
var module = try Module.init(allocator, .{
|
||||
.target = target,
|
||||
// This is an Executable, as opposed to e.g. a *library*. This does
|
||||
// not mean no ZIR is generated.
|
||||
//
|
||||
// TODO: support tests for object file building, and library builds
|
||||
// and linking. This will require a rework to support multi-file
|
||||
// tests.
|
||||
|
@ -428,6 +444,7 @@ pub const TestContext = struct {
|
|||
.bin_file_path = bin_name,
|
||||
.root_pkg = root_pkg,
|
||||
.keep_source_files_loaded = true,
|
||||
.cbe = case.cbe,
|
||||
});
|
||||
defer module.deinit();
|
||||
|
||||
|
@ -447,33 +464,66 @@ pub const TestContext = struct {
|
|||
try module.update();
|
||||
module_node.end();
|
||||
|
||||
if (update.case != .Error) {
|
||||
var all_errors = try module.getAllErrorsAlloc();
|
||||
defer all_errors.deinit(allocator);
|
||||
if (all_errors.list.len != 0) {
|
||||
std.debug.warn("\nErrors occurred updating the module:\n================\n", .{});
|
||||
for (all_errors.list) |err| {
|
||||
std.debug.warn(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
|
||||
}
|
||||
std.debug.warn("Test failed.\n", .{});
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
switch (update.case) {
|
||||
.Transformation => |expected_output| {
|
||||
update_node.estimated_total_items = 5;
|
||||
var emit_node = update_node.start("emit", null);
|
||||
emit_node.activate();
|
||||
var new_zir_module = try zir.emit(allocator, module);
|
||||
defer new_zir_module.deinit(allocator);
|
||||
emit_node.end();
|
||||
if (case.cbe) {
|
||||
var cfile: *link.File.C = module.bin_file.cast(link.File.C).?;
|
||||
cfile.file.?.close();
|
||||
cfile.file = null;
|
||||
var file = try tmp.dir.openFile(bin_name, .{ .read = true });
|
||||
defer file.close();
|
||||
var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
|
||||
defer allocator.free(out);
|
||||
|
||||
var write_node = update_node.start("write", null);
|
||||
write_node.activate();
|
||||
var out_zir = std.ArrayList(u8).init(allocator);
|
||||
defer out_zir.deinit();
|
||||
try new_zir_module.writeToStream(allocator, out_zir.outStream());
|
||||
write_node.end();
|
||||
if (expected_output.len != out.len) {
|
||||
std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
std.process.exit(1);
|
||||
}
|
||||
for (expected_output) |e, i| {
|
||||
if (out[i] != e) {
|
||||
std.debug.warn("\nTransformed C differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
update_node.estimated_total_items = 5;
|
||||
var emit_node = update_node.start("emit", null);
|
||||
emit_node.activate();
|
||||
var new_zir_module = try zir.emit(allocator, module);
|
||||
defer new_zir_module.deinit(allocator);
|
||||
emit_node.end();
|
||||
|
||||
var test_node = update_node.start("assert", null);
|
||||
test_node.activate();
|
||||
defer test_node.end();
|
||||
if (expected_output.len != out_zir.items.len) {
|
||||
std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.process.exit(1);
|
||||
}
|
||||
for (expected_output) |e, i| {
|
||||
if (out_zir.items[i] != e) {
|
||||
if (expected_output.len != out_zir.items.len) {
|
||||
std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound: {}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
var write_node = update_node.start("write", null);
|
||||
write_node.activate();
|
||||
var out_zir = std.ArrayList(u8).init(allocator);
|
||||
defer out_zir.deinit();
|
||||
try new_zir_module.writeToStream(allocator, out_zir.outStream());
|
||||
write_node.end();
|
||||
|
||||
var test_node = update_node.start("assert", null);
|
||||
test_node.activate();
|
||||
defer test_node.end();
|
||||
|
||||
if (expected_output.len != out_zir.items.len) {
|
||||
std.debug.warn("{}\nTransformed ZIR length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.process.exit(1);
|
||||
}
|
||||
for (expected_output) |e, i| {
|
||||
if (out_zir.items[i] != e) {
|
||||
std.debug.warn("{}\nTransformed ZIR differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ case.name, expected_output, out_zir.items });
|
||||
std.process.exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -511,6 +561,8 @@ pub const TestContext = struct {
|
|||
}
|
||||
},
|
||||
.Execution => |expected_stdout| {
|
||||
std.debug.assert(!case.cbe);
|
||||
|
||||
update_node.estimated_total_items = 4;
|
||||
var exec_result = x: {
|
||||
var exec_node = update_node.start("execute", null);
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
const std = @import("std");
|
||||
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
|
||||
|
||||
// These tests should work with all platforms, but we're using linux_x64 for
|
||||
// now for consistency. Will be expanded eventually.
|
||||
const linux_x64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .x86_64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
ctx.c("empty start function", linux_x64,
|
||||
\\export fn _start() noreturn {}
|
||||
,
|
||||
\\noreturn void _start(void) {}
|
||||
\\
|
||||
);
|
||||
ctx.c("less empty start function", linux_x64,
|
||||
\\fn main() noreturn {}
|
||||
\\
|
||||
\\export fn _start() noreturn {
|
||||
\\ main();
|
||||
\\}
|
||||
,
|
||||
\\noreturn void main(void);
|
||||
\\
|
||||
\\noreturn void _start(void) {
|
||||
\\ main();
|
||||
\\}
|
||||
\\
|
||||
\\noreturn void main(void) {}
|
||||
\\
|
||||
);
|
||||
// TODO: implement return values
|
||||
ctx.c("inline asm", linux_x64,
|
||||
\\fn exitGood() void {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ );
|
||||
\\}
|
||||
\\
|
||||
\\export fn _start() noreturn {
|
||||
\\ exitGood();
|
||||
\\}
|
||||
,
|
||||
\\#include <stddef.h>
|
||||
\\
|
||||
\\void exitGood(void);
|
||||
\\
|
||||
\\noreturn void _start(void) {
|
||||
\\ exitGood();
|
||||
\\}
|
||||
\\
|
||||
\\void exitGood(void) {
|
||||
\\ register size_t rax_constant __asm__("rax") = 231;
|
||||
\\ register size_t rdi_constant __asm__("rdi") = 0;
|
||||
\\ __asm volatile ("syscall" :: ""(rax_constant), ""(rdi_constant));
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
//ctx.c("basic return", linux_x64,
|
||||
// \\fn main() u8 {
|
||||
// \\ return 103;
|
||||
// \\}
|
||||
// \\
|
||||
// \\export fn _start() noreturn {
|
||||
// \\ _ = main();
|
||||
// \\}
|
||||
//,
|
||||
// \\#include <stdint.h>
|
||||
// \\
|
||||
// \\uint8_t main(void);
|
||||
// \\
|
||||
// \\noreturn void _start(void) {
|
||||
// \\ (void)main();
|
||||
// \\}
|
||||
// \\
|
||||
// \\uint8_t main(void) {
|
||||
// \\ return 103;
|
||||
// \\}
|
||||
// \\
|
||||
//);
|
||||
}
|
|
@ -4,4 +4,5 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||
try @import("compile_errors.zig").addCases(ctx);
|
||||
try @import("compare_output.zig").addCases(ctx);
|
||||
try @import("zir.zig").addCases(ctx);
|
||||
try @import("cbe.zig").addCases(ctx);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue