Merge remote-tracking branch 'origin/master' into remove-array-type-coercion
This commit is contained in:
commit
b36c07a95a
@ -7455,6 +7455,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }
|
||||
Attempting to convert a number which is out of range of the destination type results in
|
||||
safety-protected {#link|Undefined Behavior#}.
|
||||
</p>
|
||||
<p>
|
||||
If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
|
||||
then this is semantically equivalent to {#link|Type Coercion#}.
|
||||
</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@intToEnum#}
|
||||
@ -8206,10 +8210,6 @@ test "integer truncation" {
|
||||
This function always truncates the significant bits of the integer, regardless
|
||||
of endianness on the target platform.
|
||||
</p>
|
||||
<p>
|
||||
If {#syntax#}T{#endsyntax#} is {#syntax#}comptime_int{#endsyntax#},
|
||||
then this is semantically equivalent to {#link|Type Coercion#}.
|
||||
</p>
|
||||
{#header_close#}
|
||||
|
||||
{#header_open|@Type#}
|
||||
|
@ -40,6 +40,14 @@ pub fn AlignedArrayList(comptime T: type, comptime alignment: ?u29) type {
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
|
||||
/// Initialize with capacity to hold at least num elements.
|
||||
/// Deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
pub fn initCapacity(allocator: *Allocator, num: usize) !Self {
|
||||
var self = Self.init(allocator);
|
||||
try self.ensureCapacity(num);
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Release all allocated memory.
|
||||
pub fn deinit(self: Self) void {
|
||||
@ -271,6 +279,15 @@ test "std.ArrayList.init" {
|
||||
testing.expect(list.capacity() == 0);
|
||||
}
|
||||
|
||||
test "std.ArrayList.initCapacity" {
|
||||
var bytes: [1024]u8 = undefined;
|
||||
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
|
||||
var list = try ArrayList(i8).initCapacity(allocator, 200);
|
||||
defer list.deinit();
|
||||
testing.expect(list.count() == 0);
|
||||
testing.expect(list.capacity() >= 200);
|
||||
}
|
||||
|
||||
test "std.ArrayList.basic" {
|
||||
var bytes: [1024]u8 = undefined;
|
||||
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
|
||||
|
@ -16,13 +16,22 @@ pub const Buffer = struct {
|
||||
mem.copy(u8, self.list.items, m);
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
/// Initialize memory to size bytes of undefined values.
|
||||
/// Must deinitialize with deinit.
|
||||
pub fn initSize(allocator: *Allocator, size: usize) !Buffer {
|
||||
var self = initNull(allocator);
|
||||
try self.resize(size);
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Initialize with capacity to hold at least num bytes.
|
||||
/// Must deinitialize with deinit.
|
||||
pub fn initCapacity(allocator: *Allocator, num: usize) !Buffer {
|
||||
var self = Buffer{ .list = try ArrayList(u8).initCapacity(allocator, num + 1) };
|
||||
self.list.appendAssumeCapacity(0);
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Must deinitialize with deinit.
|
||||
/// None of the other operations are valid until you do one of these:
|
||||
@ -98,6 +107,13 @@ pub const Buffer = struct {
|
||||
pub fn len(self: Buffer) usize {
|
||||
return self.list.len - 1;
|
||||
}
|
||||
|
||||
pub fn capacity(self: Buffer) usize {
|
||||
return if (self.list.items.len > 0)
|
||||
self.list.items.len - 1
|
||||
else
|
||||
0;
|
||||
}
|
||||
|
||||
pub fn append(self: *Buffer, m: []const u8) !void {
|
||||
const old_len = self.len();
|
||||
@ -151,3 +167,21 @@ test "simple Buffer" {
|
||||
try buf2.resize(4);
|
||||
testing.expect(buf.startsWith(buf2.toSlice()));
|
||||
}
|
||||
|
||||
test "Buffer.initSize" {
|
||||
var buf = try Buffer.initSize(debug.global_allocator, 3);
|
||||
testing.expect(buf.len() == 3);
|
||||
try buf.append("hello");
|
||||
testing.expect(mem.eql(u8, buf.toSliceConst()[3..], "hello"));
|
||||
}
|
||||
|
||||
test "Buffer.initCapacity" {
|
||||
var buf = try Buffer.initCapacity(debug.global_allocator, 10);
|
||||
testing.expect(buf.len() == 0);
|
||||
testing.expect(buf.capacity() >= 10);
|
||||
const old_cap = buf.capacity();
|
||||
try buf.append("hello");
|
||||
testing.expect(buf.len() == 5);
|
||||
testing.expect(buf.capacity() == old_cap);
|
||||
testing.expect(mem.eql(u8, buf.toSliceConst(), "hello"));
|
||||
}
|
||||
|
@ -2062,14 +2062,28 @@ pub const RunStep = struct {
|
||||
}
|
||||
|
||||
pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
|
||||
const PATH = if (builtin.os == .windows) "Path" else "PATH";
|
||||
const env_map = self.getEnvMap();
|
||||
const prev_path = env_map.get(PATH) orelse {
|
||||
env_map.set(PATH, search_path) catch unreachable;
|
||||
return;
|
||||
};
|
||||
const new_path = self.builder.fmt("{}" ++ &[1]u8{fs.path.delimiter} ++ "{}", prev_path, search_path);
|
||||
env_map.set(PATH, new_path) catch unreachable;
|
||||
|
||||
var key: []const u8 = undefined;
|
||||
var prev_path: ?[]const u8 = undefined;
|
||||
if (builtin.os == .windows) {
|
||||
key = "Path";
|
||||
prev_path = env_map.get(key);
|
||||
if (prev_path == null) {
|
||||
key = "PATH";
|
||||
prev_path = env_map.get(key);
|
||||
}
|
||||
} else {
|
||||
key = "PATH";
|
||||
prev_path = env_map.get(key);
|
||||
}
|
||||
|
||||
if (prev_path) |pp| {
|
||||
const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", pp, search_path);
|
||||
env_map.set(key, new_path) catch unreachable;
|
||||
} else {
|
||||
env_map.set(key, search_path) catch unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getEnvMap(self: *RunStep) *BufMap {
|
||||
@ -2178,7 +2192,7 @@ const InstallArtifactStep = struct {
|
||||
|
||||
const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
|
||||
try builder.updateFile(self.artifact.getOutputPath(), full_dest_path);
|
||||
if (self.artifact.isDynamicLibrary()) {
|
||||
if (self.artifact.isDynamicLibrary() and self.artifact.target.wantSharedLibSymLinks()) {
|
||||
try doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename, self.artifact.name_only_filename);
|
||||
}
|
||||
if (self.pdb_dir) |pdb_dir| {
|
||||
@ -2405,7 +2419,7 @@ fn findVcpkgRoot(allocator: *Allocator) !?[]const u8 {
|
||||
const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
|
||||
defer allocator.free(path_file);
|
||||
|
||||
const file = fs.File.openRead(path_file) catch return null;
|
||||
const file = fs.cwd().openFile(path_file, .{}) catch return null;
|
||||
defer file.close();
|
||||
|
||||
const size = @intCast(usize, try file.getEndPos());
|
||||
|
@ -220,6 +220,7 @@ pub extern "c" fn pthread_mutex_destroy(mutex: *pthread_mutex_t) c_int;
|
||||
|
||||
pub const PTHREAD_COND_INITIALIZER = pthread_cond_t{};
|
||||
pub extern "c" fn pthread_cond_wait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t) c_int;
|
||||
pub extern "c" fn pthread_cond_timedwait(noalias cond: *pthread_cond_t, noalias mutex: *pthread_mutex_t, noalias abstime: *const timespec) c_int;
|
||||
pub extern "c" fn pthread_cond_signal(cond: *pthread_cond_t) c_int;
|
||||
pub extern "c" fn pthread_cond_destroy(cond: *pthread_cond_t) c_int;
|
||||
|
||||
|
@ -1131,7 +1131,7 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
|
||||
}
|
||||
|
||||
fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
|
||||
var f = try File.openRead(line_info.file_name);
|
||||
var f = try fs.cwd().openFile(line_info.file_name, .{});
|
||||
defer f.close();
|
||||
// TODO fstat and make sure that the file has the correct size
|
||||
|
||||
@ -2089,7 +2089,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
|
||||
const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
|
||||
|
||||
gop.kv.value = MachOFile{
|
||||
.bytes = try std.fs.Dir.cwd().readFileAllocAligned(
|
||||
.bytes = try std.fs.cwd().readFileAllocAligned(
|
||||
di.ofiles.allocator,
|
||||
ofile_path,
|
||||
maxInt(usize),
|
||||
@ -2417,6 +2417,12 @@ extern fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: *con
|
||||
std.debug.warn("Segmentation fault at address 0x{x}\n", addr);
|
||||
|
||||
switch (builtin.arch) {
|
||||
.i386 => {
|
||||
const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
|
||||
const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_EIP]);
|
||||
const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_EBP]);
|
||||
dumpStackTraceFromBase(bp, ip);
|
||||
},
|
||||
.x86_64 => {
|
||||
const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
|
||||
const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
|
||||
|
@ -54,6 +54,10 @@ pub fn Channel(comptime T: type) type {
|
||||
/// For a zero length buffer, use `[0]T{}`.
|
||||
/// TODO https://github.com/ziglang/zig/issues/2765
|
||||
pub fn init(self: *SelfChannel, buffer: []T) void {
|
||||
// The ring buffer implementation only works with power of 2 buffer sizes
|
||||
// because of relying on subtracting across zero. For example (0 -% 1) % 10 == 5
|
||||
assert(buffer.len == 0 or @popCount(usize, buffer.len) == 1);
|
||||
|
||||
self.* = SelfChannel{
|
||||
.buffer_len = 0,
|
||||
.buffer_nodes = buffer,
|
||||
@ -184,11 +188,11 @@ pub fn Channel(comptime T: type) type {
|
||||
const get_node = &self.getters.get().?.data;
|
||||
switch (get_node.data) {
|
||||
GetNode.Data.Normal => |info| {
|
||||
info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
|
||||
info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len];
|
||||
},
|
||||
GetNode.Data.OrNull => |info| {
|
||||
_ = self.or_null_queue.remove(info.or_null);
|
||||
info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
|
||||
info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len];
|
||||
},
|
||||
}
|
||||
global_event_loop.onNextTick(get_node.tick_node);
|
||||
@ -222,7 +226,7 @@ pub fn Channel(comptime T: type) type {
|
||||
while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
|
||||
const put_node = &self.putters.get().?.data;
|
||||
|
||||
self.buffer_nodes[self.buffer_index] = put_node.data;
|
||||
self.buffer_nodes[self.buffer_index % self.buffer_nodes.len] = put_node.data;
|
||||
global_event_loop.onNextTick(put_node.tick_node);
|
||||
self.buffer_index +%= 1;
|
||||
self.buffer_len += 1;
|
||||
@ -283,6 +287,29 @@ test "std.event.Channel" {
|
||||
await putter;
|
||||
}
|
||||
|
||||
test "std.event.Channel wraparound" {
|
||||
|
||||
// TODO provide a way to run tests in evented I/O mode
|
||||
if (!std.io.is_async) return error.SkipZigTest;
|
||||
|
||||
const channel_size = 2;
|
||||
|
||||
var buf : [channel_size]i32 = undefined;
|
||||
var channel: Channel(i32) = undefined;
|
||||
channel.init(&buf);
|
||||
defer channel.deinit();
|
||||
|
||||
// add items to channel and pull them out until
|
||||
// the buffer wraps around, make sure it doesn't crash.
|
||||
var result : i32 = undefined;
|
||||
channel.put(5);
|
||||
testing.expectEqual(@as(i32, 5), channel.get());
|
||||
channel.put(6);
|
||||
testing.expectEqual(@as(i32, 6), channel.get());
|
||||
channel.put(7);
|
||||
testing.expectEqual(@as(i32, 7), channel.get());
|
||||
}
|
||||
|
||||
async fn testChannelGetter(channel: *Channel(i32)) void {
|
||||
const value1 = channel.get();
|
||||
testing.expect(value1 == 1234);
|
||||
|
@ -735,24 +735,26 @@ pub fn Watch(comptime V: type) type {
|
||||
allocator: *Allocator,
|
||||
|
||||
const OsData = switch (builtin.os) {
|
||||
.macosx, .freebsd, .netbsd, .dragonfly => struct {
|
||||
file_table: FileTable,
|
||||
table_lock: event.Lock,
|
||||
|
||||
const FileTable = std.StringHashMap(*Put);
|
||||
const Put = struct {
|
||||
putter_frame: @Frame(kqPutEvents),
|
||||
cancelled: bool = false,
|
||||
value: V,
|
||||
};
|
||||
},
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/3778
|
||||
.macosx, .freebsd, .netbsd, .dragonfly => KqOsData,
|
||||
.linux => LinuxOsData,
|
||||
.windows => WindowsOsData,
|
||||
|
||||
else => @compileError("Unsupported OS"),
|
||||
};
|
||||
|
||||
const KqOsData = struct {
|
||||
file_table: FileTable,
|
||||
table_lock: event.Lock,
|
||||
|
||||
const FileTable = std.StringHashMap(*Put);
|
||||
const Put = struct {
|
||||
putter_frame: @Frame(kqPutEvents),
|
||||
cancelled: bool = false,
|
||||
value: V,
|
||||
};
|
||||
};
|
||||
|
||||
const WindowsOsData = struct {
|
||||
table_lock: event.Lock,
|
||||
dir_table: DirTable,
|
||||
@ -1291,7 +1293,7 @@ pub fn Watch(comptime V: type) type {
|
||||
os.linux.EINVAL => unreachable,
|
||||
os.linux.EFAULT => unreachable,
|
||||
os.linux.EAGAIN => {
|
||||
global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN);
|
||||
global_event_loop.linuxWaitFd(self.os_data.inotify_fd, os.linux.EPOLLET | os.linux.EPOLLIN | os.EPOLLONESHOT);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
278
lib/std/fs.zig
278
lib/std/fs.zig
@ -13,8 +13,6 @@ pub const File = @import("fs/file.zig").File;
|
||||
|
||||
pub const symLink = os.symlink;
|
||||
pub const symLinkC = os.symlinkC;
|
||||
pub const deleteFile = os.unlink;
|
||||
pub const deleteFileC = os.unlinkC;
|
||||
pub const rename = os.rename;
|
||||
pub const renameC = os.renameC;
|
||||
pub const renameW = os.renameW;
|
||||
@ -88,13 +86,15 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
|
||||
/// If any of the directories do not exist for dest_path, they are created.
|
||||
/// TODO https://github.com/ziglang/zig/issues/2885
|
||||
pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
|
||||
var src_file = try File.openRead(source_path);
|
||||
const my_cwd = cwd();
|
||||
|
||||
var src_file = try my_cwd.openFile(source_path, .{});
|
||||
defer src_file.close();
|
||||
|
||||
const src_stat = try src_file.stat();
|
||||
check_dest_stat: {
|
||||
const dest_stat = blk: {
|
||||
var dest_file = File.openRead(dest_path) catch |err| switch (err) {
|
||||
var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => break :check_dest_stat,
|
||||
else => |e| return e,
|
||||
};
|
||||
@ -157,7 +157,7 @@ pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?Fil
|
||||
/// in the same directory as dest_path.
|
||||
/// Destination file will have the same mode as the source file.
|
||||
pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
|
||||
var in_file = try File.openRead(source_path);
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
const mode = try in_file.mode();
|
||||
@ -180,7 +180,7 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
|
||||
/// merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
|
||||
var in_file = try File.openRead(source_path);
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, mode);
|
||||
@ -206,8 +206,6 @@ pub const AtomicFile = struct {
|
||||
|
||||
/// dest_path must remain valid for the lifetime of AtomicFile
|
||||
/// call finish to atomically replace dest_path with contents
|
||||
/// TODO once we have null terminated pointers, use the
|
||||
/// openWriteNoClobberN function
|
||||
pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
|
||||
const dirname = path.dirname(dest_path);
|
||||
var rand_buf: [12]u8 = undefined;
|
||||
@ -224,15 +222,19 @@ pub const AtomicFile = struct {
|
||||
|
||||
tmp_path_buf[tmp_path_len] = 0;
|
||||
|
||||
const my_cwd = cwd();
|
||||
|
||||
while (true) {
|
||||
try crypto.randomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], &rand_buf);
|
||||
|
||||
const file = File.openWriteNoClobberC(@ptrCast([*:0]u8, &tmp_path_buf), mode) catch |err| switch (err) {
|
||||
// TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast
|
||||
const file = my_cwd.createFileC(
|
||||
@ptrCast([*:0]u8, &tmp_path_buf),
|
||||
.{ .mode = mode, .exclusive = true },
|
||||
) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
// TODO zig should figure out that this error set does not include PathAlreadyExists since
|
||||
// it is handled in the above switch
|
||||
else => return err,
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
return AtomicFile{
|
||||
@ -248,7 +250,7 @@ pub const AtomicFile = struct {
|
||||
pub fn deinit(self: *AtomicFile) void {
|
||||
if (!self.finished) {
|
||||
self.file.close();
|
||||
deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
|
||||
cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
|
||||
self.finished = true;
|
||||
}
|
||||
}
|
||||
@ -350,12 +352,12 @@ pub fn deleteTree(full_path: []const u8) !void {
|
||||
CannotDeleteRootDirectory,
|
||||
}.CannotDeleteRootDirectory;
|
||||
|
||||
var dir = try Dir.cwd().openDirList(dirname);
|
||||
var dir = try cwd().openDirList(dirname);
|
||||
defer dir.close();
|
||||
|
||||
return dir.deleteTree(path.basename(full_path));
|
||||
} else {
|
||||
return Dir.cwd().deleteTree(full_path);
|
||||
return cwd().deleteTree(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -657,17 +659,6 @@ pub const Dir = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an handle to the current working directory that is open for traversal.
|
||||
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
|
||||
/// On POSIX targets, this function is comptime-callable.
|
||||
pub fn cwd() Dir {
|
||||
if (builtin.os == .windows) {
|
||||
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
|
||||
} else {
|
||||
return Dir{ .fd = os.AT_FDCWD };
|
||||
}
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
FileNotFound,
|
||||
NotDir,
|
||||
@ -683,12 +674,12 @@ pub const Dir = struct {
|
||||
DeviceBusy,
|
||||
} || os.UnexpectedError;
|
||||
|
||||
/// Deprecated; call `Dir.cwd().openDirList` directly.
|
||||
/// Deprecated; call `cwd().openDirList` directly.
|
||||
pub fn open(dir_path: []const u8) OpenError!Dir {
|
||||
return cwd().openDirList(dir_path);
|
||||
}
|
||||
|
||||
/// Deprecated; call `Dir.cwd().openDirListC` directly.
|
||||
/// Deprecated; call `cwd().openDirListC` directly.
|
||||
pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {
|
||||
return cwd().openDirListC(dir_path_c);
|
||||
}
|
||||
@ -698,29 +689,110 @@ pub const Dir = struct {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Call `File.close` on the result when done.
|
||||
pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
|
||||
/// Opens a file for reading or writing, without attempting to create a new file.
|
||||
/// Call `File.close` to release the resource.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
|
||||
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.openReadW(&path_w);
|
||||
return self.openFileW(&path_w, flags);
|
||||
}
|
||||
const path_c = try os.toPosixPath(sub_path);
|
||||
return self.openReadC(&path_c);
|
||||
return self.openFileC(&path_c, flags);
|
||||
}
|
||||
|
||||
/// Call `File.close` on the result when done.
|
||||
pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File {
|
||||
/// Same as `openFile` but the path parameter is null-terminated.
|
||||
pub fn openFileC(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
|
||||
return self.openReadW(&path_w);
|
||||
return self.openFileW(&path_w, flags);
|
||||
}
|
||||
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
|
||||
const flags = O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
|
||||
const fd = try os.openatC(self.fd, sub_path, flags, 0);
|
||||
return File.openHandle(fd);
|
||||
const os_flags = O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read)
|
||||
@as(u32, os.O_RDWR)
|
||||
else if (flags.write)
|
||||
@as(u32, os.O_WRONLY)
|
||||
else
|
||||
@as(u32, os.O_RDONLY);
|
||||
const fd = try os.openatC(self.fd, sub_path, os_flags, 0);
|
||||
return File{ .handle = fd };
|
||||
}
|
||||
|
||||
pub fn openReadW(self: Dir, sub_path_w: [*:0]const u16) File.OpenError!File {
|
||||
/// Same as `openFile` but Windows-only and the path parameter is
|
||||
/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
|
||||
pub fn openFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {
|
||||
const w = os.windows;
|
||||
const access_mask = w.SYNCHRONIZE |
|
||||
(if (flags.read) @as(u32, w.GENERIC_READ) else 0) |
|
||||
(if (flags.write) @as(u32, w.GENERIC_WRITE) else 0);
|
||||
return self.openFileWindows(sub_path_w, access_mask, w.FILE_OPEN);
|
||||
}
|
||||
|
||||
/// Creates, opens, or overwrites a file with write access.
|
||||
/// Call `File.close` on the result when done.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
|
||||
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.createFileW(&path_w, flags);
|
||||
}
|
||||
const path_c = try os.toPosixPath(sub_path);
|
||||
return self.createFileC(&path_c, flags);
|
||||
}
|
||||
|
||||
/// Same as `createFile` but the path parameter is null-terminated.
|
||||
pub fn createFileC(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
|
||||
return self.createFileW(&path_w, flags);
|
||||
}
|
||||
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
|
||||
const os_flags = O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC |
|
||||
(if (flags.truncate) @as(u32, os.O_TRUNC) else 0) |
|
||||
(if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) |
|
||||
(if (flags.exclusive) @as(u32, os.O_EXCL) else 0);
|
||||
const fd = try os.openatC(self.fd, sub_path_c, os_flags, flags.mode);
|
||||
return File{ .handle = fd };
|
||||
}
|
||||
|
||||
/// Same as `createFile` but Windows-only and the path parameter is
|
||||
/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
|
||||
pub fn createFileW(self: Dir, sub_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
|
||||
const w = os.windows;
|
||||
const access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE |
|
||||
(if (flags.read) @as(u32, w.GENERIC_READ) else 0);
|
||||
const creation = if (flags.exclusive)
|
||||
@as(u32, w.FILE_CREATE)
|
||||
else if (flags.truncate)
|
||||
@as(u32, w.FILE_OVERWRITE_IF)
|
||||
else
|
||||
@as(u32, w.FILE_OPEN_IF);
|
||||
return self.openFileWindows(sub_path_w, access_mask, creation);
|
||||
}
|
||||
|
||||
/// Deprecated; call `openFile` directly.
|
||||
pub fn openRead(self: Dir, sub_path: []const u8) File.OpenError!File {
|
||||
return self.openFile(sub_path, .{});
|
||||
}
|
||||
|
||||
/// Deprecated; call `openFileC` directly.
|
||||
pub fn openReadC(self: Dir, sub_path: [*:0]const u8) File.OpenError!File {
|
||||
return self.openFileC(sub_path, .{});
|
||||
}
|
||||
|
||||
/// Deprecated; call `openFileW` directly.
|
||||
pub fn openReadW(self: Dir, sub_path: [*:0]const u16) File.OpenError!File {
|
||||
return self.openFileW(sub_path, .{});
|
||||
}
|
||||
|
||||
pub fn openFileWindows(
|
||||
self: Dir,
|
||||
sub_path_w: [*:0]const u16,
|
||||
access_mask: os.windows.ACCESS_MASK,
|
||||
creation: os.windows.ULONG,
|
||||
) File.OpenError!File {
|
||||
const w = os.windows;
|
||||
|
||||
var result = File{ .handle = undefined };
|
||||
@ -750,13 +822,13 @@ pub const Dir = struct {
|
||||
var io: w.IO_STATUS_BLOCK = undefined;
|
||||
const rc = w.ntdll.NtCreateFile(
|
||||
&result.handle,
|
||||
w.GENERIC_READ | w.SYNCHRONIZE,
|
||||
access_mask,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
w.FILE_ATTRIBUTE_NORMAL,
|
||||
w.FILE_SHARE_READ,
|
||||
w.FILE_OPEN,
|
||||
w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE,
|
||||
creation,
|
||||
w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
null,
|
||||
0,
|
||||
@ -771,6 +843,7 @@ pub const Dir = struct {
|
||||
w.STATUS.ACCESS_DENIED => return error.AccessDenied,
|
||||
w.STATUS.PIPE_BUSY => return error.PipeBusy,
|
||||
w.STATUS.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
w.STATUS.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return w.unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
@ -790,7 +863,10 @@ pub const Dir = struct {
|
||||
/// list the contents of a directory, open it with `openDirList`.
|
||||
///
|
||||
/// Call `close` on the result when done.
|
||||
///
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
|
||||
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
|
||||
if (builtin.os == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.openDirTraverseW(&sub_path_w);
|
||||
@ -805,7 +881,10 @@ pub const Dir = struct {
|
||||
/// same and may be more efficient.
|
||||
///
|
||||
/// Call `close` on the result when done.
|
||||
///
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
|
||||
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
|
||||
if (builtin.os == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.openDirListW(&sub_path_w);
|
||||
@ -920,9 +999,12 @@ pub const Dir = struct {
|
||||
pub const DeleteFileError = os.UnlinkError;
|
||||
|
||||
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.deleteFileC(&sub_path_c);
|
||||
os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
|
||||
error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
/// Same as `deleteFile` except the parameter is null-terminated.
|
||||
@ -933,6 +1015,14 @@ pub const Dir = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Same as `deleteFile` except the parameter is WTF-16 encoded.
|
||||
pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void {
|
||||
os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
|
||||
error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
pub const DeleteDirError = error{
|
||||
DirNotEmpty,
|
||||
FileNotFound,
|
||||
@ -951,7 +1041,9 @@ pub const Dir = struct {
|
||||
|
||||
/// Returns `error.DirNotEmpty` if the directory is not empty.
|
||||
/// To delete a directory recursively, see `deleteTree`.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
|
||||
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
|
||||
if (builtin.os == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.deleteDirW(&sub_path_w);
|
||||
@ -979,7 +1071,9 @@ pub const Dir = struct {
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
/// The return value is a slice of `buffer`, from index `0`.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn readLink(self: Dir, sub_path: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
|
||||
if (std.debug.runtime_safety) for (sub_path) |byte| assert(byte != 0);
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.readLinkC(&sub_path_c, buffer);
|
||||
}
|
||||
@ -1190,8 +1284,94 @@ pub const Dir = struct {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes content to the file system, creating a new file if it does not exist, truncating
|
||||
/// if it already exists.
|
||||
pub fn writeFile(self: Dir, sub_path: []const u8, data: []const u8) !void {
|
||||
var file = try self.createFile(sub_path, .{});
|
||||
defer file.close();
|
||||
try file.write(data);
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns an handle to the current working directory that is open for traversal.
|
||||
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
|
||||
/// On POSIX targets, this function is comptime-callable.
|
||||
pub fn cwd() Dir {
|
||||
if (builtin.os == .windows) {
|
||||
return Dir{ .fd = os.windows.peb().ProcessParameters.CurrentDirectory.Handle };
|
||||
} else {
|
||||
return Dir{ .fd = os.AT_FDCWD };
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
|
||||
/// Call `File.close` to release the resource.
|
||||
/// Asserts that the path is absolute. See `Dir.openFile` for a function that
|
||||
/// operates on both absolute and relative paths.
|
||||
/// Asserts that the path parameter has no null bytes. See `openFileAbsoluteC` for a function
|
||||
/// that accepts a null-terminated path.
|
||||
pub fn openFileAbsolute(absolute_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
|
||||
assert(path.isAbsolute(absolute_path));
|
||||
return cwd().openFile(absolute_path, flags);
|
||||
}
|
||||
|
||||
/// Same as `openFileAbsolute` but the path parameter is null-terminated.
|
||||
pub fn openFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
|
||||
assert(path.isAbsoluteC(absolute_path_c));
|
||||
return cwd().openFileC(absolute_path_c, flags);
|
||||
}
|
||||
|
||||
/// Same as `openFileAbsolute` but the path parameter is WTF-16 encoded.
|
||||
pub fn openFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.OpenFlags) File.OpenError!File {
|
||||
assert(path.isAbsoluteW(absolute_path_w));
|
||||
return cwd().openFileW(absolute_path_w, flags);
|
||||
}
|
||||
|
||||
/// Creates, opens, or overwrites a file with write access, based on an absolute path.
|
||||
/// Call `File.close` to release the resource.
|
||||
/// Asserts that the path is absolute. See `Dir.createFile` for a function that
|
||||
/// operates on both absolute and relative paths.
|
||||
/// Asserts that the path parameter has no null bytes. See `createFileAbsoluteC` for a function
|
||||
/// that accepts a null-terminated path.
|
||||
pub fn createFileAbsolute(absolute_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
|
||||
assert(path.isAbsolute(absolute_path));
|
||||
return cwd().createFile(absolute_path, flags);
|
||||
}
|
||||
|
||||
/// Same as `createFileAbsolute` but the path parameter is null-terminated.
|
||||
pub fn createFileAbsoluteC(absolute_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
|
||||
assert(path.isAbsoluteC(absolute_path_c));
|
||||
return cwd().createFileC(absolute_path_c, flags);
|
||||
}
|
||||
|
||||
/// Same as `createFileAbsolute` but the path parameter is WTF-16 encoded.
|
||||
pub fn createFileAbsoluteW(absolute_path_w: [*:0]const u16, flags: File.CreateFlags) File.OpenError!File {
|
||||
assert(path.isAbsoluteW(absolute_path_w));
|
||||
return cwd().createFileW(absolute_path_w, flags);
|
||||
}
|
||||
|
||||
/// Delete a file name and possibly the file it refers to, based on an absolute path.
|
||||
/// Asserts that the path is absolute. See `Dir.deleteFile` for a function that
|
||||
/// operates on both absolute and relative paths.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn deleteFileAbsolute(absolute_path: []const u8) DeleteFileError!void {
|
||||
assert(path.isAbsolute(absolute_path));
|
||||
return cwd().deleteFile(absolute_path);
|
||||
}
|
||||
|
||||
/// Same as `deleteFileAbsolute` except the parameter is null-terminated.
|
||||
pub fn deleteFileAbsoluteC(absolute_path_c: [*:0]const u8) DeleteFileError!void {
|
||||
assert(path.isAbsoluteC(absolute_path_c));
|
||||
return cwd().deleteFileC(absolute_path_c);
|
||||
}
|
||||
|
||||
/// Same as `deleteFileAbsolute` except the parameter is WTF-16 encoded.
|
||||
pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void {
|
||||
assert(path.isAbsoluteW(absolute_path_w));
|
||||
return cwd().deleteFileW(absolute_path_w);
|
||||
}
|
||||
|
||||
pub const Walker = struct {
|
||||
stack: std.ArrayList(StackItem),
|
||||
name_buffer: std.Buffer,
|
||||
@ -1264,7 +1444,7 @@ pub const Walker = struct {
|
||||
pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
|
||||
assert(!mem.endsWith(u8, dir_path, path.sep_str));
|
||||
|
||||
var dir = try Dir.cwd().openDirList(dir_path);
|
||||
var dir = try cwd().openDirList(dir_path);
|
||||
errdefer dir.close();
|
||||
|
||||
var name_buffer = try std.Buffer.init(allocator, dir_path);
|
||||
@ -1298,18 +1478,18 @@ pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfE
|
||||
|
||||
pub fn openSelfExe() OpenSelfExeError!File {
|
||||
if (builtin.os == .linux) {
|
||||
return File.openReadC("/proc/self/exe");
|
||||
return openFileAbsoluteC("/proc/self/exe", .{});
|
||||
}
|
||||
if (builtin.os == .windows) {
|
||||
const wide_slice = selfExePathW();
|
||||
const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
|
||||
return Dir.cwd().openReadW(&prefixed_path_w);
|
||||
return cwd().openReadW(&prefixed_path_w);
|
||||
}
|
||||
var buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const self_exe_path = try selfExePath(&buf);
|
||||
buf[self_exe_path.len] = 0;
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
|
||||
return File.openReadC(@ptrCast([*:0]u8, self_exe_path.ptr));
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{});
|
||||
}
|
||||
|
||||
test "openSelfExe" {
|
||||
|
@ -25,105 +25,87 @@ pub const File = struct {
|
||||
|
||||
pub const OpenError = windows.CreateFileError || os.OpenError;
|
||||
|
||||
/// Deprecated; call `std.fs.Dir.openRead` directly.
|
||||
/// TODO https://github.com/ziglang/zig/issues/3802
|
||||
pub const OpenFlags = struct {
|
||||
read: bool = true,
|
||||
write: bool = false,
|
||||
};
|
||||
|
||||
/// TODO https://github.com/ziglang/zig/issues/3802
|
||||
pub const CreateFlags = struct {
|
||||
/// Whether the file will be created with read access.
|
||||
read: bool = false,
|
||||
|
||||
/// If the file already exists, and is a regular file, and the access
|
||||
/// mode allows writing, it will be truncated to length 0.
|
||||
truncate: bool = true,
|
||||
|
||||
/// Ensures that this open call creates the file, otherwise causes
|
||||
/// `error.FileAlreadyExists` to be returned.
|
||||
exclusive: bool = false,
|
||||
|
||||
/// For POSIX systems this is the file system mode the file will
|
||||
/// be created with.
|
||||
mode: Mode = default_mode,
|
||||
};
|
||||
|
||||
/// Deprecated; call `std.fs.Dir.openFile` directly.
|
||||
pub fn openRead(path: []const u8) OpenError!File {
|
||||
return std.fs.Dir.cwd().openRead(path);
|
||||
return std.fs.cwd().openFile(path, .{});
|
||||
}
|
||||
|
||||
/// Deprecated; call `std.fs.Dir.openReadC` directly.
|
||||
/// Deprecated; call `std.fs.Dir.openFileC` directly.
|
||||
pub fn openReadC(path_c: [*:0]const u8) OpenError!File {
|
||||
return std.fs.Dir.cwd().openReadC(path_c);
|
||||
return std.fs.cwd().openFileC(path_c, .{});
|
||||
}
|
||||
|
||||
/// Deprecated; call `std.fs.Dir.openReadW` directly.
|
||||
/// Deprecated; call `std.fs.Dir.openFileW` directly.
|
||||
pub fn openReadW(path_w: [*]const u16) OpenError!File {
|
||||
return std.fs.Dir.cwd().openReadW(path_w);
|
||||
return std.fs.cwd().openFileW(path_w, .{});
|
||||
}
|
||||
|
||||
/// Calls `openWriteMode` with `default_mode` for the mode.
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
/// Deprecated; call `std.fs.Dir.createFile` directly.
|
||||
pub fn openWrite(path: []const u8) OpenError!File {
|
||||
return openWriteMode(path, default_mode);
|
||||
return std.fs.cwd().createFile(path, .{});
|
||||
}
|
||||
|
||||
/// If the path does not exist it will be created.
|
||||
/// If a file already exists in the destination it will be truncated.
|
||||
/// Call close to clean up.
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
/// Deprecated; call `std.fs.Dir.createFile` directly.
|
||||
pub fn openWriteMode(path: []const u8, file_mode: Mode) OpenError!File {
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try windows.sliceToPrefixedFileW(path);
|
||||
return openWriteModeW(&path_w, file_mode);
|
||||
}
|
||||
const path_c = try os.toPosixPath(path);
|
||||
return openWriteModeC(&path_c, file_mode);
|
||||
return std.fs.cwd().createFile(path, .{ .mode = file_mode });
|
||||
}
|
||||
|
||||
/// Same as `openWriteMode` except `path` is null-terminated.
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
pub fn openWriteModeC(path: [*:0]const u8, file_mode: Mode) OpenError!File {
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try windows.cStrToPrefixedFileW(path);
|
||||
return openWriteModeW(&path_w, file_mode);
|
||||
}
|
||||
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
|
||||
const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
|
||||
const fd = try os.openC(path, flags, file_mode);
|
||||
return openHandle(fd);
|
||||
/// Deprecated; call `std.fs.Dir.createFileC` directly.
|
||||
pub fn openWriteModeC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File {
|
||||
return std.fs.cwd().createFileC(path_c, .{ .mode = file_mode });
|
||||
}
|
||||
|
||||
/// Same as `openWriteMode` except `path` is null-terminated and UTF16LE encoded
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
/// Deprecated; call `std.fs.Dir.createFileW` directly.
|
||||
pub fn openWriteModeW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {
|
||||
const handle = try windows.CreateFileW(
|
||||
path_w,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
null,
|
||||
windows.CREATE_ALWAYS,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
null,
|
||||
);
|
||||
return openHandle(handle);
|
||||
return std.fs.cwd().createFileW(path_w, .{ .mode = file_mode });
|
||||
}
|
||||
|
||||
/// If the path does not exist it will be created.
|
||||
/// If a file already exists in the destination this returns OpenError.PathAlreadyExists
|
||||
/// Call close to clean up.
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
/// Deprecated; call `std.fs.Dir.createFile` directly.
|
||||
pub fn openWriteNoClobber(path: []const u8, file_mode: Mode) OpenError!File {
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try windows.sliceToPrefixedFileW(path);
|
||||
return openWriteNoClobberW(&path_w, file_mode);
|
||||
}
|
||||
const path_c = try os.toPosixPath(path);
|
||||
return openWriteNoClobberC(&path_c, file_mode);
|
||||
return std.fs.cwd().createFile(path, .{
|
||||
.mode = file_mode,
|
||||
.exclusive = true,
|
||||
});
|
||||
}
|
||||
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
pub fn openWriteNoClobberC(path: [*:0]const u8, file_mode: Mode) OpenError!File {
|
||||
if (builtin.os == .windows) {
|
||||
const path_w = try windows.cStrToPrefixedFileW(path);
|
||||
return openWriteNoClobberW(&path_w, file_mode);
|
||||
}
|
||||
const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0;
|
||||
const flags = O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_EXCL;
|
||||
const fd = try os.openC(path, flags, file_mode);
|
||||
return openHandle(fd);
|
||||
/// Deprecated; call `std.fs.Dir.createFileC` directly.
|
||||
pub fn openWriteNoClobberC(path_c: [*:0]const u8, file_mode: Mode) OpenError!File {
|
||||
return std.fs.cwd().createFileC(path_c, .{
|
||||
.mode = file_mode,
|
||||
.exclusive = true,
|
||||
});
|
||||
}
|
||||
|
||||
/// TODO: deprecate this and move it to `std.fs.Dir`.
|
||||
/// Deprecated; call `std.fs.Dir.createFileW` directly.
|
||||
pub fn openWriteNoClobberW(path_w: [*:0]const u16, file_mode: Mode) OpenError!File {
|
||||
const handle = try windows.CreateFileW(
|
||||
path_w,
|
||||
windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_WRITE | windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE,
|
||||
null,
|
||||
windows.CREATE_NEW,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
null,
|
||||
);
|
||||
return openHandle(handle);
|
||||
return std.fs.cwd().createFileW(path_w, .{
|
||||
.mode = file_mode,
|
||||
.exclusive = true,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn openHandle(handle: os.fd_t) File {
|
||||
@ -246,6 +228,7 @@ pub const File = struct {
|
||||
windows.STATUS.SUCCESS => {},
|
||||
windows.STATUS.BUFFER_OVERFLOW => {},
|
||||
windows.STATUS.INVALID_PARAMETER => unreachable,
|
||||
windows.STATUS.ACCESS_DENIED => return error.AccessDenied,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
}
|
||||
return Stat{
|
||||
|
@ -130,6 +130,14 @@ test "join" {
|
||||
testJoinPosix(&[_][]const u8{ "a/", "/c" }, "a/c");
|
||||
}
|
||||
|
||||
pub fn isAbsoluteC(path_c: [*:0]const u8) bool {
|
||||
if (builtin.os == .windows) {
|
||||
return isAbsoluteWindowsC(path_c);
|
||||
} else {
|
||||
return isAbsolutePosixC(path_c);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isAbsolute(path: []const u8) bool {
|
||||
if (builtin.os == .windows) {
|
||||
return isAbsoluteWindows(path);
|
||||
@ -138,7 +146,7 @@ pub fn isAbsolute(path: []const u8) bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isAbsoluteW(path_w: [*]const u16) bool {
|
||||
pub fn isAbsoluteW(path_w: [*:0]const u16) bool {
|
||||
if (path_w[0] == '/')
|
||||
return true;
|
||||
|
||||
@ -176,10 +184,33 @@ pub fn isAbsoluteWindows(path: []const u8) bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn isAbsoluteWindowsC(path_c: [*:0]const u8) bool {
|
||||
if (path_c[0] == '/')
|
||||
return true;
|
||||
|
||||
if (path_c[0] == '\\') {
|
||||
return true;
|
||||
}
|
||||
if (path_c[0] == 0 or path_c[1] == 0 or path_c[2] == 0) {
|
||||
return false;
|
||||
}
|
||||
if (path_c[1] == ':') {
|
||||
if (path_c[2] == '/')
|
||||
return true;
|
||||
if (path_c[2] == '\\')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn isAbsolutePosix(path: []const u8) bool {
|
||||
return path[0] == sep_posix;
|
||||
}
|
||||
|
||||
pub fn isAbsolutePosixC(path_c: [*:0]const u8) bool {
|
||||
return path_c[0] == sep_posix;
|
||||
}
|
||||
|
||||
test "isAbsoluteWindows" {
|
||||
testIsAbsoluteWindows("/", true);
|
||||
testIsAbsoluteWindows("//", true);
|
||||
|
@ -61,17 +61,14 @@ pub const COutStream = @import("io/c_out_stream.zig").COutStream;
|
||||
pub const InStream = @import("io/in_stream.zig").InStream;
|
||||
pub const OutStream = @import("io/out_stream.zig").OutStream;
|
||||
|
||||
/// TODO move this to `std.fs` and add a version to `std.fs.Dir`.
|
||||
/// Deprecated; use `std.fs.Dir.writeFile`.
|
||||
pub fn writeFile(path: []const u8, data: []const u8) !void {
|
||||
var file = try File.openWrite(path);
|
||||
defer file.close();
|
||||
try file.write(data);
|
||||
return fs.cwd().writeFile(path, data);
|
||||
}
|
||||
|
||||
/// On success, caller owns returned buffer.
|
||||
/// This function is deprecated; use `std.fs.Dir.readFileAlloc`.
|
||||
/// Deprecated; use `std.fs.Dir.readFileAlloc`.
|
||||
pub fn readFileAlloc(allocator: *mem.Allocator, path: []const u8) ![]u8 {
|
||||
return fs.Dir.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
|
||||
return fs.cwd().readFileAlloc(allocator, path, math.maxInt(usize));
|
||||
}
|
||||
|
||||
pub fn BufferedInStream(comptime Error: type) type {
|
||||
|
@ -14,12 +14,14 @@ test "write a file, read it, then delete it" {
|
||||
var raw_bytes: [200 * 1024]u8 = undefined;
|
||||
var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;
|
||||
|
||||
const cwd = fs.cwd();
|
||||
|
||||
var data: [1024]u8 = undefined;
|
||||
var prng = DefaultPrng.init(1234);
|
||||
prng.random.bytes(data[0..]);
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try File.openWrite(tmp_file_name);
|
||||
var file = try cwd.createFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_out_stream = file.outStream();
|
||||
@ -32,8 +34,8 @@ test "write a file, read it, then delete it" {
|
||||
}
|
||||
|
||||
{
|
||||
// make sure openWriteNoClobber doesn't harm the file
|
||||
if (File.openWriteNoClobber(tmp_file_name, File.default_mode)) |file| {
|
||||
// Make sure the exclusive flag is honored.
|
||||
if (cwd.createFile(tmp_file_name, .{ .exclusive = true })) |file| {
|
||||
unreachable;
|
||||
} else |err| {
|
||||
std.debug.assert(err == File.OpenError.PathAlreadyExists);
|
||||
@ -41,7 +43,7 @@ test "write a file, read it, then delete it" {
|
||||
}
|
||||
|
||||
{
|
||||
var file = try File.openRead(tmp_file_name);
|
||||
var file = try cwd.openFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
const file_size = try file.getEndPos();
|
||||
@ -58,7 +60,7 @@ test "write a file, read it, then delete it" {
|
||||
expect(mem.eql(u8, contents["begin".len .. contents.len - "end".len], &data));
|
||||
expect(mem.eql(u8, contents[contents.len - "end".len ..], "end"));
|
||||
}
|
||||
try fs.deleteFile(tmp_file_name);
|
||||
try cwd.deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
test "BufferOutStream" {
|
||||
@ -274,7 +276,7 @@ test "BitOutStream" {
|
||||
test "BitStreams with File Stream" {
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
{
|
||||
var file = try File.openWrite(tmp_file_name);
|
||||
var file = try fs.cwd().createFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_out = file.outStream();
|
||||
@ -291,7 +293,7 @@ test "BitStreams with File Stream" {
|
||||
try bit_stream.flushBits();
|
||||
}
|
||||
{
|
||||
var file = try File.openRead(tmp_file_name);
|
||||
var file = try fs.cwd().openFile(tmp_file_name, .{});
|
||||
defer file.close();
|
||||
|
||||
var file_in = file.inStream();
|
||||
@ -316,7 +318,7 @@ test "BitStreams with File Stream" {
|
||||
|
||||
expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1));
|
||||
}
|
||||
try fs.deleteFile(tmp_file_name);
|
||||
try fs.cwd().deleteFile(tmp_file_name);
|
||||
}
|
||||
|
||||
fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime packing: io.Packing) !void {
|
||||
@ -599,7 +601,7 @@ test "c out stream" {
|
||||
const out_file = std.c.fopen(filename, "w") orelse return error.UnableToOpenTestFile;
|
||||
defer {
|
||||
_ = std.c.fclose(out_file);
|
||||
fs.deleteFileC(filename) catch {};
|
||||
fs.cwd().deleteFileC(filename) catch {};
|
||||
}
|
||||
|
||||
const out_stream = &io.COutStream.init(out_file).stream;
|
||||
@ -608,10 +610,10 @@ test "c out stream" {
|
||||
|
||||
test "File seek ops" {
|
||||
const tmp_file_name = "temp_test_file.txt";
|
||||
var file = try File.openWrite(tmp_file_name);
|
||||
var file = try fs.cwd().createFile(tmp_file_name, .{});
|
||||
defer {
|
||||
file.close();
|
||||
fs.deleteFile(tmp_file_name) catch {};
|
||||
fs.cwd().deleteFile(tmp_file_name) catch {};
|
||||
}
|
||||
|
||||
try file.write(&([_]u8{0x55} ** 8192));
|
||||
@ -632,10 +634,10 @@ test "File seek ops" {
|
||||
|
||||
test "updateTimes" {
|
||||
const tmp_file_name = "just_a_temporary_file.txt";
|
||||
var file = try File.openWrite(tmp_file_name);
|
||||
var file = try fs.cwd().createFile(tmp_file_name, .{ .read = true });
|
||||
defer {
|
||||
file.close();
|
||||
std.fs.deleteFile(tmp_file_name) catch {};
|
||||
std.fs.cwd().deleteFile(tmp_file_name) catch {};
|
||||
}
|
||||
var stat_old = try file.stat();
|
||||
// Set atime and mtime to 5s before
|
||||
|
@ -25,18 +25,6 @@ pub const ln2 = 0.693147180559945309417232121458176568;
|
||||
/// ln(10)
|
||||
pub const ln10 = 2.302585092994045684017991454684364208;
|
||||
|
||||
/// π/2
|
||||
pub const pi_2 = 1.570796326794896619231321691639751442;
|
||||
|
||||
/// π/4
|
||||
pub const pi_4 = 0.785398163397448309615660845819875721;
|
||||
|
||||
/// 1/π
|
||||
pub const one_pi = 0.318309886183790671537767526745028724;
|
||||
|
||||
/// 2/π
|
||||
pub const two_pi = 0.636619772367581343075535053490057448;
|
||||
|
||||
/// 2/sqrt(π)
|
||||
pub const two_sqrtpi = 1.128379167095512573896158903121545172;
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const testing = std.testing;
|
||||
const SpinLock = std.SpinLock;
|
||||
const ThreadParker = std.ThreadParker;
|
||||
const ResetEvent = std.ResetEvent;
|
||||
|
||||
/// Lock may be held only once. If the same thread
|
||||
/// tries to acquire the same mutex twice, it deadlocks.
|
||||
/// This type supports static initialization and is based off of Golang 1.13 runtime.lock_futex:
|
||||
/// https://github.com/golang/go/blob/master/src/runtime/lock_futex.go
|
||||
/// This type supports static initialization and is based off of Webkit's WTF Lock (via rust parking_lot)
|
||||
/// https://github.com/Amanieu/parking_lot/blob/master/core/src/word_lock.rs
|
||||
/// When an application is built in single threaded release mode, all the functions are
|
||||
/// no-ops. In single threaded debug mode, there is deadlock detection.
|
||||
pub const Mutex = if (builtin.single_threaded)
|
||||
@ -39,80 +38,119 @@ pub const Mutex = if (builtin.single_threaded)
|
||||
}
|
||||
else
|
||||
struct {
|
||||
state: State, // TODO: make this an enum
|
||||
parker: ThreadParker,
|
||||
state: usize,
|
||||
|
||||
const State = enum(u32) {
|
||||
Unlocked,
|
||||
Sleeping,
|
||||
Locked,
|
||||
};
|
||||
const MUTEX_LOCK: usize = 1 << 0;
|
||||
const QUEUE_LOCK: usize = 1 << 1;
|
||||
const QUEUE_MASK: usize = ~(MUTEX_LOCK | QUEUE_LOCK);
|
||||
const QueueNode = std.atomic.Stack(ResetEvent).Node;
|
||||
|
||||
/// number of iterations to spin yielding the cpu
|
||||
const SPIN_CPU = 4;
|
||||
|
||||
/// number of iterations to perform in the cpu yield loop
|
||||
/// number of iterations to spin in the cpu yield loop
|
||||
const SPIN_CPU_COUNT = 30;
|
||||
|
||||
/// number of iterations to spin yielding the thread
|
||||
const SPIN_THREAD = 1;
|
||||
|
||||
pub fn init() Mutex {
|
||||
return Mutex{
|
||||
.state = .Unlocked,
|
||||
.parker = ThreadParker.init(),
|
||||
};
|
||||
return Mutex{ .state = 0 };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Mutex) void {
|
||||
self.parker.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub const Held = struct {
|
||||
mutex: *Mutex,
|
||||
|
||||
pub fn release(self: Held) void {
|
||||
switch (@atomicRmw(State, &self.mutex.state, .Xchg, .Unlocked, .Release)) {
|
||||
.Locked => {},
|
||||
.Sleeping => self.mutex.parker.unpark(@ptrCast(*const u32, &self.mutex.state)),
|
||||
.Unlocked => unreachable, // unlocking an unlocked mutex
|
||||
else => unreachable, // should never be anything else
|
||||
// since MUTEX_LOCK is the first bit, we can use (.Sub) instead of (.And, ~MUTEX_LOCK).
|
||||
// this is because .Sub may be implemented more efficiently than the latter
|
||||
// (e.g. `lock xadd` vs `cmpxchg` loop on x86)
|
||||
const state = @atomicRmw(usize, &self.mutex.state, .Sub, MUTEX_LOCK, .Release);
|
||||
if ((state & QUEUE_MASK) != 0 and (state & QUEUE_LOCK) == 0) {
|
||||
self.mutex.releaseSlow(state);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub fn acquire(self: *Mutex) Held {
|
||||
// Try and speculatively grab the lock.
|
||||
// If it fails, the state is either Locked or Sleeping
|
||||
// depending on if theres a thread stuck sleeping below.
|
||||
var state = @atomicRmw(State, &self.state, .Xchg, .Locked, .Acquire);
|
||||
if (state == .Unlocked)
|
||||
return Held{ .mutex = self };
|
||||
// fast path close to SpinLock fast path
|
||||
if (@cmpxchgWeak(usize, &self.state, 0, MUTEX_LOCK, .Acquire, .Monotonic)) |current_state| {
|
||||
self.acquireSlow(current_state);
|
||||
}
|
||||
return Held{ .mutex = self };
|
||||
}
|
||||
|
||||
fn acquireSlow(self: *Mutex, current_state: usize) void {
|
||||
var spin: usize = 0;
|
||||
var state = current_state;
|
||||
while (true) {
|
||||
|
||||
// try and acquire the lock if unlocked
|
||||
if ((state & MUTEX_LOCK) == 0) {
|
||||
state = @cmpxchgWeak(usize, &self.state, state, state | MUTEX_LOCK, .Acquire, .Monotonic) orelse return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// spin only if the waiting queue isn't empty and when it hasn't spun too much already
|
||||
if ((state & QUEUE_MASK) == 0 and spin < SPIN_CPU + SPIN_THREAD) {
|
||||
if (spin < SPIN_CPU) {
|
||||
std.SpinLock.yield(SPIN_CPU_COUNT);
|
||||
} else {
|
||||
std.os.sched_yield() catch std.time.sleep(0);
|
||||
}
|
||||
state = @atomicLoad(usize, &self.state, .Monotonic);
|
||||
continue;
|
||||
}
|
||||
|
||||
// thread should block, try and add this event to the waiting queue
|
||||
var node = QueueNode{
|
||||
.next = @intToPtr(?*QueueNode, state & QUEUE_MASK),
|
||||
.data = ResetEvent.init(),
|
||||
};
|
||||
defer node.data.deinit();
|
||||
const new_state = @ptrToInt(&node) | (state & ~QUEUE_MASK);
|
||||
state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse {
|
||||
// node is in the queue, wait until a `held.release()` wakes us up.
|
||||
_ = node.data.wait(null) catch unreachable;
|
||||
spin = 0;
|
||||
state = @atomicLoad(usize, &self.state, .Monotonic);
|
||||
continue;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn releaseSlow(self: *Mutex, current_state: usize) void {
|
||||
// grab the QUEUE_LOCK in order to signal a waiting queue node's event.
|
||||
var state = current_state;
|
||||
while (true) {
|
||||
if ((state & QUEUE_LOCK) != 0 or (state & QUEUE_MASK) == 0)
|
||||
return;
|
||||
state = @cmpxchgWeak(usize, &self.state, state, state | QUEUE_LOCK, .Acquire, .Monotonic) orelse break;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// try and acquire the lock using cpu spinning on failure
|
||||
var spin: usize = 0;
|
||||
while (spin < SPIN_CPU) : (spin += 1) {
|
||||
var value = @atomicLoad(State, &self.state, .Monotonic);
|
||||
while (value == .Unlocked)
|
||||
value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };
|
||||
SpinLock.yield(SPIN_CPU_COUNT);
|
||||
// barrier needed to observe incoming state changes
|
||||
defer @fence(.Acquire);
|
||||
|
||||
// the mutex is currently locked. try to unset the QUEUE_LOCK and let the locker wake up the next node.
|
||||
// avoids waking up multiple sleeping threads which try to acquire the lock again which increases contention.
|
||||
if ((state & MUTEX_LOCK) != 0) {
|
||||
state = @cmpxchgWeak(usize, &self.state, state, state & ~QUEUE_LOCK, .Release, .Monotonic) orelse return;
|
||||
continue;
|
||||
}
|
||||
|
||||
// try and acquire the lock using thread rescheduling on failure
|
||||
spin = 0;
|
||||
while (spin < SPIN_THREAD) : (spin += 1) {
|
||||
var value = @atomicLoad(State, &self.state, .Monotonic);
|
||||
while (value == .Unlocked)
|
||||
value = @cmpxchgWeak(State, &self.state, .Unlocked, state, .Acquire, .Monotonic) orelse return Held{ .mutex = self };
|
||||
std.os.sched_yield() catch std.time.sleep(1);
|
||||
}
|
||||
|
||||
// failed to acquire the lock, go to sleep until woken up by `Held.release()`
|
||||
if (@atomicRmw(State, &self.state, .Xchg, .Sleeping, .Acquire) == .Unlocked)
|
||||
return Held{ .mutex = self };
|
||||
state = .Sleeping;
|
||||
self.parker.park(@ptrCast(*const u32, &self.state), @enumToInt(State.Sleeping));
|
||||
// try to pop the top node on the waiting queue stack to wake it up
|
||||
// while at the same time unsetting the QUEUE_LOCK.
|
||||
const node = @intToPtr(*QueueNode, state & QUEUE_MASK);
|
||||
const new_state = @ptrToInt(node.next) | (state & MUTEX_LOCK);
|
||||
state = @cmpxchgWeak(usize, &self.state, state, new_state, .Release, .Monotonic) orelse {
|
||||
_ = node.data.set(false);
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -812,7 +812,7 @@ fn linuxLookupNameFromHosts(
|
||||
family: os.sa_family_t,
|
||||
port: u16,
|
||||
) !void {
|
||||
const file = fs.File.openReadC("/etc/hosts") catch |err| switch (err) {
|
||||
const file = fs.openFileAbsoluteC("/etc/hosts", .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.AccessDenied,
|
||||
@ -1006,7 +1006,7 @@ fn getResolvConf(allocator: *mem.Allocator, rc: *ResolvConf) !void {
|
||||
};
|
||||
errdefer rc.deinit();
|
||||
|
||||
const file = fs.File.openReadC("/etc/resolv.conf") catch |err| switch (err) {
|
||||
const file = fs.openFileAbsoluteC("/etc/resolv.conf", .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.AccessDenied,
|
||||
|
@ -798,7 +798,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
|
||||
path_buf[search_path.len] = '/';
|
||||
mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
|
||||
path_buf[search_path.len + file_slice.len + 1] = 0;
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);
|
||||
switch (err) {
|
||||
error.AccessDenied => seen_eacces = true,
|
||||
@ -834,7 +834,7 @@ pub fn execvpe(
|
||||
@memcpy(arg_buf.ptr, arg.ptr, arg.len);
|
||||
arg_buf[arg.len] = 0;
|
||||
|
||||
// TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3731
|
||||
// TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr);
|
||||
}
|
||||
argv_buf[argv_slice.len] = null;
|
||||
@ -842,7 +842,7 @@ pub fn execvpe(
|
||||
const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
|
||||
defer freeNullDelimitedEnvMap(allocator, envp_buf);
|
||||
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr);
|
||||
|
||||
return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);
|
||||
@ -863,12 +863,12 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
|
||||
@memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
|
||||
env_buf[env_buf.len - 1] = 0;
|
||||
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);
|
||||
}
|
||||
assert(i == envp_count);
|
||||
}
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3731
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
assert(envp_buf[envp_count] == null);
|
||||
return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];
|
||||
}
|
||||
@ -1087,7 +1087,9 @@ pub const UnlinkatError = UnlinkError || error{
|
||||
};
|
||||
|
||||
/// Delete a file name and possibly the file it refers to, based on an open directory handle.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
|
||||
if (std.debug.runtime_safety) for (file_path) |byte| assert(byte != 0);
|
||||
if (builtin.os == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return unlinkatW(dirfd, &file_path_w, flags);
|
||||
@ -2026,7 +2028,10 @@ pub fn waitpid(pid: i32, flags: u32) u32 {
|
||||
}
|
||||
}
|
||||
|
||||
pub const FStatError = error{SystemResources} || UnexpectedError;
|
||||
pub const FStatError = error{
|
||||
SystemResources,
|
||||
AccessDenied,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
var stat: Stat = undefined;
|
||||
@ -2036,6 +2041,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
EINVAL => unreachable,
|
||||
EBADF => unreachable, // Always a race condition.
|
||||
ENOMEM => return error.SystemResources,
|
||||
EACCES => return error.AccessDenied,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
@ -2045,6 +2051,7 @@ pub fn fstat(fd: fd_t) FStatError!Stat {
|
||||
EINVAL => unreachable,
|
||||
EBADF => unreachable, // Always a race condition.
|
||||
ENOMEM => return error.SystemResources,
|
||||
EACCES => return error.AccessDenied,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ pub usingnamespace switch (builtin.arch) {
|
||||
};
|
||||
|
||||
pub usingnamespace switch (builtin.arch) {
|
||||
.i386 => @import("linux/i386.zig"),
|
||||
.x86_64 => @import("linux/x86_64.zig"),
|
||||
.aarch64 => @import("linux/arm64.zig"),
|
||||
.arm => @import("linux/arm-eabi.zig"),
|
||||
|
@ -466,7 +466,6 @@ pub const MAP_LOCKED = 0x2000;
|
||||
/// don't check for reservations
|
||||
pub const MAP_NORESERVE = 0x4000;
|
||||
|
||||
pub const VDSO_USEFUL = true;
|
||||
pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
|
||||
pub const VDSO_CGT_VER = "LINUX_2.6";
|
||||
|
||||
|
@ -358,7 +358,6 @@ pub const MAP_LOCKED = 0x2000;
|
||||
/// don't check for reservations
|
||||
pub const MAP_NORESERVE = 0x4000;
|
||||
|
||||
pub const VDSO_USEFUL = true;
|
||||
pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
|
||||
pub const VDSO_CGT_VER = "LINUX_2.6.39";
|
||||
|
||||
|
642
lib/std/os/bits/linux/i386.zig
Normal file
642
lib/std/os/bits/linux/i386.zig
Normal file
@ -0,0 +1,642 @@
|
||||
// i386-specific declarations that are intended to be imported into the POSIX namespace.
|
||||
// This does include Linux-only APIs.
|
||||
|
||||
const std = @import("../../../std.zig");
|
||||
const linux = std.os.linux;
|
||||
const socklen_t = linux.socklen_t;
|
||||
const iovec = linux.iovec;
|
||||
const iovec_const = linux.iovec_const;
|
||||
const uid_t = linux.uid_t;
|
||||
const gid_t = linux.gid_t;
|
||||
const stack_t = linux.stack_t;
|
||||
const sigset_t = linux.sigset_t;
|
||||
|
||||
pub const SYS_restart_syscall = 0;
|
||||
pub const SYS_exit = 1;
|
||||
pub const SYS_fork = 2;
|
||||
pub const SYS_read = 3;
|
||||
pub const SYS_write = 4;
|
||||
pub const SYS_open = 5;
|
||||
pub const SYS_close = 6;
|
||||
pub const SYS_waitpid = 7;
|
||||
pub const SYS_creat = 8;
|
||||
pub const SYS_link = 9;
|
||||
pub const SYS_unlink = 10;
|
||||
pub const SYS_execve = 11;
|
||||
pub const SYS_chdir = 12;
|
||||
pub const SYS_time = 13;
|
||||
pub const SYS_mknod = 14;
|
||||
pub const SYS_chmod = 15;
|
||||
pub const SYS_lchown = 16;
|
||||
pub const SYS_break = 17;
|
||||
pub const SYS_oldstat = 18;
|
||||
pub const SYS_lseek = 19;
|
||||
pub const SYS_getpid = 20;
|
||||
pub const SYS_mount = 21;
|
||||
pub const SYS_umount = 22;
|
||||
pub const SYS_setuid = 23;
|
||||
pub const SYS_getuid = 24;
|
||||
pub const SYS_stime = 25;
|
||||
pub const SYS_ptrace = 26;
|
||||
pub const SYS_alarm = 27;
|
||||
pub const SYS_oldfstat = 28;
|
||||
pub const SYS_pause = 29;
|
||||
pub const SYS_utime = 30;
|
||||
pub const SYS_stty = 31;
|
||||
pub const SYS_gtty = 32;
|
||||
pub const SYS_access = 33;
|
||||
pub const SYS_nice = 34;
|
||||
pub const SYS_ftime = 35;
|
||||
pub const SYS_sync = 36;
|
||||
pub const SYS_kill = 37;
|
||||
pub const SYS_rename = 38;
|
||||
pub const SYS_mkdir = 39;
|
||||
pub const SYS_rmdir = 40;
|
||||
pub const SYS_dup = 41;
|
||||
pub const SYS_pipe = 42;
|
||||
pub const SYS_times = 43;
|
||||
pub const SYS_prof = 44;
|
||||
pub const SYS_brk = 45;
|
||||
pub const SYS_setgid = 46;
|
||||
pub const SYS_getgid = 47;
|
||||
pub const SYS_signal = 48;
|
||||
pub const SYS_geteuid = 49;
|
||||
pub const SYS_getegid = 50;
|
||||
pub const SYS_acct = 51;
|
||||
pub const SYS_umount2 = 52;
|
||||
pub const SYS_lock = 53;
|
||||
pub const SYS_ioctl = 54;
|
||||
pub const SYS_fcntl = 55;
|
||||
pub const SYS_mpx = 56;
|
||||
pub const SYS_setpgid = 57;
|
||||
pub const SYS_ulimit = 58;
|
||||
pub const SYS_oldolduname = 59;
|
||||
pub const SYS_umask = 60;
|
||||
pub const SYS_chroot = 61;
|
||||
pub const SYS_ustat = 62;
|
||||
pub const SYS_dup2 = 63;
|
||||
pub const SYS_getppid = 64;
|
||||
pub const SYS_getpgrp = 65;
|
||||
pub const SYS_setsid = 66;
|
||||
pub const SYS_sigaction = 67;
|
||||
pub const SYS_sgetmask = 68;
|
||||
pub const SYS_ssetmask = 69;
|
||||
pub const SYS_setreuid = 70;
|
||||
pub const SYS_setregid = 71;
|
||||
pub const SYS_sigsuspend = 72;
|
||||
pub const SYS_sigpending = 73;
|
||||
pub const SYS_sethostname = 74;
|
||||
pub const SYS_setrlimit = 75;
|
||||
pub const SYS_getrlimit = 76;
|
||||
pub const SYS_getrusage = 77;
|
||||
pub const SYS_gettimeofday = 78;
|
||||
pub const SYS_settimeofday = 79;
|
||||
pub const SYS_getgroups = 80;
|
||||
pub const SYS_setgroups = 81;
|
||||
pub const SYS_select = 82;
|
||||
pub const SYS_symlink = 83;
|
||||
pub const SYS_oldlstat = 84;
|
||||
pub const SYS_readlink = 85;
|
||||
pub const SYS_uselib = 86;
|
||||
pub const SYS_swapon = 87;
|
||||
pub const SYS_reboot = 88;
|
||||
pub const SYS_readdir = 89;
|
||||
pub const SYS_mmap = 90;
|
||||
pub const SYS_munmap = 91;
|
||||
pub const SYS_truncate = 92;
|
||||
pub const SYS_ftruncate = 93;
|
||||
pub const SYS_fchmod = 94;
|
||||
pub const SYS_fchown = 95;
|
||||
pub const SYS_getpriority = 96;
|
||||
pub const SYS_setpriority = 97;
|
||||
pub const SYS_profil = 98;
|
||||
pub const SYS_statfs = 99;
|
||||
pub const SYS_fstatfs = 100;
|
||||
pub const SYS_ioperm = 101;
|
||||
pub const SYS_socketcall = 102;
|
||||
pub const SYS_syslog = 103;
|
||||
pub const SYS_setitimer = 104;
|
||||
pub const SYS_getitimer = 105;
|
||||
pub const SYS_stat = 106;
|
||||
pub const SYS_lstat = 107;
|
||||
pub const SYS_fstat = 108;
|
||||
pub const SYS_olduname = 109;
|
||||
pub const SYS_iopl = 110;
|
||||
pub const SYS_vhangup = 111;
|
||||
pub const SYS_idle = 112;
|
||||
pub const SYS_vm86old = 113;
|
||||
pub const SYS_wait4 = 114;
|
||||
pub const SYS_swapoff = 115;
|
||||
pub const SYS_sysinfo = 116;
|
||||
pub const SYS_ipc = 117;
|
||||
pub const SYS_fsync = 118;
|
||||
pub const SYS_sigreturn = 119;
|
||||
pub const SYS_clone = 120;
|
||||
pub const SYS_setdomainname = 121;
|
||||
pub const SYS_uname = 122;
|
||||
pub const SYS_modify_ldt = 123;
|
||||
pub const SYS_adjtimex = 124;
|
||||
pub const SYS_mprotect = 125;
|
||||
pub const SYS_sigprocmask = 126;
|
||||
pub const SYS_create_module = 127;
|
||||
pub const SYS_init_module = 128;
|
||||
pub const SYS_delete_module = 129;
|
||||
pub const SYS_get_kernel_syms = 130;
|
||||
pub const SYS_quotactl = 131;
|
||||
pub const SYS_getpgid = 132;
|
||||
pub const SYS_fchdir = 133;
|
||||
pub const SYS_bdflush = 134;
|
||||
pub const SYS_sysfs = 135;
|
||||
pub const SYS_personality = 136;
|
||||
pub const SYS_afs_syscall = 137;
|
||||
pub const SYS_setfsuid = 138;
|
||||
pub const SYS_setfsgid = 139;
|
||||
pub const SYS__llseek = 140;
|
||||
pub const SYS_getdents = 141;
|
||||
pub const SYS__newselect = 142;
|
||||
pub const SYS_flock = 143;
|
||||
pub const SYS_msync = 144;
|
||||
pub const SYS_readv = 145;
|
||||
pub const SYS_writev = 146;
|
||||
pub const SYS_getsid = 147;
|
||||
pub const SYS_fdatasync = 148;
|
||||
pub const SYS__sysctl = 149;
|
||||
pub const SYS_mlock = 150;
|
||||
pub const SYS_munlock = 151;
|
||||
pub const SYS_mlockall = 152;
|
||||
pub const SYS_munlockall = 153;
|
||||
pub const SYS_sched_setparam = 154;
|
||||
pub const SYS_sched_getparam = 155;
|
||||
pub const SYS_sched_setscheduler = 156;
|
||||
pub const SYS_sched_getscheduler = 157;
|
||||
pub const SYS_sched_yield = 158;
|
||||
pub const SYS_sched_get_priority_max = 159;
|
||||
pub const SYS_sched_get_priority_min = 160;
|
||||
pub const SYS_sched_rr_get_interval = 161;
|
||||
pub const SYS_nanosleep = 162;
|
||||
pub const SYS_mremap = 163;
|
||||
pub const SYS_setresuid = 164;
|
||||
pub const SYS_getresuid = 165;
|
||||
pub const SYS_vm86 = 166;
|
||||
pub const SYS_query_module = 167;
|
||||
pub const SYS_poll = 168;
|
||||
pub const SYS_nfsservctl = 169;
|
||||
pub const SYS_setresgid = 170;
|
||||
pub const SYS_getresgid = 171;
|
||||
pub const SYS_prctl = 172;
|
||||
pub const SYS_rt_sigreturn = 173;
|
||||
pub const SYS_rt_sigaction = 174;
|
||||
pub const SYS_rt_sigprocmask = 175;
|
||||
pub const SYS_rt_sigpending = 176;
|
||||
pub const SYS_rt_sigtimedwait = 177;
|
||||
pub const SYS_rt_sigqueueinfo = 178;
|
||||
pub const SYS_rt_sigsuspend = 179;
|
||||
pub const SYS_pread64 = 180;
|
||||
pub const SYS_pwrite64 = 181;
|
||||
pub const SYS_chown = 182;
|
||||
pub const SYS_getcwd = 183;
|
||||
pub const SYS_capget = 184;
|
||||
pub const SYS_capset = 185;
|
||||
pub const SYS_sigaltstack = 186;
|
||||
pub const SYS_sendfile = 187;
|
||||
pub const SYS_getpmsg = 188;
|
||||
pub const SYS_putpmsg = 189;
|
||||
pub const SYS_vfork = 190;
|
||||
pub const SYS_ugetrlimit = 191;
|
||||
pub const SYS_mmap2 = 192;
|
||||
pub const SYS_truncate64 = 193;
|
||||
pub const SYS_ftruncate64 = 194;
|
||||
pub const SYS_stat64 = 195;
|
||||
pub const SYS_lstat64 = 196;
|
||||
pub const SYS_fstat64 = 197;
|
||||
pub const SYS_lchown32 = 198;
|
||||
pub const SYS_getuid32 = 199;
|
||||
pub const SYS_getgid32 = 200;
|
||||
pub const SYS_geteuid32 = 201;
|
||||
pub const SYS_getegid32 = 202;
|
||||
pub const SYS_setreuid32 = 203;
|
||||
pub const SYS_setregid32 = 204;
|
||||
pub const SYS_getgroups32 = 205;
|
||||
pub const SYS_setgroups32 = 206;
|
||||
pub const SYS_fchown32 = 207;
|
||||
pub const SYS_setresuid32 = 208;
|
||||
pub const SYS_getresuid32 = 209;
|
||||
pub const SYS_setresgid32 = 210;
|
||||
pub const SYS_getresgid32 = 211;
|
||||
pub const SYS_chown32 = 212;
|
||||
pub const SYS_setuid32 = 213;
|
||||
pub const SYS_setgid32 = 214;
|
||||
pub const SYS_setfsuid32 = 215;
|
||||
pub const SYS_setfsgid32 = 216;
|
||||
pub const SYS_pivot_root = 217;
|
||||
pub const SYS_mincore = 218;
|
||||
pub const SYS_madvise = 219;
|
||||
pub const SYS_getdents64 = 220;
|
||||
pub const SYS_fcntl64 = 221;
|
||||
pub const SYS_gettid = 224;
|
||||
pub const SYS_readahead = 225;
|
||||
pub const SYS_setxattr = 226;
|
||||
pub const SYS_lsetxattr = 227;
|
||||
pub const SYS_fsetxattr = 228;
|
||||
pub const SYS_getxattr = 229;
|
||||
pub const SYS_lgetxattr = 230;
|
||||
pub const SYS_fgetxattr = 231;
|
||||
pub const SYS_listxattr = 232;
|
||||
pub const SYS_llistxattr = 233;
|
||||
pub const SYS_flistxattr = 234;
|
||||
pub const SYS_removexattr = 235;
|
||||
pub const SYS_lremovexattr = 236;
|
||||
pub const SYS_fremovexattr = 237;
|
||||
pub const SYS_tkill = 238;
|
||||
pub const SYS_sendfile64 = 239;
|
||||
pub const SYS_futex = 240;
|
||||
pub const SYS_sched_setaffinity = 241;
|
||||
pub const SYS_sched_getaffinity = 242;
|
||||
pub const SYS_set_thread_area = 243;
|
||||
pub const SYS_get_thread_area = 244;
|
||||
pub const SYS_io_setup = 245;
|
||||
pub const SYS_io_destroy = 246;
|
||||
pub const SYS_io_getevents = 247;
|
||||
pub const SYS_io_submit = 248;
|
||||
pub const SYS_io_cancel = 249;
|
||||
pub const SYS_fadvise64 = 250;
|
||||
pub const SYS_exit_group = 252;
|
||||
pub const SYS_lookup_dcookie = 253;
|
||||
pub const SYS_epoll_create = 254;
|
||||
pub const SYS_epoll_ctl = 255;
|
||||
pub const SYS_epoll_wait = 256;
|
||||
pub const SYS_remap_file_pages = 257;
|
||||
pub const SYS_set_tid_address = 258;
|
||||
pub const SYS_timer_create = 259;
|
||||
pub const SYS_timer_settime = SYS_timer_create + 1;
|
||||
pub const SYS_timer_gettime = SYS_timer_create + 2;
|
||||
pub const SYS_timer_getoverrun = SYS_timer_create + 3;
|
||||
pub const SYS_timer_delete = SYS_timer_create + 4;
|
||||
pub const SYS_clock_settime = SYS_timer_create + 5;
|
||||
pub const SYS_clock_gettime = SYS_timer_create + 6;
|
||||
pub const SYS_clock_getres = SYS_timer_create + 7;
|
||||
pub const SYS_clock_nanosleep = SYS_timer_create + 8;
|
||||
pub const SYS_statfs64 = 268;
|
||||
pub const SYS_fstatfs64 = 269;
|
||||
pub const SYS_tgkill = 270;
|
||||
pub const SYS_utimes = 271;
|
||||
pub const SYS_fadvise64_64 = 272;
|
||||
pub const SYS_vserver = 273;
|
||||
pub const SYS_mbind = 274;
|
||||
pub const SYS_get_mempolicy = 275;
|
||||
pub const SYS_set_mempolicy = 276;
|
||||
pub const SYS_mq_open = 277;
|
||||
pub const SYS_mq_unlink = SYS_mq_open + 1;
|
||||
pub const SYS_mq_timedsend = SYS_mq_open + 2;
|
||||
pub const SYS_mq_timedreceive = SYS_mq_open + 3;
|
||||
pub const SYS_mq_notify = SYS_mq_open + 4;
|
||||
pub const SYS_mq_getsetattr = SYS_mq_open + 5;
|
||||
pub const SYS_kexec_load = 283;
|
||||
pub const SYS_waitid = 284;
|
||||
pub const SYS_add_key = 286;
|
||||
pub const SYS_request_key = 287;
|
||||
pub const SYS_keyctl = 288;
|
||||
pub const SYS_ioprio_set = 289;
|
||||
pub const SYS_ioprio_get = 290;
|
||||
pub const SYS_inotify_init = 291;
|
||||
pub const SYS_inotify_add_watch = 292;
|
||||
pub const SYS_inotify_rm_watch = 293;
|
||||
pub const SYS_migrate_pages = 294;
|
||||
pub const SYS_openat = 295;
|
||||
pub const SYS_mkdirat = 296;
|
||||
pub const SYS_mknodat = 297;
|
||||
pub const SYS_fchownat = 298;
|
||||
pub const SYS_futimesat = 299;
|
||||
pub const SYS_fstatat64 = 300;
|
||||
pub const SYS_unlinkat = 301;
|
||||
pub const SYS_renameat = 302;
|
||||
pub const SYS_linkat = 303;
|
||||
pub const SYS_symlinkat = 304;
|
||||
pub const SYS_readlinkat = 305;
|
||||
pub const SYS_fchmodat = 306;
|
||||
pub const SYS_faccessat = 307;
|
||||
pub const SYS_pselect6 = 308;
|
||||
pub const SYS_ppoll = 309;
|
||||
pub const SYS_unshare = 310;
|
||||
pub const SYS_set_robust_list = 311;
|
||||
pub const SYS_get_robust_list = 312;
|
||||
pub const SYS_splice = 313;
|
||||
pub const SYS_sync_file_range = 314;
|
||||
pub const SYS_tee = 315;
|
||||
pub const SYS_vmsplice = 316;
|
||||
pub const SYS_move_pages = 317;
|
||||
pub const SYS_getcpu = 318;
|
||||
pub const SYS_epoll_pwait = 319;
|
||||
pub const SYS_utimensat = 320;
|
||||
pub const SYS_signalfd = 321;
|
||||
pub const SYS_timerfd_create = 322;
|
||||
pub const SYS_eventfd = 323;
|
||||
pub const SYS_fallocate = 324;
|
||||
pub const SYS_timerfd_settime = 325;
|
||||
pub const SYS_timerfd_gettime = 326;
|
||||
pub const SYS_signalfd4 = 327;
|
||||
pub const SYS_eventfd2 = 328;
|
||||
pub const SYS_epoll_create1 = 329;
|
||||
pub const SYS_dup3 = 330;
|
||||
pub const SYS_pipe2 = 331;
|
||||
pub const SYS_inotify_init1 = 332;
|
||||
pub const SYS_preadv = 333;
|
||||
pub const SYS_pwritev = 334;
|
||||
pub const SYS_rt_tgsigqueueinfo = 335;
|
||||
pub const SYS_perf_event_open = 336;
|
||||
pub const SYS_recvmmsg = 337;
|
||||
pub const SYS_fanotify_init = 338;
|
||||
pub const SYS_fanotify_mark = 339;
|
||||
pub const SYS_prlimit64 = 340;
|
||||
pub const SYS_name_to_handle_at = 341;
|
||||
pub const SYS_open_by_handle_at = 342;
|
||||
pub const SYS_clock_adjtime = 343;
|
||||
pub const SYS_syncfs = 344;
|
||||
pub const SYS_sendmmsg = 345;
|
||||
pub const SYS_setns = 346;
|
||||
pub const SYS_process_vm_readv = 347;
|
||||
pub const SYS_process_vm_writev = 348;
|
||||
pub const SYS_kcmp = 349;
|
||||
pub const SYS_finit_module = 350;
|
||||
pub const SYS_sched_setattr = 351;
|
||||
pub const SYS_sched_getattr = 352;
|
||||
pub const SYS_renameat2 = 353;
|
||||
pub const SYS_seccomp = 354;
|
||||
pub const SYS_getrandom = 355;
|
||||
pub const SYS_memfd_create = 356;
|
||||
pub const SYS_bpf = 357;
|
||||
pub const SYS_execveat = 358;
|
||||
pub const SYS_socket = 359;
|
||||
pub const SYS_socketpair = 360;
|
||||
pub const SYS_bind = 361;
|
||||
pub const SYS_connect = 362;
|
||||
pub const SYS_listen = 363;
|
||||
pub const SYS_accept4 = 364;
|
||||
pub const SYS_getsockopt = 365;
|
||||
pub const SYS_setsockopt = 366;
|
||||
pub const SYS_getsockname = 367;
|
||||
pub const SYS_getpeername = 368;
|
||||
pub const SYS_sendto = 369;
|
||||
pub const SYS_sendmsg = 370;
|
||||
pub const SYS_recvfrom = 371;
|
||||
pub const SYS_recvmsg = 372;
|
||||
pub const SYS_shutdown = 373;
|
||||
pub const SYS_userfaultfd = 374;
|
||||
pub const SYS_membarrier = 375;
|
||||
pub const SYS_mlock2 = 376;
|
||||
pub const SYS_copy_file_range = 377;
|
||||
pub const SYS_preadv2 = 378;
|
||||
pub const SYS_pwritev2 = 379;
|
||||
pub const SYS_pkey_mprotect = 380;
|
||||
pub const SYS_pkey_alloc = 381;
|
||||
pub const SYS_pkey_free = 382;
|
||||
pub const SYS_statx = 383;
|
||||
pub const SYS_arch_prctl = 384;
|
||||
pub const SYS_io_pgetevents = 385;
|
||||
pub const SYS_rseq = 386;
|
||||
pub const SYS_semget = 393;
|
||||
pub const SYS_semctl = 394;
|
||||
pub const SYS_shmget = 395;
|
||||
pub const SYS_shmctl = 396;
|
||||
pub const SYS_shmat = 397;
|
||||
pub const SYS_shmdt = 398;
|
||||
pub const SYS_msgget = 399;
|
||||
pub const SYS_msgsnd = 400;
|
||||
pub const SYS_msgrcv = 401;
|
||||
pub const SYS_msgctl = 402;
|
||||
pub const SYS_clock_gettime64 = 403;
|
||||
pub const SYS_clock_settime64 = 404;
|
||||
pub const SYS_clock_adjtime64 = 405;
|
||||
pub const SYS_clock_getres_time64 = 406;
|
||||
pub const SYS_clock_nanosleep_time64 = 407;
|
||||
pub const SYS_timer_gettime64 = 408;
|
||||
pub const SYS_timer_settime64 = 409;
|
||||
pub const SYS_timerfd_gettime64 = 410;
|
||||
pub const SYS_timerfd_settime64 = 411;
|
||||
pub const SYS_utimensat_time64 = 412;
|
||||
pub const SYS_pselect6_time64 = 413;
|
||||
pub const SYS_ppoll_time64 = 414;
|
||||
pub const SYS_io_pgetevents_time64 = 416;
|
||||
pub const SYS_recvmmsg_time64 = 417;
|
||||
pub const SYS_mq_timedsend_time64 = 418;
|
||||
pub const SYS_mq_timedreceive_time64 = 419;
|
||||
pub const SYS_semtimedop_time64 = 420;
|
||||
pub const SYS_rt_sigtimedwait_time64 = 421;
|
||||
pub const SYS_futex_time64 = 422;
|
||||
pub const SYS_sched_rr_get_interval_time64 = 423;
|
||||
pub const SYS_pidfd_send_signal = 424;
|
||||
pub const SYS_io_uring_setup = 425;
|
||||
pub const SYS_io_uring_enter = 426;
|
||||
pub const SYS_io_uring_register = 427;
|
||||
pub const SYS_open_tree = 428;
|
||||
pub const SYS_move_mount = 429;
|
||||
pub const SYS_fsopen = 430;
|
||||
pub const SYS_fsconfig = 431;
|
||||
pub const SYS_fsmount = 432;
|
||||
pub const SYS_fspick = 433;
|
||||
|
||||
pub const O_CREAT = 0o100;
|
||||
pub const O_EXCL = 0o200;
|
||||
pub const O_NOCTTY = 0o400;
|
||||
pub const O_TRUNC = 0o1000;
|
||||
pub const O_APPEND = 0o2000;
|
||||
pub const O_NONBLOCK = 0o4000;
|
||||
pub const O_DSYNC = 0o10000;
|
||||
pub const O_SYNC = 0o4010000;
|
||||
pub const O_RSYNC = 0o4010000;
|
||||
pub const O_DIRECTORY = 0o200000;
|
||||
pub const O_NOFOLLOW = 0o400000;
|
||||
pub const O_CLOEXEC = 0o2000000;
|
||||
|
||||
pub const O_ASYNC = 0o20000;
|
||||
pub const O_DIRECT = 0o40000;
|
||||
pub const O_LARGEFILE = 0o100000;
|
||||
pub const O_NOATIME = 0o1000000;
|
||||
pub const O_PATH = 0o10000000;
|
||||
pub const O_TMPFILE = 0o20200000;
|
||||
pub const O_NDELAY = O_NONBLOCK;
|
||||
|
||||
pub const F_DUPFD = 0;
|
||||
pub const F_GETFD = 1;
|
||||
pub const F_SETFD = 2;
|
||||
pub const F_GETFL = 3;
|
||||
pub const F_SETFL = 4;
|
||||
|
||||
pub const F_SETOWN = 8;
|
||||
pub const F_GETOWN = 9;
|
||||
pub const F_SETSIG = 10;
|
||||
pub const F_GETSIG = 11;
|
||||
|
||||
pub const F_GETLK = 12;
|
||||
pub const F_SETLK = 13;
|
||||
pub const F_SETLKW = 14;
|
||||
|
||||
pub const F_SETOWN_EX = 15;
|
||||
pub const F_GETOWN_EX = 16;
|
||||
|
||||
pub const F_GETOWNER_UIDS = 17;
|
||||
|
||||
pub const MAP_NORESERVE = 0x4000;
|
||||
pub const MAP_GROWSDOWN = 0x0100;
|
||||
pub const MAP_DENYWRITE = 0x0800;
|
||||
pub const MAP_EXECUTABLE = 0x1000;
|
||||
pub const MAP_LOCKED = 0x2000;
|
||||
pub const MAP_32BIT = 0x40;
|
||||
|
||||
pub const MMAP2_UNIT = 4096;
|
||||
|
||||
pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
|
||||
pub const VDSO_CGT_VER = "LINUX_2.6";
|
||||
|
||||
pub const msghdr = extern struct {
|
||||
msg_name: ?*sockaddr,
|
||||
msg_namelen: socklen_t,
|
||||
msg_iov: [*]iovec,
|
||||
msg_iovlen: i32,
|
||||
msg_control: ?*c_void,
|
||||
msg_controllen: socklen_t,
|
||||
msg_flags: i32,
|
||||
};
|
||||
|
||||
pub const msghdr_const = extern struct {
|
||||
msg_name: ?*const sockaddr,
|
||||
msg_namelen: socklen_t,
|
||||
msg_iov: [*]iovec_const,
|
||||
msg_iovlen: i32,
|
||||
msg_control: ?*c_void,
|
||||
msg_controllen: socklen_t,
|
||||
msg_flags: i32,
|
||||
};
|
||||
|
||||
pub const blksize_t = i32;
|
||||
pub const nlink_t = u32;
|
||||
pub const time_t = isize;
|
||||
pub const mode_t = u32;
|
||||
pub const off_t = i64;
|
||||
pub const ino_t = u64;
|
||||
pub const dev_t = u64;
|
||||
pub const blkcnt_t = i64;
|
||||
|
||||
/// Renamed to Stat to not conflict with the stat function.
|
||||
/// atime, mtime, and ctime have functions to return `timespec`,
|
||||
/// because although this is a POSIX API, the layout and names of
|
||||
/// the structs are inconsistent across operating systems, and
|
||||
/// in C, macros are used to hide the differences. Here we use
|
||||
/// methods to accomplish this.
|
||||
pub const Stat = extern struct {
|
||||
dev: dev_t,
|
||||
__dev_padding: u32,
|
||||
__ino_truncated: u32,
|
||||
mode: mode_t,
|
||||
nlink: nlink_t,
|
||||
uid: uid_t,
|
||||
gid: gid_t,
|
||||
rdev: dev_t,
|
||||
__rdev_padding: u32,
|
||||
size: off_t,
|
||||
blksize: blksize_t,
|
||||
blocks: blkcnt_t,
|
||||
atim: timespec,
|
||||
mtim: timespec,
|
||||
ctim: timespec,
|
||||
ino: ino_t,
|
||||
|
||||
pub fn atime(self: Stat) timespec {
|
||||
return self.atim;
|
||||
}
|
||||
|
||||
pub fn mtime(self: Stat) timespec {
|
||||
return self.mtim;
|
||||
}
|
||||
|
||||
pub fn ctime(self: Stat) timespec {
|
||||
return self.ctim;
|
||||
}
|
||||
};
|
||||
|
||||
pub const timespec = extern struct {
|
||||
tv_sec: i32,
|
||||
tv_nsec: i32,
|
||||
};
|
||||
|
||||
pub const timeval = extern struct {
|
||||
tv_sec: i32,
|
||||
tv_usec: i32,
|
||||
};
|
||||
|
||||
pub const timezone = extern struct {
|
||||
tz_minuteswest: i32,
|
||||
tz_dsttime: i32,
|
||||
};
|
||||
|
||||
pub const mcontext_t = extern struct {
|
||||
gregs: [19]usize,
|
||||
fpregs: [*]u8,
|
||||
oldmask: usize,
|
||||
cr2: usize,
|
||||
};
|
||||
|
||||
pub const REG_GS = 0;
|
||||
pub const REG_FS = 1;
|
||||
pub const REG_ES = 2;
|
||||
pub const REG_DS = 3;
|
||||
pub const REG_EDI = 4;
|
||||
pub const REG_ESI = 5;
|
||||
pub const REG_EBP = 6;
|
||||
pub const REG_ESP = 7;
|
||||
pub const REG_EBX = 8;
|
||||
pub const REG_EDX = 9;
|
||||
pub const REG_ECX = 10;
|
||||
pub const REG_EAX = 11;
|
||||
pub const REG_TRAPNO = 12;
|
||||
pub const REG_ERR = 13;
|
||||
pub const REG_EIP = 14;
|
||||
pub const REG_CS = 15;
|
||||
pub const REG_EFL = 16;
|
||||
pub const REG_UESP = 17;
|
||||
pub const REG_SS = 18;
|
||||
|
||||
pub const ucontext_t = extern struct {
|
||||
flags: usize,
|
||||
link: *ucontext_t,
|
||||
stack: stack_t,
|
||||
mcontext: mcontext_t,
|
||||
sigmask: sigset_t,
|
||||
regspace: [64]u64,
|
||||
};
|
||||
|
||||
pub const Elf_Symndx = u32;
|
||||
|
||||
pub const user_desc = packed struct {
|
||||
entry_number: u32,
|
||||
base_addr: u32,
|
||||
limit: u32,
|
||||
seg_32bit: u1,
|
||||
contents: u2,
|
||||
read_exec_only: u1,
|
||||
limit_in_pages: u1,
|
||||
seg_not_present: u1,
|
||||
useable: u1,
|
||||
};
|
||||
|
||||
// socketcall() call numbers
|
||||
pub const SC_socket = 1;
|
||||
pub const SC_bind = 2;
|
||||
pub const SC_connect = 3;
|
||||
pub const SC_listen = 4;
|
||||
pub const SC_accept = 5;
|
||||
pub const SC_getsockname = 6;
|
||||
pub const SC_getpeername = 7;
|
||||
pub const SC_socketpair = 8;
|
||||
pub const SC_send = 9;
|
||||
pub const SC_recv = 10;
|
||||
pub const SC_sendto = 11;
|
||||
pub const SC_recvfrom = 12;
|
||||
pub const SC_shutdown = 13;
|
||||
pub const SC_setsockopt = 14;
|
||||
pub const SC_getsockopt = 15;
|
||||
pub const SC_sendmsg = 16;
|
||||
pub const SC_recvmsg = 17;
|
||||
pub const SC_accept4 = 18;
|
||||
pub const SC_recvmmsg = 19;
|
||||
pub const SC_sendmmsg = 20;
|
@ -454,7 +454,6 @@ pub const SO_PEERSEC = 30;
|
||||
pub const SO_SNDBUFFORCE = 31;
|
||||
pub const SO_RCVBUFFORCE = 33;
|
||||
|
||||
pub const VDSO_USEFUL = true;
|
||||
pub const VDSO_CGT_SYM = "__kernel_clock_gettime";
|
||||
pub const VDSO_CGT_VER = "LINUX_2.6.39";
|
||||
|
||||
|
@ -420,7 +420,6 @@ pub const MAP_LOCKED = 0x2000;
|
||||
/// don't check for reservations
|
||||
pub const MAP_NORESERVE = 0x4000;
|
||||
|
||||
pub const VDSO_USEFUL = true;
|
||||
pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
|
||||
pub const VDSO_CGT_VER = "LINUX_2.6";
|
||||
pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
|
||||
|
@ -14,6 +14,7 @@ const vdso = @import("linux/vdso.zig");
|
||||
const dl = @import("../dynamic_library.zig");
|
||||
|
||||
pub usingnamespace switch (builtin.arch) {
|
||||
.i386 => @import("linux/i386.zig"),
|
||||
.x86_64 => @import("linux/x86_64.zig"),
|
||||
.aarch64 => @import("linux/arm64.zig"),
|
||||
.arm => @import("linux/arm-eabi.zig"),
|
||||
@ -743,26 +744,44 @@ pub fn sigismember(set: *const sigset_t, sig: u6) bool {
|
||||
}
|
||||
|
||||
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_getsockname, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) });
|
||||
}
|
||||
return syscall3(SYS_getsockname, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len));
|
||||
}
|
||||
|
||||
pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_getpeername, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len) });
|
||||
}
|
||||
return syscall3(SYS_getpeername, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len));
|
||||
}
|
||||
|
||||
pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_socket, &[3]usize{ domain, socket_type, protocol });
|
||||
}
|
||||
return syscall3(SYS_socket, domain, socket_type, protocol);
|
||||
}
|
||||
|
||||
pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_setsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen) });
|
||||
}
|
||||
return syscall5(SYS_setsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen));
|
||||
}
|
||||
|
||||
pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_getsockopt, &[5]usize{ @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen) });
|
||||
}
|
||||
return syscall5(SYS_getsockopt, @bitCast(usize, @as(isize, fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
|
||||
}
|
||||
|
||||
pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_sendmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
|
||||
}
|
||||
return syscall3(SYS_sendmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags);
|
||||
}
|
||||
|
||||
@ -807,42 +826,72 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
|
||||
}
|
||||
|
||||
pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_connect, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len });
|
||||
}
|
||||
return syscall3(SYS_connect, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), len);
|
||||
}
|
||||
|
||||
pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_recvmsg, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags });
|
||||
}
|
||||
return syscall3(SYS_recvmsg, @bitCast(usize, @as(isize, fd)), @ptrToInt(msg), flags);
|
||||
}
|
||||
|
||||
pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_recvfrom, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen) });
|
||||
}
|
||||
return syscall6(SYS_recvfrom, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
|
||||
}
|
||||
|
||||
pub fn shutdown(fd: i32, how: i32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_shutdown, &[2]usize{ @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how)) });
|
||||
}
|
||||
return syscall2(SYS_shutdown, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, how)));
|
||||
}
|
||||
|
||||
pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_bind, &[3]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len) });
|
||||
}
|
||||
return syscall3(SYS_bind, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @intCast(usize, len));
|
||||
}
|
||||
|
||||
pub fn listen(fd: i32, backlog: u32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_listen, &[2]usize{ @bitCast(usize, @as(isize, fd)), backlog });
|
||||
}
|
||||
return syscall2(SYS_listen, @bitCast(usize, @as(isize, fd)), backlog);
|
||||
}
|
||||
|
||||
pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_sendto, &[6]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen) });
|
||||
}
|
||||
return syscall6(SYS_sendto, @bitCast(usize, @as(isize, fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen));
|
||||
}
|
||||
|
||||
pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_socketpair, &[4]usize{ @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]) });
|
||||
}
|
||||
return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0]));
|
||||
}
|
||||
|
||||
pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_accept, &[4]usize{ fd, addr, len, 0 });
|
||||
}
|
||||
return accept4(fd, addr, len, 0);
|
||||
}
|
||||
|
||||
pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize {
|
||||
if (builtin.arch == .i386) {
|
||||
return socketcall(SC_accept4, &[4]usize{ @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags });
|
||||
}
|
||||
return syscall4(SYS_accept4, @bitCast(usize, @as(isize, fd)), @ptrToInt(addr), @ptrToInt(len), flags);
|
||||
}
|
||||
|
||||
|
119
lib/std/os/linux/i386.zig
Normal file
119
lib/std/os/linux/i386.zig
Normal file
@ -0,0 +1,119 @@
|
||||
usingnamespace @import("../bits.zig");
|
||||
|
||||
pub fn syscall0(number: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn syscall1(number: usize, arg1: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3),
|
||||
[arg4] "{esi}" (arg4)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3),
|
||||
[arg4] "{esi}" (arg4),
|
||||
[arg5] "{edi}" (arg5)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn syscall6(
|
||||
number: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize,
|
||||
arg4: usize,
|
||||
arg5: usize,
|
||||
arg6: usize,
|
||||
) usize {
|
||||
return asm volatile (
|
||||
\\ push %%ebp
|
||||
\\ mov %[arg6], %%ebp
|
||||
\\ int $0x80
|
||||
\\ pop %%ebp
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (number),
|
||||
[arg1] "{ebx}" (arg1),
|
||||
[arg2] "{ecx}" (arg2),
|
||||
[arg3] "{edx}" (arg3),
|
||||
[arg4] "{esi}" (arg4),
|
||||
[arg5] "{edi}" (arg5),
|
||||
[arg6] "rm" (arg6)
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub fn socketcall(call: usize, args: [*]usize) usize {
|
||||
return asm volatile ("int $0x80"
|
||||
: [ret] "={eax}" (-> usize)
|
||||
: [number] "{eax}" (@as(usize, SYS_socketcall)),
|
||||
[arg1] "{ebx}" (call),
|
||||
[arg2] "{ecx}" (@ptrToInt(args))
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
/// This matches the libc clone function.
|
||||
pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
|
||||
|
||||
pub nakedcc fn restore() void {
|
||||
return asm volatile ("int $0x80"
|
||||
:
|
||||
: [number] "{eax}" (@as(usize, SYS_sigreturn))
|
||||
: "memory"
|
||||
);
|
||||
}
|
||||
|
||||
pub nakedcc fn restore_rt() void {
|
||||
return asm volatile ("int $0x80"
|
||||
:
|
||||
: [number] "{eax}" (@as(usize, SYS_rt_sigreturn))
|
||||
: "memory"
|
||||
);
|
||||
}
|
@ -4,6 +4,7 @@ const linux = std.os.linux;
|
||||
const mem = std.mem;
|
||||
const elf = std.elf;
|
||||
const expect = std.testing.expect;
|
||||
const fs = std.fs;
|
||||
|
||||
test "getpid" {
|
||||
expect(linux.getpid() != 0);
|
||||
@ -45,14 +46,12 @@ test "timer" {
|
||||
err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);
|
||||
}
|
||||
|
||||
const File = std.fs.File;
|
||||
|
||||
test "statx" {
|
||||
const tmp_file_name = "just_a_temporary_file.txt";
|
||||
var file = try File.openWrite(tmp_file_name);
|
||||
var file = try fs.cwd().createFile(tmp_file_name, .{});
|
||||
defer {
|
||||
file.close();
|
||||
std.fs.deleteFile(tmp_file_name) catch {};
|
||||
fs.cwd().deleteFile(tmp_file_name) catch {};
|
||||
}
|
||||
|
||||
var statx_buf: linux.Statx = undefined;
|
||||
|
@ -109,12 +109,38 @@ const TLSImage = struct {
|
||||
tcb_offset: usize,
|
||||
dtv_offset: usize,
|
||||
data_offset: usize,
|
||||
// Only used on the i386 architecture
|
||||
gdt_entry_number: usize,
|
||||
};
|
||||
|
||||
pub var tls_image: ?TLSImage = null;
|
||||
|
||||
pub fn setThreadPointer(addr: usize) void {
|
||||
switch (builtin.arch) {
|
||||
.i386 => {
|
||||
var user_desc = std.os.linux.user_desc{
|
||||
.entry_number = tls_image.?.gdt_entry_number,
|
||||
.base_addr = addr,
|
||||
.limit = 0xfffff,
|
||||
.seg_32bit = 1,
|
||||
.contents = 0, // Data
|
||||
.read_exec_only = 0,
|
||||
.limit_in_pages = 1,
|
||||
.seg_not_present = 0,
|
||||
.useable = 1,
|
||||
};
|
||||
const rc = std.os.linux.syscall1(std.os.linux.SYS_set_thread_area, @ptrToInt(&user_desc));
|
||||
assert(rc == 0);
|
||||
|
||||
const gdt_entry_number = user_desc.entry_number;
|
||||
// We have to keep track of our slot as it's also needed for clone()
|
||||
tls_image.?.gdt_entry_number = gdt_entry_number;
|
||||
// Update the %gs selector
|
||||
asm volatile ("movl %[gs_val], %%gs"
|
||||
:
|
||||
: [gs_val] "r" (gdt_entry_number << 3 | 3)
|
||||
);
|
||||
},
|
||||
.x86_64 => {
|
||||
const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, std.os.linux.ARCH_SET_FS, addr);
|
||||
assert(rc == 0);
|
||||
@ -238,6 +264,7 @@ pub fn initTLS() ?*elf.Phdr {
|
||||
.tcb_offset = tcb_offset,
|
||||
.dtv_offset = dtv_offset,
|
||||
.data_offset = data_offset,
|
||||
.gdt_entry_number = @bitCast(usize, @as(isize, -1)),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ test "makePath, put some files in it, deleteTree" {
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
|
||||
try fs.deleteTree("os_test_tmp");
|
||||
if (fs.Dir.cwd().openDirTraverse("os_test_tmp")) |dir| {
|
||||
if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| {
|
||||
@panic("expected error");
|
||||
} else |err| {
|
||||
expect(err == error.FileNotFound);
|
||||
@ -111,7 +111,7 @@ test "AtomicFile" {
|
||||
const content = try io.readFileAlloc(allocator, test_out_file);
|
||||
expect(mem.eql(u8, content, test_content));
|
||||
|
||||
try fs.deleteFile(test_out_file);
|
||||
try fs.cwd().deleteFile(test_out_file);
|
||||
}
|
||||
|
||||
test "thread local storage" {
|
||||
|
@ -12,8 +12,8 @@ comptime {
|
||||
assert(@alignOf(u16) == 2);
|
||||
assert(@alignOf(i32) == 4);
|
||||
assert(@alignOf(u32) == 4);
|
||||
assert(@alignOf(i64) == 8);
|
||||
assert(@alignOf(u64) == 8);
|
||||
// assert(@alignOf(i64) == 8);
|
||||
// assert(@alignOf(u64) == 8);
|
||||
}
|
||||
|
||||
pub const iovec_t = iovec;
|
||||
|
@ -1,180 +0,0 @@
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const time = std.time;
|
||||
const testing = std.testing;
|
||||
const assert = std.debug.assert;
|
||||
const SpinLock = std.SpinLock;
|
||||
const linux = std.os.linux;
|
||||
const windows = std.os.windows;
|
||||
|
||||
pub const ThreadParker = switch (builtin.os) {
|
||||
.linux => if (builtin.link_libc) PosixParker else LinuxParker,
|
||||
.windows => WindowsParker,
|
||||
else => if (builtin.link_libc) PosixParker else SpinParker,
|
||||
};
|
||||
|
||||
const SpinParker = struct {
|
||||
pub fn init() SpinParker {
|
||||
return SpinParker{};
|
||||
}
|
||||
pub fn deinit(self: *SpinParker) void {}
|
||||
|
||||
pub fn unpark(self: *SpinParker, ptr: *const u32) void {}
|
||||
|
||||
pub fn park(self: *SpinParker, ptr: *const u32, expected: u32) void {
|
||||
var backoff = SpinLock.Backoff.init();
|
||||
while (@atomicLoad(u32, ptr, .Acquire) == expected)
|
||||
backoff.yield();
|
||||
}
|
||||
};
|
||||
|
||||
const LinuxParker = struct {
|
||||
pub fn init() LinuxParker {
|
||||
return LinuxParker{};
|
||||
}
|
||||
pub fn deinit(self: *LinuxParker) void {}
|
||||
|
||||
pub fn unpark(self: *LinuxParker, ptr: *const u32) void {
|
||||
const rc = linux.futex_wake(@ptrCast(*const i32, ptr), linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
|
||||
assert(linux.getErrno(rc) == 0);
|
||||
}
|
||||
|
||||
pub fn park(self: *LinuxParker, ptr: *const u32, expected: u32) void {
|
||||
const value = @intCast(i32, expected);
|
||||
while (@atomicLoad(u32, ptr, .Acquire) == expected) {
|
||||
const rc = linux.futex_wait(@ptrCast(*const i32, ptr), linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, value, null);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0, linux.EAGAIN => return,
|
||||
linux.EINTR => continue,
|
||||
linux.EINVAL => unreachable,
|
||||
else => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const WindowsParker = struct {
|
||||
waiters: u32,
|
||||
|
||||
pub fn init() WindowsParker {
|
||||
return WindowsParker{ .waiters = 0 };
|
||||
}
|
||||
pub fn deinit(self: *WindowsParker) void {}
|
||||
|
||||
pub fn unpark(self: *WindowsParker, ptr: *const u32) void {
|
||||
const key = @ptrCast(*const c_void, ptr);
|
||||
const handle = getEventHandle() orelse return;
|
||||
|
||||
var waiting = @atomicLoad(u32, &self.waiters, .Monotonic);
|
||||
while (waiting != 0) {
|
||||
waiting = @cmpxchgWeak(u32, &self.waiters, waiting, waiting - 1, .Acquire, .Monotonic) orelse {
|
||||
const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
|
||||
assert(rc == 0);
|
||||
return;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn park(self: *WindowsParker, ptr: *const u32, expected: u32) void {
|
||||
var spin = SpinLock.Backoff.init();
|
||||
const ev_handle = getEventHandle();
|
||||
const key = @ptrCast(*const c_void, ptr);
|
||||
|
||||
while (@atomicLoad(u32, ptr, .Monotonic) == expected) {
|
||||
if (ev_handle) |handle| {
|
||||
_ = @atomicRmw(u32, &self.waiters, .Add, 1, .Release);
|
||||
const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, null);
|
||||
assert(rc == 0);
|
||||
} else {
|
||||
spin.yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var event_handle = std.lazyInit(windows.HANDLE);
|
||||
|
||||
fn getEventHandle() ?windows.HANDLE {
|
||||
if (event_handle.get()) |handle_ptr|
|
||||
return handle_ptr.*;
|
||||
defer event_handle.resolve();
|
||||
|
||||
const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE;
|
||||
if (windows.ntdll.NtCreateKeyedEvent(&event_handle.data, access_mask, null, 0) != 0)
|
||||
return null;
|
||||
return event_handle.data;
|
||||
}
|
||||
};
|
||||
|
||||
const PosixParker = struct {
|
||||
cond: c.pthread_cond_t,
|
||||
mutex: c.pthread_mutex_t,
|
||||
|
||||
const c = std.c;
|
||||
|
||||
pub fn init() PosixParker {
|
||||
return PosixParker{
|
||||
.cond = c.PTHREAD_COND_INITIALIZER,
|
||||
.mutex = c.PTHREAD_MUTEX_INITIALIZER,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PosixParker) void {
|
||||
// On dragonfly, the destroy functions return EINVAL if they were initialized statically.
|
||||
const retm = c.pthread_mutex_destroy(&self.mutex);
|
||||
assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0));
|
||||
const retc = c.pthread_cond_destroy(&self.cond);
|
||||
assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0));
|
||||
}
|
||||
|
||||
pub fn unpark(self: *PosixParker, ptr: *const u32) void {
|
||||
assert(c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
assert(c.pthread_cond_signal(&self.cond) == 0);
|
||||
}
|
||||
|
||||
pub fn park(self: *PosixParker, ptr: *const u32, expected: u32) void {
|
||||
assert(c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
while (@atomicLoad(u32, ptr, .Acquire) == expected)
|
||||
assert(c.pthread_cond_wait(&self.cond, &self.mutex) == 0);
|
||||
}
|
||||
};
|
||||
|
||||
test "std.ThreadParker" {
|
||||
if (builtin.single_threaded)
|
||||
return error.SkipZigTest;
|
||||
|
||||
const Context = struct {
|
||||
parker: ThreadParker,
|
||||
data: u32,
|
||||
|
||||
fn receiver(self: *@This()) void {
|
||||
self.parker.park(&self.data, 0); // receives 1
|
||||
assert(@atomicRmw(u32, &self.data, .Xchg, 2, .SeqCst) == 1); // sends 2
|
||||
self.parker.unpark(&self.data); // wakes up waiters on 2
|
||||
self.parker.park(&self.data, 2); // receives 3
|
||||
assert(@atomicRmw(u32, &self.data, .Xchg, 4, .SeqCst) == 3); // sends 4
|
||||
self.parker.unpark(&self.data); // wakes up waiters on 4
|
||||
}
|
||||
|
||||
fn sender(self: *@This()) void {
|
||||
assert(@atomicRmw(u32, &self.data, .Xchg, 1, .SeqCst) == 0); // sends 1
|
||||
self.parker.unpark(&self.data); // wakes up waiters on 1
|
||||
self.parker.park(&self.data, 1); // receives 2
|
||||
assert(@atomicRmw(u32, &self.data, .Xchg, 3, .SeqCst) == 2); // sends 3
|
||||
self.parker.unpark(&self.data); // wakes up waiters on 3
|
||||
self.parker.park(&self.data, 3); // receives 4
|
||||
}
|
||||
};
|
||||
|
||||
var context = Context{
|
||||
.parker = ThreadParker.init(),
|
||||
.data = 0,
|
||||
};
|
||||
defer context.parker.deinit();
|
||||
|
||||
var receiver = try std.Thread.spawn(&context, Context.receiver);
|
||||
defer receiver.wait();
|
||||
|
||||
context.sender();
|
||||
}
|
@ -6,6 +6,7 @@ const mem = std.mem;
|
||||
const os = std.os;
|
||||
const warn = std.debug.warn;
|
||||
const coff = std.coff;
|
||||
const fs = std.fs;
|
||||
const File = std.fs.File;
|
||||
|
||||
const ArrayList = std.ArrayList;
|
||||
@ -469,7 +470,7 @@ pub const Pdb = struct {
|
||||
msf: Msf,
|
||||
|
||||
pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []u8) !void {
|
||||
self.in_file = try File.openRead(file_name);
|
||||
self.in_file = try fs.cwd().openFile(file_name, .{});
|
||||
self.allocator = coff_ptr.allocator;
|
||||
self.coff = coff_ptr;
|
||||
|
||||
|
433
lib/std/reset_event.zig
Normal file
433
lib/std/reset_event.zig
Normal file
@ -0,0 +1,433 @@
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const testing = std.testing;
|
||||
const assert = std.debug.assert;
|
||||
const Backoff = std.SpinLock.Backoff;
|
||||
const c = std.c;
|
||||
const os = std.os;
|
||||
const time = std.time;
|
||||
const linux = os.linux;
|
||||
const windows = os.windows;
|
||||
|
||||
/// A resource object which supports blocking until signaled.
|
||||
/// Once finished, the `deinit()` method should be called for correctness.
|
||||
pub const ResetEvent = struct {
|
||||
os_event: OsEvent,
|
||||
|
||||
pub fn init() ResetEvent {
|
||||
return ResetEvent{ .os_event = OsEvent.init() };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *ResetEvent) void {
|
||||
self.os_event.deinit();
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
/// Returns whether or not the event is currenetly set
|
||||
pub fn isSet(self: *ResetEvent) bool {
|
||||
return self.os_event.isSet();
|
||||
}
|
||||
|
||||
/// Sets the event if not already set and
|
||||
/// wakes up AT LEAST one thread waiting the event.
|
||||
/// Returns whether or not a thread was woken up.
|
||||
pub fn set(self: *ResetEvent, auto_reset: bool) bool {
|
||||
return self.os_event.set(auto_reset);
|
||||
}
|
||||
|
||||
/// Resets the event to its original, unset state.
|
||||
/// Returns whether or not the event was currently set before un-setting.
|
||||
pub fn reset(self: *ResetEvent) bool {
|
||||
return self.os_event.reset();
|
||||
}
|
||||
|
||||
const WaitError = error{
|
||||
/// The thread blocked longer than the maximum time specified.
|
||||
TimedOut,
|
||||
};
|
||||
|
||||
/// Wait for the event to be set by blocking the current thread.
|
||||
/// Optionally provided timeout in nanoseconds which throws an
|
||||
/// `error.TimedOut` if the thread blocked AT LEAST longer than specified.
|
||||
/// Returns whether or not the thread blocked from the event being unset at the time of calling.
|
||||
pub fn wait(self: *ResetEvent, timeout_ns: ?u64) WaitError!bool {
|
||||
return self.os_event.wait(timeout_ns);
|
||||
}
|
||||
};
|
||||
|
||||
const OsEvent = if (builtin.single_threaded) DebugEvent else switch (builtin.os) {
|
||||
.windows => WindowsEvent,
|
||||
.linux => if (builtin.link_libc) PosixEvent else LinuxEvent,
|
||||
else => if (builtin.link_libc) PosixEvent else SpinEvent,
|
||||
};
|
||||
|
||||
const DebugEvent = struct {
|
||||
is_set: @typeOf(set_init),
|
||||
|
||||
const set_init = if (std.debug.runtime_safety) false else {};
|
||||
|
||||
pub fn init() DebugEvent {
|
||||
return DebugEvent{ .is_set = set_init };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *DebugEvent) void {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn isSet(self: *DebugEvent) bool {
|
||||
if (!std.debug.runtime_safety)
|
||||
return true;
|
||||
return self.is_set;
|
||||
}
|
||||
|
||||
pub fn set(self: *DebugEvent, auto_reset: bool) bool {
|
||||
if (std.debug.runtime_safety)
|
||||
self.is_set = !auto_reset;
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn reset(self: *DebugEvent) bool {
|
||||
if (!std.debug.runtime_safety)
|
||||
return false;
|
||||
const was_set = self.is_set;
|
||||
self.is_set = false;
|
||||
return was_set;
|
||||
}
|
||||
|
||||
pub fn wait(self: *DebugEvent, timeout: ?u64) ResetEvent.WaitError!bool {
|
||||
if (std.debug.runtime_safety and !self.is_set)
|
||||
@panic("deadlock detected");
|
||||
return ResetEvent.WaitError.TimedOut;
|
||||
}
|
||||
};
|
||||
|
||||
fn AtomicEvent(comptime FutexImpl: type) type {
|
||||
return struct {
|
||||
state: u32,
|
||||
|
||||
const IS_SET: u32 = 1 << 0;
|
||||
const WAIT_MASK = ~IS_SET;
|
||||
|
||||
pub const Self = @This();
|
||||
pub const Futex = FutexImpl;
|
||||
|
||||
pub fn init() Self {
|
||||
return Self{ .state = 0 };
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn isSet(self: *const Self) bool {
|
||||
const state = @atomicLoad(u32, &self.state, .Acquire);
|
||||
return (state & IS_SET) != 0;
|
||||
}
|
||||
|
||||
pub fn reset(self: *Self) bool {
|
||||
const old_state = @atomicRmw(u32, &self.state, .Xchg, 0, .Monotonic);
|
||||
return (old_state & IS_SET) != 0;
|
||||
}
|
||||
|
||||
pub fn set(self: *Self, auto_reset: bool) bool {
|
||||
const new_state = if (auto_reset) 0 else IS_SET;
|
||||
const old_state = @atomicRmw(u32, &self.state, .Xchg, new_state, .Release);
|
||||
if ((old_state & WAIT_MASK) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Futex.wake(&self.state);
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn wait(self: *Self, timeout: ?u64) ResetEvent.WaitError!bool {
|
||||
var dummy_value: u32 = undefined;
|
||||
const wait_token = @truncate(u32, @ptrToInt(&dummy_value));
|
||||
|
||||
var state = @atomicLoad(u32, &self.state, .Monotonic);
|
||||
while (true) {
|
||||
if ((state & IS_SET) != 0)
|
||||
return false;
|
||||
state = @cmpxchgWeak(u32, &self.state, state, wait_token, .Acquire, .Monotonic) orelse break;
|
||||
}
|
||||
|
||||
try Futex.wait(&self.state, wait_token, timeout);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const SpinEvent = AtomicEvent(struct {
|
||||
fn wake(ptr: *const u32) void {}
|
||||
|
||||
fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void {
|
||||
// TODO: handle platforms where time.Timer.start() fails
|
||||
var spin = Backoff.init();
|
||||
var timer = if (timeout == null) null else time.Timer.start() catch unreachable;
|
||||
while (@atomicLoad(u32, ptr, .Acquire) == expected) {
|
||||
spin.yield();
|
||||
if (timeout) |timeout_ns| {
|
||||
if (timer.?.read() > timeout_ns)
|
||||
return ResetEvent.WaitError.TimedOut;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const LinuxEvent = AtomicEvent(struct {
|
||||
fn wake(ptr: *const u32) void {
|
||||
const key = @ptrCast(*const i32, ptr);
|
||||
const rc = linux.futex_wake(key, linux.FUTEX_WAKE | linux.FUTEX_PRIVATE_FLAG, 1);
|
||||
assert(linux.getErrno(rc) == 0);
|
||||
}
|
||||
|
||||
fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void {
|
||||
var ts: linux.timespec = undefined;
|
||||
var ts_ptr: ?*linux.timespec = null;
|
||||
if (timeout) |timeout_ns| {
|
||||
ts_ptr = &ts;
|
||||
ts.tv_sec = @intCast(isize, timeout_ns / time.ns_per_s);
|
||||
ts.tv_nsec = @intCast(isize, timeout_ns % time.ns_per_s);
|
||||
}
|
||||
|
||||
const key = @ptrCast(*const i32, ptr);
|
||||
const key_expect = @bitCast(i32, expected);
|
||||
while (@atomicLoad(i32, key, .Acquire) == key_expect) {
|
||||
const rc = linux.futex_wait(key, linux.FUTEX_WAIT | linux.FUTEX_PRIVATE_FLAG, key_expect, ts_ptr);
|
||||
switch (linux.getErrno(rc)) {
|
||||
0, linux.EAGAIN => break,
|
||||
linux.EINTR => continue,
|
||||
linux.ETIMEDOUT => return ResetEvent.WaitError.TimedOut,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const WindowsEvent = AtomicEvent(struct {
|
||||
fn wake(ptr: *const u32) void {
|
||||
if (getEventHandle()) |handle| {
|
||||
const key = @ptrCast(*const c_void, ptr);
|
||||
const rc = windows.ntdll.NtReleaseKeyedEvent(handle, key, windows.FALSE, null);
|
||||
assert(rc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn wait(ptr: *const u32, expected: u32, timeout: ?u64) ResetEvent.WaitError!void {
|
||||
// fallback to spinlock if NT Keyed Events arent available
|
||||
const handle = getEventHandle() orelse {
|
||||
return SpinEvent.Futex.wait(ptr, expected, timeout);
|
||||
};
|
||||
|
||||
// NT uses timeouts in units of 100ns with negative value being relative
|
||||
var timeout_ptr: ?*windows.LARGE_INTEGER = null;
|
||||
var timeout_value: windows.LARGE_INTEGER = undefined;
|
||||
if (timeout) |timeout_ns| {
|
||||
timeout_ptr = &timeout_value;
|
||||
timeout_value = -@intCast(windows.LARGE_INTEGER, timeout_ns / 100);
|
||||
}
|
||||
|
||||
// NtWaitForKeyedEvent doesnt have spurious wake-ups
|
||||
if (@atomicLoad(u32, ptr, .Acquire) == expected) {
|
||||
const key = @ptrCast(*const c_void, ptr);
|
||||
const rc = windows.ntdll.NtWaitForKeyedEvent(handle, key, windows.FALSE, timeout_ptr);
|
||||
switch (rc) {
|
||||
0 => {},
|
||||
windows.WAIT_TIMEOUT => return ResetEvent.WaitError.TimedOut,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var keyed_state = State.Uninitialized;
|
||||
var keyed_handle: ?windows.HANDLE = null;
|
||||
|
||||
const State = enum(u8) {
|
||||
Uninitialized,
|
||||
Intializing,
|
||||
Initialized,
|
||||
};
|
||||
|
||||
fn getEventHandle() ?windows.HANDLE {
|
||||
var spin = Backoff.init();
|
||||
var state = @atomicLoad(State, &keyed_state, .Monotonic);
|
||||
|
||||
while (true) {
|
||||
switch (state) {
|
||||
.Initialized => {
|
||||
return keyed_handle;
|
||||
},
|
||||
.Intializing => {
|
||||
spin.yield();
|
||||
state = @atomicLoad(State, &keyed_state, .Acquire);
|
||||
},
|
||||
.Uninitialized => state = @cmpxchgWeak(State, &keyed_state, state, .Intializing, .Acquire, .Monotonic) orelse {
|
||||
var handle: windows.HANDLE = undefined;
|
||||
const access_mask = windows.GENERIC_READ | windows.GENERIC_WRITE;
|
||||
if (windows.ntdll.NtCreateKeyedEvent(&handle, access_mask, null, 0) == 0)
|
||||
keyed_handle = handle;
|
||||
@atomicStore(State, &keyed_state, .Initialized, .Release);
|
||||
return keyed_handle;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const PosixEvent = struct {
|
||||
state: u32,
|
||||
cond: c.pthread_cond_t,
|
||||
mutex: c.pthread_mutex_t,
|
||||
|
||||
const IS_SET: u32 = 1;
|
||||
|
||||
pub fn init() PosixEvent {
|
||||
return PosixEvent{
|
||||
.state = .0,
|
||||
.cond = c.PTHREAD_COND_INITIALIZER,
|
||||
.mutex = c.PTHREAD_MUTEX_INITIALIZER,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *PosixEvent) void {
|
||||
// On dragonfly, the destroy functions return EINVAL if they were initialized statically.
|
||||
const retm = c.pthread_mutex_destroy(&self.mutex);
|
||||
assert(retm == 0 or retm == (if (builtin.os == .dragonfly) os.EINVAL else 0));
|
||||
const retc = c.pthread_cond_destroy(&self.cond);
|
||||
assert(retc == 0 or retc == (if (builtin.os == .dragonfly) os.EINVAL else 0));
|
||||
}
|
||||
|
||||
pub fn isSet(self: *PosixEvent) bool {
|
||||
assert(c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
|
||||
return self.state == IS_SET;
|
||||
}
|
||||
|
||||
pub fn reset(self: *PosixEvent) bool {
|
||||
assert(c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
|
||||
const was_set = self.state == IS_SET;
|
||||
self.state = 0;
|
||||
return was_set;
|
||||
}
|
||||
|
||||
pub fn set(self: *PosixEvent, auto_reset: bool) bool {
|
||||
assert(c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
|
||||
const had_waiter = self.state > IS_SET;
|
||||
self.state = if (auto_reset) 0 else IS_SET;
|
||||
if (had_waiter) {
|
||||
assert(c.pthread_cond_signal(&self.cond) == 0);
|
||||
}
|
||||
return had_waiter;
|
||||
}
|
||||
|
||||
pub fn wait(self: *PosixEvent, timeout: ?u64) ResetEvent.WaitError!bool {
|
||||
assert(c.pthread_mutex_lock(&self.mutex) == 0);
|
||||
defer assert(c.pthread_mutex_unlock(&self.mutex) == 0);
|
||||
|
||||
if (self.state == IS_SET)
|
||||
return false;
|
||||
|
||||
var ts: os.timespec = undefined;
|
||||
if (timeout) |timeout_ns| {
|
||||
var timeout_abs = timeout_ns;
|
||||
if (comptime std.Target.current.isDarwin()) {
|
||||
var tv: os.darwin.timeval = undefined;
|
||||
assert(os.darwin.gettimeofday(&tv, null) == 0);
|
||||
timeout_abs += @intCast(u64, tv.tv_sec) * time.second;
|
||||
timeout_abs += @intCast(u64, tv.tv_usec) * time.microsecond;
|
||||
} else {
|
||||
os.clock_gettime(os.CLOCK_REALTIME, &ts) catch unreachable;
|
||||
timeout_abs += @intCast(u64, ts.tv_sec) * time.second;
|
||||
timeout_abs += @intCast(u64, ts.tv_nsec);
|
||||
}
|
||||
ts.tv_sec = @intCast(@typeOf(ts.tv_sec), @divFloor(timeout_abs, time.second));
|
||||
ts.tv_nsec = @intCast(@typeOf(ts.tv_nsec), @mod(timeout_abs, time.second));
|
||||
}
|
||||
|
||||
var dummy_value: u32 = undefined;
|
||||
var wait_token = @truncate(u32, @ptrToInt(&dummy_value));
|
||||
self.state = wait_token;
|
||||
|
||||
while (self.state == wait_token) {
|
||||
const rc = switch (timeout == null) {
|
||||
true => c.pthread_cond_wait(&self.cond, &self.mutex),
|
||||
else => c.pthread_cond_timedwait(&self.cond, &self.mutex, &ts),
|
||||
};
|
||||
// TODO: rc appears to be the positive error code making os.errno() always return 0 on linux
|
||||
switch (std.math.max(@as(c_int, os.errno(rc)), rc)) {
|
||||
0 => {},
|
||||
os.ETIMEDOUT => return ResetEvent.WaitError.TimedOut,
|
||||
os.EINVAL => unreachable,
|
||||
os.EPERM => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
test "std.ResetEvent" {
|
||||
// TODO
|
||||
if (builtin.single_threaded)
|
||||
return error.SkipZigTest;
|
||||
|
||||
var event = ResetEvent.init();
|
||||
defer event.deinit();
|
||||
|
||||
// test event setting
|
||||
testing.expect(event.isSet() == false);
|
||||
testing.expect(event.set(false) == false);
|
||||
testing.expect(event.isSet() == true);
|
||||
|
||||
// test event resetting
|
||||
testing.expect(event.reset() == true);
|
||||
testing.expect(event.isSet() == false);
|
||||
testing.expect(event.reset() == false);
|
||||
|
||||
// test cross thread signaling
|
||||
const Context = struct {
|
||||
event: ResetEvent,
|
||||
value: u128,
|
||||
|
||||
fn receiver(self: *@This()) void {
|
||||
// wait for the sender to notify us with updated value
|
||||
assert(self.value == 0);
|
||||
assert((self.event.wait(1 * time.second) catch unreachable) == true);
|
||||
assert(self.value == 1);
|
||||
|
||||
// wait for sender to sleep, then notify it of new value
|
||||
time.sleep(50 * time.millisecond);
|
||||
self.value = 2;
|
||||
assert(self.event.set(false) == true);
|
||||
}
|
||||
|
||||
fn sender(self: *@This()) !void {
|
||||
// wait for the receiver() to start wait()'ing
|
||||
time.sleep(50 * time.millisecond);
|
||||
|
||||
// update value to 1 and notify the receiver()
|
||||
assert(self.value == 0);
|
||||
self.value = 1;
|
||||
assert(self.event.set(true) == true);
|
||||
|
||||
// wait for the receiver to update the value & notify us
|
||||
assert((try self.event.wait(1 * time.second)) == true);
|
||||
assert(self.value == 2);
|
||||
}
|
||||
};
|
||||
|
||||
_ = event.reset();
|
||||
var context = Context{
|
||||
.event = event,
|
||||
.value = 0,
|
||||
};
|
||||
|
||||
var receiver = try std.Thread.spawn(&context, Context.receiver);
|
||||
defer receiver.wait();
|
||||
try context.sender();
|
||||
}
|
@ -197,6 +197,49 @@ extern fn __stack_chk_fail() noreturn {
|
||||
// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
|
||||
nakedcc fn clone() void {
|
||||
switch (builtin.arch) {
|
||||
.i386 => {
|
||||
// __clone(func, stack, flags, arg, ptid, tls, ctid)
|
||||
// +8, +12, +16, +20, +24, +28, +32
|
||||
// syscall(SYS_clone, flags, stack, ptid, tls, ctid)
|
||||
// eax, ebx, ecx, edx, esi, edi
|
||||
asm volatile (
|
||||
\\ push %%ebp
|
||||
\\ mov %%esp,%%ebp
|
||||
\\ push %%ebx
|
||||
\\ push %%esi
|
||||
\\ push %%edi
|
||||
\\ // Setup the arguments
|
||||
\\ mov 16(%%ebp),%%ebx
|
||||
\\ mov 12(%%ebp),%%ecx
|
||||
\\ and $-16,%%ecx
|
||||
\\ sub $20,%%ecx
|
||||
\\ mov 20(%%ebp),%%eax
|
||||
\\ mov %%eax,4(%%ecx)
|
||||
\\ mov 8(%%ebp),%%eax
|
||||
\\ mov %%eax,0(%%ecx)
|
||||
\\ mov 24(%%ebp),%%edx
|
||||
\\ mov 28(%%ebp),%%esi
|
||||
\\ mov 32(%%ebp),%%edi
|
||||
\\ mov $120,%%eax
|
||||
\\ int $128
|
||||
\\ test %%eax,%%eax
|
||||
\\ jnz 1f
|
||||
\\ pop %%eax
|
||||
\\ xor %%ebp,%%ebp
|
||||
\\ call *%%eax
|
||||
\\ mov %%eax,%%ebx
|
||||
\\ xor %%eax,%%eax
|
||||
\\ inc %%eax
|
||||
\\ int $128
|
||||
\\ hlt
|
||||
\\1:
|
||||
\\ pop %%edi
|
||||
\\ pop %%esi
|
||||
\\ pop %%ebx
|
||||
\\ pop %%ebp
|
||||
\\ ret
|
||||
);
|
||||
},
|
||||
.x86_64 => {
|
||||
asm volatile (
|
||||
\\ xor %%eax,%%eax
|
||||
|
@ -16,6 +16,7 @@ pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
|
||||
pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
|
||||
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
|
||||
pub const Progress = @import("progress.zig").Progress;
|
||||
pub const ResetEvent = @import("reset_event.zig").ResetEvent;
|
||||
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
|
||||
pub const SinglyLinkedList = @import("linked_list.zig").SinglyLinkedList;
|
||||
pub const SpinLock = @import("spinlock.zig").SpinLock;
|
||||
@ -23,7 +24,6 @@ pub const StringHashMap = @import("hash_map.zig").StringHashMap;
|
||||
pub const TailQueue = @import("linked_list.zig").TailQueue;
|
||||
pub const Target = @import("target.zig").Target;
|
||||
pub const Thread = @import("thread.zig").Thread;
|
||||
pub const ThreadParker = @import("parker.zig").ThreadParker;
|
||||
|
||||
pub const atomic = @import("atomic.zig");
|
||||
pub const base64 = @import("base64.zig");
|
||||
|
@ -89,7 +89,26 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
|
||||
if (union_info.tag_type == null) {
|
||||
@compileError("Unable to compare untagged union values");
|
||||
}
|
||||
@compileError("TODO implement testing.expectEqual for tagged unions");
|
||||
|
||||
const TagType = @TagType(@typeOf(expected));
|
||||
|
||||
const expectedTag = @as(TagType, expected);
|
||||
const actualTag = @as(TagType, actual);
|
||||
|
||||
expectEqual(expectedTag, actualTag);
|
||||
|
||||
// we only reach this loop if the tags are equal
|
||||
inline for (std.meta.fields(@typeOf(actual))) |fld| {
|
||||
if (std.mem.eql(u8, fld.name, @tagName(actualTag))) {
|
||||
expectEqual(@field(expected, fld.name), @field(actual, fld.name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we iterate over *all* union fields
|
||||
// => we should never get here as the loop above is
|
||||
// including all possible values.
|
||||
unreachable;
|
||||
},
|
||||
|
||||
.Optional => {
|
||||
@ -124,6 +143,19 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
|
||||
}
|
||||
}
|
||||
|
||||
test "expectEqual.union(enum)"
|
||||
{
|
||||
const T = union(enum) {
|
||||
a: i32,
|
||||
b: f32,
|
||||
};
|
||||
|
||||
const a10 = T { .a = 10 };
|
||||
const a20 = T { .a = 20 };
|
||||
|
||||
expectEqual(a10, a10);
|
||||
}
|
||||
|
||||
/// This function is intended to be used only in tests. When the two slices are not
|
||||
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
|
||||
/// then aborts.
|
||||
|
@ -314,11 +314,38 @@ pub const Thread = struct {
|
||||
os.CLONE_THREAD | os.CLONE_SYSVSEM | os.CLONE_PARENT_SETTID | os.CLONE_CHILD_CLEARTID |
|
||||
os.CLONE_DETACHED;
|
||||
var newtls: usize = undefined;
|
||||
// This structure is only needed when targeting i386
|
||||
var user_desc: if (builtin.arch == .i386) os.linux.user_desc else void = undefined;
|
||||
|
||||
if (os.linux.tls.tls_image) |tls_img| {
|
||||
newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset);
|
||||
if (builtin.arch == .i386) {
|
||||
user_desc = os.linux.user_desc{
|
||||
.entry_number = tls_img.gdt_entry_number,
|
||||
.base_addr = os.linux.tls.copyTLS(mmap_addr + tls_start_offset),
|
||||
.limit = 0xfffff,
|
||||
.seg_32bit = 1,
|
||||
.contents = 0, // Data
|
||||
.read_exec_only = 0,
|
||||
.limit_in_pages = 1,
|
||||
.seg_not_present = 0,
|
||||
.useable = 1,
|
||||
};
|
||||
newtls = @ptrToInt(&user_desc);
|
||||
} else {
|
||||
newtls = os.linux.tls.copyTLS(mmap_addr + tls_start_offset);
|
||||
}
|
||||
flags |= os.CLONE_SETTLS;
|
||||
}
|
||||
const rc = os.linux.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
|
||||
|
||||
const rc = os.linux.clone(
|
||||
MainFuncs.linuxThreadMain,
|
||||
mmap_addr + stack_end_offset,
|
||||
flags,
|
||||
arg,
|
||||
&thread_ptr.data.handle,
|
||||
newtls,
|
||||
&thread_ptr.data.handle,
|
||||
);
|
||||
switch (os.errno(rc)) {
|
||||
0 => return thread_ptr,
|
||||
os.EAGAIN => return error.ThreadQuotaExceeded,
|
||||
|
@ -25,10 +25,10 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
||||
|
||||
const context = llvm_handle.node.data;
|
||||
|
||||
const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory;
|
||||
const module = llvm.ModuleCreateWithNameInContext(comp.name.toSliceConst(), context) orelse return error.OutOfMemory;
|
||||
defer llvm.DisposeModule(module);
|
||||
|
||||
llvm.SetTarget(module, comp.llvm_triple.ptr());
|
||||
llvm.SetTarget(module, comp.llvm_triple.toSliceConst());
|
||||
llvm.SetDataLayout(module, comp.target_layout_str);
|
||||
|
||||
if (util.getObjectFormat(comp.target) == .coff) {
|
||||
@ -48,23 +48,23 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
||||
const producer = try std.Buffer.allocPrint(
|
||||
&code.arena.allocator,
|
||||
"zig {}.{}.{}",
|
||||
u32(c.ZIG_VERSION_MAJOR),
|
||||
u32(c.ZIG_VERSION_MINOR),
|
||||
u32(c.ZIG_VERSION_PATCH),
|
||||
@as(u32, c.ZIG_VERSION_MAJOR),
|
||||
@as(u32, c.ZIG_VERSION_MINOR),
|
||||
@as(u32, c.ZIG_VERSION_PATCH),
|
||||
);
|
||||
const flags = "";
|
||||
const runtime_version = 0;
|
||||
const compile_unit_file = llvm.CreateFile(
|
||||
dibuilder,
|
||||
comp.name.ptr(),
|
||||
comp.root_package.root_src_dir.ptr(),
|
||||
comp.name.toSliceConst(),
|
||||
comp.root_package.root_src_dir.toSliceConst(),
|
||||
) orelse return error.OutOfMemory;
|
||||
const is_optimized = comp.build_mode != .Debug;
|
||||
const compile_unit = llvm.CreateCompileUnit(
|
||||
dibuilder,
|
||||
DW.LANG_C99,
|
||||
compile_unit_file,
|
||||
producer.ptr(),
|
||||
producer.toSliceConst(),
|
||||
is_optimized,
|
||||
flags,
|
||||
runtime_version,
|
||||
@ -99,7 +99,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
||||
|
||||
// verify the llvm module when safety is on
|
||||
if (std.debug.runtime_safety) {
|
||||
var error_ptr: ?[*]u8 = null;
|
||||
var error_ptr: ?[*:0]u8 = null;
|
||||
_ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr);
|
||||
}
|
||||
|
||||
@ -108,12 +108,12 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
|
||||
const is_small = comp.build_mode == .ReleaseSmall;
|
||||
const is_debug = comp.build_mode == .Debug;
|
||||
|
||||
var err_msg: [*]u8 = undefined;
|
||||
var err_msg: [*:0]u8 = undefined;
|
||||
// TODO integrate this with evented I/O
|
||||
if (llvm.TargetMachineEmitToFile(
|
||||
comp.target_machine,
|
||||
module,
|
||||
output_path.ptr(),
|
||||
output_path.toSliceConst(),
|
||||
llvm.EmitBinary,
|
||||
&err_msg,
|
||||
is_debug,
|
||||
@ -154,7 +154,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
|
||||
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
fn_val.symbol_name.ptr(),
|
||||
fn_val.symbol_name.toSliceConst(),
|
||||
llvm_fn_type,
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
@ -379,7 +379,7 @@ fn renderLoadUntyped(
|
||||
ptr: *llvm.Value,
|
||||
alignment: Type.Pointer.Align,
|
||||
vol: Type.Pointer.Vol,
|
||||
name: [*]const u8,
|
||||
name: [*:0]const u8,
|
||||
) !*llvm.Value {
|
||||
const result = llvm.BuildLoad(ofile.builder, ptr, name) orelse return error.OutOfMemory;
|
||||
switch (vol) {
|
||||
@ -390,7 +390,7 @@ fn renderLoadUntyped(
|
||||
return result;
|
||||
}
|
||||
|
||||
fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*]const u8) !*llvm.Value {
|
||||
fn renderLoad(ofile: *ObjectFile, ptr: *llvm.Value, ptr_type: *Type.Pointer, name: [*:0]const u8) !*llvm.Value {
|
||||
return renderLoadUntyped(ofile, ptr, ptr_type.key.alignment, ptr_type.key.vol, name);
|
||||
}
|
||||
|
||||
@ -438,7 +438,7 @@ pub fn renderAlloca(
|
||||
) !*llvm.Value {
|
||||
const llvm_var_type = try var_type.getLlvmType(ofile.arena, ofile.context);
|
||||
const name_with_null = try std.cstr.addNullByte(ofile.arena, name);
|
||||
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, name_with_null.ptr) orelse return error.OutOfMemory;
|
||||
const result = llvm.BuildAlloca(ofile.builder, llvm_var_type, @ptrCast([*:0]const u8, name_with_null.ptr)) orelse return error.OutOfMemory;
|
||||
llvm.SetAlignment(result, resolveAlign(ofile, alignment, llvm_var_type));
|
||||
return result;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ pub const ZigCompiler = struct {
|
||||
return LlvmHandle{ .node = node };
|
||||
}
|
||||
|
||||
pub async fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
|
||||
pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
|
||||
if (self.native_libc.start()) |ptr| return ptr;
|
||||
try self.native_libc.data.findNative(self.allocator);
|
||||
self.native_libc.resolve();
|
||||
@ -133,62 +133,62 @@ pub const Compilation = struct {
|
||||
zig_std_dir: []const u8,
|
||||
|
||||
/// lazily created when we need it
|
||||
tmp_dir: event.Future(BuildError![]u8),
|
||||
tmp_dir: event.Future(BuildError![]u8) = event.Future(BuildError![]u8).init(),
|
||||
|
||||
version_major: u32,
|
||||
version_minor: u32,
|
||||
version_patch: u32,
|
||||
version_major: u32 = 0,
|
||||
version_minor: u32 = 0,
|
||||
version_patch: u32 = 0,
|
||||
|
||||
linker_script: ?[]const u8,
|
||||
out_h_path: ?[]const u8,
|
||||
linker_script: ?[]const u8 = null,
|
||||
out_h_path: ?[]const u8 = null,
|
||||
|
||||
is_test: bool,
|
||||
each_lib_rpath: bool,
|
||||
strip: bool,
|
||||
is_test: bool = false,
|
||||
each_lib_rpath: bool = false,
|
||||
strip: bool = false,
|
||||
is_static: bool,
|
||||
linker_rdynamic: bool,
|
||||
linker_rdynamic: bool = false,
|
||||
|
||||
clang_argv: []const []const u8,
|
||||
lib_dirs: []const []const u8,
|
||||
rpath_list: []const []const u8,
|
||||
assembly_files: []const []const u8,
|
||||
clang_argv: []const []const u8 = [_][]const u8{},
|
||||
lib_dirs: []const []const u8 = [_][]const u8{},
|
||||
rpath_list: []const []const u8 = [_][]const u8{},
|
||||
assembly_files: []const []const u8 = [_][]const u8{},
|
||||
|
||||
/// paths that are explicitly provided by the user to link against
|
||||
link_objects: []const []const u8,
|
||||
link_objects: []const []const u8 = [_][]const u8{},
|
||||
|
||||
/// functions that have their own objects that we need to link
|
||||
/// it uses an optional pointer so that tombstone removals are possible
|
||||
fn_link_set: event.Locked(FnLinkSet),
|
||||
fn_link_set: event.Locked(FnLinkSet) = event.Locked(FnLinkSet).init(FnLinkSet.init()),
|
||||
|
||||
pub const FnLinkSet = std.TailQueue(?*Value.Fn);
|
||||
|
||||
windows_subsystem_windows: bool,
|
||||
windows_subsystem_console: bool,
|
||||
windows_subsystem_windows: bool = false,
|
||||
windows_subsystem_console: bool = false,
|
||||
|
||||
link_libs_list: ArrayList(*LinkLib),
|
||||
libc_link_lib: ?*LinkLib,
|
||||
libc_link_lib: ?*LinkLib = null,
|
||||
|
||||
err_color: errmsg.Color,
|
||||
err_color: errmsg.Color = .Auto,
|
||||
|
||||
verbose_tokenize: bool,
|
||||
verbose_ast_tree: bool,
|
||||
verbose_ast_fmt: bool,
|
||||
verbose_cimport: bool,
|
||||
verbose_ir: bool,
|
||||
verbose_llvm_ir: bool,
|
||||
verbose_link: bool,
|
||||
verbose_tokenize: bool = false,
|
||||
verbose_ast_tree: bool = false,
|
||||
verbose_ast_fmt: bool = false,
|
||||
verbose_cimport: bool = false,
|
||||
verbose_ir: bool = false,
|
||||
verbose_llvm_ir: bool = false,
|
||||
verbose_link: bool = false,
|
||||
|
||||
darwin_frameworks: []const []const u8,
|
||||
darwin_version_min: DarwinVersionMin,
|
||||
darwin_frameworks: []const []const u8 = [_][]const u8{},
|
||||
darwin_version_min: DarwinVersionMin = .None,
|
||||
|
||||
test_filters: []const []const u8,
|
||||
test_name_prefix: ?[]const u8,
|
||||
test_filters: []const []const u8 = [_][]const u8{},
|
||||
test_name_prefix: ?[]const u8 = null,
|
||||
|
||||
emit_file_type: Emit,
|
||||
emit_file_type: Emit = .Binary,
|
||||
|
||||
kind: Kind,
|
||||
|
||||
link_out_file: ?[]const u8,
|
||||
link_out_file: ?[]const u8 = null,
|
||||
events: *event.Channel(Event),
|
||||
|
||||
exported_symbol_names: event.Locked(Decl.Table),
|
||||
@ -213,7 +213,7 @@ pub const Compilation = struct {
|
||||
|
||||
target_machine: *llvm.TargetMachine,
|
||||
target_data_ref: *llvm.TargetData,
|
||||
target_layout_str: [*]u8,
|
||||
target_layout_str: [*:0]u8,
|
||||
target_ptr_bits: u32,
|
||||
|
||||
/// for allocating things which have the same lifetime as this Compilation
|
||||
@ -222,16 +222,16 @@ pub const Compilation = struct {
|
||||
root_package: *Package,
|
||||
std_package: *Package,
|
||||
|
||||
override_libc: ?*LibCInstallation,
|
||||
override_libc: ?*LibCInstallation = null,
|
||||
|
||||
/// need to wait on this group before deinitializing
|
||||
deinit_group: event.Group(void),
|
||||
|
||||
// destroy_frame: @Frame(createAsync),
|
||||
// main_loop_frame: @Frame(Compilation.mainLoop),
|
||||
main_loop_future: event.Future(void),
|
||||
destroy_frame: *@Frame(createAsync),
|
||||
main_loop_frame: *@Frame(Compilation.mainLoop),
|
||||
main_loop_future: event.Future(void) = event.Future(void).init(),
|
||||
|
||||
have_err_ret_tracing: bool,
|
||||
have_err_ret_tracing: bool = false,
|
||||
|
||||
/// not locked because it is read-only
|
||||
primitive_type_table: TypeTable,
|
||||
@ -243,7 +243,9 @@ pub const Compilation = struct {
|
||||
|
||||
c_int_types: [CInt.list.len]*Type.Int,
|
||||
|
||||
// fs_watch: *fs.Watch(*Scope.Root),
|
||||
fs_watch: *fs.Watch(*Scope.Root),
|
||||
|
||||
cancelled: bool = false,
|
||||
|
||||
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
|
||||
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
|
||||
@ -348,7 +350,9 @@ pub const Compilation = struct {
|
||||
zig_lib_dir: []const u8,
|
||||
) !*Compilation {
|
||||
var optional_comp: ?*Compilation = null;
|
||||
var frame = async createAsync(
|
||||
var frame = try zig_compiler.allocator.create(@Frame(createAsync));
|
||||
errdefer zig_compiler.allocator.destroy(frame);
|
||||
frame.* = async createAsync(
|
||||
&optional_comp,
|
||||
zig_compiler,
|
||||
name,
|
||||
@ -359,11 +363,11 @@ pub const Compilation = struct {
|
||||
is_static,
|
||||
zig_lib_dir,
|
||||
);
|
||||
// TODO causes segfault
|
||||
// return optional_comp orelse if (await frame) |_| unreachable else |err| err;
|
||||
if (optional_comp) |comp| {
|
||||
return comp;
|
||||
} else {
|
||||
if (await frame) |_| unreachable else |err| return err;
|
||||
}
|
||||
} else if (await frame) |_| unreachable else |err| return err;
|
||||
}
|
||||
|
||||
async fn createAsync(
|
||||
@ -389,50 +393,13 @@ pub const Compilation = struct {
|
||||
.build_mode = build_mode,
|
||||
.zig_lib_dir = zig_lib_dir,
|
||||
.zig_std_dir = undefined,
|
||||
.tmp_dir = event.Future(BuildError![]u8).init(),
|
||||
// .destroy_frame = @frame(),
|
||||
// .main_loop_frame = undefined,
|
||||
.main_loop_future = event.Future(void).init(),
|
||||
.destroy_frame = @frame(),
|
||||
.main_loop_frame = undefined,
|
||||
|
||||
.name = undefined,
|
||||
.llvm_triple = undefined,
|
||||
|
||||
.version_major = 0,
|
||||
.version_minor = 0,
|
||||
.version_patch = 0,
|
||||
|
||||
.verbose_tokenize = false,
|
||||
.verbose_ast_tree = false,
|
||||
.verbose_ast_fmt = false,
|
||||
.verbose_cimport = false,
|
||||
.verbose_ir = false,
|
||||
.verbose_llvm_ir = false,
|
||||
.verbose_link = false,
|
||||
|
||||
.linker_script = null,
|
||||
.out_h_path = null,
|
||||
.is_test = false,
|
||||
.each_lib_rpath = false,
|
||||
.strip = false,
|
||||
.is_static = is_static,
|
||||
.linker_rdynamic = false,
|
||||
.clang_argv = &[_][]const u8{},
|
||||
.lib_dirs = &[_][]const u8{},
|
||||
.rpath_list = &[_][]const u8{},
|
||||
.assembly_files = &[_][]const u8{},
|
||||
.link_objects = &[_][]const u8{},
|
||||
.fn_link_set = event.Locked(FnLinkSet).init(FnLinkSet.init()),
|
||||
.windows_subsystem_windows = false,
|
||||
.windows_subsystem_console = false,
|
||||
.link_libs_list = undefined,
|
||||
.libc_link_lib = null,
|
||||
.err_color = errmsg.Color.Auto,
|
||||
.darwin_frameworks = &[_][]const u8{},
|
||||
.darwin_version_min = DarwinVersionMin.None,
|
||||
.test_filters = &[_][]const u8{},
|
||||
.test_name_prefix = null,
|
||||
.emit_file_type = Emit.Binary,
|
||||
.link_out_file = null,
|
||||
.exported_symbol_names = event.Locked(Decl.Table).init(Decl.Table.init(allocator)),
|
||||
.prelink_group = event.Group(BuildError!void).init(allocator),
|
||||
.deinit_group = event.Group(void).init(allocator),
|
||||
@ -462,11 +429,9 @@ pub const Compilation = struct {
|
||||
.root_package = undefined,
|
||||
.std_package = undefined,
|
||||
|
||||
.override_libc = null,
|
||||
.have_err_ret_tracing = false,
|
||||
.primitive_type_table = undefined,
|
||||
|
||||
// .fs_watch = undefined,
|
||||
.fs_watch = undefined,
|
||||
};
|
||||
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
|
||||
comp.primitive_type_table = TypeTable.init(comp.arena());
|
||||
@ -538,13 +503,16 @@ pub const Compilation = struct {
|
||||
comp.root_package = try Package.create(comp.arena(), ".", "");
|
||||
}
|
||||
|
||||
// comp.fs_watch = try fs.Watch(*Scope.Root).create(16);
|
||||
// defer comp.fs_watch.destroy();
|
||||
comp.fs_watch = try fs.Watch(*Scope.Root).init(allocator, 16);
|
||||
defer comp.fs_watch.deinit();
|
||||
|
||||
try comp.initTypes();
|
||||
defer comp.primitive_type_table.deinit();
|
||||
|
||||
// comp.main_loop_frame = async comp.mainLoop();
|
||||
comp.main_loop_frame = try allocator.create(@Frame(mainLoop));
|
||||
defer allocator.destroy(comp.main_loop_frame);
|
||||
|
||||
comp.main_loop_frame.* = async comp.mainLoop();
|
||||
// Set this to indicate that initialization completed successfully.
|
||||
// from here on out we must not return an error.
|
||||
// This must occur before the first suspend/await.
|
||||
@ -563,7 +531,7 @@ pub const Compilation = struct {
|
||||
}
|
||||
|
||||
/// it does ref the result because it could be an arbitrary integer size
|
||||
pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
|
||||
pub fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
|
||||
if (name.len >= 2) {
|
||||
switch (name[0]) {
|
||||
'i', 'u' => blk: {
|
||||
@ -757,8 +725,11 @@ pub const Compilation = struct {
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Compilation) void {
|
||||
// await self.main_loop_frame;
|
||||
// resume self.destroy_frame;
|
||||
const allocator = self.gpa();
|
||||
self.cancelled = true;
|
||||
await self.main_loop_frame;
|
||||
resume self.destroy_frame;
|
||||
allocator.destroy(self.destroy_frame);
|
||||
}
|
||||
|
||||
fn start(self: *Compilation) void {
|
||||
@ -771,7 +742,7 @@ pub const Compilation = struct {
|
||||
|
||||
var build_result = self.initialCompile();
|
||||
|
||||
while (true) {
|
||||
while (!self.cancelled) {
|
||||
const link_result = if (build_result) blk: {
|
||||
break :blk self.maybeLink();
|
||||
} else |err| err;
|
||||
@ -799,47 +770,47 @@ pub const Compilation = struct {
|
||||
self.events.put(Event{ .Error = err });
|
||||
}
|
||||
|
||||
// // First, get an item from the watch channel, waiting on the channel.
|
||||
// var group = event.Group(BuildError!void).init(self.gpa());
|
||||
// {
|
||||
// const ev = (self.fs_watch.channel.get()) catch |err| {
|
||||
// build_result = err;
|
||||
// continue;
|
||||
// };
|
||||
// const root_scope = ev.data;
|
||||
// group.call(rebuildFile, self, root_scope) catch |err| {
|
||||
// build_result = err;
|
||||
// continue;
|
||||
// };
|
||||
// }
|
||||
// // Next, get all the items from the channel that are buffered up.
|
||||
// while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
|
||||
// if (ev_or_err) |ev| {
|
||||
// const root_scope = ev.data;
|
||||
// group.call(rebuildFile, self, root_scope) catch |err| {
|
||||
// build_result = err;
|
||||
// continue;
|
||||
// };
|
||||
// } else |err| {
|
||||
// build_result = err;
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// build_result = group.wait();
|
||||
// First, get an item from the watch channel, waiting on the channel.
|
||||
var group = event.Group(BuildError!void).init(self.gpa());
|
||||
{
|
||||
const ev = (self.fs_watch.channel.get()) catch |err| {
|
||||
build_result = err;
|
||||
continue;
|
||||
};
|
||||
const root_scope = ev.data;
|
||||
group.call(rebuildFile, self, root_scope) catch |err| {
|
||||
build_result = err;
|
||||
continue;
|
||||
};
|
||||
}
|
||||
// Next, get all the items from the channel that are buffered up.
|
||||
while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
|
||||
if (ev_or_err) |ev| {
|
||||
const root_scope = ev.data;
|
||||
group.call(rebuildFile, self, root_scope) catch |err| {
|
||||
build_result = err;
|
||||
continue;
|
||||
};
|
||||
} else |err| {
|
||||
build_result = err;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
build_result = group.wait();
|
||||
}
|
||||
}
|
||||
|
||||
async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) !void {
|
||||
async fn rebuildFile(self: *Compilation, root_scope: *Scope.Root) BuildError!void {
|
||||
const tree_scope = blk: {
|
||||
const source_code = "";
|
||||
// const source_code = fs.readFile(
|
||||
// root_scope.realpath,
|
||||
// max_src_size,
|
||||
// ) catch |err| {
|
||||
// try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
|
||||
// return;
|
||||
// };
|
||||
// errdefer self.gpa().free(source_code);
|
||||
const source_code = fs.readFile(
|
||||
self.gpa(),
|
||||
root_scope.realpath,
|
||||
max_src_size,
|
||||
) catch |err| {
|
||||
try self.addCompileErrorCli(root_scope.realpath, "unable to open: {}", @errorName(err));
|
||||
return;
|
||||
};
|
||||
errdefer self.gpa().free(source_code);
|
||||
|
||||
const tree = try std.zig.parse(self.gpa(), source_code);
|
||||
errdefer {
|
||||
@ -877,7 +848,7 @@ pub const Compilation = struct {
|
||||
try decl_group.wait();
|
||||
}
|
||||
|
||||
async fn rebuildChangedDecls(
|
||||
fn rebuildChangedDecls(
|
||||
self: *Compilation,
|
||||
group: *event.Group(BuildError!void),
|
||||
locked_table: *Decl.Table,
|
||||
@ -966,7 +937,7 @@ pub const Compilation = struct {
|
||||
}
|
||||
}
|
||||
|
||||
async fn initialCompile(self: *Compilation) !void {
|
||||
fn initialCompile(self: *Compilation) !void {
|
||||
if (self.root_src_path) |root_src_path| {
|
||||
const root_scope = blk: {
|
||||
// TODO async/await std.fs.realpath
|
||||
@ -985,7 +956,7 @@ pub const Compilation = struct {
|
||||
}
|
||||
}
|
||||
|
||||
async fn maybeLink(self: *Compilation) !void {
|
||||
fn maybeLink(self: *Compilation) !void {
|
||||
(self.prelink_group.wait()) catch |err| switch (err) {
|
||||
error.SemanticAnalysisFailed => {},
|
||||
else => return err,
|
||||
@ -1169,11 +1140,10 @@ pub const Compilation = struct {
|
||||
return link_lib;
|
||||
}
|
||||
|
||||
/// cancels itself so no need to await or cancel the promise.
|
||||
async fn startFindingNativeLibC(self: *Compilation) void {
|
||||
std.event.Loop.instance.?.yield();
|
||||
event.Loop.startCpuBoundOperation();
|
||||
// we don't care if it fails, we're just trying to kick off the future resolution
|
||||
_ = (self.zig_compiler.getNativeLibC()) catch return;
|
||||
_ = self.zig_compiler.getNativeLibC() catch return;
|
||||
}
|
||||
|
||||
/// General Purpose Allocator. Must free when done.
|
||||
@ -1188,7 +1158,7 @@ pub const Compilation = struct {
|
||||
|
||||
/// If the temporary directory for this compilation has not been created, it creates it.
|
||||
/// Then it creates a random file name in that dir and returns it.
|
||||
pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
|
||||
pub fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer {
|
||||
const tmp_dir = try self.getTmpDir();
|
||||
const file_prefix = self.getRandomFileName();
|
||||
|
||||
@ -1204,14 +1174,14 @@ pub const Compilation = struct {
|
||||
/// If the temporary directory for this Compilation has not been created, creates it.
|
||||
/// Then returns it. The directory is unique to this Compilation and cleaned up when
|
||||
/// the Compilation deinitializes.
|
||||
async fn getTmpDir(self: *Compilation) ![]const u8 {
|
||||
fn getTmpDir(self: *Compilation) ![]const u8 {
|
||||
if (self.tmp_dir.start()) |ptr| return ptr.*;
|
||||
self.tmp_dir.data = self.getTmpDirImpl();
|
||||
self.tmp_dir.resolve();
|
||||
return self.tmp_dir.data;
|
||||
}
|
||||
|
||||
async fn getTmpDirImpl(self: *Compilation) ![]u8 {
|
||||
fn getTmpDirImpl(self: *Compilation) ![]u8 {
|
||||
const comp_dir_name = self.getRandomFileName();
|
||||
const zig_dir_path = try getZigDir(self.gpa());
|
||||
defer self.gpa().free(zig_dir_path);
|
||||
@ -1221,7 +1191,7 @@ pub const Compilation = struct {
|
||||
return tmp_dir;
|
||||
}
|
||||
|
||||
async fn getRandomFileName(self: *Compilation) [12]u8 {
|
||||
fn getRandomFileName(self: *Compilation) [12]u8 {
|
||||
// here we replace the standard +/ with -_ so that it can be used in a file name
|
||||
const b64_fs_encoder = std.base64.Base64Encoder.init(
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
|
||||
@ -1247,20 +1217,23 @@ pub const Compilation = struct {
|
||||
}
|
||||
|
||||
/// Returns a value which has been ref()'d once
|
||||
async fn analyzeConstValue(
|
||||
fn analyzeConstValue(
|
||||
comp: *Compilation,
|
||||
tree_scope: *Scope.AstTree,
|
||||
scope: *Scope,
|
||||
node: *ast.Node,
|
||||
expected_type: *Type,
|
||||
) !*Value {
|
||||
const analyzed_code = try comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
|
||||
var frame = try comp.gpa().create(@Frame(genAndAnalyzeCode));
|
||||
defer comp.gpa().destroy(frame);
|
||||
frame.* = async comp.genAndAnalyzeCode(tree_scope, scope, node, expected_type);
|
||||
const analyzed_code = try await frame;
|
||||
defer analyzed_code.destroy(comp.gpa());
|
||||
|
||||
return analyzed_code.getCompTimeResult(comp);
|
||||
}
|
||||
|
||||
async fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
|
||||
fn analyzeTypeExpr(comp: *Compilation, tree_scope: *Scope.AstTree, scope: *Scope, node: *ast.Node) !*Type {
|
||||
const meta_type = &Type.MetaType.get(comp).base;
|
||||
defer meta_type.base.deref(comp);
|
||||
|
||||
@ -1291,7 +1264,7 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
|
||||
}
|
||||
|
||||
/// The function that actually does the generation.
|
||||
async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
switch (decl.id) {
|
||||
.Var => @panic("TODO"),
|
||||
.Fn => {
|
||||
@ -1302,7 +1275,7 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
|
||||
}
|
||||
}
|
||||
|
||||
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
const tree_scope = fn_decl.base.tree_scope;
|
||||
|
||||
const body_node = fn_decl.fn_proto.body_node orelse return generateDeclFnProto(comp, fn_decl);
|
||||
@ -1319,7 +1292,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
|
||||
// The Decl.Fn owns the initial 1 reference count
|
||||
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
|
||||
fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
|
||||
fn_decl.value = .{ .Fn = fn_val };
|
||||
symbol_name_consumed = true;
|
||||
|
||||
// Define local parameter variables
|
||||
@ -1354,12 +1327,15 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
try fn_type.non_key.Normal.variable_list.append(var_scope);
|
||||
}
|
||||
|
||||
const analyzed_code = try comp.genAndAnalyzeCode(
|
||||
var frame = try comp.gpa().create(@Frame(Compilation.genAndAnalyzeCode));
|
||||
defer comp.gpa().destroy(frame);
|
||||
frame.* = async comp.genAndAnalyzeCode(
|
||||
tree_scope,
|
||||
fn_val.child_scope,
|
||||
body_node,
|
||||
fn_type.key.data.Normal.return_type,
|
||||
);
|
||||
const analyzed_code = try await frame;
|
||||
errdefer analyzed_code.destroy(comp.gpa());
|
||||
|
||||
assert(fn_val.block_scope != null);
|
||||
@ -1386,7 +1362,7 @@ fn getZigDir(allocator: *mem.Allocator) ![]u8 {
|
||||
return std.fs.getAppDataDir(allocator, "zig");
|
||||
}
|
||||
|
||||
async fn analyzeFnType(
|
||||
fn analyzeFnType(
|
||||
comp: *Compilation,
|
||||
tree_scope: *Scope.AstTree,
|
||||
scope: *Scope,
|
||||
@ -1448,7 +1424,7 @@ async fn analyzeFnType(
|
||||
return fn_type;
|
||||
}
|
||||
|
||||
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
const fn_type = try analyzeFnType(
|
||||
comp,
|
||||
fn_decl.base.tree_scope,
|
||||
@ -1463,6 +1439,6 @@ async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
|
||||
|
||||
// The Decl.Fn owns the initial 1 reference count
|
||||
const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
|
||||
fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
|
||||
fn_decl.value = .{ .FnProto = fn_proto_val };
|
||||
symbol_name_consumed = true;
|
||||
}
|
||||
|
@ -69,15 +69,12 @@ pub const Decl = struct {
|
||||
|
||||
pub const Fn = struct {
|
||||
base: Decl,
|
||||
value: Val,
|
||||
fn_proto: *ast.Node.FnProto,
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
|
||||
pub const Val = union(enum) {
|
||||
value: union(enum) {
|
||||
Unresolved,
|
||||
Fn: *Value.Fn,
|
||||
FnProto: *Value.FnProto,
|
||||
};
|
||||
},
|
||||
fn_proto: *ast.Node.FnProto,
|
||||
|
||||
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {
|
||||
return if (self.fn_proto.extern_export_inline_token) |tok_index| x: {
|
||||
|
@ -110,7 +110,7 @@ pub const Inst = struct {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
pub async fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
|
||||
pub fn analyze(base: *Inst, ira: *Analyze) Analyze.Error!*Inst {
|
||||
switch (base.id) {
|
||||
.Return => return @fieldParentPtr(Return, "base", base).analyze(ira),
|
||||
.Const => return @fieldParentPtr(Const, "base", base).analyze(ira),
|
||||
@ -422,7 +422,7 @@ pub const Inst = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
|
||||
pub fn analyze(self: *const Ref, ira: *Analyze) !*Inst {
|
||||
const target = try self.params.target.getAsParam();
|
||||
|
||||
if (ira.getCompTimeValOrNullUndefOk(target)) |val| {
|
||||
@ -472,7 +472,7 @@ pub const Inst = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
|
||||
pub fn analyze(self: *const DeclRef, ira: *Analyze) !*Inst {
|
||||
(ira.irb.comp.resolveDecl(self.params.decl)) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return error.SemanticAnalysisFailed,
|
||||
@ -516,7 +516,7 @@ pub const Inst = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
|
||||
pub fn analyze(self: *const VarPtr, ira: *Analyze) !*Inst {
|
||||
switch (self.params.var_scope.data) {
|
||||
.Const => @panic("TODO"),
|
||||
.Param => |param| {
|
||||
@ -563,7 +563,7 @@ pub const Inst = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
|
||||
pub fn analyze(self: *const LoadPtr, ira: *Analyze) !*Inst {
|
||||
const target = try self.params.target.getAsParam();
|
||||
const target_type = target.getKnownType();
|
||||
if (target_type.id != .Pointer) {
|
||||
@ -645,7 +645,7 @@ pub const Inst = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
pub async fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
|
||||
pub fn analyze(self: *const PtrType, ira: *Analyze) !*Inst {
|
||||
const child_type = try self.params.child_type.getAsConstType(ira);
|
||||
// if (child_type->id == TypeTableEntryIdUnreachable) {
|
||||
// ir_add_error(ira, &instruction->base, buf_sprintf("pointer to noreturn not allowed"));
|
||||
@ -658,7 +658,7 @@ pub const Inst = struct {
|
||||
const amt = try align_inst.getAsConstAlign(ira);
|
||||
break :blk Type.Pointer.Align{ .Override = amt };
|
||||
} else blk: {
|
||||
break :blk Type.Pointer.Align{ .Abi = {} };
|
||||
break :blk .Abi;
|
||||
};
|
||||
const ptr_type = try Type.Pointer.get(ira.irb.comp, Type.Pointer.Key{
|
||||
.child_type = child_type,
|
||||
@ -927,7 +927,7 @@ pub const Variable = struct {
|
||||
|
||||
pub const BasicBlock = struct {
|
||||
ref_count: usize,
|
||||
name_hint: [*]const u8, // must be a C string literal
|
||||
name_hint: [*:0]const u8,
|
||||
debug_id: usize,
|
||||
scope: *Scope,
|
||||
instruction_list: std.ArrayList(*Inst),
|
||||
@ -1051,7 +1051,7 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
/// No need to clean up resources thanks to the arena allocator.
|
||||
pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*]const u8) !*BasicBlock {
|
||||
pub fn createBasicBlock(self: *Builder, scope: *Scope, name_hint: [*:0]const u8) !*BasicBlock {
|
||||
const basic_block = try self.arena().create(BasicBlock);
|
||||
basic_block.* = BasicBlock{
|
||||
.ref_count = 0,
|
||||
@ -1078,6 +1078,14 @@ pub const Builder = struct {
|
||||
self.current_basic_block = basic_block;
|
||||
}
|
||||
|
||||
pub fn genNodeRecursive(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
|
||||
const alloc = irb.comp.gpa();
|
||||
var frame = try alloc.create(@Frame(genNode));
|
||||
defer alloc.destroy(frame);
|
||||
frame.* = async irb.genNode(node, scope, lval);
|
||||
return await frame;
|
||||
}
|
||||
|
||||
pub async fn genNode(irb: *Builder, node: *ast.Node, scope: *Scope, lval: LVal) Error!*Inst {
|
||||
switch (node.id) {
|
||||
.Root => unreachable,
|
||||
@ -1157,7 +1165,7 @@ pub const Builder = struct {
|
||||
},
|
||||
.GroupedExpression => {
|
||||
const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", node);
|
||||
return irb.genNode(grouped_expr.expr, scope, lval);
|
||||
return irb.genNodeRecursive(grouped_expr.expr, scope, lval);
|
||||
},
|
||||
.BuiltinCall => return error.Unimplemented,
|
||||
.ErrorSetDecl => return error.Unimplemented,
|
||||
@ -1186,14 +1194,14 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
|
||||
async fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
|
||||
const fn_ref = try irb.genNode(suffix_op.lhs, scope, .None);
|
||||
fn genCall(irb: *Builder, suffix_op: *ast.Node.SuffixOp, call: *ast.Node.SuffixOp.Op.Call, scope: *Scope) !*Inst {
|
||||
const fn_ref = try irb.genNodeRecursive(suffix_op.lhs.node, scope, .None);
|
||||
|
||||
const args = try irb.arena().alloc(*Inst, call.params.len);
|
||||
var it = call.params.iterator(0);
|
||||
var i: usize = 0;
|
||||
while (it.next()) |arg_node_ptr| : (i += 1) {
|
||||
args[i] = try irb.genNode(arg_node_ptr.*, scope, .None);
|
||||
args[i] = try irb.genNodeRecursive(arg_node_ptr.*, scope, .None);
|
||||
}
|
||||
|
||||
//bool is_async = node->data.fn_call_expr.is_async;
|
||||
@ -1214,7 +1222,7 @@ pub const Builder = struct {
|
||||
//return ir_lval_wrap(irb, scope, fn_call, lval);
|
||||
}
|
||||
|
||||
async fn genPtrType(
|
||||
fn genPtrType(
|
||||
irb: *Builder,
|
||||
prefix_op: *ast.Node.PrefixOp,
|
||||
ptr_info: ast.Node.PrefixOp.PtrInfo,
|
||||
@ -1238,7 +1246,7 @@ pub const Builder = struct {
|
||||
//} else {
|
||||
// align_value = nullptr;
|
||||
//}
|
||||
const child_type = try irb.genNode(prefix_op.rhs, scope, .None);
|
||||
const child_type = try irb.genNodeRecursive(prefix_op.rhs, scope, .None);
|
||||
|
||||
//uint32_t bit_offset_start = 0;
|
||||
//if (node->data.pointer_type.bit_offset_start != nullptr) {
|
||||
@ -1307,9 +1315,9 @@ pub const Builder = struct {
|
||||
var rest: []const u8 = undefined;
|
||||
if (int_token.len >= 3 and int_token[0] == '0') {
|
||||
base = switch (int_token[1]) {
|
||||
'b' => u8(2),
|
||||
'o' => u8(8),
|
||||
'x' => u8(16),
|
||||
'b' => 2,
|
||||
'o' => 8,
|
||||
'x' => 16,
|
||||
else => unreachable,
|
||||
};
|
||||
rest = int_token[2..];
|
||||
@ -1339,7 +1347,7 @@ pub const Builder = struct {
|
||||
return inst;
|
||||
}
|
||||
|
||||
pub async fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
|
||||
pub fn genStrLit(irb: *Builder, str_lit: *ast.Node.StringLiteral, scope: *Scope) !*Inst {
|
||||
const str_token = irb.code.tree_scope.tree.tokenSlice(str_lit.token);
|
||||
const src_span = Span.token(str_lit.token);
|
||||
|
||||
@ -1389,7 +1397,7 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
|
||||
pub fn genBlock(irb: *Builder, block: *ast.Node.Block, parent_scope: *Scope) !*Inst {
|
||||
const block_scope = try Scope.Block.create(irb.comp, parent_scope);
|
||||
|
||||
const outer_block_scope = &block_scope.base;
|
||||
@ -1437,7 +1445,7 @@ pub const Builder = struct {
|
||||
child_scope = &defer_child_scope.base;
|
||||
continue;
|
||||
}
|
||||
const statement_value = try irb.genNode(statement_node, child_scope, .None);
|
||||
const statement_value = try irb.genNodeRecursive(statement_node, child_scope, .None);
|
||||
|
||||
is_continuation_unreachable = statement_value.isNoReturn();
|
||||
if (is_continuation_unreachable) {
|
||||
@ -1499,7 +1507,7 @@ pub const Builder = struct {
|
||||
return irb.buildConstVoid(child_scope, Span.token(block.rbrace), true);
|
||||
}
|
||||
|
||||
pub async fn genControlFlowExpr(
|
||||
pub fn genControlFlowExpr(
|
||||
irb: *Builder,
|
||||
control_flow_expr: *ast.Node.ControlFlowExpression,
|
||||
scope: *Scope,
|
||||
@ -1533,7 +1541,7 @@ pub const Builder = struct {
|
||||
|
||||
const outer_scope = irb.begin_scope.?;
|
||||
const return_value = if (control_flow_expr.rhs) |rhs| blk: {
|
||||
break :blk try irb.genNode(rhs, scope, .None);
|
||||
break :blk try irb.genNodeRecursive(rhs, scope, .None);
|
||||
} else blk: {
|
||||
break :blk try irb.buildConstVoid(scope, src_span, true);
|
||||
};
|
||||
@ -1596,7 +1604,7 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
|
||||
pub fn genIdentifier(irb: *Builder, identifier: *ast.Node.Identifier, scope: *Scope, lval: LVal) !*Inst {
|
||||
const src_span = Span.token(identifier.token);
|
||||
const name = irb.code.tree_scope.tree.tokenSlice(identifier.token);
|
||||
|
||||
@ -1694,7 +1702,7 @@ pub const Builder = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
async fn genDefersForBlock(
|
||||
fn genDefersForBlock(
|
||||
irb: *Builder,
|
||||
inner_scope: *Scope,
|
||||
outer_scope: *Scope,
|
||||
@ -1712,7 +1720,7 @@ pub const Builder = struct {
|
||||
};
|
||||
if (generate) {
|
||||
const defer_expr_scope = defer_scope.defer_expr_scope;
|
||||
const instruction = try irb.genNode(
|
||||
const instruction = try irb.genNodeRecursive(
|
||||
defer_expr_scope.expr_node,
|
||||
&defer_expr_scope.base,
|
||||
.None,
|
||||
@ -1797,7 +1805,7 @@ pub const Builder = struct {
|
||||
// Look at the params and ref() other instructions
|
||||
comptime var i = 0;
|
||||
inline while (i < @memberCount(I.Params)) : (i += 1) {
|
||||
const FieldType = comptime @typeOf(@field(I.Params(undefined), @memberName(I.Params, i)));
|
||||
const FieldType = comptime @typeOf(@field(@as(I.Params, undefined), @memberName(I.Params, i)));
|
||||
switch (FieldType) {
|
||||
*Inst => @field(inst.params, @memberName(I.Params, i)).ref(self),
|
||||
*BasicBlock => @field(inst.params, @memberName(I.Params, i)).ref(self),
|
||||
@ -1909,7 +1917,7 @@ pub const Builder = struct {
|
||||
VarScope: *Scope.Var,
|
||||
};
|
||||
|
||||
async fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
|
||||
fn findIdent(irb: *Builder, scope: *Scope, name: []const u8) Ident {
|
||||
var s = scope;
|
||||
while (true) {
|
||||
switch (s.id) {
|
||||
@ -2519,7 +2527,7 @@ const Analyze = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub async fn gen(
|
||||
pub fn gen(
|
||||
comp: *Compilation,
|
||||
body_node: *ast.Node,
|
||||
tree_scope: *Scope.AstTree,
|
||||
@ -2541,7 +2549,7 @@ pub async fn gen(
|
||||
return irb.finish();
|
||||
}
|
||||
|
||||
pub async fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
|
||||
pub fn analyze(comp: *Compilation, old_code: *Code, expected_type: ?*Type) !*Code {
|
||||
const old_entry_bb = old_code.basic_block_list.at(0);
|
||||
|
||||
var ira = try Analyze.init(comp, old_code.tree_scope, expected_type);
|
||||
|
@ -143,7 +143,7 @@ pub const LibCInstallation = struct {
|
||||
}
|
||||
|
||||
/// Finds the default, native libc.
|
||||
pub async fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
|
||||
pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
|
||||
self.initEmpty();
|
||||
var group = event.Group(FindError!void).init(allocator);
|
||||
errdefer group.wait() catch {};
|
||||
@ -393,14 +393,14 @@ pub const LibCInstallation = struct {
|
||||
};
|
||||
|
||||
/// caller owns returned memory
|
||||
async fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
|
||||
fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
|
||||
const cc_exe = std.os.getenv("CC") orelse "cc";
|
||||
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", o_file);
|
||||
defer allocator.free(arg1);
|
||||
const argv = [_][]const u8{ cc_exe, arg1 };
|
||||
|
||||
// TODO This simulates evented I/O for the child process exec
|
||||
std.event.Loop.instance.?.yield();
|
||||
event.Loop.startCpuBoundOperation();
|
||||
const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024);
|
||||
const exec_result = if (std.debug.runtime_safety) blk: {
|
||||
break :blk errorable_result catch unreachable;
|
||||
|
@ -11,7 +11,7 @@ const util = @import("util.zig");
|
||||
const Context = struct {
|
||||
comp: *Compilation,
|
||||
arena: std.heap.ArenaAllocator,
|
||||
args: std.ArrayList([*]const u8),
|
||||
args: std.ArrayList([*:0]const u8),
|
||||
link_in_crt: bool,
|
||||
|
||||
link_err: error{OutOfMemory}!void,
|
||||
@ -21,7 +21,7 @@ const Context = struct {
|
||||
out_file_path: std.Buffer,
|
||||
};
|
||||
|
||||
pub async fn link(comp: *Compilation) !void {
|
||||
pub fn link(comp: *Compilation) !void {
|
||||
var ctx = Context{
|
||||
.comp = comp,
|
||||
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
|
||||
@ -33,7 +33,7 @@ pub async fn link(comp: *Compilation) !void {
|
||||
.out_file_path = undefined,
|
||||
};
|
||||
defer ctx.arena.deinit();
|
||||
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
|
||||
ctx.args = std.ArrayList([*:0]const u8).init(&ctx.arena.allocator);
|
||||
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
|
||||
|
||||
if (comp.link_out_file) |out_file| {
|
||||
@ -58,7 +58,8 @@ pub async fn link(comp: *Compilation) !void {
|
||||
try ctx.args.append("lld");
|
||||
|
||||
if (comp.haveLibC()) {
|
||||
ctx.libc = ctx.comp.override_libc orelse blk: {
|
||||
// TODO https://github.com/ziglang/zig/issues/3190
|
||||
var libc = ctx.comp.override_libc orelse blk: {
|
||||
switch (comp.target) {
|
||||
Target.Native => {
|
||||
break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
|
||||
@ -66,6 +67,7 @@ pub async fn link(comp: *Compilation) !void {
|
||||
else => return error.LibCRequiredButNotProvidedOrFound,
|
||||
}
|
||||
};
|
||||
ctx.libc = libc;
|
||||
}
|
||||
|
||||
try constructLinkerArgs(&ctx);
|
||||
@ -171,7 +173,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
//}
|
||||
|
||||
try ctx.args.append("-o");
|
||||
try ctx.args.append(ctx.out_file_path.ptr());
|
||||
try ctx.args.append(ctx.out_file_path.toSliceConst());
|
||||
|
||||
if (ctx.link_in_crt) {
|
||||
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
|
||||
@ -214,10 +216,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
|
||||
if (ctx.comp.haveLibC()) {
|
||||
try ctx.args.append("-L");
|
||||
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
|
||||
// TODO addNullByte should probably return [:0]u8
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr));
|
||||
|
||||
try ctx.args.append("-L");
|
||||
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr));
|
||||
|
||||
if (!ctx.comp.is_static) {
|
||||
const dl = blk: {
|
||||
@ -226,7 +229,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
return error.LibCMissingDynamicLinker;
|
||||
};
|
||||
try ctx.args.append("-dynamic-linker");
|
||||
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,7 +241,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
// .o files
|
||||
for (ctx.comp.link_objects) |link_object| {
|
||||
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
|
||||
try ctx.args.append(link_obj_with_null.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
|
||||
}
|
||||
try addFnObjects(ctx);
|
||||
|
||||
@ -313,7 +316,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
|
||||
const full_path = try std.fs.path.join(&ctx.arena.allocator, [_][]const u8{ dirname, basename });
|
||||
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
|
||||
try ctx.args.append(full_path_with_null.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, full_path_with_null.ptr));
|
||||
}
|
||||
|
||||
fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
@ -339,12 +342,12 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
const is_library = ctx.comp.kind == .Lib;
|
||||
|
||||
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
|
||||
try ctx.args.append(out_arg.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, out_arg.ptr));
|
||||
|
||||
if (ctx.comp.haveLibC()) {
|
||||
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
|
||||
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
|
||||
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr));
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr));
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr));
|
||||
}
|
||||
|
||||
if (ctx.link_in_crt) {
|
||||
@ -353,17 +356,17 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
|
||||
if (ctx.comp.is_static) {
|
||||
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
|
||||
try ctx.args.append(cmt_lib_name.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, cmt_lib_name.ptr));
|
||||
} else {
|
||||
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
|
||||
try ctx.args.append(msvcrt_lib_name.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, msvcrt_lib_name.ptr));
|
||||
}
|
||||
|
||||
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
|
||||
try ctx.args.append(vcruntime_lib_name.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, vcruntime_lib_name.ptr));
|
||||
|
||||
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
|
||||
try ctx.args.append(crt_lib_name.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, crt_lib_name.ptr));
|
||||
|
||||
// Visual C++ 2015 Conformance Changes
|
||||
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
|
||||
@ -395,7 +398,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
|
||||
for (ctx.comp.link_objects) |link_object| {
|
||||
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
|
||||
try ctx.args.append(link_obj_with_null.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
|
||||
}
|
||||
try addFnObjects(ctx);
|
||||
|
||||
@ -504,11 +507,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
//}
|
||||
|
||||
try ctx.args.append("-arch");
|
||||
const darwin_arch_str = try std.cstr.addNullByte(
|
||||
&ctx.arena.allocator,
|
||||
ctx.comp.target.getDarwinArchString(),
|
||||
);
|
||||
try ctx.args.append(darwin_arch_str.ptr);
|
||||
try ctx.args.append(util.getDarwinArchString(ctx.comp.target));
|
||||
|
||||
const platform = try DarwinPlatform.get(ctx.comp);
|
||||
switch (platform.kind) {
|
||||
@ -517,7 +516,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
.IPhoneOSSimulator => try ctx.args.append("-ios_simulator_version_min"),
|
||||
}
|
||||
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
|
||||
try ctx.args.append(ver_str.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, ver_str.ptr));
|
||||
|
||||
if (ctx.comp.kind == .Exe) {
|
||||
if (ctx.comp.is_static) {
|
||||
@ -528,7 +527,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
}
|
||||
|
||||
try ctx.args.append("-o");
|
||||
try ctx.args.append(ctx.out_file_path.ptr());
|
||||
try ctx.args.append(ctx.out_file_path.toSliceConst());
|
||||
|
||||
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
|
||||
// Buf *rpath = g->rpath_list.at(i);
|
||||
@ -572,7 +571,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
|
||||
for (ctx.comp.link_objects) |link_object| {
|
||||
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
|
||||
try ctx.args.append(link_obj_with_null.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, link_obj_with_null.ptr));
|
||||
}
|
||||
try addFnObjects(ctx);
|
||||
|
||||
@ -593,10 +592,10 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
} else {
|
||||
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
|
||||
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
|
||||
try ctx.args.append(arg.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
|
||||
} else {
|
||||
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
|
||||
try ctx.args.append(arg.ptr);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -626,20 +625,19 @@ fn constructLinkerArgsWasm(ctx: *Context) void {
|
||||
}
|
||||
|
||||
fn addFnObjects(ctx: *Context) !void {
|
||||
// at this point it's guaranteed nobody else has this lock, so we circumvent it
|
||||
// and avoid having to be an async function
|
||||
const fn_link_set = &ctx.comp.fn_link_set.private_data;
|
||||
const held = ctx.comp.fn_link_set.acquire();
|
||||
defer held.release();
|
||||
|
||||
var it = fn_link_set.first;
|
||||
var it = held.value.first;
|
||||
while (it) |node| {
|
||||
const fn_val = node.data orelse {
|
||||
// handle the tombstone. See Value.Fn.destroy.
|
||||
it = node.next;
|
||||
fn_link_set.remove(node);
|
||||
held.value.remove(node);
|
||||
ctx.comp.gpa().destroy(node);
|
||||
continue;
|
||||
};
|
||||
try ctx.args.append(fn_val.containing_object.ptr());
|
||||
try ctx.args.append(fn_val.containing_object.toSliceConst());
|
||||
it = node.next;
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ pub const AddGlobal = LLVMAddGlobal;
|
||||
extern fn LLVMAddGlobal(M: *Module, Ty: *Type, Name: [*:0]const u8) ?*Value;
|
||||
|
||||
pub const ConstStringInContext = LLVMConstStringInContext;
|
||||
extern fn LLVMConstStringInContext(C: *Context, Str: [*:0]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
|
||||
extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) ?*Value;
|
||||
|
||||
pub const ConstInt = LLVMConstInt;
|
||||
extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) ?*Value;
|
||||
|
@ -49,14 +49,15 @@ const usage =
|
||||
|
||||
const Command = struct {
|
||||
name: []const u8,
|
||||
exec: fn (*Allocator, []const []const u8) anyerror!void,
|
||||
exec: async fn (*Allocator, []const []const u8) anyerror!void,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
// This allocator needs to be thread-safe because we use it for the event.Loop
|
||||
// which multiplexes async functions onto kernel threads.
|
||||
// libc allocator is guaranteed to have this property.
|
||||
const allocator = std.heap.c_allocator;
|
||||
// TODO https://github.com/ziglang/zig/issues/3783
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
stdout = &std.io.getStdOut().outStream().stream;
|
||||
|
||||
@ -118,14 +119,18 @@ pub fn main() !void {
|
||||
},
|
||||
};
|
||||
|
||||
for (commands) |command| {
|
||||
inline for (commands) |command| {
|
||||
if (mem.eql(u8, command.name, args[1])) {
|
||||
return command.exec(allocator, args[2..]);
|
||||
var frame = try allocator.create(@Frame(command.exec));
|
||||
defer allocator.destroy(frame);
|
||||
frame.* = async command.exec(allocator, args[2..]);
|
||||
return await frame;
|
||||
}
|
||||
}
|
||||
|
||||
try stderr.print("unknown command: {}\n\n", args[1]);
|
||||
try stderr.write(usage);
|
||||
process.argsFree(allocator, args);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -461,13 +466,12 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
comp.link_objects = link_objects;
|
||||
|
||||
comp.start();
|
||||
const frame = async processBuildEvents(comp, color);
|
||||
processBuildEvents(comp, color);
|
||||
}
|
||||
|
||||
async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
||||
fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
||||
var count: usize = 0;
|
||||
while (true) {
|
||||
// TODO directly awaiting async should guarantee memory allocation elision
|
||||
while (!comp.cancelled) {
|
||||
const build_event = comp.events.get();
|
||||
count += 1;
|
||||
|
||||
@ -545,7 +549,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
|
||||
"Try running `zig libc` to see an example for the native target.\n",
|
||||
libc_paths_file,
|
||||
@errorName(err),
|
||||
) catch process.exit(1);
|
||||
) catch {};
|
||||
process.exit(1);
|
||||
};
|
||||
}
|
||||
@ -567,12 +571,8 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
|
||||
var zig_compiler = try ZigCompiler.init(allocator);
|
||||
defer zig_compiler.deinit();
|
||||
|
||||
const frame = async findLibCAsync(&zig_compiler);
|
||||
}
|
||||
|
||||
async fn findLibCAsync(zig_compiler: *ZigCompiler) void {
|
||||
const libc = zig_compiler.getNativeLibC() catch |err| {
|
||||
stderr.print("unable to find libc: {}\n", @errorName(err)) catch process.exit(1);
|
||||
stderr.print("unable to find libc: {}\n", @errorName(err)) catch {};
|
||||
process.exit(1);
|
||||
};
|
||||
libc.render(stdout) catch process.exit(1);
|
||||
@ -644,11 +644,23 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return asyncFmtMain(
|
||||
allocator,
|
||||
&flags,
|
||||
color,
|
||||
);
|
||||
var fmt = Fmt{
|
||||
.allocator = allocator,
|
||||
.seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
|
||||
.any_error = false,
|
||||
.color = color,
|
||||
};
|
||||
|
||||
const check_mode = flags.present("check");
|
||||
|
||||
var group = event.Group(FmtError!void).init(allocator);
|
||||
for (flags.positionals.toSliceConst()) |file_path| {
|
||||
try group.call(fmtPath, &fmt, file_path, check_mode);
|
||||
}
|
||||
try group.wait();
|
||||
if (fmt.any_error) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
const FmtError = error{
|
||||
@ -673,30 +685,6 @@ const FmtError = error{
|
||||
CurrentWorkingDirectoryUnlinked,
|
||||
} || fs.File.OpenError;
|
||||
|
||||
async fn asyncFmtMain(
|
||||
allocator: *Allocator,
|
||||
flags: *const Args,
|
||||
color: errmsg.Color,
|
||||
) FmtError!void {
|
||||
var fmt = Fmt{
|
||||
.allocator = allocator,
|
||||
.seen = event.Locked(Fmt.SeenMap).init(Fmt.SeenMap.init(allocator)),
|
||||
.any_error = false,
|
||||
.color = color,
|
||||
};
|
||||
|
||||
const check_mode = flags.present("check");
|
||||
|
||||
var group = event.Group(FmtError!void).init(allocator);
|
||||
for (flags.positionals.toSliceConst()) |file_path| {
|
||||
try group.call(fmtPath, &fmt, file_path, check_mode);
|
||||
}
|
||||
try group.wait();
|
||||
if (fmt.any_error) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
|
||||
const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
|
||||
defer fmt.allocator.free(file_path);
|
||||
@ -708,33 +696,34 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
|
||||
if (try held.value.put(file_path, {})) |_| return;
|
||||
}
|
||||
|
||||
const source_code = "";
|
||||
// const source_code = event.fs.readFile(
|
||||
// file_path,
|
||||
// max_src_size,
|
||||
// ) catch |err| switch (err) {
|
||||
// error.IsDir, error.AccessDenied => {
|
||||
// // TODO make event based (and dir.next())
|
||||
// var dir = try fs.Dir.cwd().openDirList(file_path);
|
||||
// defer dir.close();
|
||||
const source_code = event.fs.readFile(
|
||||
fmt.allocator,
|
||||
file_path,
|
||||
max_src_size,
|
||||
) catch |err| switch (err) {
|
||||
error.IsDir, error.AccessDenied => {
|
||||
var dir = try fs.cwd().openDirList(file_path);
|
||||
defer dir.close();
|
||||
|
||||
// var group = event.Group(FmtError!void).init(fmt.allocator);
|
||||
// while (try dir.next()) |entry| {
|
||||
// if (entry.kind == fs.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
|
||||
// const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
|
||||
// try group.call(fmtPath, fmt, full_path, check_mode);
|
||||
// }
|
||||
// }
|
||||
// return group.wait();
|
||||
// },
|
||||
// else => {
|
||||
// // TODO lock stderr printing
|
||||
// try stderr.print("unable to open '{}': {}\n", file_path, err);
|
||||
// fmt.any_error = true;
|
||||
// return;
|
||||
// },
|
||||
// };
|
||||
// defer fmt.allocator.free(source_code);
|
||||
var group = event.Group(FmtError!void).init(fmt.allocator);
|
||||
var it = dir.iterate();
|
||||
while (try it.next()) |entry| {
|
||||
if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
|
||||
const full_path = try fs.path.join(fmt.allocator, [_][]const u8{ file_path, entry.name });
|
||||
@panic("TODO https://github.com/ziglang/zig/issues/3777");
|
||||
// try group.call(fmtPath, fmt, full_path, check_mode);
|
||||
}
|
||||
}
|
||||
return group.wait();
|
||||
},
|
||||
else => {
|
||||
// TODO lock stderr printing
|
||||
try stderr.print("unable to open '{}': {}\n", file_path, err);
|
||||
fmt.any_error = true;
|
||||
return;
|
||||
},
|
||||
};
|
||||
defer fmt.allocator.free(source_code);
|
||||
|
||||
const tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
|
||||
try stderr.print("error parsing file '{}': {}\n", file_path, err);
|
||||
@ -867,10 +856,12 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
|
||||
.exec = cmdInternalBuildInfo,
|
||||
}};
|
||||
|
||||
for (sub_commands) |sub_command| {
|
||||
inline for (sub_commands) |sub_command| {
|
||||
if (mem.eql(u8, sub_command.name, args[0])) {
|
||||
try sub_command.exec(allocator, args[1..]);
|
||||
return;
|
||||
var frame = try allocator.create(@Frame(sub_command.exec));
|
||||
defer allocator.destroy(frame);
|
||||
frame.* = async sub_command.exec(allocator, args[1..]);
|
||||
return await frame;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -279,7 +279,7 @@ fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void
|
||||
const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
|
||||
error.IsDir, error.AccessDenied => {
|
||||
// TODO make event based (and dir.next())
|
||||
var dir = try fs.Dir.cwd().openDirList(file_path);
|
||||
var dir = try fs.cwd().openDirList(file_path);
|
||||
defer dir.close();
|
||||
|
||||
var dir_it = dir.iterate();
|
||||
@ -427,11 +427,11 @@ export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextRes
|
||||
};
|
||||
}
|
||||
|
||||
export const stage2_DepTokenizer = extern struct {
|
||||
const stage2_DepTokenizer = extern struct {
|
||||
handle: *DepTokenizer,
|
||||
};
|
||||
|
||||
export const stage2_DepNextResult = extern struct {
|
||||
const stage2_DepNextResult = extern struct {
|
||||
type_id: TypeId,
|
||||
|
||||
// when type_id == error --> error text
|
||||
@ -440,7 +440,7 @@ export const stage2_DepNextResult = extern struct {
|
||||
// when type_id == prereq --> prereq pathname
|
||||
textz: [*]const u8,
|
||||
|
||||
export const TypeId = extern enum {
|
||||
const TypeId = extern enum {
|
||||
error_,
|
||||
null_,
|
||||
target,
|
||||
|
@ -26,7 +26,8 @@ test "stage2" {
|
||||
}
|
||||
|
||||
const file1 = "1.zig";
|
||||
const allocator = std.heap.c_allocator;
|
||||
// TODO https://github.com/ziglang/zig/issues/3783
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
pub const TestContext = struct {
|
||||
zig_compiler: ZigCompiler,
|
||||
@ -94,8 +95,8 @@ pub const TestContext = struct {
|
||||
&self.zig_compiler,
|
||||
"test",
|
||||
file1_path,
|
||||
Target.Native,
|
||||
Compilation.Kind.Obj,
|
||||
.Native,
|
||||
.Obj,
|
||||
.Debug,
|
||||
true, // is_static
|
||||
self.zig_lib_dir,
|
||||
@ -116,7 +117,7 @@ pub const TestContext = struct {
|
||||
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
|
||||
const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 });
|
||||
|
||||
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{.Native = {}}).exeFileExt());
|
||||
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, (Target{ .Native = {} }).exeFileExt());
|
||||
if (std.fs.path.dirname(file1_path)) |dirname| {
|
||||
try std.fs.makePath(allocator, dirname);
|
||||
}
|
||||
@ -128,8 +129,8 @@ pub const TestContext = struct {
|
||||
&self.zig_compiler,
|
||||
"test",
|
||||
file1_path,
|
||||
Target.Native,
|
||||
Compilation.Kind.Exe,
|
||||
.Native,
|
||||
.Exe,
|
||||
.Debug,
|
||||
false,
|
||||
self.zig_lib_dir,
|
||||
@ -148,15 +149,12 @@ pub const TestContext = struct {
|
||||
exe_file: []const u8,
|
||||
expected_output: []const u8,
|
||||
) anyerror!void {
|
||||
// TODO this should not be necessary
|
||||
const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
|
||||
|
||||
defer comp.destroy();
|
||||
const build_event = comp.events.get();
|
||||
|
||||
switch (build_event) {
|
||||
.Ok => {
|
||||
const argv = [_][]const u8{exe_file_2};
|
||||
const argv = [_][]const u8{exe_file};
|
||||
// TODO use event loop
|
||||
const child = try std.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
|
||||
switch (child.term) {
|
||||
@ -173,13 +171,13 @@ pub const TestContext = struct {
|
||||
return error.OutputMismatch;
|
||||
}
|
||||
},
|
||||
Compilation.Event.Error => |err| return err,
|
||||
Compilation.Event.Fail => |msgs| {
|
||||
.Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err,
|
||||
.Fail => |msgs| {
|
||||
const stderr = std.io.getStdErr();
|
||||
try stderr.write("build incorrectly failed:\n");
|
||||
for (msgs) |msg| {
|
||||
defer msg.destroy();
|
||||
try msg.printToFile(stderr, errmsg.Color.Auto);
|
||||
try msg.printToFile(stderr, .Auto);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ pub const Type = struct {
|
||||
base: *Type,
|
||||
allocator: *Allocator,
|
||||
llvm_context: *llvm.Context,
|
||||
) (error{OutOfMemory}!*llvm.Type) {
|
||||
) error{OutOfMemory}!*llvm.Type {
|
||||
switch (base.id) {
|
||||
.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
|
||||
.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
|
||||
@ -184,7 +184,7 @@ pub const Type = struct {
|
||||
|
||||
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
|
||||
/// Otherwise, this one will grab one from the pool and then release it.
|
||||
pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
|
||||
pub fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
|
||||
if (base.abi_alignment.start()) |ptr| return ptr.*;
|
||||
|
||||
{
|
||||
@ -200,7 +200,7 @@ pub const Type = struct {
|
||||
}
|
||||
|
||||
/// If you have an llvm conext handy, you can use it here.
|
||||
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
|
||||
pub fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
|
||||
if (base.abi_alignment.start()) |ptr| return ptr.*;
|
||||
|
||||
base.abi_alignment.data = base.resolveAbiAlignment(comp, llvm_context);
|
||||
@ -209,7 +209,7 @@ pub const Type = struct {
|
||||
}
|
||||
|
||||
/// Lower level function that does the work. See getAbiAlignment.
|
||||
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
|
||||
fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: *llvm.Context) !u32 {
|
||||
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
|
||||
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
|
||||
}
|
||||
@ -367,7 +367,7 @@ pub const Type = struct {
|
||||
}
|
||||
|
||||
/// takes ownership of key.Normal.params on success
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Fn {
|
||||
pub fn get(comp: *Compilation, key: Key) !*Fn {
|
||||
{
|
||||
const held = comp.fn_type_table.acquire();
|
||||
defer held.release();
|
||||
@ -564,7 +564,7 @@ pub const Type = struct {
|
||||
return comp.u8_type;
|
||||
}
|
||||
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Int {
|
||||
pub fn get(comp: *Compilation, key: Key) !*Int {
|
||||
{
|
||||
const held = comp.int_type_table.acquire();
|
||||
defer held.release();
|
||||
@ -606,7 +606,7 @@ pub const Type = struct {
|
||||
comp.registerGarbage(Int, &self.garbage_node);
|
||||
}
|
||||
|
||||
pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
|
||||
pub fn gcDestroy(self: *Int, comp: *Compilation) void {
|
||||
{
|
||||
const held = comp.int_type_table.acquire();
|
||||
defer held.release();
|
||||
@ -700,7 +700,7 @@ pub const Type = struct {
|
||||
comp.registerGarbage(Pointer, &self.garbage_node);
|
||||
}
|
||||
|
||||
pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
|
||||
pub fn gcDestroy(self: *Pointer, comp: *Compilation) void {
|
||||
{
|
||||
const held = comp.ptr_type_table.acquire();
|
||||
defer held.release();
|
||||
@ -711,14 +711,14 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
|
||||
pub fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
|
||||
switch (self.key.alignment) {
|
||||
.Abi => return self.key.child_type.getAbiAlignment(comp),
|
||||
.Override => |alignment| return alignment,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
pub fn get(
|
||||
comp: *Compilation,
|
||||
key: Key,
|
||||
) !*Pointer {
|
||||
@ -726,8 +726,10 @@ pub const Type = struct {
|
||||
switch (key.alignment) {
|
||||
.Abi => {},
|
||||
.Override => |alignment| {
|
||||
// TODO https://github.com/ziglang/zig/issues/3190
|
||||
var align_spill = alignment;
|
||||
const abi_align = try key.child_type.getAbiAlignment(comp);
|
||||
if (abi_align == alignment) {
|
||||
if (abi_align == align_spill) {
|
||||
normal_key.alignment = .Abi;
|
||||
}
|
||||
},
|
||||
@ -828,7 +830,7 @@ pub const Type = struct {
|
||||
comp.gpa().destroy(self);
|
||||
}
|
||||
|
||||
pub async fn get(comp: *Compilation, key: Key) !*Array {
|
||||
pub fn get(comp: *Compilation, key: Key) !*Array {
|
||||
key.elem_type.base.ref();
|
||||
errdefer key.elem_type.base.deref(comp);
|
||||
|
||||
|
@ -32,21 +32,21 @@ pub fn getFloatAbi(self: Target) FloatAbi {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getObjectFormat(self: Target) Target.ObjectFormat {
|
||||
return switch (self) {
|
||||
.Native => @import("builtin").object_format,
|
||||
.Cross => {
|
||||
pub fn getObjectFormat(target: Target) Target.ObjectFormat {
|
||||
switch (target) {
|
||||
.Native => return @import("builtin").object_format,
|
||||
.Cross => blk: {
|
||||
if (target.isWindows() or target.isUefi()) {
|
||||
break .coff;
|
||||
return .coff;
|
||||
} else if (target.isDarwin()) {
|
||||
break .macho;
|
||||
return .macho;
|
||||
}
|
||||
if (target.isWasm()) {
|
||||
break .wasm;
|
||||
return .wasm;
|
||||
}
|
||||
break .elf;
|
||||
return .elf;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
|
||||
@ -156,7 +156,7 @@ pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getDarwinArchString(self: Target) []const u8 {
|
||||
pub fn getDarwinArchString(self: Target) [:0]const u8 {
|
||||
const arch = self.getArch();
|
||||
switch (arch) {
|
||||
.aarch64 => return "arm64",
|
||||
@ -166,7 +166,8 @@ pub fn getDarwinArchString(self: Target) []const u8 {
|
||||
.powerpc => return "ppc",
|
||||
.powerpc64 => return "ppc64",
|
||||
.powerpc64le => return "ppc64le",
|
||||
else => return @tagName(arch),
|
||||
// @tagName should be able to return sentinel terminated slice
|
||||
else => @panic("TODO https://github.com/ziglang/zig/issues/3779"), //return @tagName(arch),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ pub const Value = struct {
|
||||
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
self.symbol_name.ptr(),
|
||||
self.symbol_name.toSliceConst(),
|
||||
llvm_fn_type,
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
@ -241,7 +241,7 @@ pub const Value = struct {
|
||||
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
|
||||
const llvm_fn = llvm.AddFunction(
|
||||
ofile.module,
|
||||
self.symbol_name.ptr(),
|
||||
self.symbol_name.toSliceConst(),
|
||||
llvm_fn_type,
|
||||
) orelse return error.OutOfMemory;
|
||||
|
||||
@ -334,7 +334,7 @@ pub const Value = struct {
|
||||
field_index: usize,
|
||||
};
|
||||
|
||||
pub async fn createArrayElemPtr(
|
||||
pub fn createArrayElemPtr(
|
||||
comp: *Compilation,
|
||||
array_val: *Array,
|
||||
mut: Type.Pointer.Mut,
|
||||
@ -350,7 +350,7 @@ pub const Value = struct {
|
||||
.mut = mut,
|
||||
.vol = Type.Pointer.Vol.Non,
|
||||
.size = size,
|
||||
.alignment = Type.Pointer.Align.Abi,
|
||||
.alignment = .Abi,
|
||||
});
|
||||
var ptr_type_consumed = false;
|
||||
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
|
||||
@ -390,13 +390,13 @@ pub const Value = struct {
|
||||
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
|
||||
const ptr_bit_count = ofile.comp.target_ptr_bits;
|
||||
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
|
||||
const indices = [_]*llvm.Value{
|
||||
var indices = [_]*llvm.Value{
|
||||
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
|
||||
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
|
||||
};
|
||||
return llvm.ConstInBoundsGEP(
|
||||
array_llvm_value,
|
||||
&indices,
|
||||
@ptrCast([*]*llvm.Value, &indices),
|
||||
@intCast(c_uint, indices.len),
|
||||
) orelse return error.OutOfMemory;
|
||||
},
|
||||
@ -423,7 +423,7 @@ pub const Value = struct {
|
||||
};
|
||||
|
||||
/// Takes ownership of buffer
|
||||
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
|
||||
pub fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
|
||||
const u8_type = Type.Int.get_u8(comp);
|
||||
defer u8_type.base.base.deref(comp);
|
||||
|
||||
|
@ -1565,7 +1565,7 @@ struct ZigFn {
|
||||
// in the case of async functions this is the implicit return type according to the
|
||||
// zig source code, not according to zig ir
|
||||
ZigType *src_implicit_return_type;
|
||||
IrExecutable ir_executable;
|
||||
IrExecutable *ir_executable;
|
||||
IrExecutable analyzed_executable;
|
||||
size_t prealloc_bbc;
|
||||
size_t prealloc_backward_branch_quota;
|
||||
@ -2204,6 +2204,8 @@ struct ZigVar {
|
||||
bool src_is_const;
|
||||
bool gen_is_const;
|
||||
bool is_thread_local;
|
||||
bool is_comptime_memoized;
|
||||
bool is_comptime_memoized_value;
|
||||
};
|
||||
|
||||
struct ErrorTableEntry {
|
||||
|
@ -3275,14 +3275,15 @@ static void get_fully_qualified_decl_name(CodeGen *g, Buf *buf, Tld *tld, bool i
|
||||
}
|
||||
|
||||
ZigFn *create_fn_raw(CodeGen *g, FnInline inline_value) {
|
||||
ZigFn *fn_entry = allocate<ZigFn>(1);
|
||||
ZigFn *fn_entry = allocate<ZigFn>(1, "ZigFn");
|
||||
fn_entry->ir_executable = allocate<IrExecutable>(1, "IrExecutablePass1");
|
||||
|
||||
fn_entry->prealloc_backward_branch_quota = default_backward_branch_quota;
|
||||
|
||||
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
|
||||
fn_entry->analyzed_executable.backward_branch_quota = &fn_entry->prealloc_backward_branch_quota;
|
||||
fn_entry->analyzed_executable.fn_entry = fn_entry;
|
||||
fn_entry->ir_executable.fn_entry = fn_entry;
|
||||
fn_entry->ir_executable->fn_entry = fn_entry;
|
||||
fn_entry->fn_inline = inline_value;
|
||||
|
||||
return fn_entry;
|
||||
@ -3792,6 +3793,16 @@ ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf
|
||||
return variable_entry;
|
||||
}
|
||||
|
||||
static void validate_export_var_type(CodeGen *g, ZigType* type, AstNode *source_node) {
|
||||
switch (type->id) {
|
||||
case ZigTypeIdMetaType:
|
||||
add_node_error(g, source_node, buf_sprintf("cannot export variable of type 'type'"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
|
||||
AstNode *source_node = tld_var->base.source_node;
|
||||
AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration;
|
||||
@ -3881,6 +3892,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var, bool allow_lazy) {
|
||||
}
|
||||
|
||||
if (is_export) {
|
||||
validate_export_var_type(g, type, source_node);
|
||||
add_var_export(g, tld_var->var, tld_var->var->name, GlobalLinkageIdStrong);
|
||||
}
|
||||
|
||||
@ -4599,7 +4611,7 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) {
|
||||
assert(!fn_type->data.fn.is_generic);
|
||||
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
|
||||
|
||||
ZigType *block_return_type = ir_analyze(g, &fn->ir_executable,
|
||||
ZigType *block_return_type = ir_analyze(g, fn->ir_executable,
|
||||
&fn->analyzed_executable, fn_type_id->return_type, return_type_node);
|
||||
fn->src_implicit_return_type = block_return_type;
|
||||
|
||||
@ -4695,7 +4707,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
|
||||
assert(!fn_type->data.fn.is_generic);
|
||||
|
||||
ir_gen_fn(g, fn_table_entry);
|
||||
if (fn_table_entry->ir_executable.first_err_trace_msg != nullptr) {
|
||||
if (fn_table_entry->ir_executable->first_err_trace_msg != nullptr) {
|
||||
fn_table_entry->anal_state = FnAnalStateInvalid;
|
||||
return;
|
||||
}
|
||||
@ -4703,7 +4715,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) {
|
||||
fprintf(stderr, "\n");
|
||||
ast_render(stderr, fn_table_entry->body_node, 4);
|
||||
fprintf(stderr, "\nfn %s() { // (IR)\n", buf_ptr(&fn_table_entry->symbol_name));
|
||||
ir_print(g, stderr, &fn_table_entry->ir_executable, 4, IrPassSrc);
|
||||
ir_print(g, stderr, fn_table_entry->ir_executable, 4, IrPassSrc);
|
||||
fprintf(stderr, "}\n");
|
||||
}
|
||||
|
||||
@ -6442,20 +6454,31 @@ Error type_resolve(CodeGen *g, ZigType *ty, ResolveStatus status) {
|
||||
}
|
||||
|
||||
bool ir_get_var_is_comptime(ZigVar *var) {
|
||||
if (var->is_comptime_memoized)
|
||||
return var->is_comptime_memoized_value;
|
||||
|
||||
var->is_comptime_memoized = true;
|
||||
|
||||
// The is_comptime field can be left null, which means not comptime.
|
||||
if (var->is_comptime == nullptr)
|
||||
return false;
|
||||
if (var->is_comptime == nullptr) {
|
||||
var->is_comptime_memoized_value = false;
|
||||
return var->is_comptime_memoized_value;
|
||||
}
|
||||
// When the is_comptime field references an instruction that has to get analyzed, this
|
||||
// is the value.
|
||||
if (var->is_comptime->child != nullptr) {
|
||||
assert(var->is_comptime->child->value->type->id == ZigTypeIdBool);
|
||||
return var->is_comptime->child->value->data.x_bool;
|
||||
var->is_comptime_memoized_value = var->is_comptime->child->value->data.x_bool;
|
||||
var->is_comptime = nullptr;
|
||||
return var->is_comptime_memoized_value;
|
||||
}
|
||||
// As an optimization, is_comptime values which are constant are allowed
|
||||
// to be omitted from analysis. In this case, there is no child instruction
|
||||
// and we simply look at the unanalyzed const parent instruction.
|
||||
assert(var->is_comptime->value->type->id == ZigTypeIdBool);
|
||||
return var->is_comptime->value->data.x_bool;
|
||||
var->is_comptime_memoized_value = var->is_comptime->value->data.x_bool;
|
||||
var->is_comptime = nullptr;
|
||||
return var->is_comptime_memoized_value;
|
||||
}
|
||||
|
||||
bool const_values_equal_ptr(ZigValue *a, ZigValue *b) {
|
||||
|
301
src/codegen.cpp
301
src/codegen.cpp
@ -9563,7 +9563,11 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e
|
||||
case ZigTypeIdVoid:
|
||||
case ZigTypeIdUnreachable:
|
||||
case ZigTypeIdBool:
|
||||
g->c_want_stdbool = true;
|
||||
return;
|
||||
case ZigTypeIdInt:
|
||||
g->c_want_stdint = true;
|
||||
return;
|
||||
case ZigTypeIdFloat:
|
||||
return;
|
||||
case ZigTypeIdOpaque:
|
||||
@ -9644,7 +9648,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
|
||||
break;
|
||||
case ZigTypeIdBool:
|
||||
buf_init_from_str(out_buf, "bool");
|
||||
g->c_want_stdbool = true;
|
||||
break;
|
||||
case ZigTypeIdUnreachable:
|
||||
buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void");
|
||||
@ -9668,7 +9671,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu
|
||||
}
|
||||
break;
|
||||
case ZigTypeIdInt:
|
||||
g->c_want_stdint = true;
|
||||
buf_resize(out_buf, 0);
|
||||
buf_appendf(out_buf, "%sint%" PRIu32 "_t",
|
||||
type_entry->data.integral.is_signed ? "" : "u",
|
||||
@ -9780,113 +9782,7 @@ static Buf *preprocessor_mangle(Buf *src) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void gen_h_file(CodeGen *g) {
|
||||
GenH gen_h_data = {0};
|
||||
GenH *gen_h = &gen_h_data;
|
||||
|
||||
assert(!g->is_test_build);
|
||||
assert(!g->disable_gen_h);
|
||||
|
||||
Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name));
|
||||
|
||||
FILE *out_h = fopen(buf_ptr(out_h_path), "wb");
|
||||
if (!out_h)
|
||||
zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno));
|
||||
|
||||
Buf *export_macro = nullptr;
|
||||
if (g->is_dynamic) {
|
||||
export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
|
||||
buf_upcase(export_macro);
|
||||
}
|
||||
|
||||
Buf *extern_c_macro = preprocessor_mangle(buf_sprintf("%s_EXTERN_C", buf_ptr(g->root_out_name)));
|
||||
buf_upcase(extern_c_macro);
|
||||
|
||||
Buf h_buf = BUF_INIT;
|
||||
buf_resize(&h_buf, 0);
|
||||
for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
|
||||
ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i);
|
||||
|
||||
if (fn_table_entry->export_list.length == 0)
|
||||
continue;
|
||||
|
||||
FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
|
||||
|
||||
Buf return_type_c = BUF_INIT;
|
||||
get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c);
|
||||
|
||||
Buf *symbol_name;
|
||||
if (fn_table_entry->export_list.length == 0) {
|
||||
symbol_name = &fn_table_entry->symbol_name;
|
||||
} else {
|
||||
GlobalExport *fn_export = &fn_table_entry->export_list.items[0];
|
||||
symbol_name = &fn_export->name;
|
||||
}
|
||||
|
||||
buf_appendf(&h_buf, "%s %s %s(",
|
||||
buf_ptr(g->is_dynamic ? export_macro : extern_c_macro),
|
||||
buf_ptr(&return_type_c),
|
||||
buf_ptr(symbol_name));
|
||||
|
||||
Buf param_type_c = BUF_INIT;
|
||||
if (fn_type_id->param_count > 0) {
|
||||
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
|
||||
FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i];
|
||||
AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i);
|
||||
Buf *param_name = param_decl_node->data.param_decl.name;
|
||||
|
||||
const char *comma_str = (param_i == 0) ? "" : ", ";
|
||||
const char *restrict_str = param_info->is_noalias ? "restrict" : "";
|
||||
get_c_type(g, gen_h, param_info->type, ¶m_type_c);
|
||||
|
||||
if (param_info->type->id == ZigTypeIdArray) {
|
||||
// Arrays decay to pointers
|
||||
buf_appendf(&h_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c),
|
||||
restrict_str, buf_ptr(param_name));
|
||||
} else {
|
||||
buf_appendf(&h_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c),
|
||||
restrict_str, buf_ptr(param_name));
|
||||
}
|
||||
}
|
||||
buf_appendf(&h_buf, ")");
|
||||
} else {
|
||||
buf_appendf(&h_buf, "void)");
|
||||
}
|
||||
|
||||
buf_appendf(&h_buf, ";\n");
|
||||
|
||||
}
|
||||
|
||||
Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name)));
|
||||
buf_upcase(ifdef_dance_name);
|
||||
|
||||
fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name));
|
||||
fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name));
|
||||
|
||||
if (g->c_want_stdbool)
|
||||
fprintf(out_h, "#include <stdbool.h>\n");
|
||||
if (g->c_want_stdint)
|
||||
fprintf(out_h, "#include <stdint.h>\n");
|
||||
|
||||
fprintf(out_h, "\n");
|
||||
|
||||
fprintf(out_h, "#ifdef __cplusplus\n");
|
||||
fprintf(out_h, "#define %s extern \"C\"\n", buf_ptr(extern_c_macro));
|
||||
fprintf(out_h, "#else\n");
|
||||
fprintf(out_h, "#define %s\n", buf_ptr(extern_c_macro));
|
||||
fprintf(out_h, "#endif\n");
|
||||
fprintf(out_h, "\n");
|
||||
|
||||
if (g->is_dynamic) {
|
||||
fprintf(out_h, "#if defined(_WIN32)\n");
|
||||
fprintf(out_h, "#define %s %s __declspec(dllimport)\n", buf_ptr(export_macro), buf_ptr(extern_c_macro));
|
||||
fprintf(out_h, "#else\n");
|
||||
fprintf(out_h, "#define %s %s __attribute__((visibility (\"default\")))\n",
|
||||
buf_ptr(export_macro), buf_ptr(extern_c_macro));
|
||||
fprintf(out_h, "#endif\n");
|
||||
fprintf(out_h, "\n");
|
||||
}
|
||||
|
||||
static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) {
|
||||
for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) {
|
||||
ZigType *type_entry = gen_h->types_to_declare.at(type_i);
|
||||
switch (type_entry->id) {
|
||||
@ -9917,25 +9813,25 @@ static void gen_h_file(CodeGen *g) {
|
||||
|
||||
case ZigTypeIdEnum:
|
||||
if (type_entry->data.enumeration.layout == ContainerLayoutExtern) {
|
||||
fprintf(out_h, "enum %s {\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry)));
|
||||
for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) {
|
||||
TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i];
|
||||
Buf *value_buf = buf_alloc();
|
||||
bigint_append_buf(value_buf, &enum_field->value, 10);
|
||||
fprintf(out_h, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
|
||||
buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf));
|
||||
if (field_i != type_entry->data.enumeration.src_field_count - 1) {
|
||||
fprintf(out_h, ",");
|
||||
buf_appendf(out_buf, ",");
|
||||
}
|
||||
fprintf(out_h, "\n");
|
||||
buf_appendf(out_buf, "\n");
|
||||
}
|
||||
fprintf(out_h, "};\n\n");
|
||||
buf_appendf(out_buf, "};\n\n");
|
||||
} else {
|
||||
fprintf(out_h, "enum %s;\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry)));
|
||||
}
|
||||
break;
|
||||
case ZigTypeIdStruct:
|
||||
if (type_entry->data.structure.layout == ContainerLayoutExtern) {
|
||||
fprintf(out_h, "struct %s {\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry)));
|
||||
for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) {
|
||||
TypeStructField *struct_field = type_entry->data.structure.fields[field_i];
|
||||
|
||||
@ -9943,43 +9839,194 @@ static void gen_h_file(CodeGen *g) {
|
||||
get_c_type(g, gen_h, struct_field->type_entry, type_name_buf);
|
||||
|
||||
if (struct_field->type_entry->id == ZigTypeIdArray) {
|
||||
fprintf(out_h, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
|
||||
buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf),
|
||||
buf_ptr(struct_field->name),
|
||||
struct_field->type_entry->data.array.len);
|
||||
} else {
|
||||
fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
|
||||
buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name));
|
||||
}
|
||||
|
||||
}
|
||||
fprintf(out_h, "};\n\n");
|
||||
buf_appendf(out_buf, "};\n\n");
|
||||
} else {
|
||||
fprintf(out_h, "struct %s;\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
|
||||
}
|
||||
break;
|
||||
case ZigTypeIdUnion:
|
||||
if (type_entry->data.unionation.layout == ContainerLayoutExtern) {
|
||||
fprintf(out_h, "union %s {\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry)));
|
||||
for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) {
|
||||
TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i];
|
||||
|
||||
Buf *type_name_buf = buf_alloc();
|
||||
get_c_type(g, gen_h, union_field->type_entry, type_name_buf);
|
||||
fprintf(out_h, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
|
||||
buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name));
|
||||
}
|
||||
fprintf(out_h, "};\n\n");
|
||||
buf_appendf(out_buf, "};\n\n");
|
||||
} else {
|
||||
fprintf(out_h, "union %s;\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry)));
|
||||
}
|
||||
break;
|
||||
case ZigTypeIdOpaque:
|
||||
fprintf(out_h, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
|
||||
buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(out_h, "%s", buf_ptr(&h_buf));
|
||||
static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) {
|
||||
for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
|
||||
ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i);
|
||||
|
||||
fprintf(out_h, "\n#endif\n");
|
||||
if (fn_table_entry->export_list.length == 0)
|
||||
continue;
|
||||
|
||||
FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
|
||||
|
||||
Buf return_type_c = BUF_INIT;
|
||||
get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c);
|
||||
|
||||
Buf *symbol_name;
|
||||
if (fn_table_entry->export_list.length == 0) {
|
||||
symbol_name = &fn_table_entry->symbol_name;
|
||||
} else {
|
||||
GlobalExport *fn_export = &fn_table_entry->export_list.items[0];
|
||||
symbol_name = &fn_export->name;
|
||||
}
|
||||
|
||||
if (export_macro != nullptr) {
|
||||
buf_appendf(out_buf, "%s %s %s(",
|
||||
buf_ptr(export_macro),
|
||||
buf_ptr(&return_type_c),
|
||||
buf_ptr(symbol_name));
|
||||
} else {
|
||||
buf_appendf(out_buf, "%s %s(",
|
||||
buf_ptr(&return_type_c),
|
||||
buf_ptr(symbol_name));
|
||||
}
|
||||
|
||||
Buf param_type_c = BUF_INIT;
|
||||
if (fn_type_id->param_count > 0) {
|
||||
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
|
||||
FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i];
|
||||
AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i);
|
||||
Buf *param_name = param_decl_node->data.param_decl.name;
|
||||
|
||||
const char *comma_str = (param_i == 0) ? "" : ", ";
|
||||
const char *restrict_str = param_info->is_noalias ? "restrict" : "";
|
||||
get_c_type(g, gen_h, param_info->type, ¶m_type_c);
|
||||
|
||||
if (param_info->type->id == ZigTypeIdArray) {
|
||||
// Arrays decay to pointers
|
||||
buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c),
|
||||
restrict_str, buf_ptr(param_name));
|
||||
} else {
|
||||
buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c),
|
||||
restrict_str, buf_ptr(param_name));
|
||||
}
|
||||
}
|
||||
buf_appendf(out_buf, ")");
|
||||
} else {
|
||||
buf_appendf(out_buf, "void)");
|
||||
}
|
||||
|
||||
buf_appendf(out_buf, ";\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) {
|
||||
for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) {
|
||||
ZigVar* var = g->global_vars.at(exp_var_i)->var;
|
||||
if (var->export_list.length == 0)
|
||||
continue;
|
||||
|
||||
Buf var_type_c = BUF_INIT;
|
||||
get_c_type(g, gen_h, var->var_type, &var_type_c);
|
||||
|
||||
if (export_macro != nullptr) {
|
||||
buf_appendf(h_buf, "extern %s %s %s;\n",
|
||||
buf_ptr(export_macro),
|
||||
buf_ptr(&var_type_c),
|
||||
var->name);
|
||||
} else {
|
||||
buf_appendf(h_buf, "extern %s %s;\n",
|
||||
buf_ptr(&var_type_c),
|
||||
var->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_h_file(CodeGen *g) {
|
||||
GenH gen_h_data = {0};
|
||||
GenH *gen_h = &gen_h_data;
|
||||
|
||||
assert(!g->is_test_build);
|
||||
assert(!g->disable_gen_h);
|
||||
|
||||
Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name));
|
||||
|
||||
FILE *out_h = fopen(buf_ptr(out_h_path), "wb");
|
||||
if (!out_h)
|
||||
zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno));
|
||||
|
||||
Buf *export_macro = nullptr;
|
||||
if (g->is_dynamic) {
|
||||
export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name)));
|
||||
buf_upcase(export_macro);
|
||||
}
|
||||
|
||||
Buf fns_buf = BUF_INIT;
|
||||
buf_resize(&fns_buf, 0);
|
||||
gen_h_file_functions(g, gen_h, &fns_buf, export_macro);
|
||||
|
||||
Buf vars_buf = BUF_INIT;
|
||||
buf_resize(&vars_buf, 0);
|
||||
gen_h_file_variables(g, gen_h, &vars_buf, export_macro);
|
||||
|
||||
// Types will be populated by exported functions and variables so it has to run last.
|
||||
Buf types_buf = BUF_INIT;
|
||||
buf_resize(&types_buf, 0);
|
||||
gen_h_file_types(g, gen_h, &types_buf);
|
||||
|
||||
Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name)));
|
||||
buf_upcase(ifdef_dance_name);
|
||||
|
||||
fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name));
|
||||
fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name));
|
||||
|
||||
if (g->c_want_stdbool)
|
||||
fprintf(out_h, "#include <stdbool.h>\n");
|
||||
if (g->c_want_stdint)
|
||||
fprintf(out_h, "#include <stdint.h>\n");
|
||||
|
||||
fprintf(out_h, "\n");
|
||||
|
||||
if (g->is_dynamic) {
|
||||
fprintf(out_h, "#if defined(_WIN32)\n");
|
||||
fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro));
|
||||
fprintf(out_h, "#else\n");
|
||||
fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n",
|
||||
buf_ptr(export_macro));
|
||||
fprintf(out_h, "#endif\n");
|
||||
fprintf(out_h, "\n");
|
||||
}
|
||||
|
||||
fprintf(out_h, "%s", buf_ptr(&types_buf));
|
||||
|
||||
fprintf(out_h, "#ifdef __cplusplus\n");
|
||||
fprintf(out_h, "extern \"C\" {\n");
|
||||
fprintf(out_h, "#endif\n");
|
||||
fprintf(out_h, "\n");
|
||||
|
||||
fprintf(out_h, "%s\n", buf_ptr(&fns_buf));
|
||||
|
||||
fprintf(out_h, "#ifdef __cplusplus\n");
|
||||
fprintf(out_h, "} // extern \"C\"\n");
|
||||
fprintf(out_h, "#endif\n\n");
|
||||
|
||||
fprintf(out_h, "%s\n", buf_ptr(&vars_buf));
|
||||
|
||||
fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name));
|
||||
|
||||
if (fclose(out_h))
|
||||
zig_panic("unable to close h file: %s", strerror(errno));
|
||||
|
486
src/ir.cpp
486
src/ir.cpp
@ -41,6 +41,7 @@ struct IrAnalyze {
|
||||
ZigList<IrInstruction *> src_implicit_return_type_list;
|
||||
ZigList<IrSuspendPosition> resume_stack;
|
||||
IrBasicBlock *const_predecessor_bb;
|
||||
size_t ref_count;
|
||||
|
||||
// For the purpose of using in a debugger
|
||||
void dump();
|
||||
@ -74,6 +75,7 @@ enum ConstCastResultId {
|
||||
ConstCastResultIdPtrLens,
|
||||
ConstCastResultIdCV,
|
||||
ConstCastResultIdPtrSentinel,
|
||||
ConstCastResultIdIntShorten,
|
||||
};
|
||||
|
||||
struct ConstCastOnly;
|
||||
@ -100,6 +102,7 @@ struct ConstCastBadAllowsZero;
|
||||
struct ConstCastBadNullTermArrays;
|
||||
struct ConstCastBadCV;
|
||||
struct ConstCastPtrSentinel;
|
||||
struct ConstCastIntShorten;
|
||||
|
||||
struct ConstCastOnly {
|
||||
ConstCastResultId id;
|
||||
@ -120,6 +123,7 @@ struct ConstCastOnly {
|
||||
ConstCastBadNullTermArrays *sentinel_arrays;
|
||||
ConstCastBadCV *bad_cv;
|
||||
ConstCastPtrSentinel *bad_ptr_sentinel;
|
||||
ConstCastIntShorten *int_shorten;
|
||||
} data;
|
||||
};
|
||||
|
||||
@ -189,6 +193,11 @@ struct ConstCastPtrSentinel {
|
||||
ZigType *actual_type;
|
||||
};
|
||||
|
||||
struct ConstCastIntShorten {
|
||||
ZigType *wanted_type;
|
||||
ZigType *actual_type;
|
||||
};
|
||||
|
||||
static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope);
|
||||
static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *scope, LVal lval,
|
||||
ResultLoc *result_loc);
|
||||
@ -248,6 +257,381 @@ static IrInstruction *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_n
|
||||
IrInstruction *source_instr, IrInstruction *container_ptr, ZigType *container_type);
|
||||
static ResultLoc *no_result_loc(void);
|
||||
|
||||
static void destroy_instruction(IrInstruction *inst) {
|
||||
#ifdef ZIG_ENABLE_MEM_PROFILE
|
||||
const char *name = ir_instruction_type_str(inst->id);
|
||||
#else
|
||||
const char *name = nullptr;
|
||||
#endif
|
||||
switch (inst->id) {
|
||||
case IrInstructionIdInvalid:
|
||||
zig_unreachable();
|
||||
case IrInstructionIdReturn:
|
||||
return destroy(reinterpret_cast<IrInstructionReturn *>(inst), name);
|
||||
case IrInstructionIdConst:
|
||||
return destroy(reinterpret_cast<IrInstructionConst *>(inst), name);
|
||||
case IrInstructionIdBinOp:
|
||||
return destroy(reinterpret_cast<IrInstructionBinOp *>(inst), name);
|
||||
case IrInstructionIdMergeErrSets:
|
||||
return destroy(reinterpret_cast<IrInstructionMergeErrSets *>(inst), name);
|
||||
case IrInstructionIdDeclVarSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionDeclVarSrc *>(inst), name);
|
||||
case IrInstructionIdCast:
|
||||
return destroy(reinterpret_cast<IrInstructionCast *>(inst), name);
|
||||
case IrInstructionIdCallSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionCallSrc *>(inst), name);
|
||||
case IrInstructionIdCallGen:
|
||||
return destroy(reinterpret_cast<IrInstructionCallGen *>(inst), name);
|
||||
case IrInstructionIdUnOp:
|
||||
return destroy(reinterpret_cast<IrInstructionUnOp *>(inst), name);
|
||||
case IrInstructionIdCondBr:
|
||||
return destroy(reinterpret_cast<IrInstructionCondBr *>(inst), name);
|
||||
case IrInstructionIdBr:
|
||||
return destroy(reinterpret_cast<IrInstructionBr *>(inst), name);
|
||||
case IrInstructionIdPhi:
|
||||
return destroy(reinterpret_cast<IrInstructionPhi *>(inst), name);
|
||||
case IrInstructionIdContainerInitList:
|
||||
return destroy(reinterpret_cast<IrInstructionContainerInitList *>(inst), name);
|
||||
case IrInstructionIdContainerInitFields:
|
||||
return destroy(reinterpret_cast<IrInstructionContainerInitFields *>(inst), name);
|
||||
case IrInstructionIdUnreachable:
|
||||
return destroy(reinterpret_cast<IrInstructionUnreachable *>(inst), name);
|
||||
case IrInstructionIdElemPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionElemPtr *>(inst), name);
|
||||
case IrInstructionIdVarPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionVarPtr *>(inst), name);
|
||||
case IrInstructionIdReturnPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionReturnPtr *>(inst), name);
|
||||
case IrInstructionIdLoadPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionLoadPtr *>(inst), name);
|
||||
case IrInstructionIdLoadPtrGen:
|
||||
return destroy(reinterpret_cast<IrInstructionLoadPtrGen *>(inst), name);
|
||||
case IrInstructionIdStorePtr:
|
||||
return destroy(reinterpret_cast<IrInstructionStorePtr *>(inst), name);
|
||||
case IrInstructionIdVectorStoreElem:
|
||||
return destroy(reinterpret_cast<IrInstructionVectorStoreElem *>(inst), name);
|
||||
case IrInstructionIdTypeOf:
|
||||
return destroy(reinterpret_cast<IrInstructionTypeOf *>(inst), name);
|
||||
case IrInstructionIdFieldPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionFieldPtr *>(inst), name);
|
||||
case IrInstructionIdStructFieldPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionStructFieldPtr *>(inst), name);
|
||||
case IrInstructionIdUnionFieldPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionUnionFieldPtr *>(inst), name);
|
||||
case IrInstructionIdSetCold:
|
||||
return destroy(reinterpret_cast<IrInstructionSetCold *>(inst), name);
|
||||
case IrInstructionIdSetRuntimeSafety:
|
||||
return destroy(reinterpret_cast<IrInstructionSetRuntimeSafety *>(inst), name);
|
||||
case IrInstructionIdSetFloatMode:
|
||||
return destroy(reinterpret_cast<IrInstructionSetFloatMode *>(inst), name);
|
||||
case IrInstructionIdArrayType:
|
||||
return destroy(reinterpret_cast<IrInstructionArrayType *>(inst), name);
|
||||
case IrInstructionIdSliceType:
|
||||
return destroy(reinterpret_cast<IrInstructionSliceType *>(inst), name);
|
||||
case IrInstructionIdAnyFrameType:
|
||||
return destroy(reinterpret_cast<IrInstructionAnyFrameType *>(inst), name);
|
||||
case IrInstructionIdGlobalAsm:
|
||||
return destroy(reinterpret_cast<IrInstructionGlobalAsm *>(inst), name);
|
||||
case IrInstructionIdAsm:
|
||||
return destroy(reinterpret_cast<IrInstructionAsm *>(inst), name);
|
||||
case IrInstructionIdSizeOf:
|
||||
return destroy(reinterpret_cast<IrInstructionSizeOf *>(inst), name);
|
||||
case IrInstructionIdTestNonNull:
|
||||
return destroy(reinterpret_cast<IrInstructionTestNonNull *>(inst), name);
|
||||
case IrInstructionIdOptionalUnwrapPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionOptionalUnwrapPtr *>(inst), name);
|
||||
case IrInstructionIdPopCount:
|
||||
return destroy(reinterpret_cast<IrInstructionPopCount *>(inst), name);
|
||||
case IrInstructionIdClz:
|
||||
return destroy(reinterpret_cast<IrInstructionClz *>(inst), name);
|
||||
case IrInstructionIdCtz:
|
||||
return destroy(reinterpret_cast<IrInstructionCtz *>(inst), name);
|
||||
case IrInstructionIdBswap:
|
||||
return destroy(reinterpret_cast<IrInstructionBswap *>(inst), name);
|
||||
case IrInstructionIdBitReverse:
|
||||
return destroy(reinterpret_cast<IrInstructionBitReverse *>(inst), name);
|
||||
case IrInstructionIdSwitchBr:
|
||||
return destroy(reinterpret_cast<IrInstructionSwitchBr *>(inst), name);
|
||||
case IrInstructionIdSwitchVar:
|
||||
return destroy(reinterpret_cast<IrInstructionSwitchVar *>(inst), name);
|
||||
case IrInstructionIdSwitchElseVar:
|
||||
return destroy(reinterpret_cast<IrInstructionSwitchElseVar *>(inst), name);
|
||||
case IrInstructionIdSwitchTarget:
|
||||
return destroy(reinterpret_cast<IrInstructionSwitchTarget *>(inst), name);
|
||||
case IrInstructionIdUnionTag:
|
||||
return destroy(reinterpret_cast<IrInstructionUnionTag *>(inst), name);
|
||||
case IrInstructionIdImport:
|
||||
return destroy(reinterpret_cast<IrInstructionImport *>(inst), name);
|
||||
case IrInstructionIdRef:
|
||||
return destroy(reinterpret_cast<IrInstructionRef *>(inst), name);
|
||||
case IrInstructionIdRefGen:
|
||||
return destroy(reinterpret_cast<IrInstructionRefGen *>(inst), name);
|
||||
case IrInstructionIdCompileErr:
|
||||
return destroy(reinterpret_cast<IrInstructionCompileErr *>(inst), name);
|
||||
case IrInstructionIdCompileLog:
|
||||
return destroy(reinterpret_cast<IrInstructionCompileLog *>(inst), name);
|
||||
case IrInstructionIdErrName:
|
||||
return destroy(reinterpret_cast<IrInstructionErrName *>(inst), name);
|
||||
case IrInstructionIdCImport:
|
||||
return destroy(reinterpret_cast<IrInstructionCImport *>(inst), name);
|
||||
case IrInstructionIdCInclude:
|
||||
return destroy(reinterpret_cast<IrInstructionCInclude *>(inst), name);
|
||||
case IrInstructionIdCDefine:
|
||||
return destroy(reinterpret_cast<IrInstructionCDefine *>(inst), name);
|
||||
case IrInstructionIdCUndef:
|
||||
return destroy(reinterpret_cast<IrInstructionCUndef *>(inst), name);
|
||||
case IrInstructionIdEmbedFile:
|
||||
return destroy(reinterpret_cast<IrInstructionEmbedFile *>(inst), name);
|
||||
case IrInstructionIdCmpxchgSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionCmpxchgSrc *>(inst), name);
|
||||
case IrInstructionIdCmpxchgGen:
|
||||
return destroy(reinterpret_cast<IrInstructionCmpxchgGen *>(inst), name);
|
||||
case IrInstructionIdFence:
|
||||
return destroy(reinterpret_cast<IrInstructionFence *>(inst), name);
|
||||
case IrInstructionIdTruncate:
|
||||
return destroy(reinterpret_cast<IrInstructionTruncate *>(inst), name);
|
||||
case IrInstructionIdIntCast:
|
||||
return destroy(reinterpret_cast<IrInstructionIntCast *>(inst), name);
|
||||
case IrInstructionIdFloatCast:
|
||||
return destroy(reinterpret_cast<IrInstructionFloatCast *>(inst), name);
|
||||
case IrInstructionIdErrSetCast:
|
||||
return destroy(reinterpret_cast<IrInstructionErrSetCast *>(inst), name);
|
||||
case IrInstructionIdFromBytes:
|
||||
return destroy(reinterpret_cast<IrInstructionFromBytes *>(inst), name);
|
||||
case IrInstructionIdToBytes:
|
||||
return destroy(reinterpret_cast<IrInstructionToBytes *>(inst), name);
|
||||
case IrInstructionIdIntToFloat:
|
||||
return destroy(reinterpret_cast<IrInstructionIntToFloat *>(inst), name);
|
||||
case IrInstructionIdFloatToInt:
|
||||
return destroy(reinterpret_cast<IrInstructionFloatToInt *>(inst), name);
|
||||
case IrInstructionIdBoolToInt:
|
||||
return destroy(reinterpret_cast<IrInstructionBoolToInt *>(inst), name);
|
||||
case IrInstructionIdIntType:
|
||||
return destroy(reinterpret_cast<IrInstructionIntType *>(inst), name);
|
||||
case IrInstructionIdVectorType:
|
||||
return destroy(reinterpret_cast<IrInstructionVectorType *>(inst), name);
|
||||
case IrInstructionIdShuffleVector:
|
||||
return destroy(reinterpret_cast<IrInstructionShuffleVector *>(inst), name);
|
||||
case IrInstructionIdSplatSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionSplatSrc *>(inst), name);
|
||||
case IrInstructionIdSplatGen:
|
||||
return destroy(reinterpret_cast<IrInstructionSplatGen *>(inst), name);
|
||||
case IrInstructionIdBoolNot:
|
||||
return destroy(reinterpret_cast<IrInstructionBoolNot *>(inst), name);
|
||||
case IrInstructionIdMemset:
|
||||
return destroy(reinterpret_cast<IrInstructionMemset *>(inst), name);
|
||||
case IrInstructionIdMemcpy:
|
||||
return destroy(reinterpret_cast<IrInstructionMemcpy *>(inst), name);
|
||||
case IrInstructionIdSliceSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionSliceSrc *>(inst), name);
|
||||
case IrInstructionIdSliceGen:
|
||||
return destroy(reinterpret_cast<IrInstructionSliceGen *>(inst), name);
|
||||
case IrInstructionIdMemberCount:
|
||||
return destroy(reinterpret_cast<IrInstructionMemberCount *>(inst), name);
|
||||
case IrInstructionIdMemberType:
|
||||
return destroy(reinterpret_cast<IrInstructionMemberType *>(inst), name);
|
||||
case IrInstructionIdMemberName:
|
||||
return destroy(reinterpret_cast<IrInstructionMemberName *>(inst), name);
|
||||
case IrInstructionIdBreakpoint:
|
||||
return destroy(reinterpret_cast<IrInstructionBreakpoint *>(inst), name);
|
||||
case IrInstructionIdReturnAddress:
|
||||
return destroy(reinterpret_cast<IrInstructionReturnAddress *>(inst), name);
|
||||
case IrInstructionIdFrameAddress:
|
||||
return destroy(reinterpret_cast<IrInstructionFrameAddress *>(inst), name);
|
||||
case IrInstructionIdFrameHandle:
|
||||
return destroy(reinterpret_cast<IrInstructionFrameHandle *>(inst), name);
|
||||
case IrInstructionIdFrameType:
|
||||
return destroy(reinterpret_cast<IrInstructionFrameType *>(inst), name);
|
||||
case IrInstructionIdFrameSizeSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionFrameSizeSrc *>(inst), name);
|
||||
case IrInstructionIdFrameSizeGen:
|
||||
return destroy(reinterpret_cast<IrInstructionFrameSizeGen *>(inst), name);
|
||||
case IrInstructionIdAlignOf:
|
||||
return destroy(reinterpret_cast<IrInstructionAlignOf *>(inst), name);
|
||||
case IrInstructionIdOverflowOp:
|
||||
return destroy(reinterpret_cast<IrInstructionOverflowOp *>(inst), name);
|
||||
case IrInstructionIdTestErrSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionTestErrSrc *>(inst), name);
|
||||
case IrInstructionIdTestErrGen:
|
||||
return destroy(reinterpret_cast<IrInstructionTestErrGen *>(inst), name);
|
||||
case IrInstructionIdUnwrapErrCode:
|
||||
return destroy(reinterpret_cast<IrInstructionUnwrapErrCode *>(inst), name);
|
||||
case IrInstructionIdUnwrapErrPayload:
|
||||
return destroy(reinterpret_cast<IrInstructionUnwrapErrPayload *>(inst), name);
|
||||
case IrInstructionIdOptionalWrap:
|
||||
return destroy(reinterpret_cast<IrInstructionOptionalWrap *>(inst), name);
|
||||
case IrInstructionIdErrWrapCode:
|
||||
return destroy(reinterpret_cast<IrInstructionErrWrapCode *>(inst), name);
|
||||
case IrInstructionIdErrWrapPayload:
|
||||
return destroy(reinterpret_cast<IrInstructionErrWrapPayload *>(inst), name);
|
||||
case IrInstructionIdFnProto:
|
||||
return destroy(reinterpret_cast<IrInstructionFnProto *>(inst), name);
|
||||
case IrInstructionIdTestComptime:
|
||||
return destroy(reinterpret_cast<IrInstructionTestComptime *>(inst), name);
|
||||
case IrInstructionIdPtrCastSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionPtrCastSrc *>(inst), name);
|
||||
case IrInstructionIdPtrCastGen:
|
||||
return destroy(reinterpret_cast<IrInstructionPtrCastGen *>(inst), name);
|
||||
case IrInstructionIdBitCastSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionBitCastSrc *>(inst), name);
|
||||
case IrInstructionIdBitCastGen:
|
||||
return destroy(reinterpret_cast<IrInstructionBitCastGen *>(inst), name);
|
||||
case IrInstructionIdWidenOrShorten:
|
||||
return destroy(reinterpret_cast<IrInstructionWidenOrShorten *>(inst), name);
|
||||
case IrInstructionIdPtrToInt:
|
||||
return destroy(reinterpret_cast<IrInstructionPtrToInt *>(inst), name);
|
||||
case IrInstructionIdIntToPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionIntToPtr *>(inst), name);
|
||||
case IrInstructionIdIntToEnum:
|
||||
return destroy(reinterpret_cast<IrInstructionIntToEnum *>(inst), name);
|
||||
case IrInstructionIdIntToErr:
|
||||
return destroy(reinterpret_cast<IrInstructionIntToErr *>(inst), name);
|
||||
case IrInstructionIdErrToInt:
|
||||
return destroy(reinterpret_cast<IrInstructionErrToInt *>(inst), name);
|
||||
case IrInstructionIdCheckSwitchProngs:
|
||||
return destroy(reinterpret_cast<IrInstructionCheckSwitchProngs *>(inst), name);
|
||||
case IrInstructionIdCheckStatementIsVoid:
|
||||
return destroy(reinterpret_cast<IrInstructionCheckStatementIsVoid *>(inst), name);
|
||||
case IrInstructionIdTypeName:
|
||||
return destroy(reinterpret_cast<IrInstructionTypeName *>(inst), name);
|
||||
case IrInstructionIdTagName:
|
||||
return destroy(reinterpret_cast<IrInstructionTagName *>(inst), name);
|
||||
case IrInstructionIdPtrType:
|
||||
return destroy(reinterpret_cast<IrInstructionPtrType *>(inst), name);
|
||||
case IrInstructionIdDeclRef:
|
||||
return destroy(reinterpret_cast<IrInstructionDeclRef *>(inst), name);
|
||||
case IrInstructionIdPanic:
|
||||
return destroy(reinterpret_cast<IrInstructionPanic *>(inst), name);
|
||||
case IrInstructionIdFieldParentPtr:
|
||||
return destroy(reinterpret_cast<IrInstructionFieldParentPtr *>(inst), name);
|
||||
case IrInstructionIdByteOffsetOf:
|
||||
return destroy(reinterpret_cast<IrInstructionByteOffsetOf *>(inst), name);
|
||||
case IrInstructionIdBitOffsetOf:
|
||||
return destroy(reinterpret_cast<IrInstructionBitOffsetOf *>(inst), name);
|
||||
case IrInstructionIdTypeInfo:
|
||||
return destroy(reinterpret_cast<IrInstructionTypeInfo *>(inst), name);
|
||||
case IrInstructionIdType:
|
||||
return destroy(reinterpret_cast<IrInstructionType *>(inst), name);
|
||||
case IrInstructionIdHasField:
|
||||
return destroy(reinterpret_cast<IrInstructionHasField *>(inst), name);
|
||||
case IrInstructionIdTypeId:
|
||||
return destroy(reinterpret_cast<IrInstructionTypeId *>(inst), name);
|
||||
case IrInstructionIdSetEvalBranchQuota:
|
||||
return destroy(reinterpret_cast<IrInstructionSetEvalBranchQuota *>(inst), name);
|
||||
case IrInstructionIdAlignCast:
|
||||
return destroy(reinterpret_cast<IrInstructionAlignCast *>(inst), name);
|
||||
case IrInstructionIdImplicitCast:
|
||||
return destroy(reinterpret_cast<IrInstructionImplicitCast *>(inst), name);
|
||||
case IrInstructionIdResolveResult:
|
||||
return destroy(reinterpret_cast<IrInstructionResolveResult *>(inst), name);
|
||||
case IrInstructionIdResetResult:
|
||||
return destroy(reinterpret_cast<IrInstructionResetResult *>(inst), name);
|
||||
case IrInstructionIdOpaqueType:
|
||||
return destroy(reinterpret_cast<IrInstructionOpaqueType *>(inst), name);
|
||||
case IrInstructionIdSetAlignStack:
|
||||
return destroy(reinterpret_cast<IrInstructionSetAlignStack *>(inst), name);
|
||||
case IrInstructionIdArgType:
|
||||
return destroy(reinterpret_cast<IrInstructionArgType *>(inst), name);
|
||||
case IrInstructionIdTagType:
|
||||
return destroy(reinterpret_cast<IrInstructionTagType *>(inst), name);
|
||||
case IrInstructionIdExport:
|
||||
return destroy(reinterpret_cast<IrInstructionExport *>(inst), name);
|
||||
case IrInstructionIdErrorReturnTrace:
|
||||
return destroy(reinterpret_cast<IrInstructionErrorReturnTrace *>(inst), name);
|
||||
case IrInstructionIdErrorUnion:
|
||||
return destroy(reinterpret_cast<IrInstructionErrorUnion *>(inst), name);
|
||||
case IrInstructionIdAtomicRmw:
|
||||
return destroy(reinterpret_cast<IrInstructionAtomicRmw *>(inst), name);
|
||||
case IrInstructionIdSaveErrRetAddr:
|
||||
return destroy(reinterpret_cast<IrInstructionSaveErrRetAddr *>(inst), name);
|
||||
case IrInstructionIdAddImplicitReturnType:
|
||||
return destroy(reinterpret_cast<IrInstructionAddImplicitReturnType *>(inst), name);
|
||||
case IrInstructionIdFloatOp:
|
||||
return destroy(reinterpret_cast<IrInstructionFloatOp *>(inst), name);
|
||||
case IrInstructionIdMulAdd:
|
||||
return destroy(reinterpret_cast<IrInstructionMulAdd *>(inst), name);
|
||||
case IrInstructionIdAtomicLoad:
|
||||
return destroy(reinterpret_cast<IrInstructionAtomicLoad *>(inst), name);
|
||||
case IrInstructionIdAtomicStore:
|
||||
return destroy(reinterpret_cast<IrInstructionAtomicStore *>(inst), name);
|
||||
case IrInstructionIdEnumToInt:
|
||||
return destroy(reinterpret_cast<IrInstructionEnumToInt *>(inst), name);
|
||||
case IrInstructionIdCheckRuntimeScope:
|
||||
return destroy(reinterpret_cast<IrInstructionCheckRuntimeScope *>(inst), name);
|
||||
case IrInstructionIdDeclVarGen:
|
||||
return destroy(reinterpret_cast<IrInstructionDeclVarGen *>(inst), name);
|
||||
case IrInstructionIdArrayToVector:
|
||||
return destroy(reinterpret_cast<IrInstructionArrayToVector *>(inst), name);
|
||||
case IrInstructionIdVectorToArray:
|
||||
return destroy(reinterpret_cast<IrInstructionVectorToArray *>(inst), name);
|
||||
case IrInstructionIdPtrOfArrayToSlice:
|
||||
return destroy(reinterpret_cast<IrInstructionPtrOfArrayToSlice *>(inst), name);
|
||||
case IrInstructionIdAssertZero:
|
||||
return destroy(reinterpret_cast<IrInstructionAssertZero *>(inst), name);
|
||||
case IrInstructionIdAssertNonNull:
|
||||
return destroy(reinterpret_cast<IrInstructionAssertNonNull *>(inst), name);
|
||||
case IrInstructionIdResizeSlice:
|
||||
return destroy(reinterpret_cast<IrInstructionResizeSlice *>(inst), name);
|
||||
case IrInstructionIdHasDecl:
|
||||
return destroy(reinterpret_cast<IrInstructionHasDecl *>(inst), name);
|
||||
case IrInstructionIdUndeclaredIdent:
|
||||
return destroy(reinterpret_cast<IrInstructionUndeclaredIdent *>(inst), name);
|
||||
case IrInstructionIdAllocaSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionAllocaSrc *>(inst), name);
|
||||
case IrInstructionIdAllocaGen:
|
||||
return destroy(reinterpret_cast<IrInstructionAllocaGen *>(inst), name);
|
||||
case IrInstructionIdEndExpr:
|
||||
return destroy(reinterpret_cast<IrInstructionEndExpr *>(inst), name);
|
||||
case IrInstructionIdUnionInitNamedField:
|
||||
return destroy(reinterpret_cast<IrInstructionUnionInitNamedField *>(inst), name);
|
||||
case IrInstructionIdSuspendBegin:
|
||||
return destroy(reinterpret_cast<IrInstructionSuspendBegin *>(inst), name);
|
||||
case IrInstructionIdSuspendFinish:
|
||||
return destroy(reinterpret_cast<IrInstructionSuspendFinish *>(inst), name);
|
||||
case IrInstructionIdResume:
|
||||
return destroy(reinterpret_cast<IrInstructionResume *>(inst), name);
|
||||
case IrInstructionIdAwaitSrc:
|
||||
return destroy(reinterpret_cast<IrInstructionAwaitSrc *>(inst), name);
|
||||
case IrInstructionIdAwaitGen:
|
||||
return destroy(reinterpret_cast<IrInstructionAwaitGen *>(inst), name);
|
||||
case IrInstructionIdSpillBegin:
|
||||
return destroy(reinterpret_cast<IrInstructionSpillBegin *>(inst), name);
|
||||
case IrInstructionIdSpillEnd:
|
||||
return destroy(reinterpret_cast<IrInstructionSpillEnd *>(inst), name);
|
||||
case IrInstructionIdVectorExtractElem:
|
||||
return destroy(reinterpret_cast<IrInstructionVectorExtractElem *>(inst), name);
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
||||
static void ira_ref(IrAnalyze *ira) {
|
||||
ira->ref_count += 1;
|
||||
}
|
||||
static void ira_deref(IrAnalyze *ira) {
|
||||
if (ira->ref_count > 1) {
|
||||
ira->ref_count -= 1;
|
||||
return;
|
||||
}
|
||||
assert(ira->ref_count != 0);
|
||||
|
||||
for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) {
|
||||
IrBasicBlock *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i];
|
||||
for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) {
|
||||
IrInstruction *pass1_inst = pass1_bb->instruction_list.items[inst_i];
|
||||
destroy_instruction(pass1_inst);
|
||||
}
|
||||
destroy(pass1_bb, "IrBasicBlock");
|
||||
}
|
||||
ira->old_irb.exec->basic_block_list.deinit();
|
||||
ira->old_irb.exec->tld_list.deinit();
|
||||
// cannot destroy here because of var->owner_exec
|
||||
//destroy(ira->old_irb.exec, "IrExecutablePass1");
|
||||
ira->src_implicit_return_type_list.deinit();
|
||||
ira->resume_stack.deinit();
|
||||
ira->exec_context.mem_slot_list.deinit();
|
||||
destroy(ira, "IrAnalyze");
|
||||
}
|
||||
|
||||
static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) {
|
||||
assert(get_src_ptr_type(const_val->type) != nullptr);
|
||||
assert(const_val->special == ConstValSpecialStatic);
|
||||
@ -4186,7 +4570,7 @@ static IrInstruction *ir_gen_bool_and(IrBuilder *irb, Scope *scope, AstNode *nod
|
||||
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
||||
incoming_values[0] = val1;
|
||||
incoming_values[1] = val2;
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
|
||||
incoming_blocks[0] = post_val1_block;
|
||||
incoming_blocks[1] = post_val2_block;
|
||||
|
||||
@ -4277,7 +4661,7 @@ static IrInstruction *ir_gen_orelse(IrBuilder *irb, Scope *parent_scope, AstNode
|
||||
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
||||
incoming_values[0] = null_result;
|
||||
incoming_values[1] = unwrapped_payload;
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
|
||||
incoming_blocks[0] = after_null_block;
|
||||
incoming_blocks[1] = after_ok_block;
|
||||
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
||||
@ -6044,7 +6428,7 @@ static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode
|
||||
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
||||
incoming_values[0] = then_expr_result;
|
||||
incoming_values[1] = else_expr_result;
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
|
||||
incoming_blocks[0] = after_then_block;
|
||||
incoming_blocks[1] = after_else_block;
|
||||
|
||||
@ -7398,7 +7782,7 @@ static IrInstruction *ir_gen_if_optional_expr(IrBuilder *irb, Scope *scope, AstN
|
||||
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
||||
incoming_values[0] = then_expr_result;
|
||||
incoming_values[1] = else_expr_result;
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
|
||||
incoming_blocks[0] = after_then_block;
|
||||
incoming_blocks[1] = after_else_block;
|
||||
|
||||
@ -7495,7 +7879,7 @@ static IrInstruction *ir_gen_if_err_expr(IrBuilder *irb, Scope *scope, AstNode *
|
||||
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
||||
incoming_values[0] = then_expr_result;
|
||||
incoming_values[1] = else_expr_result;
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
|
||||
incoming_blocks[0] = after_then_block;
|
||||
incoming_blocks[1] = after_else_block;
|
||||
|
||||
@ -8092,7 +8476,7 @@ static IrInstruction *ir_gen_catch(IrBuilder *irb, Scope *parent_scope, AstNode
|
||||
IrInstruction **incoming_values = allocate<IrInstruction *>(2);
|
||||
incoming_values[0] = err_result;
|
||||
incoming_values[1] = unwrapped_payload;
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2);
|
||||
IrBasicBlock **incoming_blocks = allocate<IrBasicBlock *>(2, "IrBasicBlock *");
|
||||
incoming_blocks[0] = after_err_block;
|
||||
incoming_blocks[1] = after_ok_block;
|
||||
IrInstruction *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent);
|
||||
@ -8680,7 +9064,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
|
||||
bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) {
|
||||
assert(fn_entry);
|
||||
|
||||
IrExecutable *ir_executable = &fn_entry->ir_executable;
|
||||
IrExecutable *ir_executable = fn_entry->ir_executable;
|
||||
AstNode *body_node = fn_entry->body_node;
|
||||
|
||||
assert(fn_entry->child_scope);
|
||||
@ -10224,6 +10608,14 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
|
||||
return result;
|
||||
}
|
||||
|
||||
if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) {
|
||||
result.id = ConstCastResultIdIntShorten;
|
||||
result.data.int_shorten = allocate_nonzero<ConstCastIntShorten>(1);
|
||||
result.data.int_shorten->wanted_type = wanted_type;
|
||||
result.data.int_shorten->actual_type = actual_type;
|
||||
return result;
|
||||
}
|
||||
|
||||
result.id = ConstCastResultIdType;
|
||||
result.data.type_mismatch = allocate_nonzero<ConstCastTypeMismatch>(1);
|
||||
result.data.type_mismatch->wanted_type = wanted_type;
|
||||
@ -11490,7 +11882,7 @@ ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
|
||||
if (expected_type != nullptr && type_is_invalid(expected_type))
|
||||
return codegen->invalid_instruction->value;
|
||||
|
||||
IrExecutable *ir_executable = allocate<IrExecutable>(1);
|
||||
IrExecutable *ir_executable = allocate<IrExecutable>(1, "IrExecutablePass1");
|
||||
ir_executable->source_node = source_node;
|
||||
ir_executable->parent_exec = parent_exec;
|
||||
ir_executable->name = exec_name;
|
||||
@ -11512,7 +11904,7 @@ ZigValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node,
|
||||
ir_print(codegen, stderr, ir_executable, 2, IrPassSrc);
|
||||
fprintf(stderr, "}\n");
|
||||
}
|
||||
IrExecutable *analyzed_executable = allocate<IrExecutable>(1);
|
||||
IrExecutable *analyzed_executable = allocate<IrExecutable>(1, "IrExecutablePass2");
|
||||
analyzed_executable->source_node = source_node;
|
||||
analyzed_executable->parent_exec = parent_exec;
|
||||
analyzed_executable->source_exec = ir_executable;
|
||||
@ -12641,6 +13033,17 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
|
||||
add_error_note(ira->codegen, parent_msg, source_node,
|
||||
buf_sprintf("calling convention mismatch"));
|
||||
break;
|
||||
case ConstCastResultIdIntShorten: {
|
||||
ZigType *wanted_type = cast_result->data.int_shorten->wanted_type;
|
||||
ZigType *actual_type = cast_result->data.int_shorten->actual_type;
|
||||
const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned";
|
||||
const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned";
|
||||
add_error_note(ira->codegen, parent_msg, source_node,
|
||||
buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values",
|
||||
wanted_signed, wanted_type->data.integral.bit_count,
|
||||
actual_signed, actual_type->data.integral.bit_count));
|
||||
break;
|
||||
}
|
||||
case ConstCastResultIdFnAlign: // TODO
|
||||
case ConstCastResultIdFnVarArgs: // TODO
|
||||
case ConstCastResultIdFnReturnType: // TODO
|
||||
@ -15597,6 +16000,7 @@ static IrInstruction *ir_analyze_instruction_decl_var(IrAnalyze *ira,
|
||||
assert(var->mem_slot_index < ira->exec_context.mem_slot_list.length);
|
||||
ZigValue *mem_slot = ira->exec_context.mem_slot_list.at(var->mem_slot_index);
|
||||
copy_const_val(mem_slot, init_val, !is_comptime_var || var->gen_is_const);
|
||||
ira_ref(var->owner_exec->analysis);
|
||||
|
||||
if (is_comptime_var || (var_class_requires_const && var->gen_is_const)) {
|
||||
return ir_const_void(ira, &decl_var_instruction->base);
|
||||
@ -15869,8 +16273,8 @@ static IrInstruction *ir_analyze_instruction_error_union(IrAnalyze *ira,
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1);
|
||||
lazy_err_union_type->ira = ira;
|
||||
LazyValueErrUnionType *lazy_err_union_type = allocate<LazyValueErrUnionType>(1, "LazyValueErrUnionType");
|
||||
lazy_err_union_type->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_err_union_type->base;
|
||||
lazy_err_union_type->base.id = LazyValueIdErrUnionType;
|
||||
|
||||
@ -17368,8 +17772,8 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
|
||||
if (type_is_invalid(impl_fn->type_entry))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
impl_fn->ir_executable.source_node = call_instruction->base.source_node;
|
||||
impl_fn->ir_executable.parent_exec = ira->new_irb.exec;
|
||||
impl_fn->ir_executable->source_node = call_instruction->base.source_node;
|
||||
impl_fn->ir_executable->parent_exec = ira->new_irb.exec;
|
||||
impl_fn->analyzed_executable.source_node = call_instruction->base.source_node;
|
||||
impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec;
|
||||
impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota;
|
||||
@ -17722,8 +18126,8 @@ static IrInstruction *ir_analyze_optional_type(IrAnalyze *ira, IrInstructionUnOp
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValueOptType *lazy_opt_type = allocate<LazyValueOptType>(1);
|
||||
lazy_opt_type->ira = ira;
|
||||
LazyValueOptType *lazy_opt_type = allocate<LazyValueOptType>(1, "LazyValueOptType");
|
||||
lazy_opt_type->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_opt_type->base;
|
||||
lazy_opt_type->base.id = LazyValueIdOptType;
|
||||
|
||||
@ -19668,8 +20072,8 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira,
|
||||
IrInstruction *result = ir_const(ira, &slice_type_instruction->base, ira->codegen->builtin_types.entry_type);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValueSliceType *lazy_slice_type = allocate<LazyValueSliceType>(1);
|
||||
lazy_slice_type->ira = ira;
|
||||
LazyValueSliceType *lazy_slice_type = allocate<LazyValueSliceType>(1, "LazyValueSliceType");
|
||||
lazy_slice_type->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_slice_type->base;
|
||||
lazy_slice_type->base.id = LazyValueIdSliceType;
|
||||
|
||||
@ -19828,8 +20232,8 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstructi
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1);
|
||||
lazy_size_of->ira = ira;
|
||||
LazyValueSizeOf *lazy_size_of = allocate<LazyValueSizeOf>(1, "LazyValueSizeOf");
|
||||
lazy_size_of->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_size_of->base;
|
||||
lazy_size_of->base.id = LazyValueIdSizeOf;
|
||||
|
||||
@ -24556,8 +24960,8 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_num_lit_int);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValueAlignOf *lazy_align_of = allocate<LazyValueAlignOf>(1);
|
||||
lazy_align_of->ira = ira;
|
||||
LazyValueAlignOf *lazy_align_of = allocate<LazyValueAlignOf>(1, "LazyValueAlignOf");
|
||||
lazy_align_of->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_align_of->base;
|
||||
lazy_align_of->base.id = LazyValueIdAlignOf;
|
||||
|
||||
@ -25040,8 +25444,8 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1);
|
||||
lazy_fn_type->ira = ira;
|
||||
LazyValueFnType *lazy_fn_type = allocate<LazyValueFnType>(1, "LazyValueFnType");
|
||||
lazy_fn_type->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_fn_type->base;
|
||||
lazy_fn_type->base.id = LazyValueIdFnType;
|
||||
|
||||
@ -26081,8 +26485,8 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct
|
||||
IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_type);
|
||||
result->value->special = ConstValSpecialLazy;
|
||||
|
||||
LazyValuePtrType *lazy_ptr_type = allocate<LazyValuePtrType>(1);
|
||||
lazy_ptr_type->ira = ira;
|
||||
LazyValuePtrType *lazy_ptr_type = allocate<LazyValuePtrType>(1, "LazyValuePtrType");
|
||||
lazy_ptr_type->ira = ira; ira_ref(ira);
|
||||
result->value->data.x_lazy = &lazy_ptr_type->base;
|
||||
lazy_ptr_type->base.id = LazyValueIdPtrType;
|
||||
|
||||
@ -27551,7 +27955,8 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
|
||||
assert(old_exec->first_err_trace_msg == nullptr);
|
||||
assert(expected_type == nullptr || !type_is_invalid(expected_type));
|
||||
|
||||
IrAnalyze *ira = allocate<IrAnalyze>(1);
|
||||
IrAnalyze *ira = allocate<IrAnalyze>(1, "IrAnalyze");
|
||||
ira->ref_count = 1;
|
||||
old_exec->analysis = ira;
|
||||
ira->codegen = codegen;
|
||||
|
||||
@ -27618,6 +28023,7 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
|
||||
ira->instruction_index += 1;
|
||||
}
|
||||
|
||||
ZigType *res_type;
|
||||
if (new_exec->first_err_trace_msg != nullptr) {
|
||||
codegen->trace_err = new_exec->first_err_trace_msg;
|
||||
if (codegen->trace_err != nullptr && new_exec->source_node != nullptr &&
|
||||
@ -27627,13 +28033,18 @@ ZigType *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutable *new_
|
||||
codegen->trace_err = add_error_note(codegen, codegen->trace_err,
|
||||
new_exec->source_node, buf_create_from_str("referenced here"));
|
||||
}
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
res_type = ira->codegen->builtin_types.entry_invalid;
|
||||
} else if (ira->src_implicit_return_type_list.length == 0) {
|
||||
return codegen->builtin_types.entry_unreachable;
|
||||
res_type = codegen->builtin_types.entry_unreachable;
|
||||
} else {
|
||||
return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
|
||||
res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
|
||||
ira->src_implicit_return_type_list.length);
|
||||
}
|
||||
|
||||
// It is now safe to free Pass 1 IR instructions.
|
||||
ira_deref(ira);
|
||||
|
||||
return res_type;
|
||||
}
|
||||
|
||||
bool ir_has_side_effects(IrInstruction *instruction) {
|
||||
@ -27969,6 +28380,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
|
||||
val->special = ConstValSpecialStatic;
|
||||
assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt);
|
||||
bigint_init_unsigned(&val->data.x_bigint, align_in_bytes);
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdSizeOf: {
|
||||
@ -28024,6 +28437,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
|
||||
val->special = ConstValSpecialStatic;
|
||||
assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt);
|
||||
bigint_init_unsigned(&val->data.x_bigint, abi_size);
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdSliceType: {
|
||||
@ -28102,6 +28517,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
|
||||
val->special = ConstValSpecialStatic;
|
||||
assert(val->type->id == ZigTypeIdMetaType);
|
||||
val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type);
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdPtrType: {
|
||||
@ -28173,6 +28590,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
|
||||
lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes,
|
||||
allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val);
|
||||
val->special = ConstValSpecialStatic;
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdOptType: {
|
||||
@ -28195,16 +28614,21 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
|
||||
assert(val->type->id == ZigTypeIdMetaType);
|
||||
val->data.x_type = get_optional_type(ira->codegen, payload_type);
|
||||
val->special = ConstValSpecialStatic;
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdFnType: {
|
||||
LazyValueFnType *lazy_fn_type = reinterpret_cast<LazyValueFnType *>(val->data.x_lazy);
|
||||
ZigType *fn_type = ir_resolve_lazy_fn_type(lazy_fn_type->ira, source_node, lazy_fn_type);
|
||||
IrAnalyze *ira = lazy_fn_type->ira;
|
||||
ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type);
|
||||
if (fn_type == nullptr)
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
val->special = ConstValSpecialStatic;
|
||||
assert(val->type->id == ZigTypeIdMetaType);
|
||||
val->data.x_type = fn_type;
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
case LazyValueIdErrUnionType: {
|
||||
@ -28233,6 +28657,8 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) {
|
||||
assert(val->type->id == ZigTypeIdMetaType);
|
||||
val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type);
|
||||
val->special = ConstValSpecialStatic;
|
||||
|
||||
// We can't free the lazy value here, because multiple other ZigValues might be pointing to it.
|
||||
return ErrorNone;
|
||||
}
|
||||
}
|
||||
|
@ -389,7 +389,7 @@ static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, Zi
|
||||
}
|
||||
Buf search_path = BUF_INIT;
|
||||
buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
|
||||
buf_append_str(&search_path, "\\..\\..\\include");
|
||||
buf_append_str(&search_path, "..\\..\\include");
|
||||
|
||||
Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path));
|
||||
bool exists;
|
||||
|
@ -13,7 +13,7 @@
|
||||
template<typename T>
|
||||
struct ZigList {
|
||||
void deinit() {
|
||||
free(items);
|
||||
deallocate(items, capacity);
|
||||
}
|
||||
void append(const T& item) {
|
||||
ensure_capacity(length + 1);
|
||||
|
@ -35,7 +35,9 @@ static const char *get_default_name(const char *name_or_null, size_t type_size)
|
||||
if (name_or_null != nullptr) return name_or_null;
|
||||
if (type_size >= unknown_names.length) {
|
||||
table_active = false;
|
||||
unknown_names.resize(type_size + 1);
|
||||
while (type_size >= unknown_names.length) {
|
||||
unknown_names.append(nullptr);
|
||||
}
|
||||
table_active = true;
|
||||
}
|
||||
if (unknown_names.at(type_size) == nullptr) {
|
||||
@ -66,7 +68,8 @@ void memprof_dealloc(const char *name, size_t count, size_t type_size) {
|
||||
name = get_default_name(name, type_size);
|
||||
auto existing_entry = usage_table.maybe_get(name);
|
||||
if (existing_entry == nullptr) {
|
||||
zig_panic("deallocated more than allocated; compromised memory usage stats");
|
||||
zig_panic("deallocated name '%s' (size %zu) not found in allocated table; compromised memory usage stats",
|
||||
name, type_size);
|
||||
}
|
||||
if (existing_entry->value.type_size != type_size) {
|
||||
zig_panic("deallocated name '%s' does not match expected type size %zu", name, type_size);
|
||||
|
@ -1554,7 +1554,7 @@ void os_stderr_set_color(TermColor color) {
|
||||
Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
|
||||
buf_appendf(output_buf, "%sLib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
@ -1586,7 +1586,7 @@ Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Ar
|
||||
Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
|
||||
buf_appendf(output_buf, "%sInclude\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
|
||||
if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
|
||||
return ErrorNone;
|
||||
}
|
||||
@ -1603,7 +1603,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
{
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
|
||||
buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
@ -1626,7 +1626,7 @@ Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
|
||||
}
|
||||
{
|
||||
buf_resize(output_buf, 0);
|
||||
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
|
||||
buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
|
||||
switch (platform_type) {
|
||||
case ZigLLVM_x86:
|
||||
buf_append_str(output_buf, "x86\\");
|
||||
|
@ -165,7 +165,7 @@ static inline void deallocate(T *old, size_t count, const char *name = nullptr)
|
||||
|
||||
template<typename T>
|
||||
static inline void destroy(T *old, const char *name = nullptr) {
|
||||
return deallocate(old, 1);
|
||||
return deallocate(old, 1, name);
|
||||
}
|
||||
|
||||
template <typename T, size_t n>
|
||||
|
@ -1670,10 +1670,17 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ var spartan_count: u16 = 300;
|
||||
\\ var byte: u8 = spartan_count;
|
||||
\\}
|
||||
\\export fn entry4() void {
|
||||
\\ var signed: i8 = -1;
|
||||
\\ var unsigned: u64 = signed;
|
||||
\\}
|
||||
,
|
||||
"tmp.zig:3:31: error: integer value 300 cannot be coerced to type 'u8'",
|
||||
"tmp.zig:7:22: error: integer value 300 cannot be coerced to type 'u8'",
|
||||
"tmp.zig:11:20: error: expected type 'u8', found 'u16'",
|
||||
"tmp.zig:11:20: note: unsigned 8-bit int cannot represent all possible unsigned 16-bit values",
|
||||
"tmp.zig:15:25: error: expected type 'u64', found 'i8'",
|
||||
"tmp.zig:15:25: note: unsigned 64-bit int cannot represent all possible signed 8-bit values",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
|
@ -10,9 +10,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\ B = 1,
|
||||
\\ C = 2
|
||||
\\};
|
||||
\\
|
||||
\\TEST_EXTERN_C void entry(enum Foo foo);
|
||||
\\
|
||||
,
|
||||
\\void entry(enum Foo foo);
|
||||
);
|
||||
|
||||
cases.add("declare struct",
|
||||
@ -34,8 +33,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\ uint64_t E;
|
||||
\\ uint64_t F;
|
||||
\\};
|
||||
\\
|
||||
\\TEST_EXTERN_C void entry(struct Foo foo);
|
||||
,
|
||||
\\void entry(struct Foo foo);
|
||||
\\
|
||||
);
|
||||
|
||||
@ -69,19 +68,19 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\ bool C;
|
||||
\\ struct Big D;
|
||||
\\};
|
||||
\\
|
||||
\\TEST_EXTERN_C void entry(union Foo foo);
|
||||
,
|
||||
\\void entry(union Foo foo);
|
||||
\\
|
||||
);
|
||||
|
||||
cases.add("declare opaque type",
|
||||
\\export const Foo = @OpaqueType();
|
||||
\\const Foo = @OpaqueType();
|
||||
\\
|
||||
\\export fn entry(foo: ?*Foo) void { }
|
||||
,
|
||||
\\struct Foo;
|
||||
\\
|
||||
\\TEST_EXTERN_C void entry(struct Foo * foo);
|
||||
,
|
||||
\\void entry(struct Foo * foo);
|
||||
);
|
||||
|
||||
cases.add("array field-type",
|
||||
@ -95,8 +94,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\ int32_t A[2];
|
||||
\\ uint32_t * B[4];
|
||||
\\};
|
||||
\\
|
||||
\\TEST_EXTERN_C void entry(struct Foo foo, uint8_t bar[]);
|
||||
,
|
||||
\\void entry(struct Foo foo, uint8_t bar[]);
|
||||
\\
|
||||
);
|
||||
|
||||
@ -110,7 +109,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\}
|
||||
,
|
||||
\\struct S;
|
||||
\\TEST_EXTERN_C uint8_t a(struct S * s);
|
||||
,
|
||||
\\uint8_t a(struct S * s);
|
||||
\\
|
||||
);
|
||||
|
||||
@ -125,7 +125,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\}
|
||||
,
|
||||
\\union U;
|
||||
\\TEST_EXTERN_C uint8_t a(union U * s);
|
||||
,
|
||||
\\uint8_t a(union U * s);
|
||||
\\
|
||||
);
|
||||
|
||||
@ -140,7 +141,8 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
||||
\\}
|
||||
,
|
||||
\\enum E;
|
||||
\\TEST_EXTERN_C uint8_t a(enum E * s);
|
||||
,
|
||||
\\uint8_t a(enum E * s);
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
const std = @import("std");
|
||||
const io = std.io;
|
||||
const process = std.process;
|
||||
const File = std.fs.File;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
const warn = std.debug.warn;
|
||||
const allocator = std.debug.global_allocator;
|
||||
@ -12,6 +12,8 @@ pub fn main() !void {
|
||||
var catted_anything = false;
|
||||
const stdout_file = io.getStdOut();
|
||||
|
||||
const cwd = fs.cwd();
|
||||
|
||||
while (args_it.next(allocator)) |arg_or_err| {
|
||||
const arg = try unwrapArg(arg_or_err);
|
||||
if (mem.eql(u8, arg, "-")) {
|
||||
@ -20,7 +22,7 @@ pub fn main() !void {
|
||||
} else if (arg[0] == '-') {
|
||||
return usage(exe);
|
||||
} else {
|
||||
const file = File.openRead(arg) catch |err| {
|
||||
const file = cwd.openFile(arg, .{}) catch |err| {
|
||||
warn("Unable to open file: {}\n", @errorName(err));
|
||||
return err;
|
||||
};
|
||||
@ -40,7 +42,7 @@ fn usage(exe: []const u8) !void {
|
||||
return error.Invalid;
|
||||
}
|
||||
|
||||
fn cat_file(stdout: File, file: File) !void {
|
||||
fn cat_file(stdout: fs.File, file: fs.File) !void {
|
||||
var buf: [1024 * 4]u8 = undefined;
|
||||
|
||||
while (true) {
|
||||
|
@ -2,3 +2,5 @@
|
||||
uint32_t add(uint32_t a, uint32_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
uint32_t foo = 12345;
|
||||
|
@ -1,2 +1,3 @@
|
||||
#include <stdint.h>
|
||||
uint32_t add(uint32_t a, uint32_t b);
|
||||
extern uint32_t foo;
|
||||
|
@ -6,3 +6,7 @@ test "C add" {
|
||||
const result = c.add(1, 2);
|
||||
expect(result == 3);
|
||||
}
|
||||
|
||||
test "C extern variable" {
|
||||
expect(c.foo == 12345);
|
||||
}
|
||||
|
@ -70,6 +70,26 @@ const test_targets = [_]TestTarget{
|
||||
.link_libc = true,
|
||||
},
|
||||
|
||||
TestTarget{
|
||||
.target = Target{
|
||||
.Cross = CrossTarget{
|
||||
.os = .linux,
|
||||
.arch = .i386,
|
||||
.abi = .none,
|
||||
},
|
||||
},
|
||||
},
|
||||
TestTarget{
|
||||
.target = Target{
|
||||
.Cross = CrossTarget{
|
||||
.os = .linux,
|
||||
.arch = .i386,
|
||||
.abi = .musl,
|
||||
},
|
||||
},
|
||||
.link_libc = true,
|
||||
},
|
||||
|
||||
TestTarget{
|
||||
.target = Target{
|
||||
.Cross = CrossTarget{
|
||||
|
Loading…
x
Reference in New Issue
Block a user