mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 12:59:04 +00:00
Merge remote-tracking branch 'origin/master' into register-allocation
This commit is contained in:
commit
7bd0500589
@ -12,7 +12,7 @@ sudo apt-get update -q
|
||||
|
||||
sudo apt-get remove -y llvm-*
|
||||
sudo rm -rf /usr/local/*
|
||||
sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build
|
||||
sudo apt-get install -y libxml2-dev libclang-10-dev llvm-10 llvm-10-dev liblld-10-dev cmake s3cmd gcc-7 g++-7 ninja-build tidy
|
||||
|
||||
QEMUBASE="qemu-linux-x86_64-5.0.0-49ee115552"
|
||||
wget https://ziglang.org/deps/$QEMUBASE.tar.xz
|
||||
@ -51,6 +51,10 @@ cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -GNinja
|
||||
ninja install
|
||||
./zig build test -Denable-qemu -Denable-wasmtime
|
||||
|
||||
# look for HTML errors
|
||||
tidy -qe ../zig-cache/langref.html
|
||||
|
||||
VERSION="$(./zig version)"
|
||||
|
||||
if [ "${BUILD_REASON}" != "PullRequest" ]; then
|
||||
|
||||
@ -97,7 +97,7 @@
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#index {
|
||||
#toc {
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@
|
||||
#main-wrapper {
|
||||
flex-direction: row;
|
||||
}
|
||||
#contents-wrapper, #index {
|
||||
#contents-wrapper, #toc {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
@ -181,7 +181,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="main-wrapper">
|
||||
<div id="index">
|
||||
<div id="toc">
|
||||
<a href="https://ziglang.org/documentation/0.1.1/">0.1.1</a> |
|
||||
<a href="https://ziglang.org/documentation/0.2.0/">0.2.0</a> |
|
||||
<a href="https://ziglang.org/documentation/0.3.0/">0.3.0</a> |
|
||||
@ -189,7 +189,7 @@
|
||||
<a href="https://ziglang.org/documentation/0.5.0/">0.5.0</a> |
|
||||
<a href="https://ziglang.org/documentation/0.6.0/">0.6.0</a> |
|
||||
master
|
||||
<h1>Index</h1>
|
||||
<h1>Contents</h1>
|
||||
{#nav#}
|
||||
</div>
|
||||
<div id="contents-wrapper"><div id="contents">
|
||||
@ -3861,6 +3861,48 @@ test "if error union" {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
test "if error union with optional" {
|
||||
// If expressions test for errors before unwrapping optionals.
|
||||
// The |optional_value| capture's type is ?u32.
|
||||
|
||||
const a: anyerror!?u32 = 0;
|
||||
if (a) |optional_value| {
|
||||
assert(optional_value.? == 0);
|
||||
} else |err| {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const b: anyerror!?u32 = null;
|
||||
if (b) |optional_value| {
|
||||
assert(optional_value == null);
|
||||
} else |err| {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const c: anyerror!?u32 = error.BadValue;
|
||||
if (c) |optional_value| {
|
||||
unreachable;
|
||||
} else |err| {
|
||||
assert(err == error.BadValue);
|
||||
}
|
||||
|
||||
// Access the value by reference by using a pointer capture each time.
|
||||
var d: anyerror!?u32 = 3;
|
||||
if (d) |*optional_value| {
|
||||
if (optional_value.*) |*value| {
|
||||
value.* = 9;
|
||||
}
|
||||
} else |err| {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
if (d) |optional_value| {
|
||||
assert(optional_value.? == 9);
|
||||
} else |err| {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
{#code_end#}
|
||||
{#see_also|Optionals|Errors#}
|
||||
{#header_close#}
|
||||
@ -8393,6 +8435,7 @@ fn foo(comptime T: type, ptr: *T) T {
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|Opaque Types#}
|
||||
<p>
|
||||
{#syntax#}@Type(.Opaque){#endsyntax#} creates a new type with an unknown (but non-zero) size and alignment.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@ -453,6 +453,8 @@ pub const Dir = struct {
|
||||
|
||||
pub const Error = IteratorError;
|
||||
|
||||
/// Memory such as file names referenced in this returned entry becomes invalid
|
||||
/// with subsequent calls to `next`, as well as when this `Dir` is deinitialized.
|
||||
pub fn next(self: *Self) Error!?Entry {
|
||||
start_over: while (true) {
|
||||
const w = os.windows;
|
||||
|
||||
@ -3,10 +3,53 @@ const testing = std.testing;
|
||||
const builtin = std.builtin;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
const wasi = std.os.wasi;
|
||||
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
const Dir = std.fs.Dir;
|
||||
const File = std.fs.File;
|
||||
const tmpDir = testing.tmpDir;
|
||||
|
||||
test "Dir.Iterator" {
|
||||
var tmp_dir = tmpDir(.{ .iterate = true });
|
||||
defer tmp_dir.cleanup();
|
||||
|
||||
// First, create a couple of entries to iterate over.
|
||||
const file = try tmp_dir.dir.createFile("some_file", .{});
|
||||
file.close();
|
||||
|
||||
try tmp_dir.dir.makeDir("some_dir");
|
||||
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var entries = std.ArrayList(Dir.Entry).init(&arena.allocator);
|
||||
|
||||
// Create iterator.
|
||||
var iter = tmp_dir.dir.iterate();
|
||||
while (try iter.next()) |entry| {
|
||||
// We cannot just store `entry` as on Windows, we're re-using the name buffer
|
||||
// which means we'll actually share the `name` pointer between entries!
|
||||
const name = try arena.allocator.dupe(u8, entry.name);
|
||||
try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
|
||||
}
|
||||
|
||||
testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
|
||||
testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
|
||||
testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
|
||||
}
|
||||
|
||||
fn entry_eql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
|
||||
return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
|
||||
}
|
||||
|
||||
fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
|
||||
for (entries.items) |entry| {
|
||||
if (entry_eql(entry, el)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
test "readAllAlloc" {
|
||||
var tmp_dir = tmpDir(.{});
|
||||
defer tmp_dir.cleanup();
|
||||
@ -237,7 +280,7 @@ test "fs.copyFile" {
|
||||
try expectFileContents(tmp.dir, dest_file2, data);
|
||||
}
|
||||
|
||||
fn expectFileContents(dir: fs.Dir, file_path: []const u8, data: []const u8) !void {
|
||||
fn expectFileContents(dir: Dir, file_path: []const u8, data: []const u8) !void {
|
||||
const contents = try dir.readFileAlloc(testing.allocator, file_path, 1000);
|
||||
defer testing.allocator.free(contents);
|
||||
|
||||
|
||||
@ -533,12 +533,13 @@ pub fn HashMapUnmanaged(
|
||||
}
|
||||
|
||||
pub fn clone(self: Self, allocator: *Allocator) !Self {
|
||||
// TODO this can be made more efficient by directly allocating
|
||||
// the memory slices and memcpying the elements.
|
||||
var other = Self.init();
|
||||
try other.initCapacity(allocator, self.entries.len);
|
||||
for (self.entries.items) |entry| {
|
||||
other.putAssumeCapacityNoClobber(entry.key, entry.value);
|
||||
var other: Self = .{};
|
||||
try other.entries.appendSlice(allocator, self.entries.items);
|
||||
|
||||
if (self.index_header) |header| {
|
||||
const new_header = try IndexHeader.alloc(allocator, header.indexes_len);
|
||||
other.insertAllEntriesIntoNewHeader(new_header);
|
||||
other.index_header = new_header;
|
||||
}
|
||||
return other;
|
||||
}
|
||||
@ -980,6 +981,25 @@ test "ensure capacity" {
|
||||
testing.expect(initial_capacity == map.capacity());
|
||||
}
|
||||
|
||||
test "clone" {
|
||||
var original = AutoHashMap(i32, i32).init(std.testing.allocator);
|
||||
defer original.deinit();
|
||||
|
||||
// put more than `linear_scan_max` so we can test that the index header is properly cloned
|
||||
var i: u8 = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
try original.putNoClobber(i, i * 10);
|
||||
}
|
||||
|
||||
var copy = try original.clone();
|
||||
defer copy.deinit();
|
||||
|
||||
i = 0;
|
||||
while (i < 10) : (i += 1) {
|
||||
testing.expect(copy.get(i).? == i * 10);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
|
||||
return struct {
|
||||
fn hash(key: K) u32 {
|
||||
|
||||
@ -27,7 +27,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
|
||||
@ -46,7 +46,7 @@ export_owners: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{},
|
||||
decl_table: std.HashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false) = .{},
|
||||
|
||||
optimize_mode: std.builtin.Mode,
|
||||
link_error_flags: link.ElfFile.ErrorFlags = .{},
|
||||
link_error_flags: link.File.ErrorFlags = .{},
|
||||
|
||||
work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
|
||||
|
||||
@ -90,7 +90,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.
|
||||
@ -168,7 +168,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,
|
||||
|
||||
@ -723,17 +723,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")) {
|
||||
@ -776,7 +778,7 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module {
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Module) void {
|
||||
self.bin_file.deinit();
|
||||
self.bin_file.destroy();
|
||||
const gpa = self.gpa;
|
||||
self.deletion_set.deinit(gpa);
|
||||
self.work_queue.deinit();
|
||||
@ -825,7 +827,7 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
|
||||
}
|
||||
|
||||
pub fn target(self: Module) std.Target {
|
||||
return self.bin_file.options.target;
|
||||
return self.bin_file.options().target;
|
||||
}
|
||||
|
||||
/// Detect changes to source files, perform semantic analysis, and update the output files.
|
||||
@ -872,7 +874,7 @@ pub fn update(self: *Module) !void {
|
||||
try self.bin_file.flush();
|
||||
}
|
||||
|
||||
self.link_error_flags = self.bin_file.error_flags;
|
||||
self.link_error_flags = self.bin_file.errorFlags();
|
||||
std.log.debug(.module, "link_error_flags: {}\n", .{self.link_error_flags});
|
||||
|
||||
// If there are any errors, we anticipate the source files being loaded
|
||||
@ -1985,8 +1987,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.gpa);
|
||||
}
|
||||
@ -2048,7 +2051,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;
|
||||
@ -2559,7 +2562,7 @@ fn createAnonymousDecl(
|
||||
) !*Decl {
|
||||
const name_index = self.getNextAnonNameIndex();
|
||||
const scope_decl = scope.decl().?;
|
||||
const name = try std.fmt.allocPrint(self.gpa, "{}${}", .{ scope_decl.name, name_index });
|
||||
const name = try std.fmt.allocPrint(self.gpa, "{}__anon_{}", .{ scope_decl.name, name_index });
|
||||
defer self.gpa.free(name);
|
||||
const name_hash = scope.namespace().fullyQualifiedNameHash(name);
|
||||
const src_hash: std.zig.SrcHash = undefined;
|
||||
|
||||
8
src-self-hosted/cbe.h
Normal file
8
src-self-hosted/cbe.h
Normal file
@ -0,0 +1,8 @@
|
||||
#if __STDC_VERSION__ >= 201112L
|
||||
#define noreturn _Noreturn
|
||||
#elif __GNUC__ && !__STRICT_ANSI__
|
||||
#define noreturn __attribute__ ((noreturn))
|
||||
#else
|
||||
#define noreturn
|
||||
#endif
|
||||
|
||||
168
src-self-hosted/cgen.zig
Normal file
168
src-self-hosted/cgen.zig
Normal file
@ -0,0 +1,168 @@
|
||||
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(allocator: *std.mem.Allocator, name: []const u8) ![]const u8 {
|
||||
return allocator.dupe(u8, 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(file.allocator, mem.spanZ(decl.name));
|
||||
defer file.allocator.free(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 => {
|
||||
// TODO: prevent inline asm constants from being emitted
|
||||
const name = try map(file.allocator, mem.span(decl.name));
|
||||
defer file.allocator.free(name);
|
||||
if (tv.val.cast(Value.Payload.Bytes)) |payload| {
|
||||
if (tv.ty.arraySentinel()) |sentinel| {
|
||||
if (sentinel.toUnsignedInt() == 0) {
|
||||
try file.constants.writer().print("const char *const {} = \"{}\";\n", .{ name, payload.data });
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO byte arrays with non-zero sentinels", .{});
|
||||
}
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO byte arrays without sentinels", .{});
|
||||
}
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-byte arrays", .{});
|
||||
}
|
||||
},
|
||||
else => |e| {
|
||||
return file.fail(decl.src(), "TODO {}", .{e});
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,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),
|
||||
@ -237,7 +237,7 @@ const InnerError = error {
|
||||
|
||||
const Function = struct {
|
||||
gpa: *Allocator,
|
||||
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
@ -74,7 +74,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);
|
||||
}
|
||||
|
||||
@ -94,14 +94,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);
|
||||
}
|
||||
}
|
||||
@ -194,6 +194,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);
|
||||
@ -209,7 +210,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;
|
||||
@ -221,12 +222,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;
|
||||
@ -240,52 +241,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;
|
||||
@ -327,39 +330,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});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -370,13 +373,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);
|
||||
}
|
||||
|
||||
@ -388,17 +391,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 '{}':
|
||||
\\
|
||||
@ -407,7 +410,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);
|
||||
},
|
||||
@ -419,21 +422,25 @@ 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 => if (cbe)
|
||||
try std.fmt.allocPrint(arena, "{}.c", .{root_name})
|
||||
else
|
||||
try std.zig.binNameAlloc(arena, root_name, target_info.target, output_mode, link_mode),
|
||||
|
||||
.yes => |p| p,
|
||||
};
|
||||
|
||||
@ -463,6 +470,7 @@ fn buildOutputType(
|
||||
.object_format = object_format,
|
||||
.optimize_mode = build_mode,
|
||||
.keep_source_files_loaded = zir_out_path != null,
|
||||
.cbe = cbe,
|
||||
});
|
||||
defer module.deinit();
|
||||
|
||||
@ -509,7 +517,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,
|
||||
@ -586,7 +594,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;
|
||||
@ -598,7 +606,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")) {
|
||||
@ -606,7 +614,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 {
|
||||
@ -617,7 +625,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);
|
||||
}
|
||||
|
||||
@ -627,7 +635,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();
|
||||
@ -650,7 +658,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);
|
||||
}
|
||||
|
||||
@ -667,7 +675,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);
|
||||
@ -705,7 +713,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;
|
||||
},
|
||||
@ -736,7 +744,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;
|
||||
};
|
||||
@ -787,7 +795,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 {
|
||||
@ -803,7 +811,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,65 @@ 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) {
|
||||
// The C file is always closed after an update, because we don't support
|
||||
// incremental updates
|
||||
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 +560,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);
|
||||
|
||||
@ -4012,6 +4012,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
|
||||
} else if (!is_extern) {
|
||||
add_node_error(g, source_node, buf_sprintf("variables must be initialized"));
|
||||
implicit_type = g->builtin_types.entry_invalid;
|
||||
} else if (explicit_type == nullptr) {
|
||||
// extern variable without explicit type
|
||||
add_node_error(g, source_node, buf_sprintf("unable to infer variable type"));
|
||||
implicit_type = g->builtin_types.entry_invalid;
|
||||
}
|
||||
|
||||
ZigType *type = explicit_type ? explicit_type : implicit_type;
|
||||
|
||||
@ -2,6 +2,15 @@ const tests = @import("tests.zig");
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add("extern variable has no type",
|
||||
\\extern var foo;
|
||||
\\pub export fn entry() void {
|
||||
\\ foo;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:1:1: error: unable to infer variable type",
|
||||
});
|
||||
|
||||
cases.add("@src outside function",
|
||||
\\comptime {
|
||||
\\ @src();
|
||||
|
||||
90
test/stage2/cbe.zig
Normal file
90
test/stage2/cbe.zig
Normal file
@ -0,0 +1,90 @@
|
||||
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
|
||||
// TODO: figure out a way to prevent asm constants from being generated
|
||||
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);
|
||||
\\
|
||||
\\const char *const exitGood__anon_0 = "{rax}";
|
||||
\\const char *const exitGood__anon_1 = "{rdi}";
|
||||
\\const char *const exitGood__anon_2 = "syscall";
|
||||
\\
|
||||
\\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);
|
||||
}
|
||||
|
||||
@ -22,8 +22,8 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
,
|
||||
\\@void = primitive(void)
|
||||
\\@fnty = fntype([], @void, cc=C)
|
||||
\\@9 = declref("9$0")
|
||||
\\@9$0 = str("entry")
|
||||
\\@9 = declref("9__anon_0")
|
||||
\\@9__anon_0 = str("entry")
|
||||
\\@unnamed$4 = str("entry")
|
||||
\\@unnamed$5 = export(@unnamed$4, "entry")
|
||||
\\@unnamed$6 = fntype([], @void, cc=C)
|
||||
@ -77,9 +77,9 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\@entry = fn(@unnamed$6, {
|
||||
\\ %0 = returnvoid()
|
||||
\\})
|
||||
\\@entry$1 = str("2\x08\x01\n")
|
||||
\\@9 = declref("9$0")
|
||||
\\@9$0 = str("entry")
|
||||
\\@entry__anon_1 = str("2\x08\x01\n")
|
||||
\\@9 = declref("9__anon_0")
|
||||
\\@9__anon_0 = str("entry")
|
||||
\\@unnamed$11 = str("entry")
|
||||
\\@unnamed$12 = export(@unnamed$11, "entry")
|
||||
\\
|
||||
@ -111,8 +111,8 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
,
|
||||
\\@void = primitive(void)
|
||||
\\@fnty = fntype([], @void, cc=C)
|
||||
\\@9 = declref("9$0")
|
||||
\\@9$0 = str("entry")
|
||||
\\@9 = declref("9__anon_0")
|
||||
\\@9__anon_0 = str("entry")
|
||||
\\@unnamed$4 = str("entry")
|
||||
\\@unnamed$5 = export(@unnamed$4, "entry")
|
||||
\\@unnamed$6 = fntype([], @void, cc=C)
|
||||
@ -187,8 +187,8 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
,
|
||||
\\@void = primitive(void)
|
||||
\\@fnty = fntype([], @void, cc=C)
|
||||
\\@9 = declref("9$2")
|
||||
\\@9$2 = str("entry")
|
||||
\\@9 = declref("9__anon_2")
|
||||
\\@9__anon_2 = str("entry")
|
||||
\\@unnamed$4 = str("entry")
|
||||
\\@unnamed$5 = export(@unnamed$4, "entry")
|
||||
\\@unnamed$6 = fntype([], @void, cc=C)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user